# Tween chain

> tween.chain plays tweens one after another and tween.all at the same time, one inside the other, declared once and played again from the start.

Example `tween-chain`, running at https://nacatamalon.com/en/examples/tween-chain. The whole catalogue: https://nacatamalon.com/llms/examples.md.

A tween is declared first and played after, as many times as you like. `tween.chain(a, b, c)` plays tweens one after another, each step with no `from` starting where the one before left it, and `tween.all` plays them at the same time. Both are tweens too, so one goes inside the other, and they are what is played: their steps are not.

A field moved `to` makes the same trip each time it is played; one moved `by` goes that far again from wherever it is.

Click the preview first so it hears the keyboard. Space plays the door scene (again, from the start), K knocks the hero back sixteen pixels from wherever he is.

## Code

`index.ts`:

```ts
import { createGame } from 'nacatamalon';
import { Scene } from './scenes/demo';
import { WIDTH, HEIGHT } from './settings';

const game = createGame('#app', {
    width: WIDTH,
    height: HEIGHT,
    background: '#0a0a18'
});

// Declare Scenes
game({
    Scene
});
```

`components/door_sequence.ts`:

```ts
import { useKeyboard, useTween, useUpdate } from 'nacatamalon';
import { easeInOutSine, easeOutBack, easeOutQuad } from 'nacatamalon/math';
import { FLOOR, SEQUENCE, KNOCK } from '../settings';
import type { Stage } from './stage';

export const DoorSequence = ({ door, hero, status }: ReturnType<typeof Stage>) => {
    const tween = useTween();
    const keyboard = useKeyboard();

    // The door slides up, the hero walks through, and the door comes down behind him: three steps,
    // one tween. Then he takes a bow, two things at once.
    const scene = tween.chain(
        tween({
            target: door,
            to: {
                transform: {
                    y: FLOOR - SEQUENCE.lift
                }
            },
            duration: SEQUENCE.open,
            ease: easeInOutSine
        }),
        tween({
            target: hero,
            to: {
                transform: {
                    x: SEQUENCE.travel
                }
            },
            duration: SEQUENCE.walk
        }),
        tween({
            target: door,
            to: {
                transform: {
                    y: FLOOR
                }
            },
            duration: SEQUENCE.close,
            ease: easeOutBack
        }),
        tween.all(
            tween({
                target: hero,
                to: {
                    transform: {
                        scaleY: 0.6
                    }
                },
                duration: SEQUENCE.bow,
                yoyo: true,
                repeat: 1
            }),
            tween({
                target: hero,
                to: {
                    tint: {
                        g: 1
                    }
                },
                duration: SEQUENCE.tint
            }),
        ),
    );

    scene.onEnd(() => {
        status.text = 'THE END';
    });

    // A push back, from wherever he stands: each press is sixteen pixels more.
    const knock = tween({
        target: hero,
        by: {
            transform: {
                x: KNOCK.distance
            }
        },
        duration: KNOCK.duration,
        ease: easeOutQuad
    });

    useUpdate(() => {
        // Played again, the door and the hero go back to where the first play found them.
        if (keyboard.justPressed(' ')) {
            scene.play();
            status.text = 'PLAYING';
        }

        if (keyboard.justPressed('k') && !scene.playing) {
            knock.play();
        }
    });
};
```

`components/stage.ts`:

```ts
import { createSprite, createText } from 'nacatamalon';
import { WIDTH, FLOOR } from '../settings';

export const Stage = () => {
    createSprite({
        tint: '#3a3a55',
        width: WIDTH - 40,
        height: 4,
        transform: {
            x: WIDTH / 2,
            y: FLOOR + 2
        }
    });

    const door = createSprite({
        tint: '#8a5a2b',
        width: 20,
        height: 64,
        anchor: {
            x: 0.5,
            y: 1
        },
        transform: {
            x: 300,
            y: FLOOR
        }
    });

    const hero = createSprite({
        tint: '#4fc3f7',
        width: 20,
        height: 28,
        anchor: {
            x: 0.5,
            y: 1
        },
        transform: {
            x: 80,
            y: FLOOR
        }
    });

    const status = createText({
        text: 'SPACE  K',
        anchor: {
            x: 0.5,
            y: 0
        },
        transform: {
            x: WIDTH / 2,
            y: 260
        }
    });

    return {
        door,
        hero,
        status
    };
};
```

`scenes/demo.ts`:

```ts
import { createScene, useSpawn } from 'nacatamalon';
import { Stage } from '../components/stage';
import { DoorSequence } from '../components/door_sequence';

export const Scene = () => {
    const stage = useSpawn(Stage)();

    useSpawn(DoorSequence)(stage.api);

    return createScene();
};
```

`settings.ts`:

```ts
export const WIDTH = 480;
export const HEIGHT = 320;
export const FLOOR = 200;
export const SEQUENCE = {
    open: 0.5,
    walk: 1.2,
    close: 0.3,
    bow: 0.15,
    tint: 0.3,
    travel: 400,
    lift: 60
};
export const KNOCK = {
    distance: -16,
    duration: 0.2
};
```
