mirror of
https://github.com/jeremysrand/a2bejwld.git
synced 2024-06-09 21:29:31 +00:00
543 lines
14 KiB
C
543 lines
14 KiB
C
//
|
|
// anim.c
|
|
// a2bejwld
|
|
//
|
|
// Created by Jeremy Rand on 2016-07-22.
|
|
// Copyright © 2016 Jeremy Rand. All rights reserved.
|
|
//
|
|
|
|
|
|
#include <conio.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "anim.h"
|
|
#include "dbllores.h"
|
|
#include "game.h"
|
|
#include "machine.h"
|
|
#include "sound.h"
|
|
#include "ui.h"
|
|
|
|
|
|
// Defines
|
|
|
|
#define STAR_CYCLES_VISIBLE 3000
|
|
#define STAR_CYCLES_INVISIBLE 1000
|
|
|
|
#define DROP_ACCELERATION 1
|
|
|
|
#define VERTICAL_PIXELS 48
|
|
#define HORIZONTAL_PIXELS 64
|
|
|
|
#define VERTICAL_PIXELS_PER_SQUARE (VERTICAL_PIXELS / BOARD_SIZE)
|
|
#define HORIZONTAL_PIXELS_PER_SQUARE (HORIZONTAL_PIXELS / BOARD_SIZE)
|
|
|
|
#define DRAW_GEM_AT_XY(x, y, gemType, starred) \
|
|
gTempY = y; \
|
|
gTempX = x; \
|
|
gTempGemType = gemType; \
|
|
if (starred) { \
|
|
__asm__ volatile("lda %v", gTempY); \
|
|
__asm__ volatile("ldx %v", gTempX); \
|
|
__asm__ volatile("ldy %v", gTempGemType); \
|
|
__asm__ volatile("jsr _drawAndStarGemAtXY"); \
|
|
} else { \
|
|
__asm__ volatile("lda %v", gTempY); \
|
|
__asm__ volatile("ldx %v", gTempX); \
|
|
__asm__ volatile("ldy %v", gTempGemType); \
|
|
__asm__ volatile("jsr _drawGemAtXY"); \
|
|
}
|
|
|
|
|
|
// Typedefs
|
|
|
|
typedef struct tStarAnimState
|
|
{
|
|
uint16_t counter;
|
|
bool starVisible;
|
|
} tStarAnimState;
|
|
|
|
|
|
typedef struct tClearGemAnimState
|
|
{
|
|
uint8_t squaresToClear[NUM_SQUARES / (sizeof(uint8_t))];
|
|
bool gotOne;
|
|
} tClearGemAnimState;
|
|
|
|
|
|
typedef struct tDropGemInfo {
|
|
uint8_t x;
|
|
int8_t y;
|
|
int8_t endY;
|
|
uint8_t speed;
|
|
tGemType gemType;
|
|
bool starred;
|
|
bool visible;
|
|
bool landed;
|
|
struct tDropGemInfo *lowerNeighbour;
|
|
} tDropGemInfo;
|
|
|
|
typedef struct tDropGemAnimState {
|
|
tDropGemInfo gemState[NUM_SQUARES];
|
|
bool gotOne;
|
|
} tDropGemAnimState;
|
|
|
|
typedef void __fastcall__ (*tClearGemHandler)(tSquare square);
|
|
|
|
|
|
// Globals
|
|
|
|
static tStarAnimState gStarAnimState;
|
|
static tClearGemAnimState gClearGemAnimState;
|
|
static tDropGemAnimState gDropGemAnimState;
|
|
|
|
static uint8_t gTempX;
|
|
static uint8_t gTempY;
|
|
static uint8_t gTempGemType;
|
|
|
|
static tClearGemHandler gClearGemHandler[] = {
|
|
explodeGemFrame1,
|
|
explodeGemFrame2,
|
|
explodeGemFrame3,
|
|
explodeGemFrame4,
|
|
explodeGemFrame5,
|
|
explodeGemFrame6,
|
|
drawBgSquare
|
|
};
|
|
|
|
|
|
// Implementation
|
|
|
|
|
|
void drawGemAtSquare(tSquare square)
|
|
{
|
|
static uint8_t tempSquare;
|
|
static uint8_t tempGemType;
|
|
|
|
tempGemType = gemTypeAtSquare(square);
|
|
tempSquare = square;
|
|
|
|
__asm__ volatile("lda %v", tempSquare);
|
|
__asm__ volatile("ldy %v", tempGemType);
|
|
__asm__ volatile("jsr _drawGem");
|
|
}
|
|
|
|
|
|
static void hideStars(void)
|
|
{
|
|
tSquare square;
|
|
|
|
gVblWait();
|
|
|
|
for (square = 0; square < NUM_SQUARES; square++) {
|
|
if (gemIsStarredAtSquare(square)) {
|
|
drawGemAtSquare(square);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void showStars(void)
|
|
{
|
|
tSquare square;
|
|
|
|
gVblWait();
|
|
|
|
for (square = 0; square < NUM_SQUARES; square++) {
|
|
if (gemIsStarredAtSquare(square)) {
|
|
starGem(square);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void resetStarAnim(void)
|
|
{
|
|
if (!gStarAnimState.starVisible) {
|
|
showStars();
|
|
}
|
|
gStarAnimState.starVisible = true;
|
|
gStarAnimState.counter = STAR_CYCLES_VISIBLE;
|
|
}
|
|
|
|
|
|
void doStarAnim(void)
|
|
{
|
|
gStarAnimState.counter--;
|
|
if (gStarAnimState == 0) {
|
|
if (gStarAnimState.starVisible) {
|
|
gStarAnimState.starVisible = false;
|
|
gStarAnimState.counter = STAR_CYCLES_INVISIBLE;
|
|
hideStars();
|
|
} else {
|
|
gStarAnimState.starVisible = true;
|
|
gStarAnimState.counter = STAR_CYCLES_VISIBLE;
|
|
showStars();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void beginClearGemAnim(void)
|
|
{
|
|
memset(&gClearGemAnimState, 0, sizeof(gClearGemAnimState));
|
|
beginClearGemSound();
|
|
}
|
|
|
|
|
|
void addClearAtSquare(tSquare square)
|
|
{
|
|
uint8_t bit = (1 << (square & 0x7));
|
|
uint8_t offset = (square >> 3);
|
|
|
|
gClearGemAnimState.squaresToClear[offset] |= bit;
|
|
gClearGemAnimState.gotOne = true;
|
|
}
|
|
|
|
|
|
void undoClearAtSquare(tSquare square)
|
|
{
|
|
uint8_t bit = (1 << (square & 0x7));
|
|
uint8_t offset = (square >> 3);
|
|
|
|
gClearGemAnimState.squaresToClear[offset] &= (~bit);
|
|
}
|
|
|
|
|
|
#undef DEBUG_CLEAR_ANIM
|
|
void endClearGemAnim(void)
|
|
{
|
|
tSquare square;
|
|
uint8_t bit;
|
|
uint8_t offset;
|
|
uint8_t frame;
|
|
|
|
if (!gClearGemAnimState.gotOne)
|
|
return;
|
|
|
|
for (frame = 0; frame < (sizeof(gClearGemHandler) / sizeof(gClearGemHandler[0])); frame++) {
|
|
bit = 1;
|
|
offset = 0;
|
|
|
|
playClearGemSound(frame);
|
|
|
|
gVblWait();
|
|
for (square = 0; square < NUM_SQUARES; square++) {
|
|
if ((gClearGemAnimState.squaresToClear[offset] & bit) != 0) {
|
|
(gClearGemHandler[frame])(square);
|
|
}
|
|
bit <<= 1;
|
|
if (bit == 0) {
|
|
bit = 1;
|
|
offset++;
|
|
}
|
|
}
|
|
#ifdef DEBUG_CLEAR_ANIM
|
|
cgetc();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
#undef DEBUG_SWAP_ANIM
|
|
void swapSquares(tSquare square1, tGemType gemType1, bool starred1,
|
|
tSquare square2, tGemType gemType2, bool starred2)
|
|
{
|
|
|
|
uint8_t x1 = (SQUARE_TO_X(square1) * HORIZONTAL_PIXELS_PER_SQUARE);
|
|
uint8_t x2 = (SQUARE_TO_X(square2) * HORIZONTAL_PIXELS_PER_SQUARE);
|
|
uint8_t y1 = (SQUARE_TO_Y(square1) * VERTICAL_PIXELS_PER_SQUARE);
|
|
uint8_t y2 = (SQUARE_TO_Y(square2) * VERTICAL_PIXELS_PER_SQUARE);
|
|
uint8_t temp;
|
|
|
|
// If x1 is bigger than x2, then swap. We want x1 to go up and x2
|
|
// to go down. Easier to make that assumption. Same for y1 and
|
|
// y2.
|
|
if ((x1 > x2) ||
|
|
(y1 > y2)) {
|
|
temp = x2;
|
|
x2 = x1;
|
|
x1 = temp;
|
|
|
|
temp = y2;
|
|
y2 = y1;
|
|
y1 = temp;
|
|
|
|
temp = gemType2;
|
|
gemType2 = gemType1;
|
|
gemType1 = temp;
|
|
|
|
temp = starred2;
|
|
starred2 = starred1;
|
|
starred1 = temp;
|
|
}
|
|
|
|
if (x1 < x2) {
|
|
temp = x2;
|
|
while (x1 < temp) {
|
|
x1++;
|
|
x2--;
|
|
gVblWait();
|
|
drawBgSquare(square1);
|
|
drawBgSquare(square2);
|
|
DRAW_GEM_AT_XY(x1, y1, gemType1, starred1);
|
|
DRAW_GEM_AT_XY(x2, y2, gemType2, starred2);
|
|
#ifdef DEBUG_SWAP_ANIM
|
|
cgetc();
|
|
#endif
|
|
}
|
|
} else {
|
|
temp = y2;
|
|
while (y1 < temp) {
|
|
y1++;
|
|
y2--;
|
|
gVblWait();
|
|
drawBgSquare(square1);
|
|
drawBgSquare(square2);
|
|
DRAW_GEM_AT_XY(x1, y1, gemType1, starred1);
|
|
DRAW_GEM_AT_XY(x2, y2, gemType2, starred2);
|
|
#ifdef DEBUG_SWAP_ANIM
|
|
cgetc();
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void beginDropAnim(void)
|
|
{
|
|
tSquare square;
|
|
|
|
memset(&gDropGemAnimState, 0, sizeof(gDropGemAnimState));
|
|
for (square = 0; square < NUM_SQUARES; square++) {
|
|
gDropGemAnimState.gemState[square].landed = true;
|
|
}
|
|
}
|
|
|
|
|
|
void dropSquareFromTo(tSquare srcSquare, tSquare tgtSquare, tGemType gemType, bool starred)
|
|
{
|
|
tPos x = SQUARE_TO_X(tgtSquare);
|
|
tPos y = SQUARE_TO_Y(tgtSquare);
|
|
tDropGemInfo *gemInfo = &(gDropGemAnimState.gemState[tgtSquare]);
|
|
|
|
gDropGemAnimState.gotOne = true;
|
|
|
|
gemInfo->x = (x * HORIZONTAL_PIXELS_PER_SQUARE);
|
|
|
|
// The y positions are multiplied by 6 to get a number from 0 to 42
|
|
gemInfo->y = (SQUARE_TO_Y(srcSquare) * VERTICAL_PIXELS_PER_SQUARE);
|
|
gemInfo->endY = (y * VERTICAL_PIXELS_PER_SQUARE);
|
|
|
|
gemInfo->gemType = gemType;
|
|
gemInfo->starred = starred;
|
|
gemInfo->visible = true;
|
|
gemInfo->landed = false;
|
|
|
|
if (y < BOARD_SIZE - 1) {
|
|
gemInfo->lowerNeighbour = &(gDropGemAnimState.gemState[XY_TO_SQUARE(x, y + 1)]);
|
|
}
|
|
}
|
|
|
|
|
|
void dropSquareFromOffscreen(tSquare tgtSquare, tGemType gemType, bool starred)
|
|
{
|
|
tPos x = SQUARE_TO_X(tgtSquare);
|
|
tPos y = SQUARE_TO_Y(tgtSquare);
|
|
tDropGemInfo *gemInfo = &(gDropGemAnimState.gemState[tgtSquare]);
|
|
|
|
gDropGemAnimState.gotOne = true;
|
|
|
|
// The x positions are multiplied by 4 to get a number from 0 to 28
|
|
gemInfo->x = (x * HORIZONTAL_PIXELS_PER_SQUARE);
|
|
|
|
// The y positions are multiplied by 6 to get a number from 0 to 42
|
|
// We skip setting the current y position. For offscreen gems,
|
|
// we determine the starting y position just before we run the
|
|
// animation.
|
|
gemInfo->endY = (y * VERTICAL_PIXELS_PER_SQUARE);
|
|
|
|
gemInfo->gemType = gemType;
|
|
gemInfo->starred = starred;
|
|
gemInfo->visible = false;
|
|
gemInfo->landed = false;
|
|
|
|
if (y < BOARD_SIZE - 1) {
|
|
gemInfo->lowerNeighbour = &(gDropGemAnimState.gemState[XY_TO_SQUARE(x, y + 1)]);
|
|
}
|
|
}
|
|
|
|
|
|
#undef DEBUG_DROP_ANIM
|
|
void endDropAnim(void)
|
|
{
|
|
tSquare square;
|
|
tDropGemInfo *gemInfo;
|
|
tDropGemInfo *neighbourInfo;
|
|
bool done;
|
|
int8_t limit;
|
|
uint8_t numLanded;
|
|
|
|
if (!gDropGemAnimState.gotOne)
|
|
return;
|
|
|
|
#ifdef DEBUG_DROP_ANIM
|
|
cgetc();
|
|
#endif
|
|
|
|
square = NUM_SQUARES;
|
|
while (square > 0) {
|
|
square--;
|
|
|
|
gemInfo = &(gDropGemAnimState.gemState[square]);
|
|
if (gemInfo->landed)
|
|
continue;
|
|
|
|
if (!gemInfo->visible) {
|
|
neighbourInfo = gemInfo->lowerNeighbour;
|
|
if ((neighbourInfo == NULL) ||
|
|
(neighbourInfo->landed) ||
|
|
(neighbourInfo->visible)) {
|
|
// If I am not visible and I have no lower neighbour, then start right at the top
|
|
// of the screen. Also, if I am not visible, I have a lower neighbout and my
|
|
// lower neighbour is landed or visible, I also start right at the top of the screen.
|
|
gemInfo->y = 1 - VERTICAL_PIXELS_PER_SQUARE;
|
|
gemInfo->y -= (rand() % VERTICAL_PIXELS_PER_SQUARE);
|
|
} else {
|
|
// My lower neighbour is also off the screen. I start six lines above my
|
|
// lower neighbour.
|
|
gemInfo->y = neighbourInfo->y - VERTICAL_PIXELS_PER_SQUARE;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (true) {
|
|
done = true;
|
|
numLanded = 0;
|
|
|
|
for (square = 0; square < NUM_SQUARES; square++) {
|
|
gemInfo = &(gDropGemAnimState.gemState[square]);
|
|
|
|
if (gemInfo->landed)
|
|
continue;
|
|
|
|
if (gemInfo->y == gemInfo->endY) {
|
|
gemInfo->landed = true;
|
|
playLandingSound(numLanded);
|
|
numLanded++;
|
|
continue;
|
|
}
|
|
|
|
done = false;
|
|
|
|
gemInfo->y += gemInfo->speed;
|
|
if (gemInfo->y > gemInfo->endY) {
|
|
gemInfo->y = gemInfo->endY;
|
|
}
|
|
|
|
neighbourInfo = gemInfo->lowerNeighbour;
|
|
if ((neighbourInfo != NULL) &&
|
|
(!neighbourInfo->landed) &&
|
|
((limit = gemInfo->lowerNeighbour->y - VERTICAL_PIXELS_PER_SQUARE) <= gemInfo->y)) {
|
|
gemInfo->y = limit;
|
|
gemInfo->speed = 0;
|
|
}
|
|
|
|
gemInfo->speed += DROP_ACCELERATION;
|
|
|
|
if (gemInfo->y > (1 - VERTICAL_PIXELS_PER_SQUARE)) {
|
|
gemInfo->visible = true;
|
|
}
|
|
}
|
|
|
|
if (done)
|
|
break;
|
|
|
|
gVblWait();
|
|
|
|
// Completely unroll the actual drawing to make it faster.
|
|
#define DRAW_SQUARE(square) \
|
|
if (!gDropGemAnimState.gemState[square].landed) { \
|
|
drawBgSquare(square); \
|
|
if (gDropGemAnimState.gemState[square].visible) { \
|
|
DRAW_GEM_AT_XY(gDropGemAnimState.gemState[square].x, \
|
|
gDropGemAnimState.gemState[square].y, \
|
|
gDropGemAnimState.gemState[square].gemType, \
|
|
gDropGemAnimState.gemState[square].starred); \
|
|
} \
|
|
}
|
|
|
|
DRAW_SQUARE(0);
|
|
DRAW_SQUARE(1);
|
|
DRAW_SQUARE(2);
|
|
DRAW_SQUARE(3);
|
|
DRAW_SQUARE(4);
|
|
DRAW_SQUARE(5);
|
|
DRAW_SQUARE(6);
|
|
DRAW_SQUARE(7);
|
|
DRAW_SQUARE(8);
|
|
DRAW_SQUARE(9);
|
|
DRAW_SQUARE(10);
|
|
DRAW_SQUARE(11);
|
|
DRAW_SQUARE(12);
|
|
DRAW_SQUARE(13);
|
|
DRAW_SQUARE(14);
|
|
DRAW_SQUARE(15);
|
|
DRAW_SQUARE(16);
|
|
DRAW_SQUARE(17);
|
|
DRAW_SQUARE(18);
|
|
DRAW_SQUARE(19);
|
|
DRAW_SQUARE(20);
|
|
DRAW_SQUARE(21);
|
|
DRAW_SQUARE(22);
|
|
DRAW_SQUARE(23);
|
|
DRAW_SQUARE(24);
|
|
DRAW_SQUARE(25);
|
|
DRAW_SQUARE(26);
|
|
DRAW_SQUARE(27);
|
|
DRAW_SQUARE(28);
|
|
DRAW_SQUARE(29);
|
|
DRAW_SQUARE(30);
|
|
DRAW_SQUARE(31);
|
|
DRAW_SQUARE(32);
|
|
DRAW_SQUARE(33);
|
|
DRAW_SQUARE(34);
|
|
DRAW_SQUARE(35);
|
|
DRAW_SQUARE(36);
|
|
DRAW_SQUARE(37);
|
|
DRAW_SQUARE(38);
|
|
DRAW_SQUARE(39);
|
|
DRAW_SQUARE(40);
|
|
DRAW_SQUARE(41);
|
|
DRAW_SQUARE(42);
|
|
DRAW_SQUARE(43);
|
|
DRAW_SQUARE(44);
|
|
DRAW_SQUARE(45);
|
|
DRAW_SQUARE(46);
|
|
DRAW_SQUARE(47);
|
|
DRAW_SQUARE(48);
|
|
DRAW_SQUARE(49);
|
|
DRAW_SQUARE(50);
|
|
DRAW_SQUARE(51);
|
|
DRAW_SQUARE(52);
|
|
DRAW_SQUARE(53);
|
|
DRAW_SQUARE(54);
|
|
DRAW_SQUARE(55);
|
|
DRAW_SQUARE(56);
|
|
DRAW_SQUARE(57);
|
|
DRAW_SQUARE(58);
|
|
DRAW_SQUARE(59);
|
|
DRAW_SQUARE(60);
|
|
DRAW_SQUARE(61);
|
|
DRAW_SQUARE(62);
|
|
DRAW_SQUARE(63);
|
|
|
|
#ifdef DEBUG_DROP_ANIM
|
|
cgetc();
|
|
#endif
|
|
}
|
|
}
|