From f5f5e2c20bbbca04899916d1a6e0c71136aba2df Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Wed, 28 Feb 2024 19:42:03 -0800 Subject: [PATCH] Get NPCs showing up in-engine --- game/BUILD | 1 + game/game.c | 2 + game/game.h | 1 + game/game_npc.c | 92 +++++++++++++++++++++++++++++++++++- game/game_npc.h | 27 +++++++++++ game/map/game_map.ldtk | 45 +++++++++++++++++- game/map/game_map_entities.c | 43 ++++++++++++----- pytools/mapdata.py | 7 ++- 8 files changed, 202 insertions(+), 16 deletions(-) diff --git a/game/BUILD b/game/BUILD index fc2abac..2086469 100644 --- a/game/BUILD +++ b/game/BUILD @@ -5,6 +5,7 @@ cc_library( name = "game", srcs = glob(["*.c"]) + [ "art/game_collectibles.c", + "art/game_npcs.c", "art/game_player.c", "art/game_tiles.c", "map/game_map.c", diff --git a/game/game.c b/game/game.c index 7ffe92f..882a4ec 100644 --- a/game/game.c +++ b/game/game.c @@ -30,6 +30,7 @@ void game_update() { game_frame += 1; game_collectibles_update(); + game_npcs_update(); game_player_update(); } @@ -41,6 +42,7 @@ void game_draw() { sys_map_draw(map_game_map, spr_game_tiles, 0, 0, 0, 0, map_game_map.width, map_game_map.height); game_collectibles_draw(); + game_npcs_draw(); game_player_draw(); sys_camera_reset(); diff --git a/game/game.h b/game/game.h index 780a906..74b43aa 100644 --- a/game/game.h +++ b/game/game.h @@ -4,6 +4,7 @@ #include "game_collectible.h" #include "game_collision.h" #include "game_math.h" +#include "game_npc.h" #include "game_palette.h" #include "game_player.h" diff --git a/game/game_npc.c b/game/game_npc.c index 5c380b1..14ae585 100644 --- a/game/game_npc.c +++ b/game/game_npc.c @@ -1 +1,91 @@ -#include "game_npc.h" \ No newline at end of file +#include +#include "art/game_npcs.h" +#include "sys/sys.h" +#include "game.h" + +// If this isn't enough, raise the number +#define GAME_NPCS_N 32 + +typedef struct { + uint8_t main; + // alt0 and alt1 are ramps + uint8_t alt0_0; uint8_t alt0_1; + uint8_t alt1_0; uint8_t alt1_1; + uint8_t eye_0; uint8_t eye_1; + uint8_t pupil; + uint8_t adornment; + uint8_t shine; +} game_npc_palette; + +game_npc_palette game_npc_palettes[1] = { + {14, 12, 13, 6, 7, 6, 7, 0, 8, 7} +}; + +#define GAME_NPC_N_PALETTES (sizeof(game_npc_palettes)/sizeof(game_npc_palettes[0])) + +sys_i32 game_npc_next = 0; +game_npc game_npcs[GAME_NPCS_N]; + +game_npc* game_npc_create(sys_i32 x, sys_i32 y) { + assert(game_npc_next < GAME_NPCS_N && "too many NPCs"); + + sys_i32 id = game_npc_next++; + game_npc npc = { + .x = x, + .y = y, + + .sprite_id = 0, + .palette_id = 0, + .inflict_id = 0, + .face_left = false, + + .n_dialogues = 0, + .no_cake_dialogue = NULL, + /* .dialogues = { doesn't matter }, */ + + .present = true, + .n_dialogues_seen = 0, + }; + game_npcs[id] = npc; + return &game_npcs[id]; +} + +void game_npcs_update() { + // TODO +} + +void game_npcs_draw() { + for (sys_i32 i = 0; i < game_npc_next; i++) { + game_npc* n = &game_npcs[i]; + + if (!n->present) { continue; } + + game_npc_palette palette = + game_npc_palettes[game_npcs[i].palette_id % GAME_NPC_N_PALETTES]; + + sys_dpal_set(14, palette.main); + sys_dpal_set(12, palette.alt0_0); + sys_dpal_set(13, palette.alt0_1); + sys_dpal_set(6, palette.alt1_0); + sys_dpal_set(7, palette.alt1_1); + sys_dpal_set(9, palette.eye_0); + sys_dpal_set(10, palette.eye_1); + sys_dpal_set(1, palette.pupil); + sys_dpal_set(8, palette.adornment); + sys_dpal_set(11, palette.shine); + + // TODO: Palette + sys_sprite_draw_ext( + spr_game_npcs, + n->sprite_id, + n->x / PIXEL_SZ_MICROPIXEL - 8, + n->y / PIXEL_SZ_MICROPIXEL - 8, + 2, + 2, + n->face_left, + false + ); + + sys_dpal_reset(); + } +} \ No newline at end of file diff --git a/game/game_npc.h b/game/game_npc.h index 1864e73..122f780 100644 --- a/game/game_npc.h +++ b/game/game_npc.h @@ -1,4 +1,31 @@ #ifndef GAME_NPC_H #define GAME_NPC_H +#include "sys/sys.h" + +#define GAME_NPC_MAX_N_DIALOGUES 16 + +typedef struct { + sys_i32 x; + sys_i32 y; + + uint8_t sprite_id; + uint8_t palette_id; + uint8_t inflict_id; + bool face_left; + + int n_dialogues; + const char* no_cake_dialogue; + const char* dialogues[GAME_NPC_MAX_N_DIALOGUES]; + + bool present; + int n_dialogues_seen; +} game_npc; + +// pointer goes to data section and will be valid forever +game_npc* game_npc_create(sys_i32 x, sys_i32 y); + +void game_npcs_update(); +void game_npcs_draw(); + #endif // GAME_NPC_H \ No newline at end of file diff --git a/game/map/game_map.ldtk b/game/map/game_map.ldtk index 0113e00..cf4a389 100644 --- a/game/map/game_map.ldtk +++ b/game/map/game_map.ldtk @@ -11,7 +11,7 @@ "iid": "7db5fd20-b0a0-11ee-9688-af2c6adbc1d6", "jsonVersion": "1.5.3", "appBuildId": 473703, - "nextUid": 130, + "nextUid": 131, "identifierStyle": "Lowercase", "toc": [], "worldLayout": "Free", @@ -3696,6 +3696,43 @@ "allowedRefsEntityUid": null, "allowedRefTags": [], "tilesetUid": null + }, + { + "identifier": "face_left", + "doc": null, + "__type": "Bool", + "uid": 130, + "type": "F_Bool", + "isArray": false, + "canBeNull": false, + "arrayMinLength": null, + "arrayMaxLength": null, + "editorDisplayMode": "Hidden", + "editorDisplayScale": 1, + "editorDisplayPos": "Above", + "editorLinkStyle": "StraightArrow", + "editorDisplayColor": null, + "editorAlwaysShow": false, + "editorShowInWorld": true, + "editorCutLongValues": true, + "editorTextSuffix": null, + "editorTextPrefix": null, + "useForSmartColor": false, + "exportToToc": false, + "searchable": false, + "min": null, + "max": null, + "regex": null, + "acceptFileTypes": null, + "defaultOverride": null, + "textLanguageMode": null, + "symmetricalRef": false, + "autoChainRef": true, + "allowOutOfLevelRef": true, + "allowedRefs": "OnlySame", + "allowedRefsEntityUid": null, + "allowedRefTags": [], + "tilesetUid": null } ] } @@ -4216,7 +4253,11 @@ "id": "V_String", "params": ["But you should go to the next screen."] } ] }, - { "__identifier": "no_cake_dialogue", "__type": "String", "__value": null, "__tile": null, "defUid": 129, "realEditorValues": [] } + { "__identifier": "no_cake_dialogue", "__type": "String", "__value": null, "__tile": null, "defUid": 129, "realEditorValues": [] }, + { "__identifier": "face_left", "__type": "Bool", "__value": true, "__tile": null, "defUid": 130, "realEditorValues": [{ + "id": "V_Bool", + "params": [ true ] + }] } ], "__worldX": 464, "__worldY": 528 diff --git a/game/map/game_map_entities.c b/game/map/game_map_entities.c index 5fca3e3..afdc11c 100644 --- a/game/map/game_map_entities.c +++ b/game/map/game_map_entities.c @@ -1,3 +1,4 @@ +#include #include "sys/sys.h" #include "game/game.h" #include "game_map.h" @@ -36,26 +37,44 @@ void map_game_map_collectible_money_small_create(sys_i32 x, sys_i32 y) { ); } +game_npc* game_map_npc_in_progress = NULL; + void map_game_map_npc_create(sys_i32 x, sys_i32 y) { - // TODO + // center NPC + game_map_npc_in_progress = game_npc_create( + (x + 1) * TILE_SZ_MICROPIXEL, + (y + 1) * TILE_SZ_MICROPIXEL + ); } void map_game_map_npc_set_sprite_id(sys_i32 id) { - // TODO -} - -void map_game_map_npc_set_inflict_id(sys_i32 id) { - // TODO -} - -void map_game_map_npc_set_no_cake_dialogue(const char* dialogue) { - // TODO + assert(game_map_npc_in_progress && "NPC has to be in progress"); + game_map_npc_in_progress->sprite_id = id; } void map_game_map_npc_set_palette_id(sys_i32 id) { - // TODO + assert(game_map_npc_in_progress && "NPC has to be in progress"); + game_map_npc_in_progress->palette_id = id; +} + +void map_game_map_npc_set_inflict_id(sys_i32 id) { + assert(game_map_npc_in_progress && "NPC has to be in progress"); + game_map_npc_in_progress->inflict_id = id; +} + +void map_game_map_npc_set_face_left(bool face_left) { + game_map_npc_in_progress->face_left = face_left; +} + +void map_game_map_npc_set_no_cake_dialogue(const char* dialogue) { + assert(game_map_npc_in_progress && "NPC has to be in progress"); + game_map_npc_in_progress->no_cake_dialogue = dialogue; } void map_game_map_npc_add_dialogue(const char* dialogue) { - // TODO + assert(game_map_npc_in_progress && "NPC has to be in progress"); + assert(game_map_npc_in_progress->n_dialogues < GAME_NPC_MAX_N_DIALOGUES && + "NPC can't have too many dialogues"); + sys_i32 ix = game_map_npc_in_progress->n_dialogues++; + game_map_npc_in_progress->dialogues[ix] = dialogue; } \ No newline at end of file diff --git a/pytools/mapdata.py b/pytools/mapdata.py index d04b9c3..4de6e90 100644 --- a/pytools/mapdata.py +++ b/pytools/mapdata.py @@ -4,6 +4,7 @@ import shared TEMPLATE = """ // generated code! be nice +#include #include "sys/sys.h" sys_maptile map_{{map_name}}_data[{{width * height}}] = { {{ tiles|join(",") }} }; @@ -128,7 +129,9 @@ def load_mapdata(fname_ldtk): def format_field_value(ty, val): - if ty == "Int": + if ty == "Bool": + return "true" if val else "false", "scalar", "bool" + elif ty == "Int": return str(val), "scalar", "sys_i32" elif ty == "String": if val is None: @@ -138,6 +141,8 @@ def format_field_value(ty, val): return [format_field_value("Int", i)[0] for i in val], "array", "sys_i32" elif ty == "Array": return [format_field_value("String", i)[0] for i in val], "array", "const char*" + else: + assert False, f"unknown type: {ty}" def annot_xy(lst, w, h):