diff --git a/CMakeLists.txt b/CMakeLists.txt index ca0216e..736b519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,30 @@ else() endif() +set(VERSION_IN "${CMAKE_SOURCE_DIR}/version.in.h") +set(VERSION_OUT "${CMAKE_BINARY_DIR}/version.h") + +find_package(Git QUIET) + +if(GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --tags --always + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE GIT_RESULT + ) + if(NOT GIT_RESULT EQUAL 0) + set(GIT_VERSION "unknown") + endif() +else() + set(GIT_VERSION "unknown") +endif() + +set(APP_VERSION "${GIT_VERSION}") +configure_file(${VERSION_IN} ${VERSION_OUT} @ONLY) + +include_directories(${CMAKE_BINARY_DIR}) include_directories(.) diff --git a/game/automap.c b/game/automap.c index 754f867..b6c03f9 100644 --- a/game/automap.c +++ b/game/automap.c @@ -1109,3 +1109,9 @@ void free_map_description() { if (texty_v_mape!=NULL)release_list(texty_v_mape); texty_v_mape = NULL; } + +TSTR_LIST swap_map_description(TSTR_LIST new_list) { + TSTR_LIST old = texty_v_mape; + texty_v_mape = new_list; + return old; +} \ No newline at end of file diff --git a/game/builder.c b/game/builder.c index e100514..e9ad729 100644 --- a/game/builder.c +++ b/game/builder.c @@ -327,14 +327,14 @@ static const void *bott_draw_normal(const void *pp, int32_t *s) { char c[]=" ";int z,lv,llv; put_picture(x,0,ablock(H_OKNO));lv=p->lives;llv=p->vlastnosti[VLS_MAXHIT]; - if (lv || p->used & 0x80 || p->kondice > 0) + if (lv || p->used & 0x80) { z=3-((lv-1)*4/llv);if (lv==llv) z=0; z*=75; if (p->xicht>=0)put_8bit_clipped(ablock(H_XICHTY+i),bott_scr+PIC_X+x+PIC_Y*scr_linelen2,z,54,75); - if (!lv && !(p->used & 0x80) && p->kondice>0) { + /* if (!lv && !(p->used & 0x80) && p->kondice>0) { greyscale_rectangle(PIC_X+x,PIC_Y, 54, 75); - } + }*/ if (p->bonus) draw_small_icone(0,PIC_X+x+1,PIC_Y+1); if (p->spell) draw_small_icone(1,PIC_X+x+1,PIC_Y+1); if (!p->voda) draw_small_icone(2,PIC_X+x+1,PIC_Y+1); diff --git a/game/chargen.c b/game/chargen.c index cf19827..a729dd9 100644 --- a/game/chargen.c +++ b/game/chargen.c @@ -745,7 +745,7 @@ static void wait_timer(EVENT_MSG *msg, void **udata) { } } -void effect_show(va_list args) +void effect_show(void) { int i; char s = exit_wait; @@ -861,7 +861,7 @@ char enter_generator() memset(&cur_stats,0,sizeof(cur_stats)); vypocet_vlastnosti(cur_angle,&cur_vls); b_disables=0x7; - redraw_generator(rep);if (!rep)effect_show(NULL);rep=1; + redraw_generator(rep);if (!rep)effect_show();rep=1; edit_name(); change_click_map(clk_page1,CLK_PAGE1); was_enter=0; diff --git a/game/dialogy.c b/game/dialogy.c index ead61d0..4172f80 100644 --- a/game/dialogy.c +++ b/game/dialogy.c @@ -766,7 +766,7 @@ void wire_dialog_drw(void) wire_dialog(); draw_all(); ukaz_mysku(); - effect_show(NULL); + effect_show(); } void unwire_dialog(void) { diff --git a/game/enemy.c b/game/enemy.c index 7a5811c..d2a2a1f 100644 --- a/game/enemy.c +++ b/game/enemy.c @@ -1317,18 +1317,24 @@ void vymaz_zasahy(THE_TIMER *q) bott_draw(0); } +static int select_drop_inventory_place(TMOB *p) { + int x,y,pl; + if (p->locx>128) x=1;else if (p->locx<128) x=-1;else x=rnd(2)*2-1; + if (p->locy>128) y=1;else if (p->locy<128) y=-1;else y=rnd(2)*2-1; + pl=0;if (x>0) pl++; + if (y>0) pl=3-pl; + return pl; +} + static int drop_inventory(TMOB *p) { - int i,x,y,pl; + int i,pl; short c[]={0,0}; - for(i=-1;iinv[i] || (i<0 && p->money)) { - if (p->locx>128) x=1;else if (p->locx<128) x=-1;else x=rnd(2)*2-1; - if (p->locy>128) y=1;else if (p->locy<128) y=-1;else y=rnd(2)*2-1; - pl=0;if (x>0) pl++; - if (y>0) pl=3-pl; + pl = select_drop_inventory_place(p); if (i<0) { int z=(int)p->money+(int)(rnd(40)-20)*(int)p->money/(int)100; @@ -1341,6 +1347,16 @@ static int drop_inventory(TMOB *p) } push_item(p->sector,pl,c); } + } + if (destroyed_items) { + int cnt = count_items_total(destroyed_items); + if (cnt) { + int idx = cnt -1; + pl = select_drop_inventory_place(p); + push_item(p->sector,pl, destroyed_items+idx); + destroyed_items[idx] = 0; + } + } return 0; } diff --git a/game/gamesave.c b/game/gamesave.c index 6a7f969..f87b217 100644 --- a/game/gamesave.c +++ b/game/gamesave.c @@ -30,6 +30,9 @@ #define SLOTS_MAX 10 #define GM_MAPENABLE 0x1 +#define GM_FASTBATTLES 0x2 +#define GM_GAMESPEED_SHIFT 2 +#define GM_GAMESPEED_MASK 0x1F #define SAVE_SLOT_S 34 #define LOAD_SLOT_S (372+34) @@ -583,6 +586,14 @@ int unpack_all_status(FILE *f) return -1; } +static void save_destroyed_items(TMPFILE_WR *f) { + short c = (short)count_items_total(destroyed_items); + temp_storage_write(&c,sizeof(c), f); + if (c) { + temp_storage_write(destroyed_items,sizeof(short)*c, f); + } +} + int save_basic_info() { TMPFILE_WR *f; @@ -618,7 +629,14 @@ int save_basic_info() s.swapchans=MIN(get_snd_effect(SND_SWAP),255); s.out_filter=MIN(get_snd_effect(SND_OUTFILTER),255); s.autosave=autosave_enabled; - s.game_flags=(enable_glmap!=0); + s.game_flags=0; + + if (enable_glmap!=0) s.game_flags |= GM_MAPENABLE; + if (gamespeedbattledemon_save!=NULL) temp_storage_write(h->demon_save,sizeof(THUMAN)*1,f); //ulozeni polozek s demony res|=save_dialog_info(f); + save_destroyed_items(f); temp_storage_close_wr(f); SEND_LOG("(SAVELOAD) Done... Result: %d",res); return res; } +static int load_destroyed_items(TMPFILE_RD *f) { + int res = 0; + short destroyed_items_count = 0; + temp_storage_read(&destroyed_items_count,sizeof(destroyed_items_count), f); + free(destroyed_items); + destroyed_items = NULL; + if (destroyed_items_count) { + destroyed_items = NewArr(short, destroyed_items_count+1); + res|=temp_storage_read(destroyed_items,destroyed_items_count*sizeof(short), f) != destroyed_items_count*sizeof(short); + destroyed_items[destroyed_items_count] = 0; + } + return res; +} + int load_basic_info() { TMPFILE_RD *f; @@ -654,6 +687,9 @@ int load_basic_info() if (f==NULL) return 1; res|=(temp_storage_read(&s,1*sizeof(s),f)!=sizeof(s)); if (s.game_flags & GM_MAPENABLE) enable_glmap=1;else enable_glmap=0; + gamespeedbattle =s.game_flags & GM_FASTBATTLES? GAMESPEED_FASTBATTLE:gamespeed; + int tmsp = (s.game_flags >> GM_GAMESPEED_SHIFT) & GM_GAMESPEED_MASK; + if (tmsp) timerspeed_val = tmsp; i=s.picks; if (picked_item!=NULL) free(picked_item); if (i) @@ -685,6 +721,7 @@ int load_basic_info() } } res|=load_dialog_info(f); + res|=load_destroyed_items(f); temp_storage_close_rd(f); viewsector=s.viewsector; viewdir=s.viewdir; @@ -776,11 +813,12 @@ int save_game(long game_time,char *gamename, char is_autosave) temp_storage_store("playtime",&new_play_time, sizeof(new_play_time)); svf=fopen_icase(sn,"wb"); - if (svf==NULL) - { - char buff[256]; - sprintf(buff,"Nelze ulozit pozici na cestu: %s", sn); - display_error(buff); + if (svf==NULL){ + if (!is_autosave) { + char buff[256]; + sprintf(buff,"Failed to create savegame at path %s", sn); + message(1,0,0,"",buff,texty[80]); + } } else { @@ -1769,12 +1807,12 @@ void wire_save_load(char save) { change_click_map(clk_save,CLK_SAVELOAD); redraw_save(); send_message(E_ADD, E_KEYBOARD, saveload_keyboard); - effect_show(NULL); + effect_show(); } else { curcolor = 0; redraw_load(); - effect_show(NULL); + effect_show(); if (save == 2) change_click_map(clk_load_error, CLK_LOAD_ERROR); else if (save == 4) { diff --git a/game/globals.h b/game/globals.h index 66658db..82cb59d 100644 --- a/game/globals.h +++ b/game/globals.h @@ -12,6 +12,10 @@ #define POCET_POSTAV 6 #define HODINA 360 +#define GAMESPEED 5 +#define GAMESPEED_FASTBATTLE 2 + + #define MAX_FILESYSTEM_PATH 256 #define A_SIDEMAP 0x8001 @@ -573,6 +577,7 @@ extern int mapsize; //pocet sektoru v mape extern int hl_ptr; //ukazatel na konec staticke tabulky registraci extern int end_ptr; //ukazatel na uplny konec tabulky registraci extern short **map_items; //ukazatel na mapu predmetu +extern short *destroyed_items; //list of destroyed items to be returned to the game extern int default_ms_cursor; //cislo zakladniho mysiho kurzoru extern void (*unwire_proc)(void); //procedura zajistujici odpojeni prave ukoncovane interakce extern void (*wire_proc)(void); //procedura zajistujici pripojeni drive ukoncene interakce @@ -581,8 +586,8 @@ extern word minimap[VIEW3D_Z+1][VIEW3D_X*2+1]; //minimalizovana mapa s informace extern char norefresh; //vypina refresh obrazovky extern char cancel_pass; //okamzite zrusi plynuly prechod extern char reverse_draw ; //kresba odpredu dozadu -extern char gamespeed; //rychlost hry -extern char gamespeedbattle; //akcelerace rychlosti pro bitvy +extern uint8_t gamespeed; //rychlost hry +extern uint8_t gamespeedbattle; //akcelerace rychlosti pro bitvy extern int num_ofsets[]; //tabulka offsetu pro steny extern int back_color; //cislo barvy pozadi extern uint8_t cur_group; //cislo aktualni skupiny @@ -986,10 +991,10 @@ char pick_item_(int id,int xa,int ya,int xr,int yr); void wire_inv_mode(THUMAN *select); void init_inventory(void); void init_items(void); -void push_item(int sect,int pos,short *picked_item); +void push_item(int sect,int pos,const short *picked_item); void pop_item(int sect,int pos,int mask,short **picked_item); -int count_items_inside(short *place); -int count_items_total(short *place); +int count_items_inside(const short *place); +int count_items_total(const short *place); char put_item_to_inv(THUMAN *p,short *picked_items); //funkce vklada predmet(y) do batohu postavy void pick_set_cursor(void); //nastavuje kurzor podle vlozeneho predmetu; void calc_fly(THE_TIMER *t); @@ -1402,7 +1407,7 @@ void destroy_all_fly(); void stop_fly(LETICI_VEC *p,char zvuk); void herni_cas(char *s); - +typedef char **TSTR_LIST; //gamesaver void leave_current_map(void); @@ -1415,6 +1420,7 @@ int save_game(long game_time,char *gamename, char is_autosave); void save_map_description(TMPFILE_WR *f); void load_map_description(TMPFILE_RD *f); void free_map_description(); +TSTR_LIST swap_map_description(TSTR_LIST new_list); void wire_save_load(char save); void do_save_dialog(); char ask_save_dialog(char *name_buffer, size_t name_size, char allow_remove); @@ -1762,8 +1768,8 @@ char clk_enter(int id,int xa,int ya,int xr,int yr); //menu int enter_menu(char open); //task! void titles(va_list args); //task! -void run_titles(va_list args); //task! -void effect_show(va_list args); //effektni zobrazeni // task! +void run_titles(void ); +void effect_show(void); //effektni zobrazeni void konec_hry(void); diff --git a/game/globmap.c b/game/globmap.c index bb016e9..38fa16e 100644 --- a/game/globmap.c +++ b/game/globmap.c @@ -630,13 +630,14 @@ struct _tag_map_save_state{ TSTENA *_map_sides; TSECTOR *_map_sectors; TMAP_EDIT_INFO *_map_coord; + TSTR_LIST _map_desc; int _map_size; int _viewsector; int _viewdir; uint32_t _hash; } MAP_SAVE_STATE; -static struct _tag_map_save_state save_state = {NULL,NULL,NULL,0,0,0,0}; +static struct _tag_map_save_state save_state = {NULL,NULL,NULL,NULL,0,0,0,0}; static void save_current_map() { if (save_state._map_coord) { @@ -647,6 +648,7 @@ static void save_current_map() { save_state._map_coord = map_coord; save_state._map_sectors = map_sectors; save_state._map_size = mapsize; + save_state._map_desc = swap_map_description(NULL); save_state._viewsector = viewsector; save_state._viewdir = viewdir; save_state._hash = current_map_hash; @@ -661,6 +663,7 @@ static void restore_saved_map() { free(map_sides); free(map_sectors); free(map_coord); + free_map_description(); mapsize =save_state._map_size; map_sides = save_state._map_sides; map_sectors = save_state._map_sectors; @@ -668,9 +671,11 @@ static void restore_saved_map() { viewsector = save_state._viewsector; viewdir = save_state._viewdir; current_map_hash = save_state._hash; + swap_map_description(save_state._map_desc); save_state._map_sides = NULL; save_state._map_coord = NULL; save_state._map_sectors = NULL; + save_state._map_desc = NULL; } } diff --git a/game/interfac.c b/game/interfac.c index c241815..188df8c 100644 --- a/game/interfac.c +++ b/game/interfac.c @@ -1486,8 +1486,9 @@ int smlouvat_prodej(int cena,int ponuka,int posledni,int puvod,int pocet) if (ponuka==0) return 0; if (ponuka<=cena) return 0; - if (posledni!=0) if (ponuka>=posledni || ponuka>min) return 1; - if (p_ok>r_ok) return 0; + if (ponuka <= min && (posledni==0 || ponukar_ok) return 0; + } if (p_ok>75) return 2; if (p_ok>50) return 3; if (p_ok>25) return 4; @@ -1544,7 +1545,7 @@ THAGGLERESULT smlouvat_dlg(int cena,int puvod,int pocet,int posledni, int money, define(-1,150,15,100,13,0,label,int2ascii(cena,buffer,10)); set_font(H_FBOLD,MSG_COLOR1); define(-1,10,30,1,1,0,label,texty[238]); - define(10,150,30,100,13,0,input_line,8,0,0,"");property(def_border(5,BAR_COLOR),NULL,NULL,0);set_default(""); + define(10,150,30,100,13,0,input_line,7,0,0,"");property(def_border(5,BAR_COLOR),NULL,NULL,0);set_default(""); 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); diff --git a/game/inv.c b/game/inv.c index e20e230..27d16b6 100644 --- a/game/inv.c +++ b/game/inv.c @@ -432,7 +432,7 @@ void draw_placed_items_normal(int celx,int cely,int sect,int side) } } -int count_items_total(short *place) +int count_items_total(const short *place) { int c=0; @@ -441,7 +441,7 @@ int count_items_total(short *place) return c; } -int count_items_visible(short *place) +int count_items_visible(const short *place) { int c=0; @@ -455,7 +455,7 @@ int count_items_visible(short *place) } -int count_items_inside(short *place) +int count_items_inside(const short *place) { int c=1; @@ -479,43 +479,37 @@ int find_item(short *place,int mask) return lastitem; } -static int lastsector; - -static char ValidateSector(word sector, void *_) - { - int pp=map_sectors[sector].sector_type; - if (pp==S_NORMAL || pp==S_SMER || pp==S_LEAVE || pp==S_FLT_SMER) - { - lastsector=sector; - return 1; - } - return 0; +void push_to_destroyed_items(const short *picked_items) { + int new_count = count_items_total(picked_items); + if (new_count == 0) return; + int cur_destroyed = count_items_total(destroyed_items); + short *nw = NewArr(short, new_count+cur_destroyed+1); + for (int i = 0; i < new_count; ++i) { + nw[i] = abs(picked_items[i]); } + for (int i = 0; i < cur_destroyed; ++i) { + nw[new_count+i] = destroyed_items[i]; + } + nw[new_count+cur_destroyed] = 0; + free(destroyed_items); + destroyed_items = nw; +} -void push_item(int sect,int pos,short *picked_item) +void push_item(int sect,int pos,const short *picked_item) { int bc; int pc; int tc; short *p; + char s = map_sectors[sect].sector_type; - if (map_sectors[sect].sector_type==S_DIRA || ISTELEPORTSECT(sect)) + + if (s==S_DIRA || ISTELEPORTSECT(sect) || s == S_SCHODY) sect=map_sectors[sect].sector_tag; - if (sect==0 || map_sectors[sect].sector_type==S_VODA) - { - if (game_extras & EX_RECOVER_DESTROYED_ITEMS) - { - labyrinth_find_path(viewsector,65535,SD_PLAY_IMPS,ValidateSector,NULL, NULL); - push_item(lastsector,viewdir,picked_item); - return; - } - else - { - free(picked_item); - picked_item=NULL; - return; - } - } + if (sect==0 || s==S_VODA || s == S_LAVA || s == S_SSMRT || s == S_LODKA) { + push_to_destroyed_items(picked_item); + return; + } sect=(sect<<2)+pos; bc=count_items_total(map_items[sect]); pc=count_items_total(picked_item); @@ -682,7 +676,7 @@ char pick_item_(int id,int xa,int ya,int xr,int yr) idd=(id+viewdir)&0x3; if (picked_item!=NULL) { - if (map_sectors[sect].sector_type==S_DIRA) + if (map_sectors[sect].sector_type==S_DIRA) { throw_fly(xa,ya,0); letici_veci->speed=0; @@ -2494,7 +2488,6 @@ static int shop_sector; T_CLK_MAP clk_shop[]= { {-1,54,378,497,479,shop_change_player,2+8,-1}, - {-1,0,0,639,479,_exit_shop,8,-1}, {-1,INV_X,INV_Y,INV_X+INV_XS*6,INV_Y+INV_YS*5,shop_bag_click,MS_EVENT_MOUSE_LPRESS,-1}, {1,2+BUYBOX_X,39+BUYBOX_Y,22+BUYBOX_X,76+BUYBOX_Y,shop_block_click,2,H_MS_DEFAULT}, {2,246+BUYBOX_X,39+BUYBOX_Y,266+BUYBOX_X,76+BUYBOX_Y,shop_block_click,2,H_MS_DEFAULT}, @@ -2503,6 +2496,7 @@ T_CLK_MAP clk_shop[]= {-1,337,0,357,14,go_map,2,H_MS_DEFAULT}, {-1,87,0,142,14,game_setup,2,H_MS_DEFAULT}, {-1,30,0,85,14,konec,2,H_MS_DEFAULT}, + {-1,0,0,639,479,_exit_shop,8,-1}, }; static void shop_mouse_event(EVENT_MSG *msg,void **unused) diff --git a/game/menu.c b/game/menu.c index 4dc173c..0721add 100644 --- a/game/menu.c +++ b/game/menu.c @@ -20,6 +20,7 @@ #include "ach_events.h" #include "lang.h" +#include #define MUSIC "TRACK06.MUS" @@ -334,6 +335,12 @@ static void klavesnice(EVENT_MSG *msg,void **unused) } } +static void show_version() { + const char *verstr = "Version: " SKELDAL_VERSION; + set_font(H_FLITT5, RGB888(255,255,255)); + set_aligned_position(639, 479, 2, 2, verstr); + outtext(verstr); +} int enter_menu(char open) { @@ -350,8 +357,9 @@ int enter_menu(char open) put_picture(0,0,ablock(H_MENU_BAR)); put_picture(0,56,ablock(H_ANIM)); ukaz_mysku(); - effect_show(NULL); - //if (open) effect_show(NULL);else showview(0,0,0,0); + show_version(); + effect_show(); + change_click_map(clk_main_menu,CLK_MAIN_MENU); send_message(E_ADD,E_TIMER,prehraj_animaci_v_menu); send_message(E_ADD,E_KEYBOARD,klavesnice); @@ -552,7 +560,7 @@ void titles(va_list args) alock(H_PICTURE); picture=ablock(H_PICTURE); put_picture(0,0,picture); - effect_show(NULL); + effect_show(); titlefont=H_FBIG; set_font(titlefont,RGB(158,210,25));charcolors[1]=0; counter=get_timer_value();newc=counter; @@ -604,7 +612,7 @@ void titles(va_list args) if (send_back)send_message(E_KEYBOARD,27); } -void run_titles(va_list args) +void run_titles(void) { int task_id; task_id=add_task(8196,titles,1,"titulky.TXT"); @@ -612,16 +620,16 @@ void run_titles(va_list args) term_task(task_id); } -void konec_hry() +void konec_hry(void) { int task_id; int timer; - + schovej_mysku(); curcolor=0; bar32(0,0,639,479); - effect_show(NULL); + effect_show(); create_playlist(texty[205]); change_music(get_next_music_from_playlist()); timer=get_timer_value(); @@ -641,7 +649,7 @@ void konec_hry() curcolor=0; bar32(0,0,639,479); ukaz_mysku(); - effect_show(NULL); + effect_show(); timer=get_timer_value(); while (get_timer_value()-timer<150) task_sleep(); } diff --git a/game/realgame.c b/game/realgame.c index 80fd10c..e946bd5 100644 --- a/game/realgame.c +++ b/game/realgame.c @@ -62,7 +62,8 @@ TSTENA *map_sides; TSECTOR *map_sectors; TVYKLENEK *map_vyk; //mapa vyklenku word vyk_max; //pocet vyklenku v mape -short **map_items; +short **map_items = 0; +short *destroyed_items = 0;; char *flag_map; TMAP_EDIT_INFO *map_coord; TSTR_LIST level_texts; diff --git a/game/setup.c b/game/setup.c index 49487a2..bff5d7b 100644 --- a/game/setup.c +++ b/game/setup.c @@ -44,7 +44,7 @@ static void checkbox_animator(THE_TIMER *t) animate_checkbox(10,130,10); } -static int effects[]={SND_GVOLUME,SND_MUSIC,SND_GFX,SND_TREBL,SND_BASS,SND_XBASS}; +static int effects[]={SND_GVOLUME,SND_MUSIC,SND_GFX,SND_TREBL,SND_BASS}; static void do_setup_change(void) { @@ -55,6 +55,7 @@ static void do_setup_change(void) { case 10:set_snd_effect(SND_SWAP,c & 1);break; case 20:set_snd_effect(SND_OUTFILTER,c & 1);break; + case 250:timerspeed_val = TIMERSPEED- c;break; default:set_snd_effect(effects[o_aktual->id/10-20],c);break; } } @@ -111,7 +112,7 @@ static void unwire_setup(void) enable_sort=f_get_value(0,100) & 1; autoattack=f_get_value(0,110) & 1; autosave_enabled=f_get_value(0,120) & 1; - level_preload=f_get_value(0,130) & 1; + gamespeedbattle=(f_get_value(0,130) & 1)?GAMESPEED_FASTBATTLE:gamespeed; delete_from_timer(TM_CHECKBOX); mix_back_sound(32768); close_current(); @@ -187,8 +188,8 @@ void new_setup() case 0:c_default(show_names);break; case 1:c_default(enable_sort);break; case 2:c_default(autoattack);break; - case 3:c_default(1);break; - case 4:c_default(level_preload);break; + case 3:c_default(autosave_enabled);break; + case 4:c_default(gamespeedbattle < gamespeed);break; } } @@ -201,6 +202,9 @@ void new_setup() define(200+i*10,50+i*60,30,30,200,0,skeldal_soupak,effects[i]==SND_MUSIC?127:255);c_default(get_snd_effect(effects[i])); on_control_change(do_setup_change); } + define(200+5*10,50+5*60,30,30,200,0,skeldal_soupak,TIMERSPEED-1);c_default(timerspeed_val >TIMERSPEED?0:TIMERSPEED - timerspeed_val); + on_control_change(do_setup_change); + define(300,559,336,81,21,0,setup_ok_button,texty[174]);on_control_change(exit_setup_action); property(NULL,ablock(H_FTINY),&color_topbar,0); redraw_window(); diff --git a/game/skeldal.c b/game/skeldal.c index 24c5859..e252d79 100644 --- a/game/skeldal.c +++ b/game/skeldal.c @@ -75,12 +75,13 @@ typedef struct inis THE_TIMER timer_tree; + int hl_ptr=H_FIRST_FREE; int debug_enabled=0; char sound_detection=1; int snd_devnum,snd_parm1,snd_parm2,snd_parm3,snd_mixing=22000; -char gamespeed=5; -char gamespeedbattle=0; +uint8_t gamespeed=GAMESPEED; +uint8_t gamespeedbattle=GAMESPEED; char level_preload=1; char *level_fname=NULL; int game_extras=0; @@ -827,6 +828,8 @@ void cti_texty(void) //patch stringtable if (!texty[98]) str_replace(&texty,98,"Ulo\x91it hru jako"); if (!texty[99]) str_replace(&texty,99,"CRT Filter (>720p)"); + str_replace(&texty, 144, "Zrychlit souboje"); + str_replace(&texty, 51, "Celkov\x88 Hudba Efekty V\x98\xA8ky Basy Rychlost"); str_replace(&texty,0,"Byl nalezen p\xA9ipojen\x98 ovlada\x87\nPro aktivaci ovlada\x87""e stiskn\x88te kter\x82koliv tla\x87\xA1tko na ovlada\x87i"); lang_patch_stringtable(&texty, "ui.csv", ""); } @@ -1622,7 +1625,7 @@ static void start(va_list args) openning =0; break; case V_OBNOVA_HRY:load_saved_game();break; - case V_AUTORI:run_titles(NULL);break; + case V_AUTORI:run_titles();break; } } while (!exit_wait); diff --git a/game/souboje.c b/game/souboje.c index a9370a9..6cd3890 100644 --- a/game/souboje.c +++ b/game/souboje.c @@ -678,6 +678,7 @@ void zacatek_kola() viewsector=postavy[select_player].sektor; viewdir=postavy[select_player].direction; redraw_scene(); + recalc_volumes(viewsector,viewdir); } char check_end_game() @@ -878,7 +879,7 @@ char JePozdrzeno() void pozdrz_akci() { - int battlespeed=gamespeed-gamespeed*gamespeedbattle/5; + int battlespeed=gamespeedbattle; SPozdrzeno=get_game_tick_count()+battlespeed*2000/6; } @@ -905,6 +906,7 @@ void prejdi_na_pohled(THUMAN *p) { viewsector=p->sektor; viewdir=p->direction; + recalc_volumes(viewsector,viewdir); pozdrz_akci(); hold_timer(TM_SCENE,1); redraw_scene(); @@ -1144,7 +1146,7 @@ static void pouzij_svitek(THUMAN *p,int ruka) static void play_weapon_anim(int anim_num,int hitpos) { char count_save=global_anim_counter; - int battlespeed=gamespeed-gamespeed*gamespeedbattle/5; + int battlespeed=gamespeedbattle; if (anim_num==0) return; hold_timer(TM_SCENE,1); @@ -1440,6 +1442,7 @@ void jadro_souboje(EVENT_MSG *msg,void **unused) //!!!! Jadro souboje viewsector=postavy[i].sektor; viewdir=postavy[i].direction; build_player_map(); + recalc_volumes(viewsector,viewdir); GlobEvent(MAGLOB_AFTERBATTLE,viewsector,viewdir); autosave(); } @@ -1450,7 +1453,7 @@ void jadro_souboje(EVENT_MSG *msg,void **unused) //!!!! Jadro souboje void wire_jadro_souboje(void) { - int battlespeed=gamespeed-gamespeed*gamespeedbattle/5; + int battlespeed=gamespeedbattle; recalc_volumes(viewsector,viewdir); if (battlespeed<1) battlespeed=1; add_to_timer(TM_SCENE,battlespeed,-1,hrat_souboj); @@ -1924,6 +1927,7 @@ static void souboje_dalsi() while ((!postavy[select_player].used || postavy[select_player].inmaphash != current_map_hash || !postavy[select_player].actions || (postavy[select_player].groupnum!=cd && j>6)) && j); viewsector=postavy[select_player].sektor; viewdir=postavy[select_player].direction; + recalc_volumes(viewsector,viewdir); cur_group=postavy[select_player].groupnum; } @@ -1944,6 +1948,7 @@ static void souboje_dalsi_user() { viewsector=postavy[select_player].sektor; viewdir=postavy[select_player].direction; cur_group=postavy[select_player].groupnum; + recalc_volumes(viewsector,viewdir); } void souboje_vybrano(int d) diff --git a/lang/en/ui.csv b/lang/en/ui.csv index 7703124..98046c6 100644 --- a/lang/en/ui.csv +++ b/lang/en/ui.csv @@ -45,7 +45,7 @@ To activate the controller, press any button on the controller" 48,Start 49,Throw 50,Combat in Progress -51,Overall Music Sounds Treble Bass +51,Overall Music Sounds Treble Bass Speed 52,Stereo Settings (Ultrasound) 53,Swap Sides 54,Output Filter (SBPro) @@ -117,7 +117,7 @@ To activate the controller, press any button on the controller" 141,Rearrange Portraits 142,Auto Attack 143,Auto Save -144,Load All Into Memory +144,Speed up battles 145,Adventure Length: 146,%d day and 147,%d days and diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 4a66d31..a551812 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -12,7 +12,7 @@ SET(files basicobj.c mgifplaya.c pcx.c wav_mem.c - strlists.c + strlite.c cztable.c music.cpp string_table.cpp ) diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index cceb15a..5d6b058 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -95,9 +95,10 @@ elseif(APPLE) target_sources(skeldal PRIVATE 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") + if(STEAM_ENABLED) + set(STEAMLIB ${STEAMWORKS_SDK_DIR}/redistributable_bin/osx/libsteam_api.dylib) + endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") message(STATUS "Building for macOS") target_link_libraries(skeldal ${all_libs} ${STEAMLIB}) else() diff --git a/platform/error.cpp b/platform/error.cpp index 8002e9e..1f635c4 100644 --- a/platform/error.cpp +++ b/platform/error.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "error.h" #include "platform.h" @@ -38,3 +39,18 @@ void send_log_impl(const char *format, ...) { void throw_exception(const char *text) { throw std::runtime_error(std::string("Invoked crash:") + text); } + +std::string exception_to_string(const std::exception& e) { + std::ostringstream oss; + oss << e.what(); + + try { + std::rethrow_if_nested(e); + } catch (const std::exception& nested) { + oss << "\n\n Reason: " << exception_to_string(nested); + } catch (...) { + oss << "\n\n Reason: unknown exception of crash"; + } + + return std::move(oss).str(); +} diff --git a/platform/error.h b/platform/error.h index 5d0e975..e88b2ea 100644 --- a/platform/error.h +++ b/platform/error.h @@ -2,6 +2,11 @@ #ifdef __cplusplus + +#include +#include +std::string exception_to_string(const std::exception& e); + extern "C" { #endif @@ -10,7 +15,8 @@ void send_log_impl(const char *format, ...); void display_error(const char *format, ...); void throw_exception(const char *text); - #ifdef __cplusplus + + } #endif diff --git a/platform/file_access.cpp b/platform/file_access.cpp index b6d1f15..6a4981f 100644 --- a/platform/file_access.cpp +++ b/platform/file_access.cpp @@ -7,23 +7,37 @@ #include "../libs/logfile.h" -std::filesystem::path break_and_compose_path(const std::string_view &pathname, char sep) { + +std::filesystem::path break_and_compose_path(std::string_view pathname, char sep) { + auto utf8_to_path = [](std::string_view sv) -> std::filesystem::path { + return std::filesystem::path(std::u8string(reinterpret_cast(sv.data()), sv.size())); + }; + auto p = pathname.rfind(sep); + if (p == pathname.npos) { if (pathname == "." || pathname == "..") { return std::filesystem::canonical("."); - } else if (pathname.empty()) { - return std::filesystem::current_path().root_path(); - } else if (pathname == std::filesystem::current_path().root_name()) { - return std::filesystem::current_path().root_path(); - } else { - return std::filesystem::current_path()/pathname; } - } - return break_and_compose_path(pathname.substr(0,p), sep) / pathname.substr(p+1); -} + // Detekce Windows drive letter jako "C:" + if (pathname.size() == 2 && std::isalpha(static_cast(pathname[0])) && pathname[1] == ':') { + return utf8_to_path(std::string(pathname) + "\\"); // vždy konstruujeme s \ pro root disku + } + // Kontrola na root (např. "/") – musíme převést pro porovnání + if (utf8_to_path(pathname) == std::filesystem::current_path().root_path()) { + return std::filesystem::current_path().root_path(); + } + + // Vše ostatní relativně vůči current_path + return std::filesystem::current_path() / utf8_to_path(pathname); + } else if (p == 0) { + return std::filesystem::current_path().root_path() / utf8_to_path(pathname); + } + + return break_and_compose_path(pathname.substr(0, p), sep) / utf8_to_path(pathname.substr(p + 1)); +} std::filesystem::path convert_pathname_to_path(const std::string_view &pathname) { auto p = pathname.find('\\'); @@ -81,7 +95,12 @@ const char *file_icase_find(const char *pathname) { FILE *fopen_icase(const char *pathname, const char *mode) { std::filesystem::path path = try_to_find_file(convert_pathname_to_path(pathname)); +#ifdef _WIN32 + std::wstring wmode(mode, mode + std::strlen(mode)); // bezpečnější převod + return _wfopen(path.wstring().c_str(), wmode.c_str()); +#else return fopen(path.string().c_str(), mode); +#endif } static thread_local std::string build_pathname_buffer; diff --git a/platform/linux/app_start.cpp b/platform/linux/app_start.cpp index 760efd1..3e658df 100644 --- a/platform/linux/app_start.cpp +++ b/platform/linux/app_start.cpp @@ -1,6 +1,7 @@ #include "../../game/skeldal.h" #include "../getopt.h" #include "../platform.h" +#include "../error.h" #include #include @@ -67,7 +68,7 @@ int main(int argc, char **argv) { } } catch (const std::exception &e) { - std::cerr << "ERROR: " << e.what() << std::endl; + std::cerr << "ERROR: " << exception_to_string(e) << std::endl; return 1; } diff --git a/platform/linux/map_file.cpp b/platform/linux/map_file.cpp index 89adbc3..24c3810 100644 --- a/platform/linux/map_file.cpp +++ b/platform/linux/map_file.cpp @@ -1,9 +1,7 @@ -extern "C" { #include "map_file.h" #include "../error.h" -} #include #include diff --git a/platform/linux/map_file.h b/platform/linux/map_file.h index e4495ed..2c4c27a 100644 --- a/platform/linux/map_file.h +++ b/platform/linux/map_file.h @@ -1,4 +1,13 @@ #include #include + +#ifdef __cplusplus +extern "C" { +#endif + void *map_file_to_memory(const char *name, size_t *sz); void unmap_file(void *ptr, size_t sz); + +#ifdef __cplusplus +} +#endif diff --git a/platform/linux/skeldal.sh b/platform/linux/skeldal.sh index 03e6c7f..1dedef9 100755 --- a/platform/linux/skeldal.sh +++ b/platform/linux/skeldal.sh @@ -2,6 +2,7 @@ temp_file=$(mktemp /tmp/skeldal.XXXXXX.log) CURDIR=`dirname "$0"` +chmod +x "$CURDIR/skeldal_bin" LD_LIBRARY_PATH=$CURDIR:$LD_LIBRARY_PATH "$CURDIR/skeldal_bin" $* > "$temp_file" 2>&1 exit_code=$? @@ -25,4 +26,3 @@ fi rm $temp_file exit $exit_code - diff --git a/platform/mem.h b/platform/mem.h index eaac886..edaf164 100644 --- a/platform/mem.h +++ b/platform/mem.h @@ -1,2 +1,6 @@ #include -#include +#if defined(__linux__) + #include +#elif defined(__APPLE__) + #include +#endif diff --git a/platform/sdl/format_mapping.h b/platform/sdl/format_mapping.h new file mode 100644 index 0000000..9ac5170 --- /dev/null +++ b/platform/sdl/format_mapping.h @@ -0,0 +1,184 @@ +#pragma once +#include + + +template +struct FormatMapping { + static_assert(false, "Unsupported pixel format"); +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 20; + static constexpr int GShift = 10; + static constexpr int BShift = 0; + static constexpr int AShift = 30; + + static constexpr int RBits = 10; + static constexpr int GBits = 10; + static constexpr int BBits = 10; + static constexpr int ABits = 2; +}; + + +template<> +struct FormatMapping { + static constexpr int RShift = 0; + static constexpr int GShift = 8; + static constexpr int BShift = 16; + static constexpr int AShift = 24; + + static constexpr int RBits = 8; + static constexpr int GBits = 8; + static constexpr int BBits = 8; + static constexpr int ABits = 8; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 16; + static constexpr int GShift = 8; + static constexpr int BShift = 0; + static constexpr int AShift = 24; + + static constexpr int RBits = 8; + static constexpr int GBits = 8; + static constexpr int BBits = 8; + static constexpr int ABits = 8; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 24; + static constexpr int GShift = 16; + static constexpr int BShift = 8; + static constexpr int AShift = 0; + + static constexpr int RBits = 8; + static constexpr int GBits = 8; + static constexpr int BBits = 8; + static constexpr int ABits = 8; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 8; + static constexpr int GShift = 16; + static constexpr int BShift = 24; + static constexpr int AShift = 0; + + static constexpr int RBits = 8; + static constexpr int GBits = 8; + static constexpr int BBits = 8; + static constexpr int ABits = 8; +}; + + +template<> +struct FormatMapping { + static constexpr int RShift = 0; + static constexpr int GShift = 4; + static constexpr int BShift = 8; + static constexpr int AShift = 12; + + static constexpr int RBits = 4; + static constexpr int GBits = 4; + static constexpr int BBits = 4; + static constexpr int ABits = 4; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 8; + static constexpr int GShift = 4; + static constexpr int BShift = 0; + static constexpr int AShift = 12; + + static constexpr int RBits = 4; + static constexpr int GBits = 4; + static constexpr int BBits = 4; + static constexpr int ABits = 4; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 12; + static constexpr int GShift = 8; + static constexpr int BShift = 4; + static constexpr int AShift = 0; + + static constexpr int RBits = 4; + static constexpr int GBits = 4; + static constexpr int BBits = 4; + static constexpr int ABits = 4; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 4; + static constexpr int GShift = 8; + static constexpr int BShift = 12; + static constexpr int AShift = 0; + + static constexpr int RBits = 4; + static constexpr int GBits = 4; + static constexpr int BBits = 4; + static constexpr int ABits = 4; +}; + + +template<> +struct FormatMapping { + static constexpr int RShift = 0; + static constexpr int GShift = 5; + static constexpr int BShift = 10; + static constexpr int AShift = 15; + + static constexpr int RBits = 5; + static constexpr int GBits = 5; + static constexpr int BBits = 5; + static constexpr int ABits = 1; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 10; + static constexpr int GShift = 5; + static constexpr int BShift = 0; + static constexpr int AShift = 15; + + static constexpr int RBits = 5; + static constexpr int GBits = 5; + static constexpr int BBits = 5; + static constexpr int ABits = 1; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 11; + static constexpr int GShift = 6; + static constexpr int BShift = 1; + static constexpr int AShift = 0; + + static constexpr int RBits = 5; + static constexpr int GBits = 5; + static constexpr int BBits = 5; + static constexpr int ABits = 1; +}; + +template<> +struct FormatMapping { + static constexpr int RShift = 1; + static constexpr int GShift = 6; + static constexpr int BShift = 11; + static constexpr int AShift = 0; + + static constexpr int RBits = 5; + static constexpr int GBits = 5; + static constexpr int BBits = 5; + static constexpr int ABits = 1; +}; + + + + diff --git a/platform/sdl/sdl_context.cpp b/platform/sdl/sdl_context.cpp index e0c59c7..01a1d2a 100644 --- a/platform/sdl/sdl_context.cpp +++ b/platform/sdl/sdl_context.cpp @@ -1,9 +1,11 @@ #include "sdl_context.h" #include "keyboard_map.h" +#include "format_mapping.h" #include #include #include "../platform.h" +#include "../error.h" #include #include @@ -34,6 +36,9 @@ void SDLContext::SDL_Deleter::operator ()(SDL_Surface* surface) { void SDLContext::SDL_Deleter::operator ()(SDL_Texture* texture) { SDL_DestroyTexture(texture); } +void SDLContext::SDL_Deleter::operator ()(SDL_PixelFormat* f) { + SDL_FreeFormat(f); +} void SDLContext::SDL_Audio_Deleter::operator()(SDL_AudioDeviceID x) { SDL_CloseAudioDevice(x); @@ -74,10 +79,24 @@ SDLContext::SDLContext() { void handle_sdl_error(const char *msg) { char buff[512]; - snprintf(buff, sizeof(buff), "SDL critical error (check video driver): %s %s",msg, SDL_GetError()); + snprintf(buff, sizeof(buff), "SDL critical error: %s %s",msg, SDL_GetError()); throw std::runtime_error(buff); } +bool isFormatSupported(SDL_Renderer *renderer, Uint32 pixel_format) { + SDL_RendererInfo info; + if (SDL_GetRendererInfo(renderer, &info) != 0) { + handle_sdl_error("Failed to get renderer info"); + return false; + } + + for (Uint32 i = 0; i < info.num_texture_formats; ++i) { + if (info.texture_formats[i] == pixel_format) { + return true; + } + } + return false; +} void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** texture, int width, int height, CrtFilterType type) { @@ -95,6 +114,8 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur default: break; } + + if (type == CrtFilterType::scanlines || type == CrtFilterType::scanlines_2) { width = 32; } else { @@ -105,8 +126,11 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur unsigned int mult_of_base = std::max((height+240)/480,interfer); height = height * interfer / mult_of_base; } + + + // Vytvoř novou texturu ve správné velikosti - *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height); + *texture = SDL_CreateTexture(renderer, _texture_render_format, SDL_TEXTUREACCESS_STREAMING, width, height); if (!*texture) { type = CrtFilterType::none; return; //crt filter failed to create, do not use filter @@ -126,10 +150,12 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur Uint32* pixelArray = (Uint32*)pixels; + if (type == CrtFilterType::scanlines) { - Uint32 darkPixel = 0xA0A0A0FF; - Uint32 transparentPixel = 0xFFFFFFC0; + + Uint32 darkPixel = SDL_MapRGBA(_main_pixel_format.get(), 0xA0, 0xA0, 0xA0, 0xFF); + Uint32 transparentPixel = SDL_MapRGBA(_main_pixel_format.get(), 0xFF, 0xFF, 0xFF, 0xC0); for (int y = 0; y < height; y++) { Uint32 color = ((y & 1)== 0) ? darkPixel : transparentPixel; @@ -141,8 +167,8 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur else if (type == CrtFilterType::scanlines_2) { - Uint32 darkPixel = 0x808080FF; - Uint32 transparentPixel = 0xFFFFFFE0; + Uint32 darkPixel = SDL_MapRGBA(_main_pixel_format.get(), 0x80, 0x80, 0x80, 0xFF); + Uint32 transparentPixel = SDL_MapRGBA(_main_pixel_format.get(), 0xFF, 0xFF, 0xFF, 0xE0); for (int y = 0; y < height; y++) { Uint32 color = (y % 3== 2) ? darkPixel : transparentPixel; @@ -152,10 +178,10 @@ void SDLContext::generateCRTTexture(SDL_Renderer* renderer, SDL_Texture** textur } } else { - static Uint32 red_pixel = 0xFF8080A0; - static Uint32 green_pixel = 0x80FF80A0; - static Uint32 blue_pixel = 0x8080FFA0; - static Uint32 dark_pixel = 0x000000C0; + static Uint32 red_pixel = SDL_MapRGBA(_main_pixel_format.get(), 0xFF, 0x80, 0x80, 0xA0); + static Uint32 green_pixel = SDL_MapRGBA(_main_pixel_format.get(), 0x80, 0xFF, 0x80, 0xA0); + static Uint32 blue_pixel = SDL_MapRGBA(_main_pixel_format.get(), 0x80, 0x80, 0xFF, 0xA0); + static Uint32 dark_pixel = SDL_MapRGBA(_main_pixel_format.get(), 0x0, 0x0, 0x00, 0xA0); for (int y = 2; y < height; y++) { if (type == CrtFilterType::rgb_matrix_2) { for (int x = 2; x < width; x+=3) { @@ -199,6 +225,48 @@ static void crash_sdl_exception() { abort(); } +// Seznam akceptovatelných formátů odpovídajících RGBA8888 (v různém pořadí) +constexpr Uint32 acceptable_formats[] = { + SDL_PIXELFORMAT_RGBA8888, + SDL_PIXELFORMAT_ARGB8888, + SDL_PIXELFORMAT_BGRA8888, + SDL_PIXELFORMAT_ABGR8888, + + SDL_PIXELFORMAT_RGBA4444, + SDL_PIXELFORMAT_ARGB4444, + SDL_PIXELFORMAT_BGRA4444, + SDL_PIXELFORMAT_ABGR4444, + + SDL_PIXELFORMAT_ARGB2101010, +}; + +constexpr bool is_acceptable_format(Uint32 format) { + for (size_t i = 0; i < sizeof(acceptable_formats)/sizeof(acceptable_formats[0]); ++i) { + if (format == acceptable_formats[i]) { + return true; + } + } + return false; +} + +static Uint32 find_best_rgba_like_format(SDL_Renderer* renderer) { + SDL_RendererInfo info; + if (SDL_GetRendererInfo(renderer, &info) != 0) { + return 0; + } + + if ((info.max_texture_width != 0 && info.max_texture_width < 640) || + (info.max_texture_height!= 0 && info.max_texture_height < 480)) return 0; + + for (Uint32 i = 0; i < info.num_texture_formats; ++i) { + Uint32 fmt = info.texture_formats[i]; + if (is_acceptable_format(fmt)) { + return fmt; + } + } + + return 0; +} void SDLContext::init_video(const VideoConfig &config, const char *title) { static Uint32 update_request_event = SDL_RegisterEvents(1); @@ -219,15 +287,17 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { _enable_crt = false; } - _fullscreen_mode = config.fullscreen; _mouse_size = config.cursor_size; std::atomic done = false; std::exception_ptr e; + std::string_view stage; + std::string rname; _render_thread = std::jthread([&](std::stop_token stp){ bool err = false; try { + stage = "window"; SDL_Window *window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_RESIZABLE|(_fullscreen_mode?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); @@ -237,13 +307,49 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { } _window.reset(window); - SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, config.composer); - if (!renderer) { - handle_sdl_error("Failed to create composer"); + + auto composer = config.composer; + + stage = "renderer"; + + while (true) { + + SDL_Renderer *renderer = SDL_CreateRenderer(_window.get(), -1, composer); + if (!renderer) { + if (config.composer & SDL_RENDERER_SOFTWARE) { + handle_sdl_error("Failed to create composer"); + } else { + composer |= SDL_RENDERER_SOFTWARE; + continue; + } + } + + _texture_render_format = find_best_rgba_like_format(renderer); + if (_texture_render_format == 0) { + if (composer & SDL_RENDERER_SOFTWARE) { + throw std::runtime_error("Failed to create composer, failed software fallback"); + } else { + SDL_DestroyRenderer(renderer); + composer |= SDL_RENDERER_SOFTWARE; + continue; + } + } + _renderer.reset(renderer); + break; } + SDL_RendererInfo rinfo; - SDL_GetRendererInfo(renderer, &rinfo); + SDL_GetRendererInfo(_renderer.get(), &rinfo); + + rname = rinfo.name; + + stage = "pixel format"; + + _main_pixel_format.reset(SDL_AllocFormat(_texture_render_format)); + if (!_main_pixel_format) { + handle_sdl_error("Failed to create texture format"); + } if (istrcmp(config.scale_quality, "auto") == 0) { if (rinfo.flags & SDL_RENDERER_ACCELERATED) { @@ -253,20 +359,30 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, config.scale_quality); } - _renderer.reset(renderer); - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB1555, SDL_TEXTUREACCESS_STREAMING, 640, 480); + + + stage = "main render target"; + + + SDL_Texture *texture = SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STREAMING, 640, 480); if (!texture) { handle_sdl_error("Failed to create render target"); } SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); _texture.reset(texture); - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB1555, SDL_TEXTUREACCESS_STREAMING, 640, 480); + + stage = "secondary render target"; + + texture = SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STREAMING, 640, 480); if (!texture) { handle_sdl_error("Failed to create second render target"); } SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); _texture2.reset(texture); + + stage = "all done"; + _visible_texture = _texture.get(); _hidden_texture = _texture2.get(); } catch (...) { @@ -294,7 +410,17 @@ void SDLContext::init_video(const VideoConfig &config, const char *title) { done.wait(false); if (e) { _render_thread.join(); - std::rethrow_exception(e); + try { + std::rethrow_exception(e); + } catch (...) { + std::throw_with_nested( + std::runtime_error(std::string("Oops! The application couldn't start properly (problem during SDL initialization). Stage: [") + .append(stage).append("]\n\n" + "Renderer: ").append(rname).append("\n\n" + "This may be caused by outdated or missing graphics or audio drivers." + "To fix this, please try the following:\n- Restart your computer and try again\n- " + "Make sure your graphics and sound drivers are up to date."))); + } } @@ -335,6 +461,102 @@ int SDLContext::adjust_deadzone(int v, short deadzone) { return 0; } +template +constexpr Uint32 shift_bits_5(Uint32 val) { + if constexpr(shift > 0) { + return val << shift | shift_bits_5(val); + } else if constexpr(shift < 0) { + return val >> (-shift); + } else { + return val; + } +} + + +template +void SDLContext::convert_bitmap(const void *pixels, SDL_Rect rect, int pitch) { + + constexpr auto RShift = FormatMapping::RShift; + constexpr auto GShift = FormatMapping::GShift; + constexpr auto BShift = FormatMapping::BShift; + constexpr auto AShift = FormatMapping::AShift; + constexpr auto RBits = FormatMapping::RBits; + constexpr auto GBits = FormatMapping::GBits; + constexpr auto BBits = FormatMapping::BBits; + constexpr auto ABits = FormatMapping::ABits; + + const Uint16 *src = static_cast(pixels); + auto trg = converted_pixels.data(); + for (int y = 0; y < rect.h; ++y) { + for (int x = 0; x < rect.w; ++x) { + Uint16 pixel = src[x]; + Uint32 a = (pixel & 0x8000) ? 0 : 0x1F; + Uint32 r = ((pixel >> 10) & 0x1F); + Uint32 g = ((pixel >> 5) & 0x1F); + Uint32 b = (pixel & 0x1F); + + r = shift_bits_5(r); + g = shift_bits_5(g); + b = shift_bits_5(b); + a = shift_bits_5(a); + + trg[x] = (r << RShift) | (g << GShift) | (b << BShift) | (a << AShift); + } + trg += rect.w; + src = src + pitch / 2; + } +} + +void SDLContext::update_texture_with_conversion(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) +{ + SDL_Rect r; + if (rect) { + r = *rect; + } else { + SDL_QueryTexture(texture, nullptr, nullptr, &r.w, &r.h); + r.x = 0; + r.y = 0; + } + + converted_pixels.clear(); + converted_pixels.resize(r.w * r.h); + + switch (_texture_render_format) { + case SDL_PIXELFORMAT_ABGR8888: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_ARGB8888: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_ARGB2101010: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_RGBA8888: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_BGRA8888: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_ABGR4444: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_ARGB4444: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_BGRA4444: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_RGBA4444: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_ABGR1555: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_ARGB1555: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_RGBA5551: + convert_bitmap(pixels, r, pitch);break; + case SDL_PIXELFORMAT_BGRA5551: + convert_bitmap(pixels, r, pitch);break; + default: + return; + } + + if (SDL_UpdateTexture(texture, &r, converted_pixels.data(), r.w * 4) < 0) { + handle_sdl_error("Failed to update texture"); + } +} + static int axis_dynamic(int c) { double f = std::floor(std::pow(std::abs(c)*0.001,2)*0.025); @@ -698,19 +920,19 @@ void SDLContext::update_screen(bool force_refresh) { SDL_Rect r; pop_item(iter, r); std::string_view data = pop_data(iter, r.w*r.h*2); - if (SDL_UpdateTexture(_texture.get(), &r, data.data(), r.w*2)<0) handle_sdl_error("Update of render target failed"); + update_texture_with_conversion(_texture.get(), &r, data.data(), r.w*2); } break; case DisplayRequest::show_mouse_cursor: { SDL_Rect r; pop_item(iter, r); std::string_view data = pop_data(iter, r.w*r.h*2); - _mouse.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_ARGB1555,SDL_TEXTUREACCESS_STATIC, r.w, r.h)); + _mouse.reset(SDL_CreateTexture(_renderer.get(), _texture_render_format,SDL_TEXTUREACCESS_STATIC, r.w, r.h)); if (!_mouse) handle_sdl_error("Failed to create surface for mouse cursor"); SDL_SetTextureBlendMode(_mouse.get(), SDL_BLENDMODE_BLEND); _mouse_rect.w = r.w; _mouse_rect.h = r.h; - if (SDL_UpdateTexture(_mouse.get(), NULL, data.data(), r.w*2)<0) handle_sdl_error("Update of mouse cursor failed"); + update_texture_with_conversion(_mouse.get(), NULL, data.data(), r.w*2); } break; case DisplayRequest::hide_mouse_cursor: { @@ -747,10 +969,10 @@ void SDLContext::update_screen(bool force_refresh) { if (iter == _sprites.end()) { iter = _sprites.insert(iter,{id}); } - iter->_txtr.reset(SDL_CreateTexture(_renderer.get(), SDL_PIXELFORMAT_ARGB1555, SDL_TEXTUREACCESS_STATIC,r.w, r.h)); + iter->_txtr.reset(SDL_CreateTexture(_renderer.get(), _texture_render_format, SDL_TEXTUREACCESS_STATIC,r.w, r.h)); if (!iter->_txtr) handle_sdl_error("Failed to create compositor sprite"); SDL_SetTextureBlendMode(iter->_txtr.get(), SDL_BLENDMODE_BLEND); - if (SDL_UpdateTexture(iter->_txtr.get(), NULL, data.data(), r.w*2)<0) handle_sdl_error("Update of sprite failed"); + update_texture_with_conversion(iter->_txtr.get(), NULL, data.data(), r.w*2); iter->_rect = r; update_zindex(); } break; @@ -851,10 +1073,8 @@ void SDLContext::push_update_msg(const SDL_Rect &rc, const uint16_t *data, int p _display_update_queue.resize(sz+rc.w*rc.h*2); short *trg = reinterpret_cast(_display_update_queue.data()+sz); for (int yp = 0; yp < rc.h; ++yp) { - for (int xp = 0; xp < rc.w; ++xp) { - *trg = data[xp] ^ 0x8000; - ++trg; - } + std::copy(data, data+rc.w, trg); + trg += rc.w; data += pitch; } } @@ -1009,7 +1229,7 @@ void put_picture_ex(unsigned short x,unsigned short y,const void *p, unsigned sh } void SDLContext::push_hi_image(const unsigned short *image) { - SDL_Rect rc; + SDL_Rect rc = {}; rc.w= image[0]; rc.h =image[1]; push_item(rc); @@ -1019,7 +1239,6 @@ void SDLContext::push_hi_image(const unsigned short *image) { unsigned short *trg = reinterpret_cast(_display_update_queue.data()+sz); std::fill(trg, trg+imgsz, 0x8000); put_picture_ex(0, 0, image, trg, rc.w); - std::transform(trg, trg+imgsz, trg, [](unsigned short &x)->unsigned short {return x ^ 0x8000;}); } void SDLContext::show_mouse_cursor(const unsigned short *ms_hi_format, SDL_Point finger) { diff --git a/platform/sdl/sdl_context.h b/platform/sdl/sdl_context.h index 0e11bda..d3cd613 100644 --- a/platform/sdl/sdl_context.h +++ b/platform/sdl/sdl_context.h @@ -170,6 +170,7 @@ protected: void operator()(SDL_Renderer *); void operator()(SDL_Surface *); void operator()(SDL_Texture *); + void operator()(SDL_PixelFormat* f); }; struct BlendTransitionReq { @@ -237,13 +238,16 @@ protected: std::unique_ptr _texture2; std::unique_ptr _crt_effect; std::unique_ptr _mouse; + std::unique_ptr _main_pixel_format; unique_value _audio; - SDL_Texture *_visible_texture; - SDL_Texture *_hidden_texture; + SDL_Texture *_visible_texture = nullptr; + SDL_Texture *_hidden_texture = nullptr; + uint32_t _texture_render_format = SDL_PIXELFORMAT_ARGB1555; bool _fullscreen_mode = false; bool _present = false; + bool _convert_format = false; std::atomic _key_control = false; std::atomic _key_shift = false; std::atomic _key_capslock = false; @@ -252,11 +256,12 @@ protected: std::vector _display_update_queue; + std::vector converted_pixels; using QueueIter = const char *; std::queue _keyboard_queue; SDL_Rect _mouse_rect; SDL_Point _mouse_finger; - float _mouse_size; + float _mouse_size = 1; SpriteList _sprites; @@ -307,4 +312,11 @@ protected: void generate_j_event(int button, char up); static int adjust_deadzone(int v, short deadzone); + void update_texture_with_conversion(SDL_Texture * texture, + const SDL_Rect * rect, + const void *pixels, int pitch); + + template + void convert_bitmap(const void *pixels, SDL_Rect r, int pitch); + }; diff --git a/platform/sdl/sound.cpp b/platform/sdl/sound.cpp index 6c2c562..2885308 100644 --- a/platform/sdl/sound.cpp +++ b/platform/sdl/sound.cpp @@ -299,7 +299,8 @@ static void update_music_volume(){ float v = music_volume * master_volume; for (int i = 0; i <2; i++) sound_mixer.visit_track(music_track_id_base+i,[&](WaveMixer<2> &m){ - m.set_channel_volume(v, v); + m.set_channel_volume(0, v); + m.set_channel_volume(1, v); }); } diff --git a/platform/sdl/wave_mixer.h b/platform/sdl/wave_mixer.h index 191b6dd..5baf9fe 100644 --- a/platform/sdl/wave_mixer.h +++ b/platform/sdl/wave_mixer.h @@ -91,8 +91,8 @@ public: } else { (*iter) += value.left * vol[0]; ++iter; - (*iter) += value.right * vol[0]; - ++iter; + (*iter) += value.right * vol[1]; + std::advance(iter, channels-1); } } diff --git a/platform/windows/app_start.cpp b/platform/windows/app_start.cpp index 64903a4..cf08557 100644 --- a/platform/windows/app_start.cpp +++ b/platform/windows/app_start.cpp @@ -1,6 +1,7 @@ #include "../../game/skeldal.h" #include "../getopt.h" #include "../platform.h" +#include "../error.h" #include #include #include @@ -86,7 +87,7 @@ int main(int argc, char **argv) { } } catch (const std::exception &e) { - cfg.show_error(e.what()); + cfg.show_error(exception_to_string(e).c_str()); return 1; } catch (...) { diff --git a/platform/windows/save_folder.cpp b/platform/windows/save_folder.cpp index c0b53c9..5b002a3 100644 --- a/platform/windows/save_folder.cpp +++ b/platform/windows/save_folder.cpp @@ -15,13 +15,17 @@ std::string getSavedGamesDirectory() { PWSTR path = nullptr; if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_SavedGames, 0, NULL, &path))) { fs::path savedGamesPath(path); - CoTaskMemFree(path); - return (savedGamesPath / SAVEGAME_FOLDERNAME).string(); + CoTaskMemFree(path); + + // Převod na UTF-8 std::string + std::u8string utf8 = (savedGamesPath / SAVEGAME_FOLDERNAME).u8string(); + return std::string(reinterpret_cast(utf8.data()), utf8.size()); } else { - display_error("Failed to retrieve FOLDEROD_SavedGames"); + display_error("Failed to retrieve FOLDERID_SavedGames"); abort(); } } + const char *get_default_savegame_directory() { static std::string dir = getSavedGamesDirectory(); diff --git a/skeldal.ini b/skeldal.ini index 5e7be5f..c49ddbe 100644 --- a/skeldal.ini +++ b/skeldal.ini @@ -47,6 +47,7 @@ #crt_filter=auto #scale_quality=auto #composer=auto +#sdl_renderer_driver=software #aspect_ratio=4:3 #cursor_size=100 diff --git a/tools/ddl_ar_class.cpp b/tools/ddl_ar_class.cpp index 0142254..e59a8bb 100644 --- a/tools/ddl_ar_class.cpp +++ b/tools/ddl_ar_class.cpp @@ -45,7 +45,7 @@ DDLArchive::Extracted DDLArchive::extract_file(std::ifstream &s, std::vector data; data.resize(sz); s.read(data.data(), sz); - if (s.gcount() != sz) return {fname, false, {}}; + if (static_cast(s.gcount()) != sz) return {fname, false, {}}; return {fname, true, std::move(data)}; } diff --git a/version.in.h b/version.in.h new file mode 100644 index 0000000..0f14310 --- /dev/null +++ b/version.in.h @@ -0,0 +1,5 @@ +#pragma once + +#define SKELDAL_VERSION "@APP_VERSION@" + +