diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..5b726ee --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,13 @@ +{ + "version": 4, + "configurations": [ + { + "name": "Default", + "includePath": [ + "${default}", + "${workspaceFolder}/gbsdk/inc", + "${workspaceFolder}/include" + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e9ea6b7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "hardware.h": "c", + "util.h": "c" + } +} \ No newline at end of file diff --git a/src/actor.c b/src/actor.c new file mode 100644 index 0000000..8e4649f --- /dev/null +++ b/src/actor.c @@ -0,0 +1,119 @@ +#include "actor.h" + +#include + +#include "sdk/oam.h" +#include "util.h" + +#define MAX_UNIQUE_MOBS 3 +#define MAX_ACTORS 5 +#define NUM_OAM_ENTRIES 40 +#define TILE_WIDTH 8 + +static uint8_t mob_ids[MAX_UNIQUE_MOBS]; +static sprite16_mob mob_anim_data[MAX_UNIQUE_MOBS]; +static sprite16_player player_anim_data; +static actor_t all_actors[MAX_ACTORS]; + +static uint8_t actor_load_mob_anim_data(uint8_t mob_id); + +void actor_reset(void) { + for (uint8_t i = 0; i < ARRSIZE(mob_ids); ++i) { + mob_ids[i] = 0xff; + } + + for (uint8_t i = 0; i < ARRSIZE(all_actors); ++i) { + all_actors[i].active = 0; + } +} + +actor_t *actor_create_mob(uint8_t id) { + uint8_t mob_anim_idx = actor_load_mob_anim_data(id); + if (mob_anim_idx == 0xff) { + return NULL; + } + + actor_t *a = NULL; + for (uint8_t i = 0; i < ARRSIZE(all_actors); ++i) { + if (all_actors[i].active) { + continue; + } + + a = &all_actors[i]; + a->active = 1; + a->mob_anim_idx = mob_anim_idx; + a->anim = ANIM_STAND; + a->frame_idx = 0; + a->frame_counter = 0; + + break; + } + + return a; +} + +void actor_remove(actor_t *a) { a->active = 0; } + +void actor_flush_oam(void) { + struct oam_entry *oam_ptr = &shadow_oam[0]; + actor_t *a = &all_actors[0]; + + for (uint8_t i = 0; i < ARRSIZE(all_actors); ++i) { + if (!a->active) { + continue; + } + + uint8_t *anim_ptr = mob_anim_data[a->mob_anim_idx].anims[a->anim]; + anim_ptr += a->frame_idx * 4; + + oam_ptr->y = a->y; + oam_ptr->x = a->x; + oam_ptr->tile = *anim_ptr++; + oam_ptr->attr = 0; + oam_ptr++; + + oam_ptr->y = a->y; + oam_ptr->x = a->x + TILE_WIDTH; + oam_ptr->tile = *anim_ptr++; + oam_ptr->attr = 0; + oam_ptr++; + + oam_ptr->y = a->y + TILE_WIDTH; + oam_ptr->x = a->x; + oam_ptr->tile = *anim_ptr++; + oam_ptr->attr = 0; + oam_ptr++; + + oam_ptr->y = a->y + TILE_WIDTH; + oam_ptr->x = a->x + TILE_WIDTH; + oam_ptr->tile = *anim_ptr++; + oam_ptr->attr = 0; + oam_ptr++; + + a++; + } +} + +static uint8_t actor_load_mob_anim_data(uint8_t mob_id) { + uint8_t mob_anim_idx = 0xff; + + for (uint8_t i = 0; i < ARRSIZE(mob_ids); ++i) { + if (mob_ids[i] == mob_id) { + mob_anim_idx = i; + break; + } + } + + if (mob_anim_idx == 0xff) { + for (uint8_t i = 0; i < ARRSIZE(mob_ids); ++i) { + if (mob_ids[i] == 0xff) { + mob_ids[i] = mob_id; + // TODO: Load data into mob_anim_data[i] + mob_anim_idx = i; + break; + } + } + } + + return mob_anim_idx; +} \ No newline at end of file diff --git a/src/actor.h b/src/actor.h new file mode 100644 index 0000000..ea3c96e --- /dev/null +++ b/src/actor.h @@ -0,0 +1,60 @@ +#ifndef IS_ACTOR_H_ +#define IS_ACTOR_H_ + +#include + +typedef enum { + ANIM_STAND, + ANIM_WALK, + ANIM_JUMP, + ANIM_ATTACK, + ANIM_HURT, + ANIM_CLIMB, +} actor_anim_state_t; + +typedef struct { + uint8_t active; + uint8_t mob_anim_idx; + actor_anim_state_t anim; + uint8_t frame_idx; + uint8_t frame_counter; + uint8_t x; + uint8_t y; +} actor_t; + +typedef struct { + uint8_t *tiles; + uint16_t num_tiles; + uint8_t *anims[ANIM_HURT + 1]; +} sprite16_mob; + +typedef struct { + uint8_t *tiles; + uint16_t num_tiles; + uint8_t *anims[ANIM_CLIMB + 1]; +} sprite16_player; + +/** + * @brief Clear all actor data. + */ +void actor_reset(void); + +/** + * @brief Register an actor for a mob. + * @param id The ID of the mob to create + * @return A pointer to the new actor or NULL if one could not be created + */ +actor_t *actor_create_mob(uint8_t id); + +/** + * @brief Remove an actor from the screen. + * @param a The actor to remove + */ +void actor_remove(actor_t *a); + +/** + * @brief Flush actor sprite data to OAM. + */ +void actor_flush_oam(void); + +#endif diff --git a/src/main.c b/src/main.c index 492a958..05ab2c3 100644 --- a/src/main.c +++ b/src/main.c @@ -1,29 +1,31 @@ -#include "sdk/hardware.h" -#include "sdk/video.h" -#include "sdk/oam.h" +#include + +#include "actor.h" #include "sdk/assets.h" +#include "sdk/hardware.h" #include "sdk/joypad.h" -#include +#include "sdk/oam.h" +#include "sdk/video.h" ASSET(tiles, "tiles.2bpp"); uint8_t cursor_x = 0; uint8_t cursor_y = 0; - void main() { - //Load our initial vram (this is slow, but always works, even outside of vblank) + // Load our initial vram (this is slow, but always works, even outside of + // vblank) vram_memcpy(0x8000, tiles, tiles_end - tiles); vram_memcpy(0x9000, tiles, tiles_end - tiles); - //Setup the OAM for sprite drawing + // Setup the OAM for sprite drawing oam_init(); - //Set some default DMG palette data + // Set some default DMG palette data rBGP = 0b11100100; rOBP0 = 0b11100100; rOBP1 = 0b11100100; - //Put some sprites on the screen + // Put some sprites on the screen shadow_oam[0].y = 0x20; shadow_oam[0].x = 0x20; shadow_oam[0].tile = 0x04; @@ -33,31 +35,32 @@ void main() { shadow_oam[1].tile = 0x02; shadow_oam[1].attr = 0x00; - //Make sure sprites and the background are drawn + // Make sure sprites and the background are drawn rLCDC = LCDC_ON | LCDC_OBJON | LCDC_BGON; - //Setup the VBLANK interrupt, but we don't actually enable interrupt handling. + // Setup the VBLANK interrupt, but we don't actually enable interrupt + // handling. // We only do this, so HALT waits for VBLANK. rIF = 0; rIE = IE_VBLANK; vram_memset(0x9800, 0x05, 32 * 18); - for(uint8_t x=0; x<20; x++) { + for (uint8_t x = 0; x < 20; x++) { vram_set(0x9800 + x, 0x07); vram_set(0x9A20 + x, 0x07); } - for(uint8_t y=1; y<18; y++) { + for (uint8_t y = 1; y < 18; y++) { vram_set(0x9800 + y * 0x20, 0x07); vram_set(0x9800 + 19 + y * 0x20, 0x07); } vram_set(0x9800 + 3 + 3 * 0x20, 0x01); - while(1) { + while (1) { joypad_update(); - if (joypad_state & PAD_LEFT) cursor_x --; - if (joypad_state & PAD_RIGHT) cursor_x ++; - if (joypad_state & PAD_UP) cursor_y --; - if (joypad_state & PAD_DOWN) cursor_y ++; + if (joypad_state & PAD_LEFT) cursor_x--; + if (joypad_state & PAD_RIGHT) cursor_x++; + if (joypad_state & PAD_UP) cursor_y--; + if (joypad_state & PAD_DOWN) cursor_y++; if (joypad_state & (PAD_A | PAD_B)) { vram_set(0x9800 + cursor_x / 8 + cursor_y / 8 * 0x20, 0x03); } @@ -66,11 +69,12 @@ void main() { shadow_oam[0].y = cursor_y + 16; shadow_oam[0].x = cursor_x + 8; - //Wait for VBLANK + // Wait for VBLANK HALT(); - rIF = 0; //As global interrupts are not enabled, we need to clear the interrupt flag. + rIF = 0; // As global interrupts are not enabled, we need to clear the + // interrupt flag. - //Copy the sprites into OAM memory + // Copy the sprites into OAM memory oam_dma_copy(); } } diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..68b4b19 --- /dev/null +++ b/src/util.h @@ -0,0 +1,6 @@ +#ifndef IS_UTIL_H_ +#define IS_UTIL_H_ + +#define ARRSIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#endif