diff --git a/assets/en.json b/assets/en.json index abfdf07c..b9280570 100644 --- a/assets/en.json +++ b/assets/en.json @@ -21,7 +21,11 @@ "maps": { "testmap": { "bob": "Hello, I am Bob.", - "sign": "This is a sign." + "sign": "This is a sign.", + "sign2": { + "1": "This is another sign.", + "2": "It has two lines." + } } } } \ No newline at end of file diff --git a/assets/testmap.json b/assets/testmap.json index b48379b6..be3ce3f9 100644 --- a/assets/testmap.json +++ b/assets/testmap.json @@ -42,6 +42,15 @@ "y": 8, "text": "maps.testmap.sign" }, + { + "type": 3, + "x": 2, + "y": 2, + "texts": [ + "maps.testmap.sign2.1", + "maps.testmap.sign2.2" + ] + }, { "type": 4, "x": 6, diff --git a/src/dawn/asset/assetmap.c b/src/dawn/asset/assetmap.c index 6ec5a4db..84148c2c 100644 --- a/src/dawn/asset/assetmap.c +++ b/src/dawn/asset/assetmap.c @@ -48,12 +48,32 @@ void assetMapLoadEntity( case ENTITY_TYPE_SIGN: val = assetJsonGetObjectValue(jEnt, "text"); - if(val != NULL) { + if(val != NULL) { assertTrue( val->type == ASSET_JSON_DATA_TYPE_STRING, "assetMapLoad: Sign text is not a string!" ); - signTextSet(&ent->sign, val->string); + signTextAppend(&ent->sign, val->string); + } + + val = assetJsonGetObjectValue(jEnt, "texts"); + if(val != NULL) { + assertTrue( + val->type == ASSET_JSON_DATA_TYPE_ARRAY, + "assetMapLoad: Sign texts is not an array!" + ); + assertTrue( + val->array.length <= SIGN_TEXT_COUNT_MAX, + "assetMapLoad: Too many sign texts!" + ); + for(int32_t i = 0; i < val->array.length; i++) { + assetjson_t *subVal = val->array.value[i]; + assertTrue( + subVal->type == ASSET_JSON_DATA_TYPE_STRING, + "assetMapLoad: Sign text is not a string!" + ); + signTextAppend(&ent->sign, subVal->string); + } } break; diff --git a/src/dawn/game/game.c b/src/dawn/game/game.c index d0d1e109..1fc093dd 100644 --- a/src/dawn/game/game.c +++ b/src/dawn/game/game.c @@ -46,7 +46,6 @@ void gameInit() { gameupdateresult_t gameUpdate(const float_t delta) { timeUpdate(delta); inputUpdate(); - conversationUpdate(); switch(GAME.state) { case GAME_STATE_INITIAL: diff --git a/src/dawn/game/state/overworld.c b/src/dawn/game/state/overworld.c index cec81808..26573112 100644 --- a/src/dawn/game/state/overworld.c +++ b/src/dawn/game/state/overworld.c @@ -10,10 +10,13 @@ #include "input.h" #include "ui/textbox.h" #include "ui/testmenu.h" +#include "rpg/conversation/conversation.h" void gameStateOverworldUpdate() { textboxUpdate(); testMenuUpdate(); + conversationUpdate(); + if(GAME.currentMap) mapUpdate(GAME.currentMap); if(inputWasPressed(INPUT_BIND_PAUSE)) GAME.state = GAME_STATE_PAUSED; } \ No newline at end of file diff --git a/src/dawn/rpg/conversation/conversation.c b/src/dawn/rpg/conversation/conversation.c index bb5f993e..6e746953 100644 --- a/src/dawn/rpg/conversation/conversation.c +++ b/src/dawn/rpg/conversation/conversation.c @@ -30,49 +30,56 @@ void conversationUpdate() { if(CONVERSATION.init == NULL || CONVERSATION.update == NULL) return; uint16_t ret; + bool_t recheck = true; // Init or update. - switch(CONVERSATION.index) { - case CONVERSATION_NOT_INITIALIZED: - CONVERSATION.index = 0; - ret = CONVERSATION.init( - &CONVERSATION, CONVERSATION.index - ); - break; - - default: - ret = CONVERSATION.update(&CONVERSATION, CONVERSATION.index); - break; - } + while(recheck) { + switch(CONVERSATION.index) { + case CONVERSATION_NOT_INITIALIZED: + CONVERSATION.index = 0; + ret = CONVERSATION.init( + &CONVERSATION, CONVERSATION.index + ); + break; + + default: + ret = CONVERSATION.update(&CONVERSATION, CONVERSATION.index); + break; + } - // Check ret value and update conversation. - switch(ret) { - case CONVERSATION_NEXT: - CONVERSATION.index++; - CONVERSATION.index = CONVERSATION.init( - &CONVERSATION, CONVERSATION.index - ); - break; - - case CONVERSATION_CONTINUE: - return; - - case CONVERSATION_RESTART: - CONVERSATION.index = 0; - CONVERSATION.init(&CONVERSATION, CONVERSATION.index); - break; - - case CONVERSATION_INVALID: - assertUnreachable("Invalid converstaion retval"); - break; - case CONVERSATION_DONE: - CONVERSATION.init = NULL; - CONVERSATION.update = NULL; - break; - - default: - CONVERSATION.index = ret; - break; + // Check ret value and update conversation. + switch(ret) { + case CONVERSATION_NEXT: + CONVERSATION.index++; + CONVERSATION.index = CONVERSATION.init( + &CONVERSATION, CONVERSATION.index + ); + break; + + case CONVERSATION_CONTINUE: + recheck = false; + break; + + case CONVERSATION_RESTART: + CONVERSATION.index = 0; + CONVERSATION.init(&CONVERSATION, CONVERSATION.index); + break; + + case CONVERSATION_INVALID: + assertUnreachable("Invalid converstaion retval"); + recheck = false; + break; + + case CONVERSATION_DONE: + CONVERSATION.init = NULL; + CONVERSATION.update = NULL; + recheck = false; + break; + + default: + CONVERSATION.index = ret; + break; + } } } \ No newline at end of file diff --git a/src/dawn/rpg/conversation/conversationinteractentity.c b/src/dawn/rpg/conversation/conversationinteractentity.c index 357b8bf0..ef234d3e 100644 --- a/src/dawn/rpg/conversation/conversationinteractentity.c +++ b/src/dawn/rpg/conversation/conversationinteractentity.c @@ -21,12 +21,12 @@ uint16_t conversationInteractEntityInit( entity_t *e = (entity_t*)convo->data.entityInteract.entity; switch(e->type) { case ENTITY_TYPE_SIGN: - textboxSetText("entities.sign.name", e->sign.text); - return 0; + textboxSetText("entities.sign.name", e->sign.texts[i]); + return i; case ENTITY_TYPE_NPC: - textboxSetText(e->npc.name, e->npc.text); - return 0; + textboxSetText(e->npc.text.name, e->npc.text.text); + return i; default: assertUnreachable("Invalid entity type for conversation."); @@ -38,11 +38,24 @@ uint16_t conversationInteractEntityUpdate( conversation_t *convo, const uint16_t i ) { - switch(i) { - case 0: - return textboxIsOpen() ? CONVERSATION_CONTINUE : CONVERSATION_DONE; - + assertNotNull(convo, "Conversation is NULL!"); + assertNotNull(convo->data.entityInteract.entity, "Conversation user is NULL!"); + + entity_t *e = (entity_t*)convo->data.entityInteract.entity; + switch(e->type) { + case ENTITY_TYPE_SIGN: + if(textboxIsOpen()) return CONVERSATION_CONTINUE; + if(i == SIGN_TEXT_COUNT_MAX-1) return CONVERSATION_DONE; + if(e->sign.texts[i+1][0] == '\0') return CONVERSATION_DONE; + return CONVERSATION_NEXT; + default: - return CONVERSATION_INVALID; + switch(i) { + case 0: + return textboxIsOpen() ? CONVERSATION_CONTINUE : CONVERSATION_DONE; + + default: + return CONVERSATION_INVALID; + } } } \ No newline at end of file diff --git a/src/dawn/rpg/entity/interact.c b/src/dawn/rpg/entity/interact.c index cb964f83..a4f405ae 100644 --- a/src/dawn/rpg/entity/interact.c +++ b/src/dawn/rpg/entity/interact.c @@ -33,6 +33,7 @@ void entityInteractEntity( conversationInteractEntityUpdate, (conversationdata_t){ .entityInteract = { .entity = target } } ); + source->state = ENTITY_STATE_TALKING; return; case ENTITY_TYPE_SIGN: @@ -41,6 +42,7 @@ void entityInteractEntity( conversationInteractEntityUpdate, (conversationdata_t){ .entityInteract = { .entity = target } } ); + source->state = ENTITY_STATE_TALKING; return; case ENTITY_TYPE_DOOR: diff --git a/src/dawn/rpg/entity/npc.c b/src/dawn/rpg/entity/npc.c index 17a4b4d8..3547953f 100644 --- a/src/dawn/rpg/entity/npc.c +++ b/src/dawn/rpg/entity/npc.c @@ -10,18 +10,16 @@ void npcInit(npc_t *npc) { assertNotNull(npc, "npcInit: NPC is NULL!"); - npc->name[0] = '\0'; - npc->text[0] = '\0'; } void npcNameSet(npc_t *npc, const char_t *name) { assertNotNull(npc, "npcNameSet: NPC is NULL!"); assertNotNull(name, "npcNameSet: Name is NULL!"); - strncpy(npc->name, name, LANGUAGE_STRING_KEY_LENGTH_MAX); + strncpy(npc->text.name, name, LANGUAGE_STRING_KEY_LENGTH_MAX); } void npcTextSet(npc_t *npc, const char_t *text) { assertNotNull(npc, "npcTextSet: NPC is NULL!"); assertNotNull(text, "npcTextSet: Text is NULL!"); - strncpy(npc->text, text, LANGUAGE_STRING_KEY_LENGTH_MAX); + strncpy(npc->text.text, text, LANGUAGE_STRING_KEY_LENGTH_MAX); } \ No newline at end of file diff --git a/src/dawn/rpg/entity/npc.h b/src/dawn/rpg/entity/npc.h index a351b6c8..48a8c4c9 100644 --- a/src/dawn/rpg/entity/npc.h +++ b/src/dawn/rpg/entity/npc.h @@ -11,6 +11,15 @@ typedef struct { char_t name[LANGUAGE_STRING_KEY_LENGTH_MAX+1]; char_t text[LANGUAGE_STRING_KEY_LENGTH_MAX+1]; +} npctext_t; + +typedef struct { + uint8_t nothing; +} npcconversation_t; + +typedef union { + npctext_t text; + npcconversation_t conversation; } npc_t; /** diff --git a/src/dawn/rpg/entity/sign.c b/src/dawn/rpg/entity/sign.c index 8940984d..c379ac54 100644 --- a/src/dawn/rpg/entity/sign.c +++ b/src/dawn/rpg/entity/sign.c @@ -7,14 +7,24 @@ #include "sign.h" #include "assert/assert.h" +#include "util/memory.h" void signInit(sign_t *sign) { assertNotNull(sign, "signInit: Sign is NULL!"); - sign->text[0] = '\0'; + memorySet(sign, 0, sizeof(sign_t)); } -void signTextSet(sign_t *sign, const char_t *text) { - assertNotNull(sign, "signTextSet: Sign is NULL!"); - assertNotNull(text, "signTextSet: Text is NULL!"); - strncpy(sign->text, text, LANGUAGE_STRING_KEY_LENGTH_MAX); +void signTextAppend(sign_t *sign, const char_t *text) { + assertNotNull(sign, "signConversationAppend: Sign is NULL!"); + assertNotNull(text, "signConversationAppend: Text is NULL!"); + + // Find the first empty slot + for(int32_t i = 0; i < SIGN_TEXT_COUNT_MAX; i++) { + if(sign->texts[i][0] == '\0') { + strncpy(sign->texts[i], text, LANGUAGE_STRING_KEY_LENGTH_MAX); + return; + } + } + + assertUnreachable("signConversationAppend: No empty slots found!"); } \ No newline at end of file diff --git a/src/dawn/rpg/entity/sign.h b/src/dawn/rpg/entity/sign.h index 42eff634..bfdc2085 100644 --- a/src/dawn/rpg/entity/sign.h +++ b/src/dawn/rpg/entity/sign.h @@ -8,8 +8,10 @@ #pragma once #include "locale/language.h" +#define SIGN_TEXT_COUNT_MAX 32 + typedef struct { - char_t text[LANGUAGE_STRING_KEY_LENGTH_MAX+1]; + char_t texts[SIGN_TEXT_COUNT_MAX][LANGUAGE_STRING_KEY_LENGTH_MAX+1]; } sign_t; /** @@ -20,9 +22,9 @@ typedef struct { void signInit(sign_t *sign); /** - * Sets the text of a sign. + * Appends text to the sign's conversation. * - * @param sign Sign to set text for. - * @param text Text to set. + * @param sign Sign to append conversation to. + * @param text Text to append. */ -void signTextSet(sign_t *sign, const char_t *text); \ No newline at end of file +void signTextAppend(sign_t *sign, const char_t *text); \ No newline at end of file