mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-05 06:00:33 -04:00
a lot of changes and support languages
This commit is contained in:
parent
185a6e5382
commit
f55f92a88b
38 changed files with 1221 additions and 467 deletions
|
@ -14,6 +14,9 @@ endif()
|
|||
|
||||
include_directories(.)
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/)
|
||||
|
||||
include_directories( ${SDL2_INCLUDE_DIRS})
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -funsigned-char")
|
||||
|
@ -21,3 +24,4 @@ enable_testing()
|
|||
add_subdirectory(libs)
|
||||
add_subdirectory(platform)
|
||||
add_subdirectory(game)
|
||||
|
||||
|
|
|
@ -36,13 +36,8 @@ console.c
|
|||
gen_stringtable.c
|
||||
advconfig.c
|
||||
temp_storage.cpp
|
||||
lang.c
|
||||
${CMAKE_BINARY_DIR}/default_font.cpp
|
||||
)
|
||||
|
||||
add_executable(skeldal ${files})
|
||||
target_link_libraries(skeldal
|
||||
skeldal_libs
|
||||
skeldal_platform
|
||||
skeldal_sdl
|
||||
skeldal_libs
|
||||
${SDL2_LIBRARIES} pthread)
|
||||
add_library(skeldal_main ${files})
|
||||
|
|
|
@ -428,7 +428,16 @@ static void draw_amap_sector(int x,int y,int sector,int mode,int turn,int line1,
|
|||
{
|
||||
i=(j+turn)&3;
|
||||
if (!(q[i].flags & SD_TRANSPARENT)||(q[i].flags & SD_SECRET)) curcolor=line1;
|
||||
else if (q[i].flags & SD_PLAY_IMPS) curcolor=line2;
|
||||
else if ((q[i].flags & SD_PLAY_IMPS) && (
|
||||
true_seeing || (q[i].flags & SD_TRUESEE) == 0)) {
|
||||
int nx = ss->step_next[i];
|
||||
if (nx && !true_seeing) {
|
||||
if (map_sides[(nx*4)+((j+2)&3)].flags & SD_TRUESEE) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
curcolor=line2;
|
||||
}
|
||||
else curcolor=AUTOMAP_FORE;
|
||||
if (q[i].flags & SD_INVIS) curcolor=AUTOMAP_FORE;
|
||||
if (curcolor!=AUTOMAP_FORE)
|
||||
|
|
|
@ -828,6 +828,7 @@ static int draw_basic_sector(int celx, int cely, int sector) {
|
|||
|
||||
int tmask = true_seeing?SD_TRUESEE:0;
|
||||
|
||||
|
||||
w = &map_sides[sector * 4];
|
||||
q = &w[dirs[1]];
|
||||
obl = GET_OBLOUK(q);
|
||||
|
@ -836,13 +837,13 @@ static int draw_basic_sector(int celx, int cely, int sector) {
|
|||
show_cel2(celx, cely, ablock(num_ofsets[OBL_NUM] + obl), 0, 0, 1, ghost_walls );
|
||||
if (q->flags & SD_RIGHT_ARC && q->oblouk & 0x0f)
|
||||
show_cel2(celx, cely, ablock(num_ofsets[OBL2_NUM] + obl), 0, 0, 2, ghost_walls);
|
||||
if (q->flags & SD_PRIM_VIS && q->prim)
|
||||
if (q->flags & (SD_PRIM_VIS|tmask) && q->prim)
|
||||
show_cel2(celx, cely,
|
||||
ablock(
|
||||
num_ofsets[MAIN_NUM] + q->prim
|
||||
+ (q->prim_anim >> 4)), 0, 0,
|
||||
1 + (q->oblouk & SD_POSITION), ghost_walls | (q->flags & tmask));
|
||||
if (q->flags & SD_SEC_VIS && q->sec) {
|
||||
if (q->flags & (SD_SEC_VIS|tmask) && q->sec) {
|
||||
if (q->side_tag & SD_SHIFTUP) {
|
||||
if (cely != 0) {
|
||||
show_cel2(celx, cely - 1,
|
||||
|
@ -866,13 +867,13 @@ static int draw_basic_sector(int celx, int cely, int sector) {
|
|||
if (left_shiftup)
|
||||
show_cel(celx, cely, ablock(num_ofsets[LEFT_NUM] + left_shiftup), 0,
|
||||
0, 2, ghost_walls), left_shiftup = 0;
|
||||
if (q->flags & SD_PRIM_VIS && q->prim )
|
||||
if (q->flags & (SD_PRIM_VIS|tmask) && q->prim )
|
||||
show_cel(-celx, cely,
|
||||
ablock(
|
||||
num_ofsets[LEFT_NUM] + q->prim
|
||||
+ (q->prim_anim >> 4)), 0, 0,
|
||||
2 + (q->oblouk & SD_POSITION), ghost_walls | (q->flags & tmask));
|
||||
if (q->flags & SD_SEC_VIS && q->sec) {
|
||||
if (q->flags & (SD_SEC_VIS|tmask) && q->sec) {
|
||||
if (q->side_tag & SD_SHIFTUP) {
|
||||
if (celx != 0) {
|
||||
left_shiftup = q->sec + (q->sec_anim >> 4);
|
||||
|
@ -898,13 +899,13 @@ static int draw_basic_sector(int celx, int cely, int sector) {
|
|||
if (right_shiftup)
|
||||
show_cel(celx, cely, ablock(num_ofsets[RIGHT_NUM] + right_shiftup),
|
||||
0, 0, 3, ghost_walls), right_shiftup = 0;
|
||||
if (q->flags & SD_PRIM_VIS && q->prim )
|
||||
if (q->flags & (SD_PRIM_VIS|tmask) && q->prim )
|
||||
show_cel(celx, cely,
|
||||
ablock(
|
||||
num_ofsets[RIGHT_NUM] + q->prim
|
||||
+ (q->prim_anim >> 4)), 0, 0,
|
||||
3 + (q->oblouk & SD_POSITION), ghost_walls | (q->flags & tmask));
|
||||
if (q->flags & SD_SEC_VIS && q->sec) {
|
||||
if (q->flags & (SD_SEC_VIS|tmask) && q->sec) {
|
||||
if (q->side_tag & SD_SHIFTUP) {
|
||||
if (celx != 0)
|
||||
right_shiftup = q->sec + (q->sec_anim >> 4);
|
||||
|
@ -1009,22 +1010,22 @@ int draw_sloup_sector(int celx,int cely,int sector)
|
|||
show_cel2(celx,cely,ablock(num_ofsets[OBL_NUM]+obl),0,0,1, ghost_walls);
|
||||
if (q->flags & SD_RIGHT_ARC && q->oblouk)
|
||||
show_cel2(celx,cely,ablock(num_ofsets[OBL2_NUM]+obl),0,0,2, ghost_walls);
|
||||
if (q->flags & SD_PRIM_VIS && q->prim )
|
||||
if (q->flags & (SD_PRIM_VIS|tmask) && q->prim )
|
||||
show_cel2(celx,cely,ablock(num_ofsets[MAIN_NUM]+q->prim+(q->prim_anim>>4)),0,0,1+(q->oblouk & SD_POSITION), ghost_walls | (q->flags & tmask));
|
||||
if (celx<=0)
|
||||
{
|
||||
q=&w[dirs[0]];
|
||||
if (q->flags & SD_PRIM_VIS && q->prim)
|
||||
if (q->flags & (SD_PRIM_VIS|tmask) && q->prim)
|
||||
show_cel(-celx,cely,ablock(num_ofsets[LEFT_NUM]+q->prim+(q->prim_anim>>4)),0,0,2+(q->oblouk & SD_POSITION), ghost_walls| (q->flags & tmask));
|
||||
}
|
||||
if (celx>=0)
|
||||
{
|
||||
q=&w[dirs[2]];
|
||||
if (q->flags & SD_PRIM_VIS && q->prim)
|
||||
if (q->flags & (SD_PRIM_VIS|tmask) && q->prim)
|
||||
show_cel(celx,cely,ablock(num_ofsets[RIGHT_NUM]+q->prim+(q->prim_anim>>4)),0,0,3+(q->oblouk & SD_POSITION), ghost_walls | (q->flags & tmask));
|
||||
}
|
||||
q=&w[dirs[1]];
|
||||
if (q->flags & SD_SEC_VIS && q->sec && cely!=0) {
|
||||
if (q->flags & (SD_SEC_VIS|tmask) && q->sec && cely!=0) {
|
||||
if (q->flags & SD_SPEC)
|
||||
show_cel2(celx,cely-1,ablock(num_ofsets[MAIN_NUM]+q->sec+(q->sec_anim>>4)),0,0,2, ghost_walls| (q->flags & tmask));
|
||||
else
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <libs/pcx.h>
|
||||
#include "globals.h"
|
||||
#include <stdarg.h>
|
||||
#include "lang.h"
|
||||
|
||||
typedef struct t_paragraph
|
||||
{
|
||||
|
@ -103,6 +104,7 @@ static char code_page=1;
|
|||
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);
|
||||
|
||||
static TSTRINGTABLE *dialogy_strtable = NULL;
|
||||
|
||||
#define CLK_DIALOG 3
|
||||
static T_CLK_MAP clk_dialog[CLK_DIALOG]=
|
||||
|
@ -288,9 +290,10 @@ static void goto_paragraph(int prgf)
|
|||
while (1);
|
||||
}
|
||||
|
||||
static char *transfer_text(char *source,char *target)
|
||||
static char *transfer_text(const char *source,char *target)
|
||||
{
|
||||
char *orgn=source,*ot=target;
|
||||
const char *orgn=source;
|
||||
char *ot=target;
|
||||
int num;
|
||||
while (*source)
|
||||
{
|
||||
|
@ -357,7 +360,7 @@ static char *transfer_text(char *source,char *target)
|
|||
return target;
|
||||
}
|
||||
|
||||
static char *conv_text(char *source)
|
||||
static char *conv_text(const char *source)
|
||||
{
|
||||
if (string_buffer==NULL) string_buffer=getmem(STR_BUFF_SIZ);
|
||||
return transfer_text(source,string_buffer);
|
||||
|
@ -370,18 +373,23 @@ static char zjisti_typ()
|
|||
|
||||
static char *Get_string()
|
||||
{
|
||||
const char *start = (const char *)ablock(H_DIALOGY_DAT);
|
||||
char *c,i;
|
||||
if (*pc==P_STRING)
|
||||
{
|
||||
int ofs = pc - start+1;
|
||||
pc++;
|
||||
c=conv_text(pc);
|
||||
const char *txt = stringtable_find(dialogy_strtable,ofs, pc);
|
||||
c=conv_text(txt);
|
||||
do
|
||||
{
|
||||
pc+=strlen(pc)+1;
|
||||
ofs = pc - start;
|
||||
if ((i=zjisti_typ())==P_STRING)
|
||||
{
|
||||
const char *txt = stringtable_find(dialogy_strtable,ofs, pc);
|
||||
pc++;
|
||||
c=transfer_text(pc,c);
|
||||
c=transfer_text(txt,c);
|
||||
}
|
||||
}
|
||||
while(i==P_STRING);
|
||||
|
@ -1222,12 +1230,22 @@ static void cast_spell(int spell)
|
|||
add_spell(spell,cil,cil,1);
|
||||
}
|
||||
|
||||
static void free_dialog_stringtable() {
|
||||
stringtable_free(dialogy_strtable);
|
||||
}
|
||||
|
||||
void do_dialog()
|
||||
{
|
||||
int i,p1,p2,p3;
|
||||
char *c;
|
||||
|
||||
if (!dialogy_strtable) {
|
||||
dialogy_strtable = lang_load("dialogs.csv");
|
||||
if (dialogy_strtable) {
|
||||
atexit(free_dialog_stringtable);
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
i=Get_short();p3=0;
|
||||
|
|
|
@ -36,8 +36,8 @@ short zooming_points[ZOOM_PHASES][4]
|
|||
{480,271,80,28},
|
||||
{460,259,90,31}
|
||||
};
|
||||
int zooming_step=1;
|
||||
int rot_phases=1;
|
||||
int zooming_step=2;
|
||||
int rot_phases=2;
|
||||
int yreq;
|
||||
int last_scale;
|
||||
char secnd_shade=1;
|
||||
|
|
|
@ -114,7 +114,7 @@ static __inline int rangrnd(int a, int b) {return rnd(b-a+1)+a;}
|
|||
#define COL_RAMEC RGB555(31,31,26) //((31*32+31)*32+26)
|
||||
|
||||
#undef RGB
|
||||
#define RGB(r,g,b) (((r)>>3)*2048+((g)>>3)*64+((b)>>3))
|
||||
#define RGB(r,g,b) RGB888(r,g,b)
|
||||
#define GET_R_COLOR(col) ((col & 0xF800)>>8)
|
||||
#define GET_G_COLOR(col) ((col & 0x07E0)>>3)
|
||||
#define GET_B_COLOR(col) ((col & 0x001F)<<3)
|
||||
|
@ -342,6 +342,7 @@ typedef enum skeldal_folders_tag {
|
|||
SR_DIALOGS,
|
||||
SR_SAVES,
|
||||
SR_WORK,
|
||||
SR_LANG,
|
||||
|
||||
|
||||
SR_COUNT} SKELDAL_FOLDERS_TAG;
|
||||
|
@ -597,7 +598,7 @@ extern char set_halucination;
|
|||
extern int hal_sector; //cislo sektoru a smeru pri halucinaci
|
||||
extern int hal_dir;
|
||||
extern char side_touched; //promena se nastavuje na 1 pri kazdem uspesnem dotyku steny
|
||||
extern char *texty_knihy; //jmeno souboru s textamy knihy
|
||||
extern const char *texty_knihy; //jmeno souboru s textamy knihy
|
||||
extern int cur_page; //cislo stranky v knize;
|
||||
extern int32_t game_time; //hraci cas
|
||||
extern char autoattack;
|
||||
|
@ -1049,9 +1050,15 @@ typedef struct tshop
|
|||
const TPRODUCT *list;
|
||||
}TSHOP;
|
||||
|
||||
|
||||
typedef struct tshop_product_state_tag {
|
||||
uint16_t count;
|
||||
uint16_t previous_price;
|
||||
} TSHOP_PRODUCT_STATE;
|
||||
|
||||
typedef struct tshop_all_state {
|
||||
const TPRODUCT *first_product;
|
||||
int32_t *first_state;
|
||||
TSHOP_PRODUCT_STATE *first_state;
|
||||
size_t count_states;
|
||||
} TSHOP_ALL_STATE;
|
||||
|
||||
|
@ -1674,7 +1681,7 @@ void add_window(int x,int y,int xs,int ys,int texture,int border,int txtx,int tx
|
|||
int message(int butts,char def,char canc,char *keys,...);
|
||||
void type_text(EVENT_MSG *msg,void **data); //event procedura (parms: X,Y,TEXT,MAX_SPACE,MAX_CHARS);
|
||||
void type_text_v2(va_list args);//char *text_buffer,int x,int y,int max_size,int max_chars,int font,int color,void (*exit_proc)(char));
|
||||
void zalamovani(char *source,char *target,int maxxs,int *xs,int *ys);
|
||||
void zalamovani(const char *source,char *target,int maxxs,int *xs,int *ys);
|
||||
const void *col_load(const void *data, int32_t *size);
|
||||
void open_story_file(void);
|
||||
void write_story_text(char *text);
|
||||
|
@ -1701,9 +1708,15 @@ TMPFILE_RD *enc_open(const char *filename); //dekoduje a otevira TXT soubor (ENC
|
|||
void enc_close(TMPFILE_RD *fil);
|
||||
int load_string_list_ex(char ***list,const char *filename);
|
||||
|
||||
typedef struct {
|
||||
int hprice;
|
||||
const char *message;
|
||||
char canceled;
|
||||
} THAGGLERESULT;
|
||||
|
||||
int smlouvat_nakup(int cena,int ponuka,int posledni,int puvod,int pocet);
|
||||
int smlouvat_prodej(int cena,int ponuka,int posledni,int puvod,int pocet);
|
||||
int smlouvat(int cena,int puvod,int pocet,int money,char mode);
|
||||
THAGGLERESULT smlouvat_dlg(int cena,int puvod,int pocet,int posledni, int money,char mode);
|
||||
|
||||
void disable_intro(void);
|
||||
void show_jrc_logo(char *filename);
|
||||
|
|
|
@ -248,7 +248,7 @@ static char test_kriterii(void)
|
|||
break;
|
||||
default:
|
||||
{
|
||||
hodn=temp_storage_find(text)>=0;
|
||||
hodn=temp_storage_find(concat2(text,".map"))>=0;
|
||||
/* char c[200];
|
||||
sprintf(c,"%s.TMP",text);
|
||||
hodn=!check_file_exists(c);*/
|
||||
|
@ -356,6 +356,7 @@ static void preskoc_prikaz(void)
|
|||
switch (ODD)
|
||||
{
|
||||
case 0:cti_retezec(1,&text,0,0);ending=1;break;
|
||||
case '\r':continue;
|
||||
case '\n':if (ending && uroven==0) return;break;
|
||||
case EOF: if (uroven!=0)ex_error(OD_OUT);return;break;
|
||||
case '{': if (last==OD_CRIT || last==OD_NEWLINE) uroven++;break;
|
||||
|
@ -422,7 +423,7 @@ static char flp_validate(word sector, void *ctx)
|
|||
int *found_place = (int *)ctx;
|
||||
char c;
|
||||
|
||||
if (found_place) return 0;
|
||||
if (*found_place) return 0;
|
||||
if (mob_map[sector])
|
||||
{
|
||||
m=mobs+mob_map[sector]-1;
|
||||
|
@ -485,7 +486,7 @@ static char load_index_map(int index)
|
|||
if (!GlobEvent(MAGLOB_LEAVEMAP,viewsector,viewdir)) return 0;
|
||||
viewsector=lv;
|
||||
strncpy(x.name,index_tab[index].mapname,12);
|
||||
x.start_pos=lv;
|
||||
x.start_pos=0;
|
||||
x.dir=0;
|
||||
macro_load_another_map(&x);
|
||||
return 0;
|
||||
|
|
|
@ -131,7 +131,7 @@ void add_window(int x,int y,int xs,int ys,int texture,int border,int txtx,int tx
|
|||
}
|
||||
|
||||
|
||||
void zalamovani(char *source,char *target,int maxxs,int *xs,int *ys)
|
||||
void zalamovani(const char *source,char *target,int maxxs,int *xs,int *ys)
|
||||
{
|
||||
strcpy(target,source);
|
||||
xs[0]=0;
|
||||
|
@ -1354,11 +1354,11 @@ TMPFILE_RD *enc_open(const char *filename)
|
|||
if (f==NULL) return NULL;
|
||||
encdata = load_file_to_string(f, &size);
|
||||
fclose(f);
|
||||
}
|
||||
for (int i = 0; i < size; ++i) {
|
||||
last = (last + encdata[i]) & 0xFF;
|
||||
encdata[i] = last;
|
||||
}
|
||||
}
|
||||
temp_storage_store("__enc_temp", encdata, size);
|
||||
free(encdata);
|
||||
return temp_storage_open("__enc_temp");
|
||||
|
@ -1478,14 +1478,12 @@ static void smlouvat_enter(EVENT_MSG *msg,OBJREC *o)
|
|||
}
|
||||
}
|
||||
|
||||
int smlouvat(int cena,int puvod,int pocet,int money,char mode)
|
||||
THAGGLERESULT smlouvat_dlg(int cena,int puvod,int pocet,int posledni, int money,char mode)
|
||||
{
|
||||
int ponuka=0,posledni=0;
|
||||
char text[255],*c,buffer[20];
|
||||
int y,yu,xu;
|
||||
int temp1,temp2;
|
||||
char buffer[20];
|
||||
int ponuka;
|
||||
THAGGLERESULT res;
|
||||
|
||||
cena,puvod,pocet,money;text[0]=0;text[1]=0;
|
||||
set_font(H_FBOLD,RGB555(31,31,31));
|
||||
add_window(170,130,300,100,H_WINTXTR,3,20,20);
|
||||
define(-1,10,15,1,1,0,label,texty[241]);
|
||||
|
@ -1496,46 +1494,45 @@ int smlouvat(int cena,int puvod,int pocet,int money,char mode)
|
|||
on_control_event(smlouvat_enter);
|
||||
define(20,20,20,80,20,2,button,texty[239]);property(def_border(5,BAR_COLOR),NULL,NULL,BAR_COLOR);on_control_change(terminate_gui);
|
||||
define(30,110,20,80,20,2,button,texty[230]);property(def_border(5,BAR_COLOR),NULL,NULL,BAR_COLOR);on_control_change(terminate_gui);
|
||||
do
|
||||
{
|
||||
redraw_window();
|
||||
schovej_mysku();set_font(H_FBOLD,RGB555(31,31,31));
|
||||
c=text;yu=y=waktual->y+50;xu=waktual->x+10;
|
||||
do {position(xu,y);outtext(c);y+=text_height(c)+1;c=strchr(c,0)+1;} while(*c);
|
||||
ukaz_mysku();
|
||||
showview(xu,yu,280,y-yu);
|
||||
goto_control(10);
|
||||
escape();
|
||||
temp1=1;
|
||||
if (o_aktual->id==20) cena=-1;
|
||||
else
|
||||
res.message = 0;
|
||||
res.hprice = posledni;
|
||||
res.canceled = 1;
|
||||
if (o_aktual->id!=20)
|
||||
{
|
||||
res.canceled = 0;
|
||||
get_value(0,10,buffer);
|
||||
if (buffer[0]==0) c=texty[240];
|
||||
if (buffer[0]==0) res.message=texty[240];
|
||||
else
|
||||
{
|
||||
if (sscanf(buffer,"%d",&ponuka)!=1) c=texty[237];
|
||||
if (sscanf(buffer,"%d",&ponuka)!=1) res.message=texty[237];
|
||||
else
|
||||
{
|
||||
if (ponuka>money && mode==1) c=texty[104];
|
||||
if (ponuka>money && mode==1) {
|
||||
message(1, 0, 0, texty[100], texty[104], texty[78]);
|
||||
res.canceled = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int temp1;
|
||||
if (mode) temp1=smlouvat_nakup(cena,ponuka,posledni,puvod,pocet);
|
||||
else temp1=smlouvat_prodej(cena,ponuka,posledni,puvod,pocet+1);
|
||||
posledni=ponuka;
|
||||
if (rnd(100)<50) c=texty[230+temp1];else c=texty[250+temp1];
|
||||
res.hprice=ponuka;
|
||||
if (temp1) {
|
||||
if (rnd(100)<50) res.message=texty[230+temp1];else res.message=texty[250+temp1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shadow_enabled=0;
|
||||
}
|
||||
if (c) zalamovani(c,text,280,&temp2,&temp2);
|
||||
}
|
||||
while (temp1!=0 && cena!=-1);
|
||||
if (temp1==0) cena=ponuka;
|
||||
close_current();
|
||||
shadow_enabled=1;
|
||||
return cena;
|
||||
return res;
|
||||
}
|
||||
|
||||
//----------------- JRC LOGO ----------------------------------
|
||||
|
|
136
game/inv.c
136
game/inv.c
|
@ -18,7 +18,9 @@
|
|||
#include <libs/pcx.h>
|
||||
#include "globals.h"
|
||||
|
||||
#include "lang.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -224,6 +226,36 @@ void load_items()
|
|||
}
|
||||
}
|
||||
|
||||
if (lang_get_folder()) {
|
||||
TSTRINGTABLE *str_table = lang_load("items.csv");
|
||||
if (str_table) {
|
||||
for (int i = 0; i < item_count; ++i) {
|
||||
const char *trn = stringtable_find(str_table, i, NULL);
|
||||
if (trn) {
|
||||
char *trnw = local_strdup(trn);
|
||||
char *sep = strchr(trnw, '\n');
|
||||
if (sep != NULL) {
|
||||
*sep = 0;
|
||||
char *nx = sep+1;
|
||||
--sep;
|
||||
while (sep > trnw && isspace(*sep)) {
|
||||
*sep = 0;
|
||||
--sep;
|
||||
}
|
||||
sep = strchr(nx,0);
|
||||
--sep;
|
||||
while (sep > nx && isspace(*sep)) {
|
||||
*sep = 0;
|
||||
--sep;
|
||||
}
|
||||
strncpy(glob_items[i].popis, nx, sizeof(glob_items[i].popis)-1);
|
||||
}
|
||||
strncpy(glob_items[i].jmeno, trnw, sizeof(glob_items[i].jmeno)-1);
|
||||
}
|
||||
}
|
||||
stringtable_free(str_table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_items()
|
||||
|
@ -2480,8 +2512,8 @@ static void rebuild_shops(const void *shop_ptr)
|
|||
TPRODUCT *prod_iter = (TPRODUCT *)(shop_iter+max_shops);
|
||||
shop_all_state.first_product = prod_iter;
|
||||
shop_all_state.count_states = products;
|
||||
shop_all_state.first_state = (int32_t *)(prod_iter+products);
|
||||
int32_t *state_iter = shop_all_state.first_state;
|
||||
shop_all_state.first_state = (TSHOP_PRODUCT_STATE *)(prod_iter+products);
|
||||
TSHOP_PRODUCT_STATE *state_iter = shop_all_state.first_state;
|
||||
|
||||
for(i=0;i<max_shops;i++) {
|
||||
shop_list[i] = shop_iter;
|
||||
|
@ -2489,7 +2521,8 @@ static void rebuild_shops(const void *shop_ptr)
|
|||
shop_iter->list = prod_iter;
|
||||
for (int j = 0; j < shop_iter->products; ++j) {
|
||||
c = load_TPRODUCT(c, prod_iter);
|
||||
*state_iter += prod_iter->pocet;
|
||||
state_iter->count = prod_iter->pocet;
|
||||
state_iter->previous_price = 0;
|
||||
++prod_iter;
|
||||
++state_iter;
|
||||
}
|
||||
|
@ -2515,10 +2548,16 @@ void load_shops(void)
|
|||
ablock_free(sh);
|
||||
}
|
||||
|
||||
static int32_t *get_product_count(const TPRODUCT *p) {
|
||||
static uint16_t *get_product_count(const TPRODUCT *p) {
|
||||
int32_t index = p - shop_all_state.first_product;
|
||||
assert(index >= 0 && index < (int32_t)shop_all_state.count_states);
|
||||
return shop_all_state.first_state + index;
|
||||
return &shop_all_state.first_state[index].count;
|
||||
}
|
||||
|
||||
static uint16_t *get_last_haggle_price(const TPRODUCT *p) {
|
||||
int32_t index = p - shop_all_state.first_product;
|
||||
assert(index >= 0 && index < (int32_t)shop_all_state.count_states);
|
||||
return &shop_all_state.first_state[index].previous_price;
|
||||
}
|
||||
|
||||
static void rebuild_keepers_items()
|
||||
|
@ -2634,6 +2673,26 @@ static void display_keepers_items()
|
|||
outtext(cur_shop->keeper);
|
||||
}
|
||||
|
||||
static const char *shop_keeper_bubble=NULL;
|
||||
|
||||
static void show_buble(int x, int y, int xs, const char *text) {
|
||||
set_font(H_FTINY, NOSHADOW(0));
|
||||
int newxs;
|
||||
int newys;
|
||||
char *buffer = (char *)alloca(strlen(text)+3);
|
||||
zalamovani(text, buffer, xs-10, &newxs, &newys);
|
||||
int lxs = newxs+10;
|
||||
int lys = newys+10;
|
||||
y-=newys+10;
|
||||
x+=(xs-lxs)/2;
|
||||
draw_rounded_rectangle(x,y,lxs, lys, 8,RGB888(0,0,0),RGB888(255,255,255));
|
||||
while (*buffer) {
|
||||
position(x+5,y+5);outtext(buffer);
|
||||
y+= text_height(buffer);
|
||||
buffer = strchr(buffer,0)+1;
|
||||
}
|
||||
}
|
||||
|
||||
static void redraw_shop()
|
||||
{
|
||||
update_mysky();
|
||||
|
@ -2648,6 +2707,11 @@ static void redraw_shop()
|
|||
info_box_below=NULL;
|
||||
if (shop_keeper_picture) put_picture(5,SCREEN_OFFLINE,shop_keeper_picture);
|
||||
ms_last_event.event_type=0x1;send_message(E_MOUSE,&ms_last_event);
|
||||
if (shop_keeper_bubble) {
|
||||
show_buble(5,SCREEN_OFFLINE+((word *)shop_keeper_picture)[1],
|
||||
((word *)shop_keeper_picture)[0], shop_keeper_bubble);
|
||||
}
|
||||
shop_keeper_bubble=NULL;
|
||||
ukaz_mysku();
|
||||
showview(0,0,0,0);
|
||||
}
|
||||
|
@ -2770,10 +2834,20 @@ char shop_keeper_click(int id, int xa, int ya, int xr, int yr) {
|
|||
}
|
||||
if (p == 1) {
|
||||
redraw_shop();
|
||||
price = smlouvat(price, pp->cena, *get_product_count(pp), money, 0);
|
||||
|
||||
THAGGLERESULT hr = smlouvat_dlg(price, pp->cena,
|
||||
*get_product_count(pp), *get_last_haggle_price(pp), money, 0);
|
||||
if (hr.canceled) {
|
||||
price = -1;
|
||||
} else if (hr.message) {
|
||||
price = -1;
|
||||
shop_keeper_bubble = hr.message;
|
||||
*get_last_haggle_price(pp) = hr.hprice;
|
||||
} else {
|
||||
price = hr.hprice;
|
||||
}
|
||||
}
|
||||
if (price >= 0) {
|
||||
*get_last_haggle_price(pp) = 0;
|
||||
play_sample_at_channel(H_SND_OBCHOD, 1, 100);
|
||||
buy_item(z);
|
||||
free(picked_item);
|
||||
|
@ -2826,23 +2900,27 @@ char shop_bag_click(int id,int xa,int ya,int xr,int yr)
|
|||
mouse_set_cursor(H_MS_DEFAULT);
|
||||
if (!price) return 0;
|
||||
if (price > money) {
|
||||
p = message(2, 0, 0, "", texty[104], texty[230], texty[78]);
|
||||
if (!p) {
|
||||
redraw_shop();
|
||||
price = smlouvat(price, pp->cena, *get_product_count(pp), money, 1);
|
||||
} else {
|
||||
price = -1;
|
||||
}
|
||||
p = message(2, 0, 0, "", texty[104], texty[230], texty[78])+1;
|
||||
} else {
|
||||
sprintf(s, texty[101], price);
|
||||
p = message(3, 0, 1, texty[118], s, texty[77], texty[230], texty[78]);
|
||||
}
|
||||
if (p == 1) {
|
||||
redraw_shop();
|
||||
price = smlouvat(price, pp->cena, *get_product_count(pp), money, 1);
|
||||
THAGGLERESULT hr = smlouvat_dlg(price, pp->cena,
|
||||
*get_product_count(pp), *get_last_haggle_price(pp), money, 1);
|
||||
if (hr.canceled) {
|
||||
price = -1;
|
||||
}else if (hr.message) {
|
||||
price = -1;
|
||||
shop_keeper_bubble = hr.message;
|
||||
*get_last_haggle_price(pp) = hr.hprice;
|
||||
} else {
|
||||
price = hr.hprice;
|
||||
}
|
||||
} else if (p == 2) {
|
||||
price = -1;
|
||||
}
|
||||
}
|
||||
if (price>=0)
|
||||
{
|
||||
play_sample_at_channel(H_SND_OBCHOD,1,100);
|
||||
|
@ -2869,12 +2947,23 @@ char shop_block_click(int id, int xa, int ya,int xr,int yr)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void shop_keyboard_proc(EVENT_MSG *msg, void **_) {
|
||||
if (msg->msg == E_KEYBOARD) {
|
||||
int c = quit_request_as_escape(va_arg(msg->data,int));
|
||||
switch(c>>8) {
|
||||
case 1: _exit_shop(0,0,0,0,0);break;
|
||||
default:break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int old_inv_view_mode;
|
||||
|
||||
void unwire_shop()
|
||||
{
|
||||
send_message(E_DONE,E_MOUSE,shop_mouse_event);
|
||||
send_message(E_DONE,E_KEYBOARD, shop_keyboard_proc);
|
||||
norefresh=0;
|
||||
wire_proc=wire_shop;
|
||||
inv_view_mode=old_inv_view_mode;
|
||||
|
@ -2896,6 +2985,7 @@ void wire_shop()
|
|||
last_shop=cur_shop;
|
||||
}
|
||||
send_message(E_ADD,E_MOUSE,shop_mouse_event);
|
||||
send_message(E_ADD,E_KEYBOARD, shop_keyboard_proc);
|
||||
unwire_proc=unwire_shop;
|
||||
change_click_map(clk_shop,CLK_SHOP);
|
||||
if (shop_sector==viewsector) redraw_shop();else _exit_shop(0,0,0,0,0);
|
||||
|
@ -3008,10 +3098,14 @@ static void reroll_shop(TSHOP *p)
|
|||
pr=p->list;
|
||||
for(i=0;i<p->list_size;i++,pr++)
|
||||
{
|
||||
if (pr->trade_flags & SHP_AUTOADD && *get_product_count(pr)<pr->max_pocet) (*get_product_count(pr))++;
|
||||
uint16_t *count = get_product_count(pr);
|
||||
if (pr->trade_flags & SHP_AUTOADD) (*count)++;
|
||||
if ((pr->trade_flags & SHP_SELL) == 0) *count = 0;
|
||||
if (*count > pr->max_pocet) *count = pr->max_pocet;
|
||||
if (pr->trade_flags & SHP_SPECIAL)
|
||||
{
|
||||
poc_spec++;if (*get_product_count(pr)>0) *get_product_count(pr)=0;
|
||||
poc_spec++;
|
||||
*count = 0;
|
||||
}
|
||||
}
|
||||
pr=p->list;
|
||||
|
@ -3022,8 +3116,12 @@ static void reroll_shop(TSHOP *p)
|
|||
for(j=0;i<r;j++) if (pr[j].trade_flags & SHP_SPECIAL) i++;
|
||||
j--;
|
||||
const TPRODUCT *sel = pr+j;
|
||||
int maxp = MAX(sel->max_pocet,1);
|
||||
int maxp = sel->max_pocet;
|
||||
if (maxp) {
|
||||
*get_product_count(pr+j)=rnd(maxp)+1;
|
||||
} else {
|
||||
*get_product_count(pr+j) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -498,6 +498,7 @@ static void seek_section(TMPFILE_RD *txt,int sect_number)
|
|||
do
|
||||
{
|
||||
while (c!='[' && c!=EOF) c=temp_storage_getc(txt);
|
||||
if (c == EOF) break;
|
||||
if (c=='[')
|
||||
{
|
||||
i=-2;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdarg.h>
|
||||
#include "engine1.h"
|
||||
#include "globals.h"
|
||||
#include "lang.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -187,6 +188,7 @@ static void animace_kouzla(MGIF_HEADER_T *_,int act,const void *data, int ssize)
|
|||
|
||||
|
||||
const void *load_spells_legacy_format(const void *p, int32_t *s) {
|
||||
TSTRINGTABLE *strtable = lang_load("spells.csv");
|
||||
void *np = getmem(*s);
|
||||
memcpy(np,p,*s);
|
||||
TKOUZLO *k = (np);
|
||||
|
@ -208,8 +210,13 @@ const void *load_spells_legacy_format(const void *p, int32_t *s) {
|
|||
size_t eofs = offsetof(TKOUZLO, spellname)-1;
|
||||
memmove(b+bofs+1, b+bofs, eofs-bofs);\
|
||||
k->traceon = traceon;
|
||||
const char *new_name = stringtable_find(strtable, i, NULL);
|
||||
if (new_name) {
|
||||
strncpy(k->spellname,new_name,sizeof(k->spellname)-1);
|
||||
}
|
||||
++k;
|
||||
}
|
||||
stringtable_free(strtable);
|
||||
return np;
|
||||
}
|
||||
|
||||
|
|
49
game/lang.c
Normal file
49
game/lang.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
#include <platform/platform.h>
|
||||
#include <libs/event.h>
|
||||
#include "globals.h"
|
||||
#include "lang.h"
|
||||
#include <libs/strlite.h>
|
||||
|
||||
|
||||
static char *lang_folder = NULL;
|
||||
|
||||
static void free_lang_folder(void) {
|
||||
free(lang_folder);
|
||||
}
|
||||
|
||||
const char *lang_get_folder(void) {
|
||||
return lang_folder;
|
||||
}
|
||||
|
||||
void lang_set_folder(const char *path) {
|
||||
if (lang_folder == NULL) atexit(free_lang_folder);
|
||||
lang_folder = strdup(path);
|
||||
}
|
||||
void lang_patch_stringtable(TSTR_LIST *lst, const char *object_name, const char *prefix) {
|
||||
if (lang_folder == NULL) return;
|
||||
const char *fname = set_file_extension(object_name, ".csv");
|
||||
fname = concat2(prefix, fname);
|
||||
const char *path = build_pathname(2, lang_folder, fname);
|
||||
TSTRINGTABLE *st = stringtable_load(path);
|
||||
if (!st) return;
|
||||
for (int i = 0, cnt = str_count(*lst); i<cnt; ++i) {
|
||||
const char *newstr = stringtable_find(st, i, NULL);
|
||||
if (newstr) str_replace(lst, i, newstr);
|
||||
}
|
||||
stringtable_free(st);
|
||||
|
||||
}
|
||||
TSTRINGTABLE *lang_load(const char *object_name) {
|
||||
if (lang_folder == NULL) return NULL;
|
||||
const char *fname = set_file_extension(object_name, ".csv");
|
||||
const char *path = build_pathname(2, lang_folder, fname);
|
||||
TSTRINGTABLE *st = stringtable_load(path);
|
||||
return st;
|
||||
}
|
||||
|
||||
const char *lang_replace_path_if_exists(const char *file) {
|
||||
if (lang_folder == NULL) return NULL;
|
||||
const char *path = build_pathname(2, lang_folder, file);
|
||||
if (check_file_exists(path)) return path;
|
||||
return NULL;
|
||||
}
|
12
game/lang.h
Normal file
12
game/lang.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <libs/strlite.h>
|
||||
#include <libs/string_table.h>
|
||||
|
||||
void lang_set_folder(const char *path);
|
||||
const char *lang_get_folder(void);
|
||||
void lang_patch_stringtable(TSTR_LIST *lst, const char *object_name, const char *prefix);
|
||||
const char *lang_replace_path_if_exists(const char *file);
|
||||
TSTRINGTABLE *lang_load(const char *object_name);
|
||||
|
20
game/menu.c
20
game/menu.c
|
@ -18,6 +18,7 @@
|
|||
#include <libs/pcx.h>
|
||||
#include "globals.h"
|
||||
|
||||
#include "lang.h"
|
||||
|
||||
#define MUSIC "TRACK06.MUS"
|
||||
|
||||
|
@ -366,7 +367,13 @@ int enter_menu(char open)
|
|||
return c;
|
||||
}
|
||||
|
||||
char *get_next_title(signed char control,char *filename)
|
||||
static const char *end_titles_path(const char *fname) {
|
||||
if (stricmp(fname,"TITULKY.TXT") == 0) fname = "end_titles.txt";
|
||||
else if (stricmp(fname,"ENDTEXT.TXT") == 0) fname = "epilog.txt";
|
||||
return lang_replace_path_if_exists(fname);
|
||||
}
|
||||
|
||||
char *get_next_title(signed char control,const char *filename)
|
||||
{
|
||||
|
||||
static TMPFILE_RD *titles=NULL;
|
||||
|
@ -377,7 +384,10 @@ char *get_next_title(signed char control,char *filename)
|
|||
switch(control)
|
||||
{
|
||||
case 1:
|
||||
path = end_titles_path(filename);
|
||||
if (path == NULL) {
|
||||
path = build_pathname(2, gpathtable[SR_MAP],filename);
|
||||
}
|
||||
path = local_strdup(path);
|
||||
titles=enc_open(path);
|
||||
if (titles==NULL)
|
||||
|
@ -395,8 +405,12 @@ char *get_next_title(signed char control,char *filename)
|
|||
}
|
||||
}
|
||||
return (char *)titles;
|
||||
case 0:if (titles!=NULL)temp_storage_gets(buffer,80,titles);
|
||||
case 0:if (titles!=NULL && temp_storage_gets(buffer,80,titles)) {
|
||||
c=strchr(buffer,'\n');if (c!=NULL) *c=0;
|
||||
c=strchr(buffer,'\r');if (c!=NULL) *c=0;
|
||||
} else {
|
||||
strcpy(buffer, "*KONEC");
|
||||
}
|
||||
return buffer;
|
||||
case -1:if (titles!=NULL)enc_close(titles);
|
||||
break;
|
||||
|
@ -517,7 +531,7 @@ void titles(va_list args)
|
|||
{
|
||||
int32_t scr_linelen2 = GetScreenPitch();
|
||||
char send_back=va_arg(args,int);
|
||||
char *textname=va_arg(args,char *);
|
||||
const char *textname=va_arg(args,const char *);
|
||||
|
||||
const void *picture;
|
||||
word *scr,*buff;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "globals.h"
|
||||
#include <libs/inicfg.h>
|
||||
|
||||
#include "lang.h"
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -197,7 +198,7 @@ int load_map(char *filename)
|
|||
char snd_load=0;
|
||||
void *mob_template;
|
||||
int32_t mob_size;
|
||||
int suc = 0;
|
||||
int failed = 0;
|
||||
|
||||
map_with_password=0;
|
||||
const char *mpath = build_pathname(2, gpathtable[SR_MAP], filename);
|
||||
|
@ -344,8 +345,11 @@ int load_map(char *filename)
|
|||
memset(minimap,0,sizeof(minimap));
|
||||
end_ptr=ofsts;
|
||||
const char *tpath=set_file_extension(mpath,".txt");
|
||||
suc=load_level_texts(tpath);
|
||||
if (!suc && level_texts!=NULL) create_playlist(level_texts[0]);
|
||||
failed=load_level_texts(tpath);
|
||||
if (!failed && level_texts!=NULL) {
|
||||
lang_patch_stringtable(&level_texts, filename, "map_");
|
||||
create_playlist(level_texts[0]);
|
||||
}
|
||||
init_tracks();
|
||||
change_music(get_next_music_from_playlist());
|
||||
for(r=0;r<mapsize*4;r++) flag_map[r]=(char)map_sides[r].flags;
|
||||
|
@ -359,7 +363,7 @@ int load_map(char *filename)
|
|||
current_map_hash = fnv1a_hash(filename);
|
||||
const char * hash_str = map_hash_to_string(current_map_hash);
|
||||
temp_storage_store(hash_str, filename, strlen(filename));
|
||||
return suc;
|
||||
return failed;
|
||||
}
|
||||
|
||||
void add_leaving_place(int sector)
|
||||
|
@ -1272,11 +1276,11 @@ void group_all(void)
|
|||
{
|
||||
if (cur_group!=1)
|
||||
{
|
||||
for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (h->used && h->groupnum==1 && h->sektor!=viewsector && h->inmaphash == current_map_hash) break;
|
||||
for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (h->used && h->groupnum==1 && h->sektor!=viewsector) break;
|
||||
if (i==POCET_POSTAV) cur_group=1;
|
||||
}
|
||||
for(i=0,h=postavy;i<POCET_POSTAV;i++,h++)
|
||||
if (h->used && h->lives && h->sektor==viewsector && h->inmaphash == current_map_hash) h->groupnum=cur_group;
|
||||
if (h->used && h->lives && h->sektor==viewsector) h->groupnum=cur_group;
|
||||
}
|
||||
|
||||
bott_draw(0);
|
||||
|
@ -1570,7 +1574,7 @@ void step_zoom(char smer)
|
|||
int i;
|
||||
THUMAN *h;
|
||||
group_all();can_go=1;
|
||||
for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (h->groupnum!=cur_group && h->lives) break;
|
||||
for(i=0,h=postavy;i<POCET_POSTAV;i++,h++) if (h->used && h->inmaphash == current_map_hash && h->groupnum!=cur_group && h->lives) break;
|
||||
if (i!=POCET_POSTAV)
|
||||
{
|
||||
bott_disp_text(texty[66]);
|
||||
|
|
108
game/skeldal.c
108
game/skeldal.c
|
@ -14,12 +14,13 @@
|
|||
#include <libs/basicobj.h>
|
||||
#include <libs/mgfplay.h>
|
||||
#include <libs/inicfg.h>
|
||||
#include <platform/getopt.h>
|
||||
#include <platform/save_folder.h>
|
||||
#include "globals.h"
|
||||
#include "default_font.h"
|
||||
//
|
||||
#include "advconfig.h"
|
||||
#include "skeldal.h"
|
||||
#include "lang.h"
|
||||
|
||||
#define CONFIG_NAME SKELDALINI
|
||||
|
||||
|
@ -95,7 +96,7 @@ const void *pcx_15bit_autofade(const void *p, int32_t *s);
|
|||
const void *pcx_15bit_backgrnd(const void *p, int32_t *s);
|
||||
const void *pcx_8bit_decomp(const void *p, int32_t *s);
|
||||
|
||||
char *texty_knihy;
|
||||
const char *texty_knihy;
|
||||
static char *patch_file=NULL;
|
||||
int cur_page=0;
|
||||
|
||||
|
@ -793,9 +794,13 @@ void cti_texty(void)
|
|||
display_error(buff);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
lang_patch_stringtable(&texty, "ui.csv", "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void global_kbd(EVENT_MSG *msg,void **usr)
|
||||
{
|
||||
char c;
|
||||
|
@ -947,7 +952,16 @@ void init_skeldal(const INI_CONFIG *cfg)
|
|||
|
||||
init_DDL_manager();
|
||||
|
||||
if (lang_get_folder()) {
|
||||
texty_knihy = build_pathname(2, lang_get_folder(), "book.txt");
|
||||
if (!check_file_exists(texty_knihy)) {
|
||||
texty_knihy=strdup(build_pathname(2,gpathtable[SR_MAP],"kniha.txt"));
|
||||
} else {
|
||||
texty_knihy=strdup(texty_knihy);
|
||||
}
|
||||
} else {
|
||||
texty_knihy=strdup(build_pathname(2,gpathtable[SR_MAP],"kniha.txt"));
|
||||
}
|
||||
|
||||
install_gui();
|
||||
|
||||
|
@ -1576,32 +1590,22 @@ const char *configure_pathtable(const INI_CONFIG *cfg) {
|
|||
gpathtable[SR_VIDEO] = ini_get_string(paths, "video", "video");
|
||||
gpathtable[SR_SAVES] = ini_get_string(paths, "savegame", get_default_savegame_directory());
|
||||
gpathtable[SR_DATA]= ini_get_string(paths, "data", "./");
|
||||
gpathtable[SR_LANG]= ini_get_string(paths, "lang", "./lang");
|
||||
|
||||
|
||||
return groot;
|
||||
}
|
||||
|
||||
|
||||
void show_help(const char *arg0) {
|
||||
printf(
|
||||
"Brany Skeldalu (Gates of Skeldal) portable game player\n"
|
||||
"Copyright (c) 2025 Ondrej Novak. All rights reserved.\n\n"
|
||||
"This work is licensed under the terms of the MIT license.\n"
|
||||
"For a copy, see <https://opensource.org/licenses/MIT>.\n"
|
||||
"\n"
|
||||
"Usage:"
|
||||
);
|
||||
printf("%s [-f <file>] [-a <file>] [-h]\n\n", arg0);
|
||||
|
||||
printf("-f <file> path to configuration file\n"
|
||||
"-a <adv> path for adventure file (.adv)\n"
|
||||
"-s <directory> generate string-tables (for localization)\n"
|
||||
"-h this help\n");
|
||||
exit(0);
|
||||
static void (*display_error_cb)(const char *);
|
||||
void display_error(const char *format, ...) {
|
||||
va_list lst;va_start(lst, format);
|
||||
if (display_error_cb) {
|
||||
char buff[1024];
|
||||
vsnprintf(buff,sizeof(buff), format, lst);
|
||||
} else {
|
||||
fprintf(stderr, format, lst);
|
||||
}
|
||||
|
||||
void show_help_short() {
|
||||
printf("Use -h for help\n");
|
||||
}
|
||||
|
||||
void quit_cb_exit_wait(void *_) {
|
||||
|
@ -1609,54 +1613,62 @@ void quit_cb_exit_wait(void *_) {
|
|||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
int skeldal_gen_string_table_entry_point(const SKELDAL_CONFIG *start_cfg, const char *save_path) {
|
||||
def_mman_group_table(gpathtable);
|
||||
zoom_speed(1);
|
||||
turn_speed(1);
|
||||
const char *config_name = CONFIG_NAME;
|
||||
const char *adv_config_file = NULL;
|
||||
const char *gen_stringtable_path = NULL;
|
||||
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:")) != -1; ) {
|
||||
switch (optchr) {
|
||||
case 'f': config_name = local_strdup(optarg);break;
|
||||
case 'a': adv_config_file = local_strdup(optarg);break;
|
||||
case 'h': show_help(argv[0]);break;
|
||||
case 's': gen_stringtable_path = local_strdup(optarg);break;
|
||||
default: show_help_short();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
INI_CONFIG *cfg = ini_open(config_name);
|
||||
INI_CONFIG *cfg = ini_open(start_cfg->config_path);
|
||||
if (cfg == NULL) {
|
||||
fprintf(stderr, "Failed to open configuration file: %s\n", CONFIG_NAME);
|
||||
show_help_short();
|
||||
start_cfg->show_error(concat2("Failed to open configuration file: ", start_cfg->config_path));
|
||||
start_cfg->short_help();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (adv_config_file) {
|
||||
TSTR_LIST adv_config=read_config(adv_config_file);
|
||||
if (start_cfg->adventure_path) {
|
||||
TSTR_LIST adv_config=read_config(start_cfg->adventure_path);
|
||||
adv_patch_config(cfg, adv_config);
|
||||
release_list(adv_config);
|
||||
}
|
||||
|
||||
const char *groot = configure_pathtable(cfg);
|
||||
if (!change_current_directory(groot)) {
|
||||
fprintf(stderr, "Can't change directory to %s", groot);
|
||||
start_cfg->show_error(concat2("Can't change directory to: ", groot));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (gen_stringtable_path) {
|
||||
init_DDL_manager();
|
||||
generate_string_tables(gen_stringtable_path);
|
||||
generate_string_tables(save_path);
|
||||
printf("Done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int skeldal_entry_point(const SKELDAL_CONFIG *start_cfg)
|
||||
{
|
||||
def_mman_group_table(gpathtable);
|
||||
|
||||
display_error_cb = start_cfg->show_error;
|
||||
|
||||
INI_CONFIG *cfg = ini_open(start_cfg->config_path);
|
||||
if (cfg == NULL) {
|
||||
start_cfg->show_error(concat2("Failed to open configuration file: ", start_cfg->config_path));
|
||||
start_cfg->short_help();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (start_cfg->adventure_path) {
|
||||
TSTR_LIST adv_config=read_config(start_cfg->adventure_path);
|
||||
adv_patch_config(cfg, adv_config);
|
||||
release_list(adv_config);
|
||||
}
|
||||
|
||||
const char *groot = configure_pathtable(cfg);
|
||||
if (!change_current_directory(groot)) {
|
||||
start_cfg->show_error(concat2("Can't change directory to: ", groot));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (start_cfg->lang_path) {
|
||||
lang_set_folder(build_pathname(2, gpathtable[SR_LANG], start_cfg->lang_path));
|
||||
}
|
||||
|
||||
start_check();
|
||||
purge_temps(1);
|
||||
|
|
25
game/skeldal.h
Normal file
25
game/skeldal.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
|
||||
void (*short_help)();
|
||||
void (*show_error)(const char *text);
|
||||
|
||||
const char *adventure_path;
|
||||
const char *config_path;
|
||||
const char *lang_path;
|
||||
|
||||
|
||||
} SKELDAL_CONFIG;
|
||||
|
||||
int skeldal_entry_point(const SKELDAL_CONFIG *cfg);
|
||||
int skeldal_gen_string_table_entry_point(const SKELDAL_CONFIG *cfg, const char *save_path);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -493,6 +493,13 @@ void auto_group()
|
|||
if (p->sektor==q->sektor && p->direction==q->direction && p->inmaphash == current_map_hash && q->used && q->lives)
|
||||
q->groupnum=p->groupnum;
|
||||
}
|
||||
|
||||
for(i=0;p=&postavy[i],i<POCET_POSTAV;i++) {
|
||||
if (p->sektor == viewsector && p->direction == viewdir) {
|
||||
cur_group = p->groupnum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
int vyber_zacinajiciho(int att_player)
|
||||
|
@ -527,9 +534,8 @@ int vyber_prvniho(int att)
|
|||
return i;
|
||||
}
|
||||
*/
|
||||
static int vyber_hrace(int att)
|
||||
{
|
||||
int gr,i;
|
||||
static int vyber_hrace(int att) {
|
||||
int gr;
|
||||
THUMAN *h;
|
||||
|
||||
if (att > POCET_POSTAV || att < 0)
|
||||
|
@ -537,11 +543,28 @@ static int vyber_hrace(int att)
|
|||
else
|
||||
gr = postavy[att].groupnum;
|
||||
h = postavy;
|
||||
for(i=0,h=postavy;i<POCET_POSTAV && (!h->used || !h->lives || !h->actions || h->groupnum!=gr) ;i++,h++);
|
||||
if (i==6)
|
||||
if (att!=0xff) return att;else return group_sort[0];
|
||||
else
|
||||
return i;
|
||||
int candidate0 = -1;
|
||||
int candidate1 = -1;
|
||||
int candidate2 = -1;
|
||||
for (int i = POCET_POSTAV; i>0;) {
|
||||
--i;
|
||||
h = postavy+i;
|
||||
if (h->used && h->inmaphash == current_map_hash) {
|
||||
candidate0 = i;
|
||||
if (h->groupnum == gr) {
|
||||
candidate1 =i;
|
||||
if (h->actions) {
|
||||
candidate2 = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (candidate2>=0) return candidate2;
|
||||
if (candidate1>=0) return candidate1;
|
||||
if (candidate0>=0) return candidate0;
|
||||
if (att != 0xFF) return att;
|
||||
display_error("Can't select PC for battle");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void zacatek_kola()
|
||||
|
|
|
@ -19,7 +19,19 @@ typedef struct _temp_storage_file_rd {
|
|||
int skp = 0;
|
||||
} TMPFILE_RD;
|
||||
|
||||
using FileSystem = std::map<std::string, std::vector<uint8_t>, std::less<> >;
|
||||
struct icompare {
|
||||
using is_transparent = std::true_type;
|
||||
bool operator()(const std::string_view &a, const std::string_view &b) const {
|
||||
if (a.size() != b.size()) return a.size() < b.size();
|
||||
for (std::size_t i = 0, cnt = a.size(); i < cnt; ++i) {
|
||||
int cmp = toupper(a[i]) - toupper(b[i]);
|
||||
if (cmp) return cmp < 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
using FileSystem = std::map<std::string, std::vector<uint8_t>, icompare >;
|
||||
static FileSystem temp_fsystem;
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ SET(files basicobj.c
|
|||
strlists.c
|
||||
cztable.c
|
||||
music.cpp
|
||||
string_table.cpp
|
||||
swaper.c )
|
||||
|
||||
add_library(skeldal_libs ${files})
|
||||
|
|
120
libs/addtext.c
120
libs/addtext.c
|
@ -1,120 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "memman.c"
|
||||
#include "strlite.c"
|
||||
|
||||
|
||||
TSTR_LIST ls_origin=NULL;
|
||||
TSTR_LIST ls_new=NULL;
|
||||
|
||||
static void load_error_msg(int err,char *filename)
|
||||
{
|
||||
switch (err)
|
||||
{
|
||||
case -1: puts ("Source or target file not found");break;
|
||||
case -2: puts ("Unexcepted EOF");break;
|
||||
case -3: puts ("Internal error in strlite.c");break;
|
||||
default: printf("Error in string table at line %d.\n",err);
|
||||
}
|
||||
printf("File:%s\n",filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void load_lists(char *filename1,char *filename2)
|
||||
{
|
||||
int err;
|
||||
|
||||
err=load_string_list(&ls_new,filename1);
|
||||
if (err) load_error_msg(err,filename1);
|
||||
err=load_string_list(&ls_origin,filename2);
|
||||
if (err) load_error_msg(err,filename2);
|
||||
}
|
||||
|
||||
|
||||
char *create_backup(char *filename)
|
||||
{
|
||||
char *c,*d;
|
||||
|
||||
c=getmem(strlen(filename)+5);strcpy(c,filename);
|
||||
d=strrchr(c,'.');if (d==NULL) d=strchr(c,0);
|
||||
strcpy(d,".bak");
|
||||
remove(c);
|
||||
rename(filename,c);
|
||||
return c;
|
||||
}
|
||||
|
||||
void spoj_stringtable()
|
||||
{
|
||||
int i;
|
||||
int cnt=str_count(ls_new);
|
||||
for(i=0;i<cnt;i++) if (ls_new[i]!=NULL && ls_origin[i]==NULL)
|
||||
str_replace(&ls_origin,i,ls_new[i]);
|
||||
}
|
||||
|
||||
static void save_num(FILE *fo,int num)
|
||||
{
|
||||
char *c=ls_origin[num];
|
||||
|
||||
if (c==NULL) return;
|
||||
fprintf(fo,"%d ",num);
|
||||
while (*c) if (*c=='\n') (putc('|',fo),c++);else putc(*c++,fo);
|
||||
putc('\n',fo);
|
||||
}
|
||||
|
||||
void save_stringtable(char *filename,char *backup_name)
|
||||
{
|
||||
FILE *fo,*fb;
|
||||
int cnt=str_count(ls_origin);
|
||||
int num,rd;
|
||||
int oldnum=-1,i;
|
||||
|
||||
fb=fopen_icase(backup_name,"rt");
|
||||
if (fb==NULL)
|
||||
{
|
||||
puts("Cannot open backup file for reading.");
|
||||
exit(1);
|
||||
}
|
||||
fo=fopen_icase(filename,"wt");
|
||||
if (fo==NULL)
|
||||
{
|
||||
puts("Cannot open target file for writting.");
|
||||
exit(1);
|
||||
}
|
||||
num=0;
|
||||
rd=fscanf(fb,"%d",&num);
|
||||
while (num!=-1)
|
||||
{
|
||||
if (rd!=1)
|
||||
do
|
||||
{
|
||||
rd=getc(fb);putc(rd,fo);
|
||||
}
|
||||
while(rd!='\n');
|
||||
else
|
||||
{
|
||||
for(i=oldnum+1;i<=num;i++) save_num(fo,i);
|
||||
while((rd=getc(fb))!=EOF && rd!='\n');
|
||||
}
|
||||
oldnum=num;
|
||||
rd=fscanf(fb,"%d",&num);
|
||||
}
|
||||
for(i=oldnum+1;i<cnt;i++) save_num(fo,i);
|
||||
fprintf(fo,"%d",-1);
|
||||
fclose(fo);
|
||||
fclose(fb);
|
||||
}
|
||||
|
||||
void main(int argc,char **argv)
|
||||
{
|
||||
char *back;
|
||||
if (argc!=3)
|
||||
{
|
||||
puts("Usage: ADDTEXT source target");
|
||||
exit(1);
|
||||
}
|
||||
load_lists(argv[1],argv[2]);
|
||||
back=create_backup(argv[2]);
|
||||
spoj_stringtable();
|
||||
save_stringtable(argv[2],back);
|
||||
puts("New texts added...");
|
||||
}
|
132
libs/advinst.c
132
libs/advinst.c
|
@ -1,132 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <stdlib.h>
|
||||
#include <conio.h>
|
||||
#include <ctype.h>
|
||||
#include <dos.h>
|
||||
|
||||
char copy_path[500];
|
||||
char *adv_name;
|
||||
|
||||
char _change_disk(unsigned znak)
|
||||
{
|
||||
unsigned total;
|
||||
znak-='@';
|
||||
_dos_setdrive(znak,&total);
|
||||
_dos_getdrive(&total);
|
||||
return total==znak;
|
||||
}
|
||||
|
||||
char disk_finder()
|
||||
{
|
||||
static struct find_t ft;
|
||||
int err;
|
||||
|
||||
if (!access("SKELDAL.EXE",F_OK) && !access("ADV",F_OK)) return 1;
|
||||
err=_dos_findfirst("*.*",_A_SUBDIR,&ft);
|
||||
while (!err)
|
||||
{
|
||||
if (ft.attrib & _A_SUBDIR && strcmp(ft.name,".") && strcmp(ft.name,".."))
|
||||
{
|
||||
chdir(ft.name);
|
||||
if (disk_finder()) return 1;
|
||||
chdir("..");
|
||||
}
|
||||
err=_dos_findnext(&ft);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char find_path(char *path)
|
||||
{SEPARATOR
|
||||
char *oldpath;
|
||||
unsigned pismeno='C';
|
||||
|
||||
cputs("Hledam...\r");
|
||||
oldpath=getcwd(NULL,PATH_MAX);
|
||||
for(pismeno='C';pismeno<='Z';pismeno++)
|
||||
{
|
||||
_change_disk(pismeno);
|
||||
chdir("..");
|
||||
if (disk_finder()==1)
|
||||
{
|
||||
getcwd(path,PATH_MAX);
|
||||
chdir(oldpath);_change_disk(oldpath[0]);
|
||||
free(oldpath);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
chdir(oldpath);_change_disk(oldpath[0]);
|
||||
free(oldpath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void main(int argc,char **argv)
|
||||
{
|
||||
char temp[550];
|
||||
char rep;
|
||||
if (argc<2)
|
||||
{
|
||||
puts("Nespravne parametry!\n"
|
||||
"\n"
|
||||
"Pouziti ADVINST [jmeno]\n"
|
||||
"\n"
|
||||
"jmeno = Nazev dobrodruzstvi bez pripony");
|
||||
return;
|
||||
}
|
||||
adv_name=argv[1];
|
||||
sprintf(temp,"%s.adv",adv_name);
|
||||
if (access(adv_name,F_OK) && access(temp,F_OK))
|
||||
{
|
||||
printf("Nemohu najit zadne dobrodruzstvi s timto jmenem (%s)!\n",adv_name);
|
||||
return;
|
||||
}
|
||||
do
|
||||
{
|
||||
printf("Vypiste celou cestu, kde lezi hra (napr: c:\\hry\\skeldal)\n"
|
||||
"Pokud vlozte otaznik (?), instalator se pokusi hru na disku vyhledat\n"
|
||||
"Pokud stisknete pouze <ENTER>, instalator se ukonci\n"
|
||||
">");
|
||||
gets(copy_path);
|
||||
if (copy_path[0]=='?')
|
||||
{
|
||||
find_path(copy_path);
|
||||
printf("\nInstalator nasel hru na ceste: %s\n\n",copy_path);
|
||||
}
|
||||
if (copy_path[0]==0) return;
|
||||
sprintf(temp,"%s\\skeldal.exe",copy_path);
|
||||
rep=access(temp,F_OK);
|
||||
if (rep) puts("Vami vlozena cesta neni spravna!\n");
|
||||
else
|
||||
{
|
||||
sprintf(temp,"%s\\adv\\%s",copy_path,adv_name);
|
||||
if (access(temp,F_OK))if (mkdir(temp)!=0) printf("Nedokazal jsem vytvorit adresar %s\n\n\n",temp),rep=1;
|
||||
}
|
||||
}
|
||||
while(rep);
|
||||
sprintf(temp,"copy %s.adv %s > nul",adv_name,copy_path);
|
||||
puts(temp);
|
||||
system(temp);
|
||||
sprintf(temp,"copy %s\\*.* %s\\adv\\%s > nul",adv_name,copy_path,adv_name);
|
||||
puts(temp);
|
||||
system(temp);
|
||||
sprintf(temp,"%s.bat",adv_name);
|
||||
if (access(temp,F_OK)==0)
|
||||
{
|
||||
sprintf(temp,"%s.bat %s",adv_name,copy_path);
|
||||
puts(temp);
|
||||
system(temp);
|
||||
}
|
||||
chdir(copy_path);
|
||||
puts("Instalace uspesna!");
|
||||
printf("Nove dobrodruzstvi spustis prikazem SKELDAL %s.adv\n",adv_name);
|
||||
puts("Chces si nyni nove dobrodruzstvi vyzkouset? ANO/NE (cokoliv/N)");
|
||||
if (toupper(getche())=='N') return;
|
||||
sprintf(temp,"SKELDAL %s.adv",adv_name);
|
||||
system(temp);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -142,6 +142,7 @@ void put_image(const word *image,word *target,int start_line,int sizex,int sizey
|
|||
void put_picture2picture(const word *source,word *target,int xp,int yp);
|
||||
//#pragma aux put_picture2picture parm [ESI][EDI][EAX][EDX] modify [ECX]
|
||||
|
||||
|
||||
void draw_rounded_rectangle(int x, int y, int xs, int ys, int radius,
|
||||
int stroke_color, int fill_color);
|
||||
|
||||
#define swap_int(a,b) do {int c=a;a=b;b=c;} while (0);
|
||||
|
|
157
libs/bgraph2.c
157
libs/bgraph2.c
|
@ -624,3 +624,160 @@ void rectangle(int x1,int y1,int x2,int y2,int color)
|
|||
ver_line32(x2,y1,y2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void put_pixel(int x, int y, int c) {
|
||||
word *addr = getadr32(x, y);
|
||||
*addr = c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
draw_circle_quarter()
|
||||
|
||||
Draws one quarter of a circle of radius 'r' (using Bresenham’s algorithm)
|
||||
centered at (cx, cy). The parameter 'quadrant' selects which quarter:
|
||||
0 = top–left (draws points in the direction of decreasing x and y)
|
||||
1 = top–right (increasing x, decreasing y)
|
||||
2 = bottom–right (increasing x and y)
|
||||
3 = bottom–left (decreasing x, increasing y)
|
||||
The function calls put_pixel() to draw the pixels.
|
||||
--------------------------------------------------------------------------*/
|
||||
static void draw_circle_quarter(int cx, int cy, int r, int quadrant, int color) {
|
||||
int x = 0, y = r;
|
||||
int d = 3 - 2 * r;
|
||||
while (x <= y) {
|
||||
switch (quadrant) {
|
||||
case 0: /* Top-left quarter */
|
||||
put_pixel(cx - x, cy - y, color);
|
||||
put_pixel(cx - y, cy - x, color);
|
||||
break;
|
||||
case 1: /* Top-right quarter */
|
||||
put_pixel(cx + x, cy - y, color);
|
||||
put_pixel(cx + y, cy - x, color);
|
||||
break;
|
||||
case 2: /* Bottom-right quarter */
|
||||
put_pixel(cx + x, cy + y, color);
|
||||
put_pixel(cx + y, cy + x, color);
|
||||
break;
|
||||
case 3: /* Bottom-left quarter */
|
||||
put_pixel(cx - x, cy + y, color);
|
||||
put_pixel(cx - y, cy + x, color);
|
||||
break;
|
||||
}
|
||||
if (d < 0) {
|
||||
d += 4 * x + 6;
|
||||
} else {
|
||||
d += 4 * (x - y) + 10;
|
||||
y--;
|
||||
}
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
draw_rounded_rectangle()
|
||||
|
||||
Draws a filled, rounded rectangle with a stroke border. The parameters are:
|
||||
- (x,y): Top–left coordinate of the bounding rectangle.
|
||||
- (xs, ys): Width and height.
|
||||
- radius: The radius of the corner arcs.
|
||||
- stroke_color: The color of the border.
|
||||
- fill_color: The color used for filling the interior.
|
||||
--------------------------------------------------------------------------*/
|
||||
void draw_rounded_rectangle(int x, int y, int xs, int ys, int radius,
|
||||
int stroke_color, int fill_color) {
|
||||
int i, j;
|
||||
|
||||
/* Clamp the radius if it is too large */
|
||||
if (radius * 2 > xs)
|
||||
radius = xs / 2;
|
||||
if (radius * 2 > ys)
|
||||
radius = ys / 2;
|
||||
|
||||
int rsq = radius * radius; /* We'll use this to avoid taking square roots */
|
||||
|
||||
/* --- Fill the rounded rectangle --- */
|
||||
for (j = y; j < y + ys; j++) {
|
||||
for (i = x; i < x + xs; i++) {
|
||||
int draw_pixel = 1; /* flag: set to 0 if the pixel is outside the filled area */
|
||||
|
||||
if (radius > 0) {
|
||||
/* Top-left corner */
|
||||
if (i < x + radius && j < y + radius) {
|
||||
int cx = x + radius;
|
||||
int cy = y + radius;
|
||||
int dx = i - cx;
|
||||
int dy = j - cy;
|
||||
if (dx * dx + dy * dy > rsq)
|
||||
draw_pixel = 0;
|
||||
}
|
||||
/* Top-right corner */
|
||||
else if (i >= x + xs - radius && j < y + radius) {
|
||||
int cx = x + xs - radius - 1;
|
||||
int cy = y + radius;
|
||||
int dx = i - cx;
|
||||
int dy = j - cy;
|
||||
if (dx * dx + dy * dy > rsq)
|
||||
draw_pixel = 0;
|
||||
}
|
||||
/* Bottom-left corner */
|
||||
else if (i < x + radius && j >= y + ys - radius) {
|
||||
int cx = x + radius;
|
||||
int cy = y + ys - radius - 1;
|
||||
int dx = i - cx;
|
||||
int dy = j - cy;
|
||||
if (dx * dx + dy * dy > rsq)
|
||||
draw_pixel = 0;
|
||||
}
|
||||
/* Bottom-right corner */
|
||||
else if (i >= x + xs - radius && j >= y + ys - radius) {
|
||||
int cx = x + xs - radius - 1;
|
||||
int cy = y + ys - radius - 1;
|
||||
int dx = i - cx;
|
||||
int dy = j - cy;
|
||||
if (dx * dx + dy * dy > rsq)
|
||||
draw_pixel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (draw_pixel)
|
||||
put_pixel(i, j, fill_color);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Draw the border (stroke) --- */
|
||||
if (radius > 0) {
|
||||
/* Draw the top and bottom horizontal lines (excluding the rounded corners) */
|
||||
for (i = x + radius; i < x + xs - radius; i++) {
|
||||
put_pixel(i, y, stroke_color); /* Top edge */
|
||||
put_pixel(i, y + ys - 1, stroke_color); /* Bottom edge */
|
||||
}
|
||||
/* Draw the left and right vertical lines (excluding the rounded corners) */
|
||||
for (j = y + radius; j < y + ys - radius; j++) {
|
||||
put_pixel(x, j, stroke_color); /* Left edge */
|
||||
put_pixel(x + xs - 1, j, stroke_color); /* Right edge */
|
||||
}
|
||||
|
||||
/* Draw the four corner arcs */
|
||||
/* Top-left corner */
|
||||
draw_circle_quarter(x + radius, y + radius, radius, 0, stroke_color);
|
||||
/* Top-right corner */
|
||||
draw_circle_quarter(x + xs - radius - 1, y + radius, radius, 1, stroke_color);
|
||||
/* Bottom-right corner */
|
||||
draw_circle_quarter(x + xs - radius - 1, y + ys - radius - 1, radius, 2, stroke_color);
|
||||
/* Bottom-left corner */
|
||||
draw_circle_quarter(x + radius, y + ys - radius - 1, radius, 3, stroke_color);
|
||||
} else {
|
||||
/* If radius == 0, draw a normal (non–rounded) rectangle border */
|
||||
for (i = x; i < x + xs; i++) {
|
||||
put_pixel(i, y, stroke_color);
|
||||
put_pixel(i, y + ys - 1, stroke_color);
|
||||
}
|
||||
for (j = y; j < y + ys; j++) {
|
||||
put_pixel(x, j, stroke_color);
|
||||
put_pixel(x + xs - 1, j, stroke_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
394
libs/csv.hpp
Normal file
394
libs/csv.hpp
Normal file
|
@ -0,0 +1,394 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
|
||||
|
||||
|
||||
///Describes mapping of column to a string field in the target structure
|
||||
/**
|
||||
* @tparam T type of target structure
|
||||
*
|
||||
* Supported types: string, int16-64, unsigned int 16-64, float, double, boolean, char
|
||||
*
|
||||
* nullptr is used to skip the field
|
||||
*/
|
||||
template<typename T>
|
||||
struct CSVFieldMapping {
|
||||
|
||||
using FieldRef = std::variant<std::string T::*,
|
||||
std::uint64_t T::*,
|
||||
std::int64_t T::*,
|
||||
std::uint32_t T::*,
|
||||
std::int32_t T::*,
|
||||
std::uint16_t T::*,
|
||||
std::int16_t T::*,
|
||||
char T::*,
|
||||
unsigned char T::*,
|
||||
float T::*,
|
||||
double T::*,
|
||||
bool T::*,
|
||||
std::nullptr_t>;
|
||||
///name of field
|
||||
std::string name;
|
||||
///pointer to member field
|
||||
FieldRef field;
|
||||
};
|
||||
|
||||
///Contains final mapping columns to structure. The instance is created by the function mapColumns
|
||||
template<typename T>
|
||||
class CSVFieldIndexMapping : public std::vector<typename CSVFieldMapping<T>::FieldRef > {
|
||||
public:
|
||||
using std::vector<typename CSVFieldMapping<T>::FieldRef >::vector;
|
||||
///contains true, if all fields has been mapped. Otherwise it is false
|
||||
/** to find, which field is not mapped you need to find it manually
|
||||
*/
|
||||
bool allMapped = false;
|
||||
|
||||
template<typename X>
|
||||
constexpr bool isMapped(X T::*ptr) const {
|
||||
if (allMapped) return true;
|
||||
return std::find(this->begin(),
|
||||
this->end(), typename CSVFieldMapping<T>::FieldRef(ptr)) != this->end();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
///Simple CSV reader
|
||||
/**
|
||||
* Parses CSV from the source. Each read returns one field until whole CSV is parsed
|
||||
*
|
||||
* @tparam Source the type of object which provides source data. This can also be a function.
|
||||
* because the type must implement following function call <int()>. The input characters
|
||||
* should be read as unsigned (so characters above 0x80 are not mapped bellow the zero). The
|
||||
* function must return EOF (-1) as end of file.
|
||||
*/
|
||||
template<typename Source>
|
||||
class CSVReader {
|
||||
public:
|
||||
|
||||
|
||||
enum class CSVState: int {
|
||||
///there is next field at the row
|
||||
next = 0,
|
||||
///this field is last field at the row
|
||||
last = 1,
|
||||
///no more field, eof reached
|
||||
eof = std::char_traits<char>::eof()
|
||||
};
|
||||
|
||||
///Inicialize the parser
|
||||
/**
|
||||
* @param src data source. This is function, which returns next character.
|
||||
* Returned type should be int.
|
||||
* If there are no more characters, it should return std::char_traits<char>::eof()
|
||||
*/
|
||||
CSVReader(Source &&src)
|
||||
: _src(std::forward<Source>(src)) {}
|
||||
|
||||
///Read next field
|
||||
/**
|
||||
* @param buffer reference to a string, which will be filled with content of the next field.
|
||||
* The string is always cleared at the beginning regardless on how the operation was completed
|
||||
* @retval next - field read successfully and there is at least one further field
|
||||
* on the same row
|
||||
* @retval last - field read successfully and this was the last field on the row,
|
||||
* next read() will read a field on a new row
|
||||
* @retval eof - reached end of file, buffer is empty
|
||||
*/
|
||||
CSVState read(std::string &buffer);
|
||||
|
||||
|
||||
///Contains eof value
|
||||
static constexpr auto eof = std::char_traits<char>::eof();
|
||||
|
||||
|
||||
///Reads line and creates mapping of columns to structure
|
||||
/**
|
||||
* Function is intended to be called for the first line, which contains columns' names
|
||||
*
|
||||
* @tparam T You probably need to explicitly specify type
|
||||
* @param def definition
|
||||
* @return mapping
|
||||
*
|
||||
*
|
||||
* @code
|
||||
* struct CSVCols {
|
||||
* std::string symbol_data_type;
|
||||
* std::string format;
|
||||
* std::string interval;
|
||||
* std::string directory;
|
||||
* };
|
||||
*
|
||||
* auto mapping = reader.mapColumns<CSVCols>({
|
||||
* {"symbol_data_type",&CSVCols::symbol_data_type},
|
||||
* {"format", &CSVCols::format},
|
||||
* {"interval", &CSVCols::interval},
|
||||
* {"directory", &CSVCols::directory},
|
||||
* });
|
||||
* if (!mapping.allMapped) return false;
|
||||
* @encode
|
||||
*/
|
||||
template<typename T>
|
||||
CSVFieldIndexMapping<T> mapColumns(const std::initializer_list<CSVFieldMapping<T> > &def);
|
||||
|
||||
|
||||
///Reads row and transfer data to target structure through the mapping object
|
||||
/**
|
||||
* @param mapping mapping object created by mapColumns
|
||||
* @param target target structure which is filled from the line
|
||||
* @retval true some data read
|
||||
* @retval false end of table (no data read)
|
||||
*/
|
||||
template<typename T>
|
||||
bool readRow(const CSVFieldIndexMapping<T> &mapping, T &target);
|
||||
|
||||
///Reset state - assume that source has been reset
|
||||
void reset() {
|
||||
_eof_reached = false;
|
||||
_beg_line = true;
|
||||
}
|
||||
|
||||
bool skipLine() {
|
||||
std::string buff;
|
||||
while (read(buff) == CSVState::next);
|
||||
return !_eof_reached;
|
||||
}
|
||||
|
||||
///Change the character for quotes
|
||||
/**
|
||||
* @param quotes new character for quotes
|
||||
*
|
||||
* @note you need to change this value before the first line is read
|
||||
* (including the function mapColumns())
|
||||
*
|
||||
* (default=")
|
||||
*/
|
||||
void setQuotes(char quotes) {_quotes = quotes;}
|
||||
|
||||
|
||||
///Retrieves character for quotes
|
||||
char getQuotes() const {return _quotes;}
|
||||
|
||||
///Change the character for field separator
|
||||
/**
|
||||
* @param sep new separator
|
||||
*
|
||||
* @note you need to change this value before the first line is read
|
||||
* (including the function mapColumns())
|
||||
*
|
||||
* (default=,)
|
||||
*/
|
||||
void setSep(char sep) {_sep = sep;}
|
||||
|
||||
|
||||
///set field separator
|
||||
char getSep() const {return _sep;}
|
||||
|
||||
|
||||
protected:
|
||||
Source _src;
|
||||
char _sep = ',';
|
||||
char _quotes = '"';
|
||||
bool _eof_reached = false;
|
||||
bool _beg_line = true;
|
||||
|
||||
};
|
||||
|
||||
///This CTAD allows to construct CSVReader without template arguments from lambda
|
||||
/**
|
||||
* CSVReader csv([&]() -> int {return .... ;});
|
||||
*/
|
||||
template<typename Source>
|
||||
CSVReader(Source) -> CSVReader<Source>;
|
||||
|
||||
///Instantiate parser supplying a lambda function as a source
|
||||
template<typename Fn>
|
||||
CSVReader<Fn> parseCSV(Fn &&fn) {
|
||||
return CSVReader<Fn>(std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
///Instantiate parse supplying an input stream as a source
|
||||
/**
|
||||
* @param input input stream. Note it must stay valid during parsing
|
||||
* @return CSVReader instance
|
||||
*/
|
||||
inline auto parseCSVFromFile(std::istream &input) {
|
||||
return parseCSV([&]{return input.get();});
|
||||
}
|
||||
|
||||
///Instantiate the parser supplying a string as a source
|
||||
/**
|
||||
* @param str string source
|
||||
* @return CSVReader reader
|
||||
*/
|
||||
inline auto parseCSVString(std::string &&str) {
|
||||
return parseCSV([s = std::move(str), pos = std::size_t(0)]() mutable {
|
||||
return pos>=s.size()?std::char_traits<char>::eof()
|
||||
:static_cast<int>(static_cast<unsigned char>(s[pos++]));
|
||||
});
|
||||
}
|
||||
|
||||
///Instantiate the parser supplying a string as a source
|
||||
/**
|
||||
* @param str string source - must be valid during parsing
|
||||
* @return CSVReader reader
|
||||
*/
|
||||
inline auto parseCSVString(std::string_view str) {
|
||||
return parseCSV([=, pos = std::size_t(0)]() mutable {
|
||||
return pos>=str.size()?std::char_traits<char>::eof()
|
||||
:static_cast<int>(static_cast<unsigned char>(str[pos++]));
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
inline typename CSVReader<Source>::CSVState CSVReader<Source>::read(std::string &buffer) {
|
||||
buffer.clear();
|
||||
if (_eof_reached) return CSVState::eof;
|
||||
int c = _src();
|
||||
if (_beg_line) {
|
||||
while (c != eof && std::iscntrl(c)) c = _src();
|
||||
}
|
||||
if (c == eof) return CSVState::eof;
|
||||
if (c == _quotes) {
|
||||
bool loop;
|
||||
do {
|
||||
c= _src();
|
||||
while (c != _quotes && c != eof) {
|
||||
buffer.push_back(static_cast<char>(c));
|
||||
c = _src();
|
||||
}
|
||||
loop = false;
|
||||
if (c == _quotes) {
|
||||
c = _src();
|
||||
if (c == _quotes) {
|
||||
loop = true;
|
||||
buffer.push_back(_quotes);
|
||||
}
|
||||
}
|
||||
} while (loop);
|
||||
while (c != eof && c != _sep && c != '\n' && c != '\r') {
|
||||
c = _src(); //ignore incorrect csv content
|
||||
}
|
||||
} else {
|
||||
while (c != _sep && c != eof && c != '\n' && c != '\r') {
|
||||
buffer.push_back(static_cast<char>(c));
|
||||
c = _src();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (c == eof) _eof_reached = true;
|
||||
_beg_line = c != _sep;
|
||||
|
||||
return _beg_line?CSVState::last:CSVState::next;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
template<typename T>
|
||||
inline CSVFieldIndexMapping<T> CSVReader<Source>::mapColumns(const std::initializer_list<CSVFieldMapping<T> > &def) {
|
||||
CSVFieldIndexMapping<T> out;
|
||||
std::string buff;
|
||||
std::size_t cnt = def.size();
|
||||
CSVState st = CSVState::next;
|
||||
while (st != CSVState::last) {
|
||||
st = this->read(buff);
|
||||
if (st == CSVState::eof) break;
|
||||
auto iter = std::find_if(def.begin(), def.end(), [&](const CSVFieldMapping<T> &x) {
|
||||
return x.name == buff;
|
||||
});
|
||||
if (iter != def.end()) {
|
||||
out.push_back(iter->field);
|
||||
cnt--;
|
||||
} else {
|
||||
out.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
out.allMapped = cnt == 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename Source>
|
||||
template<typename T>
|
||||
inline bool CSVReader<Source>::readRow(const CSVFieldIndexMapping<T> &mapping,T &target) {
|
||||
std::string buff;
|
||||
std::size_t idx = 0;
|
||||
CSVState st = CSVState::next;
|
||||
while (st == CSVState::next) {
|
||||
if (idx < mapping.size()) {
|
||||
const auto &m = mapping[idx];
|
||||
st = std::visit([&](const auto &ptr) -> CSVState{
|
||||
using TPtr = std::decay_t<decltype(ptr)>;
|
||||
if constexpr(!std::is_null_pointer_v<TPtr>) {
|
||||
using TVal = std::decay_t<decltype(target.*ptr)>;
|
||||
if constexpr(std::is_same_v<TVal, std::string>) {
|
||||
return read(target.*ptr);
|
||||
} else if constexpr(std::is_same_v<TVal, std::uint16_t> || std::is_same_v<TVal, std::uint64_t> || std::is_same_v<TVal, std::uint32_t>) {
|
||||
CSVState st = read(buff);
|
||||
auto v = std::strtoull(buff.c_str(),nullptr,10);
|
||||
target.*ptr = static_cast<TVal>(v);
|
||||
return st;
|
||||
} else if constexpr(std::is_same_v<TVal, std::int16_t> || std::is_same_v<TVal, std::int64_t> || std::is_same_v<TVal, std::int32_t>) {
|
||||
CSVState st = read(buff);
|
||||
auto v = std::strtoll(buff.c_str(),nullptr,10);
|
||||
target.*ptr = static_cast<TVal>(v);
|
||||
return st;
|
||||
} else if constexpr(std::is_same_v<TVal, float> || std::is_same_v<TVal, double>) {
|
||||
CSVState st = read(buff);
|
||||
auto v = std::strtod(buff.c_str(), nullptr);
|
||||
target.*ptr = static_cast<TVal>(v);
|
||||
return st;
|
||||
} else if constexpr(std::is_same_v<TVal, char> || std::is_same_v<TVal, unsigned char>) {
|
||||
CSVState st = read(buff);
|
||||
if (buff.empty()) target.*ptr = 0;
|
||||
else target.*ptr = static_cast<TVal>(buff[0]);
|
||||
return st;
|
||||
} else {
|
||||
static_assert(std::is_same_v<TVal, bool>);
|
||||
CSVState st = read(buff);
|
||||
std::transform(buff.begin(), buff.end(), buff.begin(), [](char c) { return std::tolower(c); });
|
||||
if (buff == "y" || buff == "t" || buff =="true" || buff == "yes" || buff == "on") {
|
||||
target.*ptr = true;
|
||||
} else if (buff == "n" || buff == "f" || buff =="false" || buff == "no" || buff == "off") {
|
||||
target.*ptr = false;
|
||||
} else {
|
||||
auto v = std::strtod(buff.c_str(), nullptr);
|
||||
target.*ptr = v != 0;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
} else {
|
||||
return read(buff);
|
||||
}
|
||||
}, m);
|
||||
// st = read(target.*m);
|
||||
if (st == CSVState::eof)
|
||||
break;
|
||||
++idx;
|
||||
} else {
|
||||
st = read(buff);
|
||||
}
|
||||
}
|
||||
if (st == CSVState::eof && idx == 0) {
|
||||
return false;
|
||||
}
|
||||
//fill-up missing fields
|
||||
while (idx < mapping.size()) {
|
||||
const auto &m = mapping[idx];
|
||||
std::visit([&](const auto &ptr) {
|
||||
using TPtr = std::decay_t<decltype(ptr)>;
|
||||
if constexpr(!std::is_null_pointer_v<TPtr>) {
|
||||
target.*ptr = {};
|
||||
}
|
||||
},m);
|
||||
++idx;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
51
libs/string_table.cpp
Normal file
51
libs/string_table.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "string_table.h"
|
||||
#include "csv.hpp"
|
||||
#include "cztable.h"
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
|
||||
typedef struct stringtable_struct_tag {
|
||||
|
||||
std::unordered_map<int, std::string> _strings;
|
||||
|
||||
}TSTRINGTABLE;
|
||||
|
||||
struct CSVRecord {
|
||||
int index;
|
||||
std::string string;
|
||||
};
|
||||
|
||||
|
||||
TSTRINGTABLE *stringtable_load(const char *filename) {
|
||||
std::ifstream input(filename);
|
||||
if (!input) return NULL;
|
||||
CSVReader reader([&input]{return input.get();});
|
||||
auto mapping = reader.mapColumns<CSVRecord>({
|
||||
{"id", &CSVRecord::index},
|
||||
{"string", &CSVRecord::string},
|
||||
});
|
||||
if (!mapping.allMapped) return NULL;
|
||||
CSVRecord rec;
|
||||
std::unique_ptr<TSTRINGTABLE> tbl = std::make_unique<TSTRINGTABLE>();
|
||||
while (reader.readRow(mapping, rec)) {
|
||||
windows2kamenik(rec.string.data(), rec.string.size(), rec.string.data());
|
||||
tbl->_strings[rec.index] = rec.string;
|
||||
}
|
||||
return tbl.release();
|
||||
}
|
||||
|
||||
const char *stringtable_find(const TSTRINGTABLE *st, int id, const char *default_value) {
|
||||
if (st) {
|
||||
auto iter = st->_strings.find(id);
|
||||
if (iter != st->_strings.end()) {
|
||||
return iter->second.c_str();
|
||||
}
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
void stringtable_free(TSTRINGTABLE *st) {
|
||||
delete st;
|
||||
}
|
||||
|
18
libs/string_table.h
Normal file
18
libs/string_table.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct stringtable_struct_tag TSTRINGTABLE;
|
||||
|
||||
TSTRINGTABLE *stringtable_load(const char *filename);
|
||||
|
||||
const char *stringtable_find(const TSTRINGTABLE *st, int id, const char *default_value);
|
||||
void stringtable_free(TSTRINGTABLE *st);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -1,10 +1,9 @@
|
|||
SET(files error.cpp
|
||||
)
|
||||
|
||||
# Základní knihovna mylib
|
||||
add_library(skeldal_platform STATIC)
|
||||
add_executable(skeldal)
|
||||
|
||||
# Přidejte soubory společné pro všechny platformy
|
||||
target_sources(skeldal_platform PRIVATE
|
||||
legacy_coroutines.cpp
|
||||
platform.cpp
|
||||
|
@ -17,7 +16,6 @@ target_sources(skeldal_platform PRIVATE
|
|||
getopt.c
|
||||
)
|
||||
|
||||
# Podmínky pro platformu Windows
|
||||
if(WIN32)
|
||||
target_sources(skeldal_platform PRIVATE
|
||||
windows/save_folder.cpp
|
||||
|
@ -25,20 +23,25 @@ if(WIN32)
|
|||
target_compile_definitions(skeldal_platform PRIVATE PLATFORM_WINDOWS)
|
||||
message(STATUS "Building for Windows")
|
||||
|
||||
# Podmínky pro platformu Linux
|
||||
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
target_sources(skeldal_platform PRIVATE
|
||||
linux/save_folder.cpp
|
||||
linux/map_file.cpp
|
||||
)
|
||||
target_sources(skeldal PRIVATE
|
||||
linux/app_start.cpp
|
||||
)
|
||||
target_compile_definitions(skeldal_platform PRIVATE PLATFORM_LINUX)
|
||||
message(STATUS "Building for Linux")
|
||||
|
||||
# Podmínky pro platformu macOS
|
||||
elseif(APPLE)
|
||||
target_sources(skeldal_platform PRIVATE
|
||||
mac_os/save_folder.cpp
|
||||
)
|
||||
target_sources(skeldal PRIVATE
|
||||
linux/app_start.cpp
|
||||
)
|
||||
target_compile_definitions(mylib PRIVATE PLATFORM_MACOS)
|
||||
message(STATUS "Building for macOS")
|
||||
else()
|
||||
|
@ -47,3 +50,11 @@ endif()
|
|||
set_property(TARGET skeldal_platform PROPERTY CXX_STANDARD 20)
|
||||
|
||||
add_subdirectory(sdl)
|
||||
|
||||
target_link_libraries(skeldal
|
||||
skeldal_main
|
||||
skeldal_libs
|
||||
skeldal_platform
|
||||
skeldal_sdl
|
||||
skeldal_libs
|
||||
${SDL2_LIBRARIES} pthread)
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
#include "platform.h"
|
||||
|
||||
|
||||
void display_error(const char *text) {
|
||||
std::cerr << "ERROR:" << text << std::endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
static std::uint32_t gtick = get_game_tick_count();
|
||||
|
|
|
@ -6,7 +6,6 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
|
||||
void display_error(const char *text);
|
||||
void send_log_impl(const char *format, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#ifndef GETOPT_H
|
||||
|
||||
#define GETOPT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
extern int opterr; /* if error message should be printed */
|
||||
extern int optind; /* index into parent argv vector */
|
||||
extern int optopt; /* character checked for validity */
|
||||
|
@ -10,4 +15,9 @@ extern char *optarg; /* argument associated with option */
|
|||
|
||||
int getopt(int nargc, char * const nargv[], const char *ostr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,8 +40,8 @@ struct MsgQueue {
|
|||
};
|
||||
|
||||
static std::queue<MsgQueue> msg_queue;
|
||||
static int recursion_count = 0;
|
||||
|
||||
static void flush_message_queue();
|
||||
|
||||
static void task_after_wakeup(TaskInfo *info) {
|
||||
info->wake_up_msg = -1;
|
||||
|
@ -67,7 +67,6 @@ static void switch_to_task(TaskInfo *task) {
|
|||
task->resume_flag.notify_all();
|
||||
resume_master_flag.wait(false);
|
||||
resume_master_flag = false;
|
||||
flush_message_queue();
|
||||
} else {
|
||||
if (task->exited) return ;
|
||||
TaskInfo *me = current_task_inst;
|
||||
|
@ -81,6 +80,7 @@ static void switch_to_task(TaskInfo *task) {
|
|||
}
|
||||
|
||||
static void clean_task_table() {
|
||||
if (recursion_count) return;
|
||||
for (auto iter = task_list.begin(); iter != task_list.end();) {
|
||||
if (iter->second->exited) {
|
||||
iter = task_list.erase(iter);
|
||||
|
@ -100,25 +100,23 @@ static void clean_up_current_task() {
|
|||
resume_master_flag.notify_all();
|
||||
}
|
||||
|
||||
static void flush_message_queue() {
|
||||
while (!msg_queue.empty()) {
|
||||
auto m = msg_queue.front();
|
||||
msg_queue.pop();
|
||||
static void broadcast_message(EVENT_MSG *msg) {
|
||||
++recursion_count;
|
||||
for (auto &[id, task]: task_list) {
|
||||
if (task->wake_up_msg == m.msg->msg && task.get() != m.sender) {
|
||||
if (task->wake_up_msg == msg->msg && task.get() != current_task_inst) {
|
||||
EVENT_MSG cpy;
|
||||
cpy.msg = m.msg->msg;
|
||||
va_copy(cpy.data, m.msg->data);
|
||||
cpy.msg = msg->msg;
|
||||
va_copy(cpy.data, msg->data);
|
||||
cur_message = &cpy;
|
||||
switch_to_task(task.get());
|
||||
va_end(cpy.data);
|
||||
cur_message = NULL;
|
||||
}
|
||||
}
|
||||
--recursion_count;
|
||||
clean_task_table();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int add_task(int stack,TaskerFunctionName fcname,...) {
|
||||
int id = get_new_task_id();
|
||||
|
@ -150,21 +148,21 @@ char is_running(int id_num) {
|
|||
return !iter->second->exited;
|
||||
}
|
||||
void unsuspend_task(EVENT_MSG *msg) {
|
||||
if (current_task_inst) return;
|
||||
msg_queue.push({msg, current_task_inst});
|
||||
flush_message_queue();
|
||||
|
||||
broadcast_message(msg);
|
||||
}
|
||||
void task_sleep(void) {
|
||||
if (current_task_inst) {
|
||||
switch_to_task(NULL);
|
||||
} else {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
recursion_count++;
|
||||
for (auto &[id, task]: task_list) {
|
||||
if (task->_wake_up_after < now && task->wake_up_msg == -1) {
|
||||
switch_to_task(task.get());
|
||||
}
|
||||
}
|
||||
recursion_count--;
|
||||
clean_task_table();
|
||||
}
|
||||
}
|
||||
|
|
73
platform/linux/app_start.cpp
Normal file
73
platform/linux/app_start.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#include "../../game/skeldal.h"
|
||||
#include "../getopt.h"
|
||||
#include "../platform.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
void show_help(const char *arg0) {
|
||||
printf(
|
||||
"Brany Skeldalu (Gates of Skeldal) portable game player\n"
|
||||
"Copyright (c) 2025 Ondrej Novak. All rights reserved.\n\n"
|
||||
"This work is licensed under the terms of the MIT license.\n"
|
||||
"For a copy, see <https://opensource.org/licenses/MIT>.\n"
|
||||
"\n"
|
||||
"Usage:"
|
||||
);
|
||||
printf("%s [-f <file>] [-a <file>] [-l <lang>] [-s <dir>] [-h]\n\n", arg0);
|
||||
|
||||
printf("-f <file> path to configuration file\n"
|
||||
"-a <adv> path for adventure file (.adv)\n"
|
||||
"-l <lang> set language (cz|en)"
|
||||
"-s <directory> generate string-tables (for localization) and exit\n"
|
||||
"-h this help\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void show_help_short() {
|
||||
printf("Use -h for help\n");
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string config_name = SKELDALINI;
|
||||
std::string adv_config_file;
|
||||
std::string gen_stringtable_path;
|
||||
std::string lang;
|
||||
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:")) != -1; ) {
|
||||
switch (optchr) {
|
||||
case 'f': config_name = optarg;break;
|
||||
case 'a': adv_config_file = optarg;break;
|
||||
case 'h': show_help(argv[0]);break;
|
||||
case 'l': lang = optarg;break;
|
||||
case 's': gen_stringtable_path = optarg;break;
|
||||
default: show_help_short();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
SKELDAL_CONFIG cfg;
|
||||
cfg.short_help = show_help_short;
|
||||
cfg.show_error = [](const char *txt) {
|
||||
std::cerr << "ERROR: " << txt << std::endl;
|
||||
};
|
||||
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
|
||||
cfg.config_path = config_name.c_str();
|
||||
cfg.lang_path = lang.c_str();
|
||||
try {
|
||||
|
||||
if (!gen_stringtable_path.empty()) {
|
||||
skeldal_gen_string_table_entry_point(&cfg, gen_stringtable_path.c_str());
|
||||
return 0;
|
||||
} else {
|
||||
return skeldal_entry_point(&cfg);
|
||||
}
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
std::cerr << "ERROR: " << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ static std::string get_default_savegame_dir() {
|
|||
if (home) {
|
||||
return std::filesystem::path(home) / ".local/share/" SAVEGAME_FOLDERNAME;
|
||||
} else {
|
||||
display_error("$HOME has no value (user with no home)");
|
||||
throw std::runtime_error("$HOME has no value (user with no home)");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ void SetWheelMapping(char up, char down);
|
|||
char get_control_key_state(void);
|
||||
char get_shift_key_state(void);
|
||||
char get_capslock_state(void);
|
||||
void display_error(const char *text);
|
||||
void display_error(const char *text,...);
|
||||
///returns -1 if doesn't exists
|
||||
char check_file_exists(const char *pathname);
|
||||
FILE *fopen_icase(const char *pathname, const char *mode);
|
||||
|
|
|
@ -259,10 +259,11 @@ void SDLContext::event_loop(std::stop_token stp) {
|
|||
if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
|
||||
_crt_effect.reset();
|
||||
}
|
||||
} else if (e.type == SDL_KEYDOWN) {
|
||||
} else if (e.type == SDL_KEYDOWN || e.type == SDL_KEYUP) {
|
||||
_key_capslock = e.key.keysym.mod & KMOD_CAPS;
|
||||
_key_shift =e.key.keysym.mod & KMOD_SHIFT;
|
||||
_key_control =e.key.keysym.mod & KMOD_CTRL;
|
||||
if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.keysym.sym == SDLK_RETURN && (e.key.keysym.mod & KMOD_ALT)) {
|
||||
_fullscreen_mode = !_fullscreen_mode;
|
||||
SDL_SetWindowFullscreen(_window.get(), _fullscreen_mode ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
|
@ -274,6 +275,7 @@ void SDLContext::event_loop(std::stop_token stp) {
|
|||
_keyboard_queue.push(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (e.type == SDL_MOUSEMOTION) {
|
||||
SDL_Point mspt(e.motion.x, e.motion.y);
|
||||
SDL_Rect winrc = get_window_aspect_rect();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
# maps = relative path to maps
|
||||
# video = relative path to video
|
||||
# data = relative path to skeldal.ddl
|
||||
# language = path language folders
|
||||
# savegame = path to savegame, if not defined, retrieved from platform settings
|
||||
|
||||
|
||||
|
@ -12,6 +13,7 @@
|
|||
# maps=./maps/
|
||||
# video=./video/
|
||||
# data=./
|
||||
# language=./lang
|
||||
# savegame = determine default
|
||||
|
||||
#### video settings
|
||||
|
@ -61,6 +63,4 @@
|
|||
# (note, internal fonts supports only characters from KEYBCS2/CP895 code page)
|
||||
# (https://en.wikipedia.org/wiki/Kamenick%C3%BD_encoding)
|
||||
#
|
||||
[localization]
|
||||
#keyboard_layout=cz_querty
|
||||
#string_table=
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue