From f49a7490c11c27beb4fe593866971f05c9c8b680 Mon Sep 17 00:00:00 2001 From: Ondrej Novak Date: Tue, 15 Apr 2025 09:49:43 +0200 Subject: [PATCH] add steam and achivements, improve console --- CMakeLists.txt | 16 +- external/steamworks/.gitignore | 6 + external/steamworks/get_sdk.md | 20 ++ game/console.c | 328 ++++++++++----------------------- game/dialogy.c | 28 ++- game/globals.h | 4 +- game/skeldal.c | 7 +- lang/en/items.csv | 20 +- lang/en/ui.csv | 2 +- platform/CMakeLists.txt | 39 +++- platform/achievements.cpp | 103 +++++++++++ platform/achievements.h | 36 ++++ platform/sdl/sdl_context.cpp | 9 + 13 files changed, 360 insertions(+), 258 deletions(-) create mode 100644 external/steamworks/.gitignore create mode 100644 external/steamworks/get_sdk.md create mode 100644 platform/achievements.cpp create mode 100644 platform/achievements.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9533397..772c57b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,20 @@ project(skeldal) # Najít SDL2 knihovnu find_package(SDL2 REQUIRED) +set(STEAMWORKS_SDK_DIR "${CMAKE_SOURCE_DIR}/external/steamworks/") +# Check if Steamworks SDK directories exist +if(NOT EXISTS "${STEAMWORKS_SDK_DIR}/public") + message(FATAL_ERROR "❌ Could not find Steamworks SDK 'public' headers. + Make sure to download the Steamworks SDK and place it in:${STEAMWORKS_SDK_DIR} + Expected directory: ${STEAMWORKS_SDK_DIR}/public + ") +endif() +if(NOT EXISTS "${STEAMWORKS_SDK_DIR}/redistributable_bin") + message(FATAL_ERROR "❌ Could not find Steamworks SDK 'redistributable_bin' libraries. + Make sure to download the Steamworks SDK and place it in: ${STEAMWORKS_SDK_DIR} + Expected directory: ${STEAMWORKS_SDK_DIR}/redistributable_bin + ") +endif() if (MSVC) add_compile_options(/W4 /EHa /DNOMINMAX /D_CRT_SECURE_NO_WARNINGS /J) @@ -19,7 +33,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/) include_directories( ${SDL2_INCLUDE_DIRS}) -enable_testing() +include_directories(${STEAMWORKS_SDK_DIR}/public) add_subdirectory(libs) add_subdirectory(platform) add_subdirectory(game) diff --git a/external/steamworks/.gitignore b/external/steamworks/.gitignore new file mode 100644 index 0000000..6e6b0ec --- /dev/null +++ b/external/steamworks/.gitignore @@ -0,0 +1,6 @@ +# Exclude all files +* + +# Allow only get_sdk.md +!get_sdk.md +!.gitignore \ No newline at end of file diff --git a/external/steamworks/get_sdk.md b/external/steamworks/get_sdk.md new file mode 100644 index 0000000..67dca10 --- /dev/null +++ b/external/steamworks/get_sdk.md @@ -0,0 +1,20 @@ +# Download and Extract Steamworks SDK + +Follow these steps to download and extract the Steamworks SDK: + +## Steps to Download and Extract +1. Visit the official [Steamworks SDK page](https://partner.steamgames.com/). +2. Log in with your Steam developer account. +3. Navigate to the "SDK & Tools" section. +4. Download the latest version of the Steamworks SDK. +5. Once downloaded, extract the contents of the SDK archive into this directory. + +## Required Directory Structure +After extraction, ensure the following directories exist in this location: +- `redistributable_bin/` +- `public/` +- `tools/` +- `sdk/` +- `samples/` + +If any of these directories are missing, re-download and extract the SDK to ensure all files are included. \ No newline at end of file diff --git a/game/console.c b/game/console.c index de58efc..9910218 100644 --- a/game/console.c +++ b/game/console.c @@ -1,5 +1,6 @@ #include #include +#include #include "globals.h" #include @@ -106,209 +107,6 @@ void spell_group_invis() } -#if 0 -static void advence_player(int player,int level,char auto_advance) - { - THUMAN *h; - float mh,mv; - - if (level<2) return; - h=postavy+player; - mh=(float)human_selected->jidlo/MAX_HLAD(human_selected); - mv=(float)human_selected->voda/MAX_ZIZEN(human_selected); - human_selected=h; - h->exp=level_map[level-2]; - check_player_new_level(h); - if (auto_advance) - { - int vlssuma=h->vlastnosti[VLS_SILA]+ - h->vlastnosti[VLS_OBRAT]+ - h->vlastnosti[VLS_POHYB]+ - h->vlastnosti[VLS_SMAGIE]; - int b,i; - - for(i=0;i<4;i++) - { - b=h->vlastnosti[i]*h->bonus/vlssuma; - h->bonus-=b;vlssuma-=h->vlastnosti[i]; - while (b--) advance_vls(i); - } - prepocitat_postavu(human_selected); - } - human_selected->jidlo=(int)(mh*MAX_HLAD(human_selected)); - human_selected->voda=(int)(mv*MAX_ZIZEN(human_selected)); - wzprintf("%s ziskal%s uroven cislo %d\r\n",h->jmeno,h->female?"a":"",level); - } - - -extern char folow_mode; -extern char folow_mob; -void macro_drop_item(); - -static char take_money() - { - int i; - if (!wzscanf("Kolik: (0 - zrusit):","%d",&i)) return 0; - money+=i; - if (i) - { - SEND_LOG("(WIZARD) Take Money %d, total %d",i,money); - } - return (i!=0); - } - -#define ALL "ALL" -static char purge_map() - { - char buffer[200]; - char *c; - - STOP(); -/* struct find_t rc; - int rs; - - concat(c,pathtable[SR_TEMP],"*.TMP"); - rs=_dos_findfirst(c,_A_NORMAL,&rc); - while (rs==0) - { - if (rc.name[0]!='~') wzputs(rc.name); - rs=_dos_findnext(&rc); - } - _dos_findclose(&rc);*/ - wzprintf("\r\n Zadej jmeno tempu (all - vse):");gets(buffer); - if (buffer[0]==0) return 0; - strupper(buffer); - concat(c,pathtable[SR_TEMP],buffer); - if (strcmp(buffer,ALL) && check_file_exists_ex(c)) - { - wzputs("Soubor nenalezen!"); - return 0; - } - SEND_LOG("(WIZARD) Purge Map: '%s'",buffer,0); - if (!strcmp(buffer,ALL)) purge_temps(0); - else remove(c); - return 1; - } - -static char heal_meditate(void) - { - int a,b,i; - THUMAN *p; - - if (!wzscanf("Obnovit postavu c: (0 - vsechny, -1 - zrusit):","%d",&b)) return 0; - if (b==-1) return 0; - if (b) a=b-1;else a=0,b=POCET_POSTAV; - p=postavy+a; - for(i=a;iused && p->lives) - { - p->lives=p->vlastnosti[VLS_MAXHIT]; - p->mana=p->vlastnosti[VLS_MAXMANA]; - p->kondice=p->vlastnosti[VLS_KONDIC]; - p->jidlo=MAX_HLAD(p); - p->voda=MAX_ZIZEN(p); - SEND_LOG("(WIZARD) Restoring character '%s'",p->jmeno,0); - bott_draw(1); - } - return 1; - } - -static char raise_death(void) - { - int b; - THUMAN *p; - char *c,*d; - - if (!wzscanf("Obzivit postavu c: (0 a -1 - zrusit):","%d",&b)) return 0; - b--; - if (b<0) return 0; - p=postavy+b; - p->lives=p->vlastnosti[VLS_MAXHIT]; - p->mana=p->vlastnosti[VLS_MAXMANA]; - p->kondice=p->vlastnosti[VLS_KONDIC]; - c="(WIZARD) '%s' has been returned to game by gods power!";d=strchr(c,'\''); - wzprintf(d,p->jmeno);putchar('\r\n'); - bott_draw(1); - return 0; - } - -/* - static char raise_killed_monster(HWND hDlg) - { - HWND listdlg=PrepareListWindow(hDlg); - HWND list=GetDlgItem(listdlg,IDC_LIST); - char buff[256]; - int i; - int res; - - for (i=0;ibonus_zbrani[i],h->weapon_expy[i]); - if (!wzscanf(" ","%[^\n]",buff)) return 0; - if (buff[0]==0) return 0; - if (sscanf(buff,"%d %d",&bonus,&value)!=2) wzputs("Huh?!"); - else - { - bonus--; - if (bonus<0 || bonus>=TPW_MAX) wzputs("Spatna zbran"); - else - if (value<0 || value>=10) wzputs("Spatna hodnota"); - else - h->bonus_zbrani[bonus]=value; - } - } - while(1); - } - -static reload_mobs() - { - extern char reset_mobiles; - reset_mobiles=1; - strcopy_n(loadlevel.name,level_fname,12); - loadlevel.start_pos=viewsector; - loadlevel.name[12]=0; - loadlevel.dir=viewdir; - send_message(E_CLOSE_MAP); - } - -#endif static char display_game_status(void) @@ -319,44 +117,39 @@ static char display_game_status(void) TSECTOR *ss; int i,cn,astr; - SEND_LOG("(WIZARD) Starting wizard window at Sect %d Side %d",viewsector,viewdir); - wzprintf("Sektor: %5d Smer: %d Skupina %d \r\n",viewsector,viewdir,cur_group); - for(i=0,p=postavy;iused) - wzprintf("%d.%-14s (%d) Sek:%5d Smr:%d HPReg:%d MPReg:%d VPReg:%d %04X%s\r\n",i+1,p->jmeno,p->groupnum,p->sektor,p->direction,p->vlastnosti[VLS_HPREG], - p->vlastnosti[VLS_MPREG], p->vlastnosti[VLS_VPREG], p->vlastnosti[VLS_KOUZLA], p->lives?"":"(smrt)"); - else - wzprintf("%d. (nepouzito)\r\n",i); - wzputs(""); - wzprintf("Predmet(y) v mysi: "); + wzprintf("Sector: %5d Dir: %d Group %d \n",viewsector,viewdir,cur_group); + for(i=0,p=postavy;iused) { + wzprintf("%d.%-14s (%d) Sek:%5d Smr:%d HPReg:%d MPReg:%d VPReg:%d %04X%s\n",i+1,p->jmeno,p->groupnum,p->sektor,p->direction,p->vlastnosti[VLS_HPREG], + p->vlastnosti[VLS_MPREG], p->vlastnosti[VLS_VPREG], p->vlastnosti[VLS_KOUZLA], p->lives?"":"(dead)"); + } + } + wzprintf("Held items: "); v=picked_item; - if (v==NULL) wzprintf("");else while(*v) wzprintf("%d ",abs(*v++)-1); - wzputs("\r\n"); + if (v==NULL) wzprintf("\n");else while(*v) wzprintf("%d \n",abs(*v++)-1); for(i=0,cn=0,astr=0;isector_type, ss->floor,ss->ceil,ss->sector_tag,ss->side_tag,ss->action); - wzprintf(" Vychody: Sev %d Vych %d Jih %d Z�p %d\r\n",ss->step_next[0],ss->step_next[1],ss->step_next[2],ss->step_next[3]); - wzprintf(" Vlajky: %02X %02X ",ss->flags,map_coord[viewsector].flags);show_flags(map_coord[viewsector].flags,mc_flags,12); - wzputs("\r\n"); - wzprintf("Stena: Prim %d Sec %d Obl %d Anim_prim %d/%d Anim_sec %d/%d\r\n", + wzprintf(" Exits: North %d East %d South %d West %d\n",ss->step_next[0],ss->step_next[1],ss->step_next[2],ss->step_next[3]); + wzprintf(" FLAGS: %02X %02X ",ss->flags,map_coord[viewsector].flags); + show_flags(map_coord[viewsector].flags,mc_flags,12); + wzprintf("\nSide: Prim %d Sec %d Arc %d Anim_prim %d/%d Anim_sec %d/%d\n", s->prim,s->sec,s->oblouk & 0xf,s->prim_anim>>4,s->prim_anim & 0xf,s->sec_anim>>4,s->sec_anim & 0xf); - wzprintf(" Cil akce %d Smer akce %d Akce %d\r\n",s->action,s->sector_tag,s->side_tag & 0x3); - wzprintf(" Multiakce: %s\r\n",macros[viewsector*4+viewdir].action_list==NULL?"":"Existuje"); - wzprintf(" Vlajky: %04X %02X %02X ",s->flags,s->oblouk>>4,s->side_tag>>2); - wzputs(""); + wzprintf(" Action target %d Action side %d Action %d\n",s->action,s->sector_tag,s->side_tag & 0x3); + wzprintf(" Multiaction: %s\n",macros[viewsector*4+viewdir].action_list==NULL?"No":"Yes"); + wzprintf(" Flags: %04X %02X %02X\n",s->flags,s->oblouk>>4,s->side_tag>>2); show_flags(s->flags,side_flags,32); show_flags(s->oblouk>>4,obl_flags,4); + wzprintf("\n"); return 0; } @@ -372,6 +165,7 @@ extern char pass_all_mobs; static char console_input_line[console_max_characters+1] = ""; static char *console_output_lines[console_max_lines] = {}; static int console_top_line = 0; +static const char *console_command = NULL; static const int console_x = 0; static const int console_y = SCREEN_OFFLINE; @@ -415,11 +209,27 @@ char console_is_visible() { return console_visible; } -static void console_add_line(const char *line) { +static void console_add_line(const char *line); + +static void flush_console_command() { + if (console_command) { + const char *cmd = concat2("> ", console_command); + console_command = NULL; + console_add_line(cmd); + } +} + +static void console_add_line_s(const char *line, size_t sz) { + flush_console_command(); free(console_output_lines[console_max_lines-1]); memmove(console_output_lines+1,console_output_lines, (console_max_lines-1)*sizeof(char *)); - console_output_lines[0] = strdup(line); + console_output_lines[0] = malloc(sz+1); + memcpy(console_output_lines[0], line, sz); + console_output_lines[0][sz] = 0; +} +static void console_add_line(const char *line) { + console_add_line_s(line, strlen(line)); } typedef struct { @@ -527,6 +337,10 @@ static int process_on_off_command(const char *cmd, char on) { dead_food = on; return 1; } + if (istrcmp(cmd, "trace-dialogs") == 0) { + trace_dialogs = on; + return 1; + } return 0; } @@ -645,6 +459,20 @@ static int process_actions(const char *command) { load_map(lname); return 1; } + if (istrcmp(command, "steam") == 0) { + if (is_steam_available()) { + char *c = get_steam_status();; + wzputs(c); + free(c); + } else { + wzputs("N/A"); + } + return 1; + } + if (istrcmp(command,"help") == 0) { + wzputs("Help has left the chat. Try the forums, brave wanderer. Set your inner-eye on"); + return 1; + } @@ -741,6 +569,34 @@ static int process_with_params(const char *cmd, const char *args) { } return 0; } + + if (istrcmp(cmd, "achieve") == 0) { + return !set_achievement(args); + } + if (istrcmp(cmd, "unachieve") == 0) { + return !clear_achievement(args); + } + if (istrcmp(cmd, "talk") == 0) { + if (args[0] == 0) return 0; + int id; + char pgf = 0; + if (args[0] == '/') { + pgf = 1; + args++; + } + id = atoi(args); + if (!pgf) id = id * 128; + if (dialog_is_paragraph(id)) { + call_dialog(id, -1); + return 1; + } + } + if (istrcmp(cmd, "unvisit") == 0) { + if (args[0] == 0) return 0; + int id = atoi(args); + return dialog_set_notvisited(id); + } + return 0; } @@ -781,13 +637,15 @@ static void console_keyboard(EVENT_MSG *msg, void **_) { } msg->msg = -1; } else if (c == '\r') { + console_command = console_input_line; PARSED_COMMAND cmd = parse_command(console_input_line); char ok = process_command(cmd); if (ok) { - console_add_line(console_input_line); + flush_console_command(); console_top_line = 0; console_input_line[0] = 0; } + console_command = NULL; free(cmd.cmd_buffer); msg->msg = -1; } else if (c >=32 && len < console_max_characters) { @@ -856,7 +714,13 @@ void wzprintf(const char *text,...) static void wzputs(const char *text) { - console_add_line(text); + const char *sep = strchr(text, '\n'); + while (sep != NULL) { + console_add_line_s(text, sep-text); + text = sep+1; + sep = strchr(text, '\n'); + } + if (text[0] != 0) console_add_line_s(text, strlen(text)); } diff --git a/game/dialogy.c b/game/dialogy.c index 904e0fb..df18fd7 100644 --- a/game/dialogy.c +++ b/game/dialogy.c @@ -101,6 +101,8 @@ static int dialog_mob=0; static char code_page=1; +char trace_dialogs=0; + static char case_click(int id,int xa,int ya,int xr,int yr); static char ask_who_proc(int id,int xa,int ya,int xr,int yr); @@ -222,11 +224,7 @@ static void run_anim(char *name,int speed,int rep) static void error(char *text) { - char buff[256]; - sprintf(buff,"%.125s v odstavci %d\r\nLocal_pgf=%d / DIALOG : %d / SENTENCE : %d\r\n",text,last_pgf+local_pgf,local_pgf,local_pgf/128,last_pgf); -// MessageBox(NULL,buff,NULL,MB_OK|MB_ICONSTOP|MB_SYSTEMMODAL); - SEND_LOG("(DIALOGS) Dialog error detected at %d:%d",local_pgf/128,last_pgf); - SEND_LOG("(DIALOGS) Error description: %s",text); + wzprintf("%.125s paragraph %d\r\nLocal_pgf=%d / DIALOG : %d / SENTENCE : %d\r\n",text,last_pgf+local_pgf,local_pgf,local_pgf/128,last_pgf); } static void show_dialog_picture() @@ -253,7 +251,7 @@ static T_PARAGRAPH *find_paragraph(int num) { char s[80]; - sprintf(s,"Odstavec %d neexistuje! Odkaz byl vyvol�n",num); + sprintf(s,"Paragraph %d doesn't exists! Called from",num); error(s); return (T_PARAGRAPH *)pp; } @@ -283,6 +281,7 @@ static void goto_paragraph(int prgf) do { z=find_paragraph(prgf); + if (trace_dialogs) wzprintf("Dialog goto_paragraph %d (visited=%d)\n",prgf+local_pgf, z->visited); if (z->visited) z->first=1; if (z->alt==z->num || !z->visited) { @@ -1507,3 +1506,20 @@ char load_dialog_info(TMPFILE_RD *f) SEND_LOG("(DIALOGS)(SAVELOAD) Done..."); return res; } + + char dialog_is_paragraph(int id) { + const int *pp=(const int *)ablock(H_DIALOGY_DAT); + int pocet=*pp; + pp+=2; + const T_PARAGRAPH *z=(const T_PARAGRAPH *)pp; + for(int i=0;inum==(unsigned)id) return 1; + return 0; + } + + char dialog_set_notvisited(int pgf) { + local_pgf = 0; + if (!dialog_is_paragraph(pgf)) return 0; + set_nvisited(pgf); + return 1; + } + diff --git a/game/globals.h b/game/globals.h index 30cef6f..a992a00 100644 --- a/game/globals.h +++ b/game/globals.h @@ -1738,7 +1738,9 @@ short *q_item_one(int i,int itnum); //test zda postava i ma vec itnum short *q_item(int itnum,int sector); //test zda-li aspon jeden na sectoru ma vec itnum void change_flag(int flag,char mode); //meni vlajku = 0 - reset, 1 - set, 2 - neg char test_flag(int flag); //vraci stav vlajky; - +char dialog_is_paragraph(int id); //vraci zda-li je id dialogem +char dialog_set_notvisited(int pgf); //nastavi ze stranka nebyla navstivena +extern char trace_dialogs; //generator diff --git a/game/skeldal.c b/game/skeldal.c index 953cde3..a04d05a 100644 --- a/game/skeldal.c +++ b/game/skeldal.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -723,7 +724,7 @@ void do_timer(void) void done_skeldal(void) { - + achievements_shutdown(); close_manager(); close_story_file(); @@ -987,7 +988,9 @@ void init_skeldal(const INI_CONFIG *cfg) cti_texty(); timer_tree.next=NULL; - init_events(); + init_events(); + + achievements_init(); char verr = game_display_init(ini_section_open(cfg, "video"), "Skeldal"); if (!verr) diff --git a/lang/en/items.csv b/lang/en/items.csv index c49366e..ff108b9 100644 --- a/lang/en/items.csv +++ b/lang/en/items.csv @@ -209,7 +209,7 @@ Attack 4-7, Defense 1-2" " 104,"Scales of Fate " -105,"Potion White +105,"Potion � White " 106,"Automapping " @@ -250,7 +250,7 @@ Gift from the Talking Tree" " 125,"Tree Trunk " -126,"Potion Green +126,"Potion � Green " 127,"Haste Potion " @@ -258,14 +258,14 @@ Gift from the Talking Tree" " 129,"Chill Potion " -130,"Silver Jug Empty +130,"Silver Jug � Empty " -131,"Golden Jug Empty +131,"Golden Jug � Empty " 132,"Silver Jug -Filled with Water from the Spring of Death" +Filled from the Spring of Death" 133,"Golden Jug -Filled with Water from the Spring of Life" +Filled from the Spring of Life" 134,"Mushroom " 135,"Tworg Fur Coat @@ -300,9 +300,9 @@ Fire Protection +40%" " 150,"Heart of the Earth " -151,"Water from Queen Mithel's Fountain +151,"Queen Mithel's Water " -152,"Scroll of the Thought of Victory +152,"Scroll of Thought of Victory " 153,"Sword of Retribution Quest item" @@ -323,7 +323,7 @@ Attack 9-11, Air 9-13, Def. +3" 161,"Long War Spear Attack 5-7, Defense 3-5" 162,"Axe with Enekra's Blessing -Attack 10-12, Earth 9-14, Protection +2" +Attack 10-12, Earth 9-14, Prot. +2" 163,"Destroyed Axe " 164,"Destroyed Shield @@ -338,7 +338,7 @@ Defense +8, Elements +20%" Attack 9-11, Air 8-10" 169,"Shater's Crossbow Attack 9-13, Air 10-12" -170,"Helmet of Invisibility Invisibility +170,"Helmet of Invisibility " 171,"Potion of Strength " diff --git a/lang/en/ui.csv b/lang/en/ui.csv index a266f4e..2d8e83c 100644 --- a/lang/en/ui.csv +++ b/lang/en/ui.csv @@ -39,7 +39,7 @@ id,string 43,Cancel 44,Escape 45,Rearm -46,State +46,Wait 47,Magic 48,Start 49,Throw diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index 713cc8f..d05d4f7 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(skeldal_platform PRIVATE error.cpp timer.cpp getopt.c + achievements.cpp ) set(all_libs @@ -19,7 +20,7 @@ set(all_libs skeldal_platform skeldal_sdl skeldal_libs - ${SDL2_LIBRARIES} + ${SDL2_LIBRARIES} ${STANDARD_LIBRARIES}) if(WIN32) @@ -34,7 +35,24 @@ if(WIN32) windows/skeldal.rc ) target_compile_definitions(skeldal_platform PRIVATE PLATFORM_WINDOWS) - target_link_libraries(skeldal ${all_libs}) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # 64-bit + set(STEAMLIB ${STEAMWORKS_SDK_DIR}redistributable_bin/win64/steam_api64.lib) + set(STEAMDLL ${STEAMWORKS_SDK_DIR}redistributable_bin/win64/steam_api64.dll) + target_compile_definitions(skeldal_platform PRIVATE PLATFORM_WINDOWS_64) + else() + # 32-bit + set(STEAMLIB ${STEAMWORKS_SDK_DIR}/redistributable_bin/win32/steam_api.lib) + set(STEAMDLL ${STEAMWORKS_SDK_DIR}redistributable_bin/win64/steam_api.dll) + target_compile_definitions(skeldal_platform PRIVATE PLATFORM_WINDOWS_32) + endif() + + target_link_libraries(skeldal ${all_libs} ${STEAMLIB}) + + add_custom_command(TARGET skeldal POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${STEAMDLL} $ +) + message(STATUS "Building for Windows") elseif(UNIX AND NOT APPLE) @@ -46,13 +64,22 @@ elseif(UNIX AND NOT APPLE) target_sources(skeldal_bin PRIVATE linux/app_start.cpp ) - target_compile_definitions(skeldal_platform PRIVATE PLATFORM_LINUX) + target_compile_definitions(skeldal_bin PRIVATE PLATFORM_LINUX) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + # 64-bit + set(STEAMLIB ${STEAMWORKS_SDK_DIR}/redistributable_bin/linux64/libsteam_api.so) + target_compile_definitions(skeldal_bin PRIVATE PLATFORM_LINUX_64) + else() + # 32-bit + set(STEAMLIB ${STEAMWORKS_SDK_DIR}/redistributable_bin/linux32/libsteam_api.so) + target_compile_definitions(skeldal_bin PRIVATE PLATFORM_LINUX_32) + endif() add_custom_command( TARGET skeldal_bin POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/linux/skeldal.sh ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/skeldal.sh) - target_link_libraries(skeldal_bin ${all_libs}) + target_link_libraries(skeldal_bin ${all_libs} ${STEAMLIB}) message(STATUS "Building for Linux") elseif(APPLE) @@ -64,8 +91,10 @@ elseif(APPLE) linux/app_start.cpp ) target_compile_definitions(mylib PRIVATE PLATFORM_MACOS) + set(STEAMLIB ${STEAMWORKS_SDK_DIR}/redistributable_bin/osx/libsteam_api.dylib) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") message(STATUS "Building for macOS") - target_link_libraries(skeldal ${all_libs}) + target_link_libraries(skeldal ${all_libs} ${STEAMLIB}) else() error("Platform not detected, please add new platform here") endif() diff --git a/platform/achievements.cpp b/platform/achievements.cpp new file mode 100644 index 0000000..74dccc8 --- /dev/null +++ b/platform/achievements.cpp @@ -0,0 +1,103 @@ +#include "achievements.h" +#include +#include "error.h" +#include +#include + +extern "C" { + #include +} + +static char steam_available = 0; +static char steam_initialized = 0; + + +void run_steam_callbacks(const EVENT_MSG *msg,void **) { + if (msg->msg == E_IDLE) { + SteamAPI_RunCallbacks(); + } +} + +void achievements_init() +{ + if (!steam_initialized) { + steam_initialized = 1; + steam_available = SteamAPI_Init(); + if (steam_available) { + send_message(E_ADD, E_IDLE, &run_steam_callbacks); + SteamUserStats()->RequestUserStats(SteamUser()->GetSteamID()); + } else { + steam_available = 0; + } + } +} + +void achievements_shutdown() +{ + if (steam_available) { + send_message(E_DONE, E_IDLE, &run_steam_callbacks); + SteamAPI_Shutdown(); + steam_available = 0; + steam_initialized = 0; + } +} + +int8_t set_achievement(const char *id) +{ + achievements_init(); + + if (!steam_available) { + return -1; + } + + if (SteamUserStats() && SteamUserStats()->SetAchievement(id)) { + SteamUserStats()->StoreStats(); + return 0; + } else { + return -1; + } + +} + +int8_t clear_achievement(const char *id) +{ + achievements_init(); + + if (!steam_available) { + return -1; + } + + if (SteamUserStats() && SteamUserStats()->ClearAchievement(id)) { + SteamUserStats()->StoreStats(); + return 0; + } else { + return -1; + } +} + +char is_steam_available() +{ + return steam_available; +} + +char *get_steam_status() +{ + std::ostringstream oss; + int num_achievements = SteamUserStats()->GetNumAchievements(); + oss << "SteamAPI_Init: " << ( SteamAPI_Init() ? "success" : "failure") << "\n"; + oss << "UserStats pointer: " << SteamUserStats() << "\n"; + oss << "Is Steam overlay enabled:" << (SteamUtils()->IsOverlayEnabled() ? "yes" : "no") << "\n"; + oss << "AppID: "<< SteamUtils()->GetAppID() << "\n"; + oss << "Num Achievements: " << num_achievements << "\n"; + for (int i = 0; i < num_achievements; ++i) { + const char* name = SteamUserStats()->GetAchievementName(i); + bool achieved = false; + SteamUserStats()->GetAchievement(name, &achieved); + + oss << "[" << i << "] " << name << " - " << (achieved ? "Yes" : "No") << "\n"; + } + + std::string str = oss.str(); + char *out = strdup(str.c_str()); + return out; +} diff --git a/platform/achievements.h b/platform/achievements.h new file mode 100644 index 0000000..b53c6c8 --- /dev/null +++ b/platform/achievements.h @@ -0,0 +1,36 @@ +#pragma once +#include +#ifdef __cplusplus +extern "C" { +#endif + +// Initialize Steam (if available). Optional to call. +// If not called manually, will auto-init on first achievement set. +void achievements_init(); +void achievements_shutdown(); + +/// Set an achievement by its API name (if Steam available, otherwise ignored) +/** + * @param id The API name of the achievement to set. + * @return 0 on success, -1 steam is not running (for diagnostic only) + */ +int8_t set_achievement(const char* id); + +// Clear an achievement by its API name (if Steam available, otherwise ignored) +/** + * @param id The API name of the achievement to set. + * @return 0 on success, -1 steam is not running (for diagnostic only) + */ +int8_t clear_achievement(const char* id); + +/// returns whether steam is available +/** + * @return + */ +char is_steam_available(); + +char *get_steam_status(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/platform/sdl/sdl_context.cpp b/platform/sdl/sdl_context.cpp index 355509a..cd243e7 100644 --- a/platform/sdl/sdl_context.cpp +++ b/platform/sdl/sdl_context.cpp @@ -11,6 +11,15 @@ #include #include #include +#include +#include +#include +#include . +#include +#include +#include +#include + void SDLContext::SDL_Deleter::operator ()(SDL_Window* window) { SDL_DestroyWindow(window); }