Files
Sprinter-SDCC/tests/spranim/spranim.c
T
snark13 eb9ca0179d libbgi: авто-анимация спрайтов — кадровая + tween (tests/spranim все PASS)
Реализация §9г по требованиям пользователя:
- sprite_anim(first,last,speed,mode): ANIM_LOOP / ANIM_PINGPONG /
  ANIM_ONCE (one-shot замирает на last), | ANIM_HORIZ для
  горизонтальных лент.  Смена кадра в тике = ±an_step к оси ленты —
  без умножений (осевое смещение first считается в setup циклом).
- sprite_moveto(tx,ty,max_step,interval): Брезенхэм порциями
  ≤max_step вдоль большей оси (меньшая — err-аккумулятором,
  нелинейные шаги Y сами собой), деления нет.
- Одновременность кадровой и tween — независимые поля/биты.
- Статус: sprite_anim_status() — битовое поле SPR_ANIM_ON/DONE +
  SPR_MOVE_ON/DONE; sprite_anim_frame() — текущий индекс кадра;
  sprite_moving().
- Стопы: sprite_anim_stop(frame | -1 = текущий);
  sprite_move_stop(0 = замереть / 1 = прыжок в цель + DONE).
- Тикер _sprite_tick — проход 0 sprite_update через funcptr
  _spr_tick_fn (DCE: без sprite_anim/moveto код не линкуется,
  цена — один if на кадр; +40 Б программе с движком, sprite_t +18 Б).

tests/spranim (MAME dev, все PASS): LOOP/PINGPONG (разворот на границе
без удвоения краёв)/ONCE+DONE; пример (0,0)→(100,50), шаг 5,
интервал 4 → ровно 80 кадров, Y идёт 3/2/3/2; стопы обоих видов.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:14:31 +03:00

143 lines
6.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* spranim — тест авто-анимаций спрайтового движка (§9г):
* sprite_anim (LOOP/PINGPONG/ONCE) + sprite_moveto + статусы + стопы.
*
* Логика проверяется ПРОГРАММНО по полям sprite_t: спрайты невидимы,
* тикер работает независимо от видимости, блитов нет — sprite_update
* гоняется в текстовом режиме (скобка W3 безвредна). Ожидания:
*
* T1 LOOP 0..3 speed=2: смена раз в 2 тика: после 8 тиков frame
* снова 0 (0→1→2→3→0), sy вернулся в 0.
* T2 PINGPONG 0..3 speed=1: 1,2,3,2,1,0,1 — на тике 7 frame==1,
* разворот на границе без удвоения краёв.
* T3 ONCE 0..2 speed=1: после 2 тиков frame==2 и ANIM_ON ещё стоит,
* после 3-го — ON снят, DONE взведён, frame==2 (замер на last).
* T4 MOVETO (0,0)→(100,50) step=5 interval=4 (пример из §9г):
* тик 4: (5,3); тик 8: (10,5); тик 79: НЕ DONE; тик 80: (100,50),
* MOVE_ON снят, MOVE_DONE взведён. Параллельно у спрайта идёт
* LOOP-анимация (одновременность) — frame успел смениться.
* T5 sprite_anim_stop(1): ON снят, frame==1, ось = 16.
* sprite_move_stop(at_target=1) на полпути: прыжок в цель + DONE.
*
* Кадры: RAM-лента 16×16×4 (getimage), кадр k залит k — содержимое
* не важно (блитов нет), важен размер h=16 → an_step.
*/
#include <stdio.h>
#include <string.h>
#include <sprite.h>
static uint8_t strip[4 + 16 * 16 * 4];
static sprite_t sp[4];
static void build_strip(void)
{
memset(strip, 0, sizeof strip);
strip[0] = 16; strip[2] = 64; /* w=16, h=64 (4 кадра) */
}
static void tick(sprite_t *arr, uint8_t n, uint16_t times)
{
while (times--)
sprite_update(arr, n);
}
int main(void)
{
uint8_t ok;
puts("SPRANIM: frame/tween auto-animation");
build_strip();
/* ---- T1: LOOP ---------------------------------------------------- */
sprite_init(&sp[0], strip);
sp[0].w = 16; sp[0].h = 16;
sprite_anim(&sp[0], 0, 3, 2, ANIM_LOOP);
tick(sp, 1, 2);
ok = (sprite_anim_frame(&sp[0]) == 1 && sp[0].sy == 16);
tick(sp, 1, 4); /* всего 6: 0→1→2→3 */
ok = ok && (sprite_anim_frame(&sp[0]) == 3 && sp[0].sy == 48);
tick(sp, 1, 2); /* всего 8: wrap → 0 */
ok = ok && (sprite_anim_frame(&sp[0]) == 0 && sp[0].sy == 0);
printf("T1 LOOP: %s\n", ok ? "PASS" : "FAIL");
/* ---- T2: PINGPONG ------------------------------------------------ */
sprite_init(&sp[0], strip);
sp[0].w = 16; sp[0].h = 16;
sprite_anim(&sp[0], 0, 3, 1, ANIM_PINGPONG);
tick(sp, 1, 3); /* 1,2,3 */
ok = (sprite_anim_frame(&sp[0]) == 3);
tick(sp, 1, 1); /* разворот: 2 */
ok = ok && (sprite_anim_frame(&sp[0]) == 2 && sp[0].sy == 32);
tick(sp, 1, 2); /* 1,0 */
ok = ok && (sprite_anim_frame(&sp[0]) == 0);
tick(sp, 1, 1); /* разворот: 1 */
ok = ok && (sprite_anim_frame(&sp[0]) == 1 && sp[0].sy == 16);
printf("T2 PINGPONG: %s\n", ok ? "PASS" : "FAIL");
/* ---- T3: ONCE ---------------------------------------------------- */
sprite_init(&sp[0], strip);
sp[0].w = 16; sp[0].h = 16;
sprite_anim(&sp[0], 0, 2, 1, ANIM_ONCE);
tick(sp, 1, 2); /* 1,2 — дошли до last */
ok = (sprite_anim_frame(&sp[0]) == 2 &&
(sprite_anim_status(&sp[0]) & SPR_ANIM_ON) &&
!(sprite_anim_status(&sp[0]) & SPR_ANIM_DONE));
tick(sp, 1, 1); /* фиксация: ON→off,DONE */
ok = ok && (sprite_anim_frame(&sp[0]) == 2 && sp[0].sy == 32 &&
!(sprite_anim_status(&sp[0]) & SPR_ANIM_ON) &&
(sprite_anim_status(&sp[0]) & SPR_ANIM_DONE));
tick(sp, 1, 5); /* стоит на месте */
ok = ok && (sprite_anim_frame(&sp[0]) == 2);
printf("T3 ONCE: %s\n", ok ? "PASS" : "FAIL");
/* ---- T4: MOVETO + одновременная кадровая ------------------------- */
sprite_init(&sp[0], strip);
sp[0].w = 16; sp[0].h = 16; /* x=y=0 после init */
sprite_anim(&sp[0], 0, 3, 3, ANIM_LOOP);
sprite_moveto(&sp[0], 100, 50, 5, 4);
ok = (sprite_moving(&sp[0]) != 0);
tick(sp, 1, 4);
ok = ok && (sp[0].x == 5 && sp[0].y == 3); /* Y: 3/2/3/2 */
tick(sp, 1, 4);
ok = ok && (sp[0].x == 10 && sp[0].y == 5);
tick(sp, 1, 71); /* всего 79 — ещё едет */
ok = ok && sprite_moving(&sp[0]) &&
!(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE);
tick(sp, 1, 1); /* 80: приехали */
ok = ok && (sp[0].x == 100 && sp[0].y == 50 &&
!sprite_moving(&sp[0]) &&
(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE));
ok = ok && (sprite_anim_frame(&sp[0]) != 0 ||
sp[0].an_timer != 3); /* кадровая тоже тикала */
printf("T4 MOVETO: %s (x=%d y=%d)\n",
ok ? "PASS" : "FAIL", sp[0].x, sp[0].y);
/* ---- T5: стопы ---------------------------------------------------- */
sprite_init(&sp[0], strip);
sp[0].w = 16; sp[0].h = 16;
sprite_anim(&sp[0], 0, 3, 1, ANIM_LOOP);
tick(sp, 1, 2); /* frame 2 */
sprite_anim_stop(&sp[0], 1); /* остановить на кадре 1 */
ok = (!(sprite_anim_status(&sp[0]) & SPR_ANIM_ON) &&
sprite_anim_frame(&sp[0]) == 1 && sp[0].sy == 16);
tick(sp, 1, 4); /* не двигается */
ok = ok && (sprite_anim_frame(&sp[0]) == 1);
sprite_moveto(&sp[0], 60, 60, 3, 1);
tick(sp, 1, 5); /* на полпути */
sprite_move_stop(&sp[0], 1); /* добить в цель */
ok = ok && (sp[0].x == 60 && sp[0].y == 60 &&
!sprite_moving(&sp[0]) &&
(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE));
sprite_moveto(&sp[0], 0, 0, 3, 1);
tick(sp, 1, 5);
sprite_move_stop(&sp[0], 0); /* замереть где есть */
ok = ok && (sp[0].x != 0 && !sprite_moving(&sp[0]) &&
!(sprite_anim_status(&sp[0]) & SPR_MOVE_DONE));
printf("T5 STOPS: %s\n", ok ? "PASS" : "FAIL");
puts("done");
return 0;
}