diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 59e79a11..cf0f48a8 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v3.7.4 +;v3.8 [Main] ;Change to 1 if you want to use command line args to tell sfall to use another ini file. @@ -345,7 +345,7 @@ CorpseLineOfFireFix=0 ;Set a number of milliseconds to idle each input loop ;Set to -1 to disable ;Set to 0 to idle only if other processes are waiting for processor time -;Set to 1 (or some higher number if needed) to prevent 100% cpu use +;Set to 1 (or some higher number if needed, the maximum is 127) to prevent 100% cpu use ProcessorIdle=-1 ;Set to 1 if using the hero appearance mod @@ -472,6 +472,12 @@ EncounterTableSize=0 ;Set to 1 to disable the pipboy alarm button DisablePipboyAlarm=0 +;Uncomment the next four lines to move the main menu buttons and credit text (the 'Copyright(c)' line on the main menu) +;MainMenuOffsetX=0 +;MainMenuOffsetY=0 +;MainMenuCreditsOffsetX=0 +;MainMenuCreditsOffsetY=0 + ;Prevents you from using super stims on a critter who is at full health. SuperStimExploitFix=0 @@ -483,7 +489,7 @@ QuickPocketsApCostReduction=2 ObjCanSeeObj_ShootThru_Fix=0 ;Set to 1 to enable the balanced bullet distribution formula for burst attacks -ComputeSprayMod=1 +ComputeSprayMod=0 ;These options modify the bullet distribution of burst attacks if ComputeSprayMod is 1 ;All the bullets are divided into three groups: central, left and right diff --git a/artifacts/scripting/README.txt b/artifacts/scripting/README.txt index 0f244cfc..9831d690 100644 --- a/artifacts/scripting/README.txt +++ b/artifacts/scripting/README.txt @@ -6,7 +6,7 @@ This folder contains documentation about sfall scripting extensions. dik.h - DX scancodes constants for use with key_pressed function and HOOK_KEYPRESS sfall function notes.txt - incomplete reference for new opcodes - sfall function list.txt - list of all sfall opcodes (w/o descriptions) + sfall opcode list.txt - list of all sfall opcodes (w/o descriptions) hookscripts.txt - detailed manual for using hook scripts to modify engine behavior arrays.txt - manual for sfall arrays diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 7f14569a..062afe42 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -187,3 +187,12 @@ #define party_member_list_critters party_member_list(0) #define party_member_list_all party_member_list(1) + + +#define spatial_radius(obj) sfall_func1("spatial_radius", obj) +#define critter_inven_obj2(obj, type) sfall_func2("critter_inven_obj2", obj, type) +#define intface_redraw sfall_func0("intface_redraw") +#define intface_hide sfall_func0("intface_hide") +#define intface_show sfall_func0("intface_show") +#define intface_is_hidden sfall_func0("intface_is_hidden") +#define exec_map_update_scripts sfall_func0("exec_map_update_scripts") diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index cd140e25..a8dd1bcc 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -77,6 +77,22 @@ force_aimed_shots and disable_aimed_shots allow overriding the normal rules rega get/set_critter_skill_points will get/set the number of additional points a critter has in a skill, on top of whatever they have from their stats and other bonuses. Note that skill points are part of the proto, so calling set_skill_points on a critter will affect all critters that share the same proto. + +------------------------------ +------ TYPES REFERENCE ------- +------------------------------ + +> Data types mentioned in this document: + +void - means opcode does not return any value +any - any type +int - integer number +float - floating point number +string - string (text) value +object - pointer to game object (actually an integer) +array - array ID to be used with array-related functions (actually an integer) + + ---------------------------------------------- ------ FUNCTION REFERENCE (incomplete) ------- ---------------------------------------------- @@ -129,15 +145,15 @@ get/set_critter_skill_points will get/set the number of additional points a crit - adjusts how fast time passes while you're on the world map. It takes a single float as an argument, where 1 is the normal speed. - This function works in addition to the WorldMapTimeMod setting in ddraw.ini and the pathfinder perk, rather than overriding it, so calling set_map_time_multi(0.5) when the player has 2 levels of pathfinder would result in time passing at 25% the normal speed on the world map. -> void remove_script(objptr) +> void remove_script(object) - accepts a pointer to an object and will remove the script from that object. -> void set_script(objptr, int scriptid) +> void set_script(object, int scriptid) - accepts a pointer to an object and scriptID, and applies the given script to an object (scriptID accept the same values as create_object_sid from sfall 3.6) - If used on an object that is already scripted, it will remove the existing script first; you cannot have multiple scripts attached to a single object. Calling set_script on self_obj will have all sorts of wacky side effects, and should be avoided. - if you add 0x80000000 to the sid when calling set_script, map_enter_p_proc will be SKIPPED. The start proc will always be run. -> int get_script(objptr) +> int get_script(object) - accepts a pointer to an object and returns it's scriptID (line number in scripts.lst), or -1 if the object is unscripted. > void set_self(int obj) @@ -159,9 +175,9 @@ get/set_critter_skill_points will get/set the number of additional points a crit - It is not saved into the save game, so needs to be called once per reload. - Be careful not to let the player obtain a perk when no perks are available to pick, or the game may crash. -> ObjectPtr get_last_target(objptr) +> object get_last_target(object) - will return the last critter to be deliberately attacked -> ObjectPtr get_last_attacker(objptr) +> object get_last_attacker(object) - will return the last critter to deliberately launch an attack against the argument critter. - If a critter has not launched/recieved an attack, it will return 0. This is only stored for the duration of combat, and outside of combat both functions will always return 0. @@ -170,7 +186,7 @@ get/set_critter_skill_points will get/set the number of additional points a crit - "max" will replace 95% success chance cap (so you can set 100% maximum chance, for instance) - "mod" will add this much percent to each success chance. for example if your chance is 50% and "mod" is 20, you will get 70% actual success rate -> void set_critter_pickpocket_mod(CritterPtr, int max, int mod) +> void set_critter_pickpocket_mod(object, int max, int mod) - the same as above, but applies only to specific critter > void reg_anim_combat_check @@ -222,17 +238,17 @@ Some utility/math functions are available: > string substr(string, start, length) - cuts a substring from a string starting at "start" up to "length" characters. If start is negative - it indicates starting position from the end of the string (for example substr("test", -2, 2) will return last 2 charactes: "st"). If length is negative - it means so many characters will be omitted from the end of string (example: substr("test", 0, -2) will return string without last 2 characters: "te") -> int strlen(char* string) +> int strlen(string string) - returns string length -> string sprintf(char* format, any value) +> string sprintf(string format, any value) - formats given value using standart syntax of C printf function (google "printf" for format details). However it is limited to formatting only 1 value. - can be used to get character by ASCII code ("%c") > int typeof(any value) - returns type of the given value: VALTYPE_INT, VALTYPE_FLOAT or VALTYPE_STR. -> int charcode(char* string) +> int charcode(string string) - returns ASCII code for the first character in given string > ^ operator (exponentiation) @@ -286,12 +302,12 @@ Some utility/math functions are available: > int tile_light(int elevation, int tileNum) - returns light intensity at the given tile in range from 0 to 65535 -> ObjectPtr obj_blocking_line(ObjectPtr objFrom, int tileTo, int blockingType) +> object obj_blocking_line(object objFrom, int tileTo, int blockingType) - returns first object which blocks direct linear path from objFrom to tileTo using selected blocking function (see BLOCKING_TYPE_* constants in sfall.h) - if path is clear (no blocker was encountered by selected function) - returns 0 - objFrom is always excluded from calculations, but is required to be a valid object -> ObjectPtr obj_blocking_tile(int tileNum, int elevation, int blockingType) +> object obj_blocking_tile(int tileNum, int elevation, int blockingType) - returns first object blocking given tile using given blocking function or 0 if tile is clear > array tile_get_objs(int tileNum, int elevation) @@ -301,22 +317,58 @@ Some utility/math functions are available: > array party_member_list(int includeHidden) - returns array of all current party members (0 - only critter-type, alive and visible will be returned, 1 - all object, including Trunk, etc.) -> array path_find_to(ObjectPtr objFrom, int tileTo, int blockingType) +> array path_find_to(object objFrom, int tileTo, int blockingType) - returns the shortest path to a given tile using given blocking function as an array of tile directions (0..5) to move on each step - array length equals to a number of steps - empty array means that specified target cannot be reached -> ObjectPtr create_spatial(int scriptID, int tile, int elevation, int radius) +> object create_spatial(int scriptID, int tile, int elevation, int radius) - creates new spatial script with given SID, at given tile, and radius > int art_exists(int artFID) - checks if given artFID exists in the game - useful when you want to check if critter can use specific weapon: art_exists((artFid bwand 0xffff0fff) bwor (weaponAnim * 0x1000)) -> int obj_is_carrying_obj(ObjectPtr invenObj, ObjectPtr itemObj) +> int obj_is_carrying_obj(object invenObj, object itemObj) - returns number of itemObj inside invenObj's inventory, note that both arguments are object pointers - useful when dealing with different stacks of same item (obj_is_carrying_obj_pid just returns total for all stacks of the same PID) +> any sfall_funcX(string funcName, ...) +- these opcodes allows to use additional scripting functions, that do not require new opcode +- first argument is always function name (string) +- there are 7 versions of this opcode for different number of additional arguments (for convenience) +- opcodes have return value, but it is not neccessary to use it + + +------------------------------------ +------ sfall_funcX functions ------- +------------------------------------ + +> array sfall_func0("get_metarule_table") +- returns names of all currently available scripting functions (via sfall_funcX) + +> int sfall_func1("spatial_radius", object object) +- return radius of spatial script, associated with given dummy-object (returned by create_spatial) + +> object sfall_func2("critter_inven_obj2", object invenObj, int type) +- works just like vanilla critter_inven_obj, but correctly reports item in player's inactive hand slot + +> void sfall_func0("intface_redraw") +- redraws main game interface +- useful after direct changes to current player weapons or stats to reflect changes + +> void sfall_func0("intface_hide") +- hides main interface + +> void sfall_func0("intface_show") +- shows main interface + +> int sfall_func0("intface_is_hidden") +- returns 1 if interface is currently hidden, 0 otherwise + +> void sfall_func0("exec_map_update_scripts") +- executes map_update_p_proc for all objects on map and global/hook scripts as well + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/artifacts/scripting/sfall function list.txt b/artifacts/scripting/sfall opcode list.txt similarity index 65% rename from artifacts/scripting/sfall function list.txt rename to artifacts/scripting/sfall opcode list.txt index f761028a..e1dd9d61 100644 --- a/artifacts/scripting/sfall function list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -2,12 +2,12 @@ *0x8156 - int read_byte(int address) *0x8157 - int read_short(int address) *0x8158 - int read_int(int address) -*0x8159 - char* read_string(int address) +*0x8159 - string read_string(int address) *0x81cf - void write_byte(int address, int value) *0x81d0 - void write_short(int address, int value) *0x81d1 - void write_int(int address, int value) -*0x821b - void write_string(int address, char* value) +*0x821b - void write_string(int address, string value) *0x81d2 - void call_offset_v0(int address) *0x81d3 - void call_offset_v1(int address, int arg1) @@ -25,10 +25,10 @@ 0x815c - int get_pc_base_stat(int StatID) 0x815d - int get_pc_extra_stat(int StatID) -0x815e - void set_critter_base_stat(CritterPtr, int StatID, int value) -0x815f - void set_critter_extra_stat(CritterPtr, int StatID, int value) -0x8160 - int get_critter_base_stat(CritterPtr, int StatID) -0x8161 - int get_critter_extra_stat(CritterPtr, int StatID) +0x815e - void set_critter_base_stat(object, int StatID, int value) +0x815f - void set_critter_extra_stat(object, int StatID, int value) +0x8160 - int get_critter_base_stat(object, int StatID) +0x8161 - int get_critter_extra_stat(object, int StatID) 0x8242 - void set_critter_skill_points(int critter, int skill, int value) 0x8243 - int get_critter_skill_points(int critter, int skill) 0x8244 - void set_available_skill_points(int value) @@ -55,18 +55,18 @@ 0x8164 - bool game_loaded() 0x8165 - bool graphics_funcs_available() -0x8166 - int load_shader(char* path) +0x8166 - int load_shader(string path) 0x8167 - void free_shader(int ID) 0x8168 - void activate_shader(int ID) 0x8169 - void deactivate_shader(int ID) -0x816d - void set_shader_int(int ID, char* param, int value) -0x816e - void set_shader_float(int ID, char* param, float value) -0x816f - void set_shader_vector(int ID, char* param, float f1, float f2, float f3, float f4) +0x816d - void set_shader_int(int ID, string param, int value) +0x816e - void set_shader_float(int ID, string param, float value) +0x816f - void set_shader_vector(int ID, string param, float f1, float f2, float f3, float f4) 0x81ad - int get_shader_version() 0x81ae - void set_shader_mode(int mode) 0x81b0 - void force_graphics_refresh(bool enabled) 0x81b1 - int get_shader_texture(int ID, int texture) -0x81b2 - void set_shader_texture(int ID, char* param, int texID) +0x81b2 - void set_shader_texture(int ID, string param, int texID) 0x816a - void set_global_script_repeat(int frames) 0x819b - void set_global_script_type(int type) @@ -82,9 +82,9 @@ 0x8173 - int get_world_map_x_pos() 0x8174 - int get_world_map_y_pos() -0x8175 - void set_dm_model(char* name) -0x8176 - void set_df_model(char* name) -0x8177 - void set_movie_path(char* filename, int movieid) +0x8175 - void set_dm_model(string name) +0x8176 - void set_df_model(string name) +0x8177 - void set_movie_path(string filename, int movieid) 0x8178 - void set_perk_image(int perkID, int value) 0x8179 - void set_perk_ranks(int perkID, int value) @@ -103,8 +103,8 @@ 0x8196 - void set_perk_int(int perkID, int value) 0x8187 - void set_perk_agl(int perkID, int value) 0x8188 - void set_perk_lck(int perkID, int value) -0x8189 - void set_perk_name(int perkID, char* value) -0x818a - void set_perk_desc(int perkID, char* value) +0x8189 - void set_perk_name(int perkID, string value) +0x818a - void set_perk_desc(int perkID, string value) 0x8247 - void set_perk_freq(int value) 0x818b - void set_pipboy_available(int available) @@ -116,18 +116,18 @@ 0x818f - void set_perk_owed(int value) 0x8190 - int get_perk_available(int perk) -0x8191 - int get_critter_current_ap(CritterPtr) -0x8192 - void set_critter_current_ap(CritterPtr, int ap) +0x8191 - int get_critter_current_ap(object critter) +0x8192 - void set_critter_current_ap(object critter, int ap) 0x8193 - int active_hand() 0x8194 - void toggle_active_hand() -0x8195 - void set_weapon_knockback(WeaponPtr, int type, float value) -0x8196 - void set_target_knockback(CritterPtr, int type, float value) -0x8197 - void set_attacker_knockback(CritterPtr, int type, float value) -0x8198 - void remove_weapon_knockback(WeaponPtr) -0x8199 - void remove_target_knockback(CritterPtr) -0x819a - void remove_attacker_knockback(CritterPtr) +0x8195 - void set_weapon_knockback(object weapon, int type, float value) +0x8196 - void set_target_knockback(object critter, int type, float value) +0x8197 - void set_attacker_knockback(object critter, int type, float value) +0x8198 - void remove_weapon_knockback(object weapon) +0x8199 - void remove_target_knockback(object critter) +0x819a - void remove_attacker_knockback(object critter) 0x819d - void set_sfall_global(string/int varname, int/float value) 0x819e - int get_sfall_global_int(string/int varname) @@ -152,26 +152,26 @@ 0x81aa - void set_xp_mod(int percentage) 0x81ab - void set_perk_level_mod(int levels) -0x81c5 - void set_critter_hit_chance_mod(CritterPtr, int max, int mod) +0x81c5 - void set_critter_hit_chance_mod(object, int max, int mod) 0x81c6 - void set_base_hit_chance_mod(int max, int mod) -0x81c7 - void set_critter_skill_mod(CritterPtr, int max) +0x81c7 - void set_critter_skill_mod(object, int max) 0x81c8 - void set_base_skill_mod(int max) -0x81c9 - void set_critter_pickpocket_mod(CritterPtr, int max, int mod) +0x81c9 - void set_critter_pickpocket_mod(object, int max, int mod) 0x81ca - void set_base_pickpocket_mod(int max, int mod) 0x81a3 - int eax_available() 0x81a4 - void set_eax_environment(int environment) -0x81a5 - void inc_npc_level(char* npc) -0x8241 - int get_npc_level(char* npc) +0x81a5 - void inc_npc_level(string npc) +0x8241 - int get_npc_level(string npc) 0x81a6 - int get_viewport_x() 0x81a7 - int get_viewport_y() 0x81a8 - void set_viewport_x(int view_x) 0x81a9 - void set_viewport_y(int view_y) -0x81ac - int get_ini_setting(char* setting) -0x81eb - char* get_ini_string(char* setting) +0x81ac - int get_ini_setting(string setting) +0x81eb - string get_ini_string(string setting) 0x81af - int get_game_mode() @@ -179,14 +179,14 @@ 0x81b6 - void set_car_current_town(int town) -0x81bb - void set_fake_perk(char* name, int level, int image, char* desc) -0x81bc - void set_fake_trait(char* name, int active, int image, char* desc) -0x81bd - void set_selectable_perk(char* name, int active, int image, char* desc) -0x81be - void set_perkbox_title(char* title) +0x81bb - void set_fake_perk(string name, int level, int image, string desc) +0x81bc - void set_fake_trait(string name, int active, int image, string desc) +0x81bd - void set_selectable_perk(string name, int active, int image, string desc) +0x81be - void set_perkbox_title(string title) 0x81bf - void hide_real_perks() 0x81c0 - void show_real_perks() -0x81c1 - int has_fake_perk(char* name) -0x81c2 - int has_fake_trait(char* name) +0x81c1 - int has_fake_perk(string name) +0x81c2 - int has_fake_trait(string name) 0x81c3 - void perk_add_mode(int type) 0x81c4 - void clear_selectable_perks() 0x8225 - void remove_trait(int traitID) @@ -231,11 +231,11 @@ 0x8266 - int ceil(float) 0x8267 - int round(float) -0x81f2 - void set_palette(char* path) +0x81f2 - void set_palette(string path) -0x81f3 - void remove_script(objptr) -0x81f4 - void set_script(objptr, int scriptid) -0x81f5 - int get_script(objptr) +0x81f3 - void remove_script(object) +0x81f4 - void set_script(object, int scriptid) +0x81f5 - int get_script(object) 0x81f6 - int nb_create_char() @@ -258,10 +258,10 @@ 0x8202 - void fs_seek(int id, int pos) 0x8203 - void fs_resize(int id, int size) -0x8204 - int get_proto_data(objptr, int offset) -0x8205 - void set_proto_data(objptr, int offset, int value) +0x8204 - int get_proto_data(object, int offset) +0x8205 - void set_proto_data(object, int offset, int value) -0x8206 - void set_self(objptr) +0x8206 - void set_self(object) 0x8207 - void register_hook(int hook) 0x820d - int list_begin(int type) @@ -277,19 +277,19 @@ 0x8214 - void set_hero_race(int style) 0x8215 - void set_hero_style(int style) -0x8216 - void set_critter_burst_disable(objptr critter, int disable) +0x8216 - void set_critter_burst_disable(object critter, int disable) -0x8217 - int get_weapon_ammo_pid(objptr weapon) -0x8218 - void set_weapon_ammo_pid(objptr weapon, int pid) -0x8219 - int get_weapon_ammo_count(objptr weapon) -0x821a - void set_weapon_ammo_count(objptr weapon, int count) +0x8217 - int get_weapon_ammo_pid(object weapon) +0x8218 - void set_weapon_ammo_pid(object weapon, int pid) +0x8219 - int get_weapon_ammo_count(object weapon) +0x821a - void set_weapon_ammo_count(object weapon, int count) 0x8220 - int get_screen_width() 0x8221 - int get_screen_height() 0x8222 - void stop_game() 0x8223 - void resume_game() -0x8224 - void create_message_window(char* message) +0x8224 - void create_message_window(string message) 0x8226 - int get_light_level() @@ -297,16 +297,16 @@ 0x8228 - int get_attack_type -0x822b - int play_sfall_sound(char* file, int loop) +0x822b - int play_sfall_sound(string file, int loop) 0x822c - void stop_sfall_sound(int ptr) -0x8235 - array string_split(char* string, char* split) -0x8237 - int atoi(char* string) -0x8238 - float atof(char* string) -0x824e - char* substr(char* string, int start, int length) -0x824f - int strlen(char* string) -0x8250 - char* sprintf(char* format, any value) -0x8251 - int charcode(char* string) +0x8235 - array string_split(string string, string split) +0x8237 - int atoi(string string) +0x8238 - float atof(string string) +0x824e - string substr(string string, int start, int length) +0x824f - int strlen(string string) +0x8250 - string sprintf(string format, any value) +0x8251 - int charcode(string string) 0x8253 - int typeof(any value) 0x823a - int get_tile_fid(int tile) @@ -318,35 +318,45 @@ 0x8240 - void mark_movie_played(int id) -0x8248 - objptr get_last_target(objptr critter) -0x8249 - objptr get_last_attacker(objptr critter) +0x8248 - object get_last_target(object critter) +0x8249 - object get_last_attacker(object critter) 0x824b - int tile_under_cursor 0x824c - int gdialog_get_barter_mod 0x824d - void set_inven_ap_cost 0x825c - void reg_anim_combat_check(int enable) -0x825a - void reg_anim_destroy(objptr object) -0x825b - void reg_anim_animate_and_hide(objptr object, int animID, int delay) -0x825d - void reg_anim_light(objptr object, int radius, int delay) -0x825e - void reg_anim_change_fid(objptr object, int FID, int delay) -0x825f - void reg_anim_take_out(objptr object, int holdFrameID, int delay) -0x8260 - void reg_anim_turn_towards(objptr object, int tile/targetObj, int delay) +0x825a - void reg_anim_destroy(object object) +0x825b - void reg_anim_animate_and_hide(object object, int animID, int delay) +0x825d - void reg_anim_light(object object, int radius, int delay) +0x825e - void reg_anim_change_fid(object object, int FID, int delay) +0x825f - void reg_anim_take_out(object object, int holdFrameID, int delay) +0x8260 - void reg_anim_turn_towards(object object, int tile/targetObj, int delay) -0x8261 - int metarule2_explosions(objptr object) +0x8261 - int metarule2_explosions(object object) 0x8262 - void register_hook_proc(int hook, procedure proc) -0x8266 - char* message_str_game(int fileId, int messageId) -0x8267 - int sneak_success -0x8268 - int tile_light(int elevation, int tileNum) -0x8269 - ObjectPtr obj_blocking_line(ObjectPtr objFrom, int tileTo, int blockingType) -0x826a - ObjectPtr obj_blocking_tile(int tileNum, int elevation, int blockingType) -0x826b - array tile_get_objs(int tileNum, int elevation) -0x826c - array party_member_list(int includeHidden) -0x826d - array path_find_to(ObjectPtr objFrom, int tileTo, int blockingType) -0x826e - ObjectPtr create_spatial(int scriptID, int tile, int elevation, int radius) -0x826f - int art_exists(int artFID) -0x8270 - int obj_is_carrying_obj(ObjectPtr invenObj, ObjectPtr itemObj) +0x826b - string message_str_game(int fileId, int messageId) +0x826c - int sneak_success +0x826d - int tile_light(int elevation, int tileNum) +0x826e - object obj_blocking_line(object objFrom, int tileTo, int blockingType) +0x826f - object obj_blocking_tile(int tileNum, int elevation, int blockingType) +0x8270 - array tile_get_objs(int tileNum, int elevation) +0x8271 - array party_member_list(int includeHidden) +0x8272 - array path_find_to(object objFrom, int tileTo, int blockingType) +0x8273 - object create_spatial(int scriptID, int tile, int elevation, int radius) +0x8274 - int art_exists(int artFID) +0x8275 - int obj_is_carrying_obj(object invenObj, object itemObj) + +0x8276 - any sfall_func0(string funcName) +0x8277 - any sfall_func1(string funcName, arg1) +0x8278 - any sfall_func2(string funcName, arg1, arg2) +0x8279 - any sfall_func3(string funcName, arg1, arg2, arg3) +0x827a - any sfall_func4(string funcName, arg1, arg2, arg3, arg4) +0x827b - any sfall_func5(string funcName, arg1, arg2, arg3, arg4, arg5) +0x827c - any sfall_func6(string funcName, arg1, arg2, arg3, arg4, arg5, arg6) + * These functions require AllowUnsafeScripting to be enabled in ddraw.ini + diff --git a/sfall/Arrays.cpp b/sfall/Arrays.cpp index 61c95efe..2edae383 100644 --- a/sfall/Arrays.cpp +++ b/sfall/Arrays.cpp @@ -311,9 +311,26 @@ void DESetArray(int id, const DWORD* types, const void* data) { /* Array manipulation functions for script operators + TODO: move somewhere else */ + +const char* _stdcall GetSfallTypeName(DWORD dataType) { + switch (dataType) { + case DATATYPE_NONE: + return "(none)"; + case DATATYPE_STR: + return "string"; + case DATATYPE_FLOAT: + return "float"; + case DATATYPE_INT: + return "integer"; + default: + return "(unknown)"; + } +} + DWORD _stdcall getSfallTypeByScriptType(DWORD varType) { - varType&=0xffff; + varType &= 0xffff; switch (varType) { case VAR_TYPE_STR: case VAR_TYPE_STR2: diff --git a/sfall/Arrays.h b/sfall/Arrays.h index 09b1c57a..90c94fa0 100644 --- a/sfall/Arrays.h +++ b/sfall/Arrays.h @@ -4,7 +4,7 @@ #include #include -#define ARRAY_MAX_STRING (1024) // maximum length of string to be stored as array key or value +#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value #define ARRAY_MAX_SIZE (100000) // maximum number of array elements, // so total maximum memory/disk footprint of one array is: 16 + (ARRAY_MAX_STRING + 8) * ARRAY_MAX_SIZE @@ -16,6 +16,7 @@ extern char get_all_arrays_special_key[]; +// TODO: rewrite class sArrayElement { public: @@ -79,6 +80,8 @@ typedef std::tr1::unordered_map. + */ + +#pragma once + +#include + +// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/nullptr +const // It is a const object... +class nullptr_t { + public: + template + inline operator T*() const // convertible to any type of null non-member pointer... + { return 0; } + + template + inline operator T C::*() const // or any type of null member pointer... + { return 0; } + + private: + void operator&() const; // Can't take address of nullptr +} nullptr = {}; + +// http://stackoverflow.com/questions/33026118/c-stdbeginc-for-vs-2008 +template +T* std_begin(T (&a)[N]) { return a; } + +template +T* std_end(T (&a)[N]) { return a + N; } diff --git a/sfall/Credits.cpp b/sfall/Credits.cpp index b72be123..cd489861 100644 --- a/sfall/Credits.cpp +++ b/sfall/Credits.cpp @@ -141,7 +141,6 @@ static void __declspec(naked) CreditsNextLineHook_Bottom() { test eax, eax; // if any extra lines left, return 1 (from function), 0 otherwise popad; jnz morelines; -theend: mov eax, 0x0; retn; morelines: diff --git a/sfall/DebugEditor.cpp b/sfall/DebugEditor.cpp index f347790f..a2ea017a 100644 --- a/sfall/DebugEditor.cpp +++ b/sfall/DebugEditor.cpp @@ -16,9 +16,10 @@ * along with this program. If not, see . */ +#include + #include "main.h" -#include #include "Arrays.h" #include "DebugEditor.h" #include "FalloutEngine.h" diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index 766110a2..7ef25235 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -18,16 +18,189 @@ #include "main.h" -#include "Logging.h" +#include "Define.h" #include "FalloutEngine.h" +#include "Logging.h" // global variables -TGameObj** obj_dude_ptr = (TGameObj**)(0x6610B8); -TGameObj** inven_dude_ptr = (TGameObj**)(0x519058); -DWORD* activeUIHand_ptr = (DWORD*)(0x518F78); // 0 - left, 1 - right -DWORD* dude_traits = (DWORD*)(0x66BE40); // 2 of them -DWORD* itemCurrentItem = (DWORD*)(0x518F78); -DWORD* itemButtonItems = (DWORD*)(0x5970F8); +long* ptr_pc_traits = reinterpret_cast(_pc_trait); // 2 of them + +DWORD* ptr_aiInfoList = reinterpret_cast(_aiInfoList); +DWORD* ptr_ambient_light = reinterpret_cast(_ambient_light); +DWORD* ptr_art = reinterpret_cast(_art); +DWORD* ptr_art_name = reinterpret_cast(_art_name); +DWORD* ptr_art_vault_guy_num = reinterpret_cast(_art_vault_guy_num); +DWORD* ptr_art_vault_person_nums = reinterpret_cast(_art_vault_person_nums); +DWORD* ptr_bckgnd = reinterpret_cast(_bckgnd); +DWORD* ptr_black_palette = reinterpret_cast(_black_palette); +DWORD* ptr_bottom_line = reinterpret_cast(_bottom_line); +DWORD* ptr_btable = reinterpret_cast(_btable); +DWORD* ptr_btncnt = reinterpret_cast(_btncnt); +DWORD* ptr_CarCurrArea = reinterpret_cast(_CarCurrArea); +DWORD* ptr_cmap = reinterpret_cast(_cmap); +DWORD* ptr_colorTable = reinterpret_cast(_colorTable); +DWORD* ptr_combat_free_move = reinterpret_cast(_combat_free_move); +DWORD* ptr_combat_list = reinterpret_cast(_combat_list); +DWORD* ptr_combat_state = reinterpret_cast(_combat_state); +DWORD* ptr_combat_turn_running = reinterpret_cast(_combat_turn_running); +DWORD* ptr_combatNumTurns = reinterpret_cast(_combatNumTurns); +DWORD* ptr_crit_succ_eff = reinterpret_cast(_crit_succ_eff); +DWORD* ptr_critter_db_handle = reinterpret_cast(_critter_db_handle); +DWORD* ptr_critterClearObj = reinterpret_cast(_critterClearObj); +DWORD* ptr_crnt_func = reinterpret_cast(_crnt_func); +DWORD* ptr_curr_font_num = reinterpret_cast(_curr_font_num); +DWORD* ptr_curr_pc_stat = reinterpret_cast(_curr_pc_stat); +DWORD* ptr_curr_stack = reinterpret_cast(_curr_stack); +DWORD* ptr_cursor_line = reinterpret_cast(_cursor_line); +DWORD* ptr_dialog_target = reinterpret_cast(_dialog_target); +DWORD* ptr_dialog_target_is_party = reinterpret_cast(_dialog_target_is_party); +DWORD* ptr_drugInfoList = reinterpret_cast(_drugInfoList); +DWORD* ptr_edit_win = reinterpret_cast(_edit_win); +DWORD* ptr_Educated = reinterpret_cast(_Educated); +DWORD* ptr_Experience_ = reinterpret_cast(_Experience_); +DWORD* ptr_fallout_game_time = reinterpret_cast(_fallout_game_time); +DWORD* ptr_flptr = reinterpret_cast(_flptr); +DWORD* ptr_folder_card_desc = reinterpret_cast(_folder_card_desc); +DWORD* ptr_folder_card_fid = reinterpret_cast(_folder_card_fid); +DWORD* ptr_folder_card_title = reinterpret_cast(_folder_card_title); +DWORD* ptr_folder_card_title2 = reinterpret_cast(_folder_card_title2); +DWORD* ptr_frame_time = reinterpret_cast(_frame_time); +char* ptr_free_perk = reinterpret_cast(_free_perk); +DWORD* ptr_game_global_vars = reinterpret_cast(_game_global_vars); +DWORD* ptr_game_user_wants_to_quit = reinterpret_cast(_game_user_wants_to_quit); +DWORD* ptr_gcsd = reinterpret_cast(_gcsd); +DWORD* ptr_gdBarterMod = reinterpret_cast(_gdBarterMod); +DWORD* ptr_gdNumOptions = reinterpret_cast(_gdNumOptions); +DWORD* ptr_gIsSteal = reinterpret_cast(_gIsSteal); +DWORD* ptr_glblmode = reinterpret_cast(_glblmode); +DWORD* ptr_gmouse_current_cursor = reinterpret_cast(_gmouse_current_cursor); +DWORD* ptr_gmovie_played_list = reinterpret_cast(_gmovie_played_list); +DWORD* ptr_GreenColor = reinterpret_cast(_GreenColor); +DWORD* ptr_gsound_initialized = reinterpret_cast(_gsound_initialized); +DWORD* ptr_hit_location_penalty = reinterpret_cast(_hit_location_penalty); +DWORD* ptr_holo_flag = reinterpret_cast(_holo_flag); +DWORD* ptr_holopages = reinterpret_cast(_holopages); +DWORD* ptr_hot_line_count = reinterpret_cast(_hot_line_count); +DWORD* ptr_i_fid = reinterpret_cast(_i_fid); +DWORD* ptr_i_lhand = reinterpret_cast(_i_lhand); +DWORD* ptr_i_rhand = reinterpret_cast(_i_rhand); +DWORD* ptr_i_wid = reinterpret_cast(_i_wid); +DWORD* ptr_i_worn = reinterpret_cast(_i_worn); +DWORD* ptr_In_WorldMap = reinterpret_cast(_In_WorldMap); +DWORD* ptr_info_line = reinterpret_cast(_info_line); +DWORD* ptr_interfaceWindow = reinterpret_cast(_interfaceWindow); +DWORD* ptr_intfaceEnabled = reinterpret_cast(_intfaceEnabled); +DWORD* ptr_intotal = reinterpret_cast(_intotal); +TGameObj** ptr_inven_dude = reinterpret_cast(_inven_dude); +DWORD* ptr_inven_pid = reinterpret_cast(_inven_pid); +DWORD* ptr_inven_scroll_dn_bid = reinterpret_cast(_inven_scroll_dn_bid); +DWORD* ptr_inven_scroll_up_bid = reinterpret_cast(_inven_scroll_up_bid); +DWORD* ptr_inventry_message_file = reinterpret_cast(_inventry_message_file); +DWORD* ptr_itemButtonItems = reinterpret_cast(_itemButtonItems); +DWORD* ptr_itemCurrentItem = reinterpret_cast(_itemCurrentItem); // 0 - left, 1 - right +DWORD* ptr_kb_lock_flags = reinterpret_cast(_kb_lock_flags); +DWORD* ptr_last_buttons = reinterpret_cast(_last_buttons); +DWORD* ptr_last_button_winID = reinterpret_cast(_last_button_winID); +DWORD* ptr_last_level = reinterpret_cast(_last_level); +DWORD* ptr_Level_ = reinterpret_cast(_Level_); +DWORD* ptr_Lifegiver = reinterpret_cast(_Lifegiver); +DWORD* ptr_list_com = reinterpret_cast(_list_com); +DWORD* ptr_list_total = reinterpret_cast(_list_total); +DWORD* ptr_loadingGame = reinterpret_cast(_loadingGame); +DWORD* ptr_LSData = reinterpret_cast(_LSData); +DWORD* ptr_lsgwin = reinterpret_cast(_lsgwin); +DWORD* ptr_main_ctd = reinterpret_cast(_main_ctd); +DWORD* ptr_main_window = reinterpret_cast(_main_window); +DWORD* ptr_map_elevation = reinterpret_cast(_map_elevation); +DWORD* ptr_map_global_vars = reinterpret_cast(_map_global_vars); +DWORD* ptr_master_db_handle = reinterpret_cast(_master_db_handle); +DWORD* ptr_max = reinterpret_cast(_max); +DWORD* ptr_maxScriptNum = reinterpret_cast(_maxScriptNum); +DWORD* ptr_Meet_Frank_Horrigan = reinterpret_cast(_Meet_Frank_Horrigan); +DWORD* ptr_mouse_hotx = reinterpret_cast(_mouse_hotx); +DWORD* ptr_mouse_hoty = reinterpret_cast(_mouse_hoty); +DWORD* ptr_mouse_is_hidden = reinterpret_cast(_mouse_is_hidden); +DWORD* ptr_mouse_x_ = reinterpret_cast(_mouse_x_); +DWORD* ptr_mouse_y = reinterpret_cast(_mouse_y); +DWORD* ptr_mouse_y_ = reinterpret_cast(_mouse_y_); +DWORD* ptr_Mutate_ = reinterpret_cast(_Mutate_); +DWORD* ptr_name_color = reinterpret_cast(_name_color); +DWORD* ptr_name_font = reinterpret_cast(_name_font); +DWORD* ptr_name_sort_list = reinterpret_cast(_name_sort_list); +DWORD* ptr_num_game_global_vars = reinterpret_cast(_num_game_global_vars); +DWORD* ptr_num_map_global_vars = reinterpret_cast(_num_map_global_vars); +TGameObj** ptr_obj_dude = reinterpret_cast(_obj_dude); +DWORD* ptr_objectTable = reinterpret_cast(_objectTable); +DWORD* ptr_objItemOutlineState = reinterpret_cast(_objItemOutlineState); +DWORD* ptr_optionRect = reinterpret_cast(_optionRect); +DWORD* ptr_outlined_object = reinterpret_cast(_outlined_object); +DWORD* ptr_partyMemberAIOptions = reinterpret_cast(_partyMemberAIOptions); +DWORD* ptr_partyMemberCount = reinterpret_cast(_partyMemberCount); +DWORD* ptr_partyMemberLevelUpInfoList = reinterpret_cast(_partyMemberLevelUpInfoList); +DWORD* ptr_partyMemberList = reinterpret_cast(_partyMemberList); // each struct - 4 integers, first integer - objPtr +DWORD* ptr_partyMemberMaxCount = reinterpret_cast(_partyMemberMaxCount); +DWORD* ptr_partyMemberPidList = reinterpret_cast(_partyMemberPidList); +DWORD* ptr_patches = reinterpret_cast(_patches); +DWORD* ptr_paths = reinterpret_cast(_paths); +DWORD* ptr_pc_crit_succ_eff = reinterpret_cast(_pc_crit_succ_eff); +DWORD* ptr_pc_kill_counts = reinterpret_cast(_pc_kill_counts); +char* ptr_pc_name = reinterpret_cast(_pc_name); +DWORD* ptr_pc_proto = reinterpret_cast(_pc_proto); +DWORD* ptr_perk_data = reinterpret_cast(_perk_data); +int** ptr_perkLevelDataList = reinterpret_cast(_perkLevelDataList); +DWORD* ptr_pip_win = reinterpret_cast(_pip_win); +DWORD* ptr_pipboy_message_file = reinterpret_cast(_pipboy_message_file); +DWORD* ptr_pipmesg = reinterpret_cast(_pipmesg); +DWORD* ptr_preload_list_index = reinterpret_cast(_preload_list_index); +DWORD* ptr_procTableStrs = reinterpret_cast(_procTableStrs); // table of procId (from define.h) => procName map +DWORD* ptr_proto_main_msg_file = reinterpret_cast(_proto_main_msg_file); +DWORD* ptr_ptable = reinterpret_cast(_ptable); +DWORD* ptr_pud = reinterpret_cast(_pud); +DWORD* ptr_queue = reinterpret_cast(_queue); +DWORD* ptr_quick_done = reinterpret_cast(_quick_done); +DWORD* ptr_read_callback = reinterpret_cast(_read_callback); +DWORD* ptr_RedColor = reinterpret_cast(_RedColor); +DWORD* ptr_retvals = reinterpret_cast(_retvals); +DWORD* ptr_scr_size = reinterpret_cast(_scr_size); +DWORD* ptr_scriptListInfo = reinterpret_cast(_scriptListInfo); +DWORD* ptr_skill_data = reinterpret_cast(_skill_data); +DWORD* ptr_slot_cursor = reinterpret_cast(_slot_cursor); +DWORD* ptr_sneak_working = reinterpret_cast(_sneak_working); // DWORD var +DWORD* ptr_square = reinterpret_cast(_square); +DWORD* ptr_squares = reinterpret_cast(_squares); +DWORD* ptr_stack = reinterpret_cast(_stack); +DWORD* ptr_stack_offset = reinterpret_cast(_stack_offset); +DWORD* ptr_stat_data = reinterpret_cast(_stat_data); +DWORD* ptr_stat_flag = reinterpret_cast(_stat_flag); +DWORD* ptr_Tag_ = reinterpret_cast(_Tag_); +DWORD* ptr_tag_skill = reinterpret_cast(_tag_skill); +DWORD* ptr_target_curr_stack = reinterpret_cast(_target_curr_stack); +DWORD* ptr_target_pud = reinterpret_cast(_target_pud); +DWORD* ptr_target_stack = reinterpret_cast(_target_stack); +DWORD* ptr_target_stack_offset = reinterpret_cast(_target_stack_offset); +DWORD* ptr_target_str = reinterpret_cast(_target_str); +DWORD* ptr_target_xpos = reinterpret_cast(_target_xpos); +DWORD* ptr_target_ypos = reinterpret_cast(_target_ypos); +DWORD* ptr_text_char_width = reinterpret_cast(_text_char_width); +DWORD* ptr_text_height = reinterpret_cast(_text_height); +DWORD* ptr_text_max = reinterpret_cast(_text_max); +DWORD* ptr_text_mono_width = reinterpret_cast(_text_mono_width); +DWORD* ptr_text_spacing = reinterpret_cast(_text_spacing); +DWORD* ptr_text_to_buf = reinterpret_cast(_text_to_buf); +DWORD* ptr_text_width = reinterpret_cast(_text_width); +DWORD* ptr_title_color = reinterpret_cast(_title_color); +DWORD* ptr_title_font = reinterpret_cast(_title_font); +DWORD* ptr_trait_data = reinterpret_cast(_trait_data); +DWORD* ptr_view_page = reinterpret_cast(_view_page); +DWORD* ptr_wd_obj = reinterpret_cast(_wd_obj); +DWORD* ptr_wmAreaInfoList = reinterpret_cast(_wmAreaInfoList); +DWORD* ptr_wmLastRndTime = reinterpret_cast(_wmLastRndTime); +DWORD* ptr_wmWorldOffsetX = reinterpret_cast(_wmWorldOffsetX); +DWORD* ptr_wmWorldOffsetY = reinterpret_cast(_wmWorldOffsetY); +DWORD* ptr_world_xpos = reinterpret_cast(_world_xpos); +DWORD* ptr_world_ypos = reinterpret_cast(_world_ypos); +DWORD* ptr_WorldMapCurrArea = reinterpret_cast(_WorldMapCurrArea); +DWORD* ptr_YellowColor = reinterpret_cast(_YellowColor); /** ENGINE FUNCTIONS OFFSETS @@ -120,6 +293,7 @@ const DWORD db_get_file_list_ = 0x4C6628; const DWORD db_read_to_buf_ = 0x4C5DD4; const DWORD dbase_close_ = 0x4E5270; const DWORD dbase_open_ = 0x4E4F58; +const DWORD debug_printf_ = 0x4C6F48; const DWORD debug_register_env_ = 0x4C6D90; const DWORD determine_to_hit_func_ = 0x4243A8; const DWORD dialog_out_ = 0x41CF20; @@ -145,6 +319,7 @@ const DWORD elevator_start_ = 0x43F324; const DWORD endgame_slideshow_ = 0x43F788; const DWORD exec_script_proc_ = 0x4A4810; const DWORD executeProcedure_ = 0x46DD2C; +const DWORD findCurrentProc_ = 0x467160; const DWORD fadeSystemPalette_ = 0x4C7320; const DWORD findVar_ = 0x4410AC; const DWORD folder_print_line_ = 0x43E3D8; @@ -177,6 +352,7 @@ const DWORD interpretPopLong_ = 0x467500; const DWORD interpretPopShort_ = 0x4674F0; const DWORD interpretPushLong_ = 0x4674DC; const DWORD interpretPushShort_ = 0x46748C; +const DWORD interpretError_ = 0x4671F0; const DWORD intface_redraw_ = 0x45EB98; const DWORD intface_toggle_item_state_ = 0x45F4E0; const DWORD intface_toggle_items_ = 0x45F404; @@ -185,6 +361,9 @@ const DWORD intface_update_hit_points_ = 0x45EBD8; const DWORD intface_update_items_ = 0x45EFEC; const DWORD intface_update_move_points_ = 0x45EE0C; const DWORD intface_use_item_ = 0x45F5EC; +const DWORD intface_show_ = 0x45EA10; +const DWORD intface_hide_ = 0x45E9E0; +const DWORD intface_is_hidden_ = 0x45EA5C; const DWORD invenUnwieldFunc_ = 0x472A64; const DWORD invenWieldFunc_ = 0x472768; const DWORD inven_display_msg_ = 0x472D24; @@ -264,6 +443,7 @@ const DWORD mouse_in_ = 0x4CA8C8; const DWORD mouse_show_ = 0x4CA34C; const DWORD move_inventory_ = 0x474708; const DWORD NixHotLines_ = 0x4999C0; +const DWORD nrealloc_ = 0x4F1669; const DWORD obj_ai_blocking_at_ = 0x48BA20; const DWORD obj_blocking_at_ = 0x48B848; // (EAX *obj, EDX hexNum, EBX level) const DWORD obj_bound_ = 0x48B66C; @@ -435,29 +615,36 @@ const DWORD xvfprintf_ = 0x4DF1AC; int __stdcall ItemGetType(TGameObj* item) { __asm { - mov eax, item; - call item_get_type_; + mov eax, item + call item_get_type_ } } int _stdcall IsPartyMember(TGameObj* obj) { __asm { - mov eax, obj; - call isPartyMember_; + mov eax, obj + call isPartyMember_ } } -TGameObj* GetInvenWeaponLeft(TGameObj* obj) { +int _stdcall PartyMemberGetCurrentLevel(TGameObj* obj) { __asm { - mov eax, obj; - call inven_left_hand_; + mov eax, obj + call partyMemberGetCurLevel_ } } -TGameObj* GetInvenWeaponRight(TGameObj* obj) { +TGameObj* __stdcall GetInvenWeaponLeft(TGameObj* obj) { __asm { - mov eax, obj; - call inven_right_hand_; + mov eax, obj + call inven_left_hand_ + } +} + +TGameObj* __stdcall GetInvenWeaponRight(TGameObj* obj) { + __asm { + mov eax, obj + call inven_right_hand_ } } @@ -465,9 +652,9 @@ TGameObj* GetInvenWeaponRight(TGameObj* obj) { char* GetProtoPtr(DWORD pid) { char* proto; __asm { - mov eax, pid; - lea edx, proto; - call proto_ptr_; + mov eax, pid + lea edx, proto + call proto_ptr_ } return proto; } @@ -482,17 +669,15 @@ char AnimCodeByWeapon(TGameObj* weapon) { return 0; } - void DisplayConsoleMessage(const char* msg) { __asm { - mov eax, msg; - call display_print_; + mov eax, msg + call display_print_ } } static DWORD mesg_buf[4] = {0, 0, 0, 0}; -const char* _stdcall GetMessageStr(DWORD fileAddr, DWORD messageId) -{ +const char* _stdcall GetMessageStr(DWORD fileAddr, DWORD messageId) { DWORD buf = (DWORD)mesg_buf; const char* result; __asm { @@ -504,3 +689,139 @@ const char* _stdcall GetMessageStr(DWORD fileAddr, DWORD messageId) } return result; } + +// Change the name of playable character +void CritterPcSetName(const char* newName) { + __asm { + mov eax, newName + call critter_pc_set_name_ + } +} + +// Returns the name of the critter +const char* __stdcall CritterName(TGameObj* critter) { + __asm { + mov eax, critter + call critter_name_ + } +} + +void SkillGetTags(int* result, DWORD num) { + if (num > 4) { + num = 4; + } + __asm { + mov eax, result + mov edx, num + call skill_get_tags_ + } +} + +void SkillSetTags(int* tags, DWORD num) { + if (num > 4) { + num = 4; + } + __asm { + mov eax, tags + mov edx, num + call skill_set_tags_ + } +} + +int __stdcall ScrPtr(int scriptId, TScript** scriptPtr) { + __asm { + mov eax, scriptId; + mov edx, scriptPtr; + call scr_ptr_; + } +} + +// redraws the main game interface windows (useful after changing some data like active hand, etc.) +void InterfaceRedraw() { + __asm call intface_redraw_ +} + +// pops value type from Data stack (must be followed by InterpretPopLong) +DWORD __stdcall InterpretPopShort(TProgram* scriptPtr) { + __asm { + mov eax, scriptPtr + call interpretPopShort_ + } +} + +// pops value from Data stack (must be preceded by InterpretPopShort) +DWORD __stdcall InterpretPopLong(TProgram* scriptPtr) { + __asm { + mov eax, scriptPtr + call interpretPopLong_ + } +} + +// pushes value to Data stack (must be followed by InterpretPushShort) +void __stdcall InterpretPushLong(TProgram* scriptPtr, DWORD val) { + __asm { + mov edx, val + mov eax, scriptPtr + call interpretPushLong_ + } +} + +// pushes value type to Data stack (must be preceded by InterpretPushLong) +void __stdcall InterpretPushShort(TProgram* scriptPtr, DWORD valType) { + __asm { + mov edx, valType + mov eax, scriptPtr + call interpretPushShort_ + } +} + +DWORD __stdcall InterpretAddString(TProgram* scriptPtr, const char* strval) { + __asm { + mov edx, strval + mov eax, scriptPtr + call interpretAddString_ + } +} + +const char* __stdcall InterpretGetString(TProgram* scriptPtr, DWORD strId, DWORD dataType) { + __asm { + mov edx, dataType + mov ebx, strId + mov eax, scriptPtr + call interpretGetString_ + } +} + +void __declspec(naked) InterpretError(const char* fmt, ...) { + __asm { + jmp interpretError_ + } +} + +void __declspec(naked) DebugPrintf(const char* fmt, ...) { + __asm { + jmp debug_printf_ + } +} + +const char* __stdcall FindCurrentProc(TProgram* program) { + __asm { + mov eax, program + call findCurrentProc_ + } +} + +TGameObj* __stdcall InvenWorn(TGameObj* critter) { + __asm mov eax, critter + __asm call inven_worn_ +} + +TGameObj* __stdcall InvenLeftHand(TGameObj* critter) { + __asm mov eax, critter + __asm call inven_left_hand_ +} + +TGameObj* __stdcall InvenRightHand(TGameObj* critter) { + __asm mov eax, critter + __asm call inven_right_hand_ +} diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 3163e649..5b7385fc 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -26,6 +26,9 @@ #include "FalloutStructs.h" +// Global variable offsets +// TODO: probably need to hide these by moving inside implementation file + #define _aiInfoList 0x510948 #define _ambient_light 0x51923C #define _art 0x510738 @@ -87,6 +90,7 @@ #define _i_rhand 0x59E968 #define _i_wid 0x59E964 #define _i_worn 0x59E954 +#define _idle_func 0x51E234 #define _In_WorldMap 0x672E1C #define _info_line 0x5707D0 #define _interfaceWindow 0x519024 @@ -206,15 +210,189 @@ #define _YellowColor 0x6AB8BB // variables -extern TGameObj** obj_dude_ptr; -extern TGameObj** inven_dude_ptr; -extern DWORD* activeUIHand_ptr; // 0 - left, 1 - right -extern DWORD* dude_traits; // 2 of them -extern DWORD* itemCurrentItem; -extern DWORD* itemButtonItems; +// TODO: move to separate namespace +extern long* ptr_pc_traits; // 2 of them -// misc. offsets from engine +extern DWORD* ptr_aiInfoList; +extern DWORD* ptr_ambient_light; +extern DWORD* ptr_art; +extern DWORD* ptr_art_name; +extern DWORD* ptr_art_vault_guy_num; +extern DWORD* ptr_art_vault_person_nums; +extern DWORD* ptr_bckgnd; +extern DWORD* ptr_black_palette; +extern DWORD* ptr_bottom_line; +extern DWORD* ptr_btable; +extern DWORD* ptr_btncnt; +extern DWORD* ptr_CarCurrArea; +extern DWORD* ptr_cmap; +extern DWORD* ptr_colorTable; +extern DWORD* ptr_combat_free_move; +extern DWORD* ptr_combat_list; +extern DWORD* ptr_combat_state; +extern DWORD* ptr_combat_turn_running; +extern DWORD* ptr_combatNumTurns; +extern DWORD* ptr_crit_succ_eff; +extern DWORD* ptr_critter_db_handle; +extern DWORD* ptr_critterClearObj; +extern DWORD* ptr_crnt_func; +extern DWORD* ptr_curr_font_num; +extern DWORD* ptr_curr_pc_stat; +extern DWORD* ptr_curr_stack; +extern DWORD* ptr_cursor_line; +extern DWORD* ptr_dialog_target; +extern DWORD* ptr_dialog_target_is_party; +extern DWORD* ptr_drugInfoList; +extern DWORD* ptr_edit_win; +extern DWORD* ptr_Educated; +extern DWORD* ptr_Experience_; +extern DWORD* ptr_fallout_game_time; +extern DWORD* ptr_flptr; +extern DWORD* ptr_folder_card_desc; +extern DWORD* ptr_folder_card_fid; +extern DWORD* ptr_folder_card_title; +extern DWORD* ptr_folder_card_title2; +extern DWORD* ptr_frame_time; +extern char* ptr_free_perk; +extern DWORD* ptr_game_global_vars; +extern DWORD* ptr_game_user_wants_to_quit; +extern DWORD* ptr_gcsd; +extern DWORD* ptr_gdBarterMod; +extern DWORD* ptr_gdNumOptions; +extern DWORD* ptr_gIsSteal; +extern DWORD* ptr_glblmode; +extern DWORD* ptr_gmouse_current_cursor; +extern DWORD* ptr_gmovie_played_list; +extern DWORD* ptr_GreenColor; +extern DWORD* ptr_gsound_initialized; +extern DWORD* ptr_hit_location_penalty; +extern DWORD* ptr_holo_flag; +extern DWORD* ptr_holopages; +extern DWORD* ptr_hot_line_count; +extern DWORD* ptr_i_fid; +extern DWORD* ptr_i_lhand; +extern DWORD* ptr_i_rhand; +extern DWORD* ptr_i_wid; +extern DWORD* ptr_i_worn; +extern DWORD* ptr_In_WorldMap; +extern DWORD* ptr_info_line; +extern DWORD* ptr_interfaceWindow; +extern DWORD* ptr_intfaceEnabled; +extern DWORD* ptr_intotal; +extern TGameObj** ptr_inven_dude; +extern DWORD* ptr_inven_pid; +extern DWORD* ptr_inven_scroll_dn_bid; +extern DWORD* ptr_inven_scroll_up_bid; +extern DWORD* ptr_inventry_message_file; +extern DWORD* ptr_itemButtonItems; +extern DWORD* ptr_itemCurrentItem; // 0 - left, 1 - right +extern DWORD* ptr_kb_lock_flags; +extern DWORD* ptr_last_buttons; +extern DWORD* ptr_last_button_winID; +extern DWORD* ptr_last_level; +extern DWORD* ptr_Level_; +extern DWORD* ptr_Lifegiver; +extern DWORD* ptr_list_com; +extern DWORD* ptr_list_total; +extern DWORD* ptr_loadingGame; +extern DWORD* ptr_LSData; +extern DWORD* ptr_lsgwin; +extern DWORD* ptr_main_ctd; +extern DWORD* ptr_main_window; +extern DWORD* ptr_map_elevation; +extern DWORD* ptr_map_global_vars; +extern DWORD* ptr_master_db_handle; +extern DWORD* ptr_max; +extern DWORD* ptr_maxScriptNum; +extern DWORD* ptr_Meet_Frank_Horrigan; +extern DWORD* ptr_mouse_hotx; +extern DWORD* ptr_mouse_hoty; +extern DWORD* ptr_mouse_is_hidden; +extern DWORD* ptr_mouse_x_; +extern DWORD* ptr_mouse_y; +extern DWORD* ptr_mouse_y_; +extern DWORD* ptr_Mutate_; +extern DWORD* ptr_name_color; +extern DWORD* ptr_name_font; +extern DWORD* ptr_name_sort_list; +extern DWORD* ptr_num_game_global_vars; +extern DWORD* ptr_num_map_global_vars; +extern TGameObj** ptr_obj_dude; +extern DWORD* ptr_objectTable; +extern DWORD* ptr_objItemOutlineState; +extern DWORD* ptr_optionRect; +extern DWORD* ptr_outlined_object; +extern DWORD* ptr_partyMemberAIOptions; +extern DWORD* ptr_partyMemberCount; +extern DWORD* ptr_partyMemberLevelUpInfoList; +extern DWORD* ptr_partyMemberList; // each struct - 4 integers, first integer - objPtr +extern DWORD* ptr_partyMemberMaxCount; +extern DWORD* ptr_partyMemberPidList; +extern DWORD* ptr_patches; +extern DWORD* ptr_paths; +extern DWORD* ptr_pc_crit_succ_eff; +extern DWORD* ptr_pc_kill_counts; +extern char* ptr_pc_name; +extern DWORD* ptr_pc_proto; +extern DWORD* ptr_perk_data; +extern int** ptr_perkLevelDataList; // limited to PERK_Count +extern DWORD* ptr_pip_win; +extern DWORD* ptr_pipboy_message_file; +extern DWORD* ptr_pipmesg; +extern DWORD* ptr_preload_list_index; +extern DWORD* ptr_procTableStrs; // table of procId (from define.h) => procName map +extern DWORD* ptr_proto_main_msg_file; +extern DWORD* ptr_ptable; +extern DWORD* ptr_pud; +extern DWORD* ptr_queue; +extern DWORD* ptr_quick_done; +extern DWORD* ptr_read_callback; +extern DWORD* ptr_RedColor; +extern DWORD* ptr_retvals; +extern DWORD* ptr_scr_size; +extern DWORD* ptr_scriptListInfo; +extern DWORD* ptr_skill_data; +extern DWORD* ptr_slot_cursor; +extern DWORD* ptr_sneak_working; // DWORD var +extern DWORD* ptr_square; +extern DWORD* ptr_squares; +extern DWORD* ptr_stack; +extern DWORD* ptr_stack_offset; +extern DWORD* ptr_stat_data; +extern DWORD* ptr_stat_flag; +extern DWORD* ptr_Tag_; +extern DWORD* ptr_tag_skill; +extern DWORD* ptr_target_curr_stack; +extern DWORD* ptr_target_pud; +extern DWORD* ptr_target_stack; +extern DWORD* ptr_target_stack_offset; +extern DWORD* ptr_target_str; +extern DWORD* ptr_target_xpos; +extern DWORD* ptr_target_ypos; +extern DWORD* ptr_text_char_width; +extern DWORD* ptr_text_height; +extern DWORD* ptr_text_max; +extern DWORD* ptr_text_mono_width; +extern DWORD* ptr_text_spacing; +extern DWORD* ptr_text_to_buf; +extern DWORD* ptr_text_width; +extern DWORD* ptr_title_color; +extern DWORD* ptr_title_font; +extern DWORD* ptr_trait_data; +extern DWORD* ptr_view_page; +extern DWORD* ptr_wd_obj; +extern DWORD* ptr_wmAreaInfoList; +extern DWORD* ptr_wmLastRndTime; +extern DWORD* ptr_wmWorldOffsetX; +extern DWORD* ptr_wmWorldOffsetY; +extern DWORD* ptr_world_xpos; +extern DWORD* ptr_world_ypos; +extern DWORD* ptr_WorldMapCurrArea; +extern DWORD* ptr_YellowColor; + +// engine function offsets +// TODO: move to separate namespace extern const DWORD action_get_an_object_; extern const DWORD action_loot_container_; extern const DWORD action_use_an_item_on_object_; @@ -285,6 +463,7 @@ extern const DWORD db_get_file_list_; extern const DWORD db_read_to_buf_; extern const DWORD dbase_close_; extern const DWORD dbase_open_; +extern const DWORD debug_printf_; extern const DWORD debug_register_env_; extern const DWORD determine_to_hit_func_; extern const DWORD dialog_out_; @@ -342,6 +521,7 @@ extern const DWORD interpretPopLong_; extern const DWORD interpretPopShort_; extern const DWORD interpretPushLong_; extern const DWORD interpretPushShort_; +extern const DWORD interpretError_; extern const DWORD intface_redraw_; // no args extern const DWORD intface_toggle_item_state_; extern const DWORD intface_toggle_items_; @@ -350,6 +530,9 @@ extern const DWORD intface_update_hit_points_; extern const DWORD intface_update_items_; extern const DWORD intface_update_move_points_; extern const DWORD intface_use_item_; +extern const DWORD intface_show_; +extern const DWORD intface_hide_; +extern const DWORD intface_is_hidden_; extern const DWORD invenUnwieldFunc_; // (int critter@, int slot@, int a3@) - int result (-1 on error, 0 on success) extern const DWORD invenWieldFunc_; // (int who@, int item@, int a3@, int slot@) - int result (-1 on error, 0 on success) extern const DWORD inven_display_msg_; @@ -432,6 +615,7 @@ extern const DWORD mouse_in_; extern const DWORD mouse_show_; extern const DWORD move_inventory_; extern const DWORD NixHotLines_; +extern const DWORD nrealloc_; extern const DWORD obj_ai_blocking_at_; extern const DWORD obj_blocking_at_; // (int aExcludeObject /* can be 0 */, signed int aTile, int aElevation) extern const DWORD obj_bound_; @@ -530,11 +714,11 @@ extern const DWORD set_game_time_; extern const DWORD SexWindow_; extern const DWORD skill_check_stealing_; extern const DWORD skill_dec_point_; -extern const DWORD skill_get_tags_; +extern const DWORD skill_get_tags_; // eax - pointer to array DWORD, edx - number of elements to read extern const DWORD skill_inc_point_; extern const DWORD skill_level_; extern const DWORD skill_points_; -extern const DWORD skill_set_tags_; +extern const DWORD skill_set_tags_; // eax - pointer to array DWORD, edx - number of elements to write extern const DWORD skill_use_; extern const DWORD skilldex_select_; extern const DWORD sprintf_; @@ -542,7 +726,7 @@ extern const DWORD square_num_; extern const DWORD stat_get_base_direct_; extern const DWORD stat_get_bonus_; extern const DWORD stat_level_; -extern const DWORD stat_pc_add_experience_; +extern const DWORD stat_pc_add_experience_; // eax - amount to add extern const DWORD stat_pc_get_; extern const DWORD stat_pc_set_; extern const DWORD stat_set_bonus_; @@ -656,11 +840,65 @@ extern const DWORD getmsg_; // eax - msg file addr, ebx - message ID, edx - int[ #define MSG_FILE_WORLDMAP (0x672FB0) // WRAPPERS: +// TODO: move these to different namespace int _stdcall IsPartyMember(TGameObj* obj); -TGameObj* GetInvenWeaponLeft(TGameObj* obj); -TGameObj* GetInvenWeaponRight(TGameObj* obj); +int _stdcall PartyMemberGetCurrentLevel(TGameObj* obj); +TGameObj* __stdcall GetInvenWeaponLeft(TGameObj* obj); +TGameObj* __stdcall GetInvenWeaponRight(TGameObj* obj); char* GetProtoPtr(DWORD pid); char AnimCodeByWeapon(TGameObj* weapon); +// Displays message in main UI console window void DisplayConsoleMessage(const char* msg); const char* _stdcall GetMessageStr(DWORD fileAddr, DWORD messageId); -int __stdcall ItemGetType(TGameObj* item); \ No newline at end of file +int __stdcall ItemGetType(TGameObj* item); + +// Change the name of playable character +void CritterPcSetName(const char* newName); + +// Returns the name of the critter +const char* __stdcall CritterName(TGameObj* critter); + +// Saves pointer to script object into scriptPtr using scriptID. +// Returns 0 on success, -1 on failure. +int __stdcall ScrPtr(int scriptId, TScript** scriptPtr); + +void SkillGetTags(int* result, DWORD num); +void SkillSetTags(int* tags, DWORD num); + +// redraws the main game interface windows (useful after changing some data like active hand, etc.) +void InterfaceRedraw(); + +// critter worn item (armor) +TGameObj* __stdcall InvenWorn(TGameObj* critter); + +// item in critter's left hand slot +TGameObj* __stdcall InvenLeftHand(TGameObj* critter); + +// item in critter's right hand slot +TGameObj* __stdcall InvenRightHand(TGameObj* critter); + +// pops value type from Data stack (must be followed by InterpretPopLong) +DWORD __stdcall InterpretPopShort(TProgram* scriptPtr); + +// pops value from Data stack (must be preceded by InterpretPopShort) +DWORD __stdcall InterpretPopLong(TProgram* scriptPtr); + +// pushes value to Data stack (must be followed by InterpretPushShort) +void __stdcall InterpretPushLong(TProgram* scriptPtr, DWORD val); + +// pushes value type to Data stack (must be preceded by InterpretPushLong) +void __stdcall InterpretPushShort(TProgram* scriptPtr, DWORD valType); + +const char* __stdcall InterpretGetString(TProgram* scriptPtr, DWORD strId, DWORD dataType); + +DWORD __stdcall InterpretAddString(TProgram* scriptPtr, const char* str); + +// prints scripting error in debug.log and stops current script execution by performing longjmp +// USE WITH CAUTION +void __declspec() InterpretError(const char* fmt, ...); + +// prints message to debug.log file +void __declspec() DebugPrintf(const char* fmt, ...); + +// returns the name of current procedure by program pointer +const char* __stdcall FindCurrentProc(TProgram* program); diff --git a/sfall/FalloutStructs.h b/sfall/FalloutStructs.h index 2bfd4acb..b4cb1b93 100644 --- a/sfall/FalloutStructs.h +++ b/sfall/FalloutStructs.h @@ -34,7 +34,7 @@ struct TInvenRec }; #pragma pack(pop) -/* 8 */ +/* 15 */ #pragma pack(push, 1) struct TGameObj { @@ -52,7 +52,7 @@ struct TGameObj TInvenRec *invenTablePtr; char gap_38[4]; int itemCharges; - int movePoints; + int critterAP_weaponAmmoPid; char gap_44[16]; int lastTarget; char gap_58[12]; @@ -139,7 +139,7 @@ enum ObjectTypes #pragma pack(push, 1) struct TProgram { - char gap_0[4]; + const char* fileName; int *codeStackPtr; char gap_8[8]; int *codePtr; diff --git a/sfall/FileSystem.cpp b/sfall/FileSystem.cpp index dadce911..543034be 100644 --- a/sfall/FileSystem.cpp +++ b/sfall/FileSystem.cpp @@ -16,11 +16,12 @@ * along with this program. If not, see . */ +#include + #include "main.h" #include "FalloutEngine.h" #include "FileSystem.h" -#include "vector9x.cpp" extern void GetSavePath(char* buf, char* ftype); @@ -31,7 +32,7 @@ struct fsFile { DWORD wpos; }; -vector files; +std::vector files; static DWORD loadedtiles=0; static DWORD retval; @@ -417,7 +418,7 @@ void FileSystemReset() { } if(!loadedtiles) files.clear(); else { - for(DWORD i=files.size()-1;i>=loadedtiles;i--) files.remove_at(i); + for(DWORD i=files.size()-1;i>=loadedtiles;i--) files.erase(files.begin() + i); } } void FileSystemSave(HANDLE h) { diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp index 8c482b88..f8d5a58f 100644 --- a/sfall/HookScripts.cpp +++ b/sfall/HookScripts.cpp @@ -1088,7 +1088,7 @@ void _stdcall RegisterHook( DWORD script, DWORD id, DWORD procNum ) } sScriptProgram *prog = GetGlobalScriptProgram(script); if (prog) { - dlog_f( "Global script %8x registered as hook id %d ", DL_HOOK, script, id); + dlog_f("Global script %08x registered as hook id %d\r\n", DL_HOOK, script, id); sHookScript hook; hook.prog = *prog; hook.callback = procNum; @@ -1102,39 +1102,40 @@ static void LoadHookScript(const char* name, int id) { char filename[MAX_PATH]; sprintf(filename, "scripts\\%s.int", name); - bool result; + bool fileExist; __asm { lea eax, filename call db_access_ - mov result, al + mov fileExist, al } - if (result && !isGameScript(name)) { + if (fileExist && !isGameScript(name)) { sScriptProgram prog; - dlog("Loading hook script: ", DL_HOOK); - dlog(filename, DL_HOOK); + dlog(">", DL_HOOK); + dlog(name, DL_HOOK); LoadScriptProgram(prog, name); if (prog.ptr) { + dlogr(" Done", DL_HOOK); sHookScript hook; hook.prog = prog; hook.callback = -1; hook.isGlobalScript = false; hooks[id].push_back(hook); AddProgramToMap(prog); - dlogr(" Done", DL_HOOK); } else dlogr(" Error!", DL_HOOK); } } static void HookScriptInit2() { - dlogr("Initing hook scripts", DL_HOOK|DL_INIT); + dlogr("Loading hook scripts", DL_HOOK|DL_INIT); char* mask = "scripts\\hs_*.int"; DWORD *filenames; __asm { + xor ecx, ecx xor ebx, ebx - mov eax, mask lea edx, filenames + mov eax, mask call db_get_file_list_ } @@ -1289,7 +1290,7 @@ static void HookScriptInit2() { call db_free_file_list_ } - dlogr("Completed hook script init", DL_HOOK|DL_INIT); + dlogr("Finished loading hook scripts", DL_HOOK|DL_INIT); } void HookScriptClear() { diff --git a/sfall/Inventory.cpp b/sfall/Inventory.cpp index 62c848d6..9466bf9c 100644 --- a/sfall/Inventory.cpp +++ b/sfall/Inventory.cpp @@ -49,11 +49,11 @@ static const char* MsgSearch(int msgno, DWORD file) { } DWORD& GetActiveItemMode() { - return itemButtonItems[(*itemCurrentItem * 6) + 4]; + return ptr_itemButtonItems[(*ptr_itemCurrentItem * 6) + 4]; } TGameObj* GetActiveItem() { - return (TGameObj*)itemButtonItems[*itemCurrentItem * 6]; + return (TGameObj*)ptr_itemButtonItems[*ptr_itemCurrentItem * 6]; } void InventoryKeyPressedHook(DWORD dxKey, bool pressed, DWORD vKey) { diff --git a/sfall/Knockback.cpp b/sfall/Knockback.cpp index 9ad92eff..a5e50d94 100644 --- a/sfall/Knockback.cpp +++ b/sfall/Knockback.cpp @@ -16,14 +16,15 @@ * along with this program. If not, see . */ +#include +#include + #include "main.h" -#include #include "FalloutEngine.h" #include "Knockback.h" -#include "vector9x.cpp" -static vector NoBursts; +static std::vector NoBursts; struct KnockbackModifier { DWORD id; @@ -31,9 +32,9 @@ struct KnockbackModifier { double value; }; -static vector mTargets; -static vector mAttackers; -static vector mWeapons; +static std::vector mTargets; +static std::vector mAttackers; +static std::vector mWeapons; struct ChanceModifier { DWORD id; @@ -41,8 +42,8 @@ struct ChanceModifier { int mod; }; -static vector HitChanceMods; -static vector PickpocketMods; +static std::vector HitChanceMods; +static std::vector PickpocketMods; static ChanceModifier BaseHitChance; static ChanceModifier BasePickpocket; @@ -50,10 +51,10 @@ static ChanceModifier BasePickpocket; static bool hookedAimedShot; static const DWORD aimedShotRet1=0x478EE4; static const DWORD aimedShotRet2=0x478EEA; -static vector disabledAS; -static vector forcedAS; +static std::vector disabledAS; +static std::vector forcedAS; -static double ApplyModifiers(vector* mods, DWORD id, double val) { +static double ApplyModifiers(std::vector* mods, DWORD id, double val) { for(DWORD i=0;isize();i++) { if((*mods)[i].id==id) { KnockbackModifier* mod=&(*mods)[i]; @@ -207,7 +208,7 @@ void Knockback_OnGameLoad() { } void _stdcall KnockbackSetMod(DWORD id, DWORD type, float val, DWORD on) { - vector* mods; + std::vector* mods; switch(on) { case 0: mods=&mWeapons; break; case 1: mods=&mTargets; break; @@ -225,7 +226,7 @@ void _stdcall KnockbackSetMod(DWORD id, DWORD type, float val, DWORD on) { } void _stdcall KnockbackRemoveMod(DWORD id, DWORD on) { - vector* mods; + std::vector* mods; switch(on) { case 0: mods=&mWeapons; break; case 1: mods=&mTargets; break; @@ -234,7 +235,7 @@ void _stdcall KnockbackRemoveMod(DWORD id, DWORD on) { } for(DWORD i=0;isize();i++) { if((*mods)[i].id==id) { - mods->remove_at(i); + mods->erase(mods->begin() + i); return; } } @@ -287,7 +288,7 @@ void _stdcall SetNoBurstMode(DWORD critter, DWORD on) { } else { for(DWORD i=0;i 2) SaveInCombatFix = 0; - if (SaveInCombatFix) { - GetPrivateProfileString("sfall", "SaveInCombat", "Cannot save at this time", SaveFailMsg, 128, translationIni); - } + GetPrivateProfileString("sfall", "SaveInCombat", "Cannot save at this time", SaveFailMsg, 128, translationIni); GetPrivateProfileString("sfall", "SaveSfallDataFail", "ERROR saving extended savegame information! Check if other programs interfere with savegame files/folders and try again!", SaveSfallDataFailMsg, 128, translationIni); switch (GetPrivateProfileInt("Misc", "PipBoyAvailableAtGameStart", 0, ini)) { diff --git a/sfall/Logging.cpp b/sfall/Logging.cpp index 6fb9b32b..0f146b25 100644 --- a/sfall/Logging.cpp +++ b/sfall/Logging.cpp @@ -41,16 +41,15 @@ void dlogr(const char* a, int type) { } } -void dlog_f(const char *format, int type, ...) { +void dlog_f(const char *fmt, int type, ...) { if (IsDebug) { - int i; - va_list arg; - char buf[1024]; - va_start(arg, format); - i = vsprintf(buf, format, arg); - va_end(arg); + va_list args; + va_start(args, type); + char buf[4096]; + vsnprintf_s(buf, sizeof buf, _TRUNCATE, fmt, args); Log << buf; Log.flush(); + va_end(args); } } diff --git a/sfall/Numbers.cpp b/sfall/Numbers.cpp deleted file mode 100644 index 7a0f05ec..00000000 --- a/sfall/Numbers.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "Defines.h" -#include "Imports.h" -#include "Input.h" -#include "Numbers.h" -#include "SafeWrite.h" - -static void RunNumbers(DWORD load) { - if(!load) { - - } - exit(0); -} - -static void _declspec(naked) NewGameHook() { - _asm { - pushad; - push DIK_LSHIFT; - call KeyDown; - test eax, eax; - jz fail; - popad; - xor eax, eax; - push eax; - call RunNumbers(); -fail: - popad; - jmp main_menu_hide_; - } -} - -static void LoadGameRet=0x480AFE; -static void _declspec(naked) LoadGameHook() { - _asm { - pushad; - push DIK_LSHIFT; - call KeyDown; - test eax, eax; - jz fail; - popad; - push 1; - call RunNumbers(); -fail: - popad; - mov ecx, 0x1e0; - jmp LoadGameRet; - } -} - -NumbersInit() { - HookCall(0x480A81, &NewGameHook); - MakeCall(0x480AF9, &LoadGameHook, true); -} diff --git a/sfall/Numbers.h b/sfall/Numbers.h deleted file mode 100644 index 57a67d65..00000000 --- a/sfall/Numbers.h +++ /dev/null @@ -1,4 +0,0 @@ -#define WIN32_LEAN_AND_MEAN -#include - -void NumbersInit(); \ No newline at end of file diff --git a/sfall/PartyControl.cpp b/sfall/PartyControl.cpp index 87e3e5a7..dd17acea 100644 --- a/sfall/PartyControl.cpp +++ b/sfall/PartyControl.cpp @@ -20,20 +20,42 @@ /* KNOWN ISSUES with party control - doesn't work with NPC's wearing armor mod, armor won't change when you change it from critter's inventory - - shows perks and some other info for the main dude in character screen */ +#include +#include + #include "main.h" +#include "Define.h" #include "FalloutEngine.h" #include "PartyControl.h" -#include +#if (_MSC_VER < 1600) +#include "Cpp11_emu.h" +#endif static DWORD Mode; static int IsControllingNPC = 0; static std::vector Chars; +static int DelayedExperience; -TGameObj* real_dude = NULL; -DWORD real_traits[2]; +static TGameObj* real_dude = nullptr; +static DWORD real_traits[2]; +static char real_pc_name[32]; +static DWORD real_last_level; +static DWORD real_Level; +static DWORD real_Experience; +static char real_free_perk; +static DWORD real_unspent_skill_points; +//static DWORD real_map_elevation; +static DWORD real_sneak_working; +//static DWORD real_sneak_queue_time; +static DWORD real_hand; +static DWORD real_itemButtonItems[6*2]; +static DWORD real_perkLevelDataList[PERK_count]; +//static DWORD real_drug_gvar[6]; +//static DWORD real_jet_gvar; +static int real_tag_skill[4]; +//static DWORD real_bbox_sneak; static const DWORD* list_com = (DWORD*)_list_com; @@ -57,43 +79,140 @@ static void _stdcall SetInventoryCheck(bool skip) { } } +static void __stdcall StatPcAddExperience(int amount) { + __asm { + mov eax, amount + call stat_pc_add_experience_ + } +} + +// saves the state of PC before moving control to NPC +static void SaveRealDudeState() { + real_dude = *ptr_obj_dude; + real_hand = *ptr_itemCurrentItem; + memcpy(real_itemButtonItems, ptr_itemButtonItems, sizeof(DWORD) * 6 * 2); + memcpy(real_traits, ptr_pc_traits, sizeof(DWORD) * 2); + memcpy(real_perkLevelDataList, *ptr_perkLevelDataList, sizeof(DWORD) * PERK_count); + strcpy_s(real_pc_name, 32, ptr_pc_name); + real_Level = *ptr_Level_; + real_last_level = *ptr_last_level; + real_Experience = *ptr_Experience_; + real_free_perk = *ptr_free_perk; + real_unspent_skill_points = ptr_curr_pc_stat[0]; + //real_map_elevation = *ptr_map_elevation; + real_sneak_working = *ptr_sneak_working; + SkillGetTags(real_tag_skill, 4); +} + +// take control of the NPC +static void TakeControlOfNPC(TGameObj* npc) { + // remove skill tags + int tagSkill[4]; +#if (_MSC_VER < 1600) + std::fill(std_begin(tagSkill), std_end(tagSkill), -1); +#else + std::fill(std::begin(tagSkill), std::end(tagSkill), -1); +#endif + SkillSetTags(tagSkill, 4); + + // reset traits + ptr_pc_traits[0] = ptr_pc_traits[1] = -1; + + // reset perks + for (int i = 0; i < PERK_count; i++) { + (*ptr_perkLevelDataList)[i] = 0; + } + + // change character name + CritterPcSetName(CritterName(npc)); + + // change level + int level = IsPartyMember(npc) + ? PartyMemberGetCurrentLevel(npc) + : 0; + + *ptr_Level_ = level; + *ptr_last_level = level; + + // reset other stats + *ptr_Experience_ = 0; + *ptr_free_perk = 0; + ptr_curr_pc_stat[0] = 0; + *ptr_sneak_working = 0; + + // deduce active hand by weapon anim code + char critterAnim = (npc->artFID & 0xF000) >> 12; // current weapon as seen in hands + if (AnimCodeByWeapon(GetInvenWeaponLeft(npc)) == critterAnim) { // definitely left hand.. + *ptr_itemCurrentItem = 0; + } else { + *ptr_itemCurrentItem = 1; + } + + *ptr_inven_pid = npc->pid; + + // switch main dude_obj pointers - this should be done last! + *ptr_obj_dude = npc; + *ptr_inven_dude = npc; + + IsControllingNPC = 1; + DelayedExperience = 0; + SetInventoryCheck(true); + + InterfaceRedraw(); +} + +// restores the real dude state +static void RestoreRealDudeState() { + *ptr_obj_dude = real_dude; + *ptr_inven_dude = real_dude; + + *ptr_itemCurrentItem = real_hand; + memcpy(ptr_itemButtonItems, real_itemButtonItems, sizeof(DWORD) * 6 * 2); + memcpy(ptr_pc_traits, real_traits, sizeof(DWORD) * 2); + memcpy(*ptr_perkLevelDataList, real_perkLevelDataList, sizeof(DWORD) * PERK_count); + strcpy_s(ptr_pc_name, 32, real_pc_name); + *ptr_Level_ = real_Level; + *ptr_last_level = real_last_level; + *ptr_Experience_ = real_Experience; + *ptr_free_perk = real_free_perk; + ptr_curr_pc_stat[0] = real_unspent_skill_points; + //real_map_elevation = *ptr_map_elevation; -- why save elevation? + *ptr_sneak_working = real_sneak_working; + SkillSetTags(real_tag_skill, 4); + + *ptr_inven_pid = real_dude->pid; + + if (DelayedExperience > 0) { + StatPcAddExperience(DelayedExperience); + } + + InterfaceRedraw(); + + SetInventoryCheck(false); + IsControllingNPC = 0; + real_dude = nullptr; +} + +static int __stdcall CombatTurn(TGameObj* obj) { + __asm { + mov eax, obj; + call combat_turn_; + } +} + // return values: 0 - use vanilla handler, 1 - skip vanilla handler, return 0 (normal status), -1 - skip vanilla, return -1 (game ended) static int _stdcall CombatWrapperInner(TGameObj* obj) { - if ((obj != *obj_dude_ptr) && (Chars.size() == 0 || IsInPidList(obj)) && (Mode == 1 || IsPartyMember(obj))) { - IsControllingNPC = 1; - SetInventoryCheck(true); - char dudeWeaponSlot = (char)*activeUIHand_ptr; + if ((obj != *ptr_obj_dude) && (Chars.size() == 0 || IsInPidList(obj)) && (Mode == 1 || IsPartyMember(obj))) { // save "real" dude state - real_dude = *obj_dude_ptr; - *obj_dude_ptr = obj; - *inven_dude_ptr = obj; - memcpy(real_traits, dude_traits, sizeof(DWORD)*2); + SaveRealDudeState(); + TakeControlOfNPC(obj); + + // Do combat turn + int turnResult = CombatTurn(obj); - // deduce active hand by weapon anim code - char critterAnim = (obj->artFID & 0xF000) >> 12; // current weapon as seen in hands - if (AnimCodeByWeapon(GetInvenWeaponLeft(obj)) == critterAnim) { // definitely left hand.. - *activeUIHand_ptr = 0; - } else { - *activeUIHand_ptr = 1; - } - int turnResult; - __asm { - call intface_redraw_; - mov eax, obj; - call combat_turn_; - mov turnResult, eax; - } // restore state if (IsControllingNPC) { // if game was loaded during turn, PartyControlReset() was called and already restored state - *activeUIHand_ptr = dudeWeaponSlot; - memcpy(dude_traits, real_traits, sizeof(DWORD)*2); - *obj_dude_ptr = real_dude; - *inven_dude_ptr = real_dude; - __asm { - call intface_redraw_; - } - SetInventoryCheck(false); - IsControllingNPC = 0; + RestoreRealDudeState(); } // -1 means that combat ended during turn return (turnResult == -1) ? -1 : 1; @@ -128,6 +247,10 @@ static void _declspec(naked) ItemDropHook() { } */ +static void __stdcall DisplayCantDoThat() { + DisplayConsoleMessage(GetMessageStr(MSG_FILE_PROTO, 675)); // I Can't do that +} + // 1 skip handler, -1 don't skip int __stdcall PartyControl_SwitchHandHook(TGameObj* item) { if (ItemGetType(item) == 3 && IsControllingNPC > 0) { @@ -141,7 +264,7 @@ int __stdcall PartyControl_SwitchHandHook(TGameObj* item) { call ai_can_use_weapon_; mov canUse, eax; }*/ - int fId = (*obj_dude_ptr)->artFID; + int fId = (*ptr_obj_dude)->artFID; char weaponCode = AnimCodeByWeapon(item); fId = (fId & 0xffff0fff) | (weaponCode << 12); // check if art with this weapon exists @@ -151,7 +274,7 @@ int __stdcall PartyControl_SwitchHandHook(TGameObj* item) { mov canUse, eax; } if (!canUse) { - DisplayConsoleMessage(GetMessageStr(MSG_FILE_PROTO, 675)); // I can't do that + DisplayCantDoThat(); return 1; } } @@ -197,13 +320,39 @@ gonormal: } } +static void __declspec(naked) stat_pc_add_experience_hook() { + __asm { + xor eax, eax + cmp IsControllingNPC, eax + je skip + add DelayedExperience, esi + retn +skip: + xchg esi, eax + jmp stat_pc_add_experience_ + } +} + +// prevents using sneak when controlling NPCs +static void __declspec(naked) pc_flag_toggle_hook() { + __asm { + cmp IsControllingNPC, 0 + je end + call DisplayCantDoThat + retn +end: + call pc_flag_toggle_ + retn + } +} + void PartyControlInit() { Mode = GetPrivateProfileIntA("Misc", "ControlCombat", 0, ini); if (Mode > 2) Mode = 0; if (Mode > 0) { char pidbuf[512]; - pidbuf[511]=0; + pidbuf[511] = 0; if (GetPrivateProfileStringA("Misc", "ControlCombatPIDList", "", pidbuf, 511, ini)) { char* ptr = pidbuf; char* comma; @@ -222,23 +371,24 @@ void PartyControlInit() { dlog_f(" Mode %d, Chars read: %d.", DL_INIT, Mode, Chars.size()); HookCall(0x46EBEE, &FidChangeHook); - //TODO: Change trait check. MakeCall(0x422354, &CombatHack_add_noncoms_, true); HookCall(0x422D87, &CombatWrapper_v2); HookCall(0x422E20, &CombatWrapper_v2); + + HookCall(0x454218, &stat_pc_add_experience_hook); // call inside op_give_exp_points_hook + HookCall(0x4124F1, &pc_flag_toggle_hook); + HookCall(0x41279A, &pc_flag_toggle_hook); } else dlog(" Disabled.", DL_INIT); } -void __stdcall PartyControlReset() -{ - if (real_dude != NULL && IsControllingNPC > 0) { - memcpy(dude_traits, real_traits, sizeof(DWORD)*2); - *obj_dude_ptr = real_dude; - *inven_dude_ptr = real_dude; - IsControllingNPC = 0; +void __stdcall PartyControlReset() { + if (real_dude != nullptr && IsControllingNPC > 0) { + RestoreRealDudeState(); } - real_dude = NULL; - SetInventoryCheck(false); +} + +bool IsNpcControlled() { + return IsControllingNPC != 0; } diff --git a/sfall/PartyControl.h b/sfall/PartyControl.h index 79c8296a..70dc26a2 100644 --- a/sfall/PartyControl.h +++ b/sfall/PartyControl.h @@ -19,4 +19,5 @@ void PartyControlInit(); void __stdcall PartyControlReset(); -int __stdcall PartyControl_SwitchHandHook(TGameObj* item); \ No newline at end of file +int __stdcall PartyControl_SwitchHandHook(TGameObj* item); +bool IsNpcControlled(); \ No newline at end of file diff --git a/sfall/Perks.cpp b/sfall/Perks.cpp index fc838962..3a29297b 100644 --- a/sfall/Perks.cpp +++ b/sfall/Perks.cpp @@ -16,12 +16,13 @@ * along with this program. If not, see . */ +#include + #include "main.h" #include "Define.h" #include "FalloutEngine.h" #include "Perks.h" -#include "vector9x.cpp" //static const BYTE PerksUsed=121; @@ -74,9 +75,9 @@ struct FakePerk { char Desc[1024]; }; -vector fakeTraits; -vector fakePerks; -vector fakeSelectablePerks; +std::vector fakeTraits; +std::vector fakePerks; +std::vector fakeSelectablePerks; static DWORD RemoveTraitID; static DWORD RemovePerkID; @@ -163,7 +164,7 @@ void _stdcall SetSelectablePerk(char* name, int level, int image, char* desc) { if(level==0) { for(DWORD i=0;i. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ #include "main.h" +#include #include #include #include + #include "Arrays.h" #include "BarBoxes.h" #include "Console.h" @@ -31,75 +33,317 @@ #include "input.h" #include "LoadGameHook.h" #include "Logging.h" -#include "numbers.h" #include "ScriptExtender.h" #include "version.h" +#if (_MSC_VER < 1600) +#include "Cpp11_emu.h" +#endif void _stdcall HandleMapUpdateForScripts(DWORD procId); // variables for new opcodes -static DWORD opArgCount = 0; -static DWORD opArgs[5]; -static DWORD opArgTypes[5]; -static DWORD opRet; -static DWORD opRetType; +#define OP_MAX_ARGUMENTS (10) -static void _stdcall SetOpReturn(DWORD value, DWORD type) { - opRet = value; - opRetType = type; -} +// masks for argument validation +#define DATATYPE_MASK_INT (1 << DATATYPE_INT) +#define DATATYPE_MASK_FLOAT (1 << DATATYPE_FLOAT) +#define DATATYPE_MASK_STR (1 << DATATYPE_STR) +#define DATATYPE_MASK_NOT_NULL (0x00010000) +#define DATATYPE_MASK_VALID_OBJ (DATATYPE_MASK_INT | DATATYPE_MASK_NOT_NULL) -static void _stdcall SetOpReturn(int value) { - SetOpReturn((DWORD)value, DATATYPE_INT); -} +struct SfallOpcodeMetadata { + // opcode handler, will be used as key + void (*handler)(); -static void _stdcall SetOpReturn(float value) { - SetOpReturn(*(DWORD*)&value, DATATYPE_FLOAT); -} + // opcode name, only used for logging + const char* name; -static void _stdcall SetOpReturn(const char* value) { - SetOpReturn((DWORD)value, DATATYPE_STR); -} + // argument validation masks + int argTypeMasks[OP_MAX_ARGUMENTS]; +}; -static bool _stdcall IsOpArgInt(int num) { - return (opArgTypes[num] == DATATYPE_INT); -} +typedef std::tr1::unordered_map OpcodeMetaTableType; -static bool _stdcall IsOpArgFloat(int num) { - return (opArgTypes[num] == DATATYPE_FLOAT); -} +static OpcodeMetaTableType opcodeMetaTable; -static bool _stdcall IsOpArgStr(int num) { - return (opArgTypes[num] == DATATYPE_STR); -} - -static int _stdcall GetOpArgInt(int num) { - switch (opArgTypes[num]) { - case DATATYPE_FLOAT: - return (int)*(float*)&opArgs[num]; - case DATATYPE_INT: - return (int)opArgs[num]; - default: - return 0; +class ScriptValue { +public: + ScriptValue(SfallDataType type, DWORD value) { + _val.dw = value; + _type = type; } -} -static float _stdcall GetOpArgFloat(int num) { - switch (opArgTypes[num]) { - case DATATYPE_FLOAT: - return *(float*)&opArgs[num]; - case DATATYPE_INT: - return (float)(int)opArgs[num]; - default: - return 0.0; + ScriptValue() { + _val.dw = 0; + _type = DATATYPE_NONE; } -} -static const char* _stdcall GetOpArgStr(int num) { - return (opArgTypes[num] == DATATYPE_STR) - ? (const char*)opArgs[num] - : ""; -} + ScriptValue(const char* strval) { + _val.str = strval; + _type = DATATYPE_STR; + } + + ScriptValue(int val) { + _val.i = val; + _type = DATATYPE_INT; + } + + ScriptValue(float strval) { + _val.f = strval; + _type = DATATYPE_FLOAT; + } + + ScriptValue(TGameObj* obj) { + _val.gObj = obj; + _type = DATATYPE_INT; + } + + bool isInt() const { + return _type == DATATYPE_INT; + } + + bool isFloat() const { + return _type == DATATYPE_FLOAT; + } + + bool isString() const { + return _type == DATATYPE_STR; + } + + DWORD rawValue() const { + return _val.dw; + } + + int asInt() const { + switch (_type) { + case DATATYPE_FLOAT: + return static_cast(_val.f); + case DATATYPE_INT: + return _val.i; + default: + return 0; + } + } + + float asFloat() const { + switch (_type) { + case DATATYPE_FLOAT: + return _val.f; + case DATATYPE_INT: + return static_cast(_val.i); + default: + return 0.0; + } + } + + const char* asString() const { + return (_type == DATATYPE_STR) + ? _val.str + : ""; + } + + TGameObj* asObject() const { + return (_type == DATATYPE_INT) + ? _val.gObj + : nullptr; + } + + SfallDataType type() const { + return _type; + } + +private: + union Value { + DWORD dw; + int i; + float f; + const char* str; + TGameObj* gObj; + } _val; + + SfallDataType _type; // TODO: replace with enum class +} ; + +typedef struct { + union Value { + DWORD dw; + int i; + float f; + const char* str; + TGameObj* gObj; + } val; + DWORD type; // TODO: replace with enum class +} ScriptValue1; + +class OpcodeHandler { +public: + OpcodeHandler() { + _argShift = 0; + _args.reserve(OP_MAX_ARGUMENTS); + } + + // number of arguments, possibly reduced by argShift + int numArgs() const { + return _args.size() - _argShift; + } + + // returns current argument shift, default is 0 + int argShift() const { + return _argShift; + } + + // sets shift value for arguments + // for example if shift value is 2, then subsequent calls to arg(i) will return arg(i+2) instead, etc. + void setArgShift(int shift) { + assert(shift >= 0); + + _argShift = shift; + } + + // returns argument with given index, possible shifted by argShift + const ScriptValue& arg(int index) const { + return _args.at(index + _argShift); + } + + // current return value + const ScriptValue& returnValue() const { + return _ret; + } + + // current script program + TProgram* program() const { + return _program; + } + + // set return value for current opcode + void setReturn(DWORD value, SfallDataType type) { + _ret = ScriptValue(type, value); + } + + // set return value for current opcode + void setReturn(const ScriptValue& val) { + _ret = val; + } + + // resets the state of handler for new opcode invocation + void resetState(TProgram* program, int argNum) { + _program = program; + + // reset return value + _ret = ScriptValue(); + // reset argument list + _args.resize(argNum); + // reset arg shift + _argShift = 0; + } + + // writes error message to debug.log along with the name of script & procedure + void printOpcodeError(const char* fmt, ...) const { + assert(_program != nullptr); + + va_list args; + va_start(args, fmt); + char msg[1024]; + vsnprintf_s(msg, sizeof msg, _TRUNCATE, fmt, args); + va_end(args); + + const char* procName = FindCurrentProc(_program); + DebugPrintf("\nOPCODE ERROR: %s\n Current script: %s, procedure %s.", msg, _program->fileName, procName); + } + + // Validate opcode arguments against type masks + // if validation pass, returns true, otherwise writes error to debug.log and returns false + bool validateArguments(const int argTypeMasks[], int argCount, const char* opcodeName) const { + for (int i = 0; i < argCount; i++) { + int typeMask = argTypeMasks[i]; + const ScriptValue &argI = arg(i); + if (typeMask != 0 && ((1 << argI.type()) & typeMask) == 0) { + printOpcodeError( + "%s() - argument #%d has invalid type: %s.", + opcodeName, + i, + GetSfallTypeName(argI.type())); + + return false; + } else if ((typeMask & DATATYPE_MASK_NOT_NULL) && argI.rawValue() == 0) { + printOpcodeError( + "%s() - argument #%d is null.", + opcodeName, + i); + + return false; + } + } + return true; + } + + // Handle opcodes + // scriptPtr - pointer to script program (from the engine) + // func - opcode handler + // hasReturn - true if opcode has return value (is expression) + void __thiscall handleOpcode(TProgram* program, void(*func)(), int argNum, bool hasReturn) { + assert(argNum < OP_MAX_ARGUMENTS); + + // reset state after previous + resetState(program, argNum); + + // process arguments on stack (reverse order) + for (int i = argNum - 1; i >= 0; i--) { + // get argument from stack + DWORD rawValueType = InterpretPopShort(program); + DWORD rawValue = InterpretPopLong(program); + SfallDataType type = static_cast(getSfallTypeByScriptType(rawValueType)); + + // retrieve string argument + if (type == DATATYPE_STR) { + _args.at(i) = InterpretGetString(program, rawValue, rawValueType); + } else { + _args.at(i) = ScriptValue(type, rawValue); + } + } + // flag that arguments passed are valid + bool argumentsValid = true; + + // check if metadata is available + OpcodeMetaTableType::iterator it = opcodeMetaTable.find(func); + if (it != opcodeMetaTable.end()) { + const SfallOpcodeMetadata* meta = it->second; + + // automatically validate argument types + argumentsValid = validateArguments(meta->argTypeMasks, argNum, meta->name); + } + + // call opcode handler if arguments are valid (or no automatic validation was done) + if (argumentsValid) { + func(); + } + + // process return value + if (hasReturn) { + if (_ret.type() == DATATYPE_NONE) { + // if no value was set in handler, force return 0 to avoid stack error + _ret = ScriptValue(0); + } + DWORD rawResult = _ret.rawValue(); + if (_ret.type() == DATATYPE_STR) { + rawResult = InterpretAddString(program, _ret.asString()); + } + InterpretPushLong(program, rawResult); + InterpretPushShort(program, getScriptTypeBySfallType(_ret.type())); + } + } + +private: + TProgram* _program; + + int _argShift; + std::vector _args; + ScriptValue _ret; +}; + +static OpcodeHandler opHandler; + + #include "ScriptOps\AsmMacros.h" #include "ScriptOps\ScriptArrays.hpp" @@ -115,7 +359,30 @@ static const char* _stdcall GetOpArgStr(int num) { #include "ScriptOps\ObjectsOps.hpp" #include "ScriptOps\AnimOps.hpp" #include "ScriptOps\MiscOps.hpp" +#include "ScriptOps\MetaruleOp.hpp" +/* + Array for opcodes metadata. + + This is completely optional, added for convenience only. + + By adding opcode to this array, Sfall will automatically validate it's arguments using provided info. + On fail, errors will be printed to debug.log and opcode will not be executed. + If you don't include opcode in this array, you should take care of all argument validation inside handler itself. +*/ +static const SfallOpcodeMetadata opcodeMetaArray[] = { + {sf_test, "validate_test", {DATATYPE_MASK_INT, DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT, DATATYPE_MASK_STR, DATATYPE_NONE}}, + {sf_spatial_radius, "spatial_radius", {DATATYPE_MASK_VALID_OBJ}}, + {sf_critter_inven_obj2, "critter_inven_obj2", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, + //{op_message_str_game, {}} +}; + +static void InitOpcodeMetaTable() { + int length = sizeof(opcodeMetaArray) / sizeof(SfallOpcodeMetadata); + for (int i = 0; i < length; ++i) { + opcodeMetaTable[opcodeMetaArray[i].handler] = &opcodeMetaArray[i]; + } +} typedef void (_stdcall *regOpcodeProc)(WORD opcode,void* ptr); @@ -124,7 +391,7 @@ static DWORD MotionSensorMode; static BYTE toggleHighlightsKey; static DWORD HighlightContainers; static DWORD Color_Containers; -static int idle; +static signed char idle; static char HighlightFail1[128]; static char HighlightFail2[128]; @@ -927,8 +1194,13 @@ void ScriptExtenderSetup() { GetPrivateProfileStringA("Sfall", "HighlightFail1", "You aren't carrying a motion sensor.", HighlightFail1, 128, translationIni); GetPrivateProfileStringA("Sfall", "HighlightFail2", "Your motion sensor is out of charge.", HighlightFail2, 128, translationIni); - idle=GetPrivateProfileIntA("Misc", "ProcessorIdle", -1, ini); - modifiedIni=GetPrivateProfileIntA("Main", "ModifiedIni", 0, ini); + idle = GetPrivateProfileIntA("Misc", "ProcessorIdle", -1, ini); + if (idle > -1) { + SafeWrite32(_idle_func, (DWORD)Sleep); + SafeWrite8(0x4C9F12, 0x6A); // push + SafeWrite8(0x4C9F13, idle); + } + modifiedIni = GetPrivateProfileIntA("Main", "ModifiedIni", 0, ini); dlogr("Adding additional opcodes", DL_SCRIPT); if(AllowUnsafeScripting) dlogr(" Unsafe opcodes enabled", DL_SCRIPT); @@ -1248,6 +1520,17 @@ void ScriptExtenderSetup() { opcodes[0x273]=op_create_spatial; opcodes[0x274]=op_art_exists; opcodes[0x275]=op_obj_is_carrying_obj; + // universal opcodes + opcodes[0x276]=op_sfall_metarule0; + opcodes[0x277]=op_sfall_metarule1; + opcodes[0x278]=op_sfall_metarule2; + opcodes[0x279]=op_sfall_metarule3; + opcodes[0x27a]=op_sfall_metarule4; + opcodes[0x27b]=op_sfall_metarule5; + opcodes[0x27c]=op_sfall_metarule6; // if you need more arguments - use arrays + + InitOpcodeMetaTable(); + InitMetaruleTable(); } @@ -1261,6 +1544,7 @@ DWORD GetScriptProcByName(DWORD scriptPtr, const char* procName) { } return result; } + // loads script from .int file into a sScriptProgram struct, filling script pointer and proc lookup table void LoadScriptProgram(sScriptProgram &prog, const char* fileName) { DWORD scriptPtr; @@ -1318,14 +1602,15 @@ bool _stdcall isGameScript(const char* filename) { void LoadGlobalScripts() { isGameLoading = false; HookScriptInit(); - dlogr("Loading global scripts", DL_SCRIPT); + dlogr("Loading global scripts", DL_SCRIPT|DL_INIT); char* name = "scripts\\gl*.int"; DWORD count, *filenames; __asm { + xor ecx, ecx xor ebx, ebx - mov eax, name lea edx, filenames + mov eax, name call db_get_file_list_ mov count, eax } @@ -1336,10 +1621,12 @@ void LoadGlobalScripts() { name = _strlwr((char*)filenames[i]); name[strlen(name) - 4] = 0; if (!isGameScript(name)) { + dlog(">", DL_SCRIPT); dlog(name, DL_SCRIPT); isGlobalScriptLoading = 1; LoadScriptProgram(prog, name); if (prog.ptr) { + dlogr(" Done", DL_SCRIPT); DWORD idx; sGlobalScript gscript = sGlobalScript(prog); idx = globalScripts.size(); @@ -1347,7 +1634,6 @@ void LoadGlobalScripts() { AddProgramToMap(prog); // initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array InitScriptProgram(prog); - dlogr(" Done", DL_SCRIPT); } else dlogr(" Error!", DL_SCRIPT); isGlobalScriptLoading = 0; } @@ -1357,7 +1643,7 @@ void LoadGlobalScripts() { lea eax, filenames call db_free_file_list_ } - dlogr("Finished loading global scripts", DL_SCRIPT); + dlogr("Finished loading global scripts", DL_SCRIPT|DL_INIT); //ButtonsReload(); } @@ -1454,7 +1740,7 @@ void AfterAttackCleanup() { } static void RunGlobalScripts1() { - if (idle>-1) Sleep(idle); + if (idle > -1) Sleep(idle); if (toggleHighlightsKey) { //0x48C294 to toggle if (KeyDown(toggleHighlightsKey)) { @@ -1476,7 +1762,7 @@ static void RunGlobalScripts1() { mov highlightingToggled, eax; } if (!highlightingToggled) DisplayConsoleMessage(HighlightFail2); - } else highlightingToggled=1; + } else highlightingToggled = 1; } else { DisplayConsoleMessage(HighlightFail1); } @@ -1486,10 +1772,10 @@ static void RunGlobalScripts1() { } } else if(highlightingToggled) { if (highlightingToggled == 1) obj_outline_all_items_off(); - highlightingToggled=0; + highlightingToggled = 0; } } - for (DWORD d=0; d= globalScripts[d].repeat) { RunScript(&globalScripts[d]); @@ -1499,20 +1785,20 @@ static void RunGlobalScripts1() { } void RunGlobalScripts2() { - if(idle>-1) Sleep(idle); - for(DWORD d=0;d=globalScripts[d].repeat) { + if (idle > -1) Sleep(idle); + for (DWORD d = 0; d < globalScripts.size(); d++) { + if (!globalScripts[d].repeat || globalScripts[d].mode != 1) continue; + if (++globalScripts[d].count >= globalScripts[d].repeat) { RunScript(&globalScripts[d]); } } ResetStateAfterFrame(); } void RunGlobalScripts3() { - if(idle>-1) Sleep(idle); - for(DWORD d=0;d=globalScripts[d].repeat) { + if (idle > -1) Sleep(idle); + for (DWORD d = 0; d < globalScripts.size(); d++) { + if (!globalScripts[d].repeat || (globalScripts[d].mode != 2 && globalScripts[d].mode != 3)) continue; + if (++globalScripts[d].count >= globalScripts[d].repeat) { RunScript(&globalScripts[d]); } } diff --git a/sfall/ScriptExtender.h b/sfall/ScriptExtender.h index f8cc7428..97ae3e8e 100644 --- a/sfall/ScriptExtender.h +++ b/sfall/ScriptExtender.h @@ -19,20 +19,21 @@ #pragma once #include "main.h" -#define DATATYPE_NONE (0) -#define DATATYPE_INT (1) -#define DATATYPE_FLOAT (2) -#define DATATYPE_STR (3) +// TODO: replace with enum class +enum SfallDataType { + DATATYPE_NONE = 0, + DATATYPE_INT, + DATATYPE_FLOAT, + DATATYPE_STR +}; -struct sGlobalVar -{ +struct sGlobalVar { __int64 id; int val; }; #define SCRIPT_PROC_MAX (27) -typedef struct -{ +typedef struct { DWORD ptr; DWORD procLookup[SCRIPT_PROC_MAX+1]; char initialized; @@ -57,7 +58,6 @@ int GetNumGlobals(); void GetGlobals(sGlobalVar* globals); void SetGlobals(sGlobalVar* globals); - extern DWORD AddUnarmedStatToGetYear; extern DWORD AvailableGlobalScriptTypes; @@ -67,6 +67,8 @@ void GetAppearanceGlobals(int *race, int *style); void _stdcall RegAnimCombatCheck(DWORD newValue); DWORD _stdcall ScriptHasLoaded(DWORD script); +// finds procedure ID for given script program pointer and procedure name +DWORD GetScriptProcByName(DWORD scriptPtr, const char* procName); // loads script from .int file into scripting engine, fill scriptPtr and proc table void LoadScriptProgram(sScriptProgram &prog, const char* fileName); // init program after load, needs to be called once diff --git a/sfall/ScriptOps/AsmMacros.h b/sfall/ScriptOps/AsmMacros.h index 78a0b1d8..f2a0ebf1 100644 --- a/sfall/ScriptOps/AsmMacros.h +++ b/sfall/ScriptOps/AsmMacros.h @@ -135,50 +135,19 @@ __asm resultnotstr##num: \ - use opArgs, opArgTypes to access arguments - use opRet, opRetType to set return value - argnum - number of arguments func - C-function to call (should not have arguments) + argnum - number of opcode arguments + isExpression - 1 if opcode has return value, 0 otherwise */ -#define _WRAP_OPCODE(argnum, func) __asm { \ +#define _WRAP_OPCODE(func, argnum, isExpression) __asm { \ __asm pushad \ - __asm mov ebp, eax \ - __asm mov esi, argnum \ - __asm shl esi, 2 \ - __asm mov opArgCount, argnum \ - __asm mov opRetType, 0 \ -__asm loopbegin: \ - __asm test esi, esi \ - __asm jz loopend \ - __asm sub esi, 4 \ - __asm mov eax, ebp \ - __asm call interpretPopShort_ \ + __asm push isExpression \ + __asm push argnum \ + __asm push func \ __asm push eax \ - __asm call getSfallTypeByScriptType \ - __asm mov opArgTypes[esi], eax \ - __asm mov eax, ebp \ - __asm call interpretPopLong_ \ - __asm mov opArgs[esi], eax \ - __asm jmp loopbegin \ -__asm loopend: \ - __asm call func \ - __asm mov eax, opRetType \ - __asm test eax, eax \ - __asm jz end \ - __asm push eax \ - __asm call getScriptTypeBySfallType \ - __asm mov ecx, eax \ - __asm mov edx, opRet \ - __asm cmp ecx, VAR_TYPE_STR \ - __asm jne notstring \ - __asm mov eax, ebp \ - __asm call interpretAddString_ \ - __asm mov edx, eax \ -__asm notstring: \ - __asm mov eax, ebp \ - __asm call interpretPushLong_ \ - __asm mov edx, ecx \ - __asm mov eax, ebp \ - __asm call interpretPushShort_ \ -__asm end: \ + __asm lea ecx, opHandler \ + __asm call OpcodeHandler::handleOpcode \ __asm popad \ __asm retn \ } + diff --git a/sfall/ScriptOps/InterfaceOp.hpp b/sfall/ScriptOps/InterfaceOp.hpp index 37751eaf..00eea764 100644 --- a/sfall/ScriptOps/InterfaceOp.hpp +++ b/sfall/ScriptOps/InterfaceOp.hpp @@ -440,3 +440,24 @@ end: retn; } } + +static void sf_intface_redraw() { + InterfaceRedraw(); +} + +static void sf_intface_show() { + __asm call intface_show_ +} + +static void sf_intface_hide() { + __asm call intface_hide_ +} + +static void sf_intface_is_hidden() { + int isHidden; + __asm { + call intface_is_hidden_ + mov isHidden, eax; + } + opHandler.setReturn(isHidden); +} \ No newline at end of file diff --git a/sfall/ScriptOps/MetaruleOp.hpp b/sfall/ScriptOps/MetaruleOp.hpp new file mode 100644 index 00000000..43e06997 --- /dev/null +++ b/sfall/ScriptOps/MetaruleOp.hpp @@ -0,0 +1,184 @@ +/* + * sfall + * Copyright (C) 2008, 2009, 2010, 2012 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include + +#include "main.h" +#include "ScriptExtender.h" + +// Metarule is a universal opcode(s) for all kinds of new sfall scripting functions. +// Prefix all function handlers with sf_ and add them to sfall_metarule_table. +// DO NOT add arguments and/or return values to function handlers! +// Use opHandler.arg(i), inside handler function to access arguments. +// Use opHandler.setReturn(x) to set return value. +// If you want to call user-defined procedures in your handler, use RunScriptProc(). + +struct SfallMetarule { + // function name + const char* name; + // pointer to handler function + void(*func)(); + // mininum number of arguments + int minArgs; + // maximum number of arguments + int maxArgs; +}; + +typedef std::tr1::unordered_map MetaruleTableType; + +static MetaruleTableType metaruleTable; + +// currently executed metarule +static const SfallMetarule* currentMetarule; + +static std::string sf_test_stringBuf; + +// Example handler. Feel free to add handlers in other files. +static void sf_test() { + std::ostringstream sstream; + sstream << "sfall_funcX(\"test\""; + for (int i = 0; i < opHandler.numArgs(); i++) { + const ScriptValue &arg = opHandler.arg(i); + sstream << ", "; + switch (arg.type()) { + case DATATYPE_INT: + sstream << arg.asInt(); + break; + case DATATYPE_FLOAT: + sstream << arg.asFloat(); + break; + case DATATYPE_STR: + sstream << '"' << arg.asString() << '"'; + break; + default: + sstream << "???"; + break; + } + } + sstream << ")"; + + sf_test_stringBuf = sstream.str(); + opHandler.setReturn(sf_test_stringBuf.c_str()); +} + +// returns current contents of metarule table +static void sf_get_metarule_table() { + DWORD arr = TempArray(metaruleTable.size(), 0); + int i = 0; + for (MetaruleTableType::iterator it = metaruleTable.begin(); it != metaruleTable.end(); it++) { + arrays[arr].val[i].set(it->first.c_str()); + i++; + } + opHandler.setReturn(arr, DATATYPE_INT); +} + +/* + Metarule array. + + Add your custom scripting functions here. + + Format is as follows: + { name, handler, minArgs, maxArgs } + - name - name of function that will be used to call it from scripts, + - handler - pointer to handler function (see examples below), + - minArgs/maxArgs - minimum and maximum number of arguments allowed for this function +*/ +static const SfallMetarule metaruleArray[] = { + {"get_metarule_table", sf_get_metarule_table, 0, 0}, + {"validate_test", sf_test, 2, 5}, + {"spatial_radius", sf_spatial_radius, 1, 1}, + {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2}, + {"intface_redraw", sf_intface_redraw, 0, 0}, + {"intface_show", sf_intface_show, 0, 0}, + {"intface_hide", sf_intface_hide, 0, 0}, + {"intface_is_hidden", sf_intface_is_hidden, 0, 0}, + {"exec_map_update_scripts", sf_exec_map_update_scripts, 0, 0}, +}; + +static void InitMetaruleTable() { + int length = sizeof(metaruleArray) / sizeof(SfallMetarule); + for (int i = 0; i < length; ++i) { + metaruleTable[metaruleArray[i].name] = &metaruleArray[i]; + } +} + +// Validates arguments against metarule specification. +// On error prints to debug.log and returns false. +static bool ValidateMetaruleArguments(const SfallMetarule* metaruleInfo) { + int argCount = opHandler.numArgs(); + if (argCount < metaruleInfo->minArgs || argCount > metaruleInfo->maxArgs) { + opHandler.printOpcodeError( + "sfall_funcX(\"%s\", ...) - invalid number of arguments (%d), must be from %d to %d.", + metaruleInfo->name, + argCount, + metaruleInfo->minArgs, + metaruleInfo->maxArgs); + + return false; + } else { + // check if metadata is available for this handler + OpcodeMetaTableType::iterator it = opcodeMetaTable.find(metaruleInfo->func); + if (it != opcodeMetaTable.end()) { + const SfallOpcodeMetadata* meta = it->second; + + // automatically validate argument types + return opHandler.validateArguments(meta->argTypeMasks, argCount, metaruleInfo->name); + } + } + return true; +} + +static void _stdcall op_sfall_metarule_handler() { + const ScriptValue &nameArg = opHandler.arg(0); + if (nameArg.isString()) { + const char* name = nameArg.asString(); + MetaruleTableType::iterator lookup = metaruleTable.find(name); + if (lookup != metaruleTable.end()) { + currentMetarule = lookup->second; + // shift function name away, so argument #0 will correspond to actual first argument of function + // this allows to use the same handlers for opcodes and metarule functions + opHandler.setArgShift(1); + if (ValidateMetaruleArguments(currentMetarule)) { + currentMetarule->func(); + } + } else { + opHandler.printOpcodeError("sfall_funcX(name, ...) - name '%s' is unknown.", name); + } + } else { + opHandler.printOpcodeError("sfall_funcX(name, ...) - name must be string."); + } +} + +#define metaruleOpcode(numArg, numArgPlusOne) \ + static void __declspec(naked) op_sfall_metarule##numArg() {\ + _WRAP_OPCODE(op_sfall_metarule_handler, numArgPlusOne, 1)\ + } + +metaruleOpcode(0, 1) +metaruleOpcode(1, 2) +metaruleOpcode(2, 3) +metaruleOpcode(3, 4) +metaruleOpcode(4, 5) +metaruleOpcode(5, 6) +metaruleOpcode(6, 7) + +#undef metaruleOpcode diff --git a/sfall/ScriptOps/MiscOps.hpp b/sfall/ScriptOps/MiscOps.hpp index 3e45d422..dc2aa98f 100644 --- a/sfall/ScriptOps/MiscOps.hpp +++ b/sfall/ScriptOps/MiscOps.hpp @@ -1674,3 +1674,8 @@ end: _RET_VAL_INT(ebp) _OP_END } + + +static void sf_exec_map_update_scripts() { + __asm call scr_exec_map_update_scripts_ +} \ No newline at end of file diff --git a/sfall/ScriptOps/ObjectsOps.hpp b/sfall/ScriptOps/ObjectsOps.hpp index 7719b031..3752b192 100644 --- a/sfall/ScriptOps/ObjectsOps.hpp +++ b/sfall/ScriptOps/ObjectsOps.hpp @@ -21,9 +21,13 @@ #include "main.h" #include "Inventory.h" #include "ScriptExtender.h" +#if (_MSC_VER < 1600) +#include "Cpp11_emu.h" +#endif //script control functions +// TODO: rewrite static void __declspec(naked) RemoveScript() { __asm { push ebx; @@ -52,6 +56,7 @@ end: } } +// TODO: rewrite static void __declspec(naked) SetScript() { __asm { pushad; @@ -111,11 +116,12 @@ end: } } +// TODO: rewrite, remove all ASM static void _stdcall op_create_spatial2() { - DWORD scriptIndex = GetOpArgInt(0), - tile = GetOpArgInt(1), - elevation = GetOpArgInt(2), - radius = GetOpArgInt(3), + DWORD scriptIndex = opHandler.arg(0).asInt(), + tile = opHandler.arg(1).asInt(), + elevation = opHandler.arg(2).asInt(), + radius = opHandler.arg(3).asInt(), scriptId, tmp, objectPtr, scriptPtr; __asm { @@ -148,24 +154,19 @@ static void _stdcall op_create_spatial2() { call scr_find_obj_from_program_; mov objectPtr, eax; } - SetOpReturn((int)objectPtr); + opHandler.setReturn((int)objectPtr); } static void __declspec(naked) op_create_spatial() { - _WRAP_OPCODE(4, op_create_spatial2) + _WRAP_OPCODE(op_create_spatial2, 4, 1) } -static void __declspec(naked) op_spatial_radius() { - _OP_BEGIN(ebp) - _GET_ARG_R32(ebp, ecx, eax) - _CHECK_ARG_INT(cx, fail) - __asm { - test eax, eax; - js fail; - mov eax, [eax+0xC]; -fail: + +static void sf_spatial_radius() { + TGameObj* spatialObj = opHandler.arg(0).asObject(); + TScript* script; + if (ScrPtr(spatialObj->scriptID, &script) != -1) { + opHandler.setReturn(script->spatial_radius); } - _RET_VAL_INT(ebp) - _OP_END } static void __declspec(naked) GetScript() { @@ -334,19 +335,15 @@ end: } static DWORD _stdcall obj_blocking_at_wrapper(DWORD obj, DWORD tile, DWORD elevation, DWORD func) { - DWORD retval; __asm { mov eax, obj; mov edx, tile; mov ebx, elevation; call func; - mov retval, eax; } - return retval; } static DWORD _stdcall make_straight_path_func_wrapper(DWORD obj, DWORD tileFrom, DWORD a3, DWORD tileTo, DWORD* result, DWORD a6, DWORD func) { - DWORD retval; __asm { mov eax, obj; mov edx, tileFrom; @@ -356,9 +353,7 @@ static DWORD _stdcall make_straight_path_func_wrapper(DWORD obj, DWORD tileFrom, push a6; push result; call make_straight_path_func_; - mov retval, eax; } - return retval; } #define BLOCKING_TYPE_BLOCK (0) @@ -384,29 +379,29 @@ static DWORD getBlockingFunc(DWORD type) { } static void _stdcall op_make_straight_path2() { - DWORD objFrom = GetOpArgInt(0), - tileTo = GetOpArgInt(1), - type = GetOpArgInt(2), + DWORD objFrom = opHandler.arg(0).asInt(), + tileTo = opHandler.arg(1).asInt(), + type = opHandler.arg(2).asInt(), resultObj, arg6; arg6 = (type == BLOCKING_TYPE_SHOOT) ? 32 : 0; make_straight_path_func_wrapper(objFrom, *(DWORD*)(objFrom + 4), 0, tileTo, &resultObj, arg6, getBlockingFunc(type)); - SetOpReturn(resultObj, DATATYPE_INT); + opHandler.setReturn(resultObj, DATATYPE_INT); } static void __declspec(naked) op_make_straight_path() { - _WRAP_OPCODE(3, op_make_straight_path2) + _WRAP_OPCODE(op_make_straight_path2, 3, 1) } static void _stdcall op_make_path2() { - DWORD objFrom = GetOpArgInt(0), + DWORD objFrom = opHandler.arg(0).asInt(), tileFrom = 0, - tileTo = GetOpArgInt(1), - type = GetOpArgInt(2), + tileTo = opHandler.arg(1).asInt(), + type = opHandler.arg(2).asInt(), func = getBlockingFunc(type), arr; long pathLength, a5 = 1; if (!objFrom) { - SetOpReturn(0, DATATYPE_INT); + opHandler.setReturn(0, DATATYPE_INT); return; } tileFrom = *(DWORD*)(objFrom + 4); @@ -426,33 +421,33 @@ static void _stdcall op_make_path2() { for (int i=0; i < pathLength; i++) { arrays[arr].val[i].set((long)pathData[i]); } - SetOpReturn(arr, DATATYPE_INT); + opHandler.setReturn(arr, DATATYPE_INT); } static void __declspec(naked) op_make_path() { - _WRAP_OPCODE(3, op_make_path2) + _WRAP_OPCODE(op_make_path2, 3, 1) } static void _stdcall op_obj_blocking_at2() { - DWORD tile = GetOpArgInt(0), - elevation = GetOpArgInt(1), - type = GetOpArgInt(2), + DWORD tile = opHandler.arg(0).asInt(), + elevation = opHandler.arg(1).asInt(), + type = opHandler.arg(2).asInt(), resultObj; resultObj = obj_blocking_at_wrapper(0, tile, elevation, getBlockingFunc(type)); if (resultObj && type == BLOCKING_TYPE_SHOOT && (*(DWORD*)(resultObj + 39) & 0x80)) { // don't know what this flag means, copy-pasted from the engine code // this check was added because the engine always does exactly this when using shoot blocking checks resultObj = 0; } - SetOpReturn(resultObj, DATATYPE_INT); + opHandler.setReturn(resultObj, DATATYPE_INT); } static void __declspec(naked) op_obj_blocking_at() { - _WRAP_OPCODE(3, op_obj_blocking_at2) + _WRAP_OPCODE(op_obj_blocking_at2, 3, 1) } static void _stdcall op_tile_get_objects2() { - DWORD tile = GetOpArgInt(0), - elevation = GetOpArgInt(1), + DWORD tile = opHandler.arg(0).asInt(), + elevation = opHandler.arg(1).asInt(), obj; DWORD arrayId = TempArray(0, 4); __asm { @@ -468,15 +463,15 @@ static void _stdcall op_tile_get_objects2() { mov obj, eax; } } - SetOpReturn(arrayId, DATATYPE_INT); + opHandler.setReturn(arrayId, DATATYPE_INT); } static void __declspec(naked) op_tile_get_objects() { - _WRAP_OPCODE(2, op_tile_get_objects2) + _WRAP_OPCODE(op_tile_get_objects2, 2, 1) } static void _stdcall op_get_party_members2() { - DWORD obj, mode = GetOpArgInt(0), isDead; + DWORD obj, mode = opHandler.arg(0).asInt(), isDead; int i, actualCount = *(DWORD*)_partyMemberCount; DWORD arrayId = TempArray(0, 4); DWORD* partyMemberList = *(DWORD**)_partyMemberList; @@ -497,11 +492,11 @@ static void _stdcall op_get_party_members2() { } arrays[arrayId].push_back((long)obj); } - SetOpReturn(arrayId, DATATYPE_INT); + opHandler.setReturn(arrayId, DATATYPE_INT); } static void __declspec(naked) op_get_party_members() { - _WRAP_OPCODE(1, op_get_party_members2) + _WRAP_OPCODE(op_get_party_members2, 1, 1) } static void __declspec(naked) op_art_exists() { @@ -520,11 +515,14 @@ end: } static void _stdcall op_obj_is_carrying_obj2() { - DWORD num = 0; - if (IsOpArgInt(0) && IsOpArgInt(1)) { - TGameObj *invenObj = (TGameObj*)GetOpArgInt(0), - *itemObj = (TGameObj*)GetOpArgInt(1); - if (invenObj && itemObj) { + int num = 0; + const ScriptValue &invenObjArg = opHandler.arg(0), + &itemObjArg = opHandler.arg(1); + + if (invenObjArg.isInt() && itemObjArg.isInt()) { + TGameObj *invenObj = (TGameObj*)invenObjArg.asObject(), + *itemObj = (TGameObj*)itemObjArg.asObject(); + if (invenObj != nullptr && itemObj != nullptr) { for (int i = 0; i < invenObj->invenCount; i++) { if (invenObj->invenTablePtr[i].object == itemObj) { num = invenObj->invenTablePtr[i].count; @@ -533,9 +531,30 @@ static void _stdcall op_obj_is_carrying_obj2() { } } } - SetOpReturn(num, DATATYPE_INT); + opHandler.setReturn(num); } static void __declspec(naked) op_obj_is_carrying_obj() { - _WRAP_OPCODE(2, op_obj_is_carrying_obj2) + _WRAP_OPCODE(op_obj_is_carrying_obj2, 2, 1) +} + +static void sf_critter_inven_obj2() { + TGameObj* critter = opHandler.arg(0).asObject(); + int slot = opHandler.arg(1).asInt(); + switch (slot) { + case 0: + opHandler.setReturn(InvenWorn(critter)); + break; + case 1: + opHandler.setReturn(InvenRightHand(critter)); + break; + case 2: + opHandler.setReturn(InvenLeftHand(critter)); + break; + case -2: + opHandler.setReturn(critter->invenCount); + break; + default: + opHandler.printOpcodeError("critter_inven_obj2() - invalid type."); + } } diff --git a/sfall/ScriptOps/ScriptUtils.hpp b/sfall/ScriptOps/ScriptUtils.hpp index 469de6f4..82dd2550 100644 --- a/sfall/ScriptOps/ScriptUtils.hpp +++ b/sfall/ScriptOps/ScriptUtils.hpp @@ -770,72 +770,72 @@ end: } static void funcPow2() { - /*char buf[1024]; - sprintf(buf, "Pow! %d %d %d %d", opArgs[0], opArgTypes[0], opArgs[1], opArgTypes[1]); - DisplayConsoleMessage(buf);*/ - float base, result = 0.0; - if (!IsOpArgStr(0) && !IsOpArgStr(1)) { - base = GetOpArgFloat(0); - if (IsOpArgFloat(1)) - result = pow(base, GetOpArgFloat(1)); + const ScriptValue &base = opHandler.arg(0), + &power = opHandler.arg(1); + float result = 0.0; + if (!base.isString() && !power.isString()) { + if (power.isFloat()) + result = pow(base.asFloat(), power.asFloat()); else - result = pow(base, GetOpArgInt(1)); + result = pow(base.asFloat(), power.asInt()); - if (IsOpArgInt(0) && IsOpArgInt(1)) { - SetOpReturn((DWORD)(int)result, DATATYPE_INT); + if (base.isInt() && power.isInt()) { + opHandler.setReturn(static_cast(result)); } else { - SetOpReturn(result); + opHandler.setReturn(result); } } else { - SetOpReturn(0, DATATYPE_INT); + opHandler.setReturn(0); } } static void __declspec(naked) funcPow() { - _WRAP_OPCODE(2, funcPow2) + _WRAP_OPCODE(funcPow2, 2, 1) } static void funcLog2() { - SetOpReturn(log(GetOpArgFloat(0))); + opHandler.setReturn(log(opHandler.arg(0).asFloat())); } static void __declspec(naked) funcLog() { - _WRAP_OPCODE(1, funcLog2) + _WRAP_OPCODE(funcLog2, 1, 1) } static void funcExp2() { - SetOpReturn(exp(GetOpArgFloat(0))); + opHandler.setReturn(exp(opHandler.arg(0).asFloat())); } static void __declspec(naked) funcExp() { - _WRAP_OPCODE(1, funcExp2) + _WRAP_OPCODE(funcExp2, 1, 1) } static void funcCeil2() { - SetOpReturn((int)ceil(GetOpArgFloat(0)), DATATYPE_INT); + opHandler.setReturn(static_cast(ceil(opHandler.arg(0).asFloat()))); } static void __declspec(naked) funcCeil() { - _WRAP_OPCODE(1, funcCeil2) + _WRAP_OPCODE(funcCeil2, 1, 1) } static void funcRound2() { - float arg = GetOpArgFloat(0); - int argI = (int)arg; - float mod = arg - (float)argI; - if (abs(mod) >= 0.5) + float arg = opHandler.arg(0).asFloat(); + int argI = static_cast(arg); + float mod = arg - static_cast(argI); + if (abs(mod) >= 0.5) { argI += (mod > 0 ? 1 : -1); - SetOpReturn(argI, DATATYPE_INT); + } + opHandler.setReturn(argI); } static void __declspec(naked) funcRound() { - _WRAP_OPCODE(1, funcRound2) + _WRAP_OPCODE(funcRound2, 1, 1) } /* */ +// TODO: move to FalloutEngine module static const DWORD game_msg_files[] = { 0x56D368 // COMBAT , 0x56D510 // AI @@ -858,14 +858,16 @@ static const DWORD game_msg_files[] = , 0x66BE38 // TRAIT , 0x672FB0 }; // WORLDMAP +// TODO: move to FalloutEngine static const DWORD* proto_msg_files = (DWORD*)0x006647AC; static void _stdcall op_message_str_game2() { - DWORD fileId = opArgs[0]; const char* msg = 0; - - if (IsOpArgInt(0) && IsOpArgInt(1)) { - int msgId = GetOpArgInt(1); + const ScriptValue &fileIdArg = opHandler.arg(0), + &msgIdArg = opHandler.arg(1); + if (fileIdArg.isInt() && msgIdArg.isInt()) { + int fileId = fileIdArg.asInt(); + int msgId = msgIdArg.asInt(); if (fileId < 20) { // main msg files msg = GetMessageStr(game_msg_files[fileId], msgId); } @@ -875,17 +877,17 @@ static void _stdcall op_message_str_game2() { else if (fileId >= 0x2000) { // Extra game message files. std::tr1::unordered_map::iterator it = gExtraGameMsgLists.find(fileId); - if (it != gExtraGameMsgLists.end()) + if (it != gExtraGameMsgLists.end()) { msg = GetMsg(it->second, msgId, 2); + } } } - - if (msg == 0) + if (msg == 0) { msg = "Error"; - - SetOpReturn(msg); + } + opHandler.setReturn(msg); } static void __declspec(naked) op_message_str_game() { - _WRAP_OPCODE(2, op_message_str_game2) + _WRAP_OPCODE(op_message_str_game2, 2, 1) } diff --git a/sfall/ScriptOps/StatsOp.hpp b/sfall/ScriptOps/StatsOp.hpp index 626e640c..b5d8916d 100644 --- a/sfall/ScriptOps/StatsOp.hpp +++ b/sfall/ScriptOps/StatsOp.hpp @@ -1002,8 +1002,8 @@ static void __declspec(naked) SetStatMin() { push eax; push edi; push eax; - call SetPCStatMax; - call SetNPCStatMax; + call SetPCStatMin; + call SetNPCStatMin; end: pop esi; pop edi; @@ -1071,7 +1071,7 @@ static void __declspec(naked) fSetPCStatMin() { jnz end; push edi; push eax; - call SetPCStatMax; + call SetPCStatMin; end: pop esi; pop edi; @@ -1139,7 +1139,7 @@ static void __declspec(naked) fSetNPCStatMin() { jnz end; push edi; push eax; - call SetNPCStatMax; + call SetNPCStatMin; end: pop esi; pop edi; diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index 7389fb52..6900b6aa 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -76,7 +76,6 @@ Level3 EditAndContinue - true kernel32.lib;user32.lib;d3d9.lib;d3dx9d.lib;Strmiids.lib;dinput.lib;ws2_32.lib;%(AdditionalDependencies) @@ -206,6 +205,7 @@ + @@ -242,6 +242,7 @@ + @@ -303,8 +304,6 @@ - - diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index c83ff852..f269eadf 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -7,9 +7,6 @@ {0e01fde3-77b4-4b8f-a0c5-71f211b14cf7} - - {07dcfbfe-dd6b-4b88-8b9e-c512c333406a} - {34e14844-5e67-4d8c-8817-89d2d1c23639} @@ -196,6 +193,12 @@ Headers + + Headers\ScriptOps + + + Headers + @@ -279,9 +282,6 @@ Source - - Source\win9x - Source @@ -298,9 +298,6 @@ Source - - Source\win9x - Source diff --git a/sfall/ddraw2.cpp b/sfall/ddraw2.cpp index 62f8b267..37ca1b91 100644 --- a/sfall/ddraw2.cpp +++ b/sfall/ddraw2.cpp @@ -16,19 +16,6 @@ * along with this program. If not, see . */ -#include "main.h" - -#include -#include "ddraw.h" -#include "FalloutEngine.h" -#include "Graphics.h" -#include "Version.h" -#include "input.h" -#include "math.h" -#include "vector9x.cpp" -#include "LoadGameHook.h" -#include "heads.h" - #ifdef _DEBUG #define D3D_DEBUG_INFO #define DEBUGMESS(a) OutputDebugStringA(a) @@ -38,6 +25,19 @@ #include #include +#include +#include +#include +#include + +#include "main.h" + +#include "FalloutEngine.h" +#include "Graphics.h" +#include "heads.h" +#include "input.h" +#include "LoadGameHook.h" +#include "Version.h" typedef HRESULT (_stdcall *DDrawCreateProc)(void*, IDirectDraw**, void*); typedef IDirect3D9* (_stdcall *D3DCreateProc)(UINT version); @@ -141,8 +141,8 @@ struct sShader { mode2=0; } }; -static vector shaders; -static vector shaderTextures; +static std::vector shaders; +static std::vector shaderTextures; #define MYVERTEXFORMAT D3DFVF_XYZRHW|D3DFVF_TEX1 struct MyVertex { diff --git a/sfall/dinput.cpp b/sfall/dinput.cpp index 08a3c53c..43455e9c 100644 --- a/sfall/dinput.cpp +++ b/sfall/dinput.cpp @@ -16,12 +16,13 @@ * along with this program. If not, see . */ +#define DIRECTINPUT_VERSION 0x0700 +#include +#include +#include + #include "main.h" -#define DIRECTINPUT_VERSION 0x0700 -#include -#include "queue9x.cpp" -#include #include "graphics.h" #include "ScriptExtender.h" #include "HookScripts.h" @@ -67,7 +68,7 @@ static int MPMouseY; #define kDeviceType_KEYBOARD 0 #define kDeviceType_MOUSE 1 -static queue bufferedPresses; +static std::queue bufferedPresses; static HKL keyboardLayout; diff --git a/sfall/main.cpp b/sfall/main.cpp index 411d93af..153ecfdf 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -60,6 +60,9 @@ #include "Tiles.h" #include "timer.h" #include "version.h" +#if (_MSC_VER < 1600) +#include "Cpp11_emu.h" +#endif bool IsDebug = false; @@ -83,7 +86,7 @@ static const char* musicOverridePath="data\\sound\\music\\"; bool npcautolevel; -static int* scriptDialog; +static int* scriptDialog = nullptr; //GetTickCount calls static const DWORD offsetsA[] = { @@ -383,12 +386,12 @@ static void __declspec(naked) ViewportHook() { } } -HANDLE _stdcall FakeFindFirstFile(const char* str, WIN32_FIND_DATAA* data) { - HANDLE h=FindFirstFileA(str,data); - if(h==INVALID_HANDLE_VALUE) return h; - while(strlen(data->cFileName)>12) { - int i=FindNextFileA(h, data); - if(i==0) { +/*HANDLE _stdcall FakeFindFirstFile(const char* str, WIN32_FIND_DATAA* data) { + HANDLE h = FindFirstFileA(str,data); + if (h == INVALID_HANDLE_VALUE) return h; + while (strlen(data->cFileName) > 12) { + int i = FindNextFileA(h, data); + if(i == 0) { FindClose(h); return INVALID_HANDLE_VALUE; } @@ -396,12 +399,13 @@ HANDLE _stdcall FakeFindFirstFile(const char* str, WIN32_FIND_DATAA* data) { return h; } int _stdcall FakeFindNextFile(HANDLE h, WIN32_FIND_DATAA* data) { - int i=FindNextFileA(h, data); - while(strlen(data->cFileName)>12&&i) { - i=FindNextFileA(h, data); + int i = FindNextFileA(h, data); + while (strlen(data->cFileName) > 12 && i) { + i = FindNextFileA(h, data); } return i; -} +}*/ + static void __declspec(naked) WeaponAnimHook() { __asm { cmp edx, 11; @@ -426,7 +430,7 @@ static void __declspec(naked) removeDatabase() { mov ecx, ebx nextPath: mov edx, [esp+0x104+4+4] // path_patches - mov eax, [ebx+0x0] // database.path + mov eax, [ebx] // database.path call stricmp_ test eax, eax // found path? jz skip // Yes @@ -458,7 +462,7 @@ static void __declspec(naked) game_init_databases_hack2() { cmp eax, -1 je end mov eax, ds:[_master_db_handle] - mov eax, [eax+0x0] // eax = master_patches.path + mov eax, [eax] // eax = master_patches.path call xremovepath_ dec eax // remove path (critter_patches == master_patches)? jz end // Yes @@ -848,6 +852,10 @@ static void DllMain2() { DWORD tmp; dlogr("In DllMain2", DL_MAIN); + dlogr("Running BugsInit.", DL_INIT); + BugsInit(); + dlogr(" Done", DL_INIT); + if (GetPrivateProfileIntA("Speed", "Enable", 0, ini)) { dlog("Applying speed patch.", DL_INIT); AddrGetTickCount = (DWORD)&FakeGetTickCount; @@ -1246,18 +1254,12 @@ static void DllMain2() { FileSystemInit(); } - /*DWORD horrigan=GetPrivateProfileIntA("Misc", "DisableHorrigan", 0, ini); - if(horrigan) { - if(*((BYTE*)0x004C06D8)!=0x75) return false; - SafeWrite8(0x004C06D8, 0xeb); - }*/ - - //if(GetPrivateProfileIntA("Misc", "PrintToFileFix", 0, ini)) { + /*if (GetPrivateProfileIntA("Misc", "PrintToFileFix", 0, ini)) { dlog("Applying print to file patch.", DL_INIT); - SafeWrite32(0x006C0364, (DWORD)&FakeFindFirstFile); - SafeWrite32(0x006C0368, (DWORD)&FakeFindNextFile); + SafeWrite32(0x6C0364, (DWORD)&FakeFindFirstFile); + SafeWrite32(0x6C0368, (DWORD)&FakeFindNextFile); dlogr(" Done", DL_INIT); - //} + }*/ if(GetPrivateProfileIntA("Misc", "AdditionalWeaponAnims", 0, ini)) { dlog("Applying additional weapon animations patch.", DL_INIT); @@ -1278,8 +1280,8 @@ static void DllMain2() { if (GetPrivateProfileIntA("Misc", "MultiPatches", 0, ini)) { dlog("Applying load multiple patches patch.", DL_INIT); - SafeWrite8(0x444338, 0x90); // Change step from 2 to 1 - SafeWrite32(0x444363, 0xEB909090); // Disable check + SafeWrite8(0x444354, 0x90); // Change step from 2 to 1 + SafeWrite8(0x44435C, 0xC4); // Disable check dlogr(" Done", DL_INIT); } @@ -1619,13 +1621,13 @@ static void DllMain2() { dlogr(" Done", DL_INIT); } - dlog("Running BugsInit.", DL_INIT); - BugsInit(); - dlogr(" Done", DL_INIT); dlogr("Leave DllMain2", DL_MAIN); } static void _stdcall OnExit() { + if (scriptDialog != nullptr) { + delete[] scriptDialog; + } ClearReadExtraGameMsgFiles(); ConsoleExit(); AnimationsAtOnceExit(); diff --git a/sfall/movies.cpp b/sfall/movies.cpp index 339aa082..25c5f32b 100644 --- a/sfall/movies.cpp +++ b/sfall/movies.cpp @@ -18,14 +18,15 @@ #include "main.h" +#include // should be above DX SDK includes to avoid warning 4995 #include #include #include + #include "FalloutEngine.h" #include "Graphics.h" #include "Logging.h" #include "movies.h" -#include "vector9x.cpp" static DWORD MoviePtrs[MaxMovies]; char MoviePaths[MaxMovies*65]; @@ -331,8 +332,8 @@ fail: } } -static vector playingSounds; -static vector loopingSounds; +static std::vector playingSounds; +static std::vector loopingSounds; DWORD playID=0; DWORD loopID=0; static HWND soundwindow=0; @@ -384,7 +385,7 @@ LRESULT CALLBACK SoundWndProc(HWND wnd, UINT msg, WPARAM w, LPARAM l) { for(DWORD i=0;i class queue { -private: - int csize; - int asize; - int start; - T* data; -public: - queue(); - void push(T obj); - void pop(); - void clear(); - T front() const; - bool empty() const; -}; - -template -queue::queue() { - csize=0; - asize=1; - start=0; - data=new T[1]; -} - -template -void queue::push(T obj) { - if(csize==asize) { - asize*=2; - T* newData=new T[asize]; - int pos=start-1; - for(int i=0;i -void queue::pop() { - if(csize==0) return; - start++; - csize--; - if(start==asize) start=0; -} - -template -T queue::front() const { return data[start]; } - -template -void queue::clear() { csize=0; start=0; } - -template -bool queue::empty() const { return csize==0; } diff --git a/sfall/skills.cpp b/sfall/skills.cpp index 52cfd777..5eb76a10 100644 --- a/sfall/skills.cpp +++ b/sfall/skills.cpp @@ -16,14 +16,15 @@ * along with this program. If not, see . */ -#include "main.h" - #include #include +#include + +#include "main.h" + #include "Define.h" #include "FalloutEngine.h" #include "Knockback.h" -#include "vector9x.cpp" struct SkillInfo { DWORD a, b, c; //Not interested in these at the moment @@ -42,7 +43,7 @@ struct ChanceModifier { int mod; }; -static vector SkillMaxMods; +static std::vector SkillMaxMods; static ChanceModifier BaseSkillMax; static BYTE skillCosts[512*SKILL_count]; static DWORD basedOnPoints; diff --git a/sfall/vector9x.cpp b/sfall/vector9x.cpp deleted file mode 100644 index 2cdcd357..00000000 --- a/sfall/vector9x.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//#include "main.h" - -#pragma once - -template class vector { -private: - int csize; - int asize; - T* data; -public: - vector(); - void push_back(T obj); - void clear(); - unsigned int size() const; - T& operator[](int i) const; - void remove_at(int i); -}; - -template -vector::vector() { - csize=0; - asize=8; - data=new T[8]; -} - -template -void vector::push_back(T obj) { - if(csize==asize) { - asize*=2; - T* newData=new T[asize]; - for(int i=0;i -unsigned int vector::size() const { return csize; } - -template -void vector::clear() { csize=0; } - -template -T& vector::operator[](int i) const { - return data[i]; -} - -template -void vector::remove_at(int i) { - if(i>=csize) return; - for(int j=i;j