ReC98/th02/main/pointnum/pointnum.cpp

169 lines
4.1 KiB
C++

#pragma option -zPmain_01 -G
#include "libs/master.lib/pc98_gfx.hpp"
#include "th01/rank.h"
#include "th02/v_colors.hpp"
#include "th02/core/globals.hpp"
#include "th02/hardware/pages.hpp"
#include "th02/formats/tile.hpp"
#include "th02/main/entity.hpp"
#include "th02/main/scroll.hpp"
#include "th02/main/pointnum/pointnum.hpp"
#include "th02/main/tile/tile.hpp"
#include "th02/sprites/pointnum.h"
#pragma option -a2
static const int POINTNUM_COUNT = 20;
struct CPointnums {
vc_t col;
int8_t unused;
screen_x_t left[POINTNUM_COUNT];
screen_y_t top[POINTNUM_COUNT][2];
uint16_t points[POINTNUM_COUNT];
entity_flag_t flag[POINTNUM_COUNT];
uint8_t age[POINTNUM_COUNT];
pointnum_cel_t op; // `operator` is a keyword.
pointnum_cel_t operand;
};
extern CPointnums pointnums;
// Function ordering fails
// -----------------------
// Blits the given [numeral] to the given position. Assumptions:
// • ES is already set to the beginning of a VRAM segment
// • The GRCG is active, and set to the intended color
void pascal near pointnum_put(screen_x_t left, vram_y_t top, int numeral);
// -----------------------
void near pointnums_init_for_rank_and_reset(void)
{
for(int i = 0; i < POINTNUM_COUNT; i++) {
pointnums.flag[i] = F_FREE;
}
pointnums.col = V_WHITE;
// Matches the score multiplier at the end of items_update_and_render().
switch(rank) {
case RANK_EASY:
case RANK_NORMAL:
pointnums.op = POINTNUM_EMPTY;
pointnums.operand = POINTNUM_EMPTY;
break;
case RANK_HARD:
pointnums.op = POINTNUM_MUL;
pointnums.operand = POINTNUM_2;
break;
case RANK_LUNATIC:
pointnums.op = POINTNUM_MUL;
pointnums.operand = POINTNUM_4;
break;
case RANK_EXTRA:
pointnums.op = POINTNUM_MUL;
pointnums.operand = POINTNUM_8;
break;
}
}
void pascal near pointnums_add(
screen_x_t left_, screen_y_t top_, uint16_t points_
)
{
register screen_x_t left = left_;
register screen_y_t top = top_;
register uint16_t points = points_;
for(register int i = 0; i < POINTNUM_COUNT; i++) {
if(pointnums.flag[i] == F_FREE) {
pointnums.flag[i] = F_ALIVE;
pointnums.age[i] = 0;
pointnums.points[i] = points;
pointnums.left[i] = left;
pointnums.top[i][0] = top;
pointnums.top[i][1] = top;
break;
}
}
}
void near pointnums_invalidate(void)
{
for(int i = 0; i < POINTNUM_COUNT; i++) {
if(pointnums.flag[i] == F_FREE) {
continue;
}
tiles_invalidate_rect(
pointnums.left[i],
pointnums.top[i][page_back],
// Adding the trailing 0, operator, and operand.
// ZUN bloat: And one unnecessary glyph?
(POINTNUM_W * (POINTNUM_DIGITS + 3 + 1)),
POINTNUM_H
);
if(pointnums.flag[i] == F_REMOVE) {
pointnums.flag[i] = F_FREE;
} else {
pointnums.top[i][page_back] = pointnums.top[i][page_front];
}
}
}
void pascal near pointnum_render(screen_x_t left, vram_y_t top, uint16_t points)
{
extern uint16_t FOUR_DIGIT_POWERS_OF_10[POINTNUM_DIGITS];
int i;
bool past_leading_zeroes = false;
for(i = 0; i < POINTNUM_DIGITS; (i++, left += POINTNUM_W)) {
// ZUN landmine: Since [FOUR_DIGIT_POWERS_OF_10] doesn't cover the
// full value range of an uint16_t, [numeral] should be limited to the
// 0-9 range.
int8_t numeral = (POINTNUM_0 + (points / FOUR_DIGIT_POWERS_OF_10[i]));
points %= FOUR_DIGIT_POWERS_OF_10[i];
if((numeral != POINTNUM_0) || past_leading_zeroes) {
past_leading_zeroes = true;
pointnum_put(left, top, numeral);
}
}
pointnum_put((left + (POINTNUM_W * 0)), top, POINTNUM_0);
pointnum_put((left + (POINTNUM_W * 1)), top, pointnums.op);
pointnum_put((left + (POINTNUM_W * 2)), top, pointnums.operand);
}
void near pointnums_update_and_render(void)
{
grcg_setcolor(GC_RMW, pointnums.col);
_ES = SEG_PLANE_B;
for(int i = 0; i < POINTNUM_COUNT; i++) {
if(pointnums.flag[i] != F_ALIVE) {
continue;
}
// Update
if(pointnums.age[i] > 6) {
pointnums.top[i][page_back] -= 1;
if(pointnums.age[i] > 24) {
pointnums.flag[i] = F_REMOVE;
continue;
}
}
pointnums.age[i]++;
// Render
vram_y_t top = scroll_screen_y_to_vram(
top, pointnums.top[i][page_back]
);
pointnum_render(pointnums.left[i], top, pointnums.points[i]);
}
grcg_off();
}