diff --git a/.gitignore b/.gitignore index 450224e..7ea0ff6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,4 @@ __pycache__/ *.sublime-workspace .vscode -*.json +level*.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 65291bf..be43288 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ set(SOURCES # Shared assets, fx-9860G-only assets and fx-CG-50-only assets set(ASSETS assets/WorldRPG.world + assets/DialogsRPG.json # ... ) diff --git a/assets/DialogsRPG.json b/assets/DialogsRPG.json new file mode 100644 index 0000000..d0ca35a --- /dev/null +++ b/assets/DialogsRPG.json @@ -0,0 +1,134 @@ +{ "dialogs":[ + { "ID":0, + "dialog":"Bienvenue a Toi, l'Ami, dans cette magnifique ville de Nabrouch. Comme tu peux le constater, les habitants ne sont pas tres nombreux et ont tendance a se terrer dans leur maison depuis la revolution. Il faut dire que les Anciens dirigeants, bien qu'exiles, exercent encore une certaine forme de pouvoir ...", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":1 + }, + { + "ID":1, + "dialog":"Depuis la mort de ton Pere, la region a bien change ... Enfin, surtout ses habitants. Tu devras te mefier de tout le monde.", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":2 + }, + { + "ID":2, + "dialog":"Tu devrais commencer par rejoindre la taverne que ton pere t'a laisse en heritage.", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":-1 + }, + { + "ID":3, + "dialog":"Taverne de Pue Le Bouc", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":-1 + }, + { + "ID":4, + "dialog":"Tiens !! Quelqu'un est mort ici. Tu le connais ?", + "isQuestion":1, + "choice":"Oui$Non", + "conclusion1":"C'est bien triste mon Ami.`$life-2``$power-5`", + "next1":-1, + "conclusion2":"Dommage quand meme pour Lui.", + "next2":-1, + "nextOther":-1 + }, + { + "ID":5, + "dialog":"Salut Hero !! Quel bon vent t'ammene ici a Nabrouch ?", + "isQuestion":1, + "choice":"De Passage$La Famille", + "conclusion1":"Entre donc te reposer dans la taverne, c'est la seule du coin.", + "next1":-1, + "conclusion2":"Fais bien attention alors.", + "next2":6, + "nextOther":-1 + }, + { + "ID":6, + "dialog":"On dit qu'il se passe des choses étranges par ici depuis quelques temps. Fais bien attention a Toi.", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":-1 + }, + { + "ID":7, + "dialog":"Salut Hero, je suis le cremier. Veux tu me delester un peu ?", + "isQuestion":1, + "choice":"Oui$Non", + "conclusion1":"Voici donc pour toi`$life+5``$mana+5``$power+2`", + "next1":-1, + "conclusion2":"Bon bah casse toi ...", + "next2":-1, + "nextOther":-1 + }, + { + "ID":8, + "dialog":"Le Sanctuaire Maudit ...", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":-1 + }, + { + "ID":9, + "dialog":"Etrange ce coffre abandonne et ouvert ... Fouiller dedans ?", + "isQuestion":1, + "choice":"Oui$Non", + "conclusion1":"Trop cool, du stuff `$life+5``$mana+5``$power+2`", + "next1":-1, + "conclusion2":"Je ferai mieux de partir loin ...", + "next2":-1, + "nextOther":-1 + }, + { + "ID":10, + "dialog":"Et sa tombe est en train d'etre creusee ?", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":11 + }, + { + "ID":11, + "dialog":"Beaucoup de morts pour une si petite bourgade ...", + "isQuestion":0, + "choice":"_", + "conclusion1":"_", + "next1":-1, + "conclusion2":"_", + "next2":-1, + "nextOther":-1 + } + ] +} \ No newline at end of file diff --git a/assets/WorldRPG.world b/assets/WorldRPG.world index 7cbaa1d..fc910f1 100644 --- a/assets/WorldRPG.world +++ b/assets/WorldRPG.world @@ -6,27 +6,6 @@ "width": 384, "x": 0, "y": 0 - }, - { - "fileName": "level1.tmx", - "height": 192, - "width": 384, - "x": 384, - "y": 0 - }, - { - "fileName": "level2.tmx", - "height": 192, - "width": 384, - "x": 0, - "y": 192 - }, - { - "fileName": "level3.tmx", - "height": 192, - "width": 384, - "x": 384, - "y": 192 } ], "onlyShowAdjacentMaps": false, diff --git a/assets/WorldRPG.world.bak b/assets/WorldRPG.world.bak new file mode 100644 index 0000000..7cbaa1d --- /dev/null +++ b/assets/WorldRPG.world.bak @@ -0,0 +1,34 @@ +{ + "maps": [ + { + "fileName": "level0.tmx", + "height": 192, + "width": 384, + "x": 0, + "y": 0 + }, + { + "fileName": "level1.tmx", + "height": 192, + "width": 384, + "x": 384, + "y": 0 + }, + { + "fileName": "level2.tmx", + "height": 192, + "width": 384, + "x": 0, + "y": 192 + }, + { + "fileName": "level3.tmx", + "height": 192, + "width": 384, + "x": 384, + "y": 192 + } + ], + "onlyShowAdjacentMaps": false, + "type": "world" +} diff --git a/assets/converters.py b/assets/converters.py index 337fddc..d63ee8f 100644 --- a/assets/converters.py +++ b/assets/converters.py @@ -19,6 +19,9 @@ def convert(input, output, params, target): elif params["custom-type"] == "font": convert_font(input, output, params, target) return 0 + elif params["custom-type"] == "dialogs": + convert_dialogs(input, output, params, target) + return 0 else: return 1 @@ -68,20 +71,8 @@ def convert_world(input, output, params, target): print( "Map = ", map ) structWorld += fxconv.ptr( map ) - #ext = get_extra_map_data( mapPath, output, params, target, xmin, ymin, xmax, ymax ) - #print( "Data = ", ext ) - #if (ext!=fxconv.u32(0)): structExtra += fxconv.ptr( ext ) - structWorld += fxconv.u32(0) - #structExtra += fxconv.u32(0) - """ - #and all the extra data (PNJ, SGN, ...) - fxconv.elf_multi( - [("_" + params["varMapData"], structWorld), - ("_" + params["varExtraData"], structExtra)], - output, **target) - """ #generate ! fxconv.elf(structWorld, output, "_" + params["name"], **target) @@ -251,11 +242,8 @@ def get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax): nme = i["name"] - dialog = None - quest = 0 - choi = None - conc1 = None - conc2 = None + dialogID = None + needAction = None path = 0 path_length = 0 xdata = None @@ -264,17 +252,10 @@ def get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax): #we now fill all the properties of this item for j in i["properties"]: #property "dialog" - if j["name"]=="dialog": dialog = j[ "value" ] + if j["name"]=="dialogID": dialogID = j[ "value" ] #property "isQuestion" - elif j["name"]=="isQuestion": quest = j[ "value" ] - #property "choices" - elif j["name"]=="choices": - choi = j[ "value" ] - choi = choi.replace( '$', chr(0) ) - #property "conclusion1" - elif j["name"]=="conclusion1": conc1 = j[ "value" ] - #property "conclusion2" - elif j["name"]=="conclusion2": conc2 = j[ "value" ] + elif j["name"]=="needAction": needAction = j[ "value" ] + else: #Extra properties for NPCs (path) if tpe=="NPC": @@ -316,15 +297,14 @@ def get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax): print( " Q?= ", quest, " Choi= ", choi ) print( " c1= ", conc1, " c2=", conc2) + structData += fxconv.u32( int(x) ) structData += fxconv.u32( int(y) ) structData += fxconv.string( nme ) structData += fxconv.string( tpe ) - structData += fxconv.string( dialog ) - structData += fxconv.u32( int(quest) ) - structData += fxconv.string( choi ) - structData += fxconv.string( conc1 ) - structData += fxconv.string( conc2 ) + structData += fxconv.u32( int(dialogID)) + structData += fxconv.u32( int(needAction) ) + if path==0: structData += fxconv.u32(0) structData += fxconv.u32(0) @@ -347,6 +327,7 @@ def get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax): return nbExtraData, structData + def convert_custom_image(input, output, params, target): scale = int(params.get("scale", 1)) @@ -358,6 +339,31 @@ def convert_custom_image(input, output, params, target): o = fxconv.convert_image_cg(im, params) fxconv.elf(o, output, "_" + params["name"], **target) + + def convert_font(input, output, params, target): o = fxconv.convert_topti(input, params) fxconv.elf(o, output, "_" + params["name"], **target) + + + +def convert_dialogs(input, output, params, target): + + print( "WE ARE COMPUTING THE DIALOGS FROM : ", input ) + data = json.load(open(input, "r")) + + structDialogs = fxconv.Structure() + + for d in data["dialogs"]: + structDialogs += fxconv.u32( int(d[ "ID" ] ) ) + structDialogs += fxconv.string( int(d[ "dialog" ] ) ) + structDialogs += fxconv.u32( int(d[ "isQuestion" ] ) ) + structDialogs += fxconv.string( int(d[ "choice" ] ) ) + structDialogs += fxconv.string( int(d[ "conclusion1" ] ) ) + structDialogs += fxconv.u32( int(d[ "next1" ] ) ) + structDialogs += fxconv.string( int(d[ "conclusion2" ] ) ) + structDialogs += fxconv.u32( int(d[ "next2" ] ) ) + structDialogs += fxconv.u32( int(d[ "nextOther" ] ) ) + + + fxconv.elf(structDialogs, output, "_" + params["name"], **target) \ No newline at end of file diff --git a/assets/dialogs.json.bak b/assets/dialogs.json.bak new file mode 100644 index 0000000..987338b --- /dev/null +++ b/assets/dialogs.json.bak @@ -0,0 +1,134 @@ +[ + { + "ID": 0, + "dialog": "Bienvenue a Toi, l'Ami, dans cette magnifique ville de Nabrouch. Comme tu peux le constater, les habitants ne sont pas tres nombreux et ont tendance a se terrer dans leur maison depuis la revolution. Il faut dire que les Anciens dirigeants, bien qu'exiles, exercent encore une certaine forme de pouvoir ...", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": 1 + }, + { + "ID": 1, + "dialog": "Depuis la mort de ton Pere, la region a bien change ... Enfin, surtout ses habitants. Tu devras te mefier de tout le monde.", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": 2 + }, + { + "ID": 2, + "dialog": "Tu devrais commencer par rejoindre la taverne que ton pere t'a laisse en heritage.", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 3, + "dialog": "Taverne de Pue Le Bouc", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 4, + "dialog": "Tiens !! Quelqu'un est mort ici. Tu le connais ?", + "isQuestion": 1, + "choice": "Oui$Non", + "conclusion1": "C'est bien triste mon Ami.`$life-2``$power-5`", + "next1": -1, + "conclusion2": "Dommage quand meme pour Lui.", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 5, + "dialog": "Salut Hero !! Quel bon vent t'ammene ici a Nabrouch ?", + "isQuestion": 1, + "choice": "De Passage$La Famille", + "conclusion1": "Entre donc te reposer dans la taverne, c'est la seule du coin.", + "next1": -1, + "conclusion2": "Fais bien attention alors.", + "next2": 6, + "nextOther": -1 + }, + { + "ID": 6, + "dialog": "On dit qu'il se passe des choses étranges par ici depuis quelques temps. Fais bien attention a Toi.", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 7, + "dialog": "Salut Hero, je suis le cremier. Veux tu me delester un peu ?", + "isQuestion": 1, + "choice": "Oui$Non", + "conclusion1": "Voici donc pour toi`$life+5``$mana+5``$power+2`", + "next1": -1, + "conclusion2": "Bon bah casse toi ...", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 8, + "dialog": "Le Sanctuaire Maudit ...", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 9, + "dialog": "Etrange ce coffre abandonne et ouvert ... Fouiller dedans ?", + "isQuestion": 1, + "choice": "Oui$Non", + "conclusion1": "Trop cool, du stuff `$life+5``$mana+5``$power+2`", + "next1": -1, + "conclusion2": "Je ferai mieux de partir loin ...", + "next2": -1, + "nextOther": -1 + }, + { + "ID": 10, + "dialog": "Et sa tombe est en train d'etre creusee ?", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": 11 + }, + { + "ID": 11, + "dialog": "Beaucoup de morts pour une si petite bourgade ...", + "isQuestion": 0, + "choice": "_", + "conclusion1": "_", + "next1": -1, + "conclusion2": "_", + "next2": -1, + "nextOther": -1 + } +] \ No newline at end of file diff --git a/assets/fxconv-metadata.txt b/assets/fxconv-metadata.txt index 3122176..9150b69 100644 --- a/assets/fxconv-metadata.txt +++ b/assets/fxconv-metadata.txt @@ -1,3 +1,7 @@ WorldRPG.world: custom-type: world name: worldRPG + +DialogsRPG.json + custom-type: dialogs + name: dialogRPG diff --git a/assets/level0.tmx b/assets/level0.tmx index 461f9e6..a3b4853 100644 --- a/assets/level0.tmx +++ b/assets/level0.tmx @@ -1,5 +1,5 @@ - + @@ -92,80 +92,66 @@ - - - - - + + - - - - - + + - - - - - + + - - - - + - + - - - - + - + - - - - - + + - - - - - + + + + + + + + + diff --git a/assets/tilesetnpp.json b/assets/tilesetnpp.json new file mode 100644 index 0000000..281733b --- /dev/null +++ b/assets/tilesetnpp.json @@ -0,0 +1,14 @@ +{ "columns":24, + "image":"tileset.png", + "imageheight":136, + "imagewidth":192, + "margin":0, + "name":"tileset", + "spacing":0, + "tilecount":408, + "tiledversion":"1.8.0", + "tileheight":8, + "tilewidth":8, + "type":"tileset", + "version":"1.8" +} \ No newline at end of file diff --git a/clean b/clean index db7550f..968770d 100755 --- a/clean +++ b/clean @@ -1,5 +1,5 @@ cd assets -rm -f *.json +rm -f level*.json rm -r __pycache__ cd .. rm -r build-cg diff --git a/src/game.c b/src/game.c index 587e746..5dea9b4 100644 --- a/src/game.c +++ b/src/game.c @@ -13,6 +13,8 @@ extern bopti_image_t SignAction_img; +extern Dialog *dialogRPG; + #define MAX_INTERACTION_DISTANCE 12 diff --git a/src/game.h b/src/game.h index aacf89d..ac70e88 100644 --- a/src/game.h +++ b/src/game.h @@ -43,13 +43,6 @@ typedef struct { typedef struct { - /* position of the item */ - uint32_t x; - uint32_t y; - /* its name */ - char *name; - /* its class (NPC, SGN, INFO, ... )*/ - char *type; /* data to be shown in the dialog*/ char *dialog; /* is it a question or a simple dialog ? */ @@ -59,7 +52,28 @@ typedef struct { /* the conclusion of the dialog for answer 1 and 2 respectively */ /* Note : it may contain a set of event with a dedicated syntax */ char *conclusion1; + int32_t next1; char *conclusion2; + int32_t next2; + int32_t nextOther; +} Dialog; + + +typedef struct { + /* position of the item */ + uint32_t x; + uint32_t y; + /* its name */ + char *name; + /* its class (NPC, SGN, INFO, ... )*/ + char *type; + + /* the ID of the first element of the dialog */ + /* (to be aligned with "dialogs.json" IDs)*/ + uint32_t dialogID; + /* 0 if imperative dialog (story mode) */ + /* or 1 if the player need to press [SHIFT] to initiate the sequence*/ + uint32_t needAction; /* data for NPC's trajectories */ uint32_t hasPath; diff --git a/src/main.c b/src/main.c index 9b977be..4815df8 100644 --- a/src/main.c +++ b/src/main.c @@ -156,7 +156,7 @@ int main(void) { dprint( 10, 90+i*15, C_RED, "X= %d - Y= %d - T: %s", game.map_level->extradata[i].x, game.map_level->extradata[i].y, - game.map_level->extradata[i].dialog ); + game.map_level->extradata[i].dialogID ); } #endif diff --git a/src/player.c b/src/player.c index 08f1716..8e30a89 100644 --- a/src/player.c +++ b/src/player.c @@ -32,6 +32,9 @@ extern bopti_image_t SGN_Icon_img; extern bopti_image_t INFO_Icon_img; +extern Dialog *dialogRPG; + + void player_draw(Game *game) { Player *player = &game->player; dimage(player->px-P_WIDTH/2, player->py-P_HEIGHT/2, &demo_player_img); @@ -94,7 +97,7 @@ void player_action(Game *game) { ExtraData *currentData = &game->map_level->extradata[game->player.whichAction]; /* we collect the information */ - char *text = currentData->dialog; + char *text = currentData->dialogID; /* we use the correct image as per the class of the item */ bopti_image_t *face; if (strcmp("INFO", currentData->type)==0) @@ -106,11 +109,11 @@ void player_action(Game *game) { else face = &demo_player_img; /* we treat the action - i.e. we show a dialog */ - if (currentData->isQuestion==1) /* we have to manage a question */ + if (dialogRPG[ currentData->dialogID ].isQuestion ==1) /* we have to manage a question */ { - char *choices = currentData->choices; - char *conclusion1 = currentData->conclusion1; - char *conclusion2 = currentData->conclusion2; + char *choices = dialogRPG[ currentData->dialogID].choices ; + char *conclusion1 = dialogRPG[currentData->dialogID].conclusion1; + char *conclusion2 = dialogRPG[currentData->dialogID].conclusion2; int answer = showtext_dialog_ask( game, face, text, true, true, choices, 2, 0 );