From 0bb07dbd50258a754a0308248036a88563939f9d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Apr 2023 09:02:54 +0300 Subject: [PATCH 01/10] Add pc base stats opcodes --- src/sfall_opcodes.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index d746f93..fe642a7 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -39,6 +39,17 @@ static void opReadByte(Program* program) programStackPushInteger(program, value); } +// set_pc_base_stat +static void op_set_pc_base_stat(Program* program) +{ + // CE: Implementation is different. Sfall changes value directly on the + // dude's proto, without calling |critterSetBaseStat|. This function has + // important call to update derived stats, which is not present in Sfall. + int value = programStackPopInteger(program); + int stat = programStackPopInteger(program); + critterSetBaseStat(gDude, stat, value); +} + // set_pc_extra_stat static void opSetPcBonusStat(Program* program) { @@ -50,6 +61,16 @@ static void opSetPcBonusStat(Program* program) critterSetBonusStat(gDude, stat, value); } +// get_pc_base_stat +static void op_get_pc_base_stat(Program* program) +{ + // CE: Implementation is different. Sfall obtains value directly from + // dude's proto. This can have unforeseen consequences when dealing with + // current stats. + int stat = programStackPopInteger(program); + programStackPushInteger(program, critterGetBaseStat(gDude, stat)); +} + // get_pc_extra_stat static void opGetPcBonusStat(Program* program) { @@ -308,7 +329,9 @@ static void opArtExists(Program* program) void sfallOpcodesInit() { interpreterRegisterOpcode(0x8156, opReadByte); + interpreterRegisterOpcode(0x815A, op_set_pc_base_stat); interpreterRegisterOpcode(0x815B, opSetPcBonusStat); + interpreterRegisterOpcode(0x815C, op_get_pc_base_stat); interpreterRegisterOpcode(0x815D, opGetPcBonusStat); interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819D, opSetGlobalVar); From 9b02f600dead3cea8925eeb104ce3404718d6447 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Apr 2023 09:09:17 +0300 Subject: [PATCH 02/10] Add worldmap opcodes --- src/sfall_opcodes.cc | 25 +++++++++++++++++++++++++ src/worldmap.cc | 11 +++++++++++ src/worldmap.h | 3 +++ 3 files changed, 39 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index fe642a7..8d5c57d 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -14,6 +14,7 @@ #include "sfall_lists.h" #include "stat.h" #include "svga.h" +#include "worldmap.h" namespace fallout { @@ -79,6 +80,20 @@ static void opGetPcBonusStat(Program* program) programStackPushInteger(program, value); } +// in_world_map +static void op_in_world_map(Program* program) +{ + programStackPushInteger(program, GameMode::isInGameMode(GameMode::kWorldmap) ? 1 : 0); +} + +// set_world_map_pos +static void op_set_world_map_pos(Program* program) +{ + int y = programStackPopInteger(program); + int x = programStackPopInteger(program); + wmSetPartyWorldPos(x, y); +} + // active_hand static void opGetCurrentHand(Program* program) { @@ -121,6 +136,13 @@ static void opGetGameMode(Program* program) programStackPushInteger(program, GameMode::getCurrentGameMode()); } +// set_car_current_town +static void op_set_car_current_town(Program* program) +{ + int area = programStackPopInteger(program); + wmCarSetCurrentArea(area); +} + // list_begin static void opListBegin(Program* program) { @@ -333,10 +355,13 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x815B, opSetPcBonusStat); interpreterRegisterOpcode(0x815C, op_get_pc_base_stat); interpreterRegisterOpcode(0x815D, opGetPcBonusStat); + interpreterRegisterOpcode(0x8170, op_in_world_map); + interpreterRegisterOpcode(0x8172, op_set_world_map_pos); interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819D, opSetGlobalVar); interpreterRegisterOpcode(0x819E, opGetGlobalInt); interpreterRegisterOpcode(0x81AF, opGetGameMode); + interpreterRegisterOpcode(0x81B6, op_set_car_current_town); interpreterRegisterOpcode(0x820D, opListBegin); interpreterRegisterOpcode(0x820E, opListNext); interpreterRegisterOpcode(0x820F, opListEnd); diff --git a/src/worldmap.cc b/src/worldmap.cc index a02914f..9b6ff3d 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -6592,4 +6592,15 @@ void wmBlinkRndEncounterIcon(bool special) wmGenData.encounterIconIsVisible = false; } +void wmSetPartyWorldPos(int x, int y) +{ + wmGenData.worldPosX = x; + wmGenData.worldPosY = y; +} + +void wmCarSetCurrentArea(int area) +{ + wmGenData.currentCarAreaId = area; +} + } // namespace fallout diff --git a/src/worldmap.h b/src/worldmap.h index 1180d70..901b0a6 100644 --- a/src/worldmap.h +++ b/src/worldmap.h @@ -277,6 +277,9 @@ int wmSetMapMusic(int mapIdx, const char* name); int wmMatchAreaContainingMapIdx(int mapIdx, int* areaIdxPtr); int wmTeleportToArea(int areaIdx); +void wmSetPartyWorldPos(int x, int y); +void wmCarSetCurrentArea(int area); + } // namespace fallout #endif /* WORLD_MAP_H */ From ef34fdb519a1f3deaadf5b6c0c93d8fbed317f7a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Apr 2023 09:21:38 +0300 Subject: [PATCH 03/10] Add proto data opcodes --- src/proto.cc | 12 ++++++---- src/proto.h | 1 + src/sfall_opcodes.cc | 53 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/proto.cc b/src/proto.cc index 86d767b..efb05b0 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -249,6 +249,12 @@ int _proto_list_str(int pid, char* proto_path) return 0; } +// 0x49E984 +size_t proto_size(int type) +{ + return type >= 0 && type < OBJ_TYPE_COUNT ? _proto_sizes[type] : 0; +} + // 0x49E99C bool _proto_action_can_use(int pid) { @@ -1704,12 +1710,10 @@ static int _proto_load_pid(int pid, Proto** protoPtr) return 0; } -// allocate memory for proto of given type and adds it to proto cache +// 0x4A1D98 static int _proto_find_free_subnode(int type, Proto** protoPtr) { - size_t size = (type >= 0 && type < 11) ? _proto_sizes[type] : 0; - - Proto* proto = (Proto*)internal_malloc(size); + Proto* proto = (Proto*)internal_malloc(proto_size(type)); *protoPtr = proto; if (proto == NULL) { return -1; diff --git a/src/proto.h b/src/proto.h index c7f481d..d6f13e6 100644 --- a/src/proto.h +++ b/src/proto.h @@ -104,6 +104,7 @@ extern char* _proto_none_str; void _proto_make_path(char* path, int pid); int _proto_list_str(int pid, char* proto_path); +size_t proto_size(int type); bool _proto_action_can_use(int pid); bool _proto_action_can_use_on(int pid); bool _proto_action_can_talk_to(int pid); diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 8d5c57d..82807c7 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -10,6 +10,7 @@ #include "message.h" #include "mouse.h" #include "object.h" +#include "proto.h" #include "sfall_global_vars.h" #include "sfall_lists.h" #include "stat.h" @@ -143,6 +144,56 @@ static void op_set_car_current_town(Program* program) wmCarSetCurrentArea(area); } +// get_proto_data +static void op_get_proto_data(Program* program) +{ + size_t offset = static_cast(programStackPopInteger(program)); + int pid = programStackPopInteger(program); + + Proto* proto; + if (protoGetProto(pid, &proto) != 0) { + debugPrint("op_get_proto_data: bad proto %d", pid); + programStackPushInteger(program, -1); + return; + } + + // CE: Make sure the requested offset is within memory bounds and is + // properly aligned. + if (offset + sizeof(int) > proto_size(PID_TYPE(pid)) || offset % sizeof(int) != 0) { + debugPrint("op_get_proto_data: bad offset %d", offset); + programStackPushInteger(program, -1); + return; + } + + int value = *reinterpret_cast(reinterpret_cast(proto) + offset); + programStackPushInteger(program, value); +} + +// set_proto_data +static void op_set_proto_data(Program* program) +{ + int value = programStackPopInteger(program); + size_t offset = static_cast(programStackPopInteger(program)); + int pid = programStackPopInteger(program); + + Proto* proto; + if (protoGetProto(pid, &proto) != 0) { + debugPrint("op_set_proto_data: bad proto %d", pid); + programStackPushInteger(program, -1); + return; + } + + // CE: Make sure the requested offset is within memory bounds and is + // properly aligned. + if (offset + sizeof(int) > proto_size(PID_TYPE(pid)) || offset % sizeof(int) != 0) { + debugPrint("op_set_proto_data: bad offset %d", offset); + programStackPushInteger(program, -1); + return; + } + + *reinterpret_cast(reinterpret_cast(proto) + offset) = value; +} + // list_begin static void opListBegin(Program* program) { @@ -362,6 +413,8 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x819E, opGetGlobalInt); interpreterRegisterOpcode(0x81AF, opGetGameMode); interpreterRegisterOpcode(0x81B6, op_set_car_current_town); + interpreterRegisterOpcode(0x8204, op_get_proto_data); + interpreterRegisterOpcode(0x8205, op_set_proto_data); interpreterRegisterOpcode(0x820D, opListBegin); interpreterRegisterOpcode(0x820E, opListNext); interpreterRegisterOpcode(0x820F, opListEnd); From 540cc1e08b4c1490e5ffc8b5bdcfdc3835de7be3 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Apr 2023 10:03:04 +0300 Subject: [PATCH 04/10] Add combat opcodes --- src/combat.cc | 35 +++++++++++++++++++++++++++++++---- src/combat.h | 3 +++ src/sfall_opcodes.cc | 17 +++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/combat.cc b/src/combat.cc index f7aafaf..cde7b7d 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -168,7 +168,7 @@ static bool _combat_call_display = false; // Accuracy modifiers for hit locations. // // 0x510954 -static const int _hit_location_penalty[HIT_LOCATION_COUNT] = { +static int hit_location_penalty_default[HIT_LOCATION_COUNT] = { -40, -30, -30, @@ -180,6 +180,8 @@ static const int _hit_location_penalty[HIT_LOCATION_COUNT] = { 0, }; +static int hit_location_penalty[HIT_LOCATION_COUNT]; + // Critical hit tables for every kill type. // // 0x510978 @@ -2029,6 +2031,7 @@ int combatInit() burstModInit(); unarmedInit(); damageModInit(); + combat_reset_hit_location_penalty(); return 0; } @@ -2058,6 +2061,7 @@ void combatReset() // SFALL criticalsReset(); + combat_reset_hit_location_penalty(); } // 0x420E14 @@ -3831,7 +3835,7 @@ static int attackCompute(Attack* attack) roll = _compute_spray(attack, accuracy, &ammoQuantity, &v26, anim); } else { int chance = critterGetStat(attack->attacker, STAT_CRITICAL_CHANCE); - roll = randomRoll(accuracy, chance - _hit_location_penalty[attack->defenderHitLocation], NULL); + roll = randomRoll(accuracy, chance - hit_location_penalty[attack->defenderHitLocation], NULL); } if (roll == ROLL_FAILURE) { @@ -4417,9 +4421,9 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in } if (isRangedWeapon) { - accuracy += _hit_location_penalty[hitLocation]; + accuracy += hit_location_penalty[hitLocation]; } else { - accuracy += _hit_location_penalty[hitLocation] / 2; + accuracy += hit_location_penalty[hitLocation] / 2; } if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) { @@ -6798,4 +6802,27 @@ static void damageModCalculateYaam(DamageCalculationContext* context) } } +int combat_get_hit_location_penalty(int hit_location) +{ + if (hit_location >= 0 && hit_location < HIT_LOCATION_COUNT) { + return hit_location_penalty[hit_location]; + } else { + return 0; + } +} + +void combat_set_hit_location_penalty(int hit_location, int penalty) +{ + if (hit_location >= 0 && hit_location < HIT_LOCATION_COUNT) { + hit_location_penalty[hit_location] = penalty; + } +} + +void combat_reset_hit_location_penalty() +{ + for (int hit_location = 0; hit_location < HIT_LOCATION_COUNT; hit_location++) { + hit_location_penalty[hit_location] = hit_location_penalty_default[hit_location]; + } +} + } // namespace fallout diff --git a/src/combat.h b/src/combat.h index 5775eae..4dc0430 100644 --- a/src/combat.h +++ b/src/combat.h @@ -71,6 +71,9 @@ int unarmedGetKickHitMode(bool isSecondary); bool unarmedIsPenetrating(int hitMode); bool damageModGetBonusHthDamageFix(); bool damageModGetDisplayBonusDamage(); +int combat_get_hit_location_penalty(int hit_location); +void combat_set_hit_location_penalty(int hit_location, int penalty); +void combat_reset_hit_location_penalty(); static inline bool isInCombat() { diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index 82807c7..db28fb5 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -144,6 +144,21 @@ static void op_set_car_current_town(Program* program) wmCarSetCurrentArea(area); } +// get_bodypart_hit_modifier +static void op_get_bodypart_hit_modifier(Program* program) +{ + int hit_location = programStackPopInteger(program); + programStackPushInteger(program, combat_get_hit_location_penalty(hit_location)); +} + +// set_bodypart_hit_modifier +static void op_set_bodypart_hit_modifier(Program* program) +{ + int penalty = programStackPopInteger(program); + int hit_location = programStackPopInteger(program); + combat_set_hit_location_penalty(hit_location, penalty); +} + // get_proto_data static void op_get_proto_data(Program* program) { @@ -413,6 +428,8 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x819E, opGetGlobalInt); interpreterRegisterOpcode(0x81AF, opGetGameMode); interpreterRegisterOpcode(0x81B6, op_set_car_current_town); + interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier); + interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier); interpreterRegisterOpcode(0x8204, op_get_proto_data); interpreterRegisterOpcode(0x8205, op_set_proto_data); interpreterRegisterOpcode(0x820D, opListBegin); From a39f149817ee777709dad71432713da97ec35301 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 19 Apr 2023 19:10:09 +0300 Subject: [PATCH 05/10] Add date/time opcodes --- src/sfall_opcodes.cc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index db28fb5..bef609d 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -4,6 +4,7 @@ #include "combat.h" #include "debug.h" #include "game.h" +#include "input.h" #include "interface.h" #include "interpreter.h" #include "item.h" @@ -11,6 +12,7 @@ #include "mouse.h" #include "object.h" #include "proto.h" +#include "scripts.h" #include "sfall_global_vars.h" #include "sfall_lists.h" #include "stat.h" @@ -81,6 +83,14 @@ static void opGetPcBonusStat(Program* program) programStackPushInteger(program, value); } +// get_year +static void op_get_year(Program* program) +{ + int year; + gameTimeGetDate(nullptr, nullptr, &year); + programStackPushInteger(program, year); +} + // in_world_map static void op_in_world_map(Program* program) { @@ -137,6 +147,12 @@ static void opGetGameMode(Program* program) programStackPushInteger(program, GameMode::getCurrentGameMode()); } +// get_uptime +static void op_get_uptime(Program* program) +{ + programStackPushInteger(program, getTicks()); +} + // set_car_current_town static void op_set_car_current_town(Program* program) { @@ -421,12 +437,14 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x815B, opSetPcBonusStat); interpreterRegisterOpcode(0x815C, op_get_pc_base_stat); interpreterRegisterOpcode(0x815D, opGetPcBonusStat); + interpreterRegisterOpcode(0x8163, op_get_year); interpreterRegisterOpcode(0x8170, op_in_world_map); interpreterRegisterOpcode(0x8172, op_set_world_map_pos); interpreterRegisterOpcode(0x8193, opGetCurrentHand); interpreterRegisterOpcode(0x819D, opSetGlobalVar); interpreterRegisterOpcode(0x819E, opGetGlobalInt); interpreterRegisterOpcode(0x81AF, opGetGameMode); + interpreterRegisterOpcode(0x81B3, op_get_uptime); interpreterRegisterOpcode(0x81B6, op_set_car_current_town); interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier); interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier); From ecc6a8679b1dda4cd7a9b70ae3ead8b13e17c5b7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Apr 2023 09:53:50 +0300 Subject: [PATCH 06/10] Add math opcodes --- src/interpreter.cc | 25 ++++++++++++++++ src/interpreter.h | 3 ++ src/sfall_opcodes.cc | 69 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/src/interpreter.cc b/src/interpreter.cc index 8729fd5..23793ac 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -3268,4 +3268,29 @@ bool ProgramValue::isEmpty() return true; } +// Matches Sfall implementation. +bool ProgramValue::isInt() +{ + return opcode == VALUE_TYPE_INT; +} + +// Matches Sfall implementation. +bool ProgramValue::isFloat() +{ + return opcode == VALUE_TYPE_FLOAT; +} + +// Matches Sfall implementation. +float ProgramValue::asFloat() +{ + switch (opcode) { + case VALUE_TYPE_INT: + return static_cast(integerValue); + case VALUE_TYPE_FLOAT: + return floatValue; + default: + return 0.0; + } +} + } // namespace fallout diff --git a/src/interpreter.h b/src/interpreter.h index 91d27bb..f84347f 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -149,6 +149,9 @@ typedef struct ProgramValue { }; bool isEmpty(); + bool isInt(); + bool isFloat(); + float asFloat(); } ProgramValue; typedef std::vector ProgramStack; diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index bef609d..dd1580c 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -175,6 +175,25 @@ static void op_set_bodypart_hit_modifier(Program* program) combat_set_hit_location_penalty(hit_location, penalty); } +// sqrt +static void op_sqrt(Program* program) +{ + ProgramValue programValue = programStackPopValue(program); + programStackPushFloat(program, sqrtf(programValue.asFloat())); +} + +// abs +static void op_abs(Program* program) +{ + ProgramValue programValue = programStackPopValue(program); + + if (programValue.isInt()) { + programStackPushInteger(program, abs(programValue.integerValue)); + } else { + programStackPushFloat(program, abs(programValue.asFloat())); + } +} + // get_proto_data static void op_get_proto_data(Program* program) { @@ -395,6 +414,13 @@ static void opParseInt(Program* program) programStackPushInteger(program, static_cast(strtol(string, nullptr, 0))); } +// atof +static void op_atof(Program* program) +{ + const char* string = programStackPopString(program); + programStackPushFloat(program, static_cast(atof(string))); +} + // strlen static void opGetStringLength(Program* program) { @@ -402,6 +428,22 @@ static void opGetStringLength(Program* program) programStackPushInteger(program, static_cast(strlen(string))); } +// pow (^) +static void op_power(Program* program) +{ + ProgramValue expValue = programStackPopValue(program); + ProgramValue baseValue = programStackPopValue(program); + + // CE: Implementation is slightly different, check. + float result = powf(baseValue.asFloat(), expValue.asFloat()); + + if (baseValue.isInt() && expValue.isInt()) { + programStackPushInteger(program, static_cast(result)); + } else { + programStackPushFloat(program, result); + } +} + // message_str_game static void opGetMessage(Program* program) { @@ -430,6 +472,28 @@ static void opArtExists(Program* program) programStackPushInteger(program, artExists(fid)); } +// div (/) +static void op_div(Program* program) +{ + ProgramValue divisorValue = programStackPopValue(program); + ProgramValue dividendValue = programStackPopValue(program); + + if (divisorValue.integerValue == 0) { + debugPrint("Division by zero"); + + // TODO: Looks like execution is not halted in Sfall's div, check. + programStackPushInteger(program, 0); + return; + } + + if (dividendValue.isFloat() || divisorValue.isFloat()) { + programStackPushFloat(program, dividendValue.asFloat() / divisorValue.asFloat()); + } else { + // Unsigned divison. + programStackPushInteger(program, static_cast(dividendValue.integerValue) / static_cast(divisorValue.integerValue)); + } +} + void sfallOpcodesInit() { interpreterRegisterOpcode(0x8156, opReadByte); @@ -448,6 +512,8 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x81B6, op_set_car_current_town); interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier); interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier); + interpreterRegisterOpcode(0x81EC, op_sqrt); + interpreterRegisterOpcode(0x81ED, op_abs); interpreterRegisterOpcode(0x8204, op_get_proto_data); interpreterRegisterOpcode(0x8205, op_set_proto_data); interpreterRegisterOpcode(0x820D, opListBegin); @@ -465,10 +531,13 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8237, opParseInt); + interpreterRegisterOpcode(0x8238, op_atof); interpreterRegisterOpcode(0x824F, opGetStringLength); + interpreterRegisterOpcode(0x8263, op_power); interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x8267, opRound); interpreterRegisterOpcode(0x8274, opArtExists); + interpreterRegisterOpcode(0x827F, op_div); } void sfallOpcodesExit() From cf4921de1ef4d59f2939c6272766e4f0eb9f6c54 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Apr 2023 10:25:13 +0300 Subject: [PATCH 07/10] Add op_tile_under_cursor --- src/sfall_opcodes.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index dd1580c..f918d79 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -17,6 +17,7 @@ #include "sfall_lists.h" #include "stat.h" #include "svga.h" +#include "tile.h" #include "worldmap.h" namespace fallout { @@ -421,6 +422,17 @@ static void op_atof(Program* program) programStackPushFloat(program, static_cast(atof(string))); } +// tile_under_cursor +static void op_tile_under_cursor(Program* program) +{ + int x; + int y; + mouseGetPosition(&x, &y); + + int tile = tileFromScreenXY(x, y, gElevation); + programStackPushInteger(program, tile); +} + // strlen static void opGetStringLength(Program* program) { @@ -532,6 +544,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x8238, op_atof); + interpreterRegisterOpcode(0x824B, op_tile_under_cursor); interpreterRegisterOpcode(0x824F, opGetStringLength); interpreterRegisterOpcode(0x8263, op_power); interpreterRegisterOpcode(0x826B, opGetMessage); From 129361836f9043348f17e1a3fd3db0b60579db0b Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Apr 2023 10:31:32 +0300 Subject: [PATCH 08/10] Add op_get_mouse_buttons --- src/mouse.cc | 17 +++++++++++------ src/mouse.h | 1 + src/sfall_opcodes.cc | 9 +++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/mouse.cc b/src/mouse.cc index 09e4334..6e8a8bb 100644 --- a/src/mouse.cc +++ b/src/mouse.cc @@ -54,7 +54,7 @@ static unsigned char* _mouse_fptr = NULL; static double gMouseSensitivity = 1.0; // 0x51E2AC -static int gMouseButtonsState = 0; +static int last_buttons = 0; // 0x6AC790 static bool gCursorIsHidden; @@ -415,7 +415,7 @@ void _mouse_info() } x = 0; y = 0; - buttons = gMouseButtonsState; + buttons = last_buttons; } _mouse_simulate_input(x, y, buttons); @@ -447,7 +447,7 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons) return; } - if (delta_x || delta_y || buttons != gMouseButtonsState) { + if (delta_x || delta_y || buttons != last_buttons) { if (gVcrState == 0) { if (_vcr_buffer_index == VCR_BUFFER_CAPACITY - 1) { vcrDump(); @@ -464,13 +464,13 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons) _vcr_buffer_index++; } } else { - if (gMouseButtonsState == 0) { + if (last_buttons == 0) { if (!_mouse_idling) { _mouse_idle_start_time = getTicks(); _mouse_idling = 1; } - gMouseButtonsState = 0; + last_buttons = 0; _raw_buttons = 0; gMouseEvent = 0; @@ -479,7 +479,7 @@ void _mouse_simulate_input(int delta_x, int delta_y, int buttons) } _mouse_idling = 0; - gMouseButtonsState = buttons; + last_buttons = buttons; previousEvent = gMouseEvent; gMouseEvent = 0; @@ -703,4 +703,9 @@ void convertMouseWheelToArrowKey(int* keyCodePtr) } } +int mouse_get_last_buttons() +{ + return last_buttons; +} + } // namespace fallout diff --git a/src/mouse.h b/src/mouse.h index 6b091c5..667250f 100644 --- a/src/mouse.h +++ b/src/mouse.h @@ -52,6 +52,7 @@ void mouseGetPositionInWindow(int win, int* x, int* y); bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom); void mouseGetWheel(int* x, int* y); void convertMouseWheelToArrowKey(int* keyCodePtr); +int mouse_get_last_buttons(); } // namespace fallout diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index f918d79..d21acb6 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -396,6 +396,14 @@ static void opGetMouseY(Program* program) programStackPushInteger(program, y); } +// get_mouse_buttons +static void op_get_mouse_buttons(Program* program) +{ + // CE: Implementation is slightly different - it does not handle middle + // mouse button. + programStackPushInteger(program, mouse_get_last_buttons()); +} + // get_screen_width static void opGetScreenWidth(Program* program) { @@ -540,6 +548,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x821A, opSetWeaponAmmoCount); interpreterRegisterOpcode(0x821C, opGetMouseX); interpreterRegisterOpcode(0x821D, opGetMouseY); + interpreterRegisterOpcode(0x821E, op_get_mouse_buttons); interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8237, opParseInt); From 6ca1329720f46fec078cf5588c13020894cf51b7 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Apr 2023 10:51:20 +0300 Subject: [PATCH 09/10] Add op_get_attack_type --- src/interface.cc | 33 +++++++++++++++++++++++++++++++++ src/interface.h | 1 + src/sfall_opcodes.cc | 12 ++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/interface.cc b/src/interface.cc index 3204faa..1415892 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -2616,4 +2616,37 @@ static void sidePanelsDraw(const char* path, int win, bool isLeading) internal_free(image); } +// NOTE: Follows Sfall implementation of `GetCurrentAttackMode`. It slightly +// differs from `interfaceGetCurrentHitMode` (can return one of `reload` hit +// modes, the default is `punch`). +// +// 0x45EF6C +bool interface_get_current_attack_mode(int* hit_mode) +{ + if (gInterfaceBarWindow == -1) { + return false; + } + + switch (gInterfaceItemStates[gInterfaceCurrentHand].action) { + case INTERFACE_ITEM_ACTION_PRIMARY_AIMING: + case INTERFACE_ITEM_ACTION_PRIMARY: + *hit_mode = gInterfaceItemStates[gInterfaceCurrentHand].primaryHitMode; + break; + case INTERFACE_ITEM_ACTION_SECONDARY_AIMING: + case INTERFACE_ITEM_ACTION_SECONDARY: + *hit_mode = gInterfaceItemStates[gInterfaceCurrentHand].secondaryHitMode; + break; + case INTERFACE_ITEM_ACTION_RELOAD: + *hit_mode = gInterfaceCurrentHand == HAND_LEFT + ? HIT_MODE_LEFT_WEAPON_RELOAD + : HIT_MODE_RIGHT_WEAPON_RELOAD; + break; + default: + *hit_mode = HIT_MODE_PUNCH; + break; + } + + return true; +} + } // namespace fallout diff --git a/src/interface.h b/src/interface.h index 07772df..acec2df 100644 --- a/src/interface.h +++ b/src/interface.h @@ -69,6 +69,7 @@ void interfaceBarEndButtonsRenderRedLights(); int indicatorBarRefresh(); bool indicatorBarShow(); bool indicatorBarHide(); +bool interface_get_current_attack_mode(int* hit_mode); unsigned char* customInterfaceBarGetBackgroundImageData(); diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index d21acb6..d473f18 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -416,6 +416,17 @@ static void opGetScreenHeight(Program* program) programStackPushInteger(program, screenGetHeight()); } +// get_attack_type +static void op_get_attack_type(Program* program) +{ + int hit_mode; + if (interface_get_current_attack_mode(&hit_mode)) { + programStackPushInteger(program, hit_mode); + } else { + programStackPushInteger(program, -1); + } +} + // atoi static void opParseInt(Program* program) { @@ -551,6 +562,7 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x821E, op_get_mouse_buttons); interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8221, opGetScreenHeight); + interpreterRegisterOpcode(0x8228, op_get_attack_type); interpreterRegisterOpcode(0x8237, opParseInt); interpreterRegisterOpcode(0x8238, op_atof); interpreterRegisterOpcode(0x824B, op_tile_under_cursor); From df3ac30de6f5f59ba31168d93a56eaecf2b1667e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 20 Apr 2023 11:22:47 +0300 Subject: [PATCH 10/10] Add blocking objects opcodes --- src/sfall_opcodes.cc | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index d473f18..103d29f 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -1,5 +1,6 @@ #include "sfall_opcodes.h" +#include "animation.h" #include "art.h" #include "combat.h" #include "debug.h" @@ -496,6 +497,61 @@ static void opRound(Program* program) programStackPushInteger(program, integerValue); } +enum BlockType { + BLOCKING_TYPE_BLOCK, + BLOCKING_TYPE_SHOOT, + BLOCKING_TYPE_AI, + BLOCKING_TYPE_SIGHT, + BLOCKING_TYPE_SCROLL, +}; + +PathBuilderCallback* get_blocking_func(int type) +{ + switch (type) { + case BLOCKING_TYPE_SHOOT: + return _obj_shoot_blocking_at; + case BLOCKING_TYPE_AI: + return _obj_ai_blocking_at; + case BLOCKING_TYPE_SIGHT: + return _obj_sight_blocking_at; + default: + return _obj_blocking_at; + } +} + +// obj_blocking_line +static void op_make_straight_path(Program* program) +{ + int type = programStackPopInteger(program); + int dest = programStackPopInteger(program); + Object* object = static_cast(programStackPopPointer(program)); + + int flags = type == BLOCKING_TYPE_SHOOT ? 32 : 0; + + Object* obstacle = nullptr; + _make_straight_path_func(object, object->tile, dest, nullptr, &obstacle, flags, get_blocking_func(type)); + programStackPushPointer(program, obstacle); +} + +// obj_blocking_tile +static void op_obj_blocking_at(Program* program) +{ + int type = programStackPopInteger(program); + int elevation = programStackPopInteger(program); + int tile = programStackPopInteger(program); + + PathBuilderCallback* func = get_blocking_func(type); + Object* obstacle = func(NULL, tile, elevation); + if (obstacle != NULL) { + if (type == BLOCKING_TYPE_SHOOT) { + if ((obstacle->flags & OBJECT_SHOOT_THRU) != 0) { + obstacle = nullptr; + } + } + } + programStackPushPointer(program, obstacle); +} + // art_exists static void opArtExists(Program* program) { @@ -570,6 +626,8 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8263, op_power); interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x8267, opRound); + interpreterRegisterOpcode(0x826E, op_make_straight_path); + interpreterRegisterOpcode(0x826F, op_obj_blocking_at); interpreterRegisterOpcode(0x8274, opArtExists); interpreterRegisterOpcode(0x827F, op_div); }