/* * sprite_anim — запустить кадровую авто-анимацию: кадры first..last * вдоль оси ленты (вертикальная; ANIM_HORIZ — вдоль X), смена раз в * speed кадров, mode = ANIM_LOOP / ANIM_PINGPONG / ANIM_ONCE. * * Всё дорогое считается ЗДЕСЬ один раз, чтобы тикеру осталась только * 16-бит арифметика указателя: * an_step — шаг кадра в БАЙТАХ src: fw (гориз. лента: следующий кадр * правее на ширину кадра) или fh*stride (верт.: ниже на * fh строк источника); * an_src0 — готовый адрес кадра first (для wrap ANIM_LOOP: тикер * прыгает указателем, ничего не пересчитывая). * Смена кадра тикером = s->src ± an_step; sx/sy тикер НЕ трогает * (см. sprite.h про кэш src). * * Ставит текущий кадр на first (через sprite_frame — единственную * точку пересчёта (sx,sy)→src), сбрасывает SPR_ANIM_DONE, подключает * тикер (_spr_tick_fn) — до первого вызова sprite_anim/sprite_moveto * код тикера в программу не линкуется. Требование: индексы ≤ 127, * first ≤ last. Move-статусы (SPR_MOVE_*) не трогаются. */ #include "../_bgi.h" #include void sprite_anim(sprite_t *s, uint8_t first, uint8_t last, uint8_t speed, uint8_t mode) { uint8_t f = s->an_flags & (SPR_MOVE_ON | SPR_MOVE_DONE); uint8_t step_px = (mode & ANIM_HORIZ) ? s->w : s->h; int lo = 0; uint8_t i; for (i = 0; i < first; i++) lo += step_px; /* first*step без умножения */ if (!speed) speed = 1; /* Ось — на кадр first (sprite_frame пересчитает кэш src + dirty). */ if (mode & ANIM_HORIZ) sprite_frame(s, lo, s->sy); else sprite_frame(s, s->sx, lo); /* Байтовый шаг кадра и адрес first-кадра — всё, что нужно тикеру. */ s->an_step = (mode & ANIM_HORIZ) ? s->w : (uint16_t)s->h * s->stride; s->an_src0 = s->src; /* src уже стоит на first */ s->an_first = first; s->an_last = last; s->an_frame = first; s->an_speed = speed; s->an_timer = speed; f |= SPR_ANIM_ON; if (mode & ANIM_HORIZ) f |= _SPR_STRIP_HORZ; if ((mode & 0x03u) == ANIM_PINGPONG) f |= _SPR_ANIM_PP; if ((mode & 0x03u) == ANIM_ONCE) f |= _SPR_ANIM_ONCE; s->an_flags = f; _spr_tick_fn = _sprite_tick; }