# Pointer pressed, held, released

> The pointer asked like the keyboard, from an update: a click fires once, holding charges, letting go of a full charge fires a big shot, and the right button changes colour.

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

`usePointer()` can be asked from an update the same way as the keyboard. `justPressed()` is true for the one frame a finger or a button goes down, so a click fires one shot however long it is held. `isDown()` is true for as long as it is held, and here charges the ring around the cannon. `justReleased()` is true for the frame it comes up: letting go of a full charge fires a big shot.

With a button, only that one: `justPressed('right')` is the right button, and changes the colour of the shots. `'main'` is the left button or a finger, and left out it is any; every name is in `INPUT.POINTER_BUTTONS`. None of them says what was pressed: for that there are `onDown` and its `target`, or `isPressing(sprite)`. Click to shoot, hold to charge, right click to change colour.

## Code

`index.ts`:

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

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

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

`components/cannon.ts`:

```ts
import { createSprite, usePointer, useSpawn, useUpdate } from 'nacatamalon';
import { clamp, vec2Normalize, vec2Sub } from 'nacatamalon/math';
import { Shot } from './shot';
import { WIDTH, HEIGHT, COLORS, RING, CHARGE, SHOT } from '../settings';

export const Cannon = () => {
    const pointer = usePointer();

    const fire = useSpawn(Shot, {
        parent: 'scene'
    });

    const cannon = {
        x: WIDTH / 2,
        y: HEIGHT - 16
    };

    createSprite({
        width: 20,
        height: 20,
        tint: '#262d48',
        transform: {
            x: cannon.x,
            y: cannon.y
        }
    });

    const ring = createSprite({
        width: 28,
        height: 28,
        tint: RING,
        transform: {
            x: cannon.x,
            y: cannon.y,
            rotation: Math.PI / 4
        }
    });

    let color = 0;
    let held = 0;
    let last = '';

    const shoot = (size: number, speed: number) => {
        const at = pointer.position();

        if (at === null) {
            return;
        }

        const aim = vec2Normalize(vec2Sub({
            x: at.screenX,
            y: at.screenY
        }, cannon));

        fire({
            x: cannon.x,
            y: cannon.y,
            dx: aim.x * speed,
            dy: aim.y * speed,
            size,
            color
        });
    };

    useUpdate((delta) => {
        // Once: the frame it went down.
        if (pointer.justPressed('main')) {
            held = 0;
            shoot(SHOT.smallSize, SHOT.smallSpeed);
            last = 'JUST PRESSED';
        }

        // While: every frame it is held.
        if (pointer.isDown()) {
            held += delta;

            if (held > 0.15) {
                last = 'DOWN';
            }
        }

        // On letting go: the frame it came up.
        if (pointer.justReleased('main')) {
            if (held >= CHARGE) {
                shoot(SHOT.chargedSize, SHOT.chargedSpeed);
            }

            last = held >= CHARGE ? 'JUST RELEASED: BIG ONE' : 'JUST RELEASED';
            held = 0;
        }

        // Only the right button.
        if (pointer.justPressed('right')) {
            color = (color + 1) % COLORS.length;
            last = 'RIGHT BUTTON';
        }

        // Held too, since `isDown` is any button: it charges nothing.
        if (pointer.justReleased('right')) {
            held = 0;
        }

        const charge = clamp(held / CHARGE, 0, 1);

        ring.transform.scaleX = 1 + charge * 0.6;
        ring.transform.scaleY = 1 + charge * 0.6;
        ring.tint = charge >= 1 ? COLORS[color] : RING;
    });

    return {
        message: () => last
    };
};
```

`components/hud.ts`:

```ts
import { createText, useUpdate } from 'nacatamalon';
import { WIDTH } from '../settings';
import type { Cannon } from './cannon';

export const Hud = ({ cannon }: { cannon: ReturnType<typeof Cannon> }) => {
    const said = createText({
        text: '',
        transform: {
            x: 8,
            y: 8
        },
        tint: '#9aa4c0'
    });

    createText({
        text: 'CLICK  HOLD  RIGHT CLICK',
        transform: {
            x: WIDTH / 2 - 120,
            y: 20
        },
        tint: '#6b7390'
    });
    useUpdate(() => {
        said.text = cannon.message();
    });
};
```

`components/shot.ts`:

```ts
import { createSprite, destroy, useSelf, useTransform, useUpdate } from 'nacatamalon';
import { COLORS, WIDTH, HEIGHT } from '../settings';

export const Shot = ({ x, y, dx, dy, size, color }: { x: number; y: number; dx: number; dy: number; size: number; color: number }) => {
    const self = useSelf();

    const place = useTransform({
        x,
        y
    });

    createSprite({
        width: size,
        height: size,
        tint: COLORS[color],
        transform: {
            x: 0,
            y: 0,
            rotation: Math.PI / 4
        }
    });
    useUpdate((delta) => {
        place.x += dx * delta;
        place.y += dy * delta;

        if (place.x < -20 || place.x > WIDTH + 20 || place.y < -20 || place.y > HEIGHT + 20) {
            destroy(self);
        }
    });
};
```

`scenes/demo.ts`:

```ts
import { createScene, useSpawn } from 'nacatamalon';
import { Cannon } from '../components/cannon';
import { Hud } from '../components/hud';

export const Range = () => {
    const cannon = useSpawn(Cannon)();

    useSpawn(Hud)({
        cannon: cannon.api
    });

    return createScene();
};
```

`settings.ts`:

```ts
import { getColor } from 'nacatamalon';

export const WIDTH = 320;
export const HEIGHT = 180;
export const CHARGE = 0.6;
export const COLORS = ['#ffcf4a', '#7fc8f8', '#ff7a90'].map(getColor);
export const RING = getColor('#3a4266');
export const SHOT = {
    smallSize: 6,
    smallSpeed: 220,
    chargedSize: 16,
    chargedSpeed: 160
};
```
