/* * obj.c * Duality * * Copyright (C) 2025 Thomas Buck * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * See . */ #include #include #include #include "gb/hardware.h" #include "sprites.h" #include "game.h" #include "obj.h" /* * sprite budget: * * fixed: * ship: 4 * thruster: 1 * health: 4 * power: 4 * --> 13 fixed * * hardware tiles: 40 - 13 = 27 * * dynamic: * shot: 1 * light: 4 * dark: 4 * --> 2x dark & 2x light = 16 * --> 5x shot & 6x small = 11 * --> 16 + 11 = 27 */ #define MAX_DARK 2 #define MAX_LIGHT 2 #define MAX_SHOT 5 #define MAX_SHOT_DARK 3 #define MAX_SHOT_LIGHT 3 #define MAX_OBJ ((4 * MAX_DARK) + (4 * MAX_LIGHT) + MAX_SHOT + MAX_SHOT_DARK + MAX_SHOT_LIGHT) #define MAX_TRAVEL 32 struct obj { uint8_t active; enum SPRITES sprite; int16_t off_x; int16_t off_y; int16_t spd_x; int16_t spd_y; uint8_t travel; }; static struct obj objs[MAX_OBJ]; void obj_init(void) { memset(objs, 0, sizeof(objs)); } static uint8_t cnt_sprite(enum SPRITES sprite) { uint8_t cnt = 0; for (uint8_t i = 0; i < MAX_OBJ; i++) { if (!objs[i].active) { continue; } if (objs[i].sprite == sprite) { cnt++; } } return cnt; } enum OBJ_STATE obj_add(enum SPRITES sprite, int16_t off_x, int16_t off_y, int16_t spd_x, int16_t spd_y) { uint8_t obj_cnt = 0xFF; for (uint8_t i = 0; i < MAX_OBJ; i++) { if (!objs[i].active) { obj_cnt = i; break; } } if (obj_cnt >= MAX_OBJ) { return OBJ_LIST_FULL; } if (((sprite == SPR_DARK) && (cnt_sprite(sprite) >= MAX_DARK)) || ((sprite == SPR_LIGHT) && (cnt_sprite(sprite) >= MAX_LIGHT)) || ((sprite == SPR_SHOT) && (cnt_sprite(sprite) >= MAX_SHOT)) || ((sprite == SPR_SHOT_DARK) && (cnt_sprite(sprite) >= MAX_SHOT_DARK)) || ((sprite == SPR_SHOT_LIGHT) && (cnt_sprite(sprite) >= MAX_SHOT_LIGHT))) { return OBJ_TYPE_FULL; } objs[obj_cnt].active = 1; objs[obj_cnt].sprite = sprite; objs[obj_cnt].off_x = off_x << POS_SCALE_OBJS; objs[obj_cnt].off_y = off_y << POS_SCALE_OBJS; objs[obj_cnt].spd_x = spd_x; objs[obj_cnt].spd_y = spd_y; objs[obj_cnt].travel = 0; obj_cnt += 1; return OBJ_ADDED; } void obj_act(int16_t pos_x, int16_t pos_y, int16_t *spd_off_x, int16_t *spd_off_y) { pos_x += DEVICE_SPRITE_PX_OFFSET_X + (DEVICE_SCREEN_PX_WIDTH / 2) - 16; pos_y += DEVICE_SPRITE_PX_OFFSET_Y + (DEVICE_SCREEN_PX_HEIGHT / 2); for (uint8_t i = 0; i < MAX_OBJ; i++) { if (!objs[i].active) { continue; } switch (objs[i].sprite) { case SPR_DARK: { *spd_off_x += (objs[i].off_x - pos_x) >> 8; *spd_off_y += (objs[i].off_y - pos_y) >> 8; } break; default: break; } } } void obj_draw(int16_t spd_x, int16_t spd_y, uint8_t *hiwater) { for (uint8_t i = 0; i < MAX_OBJ; i++) { if (!objs[i].active) { continue; } // move objects by their speed and compensate for movement of the background / ship objs[i].off_x += objs[i].spd_x - spd_x; objs[i].off_y += objs[i].spd_y - spd_y; // only update travel time if we're actually moving if ((objs[i].spd_x != 0) || (objs[i].spd_y != 0)) { objs[i].travel += 1; } // remove objects that have traveled for too long if (objs[i].travel >= MAX_TRAVEL) { objs[i].active = 0; } spr_draw(objs[i].sprite, FLIP_NONE, objs[i].off_x >> POS_SCALE_OBJS, objs[i].off_y >> POS_SCALE_OBJS, hiwater); } }