add steam and achivements, improve console

This commit is contained in:
Ondrej Novak 2025-04-15 09:49:43 +02:00
parent 9bfb0f1d5d
commit f49a7490c1
13 changed files with 360 additions and 258 deletions

View file

@ -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)

6
external/steamworks/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
# Exclude all files
*
# Allow only get_sdk.md
!get_sdk.md
!.gitignore

20
external/steamworks/get_sdk.md vendored Normal file
View file

@ -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.

View file

@ -1,5 +1,6 @@
#include <libs/bgraph.h>
#include <libs/event.h>
#include <platform/achievements.h>
#include "globals.h"
#include <ctype.h>
@ -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;i<b;i++,p++) if (p->used && 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;i<MAX_MOBS;i++) if (~mobs[i].vlajky & MOB_LIVE && mobs[i].cislo_vzoru!=0)
{
int p;
_snprintf(buff,sizeof(buff),"%4d. %s (sector: %d home %d)",i,mobs[i].name,mobs[i].sector,mobs[i].home_pos);
kamenik2windows(buff,strlen(buff),buff);
p=ListBox_AddString(list,buff);
ListBox_SetItemData(list,p,i);
}
res=PumpDialogMessages(listdlg);
while (res==IDOK)
{
int cnt;
for (i=0,cnt=ListBox_GetCount(list);i<cnt;i++) if (ListBox_GetSel(list,i))
{
int idx=ListBox_GetItemData(list,i);
mobs[idx].vlajky|=MOB_LIVE;
mobs[idx].lives=mobs[idx].vlastnosti[VLS_MAXHIT];
wzprintf("%s znovu povstal(a)\r\n",mobs[idx].name);
SEND_LOG("(WIZARD) '%s' has been raised",mobs[idx].name,0);
}
res=PumpDialogMessages(listdlg);
}
CloseListWindow(listdlg);
return 1;
}
*/
static char advance_weapon()
{
int p,i;
char buff[128];
THUMAN *h;
if (!wzscanf("Cislo postavy: (0 = Zpet)","%d",&p)) return 0;
if (p==0) return 0;
h=postavy+p-1;
do
{
int bonus, value;
for(i=0;i<TPW_MAX;i++) wzprintf("%d. %-15s: %2d Exp %5d\r\n",i+1,texty[91+i],h->bonus_zbrani[i],h->weapon_expy[i]);
if (!wzscanf("<Zbran> <Hodnota>","%[^\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;i<POCET_POSTAV;i++,p++)
if (p->used)
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;i<POCET_POSTAV;i++,p++) {
if (p->used) {
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("<zadne>");else while(*v) wzprintf("%d ",abs(*v++)-1);
wzputs("\r\n");
if (v==NULL) wzprintf("<none>\n");else while(*v) wzprintf("%d \n",abs(*v++)-1);
for(i=0,cn=0,astr=0;i<MAX_MOBS;i++)
{
if (mobs[i].vlajky & MOB_LIVE) cn++;
if (mobs[i].vlajky & MOB_MOBILE) astr++;
}
wzprintf("Celkem potvor ve hre: %5d (+%d) astral mobiles\r\n"
"Celkem predmetu ve hre:%5d\r\n"
" .. z toho klonu: %5d\r\n",cn-astr,astr,item_count,item_count-it_count_orgn);
wzprintf("Total monsters: %5d (+%d astral)\n"
"Total items: %5d (+%d clones)\n",cn-astr,astr,item_count,item_count-it_count_orgn);
wzputs("");
ss=map_sectors+viewsector;
s=map_sides+viewsector*4+viewdir;
wzprintf("Sector: (%d) Podlaha %d Strop %d Cil akce %d Smer akce %d Akce %d\r\n",
wzprintf("Sector: (%d) Floor %d Ceil %d Action target %d Action side %d Action ID %d\r\n",
ss->sector_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?"<zadna>":"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));
}

View file

@ -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<6F>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;i<pocet;i++,z++) if (z->num==(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;
}

View file

@ -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

View file

@ -1,4 +1,5 @@
#include <platform/platform.h>
#include <platform/achievements.h>
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
@ -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)

View file

@ -209,7 +209,7 @@ Attack 4-7, Defense 1-2"
"
104,"Scales of Fate
"
105,"Potion White
105,"Potion <EFBFBD> White
"
106,"Automapping
"
@ -250,7 +250,7 @@ Gift from the Talking Tree"
"
125,"Tree Trunk
"
126,"Potion Green
126,"Potion <EFBFBD> Green
"
127,"Haste Potion
"
@ -258,14 +258,14 @@ Gift from the Talking Tree"
"
129,"Chill Potion
"
130,"Silver Jug Empty
130,"Silver Jug <EFBFBD> Empty
"
131,"Golden Jug Empty
131,"Golden Jug <EFBFBD> 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
"

1 id string
209
210
211
212
213
214
215
250
251
252
253
254
255
256
258
259
260
261
262
263
264
265
266
267
268
269
270
271
300
301
302
303
304
305
306
307
308
323
324
325
326
327
328
329
338
339
340
341
342
343
344

View file

@ -39,7 +39,7 @@ id,string
43,Cancel
44,Escape
45,Rearm
46,State
46,Wait
47,Magic
48,Start
49,Throw

1 id string
39 43 Cancel
40 44 Escape
41 45 Rearm
42 46 State Wait
43 47 Magic
44 48 Start
45 49 Throw

View file

@ -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} $<TARGET_FILE_DIR:skeldal>
)
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()

103
platform/achievements.cpp Normal file
View file

@ -0,0 +1,103 @@
#include "achievements.h"
#include <steam/steam_api.h>
#include "error.h"
#include <string.h>
#include <sstream>
extern "C" {
#include <libs/event.h>
}
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;
}

36
platform/achievements.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#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

View file

@ -11,6 +11,15 @@
#include <stdexcept>
#include <sstream>
#include <algorithm>
#include <steam/steam_api.h>
#include <stdbool.h>
#include <thread>
#include <mutex>.
#include <condition_variable>
#include <chrono>
#include <string_view>
#include <stop_token>
void SDLContext::SDL_Deleter::operator ()(SDL_Window* window) {
SDL_DestroyWindow(window);
}