diff --git a/CMakeLists.txt b/CMakeLists.txt index b6ba6dd..65291bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES src/memory.c src/game.c src/dialogs.c + src/npc.c # ... ) # Shared assets, fx-9860G-only assets and fx-CG-50-only assets diff --git a/assets-cg/NPC_Icon_2.png b/assets-cg/NPC_Icon_2.png new file mode 100644 index 0000000..e347bc7 Binary files /dev/null and b/assets-cg/NPC_Icon_2.png differ diff --git a/assets-cg/SGN_Icon.png b/assets-cg/SGN_Icon.png index 2659532..567996c 100644 Binary files a/assets-cg/SGN_Icon.png and b/assets-cg/SGN_Icon.png differ diff --git a/assets-cg/font.png b/assets-cg/font.png index 73c7244..d1dd75a 100644 Binary files a/assets-cg/font.png and b/assets-cg/font.png differ diff --git a/assets-fx/font.png b/assets-fx/font.png index b89f3dd..285cf81 100644 Binary files a/assets-fx/font.png and b/assets-fx/font.png differ diff --git a/assets/converters.py b/assets/converters.py index cd3b933..a942fa6 100644 --- a/assets/converters.py +++ b/assets/converters.py @@ -123,10 +123,10 @@ def get_tile_map_data(input, output, params, target, xmin, ymin, xmax, ymax): #create the structure of the map structMap = fxconv.Structure() - structMap += fxconv.u16(w) + fxconv.u16(h) + fxconv.u16(nbTilelayer) - structMap += fxconv.u16(tileset_size) + structMap += fxconv.u32(w) + fxconv.u32(h) + fxconv.u32(nbTilelayer) + structMap += fxconv.u32(tileset_size) - structMap += fxconv.u16(xmin) + fxconv.u16(ymin) + fxconv.u16(xmax) + fxconv.u16(ymax) + structMap += fxconv.u32(xmin) + fxconv.u32(ymin) + fxconv.u32(xmax) + fxconv.u32(ymax) structMap += fxconv.ref(f"img_{nameTilesetFree}") @@ -230,24 +230,105 @@ def get_extra_map_data(input, output, params, target, xmin, ymin, xmax, ymax): #create the structure of the map structData = fxconv.Structure() - nbExtraData = 0 layer = data["layers"][layer_extradata] for i in layer["objects"]: - nbExtraData = nbExtraData + 1 - x = i["x"] + xmin - y = i["y"] + ymin - nme = i["name"] + + #get the type of the item tpe = i["type"] - for j in i["properties"]: - stg = j[ "value" ] - print( "OBJECT X= ", x, " Y= ", y, "STR= ", stg ) + + #we check if the type corresponds to a items of type Point in Tiled + if tpe in ( "SGN", "NPC", "INFO" ): + + nbExtraData = nbExtraData + 1 + x = i["x"] + xmin + y = i["y"] + ymin + nme = i["name"] + + + dialog = None + quest = 0 + choi = None + conc1 = None + conc2 = None + path = 0 + path_length = 0 + xdata = None + ydata = None + + #we now fill all the properties of this item + for j in i["properties"]: + #property "dialog" + if j["name"]=="dialog": dialog = 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" ] + else: + #Extra properties for NPCs (path) + if tpe=="NPC": + if j["name"]=="hasPath": + pathID = None + path = j[ "value" ] + if path==1: + print( "PNJ has path - NOW LOOKING FOR RELEVANT DATA" ) + + # we start looking for path data with first the ID of the path Object + for u in i["properties"]: + if u["name"]=="path": + pathID = u[ "value" ] + print( "path ID is identified : ID= ", pathID ) + + for v in layer["objects"]: + if v[ "id" ] == pathID: + print( "path data found : " ) + xdata = bytes() + ydata = bytes() + for w in v[ "polyline" ]: + path_length = path_length + 1 + print( "X= ", w[ "x" ], " Y= ", w[ "y" ] ) + xdata += fxconv.u16( int( w[ "x" ] ) ) + ydata += fxconv.u16( int( w[ "y" ] ) ) + + else: + print( "PNJ has no Path" ) + + else: + print( "UNIDENTIFIED PROPERTY : ", j["name"]) + + print( "OBJECT X= ", x, " Y= ", y, "STR= ", dialog ) print( " Type= ", tpe, " Name= ", nme ) - - structData += fxconv.u16( int(x) ) - structData += fxconv.u16( int(y) ) - structData += fxconv.string( nme ) - structData += fxconv.string( tpe ) - structData += fxconv.string( stg ) + 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 ) + if path==0: + structData += fxconv.u32(0) + structData += fxconv.u32(0) + structData += fxconv.u32(0) + structData += fxconv.u32(0) + else: + structData += fxconv.u32(path) + structData += fxconv.u32(path_length) + structData += fxconv.ptr( xdata ) + structData += fxconv.ptr( ydata ) + + #else we do nothing (yet) + else: + break return nbExtraData, structData diff --git a/assets/level0.tmx b/assets/level0.tmx index e6fd8eb..e4aa782 100644 --- a/assets/level0.tmx +++ b/assets/level0.tmx @@ -1,5 +1,5 @@ - + @@ -92,39 +92,80 @@ - + + + + + + + + + - + - + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/level1.tmx b/assets/level1.tmx index 20e3824..5f6f37a 100644 --- a/assets/level1.tmx +++ b/assets/level1.tmx @@ -89,7 +89,11 @@ + + + + diff --git a/assets/level2.tmx b/assets/level2.tmx index 6d1fb52..6eede1e 100644 --- a/assets/level2.tmx +++ b/assets/level2.tmx @@ -89,19 +89,31 @@ + + + + + + + + + + + + diff --git a/assets/level3.tmx b/assets/level3.tmx index d756a70..2ac318b 100644 --- a/assets/level3.tmx +++ b/assets/level3.tmx @@ -89,31 +89,51 @@ + + + + + + + + - + + + + + + + + + + + + + diff --git a/src/dialogs.c b/src/dialogs.c index f982581..4a44fac 100644 --- a/src/dialogs.c +++ b/src/dialogs.c @@ -10,6 +10,10 @@ #define BOX_HEIGHT (F_HEIGHT/PXSIZE+8) +#define CHOICE_BOX_HEIGHT 10 +#define CHOICE_BOX_PADDING_TOP 3 + + extern font_t fontRPG; #define FONT_USED fontRPG @@ -19,6 +23,14 @@ extern font_t fontRPG; uint32_t *lightVRAMcurrent, *darkVRAMcurrent; #endif //GRAYMODEOK +/* the color of the text to go to the next dialog phase */ +/* it improves readability to have somathing lighter */ +#if GRAYMODEOK || (defined(FXCG50) && !defined(COLOR1BIT)) + #define NEXT_COLOR C_LIGHT +#else + #define NEXT_COLOR C_BLACK +#endif + void blit() { @@ -125,15 +137,15 @@ int showtext_opt(Game *game, bopti_image_t *face, char *text, if(update_screen) blit(); while(game->frame_duration < line_duration) sleep(); game->frame_duration = 0; - /* Ask the user to press EXE to continue. */ - dtext(BOX_HEIGHT*PXSIZE, y, C_BLACK, "[EXE] to continue ..."); + /* Ask the user to press SHIFT to continue. */ + dtext(BOX_HEIGHT*PXSIZE, y, NEXT_COLOR, "[SHIFT] : suite..."); } /* Make a little animation :). */ if(update_screen) blit(); if(l>=max_lines_amount-1){ /* If we drew one entire screen. */ - /* Wait that the EXE key is pressed if we should. */ - if(wait_continue) while(getkey().key != KEY_EXE) sleep(); + /* Wait that the SHIFT key is pressed if we should. */ + if(wait_continue) while(getkey_opt(GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA, NULL).key != KEY_SHIFT) sleep(); /* Clear the text area. */ drect(BOX_HEIGHT*PXSIZE, 0, DWIDTH, (BOX_HEIGHT-1)*PXSIZE-2, C_WHITE); @@ -153,11 +165,11 @@ int showtext_opt(Game *game, bopti_image_t *face, char *text, if(update_screen) blit(); while(game->frame_duration < line_duration) sleep(); game->frame_duration = 0; - /* Ask the user to press EXE to continue. */ - dtext(BOX_HEIGHT*PXSIZE, y, C_BLACK, "[EXE] to continue ..."); - /* Update the screen and wait for EXE being pressed, if needed. */ + /* Ask the user to press SHIFT to continue. */ + dtext(BOX_HEIGHT*PXSIZE, y, NEXT_COLOR, "[SHIFT] : suite..."); + /* Update the screen and wait for SHIFT being pressed, if needed. */ if(update_screen) blit(); - if(wait_continue) while(getkey().key != KEY_EXE) sleep(); + if(wait_continue) while(getkey_opt( GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA, NULL).key != KEY_SHIFT) sleep(); } if(call_before_end) return_int = call_before_end(game, i); if(end_anim){ @@ -187,8 +199,6 @@ void showtext_dialog(Game *game, bopti_image_t *face, char *text, true, 0, true); } -#define CHOICE_BOX_HEIGHT 10 -#define CHOICE_BOX_PADDING_TOP 3 /* Some variables and pointers used to get some arguments passed in * showtext_dialog_ask in _choice_call_before_end. */ @@ -253,7 +263,7 @@ int _choice_call_before_end(Game *game, unsigned int org_i) { C_BLACK, ">"); } blit(); - key = getkey().key; + key = getkey_opt( GETKEY_DEFAULT & ~GETKEY_MOD_SHIFT & ~GETKEY_MOD_ALPHA, NULL).key; /* If the player pressed the left arrow key and has not already selected * the first possible choice. */ if(key == KEY_LEFT && selected > 0){ @@ -280,9 +290,9 @@ int _choice_call_before_end(Game *game, unsigned int org_i) { /* Move the selection arrow and update the selected item. */ selected++; } - /* If the user has not validated his choice by pressing EXE, we loop one + /* If the user has not validated his choice by pressing SHIFT, we loop one * more time. */ - }while(key != KEY_EXE); + }while(key != KEY_SHIFT); /* Make a little animation because we looove little animations ;) */ for(i=DWIDTH/8+1;i>0;i--){ /* I'm drawing the same box as on the start animation */ diff --git a/src/game.c b/src/game.c index e04fea4..587e746 100644 --- a/src/game.c +++ b/src/game.c @@ -8,6 +8,7 @@ #include #include +#include "npc.h" #include "stdlib.h" extern bopti_image_t SignAction_img; @@ -58,6 +59,7 @@ void render_indicator(Game *game) void draw(Game *game) { /* Draw everything. */ render_map_by_layer(game, BACKGROUND); + npc_draw( game ); player_draw(game); render_map_by_layer(game, FOREGROUND); render_indicator( game ); diff --git a/src/game.h b/src/game.h index d6237e6..aacf89d 100644 --- a/src/game.h +++ b/src/game.h @@ -44,31 +44,46 @@ typedef struct { typedef struct { /* position of the item */ - uint16_t x; - uint16_t y; + 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 ? */ + uint32_t isQuestion; + /* if it is a question, then the choices for answering */ + char *choices; + /* 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; + char *conclusion2; + + /* data for NPC's trajectories */ + uint32_t hasPath; + uint32_t path_length; + uint16_t *xpath; + uint16_t *ypath; + /* ... this can be extended as per needs ... */ } ExtraData; typedef struct { /* width, height and the number of layer of the map */ - uint16_t w; - uint16_t h; - uint16_t nblayers; - uint16_t tileset_size; + uint32_t w; + uint32_t h; + uint32_t nblayers; + uint32_t tileset_size; /* world coordinates of the upper left and bootom right*/ /* corners of the current map to be multiplied in game by PXSIZE */ - uint16_t xmin; - uint16_t ymin; - uint16_t xmax; - uint16_t ymax; + uint32_t xmin; + uint32_t ymin; + uint32_t xmax; + uint32_t ymax; /* the tileset to use */ bopti_image_t *tileset; diff --git a/src/main.c b/src/main.c index f0a98dc..5d1cc83 100644 --- a/src/main.c +++ b/src/main.c @@ -108,11 +108,13 @@ int main(void) { dgray(DGRAY_ON); #endif +/* showtext_dialog(&game, &player_face_img, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet.", true, true); int in = showtext_dialog_ask(&game, &player_face_img, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet.", true, false, "Lorem\0Ipsum\0Dolor", 3, 0); if(in==2) showtext_dialog(&game, &player_face_img, "You choosed Dolor", false, true); else if(in==1) showtext_dialog(&game, &player_face_img, "You choosed Ipsum", false, true); else showtext_dialog(&game, &player_face_img, "You choosed Lorem", false, true); +*/ do{ /* clear screen */ diff --git a/src/npc.c b/src/npc.c new file mode 100644 index 0000000..ca7b4a7 --- /dev/null +++ b/src/npc.c @@ -0,0 +1,68 @@ +#include "npc.h" +#include "dialogs.h" +#include "game.h" +#include "map.h" +#include "config.h" +#include +#include + + +extern bopti_image_t demo_PNJ_img; + + +/* the color of the text to go to the next dialog phase */ +/* it improves readability to have somathing lighter */ +#if defined(FXCG50) + #define PATH_COLOR C_RED +#else + #define PATH_COLOR C_BLACK +#endif + + +void npc_draw(Game *game) { + Player *player = &game->player; + + for (int u=0; umap_level->nbextradata; u++) + { + ExtraData *Data = &game->map_level->extradata[u]; + + if (strcmp(Data->type, "NPC")==0) /* the current data is a NPC */ + { + + /* TODO : This is for debugging purpose, JUste to render the path */ + /* to be followed by the NPC when this will be implemented */ + if (Data->hasPath==1) /* this NPC has a trajectory */ + { + int NbPoints = Data->path_length; + for(int v=0; vxpath[v % NbPoints] * PXSIZE))-(int16_t) player->wx; + int16_t deltaY1=((int16_t) (Data->ypath[v % NbPoints] * PXSIZE))-(int16_t) player->wy; + + int16_t deltaX2=((int16_t) (Data->xpath[(v+1) % NbPoints] * PXSIZE))-(int16_t) player->wx; + int16_t deltaY2=((int16_t) (Data->ypath[(v+1) % NbPoints] * PXSIZE))-(int16_t) player->wy; + + dline( player->px + deltaX1, player->py + deltaY1, + player->px + deltaX2, player->py + deltaY2, + PATH_COLOR); + */ + + } + + + } + + int16_t deltaX=((int16_t) (Data->x * PXSIZE))-(int16_t) player->wx; + int16_t deltaY=((int16_t) (Data->y * PXSIZE))-(int16_t) player->wy; + dimage( player->px-P_WIDTH/2+deltaX, + player->py-P_HEIGHT/2+deltaY, + &demo_PNJ_img); + + } + } + + +} + diff --git a/src/npc.h b/src/npc.h new file mode 100644 index 0000000..0293824 --- /dev/null +++ b/src/npc.h @@ -0,0 +1,18 @@ +#ifndef NPC_H +#define NPC_H + + +#include + +#include "game.h" +#include "memory.h" + + + +/* Draws the player player. This function should be called after drawing the + * map! */ +void npc_draw(Game *game); + + +#endif + diff --git a/src/player.c b/src/player.c index ed7067f..08f1716 100644 --- a/src/player.c +++ b/src/player.c @@ -1,5 +1,6 @@ #include "player.h" #include "dialogs.h" +#include "game.h" #include "map.h" #include "config.h" #include @@ -90,21 +91,40 @@ void player_action(Game *game) { /* we indicate that the player is occupied */ game->player.isDoingAction = true; + ExtraData *currentData = &game->map_level->extradata[game->player.whichAction]; + /* we collect the information */ - char *text = game->map_level->extradata[game->player.whichAction].dialog; + char *text = currentData->dialog; /* we use the correct image as per the class of the item */ bopti_image_t *face; - if (strcmp("INFO", game->map_level->extradata[game->player.whichAction].type)==0) + if (strcmp("INFO", currentData->type)==0) face = &INFO_Icon_img; - else if (strcmp("NPC", game->map_level->extradata[game->player.whichAction].type)==0) + else if (strcmp("NPC", currentData->type)==0) face = &NPC_Icon_img; - else if (strcmp("SGN", game->map_level->extradata[game->player.whichAction].type)==0) + else if (strcmp("SGN", currentData->type)==0) face = &SGN_Icon_img; else face = &demo_player_img; - /* we treat the action - i.e. we show a dialog */ - showtext_dialog( game, face, text, true, true ); - + /* we treat the action - i.e. we show a dialog */ + if (currentData->isQuestion==1) /* we have to manage a question */ + { + char *choices = currentData->choices; + char *conclusion1 = currentData->conclusion1; + char *conclusion2 = currentData->conclusion2; + + int answer = showtext_dialog_ask( game, face, text, true, true, choices, 2, 0 ); + + /* TO DO we need to split the strings conclusion1 and conclusion2 */ + /* to extract the "gift" part */ + + if (answer==0) showtext_dialog( game, face, conclusion1, true, true ); + else showtext_dialog( game, face, conclusion2, true, true ); + } + else + { + showtext_dialog( game, face, text, true, true ); + } + /* when done we release the occupied status of the player */ game->player.isDoingAction = false; }