Compare commits

...
Author SHA1 Message Date
Mike Klaas cd38c82514 Remove 100 hard cap on outlined objects (#612)
Fixes odd highlighting behaviour in https://github.com/fallout2-ce/fallout2-ce/pull/603
2026-07-27 08:37:07 -07:00
alpacacommunist-cmd 552505e3f5 fixes party_member.cc memory leaks (#601)
updates functions:

--- File: src/party_member.cc ---
661 | static int _partyMemberRecoverLoadInstance(PartyMemberListItem* a1)
--- File: src/party_member.cc ---
710 | int partyMembersLoad(File* stream)
2026-07-27 13:09:37 +03:00
alpacacommunist-cmd b9dd399457 adds OBJECT_HIDDEN flag check for exit grids in objectSetLocation (#588) 2026-07-27 12:45:31 +03:00
5f544ca985 Change body replacement with the blood stains so the dropped items are on top (#602)
* change the body replacement sequence with the blood stains so that items are dropped after the blood is created

* chore: auto-format with clang-format

* typo

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-27 08:39:26 +02:00
jirik2077andgithub-actions[bot] 0c7752fd33 Type safe usages of DamageType, KillType, BodyType, Editor, Skill, CriticalEffect, RaceType enums. (#599)
* type safe usages or DamageType, KillType, BodyType, Editor, Skill, CriticalEffect, RaceType enums...
replaced numeric constants to Editor enum in character_editor.cc

* chore: auto-format with clang-format

* added defaults to the switches to satisfy -Wswitch

* chore: auto-format with clang-format

* copilot review fixes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 06:20:57 +02:00
Mike Klaas 0a5364972c [Et tu] DisableSpecialMapIDs=1 (#611) 2026-07-26 21:03:46 -07:00
Mike Klaas 1db054e47a [Et tu] Implement Start{X|Y}Pos (#585)
* [Et tu] Implement `Start{X|Y}Pos`, `ViewX|Y`
2026-07-26 14:07:55 -07:00
8e60a2fba2 Show weight instead of size in container loot dialog for better comparison (#551)
* show weight instead of size in loot dialog

* chore: auto-format with clang-format

* added configuration

* chore: auto-format with clang-format

* added percentage mode only

* chore: auto-format with clang-format

* use percentage for simple indiator in containers, remove the percentage mode only, change default to simple indicator

* Update src/settings.h

typo

Co-authored-by: Mike Klaas <mike.klaas@gmail.com>

* changed the threshold to be applicable only to the containers and se it to 50%.. changed the colors for the weight indicator and the party member name in the loot dialog.. also tweaked label position for bigger critters like Marcus

* restore font to the previous value in case threshold filters out the indicator

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* missing leading space in  weight indicator comments in fallout2.cfg

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* use actual runtime value of the size of the columns instead of trusting the setting value

Co-authored-by: Mike Klaas <mike.klaas@gmail.com>

* minor reformulation of the comments in fallout2.cfg

Co-authored-by: Mike Klaas <mike.klaas@gmail.com>

* explicitly use std::ceil() over ceil() to follow convention in this file and to make copilot happy

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mike Klaas <mike.klaas@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-26 09:52:04 +02:00
Mike Klaas ca89abad6c [Et tu] Fix TMA (Tell me about) (#596)
* Honor frm global offsets in draw_image

* Return correct dialog window in getInterfaceWindowByType
2026-07-25 20:44:25 -07:00
Mike Klaas 21a98bebb1 Correct PC placement when screen is large (#591)
Fixes https://github.com/fallout2-ce/fallout2-ce/issues/578
2026-07-25 15:37:09 -07:00
Mike Klaas 283107bebe [Et tu] Move pipboy GAMEMODECHANGE hook (#595)
This now fires when the window is created, allowing scripts to manipulate it (and in particular, add "X days left" note)
2026-07-25 08:57:50 -07:00
Mike Klaas dd21129464 [Et tu] get/set_terrain_name (#593)
* [Et tu] get/set_terrain_name
2026-07-24 21:42:30 -07:00
Mike Klaas 7604061e1c Fix script use-after-free when scripts remove themselves (#573)
* Fix script use-after-free when scripts remove themselves
2026-07-24 08:02:15 -07:00
Mike Klaas f832d78949 [Et tu] Filter out GL* scripts as global if they are in scripts.lst (#586)
* [Et tu] Filter out GL* scripts as global if they are in scripts.lst

Matches Sfall, prevents Et tu problems since a lot of Fo1 scripts start with GL*
2026-07-23 22:26:32 -07:00
Mike Klaas 57b784b8a0 Trigger GameMode::kLoot later (#590)
Mods like https://github.com/FalloutTeamX/sfall-Extended/blob/Extended/artifacts/mods/gl_auto_closebox.ssl access loot_obj in the GAMEMODECHANGE handler.  We need to move it later to prevent crashes (and match Sfall)

Fixes https://github.com/fallout2-ce/fallout2-ce/issues/589
2026-07-23 22:07:31 -07:00
Mike Klaas d5fd197dd9 Fix OOM memory access with alternate damage calculation (#584)
Was causing absurd values and/or lock ups due to using uninitized memory.

Also fix correctness bugs with it.
2026-07-23 21:48:30 -07:00
jirik2077andgithub-actions[bot] 6375604af8 Typesafe combat_defs.h enum usages (#580)
* change HitMode, HitLocation, CriticalHitDescriptionDataMember and CombatBatShot to its respective underlying integer types and enforce enum usage in the functions

* chore: auto-format with clang-format

* renamed CriticalHitDescriptionDataMember to CriticalHitDataMember. implemented isValid functions for the refactored enums in combat_defs.h. created template function for getting validated enum value in interpreter.h.. used isValid enum functions around the place

* chore: auto-format with clang-format

* changed gCombatState from uint handling to bit flags enum

* chore: auto-format with clang-format

* remove unused valid/interpreter method for CombatBadShot

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-23 20:16:18 +02:00
Mike Klaas c02c5efcf6 Don't crash on long config file lengths (#581) 2026-07-22 21:36:56 -07:00
61 changed files with 1665 additions and 983 deletions
+7 -1
View File
@@ -42,7 +42,13 @@ The following settings were moved into [`<DAT>/config/game.cfg`](files/ce.dat/co
| ddraw.ini section | ddraw.ini key | game.cfg section | game.cfg key | | ddraw.ini section | ddraw.ini key | game.cfg section | game.cfg key |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `Misc` | `StartingMap` | `start` | `map` |
| `Misc` | `StartXPos` | `start` | `worldmap_x` |
| `Misc` | `StartYPos` | `start` | `worldmap_y` |
| `Misc` | `ViewXPos` | `start` | `worldmap_view_x` |
| `Misc` | `ViewYPos` | `start` | `worldmap_view_y` |
| `Misc` | `StartGDialogFix` | `dialog` | `start_gdialog_fix` | | `Misc` | `StartGDialogFix` | `dialog` | `start_gdialog_fix` |
| `Misc` | `DisableSpecialMapIDs` | `maps` | `disable_special_map_ids` |
## Opcodes / Metarules ## Opcodes / Metarules
@@ -59,7 +65,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Perks and traits | set_perk_image<br>set_perk_*<br>set_pyromaniac_mod<br>apply_heaveho_fix<br>set_swiftlearner_mod<br>has/set_fake_perk<br>has/set_fake_trait<br>set_selectable_perk<br>set_perkbox_title<br>show/hide_real_perks<br>perk_add_mode<br>clear_selectable_perks<br>add/remove_trait<br>seq_perk_freq | not implemented | - | | Perks and traits | set_perk_image<br>set_perk_*<br>set_pyromaniac_mod<br>apply_heaveho_fix<br>set_swiftlearner_mod<br>has/set_fake_perk<br>has/set_fake_trait<br>set_selectable_perk<br>set_perkbox_title<br>show/hide_real_perks<br>perk_add_mode<br>clear_selectable_perks<br>add/remove_trait<br>seq_perk_freq | not implemented | - |
| Virtual file system | fs_create<br>fs_copy<br>fs_find<br>fs_read/write_*<br>fs_delete<br>fs_size<br>fs_pos<br>fs_seek<br>fs_resize | đźš« | Open an issue if you have a use case for these | | Virtual file system | fs_create<br>fs_copy<br>fs_find<br>fs_read/write_*<br>fs_delete<br>fs_size<br>fs_pos<br>fs_seek<br>fs_resize | đźš« | Open an issue if you have a use case for these |
| Combat / Knockback | set_weapon_knockback<br>set_target_knockback<br>set_attacker_knockback<br>remove_weapon_knockback<br>remove_target_knockback<br>remove_attacker_knockback | not implemented | - | | Combat / Knockback | set_weapon_knockback<br>set_target_knockback<br>set_attacker_knockback<br>remove_weapon_knockback<br>remove_target_knockback<br>remove_attacker_knockback | not implemented | - |
| Maps and encounters | in_world_map<br>force_encounter{_with_flags}<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi | - | | Maps and encounters | in_world_map<br>force_encounter{_with_flags}<br>set_map_time_multi<br>get/set_map_enter_position<br>exec_map_update_scripts<br>get/set_terrain_name<br>set_town_title<br>get/set_can_rest_on_map<br>set_rest_heal_time<br>set_rest_mode<br>set_worldmap_heal_time | implemented: in_world_map, force_encounter, force_encounter_with_flags, set_map_time_multi, get_terrain_name, set_terrain_name | - |
| Maps and encounters / Worldmap | get_world_map_x/y_pos<br>set_world_map_pos | âś… | - | | Maps and encounters / Worldmap | get_world_map_x/y_pos<br>set_world_map_pos | âś… | - |
| Audio | play_sfall_sound<br>stop_sfall_sound | âś… | `play_sfall_sound` currently supports `.acm`, `.wav`, `.ogg` formats, and can load from `.dat` archives. `.mp3` is not yet supported. | | Audio | play_sfall_sound<br>stop_sfall_sound | âś… | `play_sfall_sound` currently supports `.acm`, `.wav`, `.ogg` formats, and can load from `.dat` archives. `.mp3` is not yet supported. |
| Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | âś… | - | | Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | âś… | - |
+11
View File
@@ -11,6 +11,12 @@ month=6
day=24 day=24
; To start a new game somewhere other than artemple.map, set this to the map file name. ; To start a new game somewhere other than artemple.map, set this to the map file name.
map= map=
; Starting worldmap marker position. -1 uses the starting map's world area.
worldmap_x=-1
worldmap_y=-1
; Starting worldmap viewport position. -1 uses the default upper-left view.
worldmap_view_x=-1
worldmap_view_y=-1
; Starting (tribal) and default (jumpsuit) player models. Can also be changed in-game via script. ; Starting (tribal) and default (jumpsuit) player models. Can also be changed in-game via script.
model_male=hmwarr model_male=hmwarr
model_male_default=hmjmps model_male_default=hmjmps
@@ -20,6 +26,11 @@ model_female_default=hfjmps
; Set to 2 to make the pipboy available by only skipping the vault suit movie check. ; Set to 2 to make the pipboy available by only skipping the vault suit movie check.
pipboy=0 pipboy=0
[maps]
; Set to 1 to apply transition tile, elevation, and rotation overrides on all maps,
; including Fallout 2 map IDs 19 and 37.
disable_special_map_ids=0
[karma] [karma]
; FRM numbers for karma icons on the character screen. Number of points entries should be 1 less than number of frms entries. ; FRM numbers for karma icons on the character screen. Number of points entries should be 1 less than number of frms entries.
; Example: frms=47,48,49 points=-100,100 ; Example: frms=47,48,49 points=-100,100
+10
View File
@@ -165,6 +165,16 @@ main_menu_scale_mode=1
; Set to 1 to show a Help option in the in-game options menu which opens the F1 help screen. ; Set to 1 to show a Help option in the in-game options menu which opens the F1 help screen.
in_game_menu_help=1 in_game_menu_help=1
; Weight/size indicator for PC, critters and containers
; Set to 0 for vanilla behavior (no weight indicator)
; Set to 1 to show a simple weight indicator; works with all inventory_columns values
; Set to 2 to show a detailed weight indicator; works with inventory_columns > 1 only
loot_weight_indicator=1
; Set to 0 to show container indicator at all times
; Set to XX to show container indicator when size reaches XX%
; Set to 100 to show container indicator when full
loot_container_size_indicator_threshold=50
[qol] [qol]
; Automatically open doors that are unlocked and has no script attached. ; Automatically open doors that are unlocked and has no script attached.
+27
View File
@@ -0,0 +1,27 @@
#include "test_utils.h"
#include "sfall.h"
procedure start begin
variable defaultName;
display_msg("Testing terrain name metarules...");
call assertEquals("get metarule exists", metarule_exist("get_terrain_name"), 1);
call assertEquals("set metarule exists", metarule_exist("set_terrain_name"), 1);
defaultName := get_terrain_name(0, 0);
call assertNotEquals("default terrain name is not error", defaultName, "Error");
call assertNotEquals("default terrain name is not empty", defaultName, "");
set_terrain_name(0, 0, "CE Terrain Override");
call assertEquals("terrain override is returned", get_terrain_name(0, 0), "CE Terrain Override");
set_terrain_name(0, 0, "CE Terrain Override 2");
call assertEquals("latest terrain override wins", get_terrain_name(0, 0), "CE Terrain Override 2");
call assertEquals("one argument get returns error", sfall_func1("get_terrain_name", 0), -1);
call assertEquals("invalid get returns error", get_terrain_name(-1, 0), "Error");
call assertNotEquals("current terrain name is available", get_current_terrain_name, "Error");
call report_test_results("terrain_name");
signal_close_game;
end
+7 -7
View File
@@ -97,7 +97,7 @@ static int _compute_explosion_damage(int min, int max, Object* defender, int* kn
static int _can_talk_to(Object* obj, Object* critter); static int _can_talk_to(Object* obj, Object* critter);
static int _talk_to(Object* _, Object* critter); static int _talk_to(Object* _, Object* critter);
static int _report_dmg(Attack* attack, Object* _); static int _report_dmg(Attack* attack, Object* _);
static int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, int damageType); static int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, DamageType damageType);
static int hideProjectile(void* _, void* projectile); static int hideProjectile(void* _, void* projectile);
@@ -200,7 +200,7 @@ int pickDeathAnim(Object* attacker, Object* defender, Object* weapon, int damage
int normalViolenceLevelDamageThreshold = 15; int normalViolenceLevelDamageThreshold = 15;
int maximumBloodViolenceLevelDamageThreshold = 45; int maximumBloodViolenceLevelDamageThreshold = 45;
int damageType = weaponGetDamageType(attacker, weapon); DamageType damageType = weaponGetDamageType(attacker, weapon);
if (weapon != nullptr && weapon->pid == PROTO_ID_MOLOTOV_COCKTAIL) { if (weapon != nullptr && weapon->pid == PROTO_ID_MOLOTOV_COCKTAIL) {
normalViolenceLevelDamageThreshold = 5; normalViolenceLevelDamageThreshold = 5;
@@ -729,7 +729,7 @@ int _action_ranged(Attack* attack, int anim)
weaponGetRange(attack->attacker, attack->hitMode); weaponGetRange(attack->attacker, attack->hitMode);
int damageType = weaponGetDamageType(attack->attacker, attack->weapon); DamageType damageType = weaponGetDamageType(attack->attacker, attack->weapon);
tileGetTileInDirection(attack->attacker->tile, attack->attacker->rotation, 1); tileGetTileInDirection(attack->attacker->tile, attack->attacker->rotation, 1);
@@ -1306,7 +1306,7 @@ int actionLootCritter(Object* critter, Object* target)
} }
// 0x4124E0 // 0x4124E0
int _action_skill_use(int skill) int _action_skill_use(Skill skill)
{ {
if (skill == SKILL_SNEAK) { if (skill == SKILL_SNEAK) {
reg_anim_clear(gDude); reg_anim_clear(gDude);
@@ -1336,7 +1336,7 @@ static int _action_use_skill_in_combat_error(Object* critter)
// skill_use // skill_use
// 0x41255C // 0x41255C
int actionUseSkill(Object* user, Object* target, int skill) int actionUseSkill(Object* user, Object* target, Skill skill)
{ {
UseSkillOnHookResult hookResult = scriptHooks_UseSkillOn(&user, target, skill); UseSkillOnHookResult hookResult = scriptHooks_UseSkillOn(&user, target, skill);
if (!hookResult.shouldContinue) { if (!hookResult.shouldContinue) {
@@ -1907,7 +1907,7 @@ int _talk_to(Object* _, Object* critter)
} }
// 0x413494 // 0x413494
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor) void actionDamage(int tile, int elevation, int minDamage, int maxDamage, DamageType damageType, bool animated, bool bypassArmor)
{ {
Attack* attack = (Attack*)internal_malloc(sizeof(*attack)); Attack* attack = (Attack*)internal_malloc(sizeof(*attack));
if (attack == nullptr) { if (attack == nullptr) {
@@ -1988,7 +1988,7 @@ int _report_dmg(Attack* attack, Object* _)
// Calculate damage by applying threshold and resistances. // Calculate damage by applying threshold and resistances.
// //
// 0x413660 // 0x413660
int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, int damageType) int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, DamageType damageType)
{ {
if (!critterFlagCheck(obj->pid, CRITTER_NO_KNOCKBACK)) { if (!critterFlagCheck(obj->pid, CRITTER_NO_KNOCKBACK)) {
knockbackDistancePtr = nullptr; knockbackDistancePtr = nullptr;
+5 -3
View File
@@ -3,6 +3,8 @@
#include "combat_defs.h" #include "combat_defs.h"
#include "obj_types.h" #include "obj_types.h"
#include "proto_types.h"
#include "skill_defs.h"
namespace fallout { namespace fallout {
@@ -13,14 +15,14 @@ int _action_use_an_item_on_object(Object* user, Object* targetObj, Object* item)
int _action_use_an_object(Object* user, Object* targetObj); int _action_use_an_object(Object* user, Object* targetObj);
int actionPickUp(Object* critter, Object* item); int actionPickUp(Object* critter, Object* item);
int actionLootCritter(Object* critter, Object* target); int actionLootCritter(Object* critter, Object* target);
int _action_skill_use(int skill); int _action_skill_use(Skill skill);
int actionUseSkill(Object* user, Object* target, int skill); int actionUseSkill(Object* user, Object* target, Skill skill);
bool _is_hit_from_front(const Object* attacker, const Object* defender); bool _is_hit_from_front(const Object* attacker, const Object* defender);
bool _can_see(Object* source, Object* target); bool _can_see(Object* source, Object* target);
bool _action_explode_running(); bool _action_explode_running();
int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object* sourceObj, bool animate); int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object* sourceObj, bool animate);
int actionTalk(Object* obj, Object* critter); int actionTalk(Object* obj, Object* critter);
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor); void actionDamage(int tile, int elevation, int minDamage, int maxDamage, DamageType damageType, bool animated, bool bypassArmor);
bool actionCheckPush(Object* obj, Object* target); bool actionCheckPush(Object* obj, Object* target);
int actionPush(Object* obj, Object* target); int actionPush(Object* obj, Object* target);
int _action_can_talk_to(Object* obj, Object* critter); int _action_can_talk_to(Object* obj, Object* critter);
+2 -2
View File
@@ -1736,7 +1736,7 @@ static bool canUseDoor(Object* critter, Object* door)
return false; return false;
} }
int bodyType = critterGetBodyType(critter); BodyType bodyType = critterGetBodyType(critter);
if (bodyType != BODY_TYPE_BIPED && bodyType != BODY_TYPE_ROBOTIC) { if (bodyType != BODY_TYPE_BIPED && bodyType != BODY_TYPE_ROBOTIC) {
return false; return false;
} }
@@ -3019,7 +3019,7 @@ int _check_move(int* actionPointsPtr)
if (isInCombat()) { if (isInCombat()) {
if (*actionPointsPtr != -1) { if (*actionPointsPtr != -1) {
if (gPressedPhysicalKeys[SDL_SCANCODE_RCTRL] || gPressedPhysicalKeys[SDL_SCANCODE_LCTRL]) { if (gPressedPhysicalKeys[SDL_SCANCODE_RCTRL] || gPressedPhysicalKeys[SDL_SCANCODE_LCTRL]) {
int hitMode; HitMode hitMode;
bool aiming; bool aiming;
interfaceGetCurrentHitMode(&hitMode, &aiming); interfaceGetCurrentHitMode(&hitMode, &aiming);
+3 -1
View File
@@ -1789,11 +1789,13 @@ void FrmImage::resetInternal()
bool FrmImage::setFrame(const Art* art, int frame, int direction) bool FrmImage::setFrame(const Art* art, int frame, int direction)
{ {
unsigned char* data = artGetFrameData(art, frame, direction, &_width, &_height, &_xOffset, &_yOffset); unsigned char* data = artGetFrameData(art, frame, direction, &_width, &_height, nullptr, nullptr);
if (data == nullptr) { if (data == nullptr) {
return false; return false;
} }
_xOffset = art->xOffsets[direction];
_yOffset = art->yOffsets[direction];
_data = data; _data = data;
return true; return true;
} }
+203 -172
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -827,7 +827,7 @@ static bool characterSelectorWindowRenderStats()
y += vh; // blank line y += vh; // blank line
// SKILLS // SKILLS
int skills[DEFAULT_TAGGED_SKILLS]; Skill skills[DEFAULT_TAGGED_SKILLS];
skillsGetTagged(skills, DEFAULT_TAGGED_SKILLS); skillsGetTagged(skills, DEFAULT_TAGGED_SKILLS);
for (int index = 0; index < DEFAULT_TAGGED_SKILLS; index++) { for (int index = 0; index < DEFAULT_TAGGED_SKILLS; index++) {
+186 -181
View File
File diff suppressed because it is too large Load Diff
+21 -21
View File
@@ -9,7 +9,7 @@
namespace fallout { namespace fallout {
extern int _combatNumTurns; extern int _combatNumTurns;
extern unsigned int gCombatState; extern CombatState gCombatState;
extern int _combat_free_move; extern int _combat_free_move;
@@ -19,7 +19,7 @@ void combatExit();
int _find_cid(int a1, int a2, Object** a3, int a4); int _find_cid(int a1, int a2, Object** a3, int a4);
int combatLoad(File* stream); int combatLoad(File* stream);
int combatSave(File* stream); int combatSave(File* stream);
bool _combat_safety_invalidate_weapon(Object* attacker, Object* weapon, int hitMode, Object* defender, int* safeDistancePtr); bool _combat_safety_invalidate_weapon(Object* attacker, Object* weapon, HitMode hitMode, Object* defender, int* safeDistancePtr);
bool _combatTestIncidentalHit(Object* attacker, Object* defender, Object* attackerFriend, Object* weapon); bool _combatTestIncidentalHit(Object* attacker, Object* defender, Object* attackerFriend, Object* weapon);
Object* _combat_whose_turn(); Object* _combat_whose_turn();
void _combat_data_init(Object* obj); void _combat_data_init(Object* obj);
@@ -34,19 +34,19 @@ void _combat_over_from_load();
void _combat_give_exps(int exp_points); void _combat_give_exps(int exp_points);
void _combat_turn_run(); void _combat_turn_run();
void _combat(CombatStartData* csd); void _combat(CombatStartData* csd);
void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode, int hitLocation); void attackInit(Attack* attack, Object* attacker, Object* defender, HitMode hitMode, HitLocation hitLocation);
int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation); int _combat_attack(Object* attacker, Object* defender, HitMode hitMode, HitLocation hitLocation);
int _combat_bullet_start(const Object* attacker, const Object* target); int _combat_bullet_start(const Object* attacker, const Object* target);
void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage); void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage);
int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode); int _determine_to_hit(Object* a1, Object* a2, HitLocation hitLocation, HitMode hitMode);
int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5); int _determine_to_hit_no_range(Object* attacker, Object* defender, HitLocation hitLocation, HitMode hitMode, unsigned char* a5);
int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode); int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, HitLocation hitLocation, HitMode hitMode);
void attackComputeDeathFlags(Attack* attack); void attackComputeDeathFlags(Attack* attack);
void _apply_damage(Attack* attack, bool animated); void _apply_damage(Attack* attack, bool animated);
void _combat_display(Attack* attack); void _combat_display(Attack* attack);
void _combat_anim_begin(); void _combat_anim_begin();
void _combat_anim_finished(); void _combat_anim_finished();
int _combat_check_bad_shot(Object* attacker, Object* defender, int hitMode, bool aiming); CombatBadShot _combat_check_bad_shot(Object* attacker, Object* defender, HitMode hitMode, bool aiming);
bool _combat_to_hit(Object* target, int* accuracy); bool _combat_to_hit(Object* target, int* accuracy);
void _combat_attack_this(Object* target); void _combat_attack_this(Object* target);
void _combat_outline_on(); void _combat_outline_on();
@@ -59,21 +59,21 @@ void _combat_delete_critter(Object* obj);
void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg); void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
int combatGetTargetHighlight(); int combatGetTargetHighlight();
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember); int criticalsGetValue(KillType killType, HitLocation hitLocation, CriticalEffect effect, CriticalHitDataMember dataMember);
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value); void criticalsSetValue(KillType killType, HitLocation hitLocation, CriticalEffect effect, CriticalHitDataMember dataMember, int value);
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember); void criticalsResetValue(KillType killType, HitLocation hitLocation, CriticalEffect effect, CriticalHitDataMember dataMember);
bool criticalsNoTimeLimits(); bool criticalsNoTimeLimits();
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr); int unarmedGetDamage(HitMode hitMode, int* minDamagePtr, int* maxDamagePtr);
int unarmedGetBonusCriticalChance(int hitMode); int unarmedGetBonusCriticalChance(HitMode hitMode);
int unarmedGetActionPointCost(int hitMode); int unarmedGetActionPointCost(HitMode hitMode);
bool unarmedIsPenetrating(int hitMode); bool unarmedIsPenetrating(HitMode hitMode);
int unarmedGetPunchHitMode(bool isSecondary); HitMode unarmedGetPunchHitMode(bool isSecondary);
int unarmedGetKickHitMode(bool isSecondary); HitMode unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(int hitMode); bool unarmedIsPenetrating(HitMode hitMode);
bool damageModGetBonusHthDamageFix(); bool damageModGetBonusHthDamageFix();
bool damageModGetDisplayBonusDamage(); bool damageModGetDisplayBonusDamage();
int combat_get_hit_location_penalty(int hit_location); int combat_get_hit_location_penalty(HitLocation hitLocation);
void combat_set_hit_location_penalty(int hit_location, int penalty); void combat_set_hit_location_penalty(HitLocation hitLocation, int penalty);
void combat_reset_hit_location_penalty(); void combat_reset_hit_location_penalty();
Attack* combat_get_data(); Attack* combat_get_data();
@@ -82,7 +82,7 @@ static inline bool isInCombat()
return (gCombatState & COMBAT_STATE_IN_COMBAT) != 0; return (gCombatState & COMBAT_STATE_IN_COMBAT) != 0;
} }
static inline bool isUnarmedHitMode(int hitMode) static inline bool isUnarmedHitMode(HitMode hitMode)
{ {
return hitMode == HIT_MODE_PUNCH return hitMode == HIT_MODE_PUNCH
|| hitMode == HIT_MODE_KICK || hitMode == HIT_MODE_KICK
+28 -31
View File
@@ -93,19 +93,19 @@ static Object* _ai_danger_source(Object* a1);
static bool aiHaveAmmo(Object* critter, Object* weapon, Object** ammoPtr); static bool aiHaveAmmo(Object* critter, Object* weapon, Object** ammoPtr);
static bool _caiHasWeapPrefType(AiPacket* ai, int attackType); static bool _caiHasWeapPrefType(AiPacket* ai, int attackType);
static Object* _ai_best_weapon(Object* a1, Object* a2, Object* a3, Object* a4); static Object* _ai_best_weapon(Object* a1, Object* a2, Object* a3, Object* a4);
static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode); static bool _ai_can_use_weapon(Object* critter, Object* weapon, HitMode hitMode);
static bool aiCanUseItem(Object* obj, Object* a2); static bool aiCanUseItem(Object* obj, Object* a2);
static Object* _ai_search_environ(Object* critter, int itemType); static Object* _ai_search_environ(Object* critter, int itemType);
static Object* _ai_retrieve_object(Object* critter, Object* item); static Object* _ai_retrieve_object(Object* critter, Object* item);
static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender); static HitMode _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender);
static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool taunt); static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool taunt);
static int _ai_move_closer(Object* a1, Object* a2, bool taunt); static int _ai_move_closer(Object* a1, Object* a2, bool taunt);
static int _cai_retargetTileFromFriendlyFire(Object* source, Object* target, int* tilePtr); static int _cai_retargetTileFromFriendlyFire(Object* source, Object* target, int* tilePtr);
static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetData, int tile); static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetData, int tile);
static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance); static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance);
static int _ai_switch_weapons(Object* a1, int* hitMode, Object** weapon, Object* a4); static int _ai_switch_weapons(Object* a1, HitMode* hitMode, Object** weapon, Object* a4);
static int _ai_called_shot(Object* attacker, Object* defender, int hitMode); static HitLocation _ai_called_shot(Object* attacker, Object* defender, HitMode hitMode);
static int _ai_attack(Object* attacker, Object* defender, int hitMode); static int _ai_attack(Object* attacker, Object* defender, HitMode hitMode);
static int _ai_try_attack(Object* a1, Object* a2); static int _ai_try_attack(Object* a1, Object* a2);
static int _cai_get_min_hp(AiPacket* ai); static int _cai_get_min_hp(AiPacket* ai);
static int _ai_print_msg(Object* critter, int type); static int _ai_print_msg(Object* critter, int type);
@@ -610,7 +610,7 @@ static int aiPacketRead(File* stream, AiPacket* ai)
if (fileReadInt32(stream, &(ai->miss.start)) == -1) return -1; if (fileReadInt32(stream, &(ai->miss.start)) == -1) return -1;
if (fileReadInt32(stream, &(ai->miss.end)) == -1) return -1; if (fileReadInt32(stream, &(ai->miss.end)) == -1) return -1;
for (int index = 0; index < HIT_LOCATION_SPECIFIC_COUNT; index++) { for (int index = HIT_LOCATION_FIRST; index < HIT_LOCATION_SPECIFIC_COUNT; index++) {
AiMessageRange* range = &(ai->hit[index]); AiMessageRange* range = &(ai->hit[index]);
if (fileReadInt32(stream, &(range->start)) == -1) return -1; if (fileReadInt32(stream, &(range->start)) == -1) return -1;
if (fileReadInt32(stream, &(range->end)) == -1) return -1; if (fileReadInt32(stream, &(range->end)) == -1) return -1;
@@ -654,7 +654,7 @@ static int aiPacketWrite(File* stream, AiPacket* ai)
if (fileWriteInt32(stream, ai->miss.start) == -1) return -1; if (fileWriteInt32(stream, ai->miss.start) == -1) return -1;
if (fileWriteInt32(stream, ai->miss.end) == -1) return -1; if (fileWriteInt32(stream, ai->miss.end) == -1) return -1;
for (int index = 0; index < HIT_LOCATION_SPECIFIC_COUNT; index++) { for (int index = HIT_LOCATION_FIRST; index < HIT_LOCATION_SPECIFIC_COUNT; index++) {
AiMessageRange* range = &(ai->hit[index]); AiMessageRange* range = &(ai->hit[index]);
if (fileWriteInt32(stream, range->start) == -1) return -1; if (fileWriteInt32(stream, range->start) == -1) return -1;
if (fileWriteInt32(stream, range->end) == -1) return -1; if (fileWriteInt32(stream, range->end) == -1) return -1;
@@ -1619,7 +1619,7 @@ static Object* _ai_danger_source(Object* a1)
// Check if we can attack it. No ammo and out of // Check if we can attack it. No ammo and out of
// range are ok results, since we can reload weapon // range are ok results, since we can reload weapon
// move closer if necessary. // move closer if necessary.
int badShot = _combat_check_bad_shot(a1, critter, HIT_MODE_RIGHT_WEAPON_PRIMARY, false); CombatBadShot badShot = _combat_check_bad_shot(a1, critter, HIT_MODE_RIGHT_WEAPON_PRIMARY, false);
if (badShot != COMBAT_BAD_SHOT_OK if (badShot != COMBAT_BAD_SHOT_OK
&& badShot != COMBAT_BAD_SHOT_NO_AMMO && badShot != COMBAT_BAD_SHOT_NO_AMMO
&& badShot != COMBAT_BAD_SHOT_OUT_OF_RANGE) { && badShot != COMBAT_BAD_SHOT_OUT_OF_RANGE) {
@@ -1975,13 +1975,13 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
} }
// 0x4298EC // 0x4298EC
static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode) static bool _ai_can_use_weapon(Object* critter, Object* weapon, HitMode hitMode)
{ {
bool result = critterCanUseWeapon(critter, weapon, hitMode); bool result = critterCanUseWeapon(critter, weapon, hitMode);
AiPacket* ai = aiGetPacket(critter); AiPacket* ai = aiGetPacket(critter);
if (result) { if (result) {
int skill = weaponGetSkillForHitMode(weapon, hitMode); Skill skill = weaponGetSkillForHitMode(weapon, hitMode);
if (skillGetValue(critter, skill) < ai->min_to_hit) { if (skillGetValue(critter, skill) < ai->min_to_hit) {
result = false; result = false;
} }
@@ -1998,7 +1998,7 @@ static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode)
// 0x4299A0 // 0x4299A0
Object* _ai_search_inven_weap(Object* critter, bool checkRequiredActionPoints, Object* defender) Object* _ai_search_inven_weap(Object* critter, bool checkRequiredActionPoints, Object* defender)
{ {
int bodyType = critterGetBodyType(critter); BodyType bodyType = critterGetBodyType(critter);
if (bodyType != BODY_TYPE_BIPED if (bodyType != BODY_TYPE_BIPED
&& bodyType != BODY_TYPE_ROBOTIC && bodyType != BODY_TYPE_ROBOTIC
&& critter->pid != PROTO_ID_GORIS) { && critter->pid != PROTO_ID_GORIS) {
@@ -2062,7 +2062,7 @@ Object* _ai_search_inven_armor(Object* critter)
if (armor != nullptr) { if (armor != nullptr) {
armorScore = armorGetArmorClass(armor); armorScore = armorGetArmorClass(armor);
for (int damageType = 0; damageType < DAMAGE_TYPE_COUNT; damageType++) { for (DamageType damageType = DAMAGE_TYPE_FIRST; damageType < DAMAGE_TYPE_COUNT; damageType++) {
armorScore += armorGetDamageResistance(armor, damageType); armorScore += armorGetDamageResistance(armor, damageType);
armorScore += armorGetDamageThreshold(armor, damageType); armorScore += armorGetDamageThreshold(armor, damageType);
} }
@@ -2081,7 +2081,7 @@ Object* _ai_search_inven_armor(Object* critter)
if (armor != candidate) { if (armor != candidate) {
int candidateScore = armorGetArmorClass(candidate); int candidateScore = armorGetArmorClass(candidate);
for (int damageType = 0; damageType < DAMAGE_TYPE_COUNT; damageType++) { for (DamageType damageType = DAMAGE_TYPE_FIRST; damageType < DAMAGE_TYPE_COUNT; damageType++) {
candidateScore += armorGetDamageResistance(candidate, damageType); candidateScore += armorGetDamageResistance(candidate, damageType);
candidateScore += armorGetDamageThreshold(candidate, damageType); candidateScore += armorGetDamageThreshold(candidate, damageType);
} }
@@ -2128,7 +2128,7 @@ static bool aiCanUseItem(Object* critter, Object* item)
return false; return false;
} }
int killType = critterGetKillType(critter); KillType killType = critterGetKillType(critter);
if (killType != KILL_TYPE_MAN if (killType != KILL_TYPE_MAN
&& killType != KILL_TYPE_WOMAN && killType != KILL_TYPE_WOMAN
&& killType != KILL_TYPE_SUPER_MUTANT && killType != KILL_TYPE_SUPER_MUTANT
@@ -2260,7 +2260,7 @@ static Object* _ai_retrieve_object(Object* critter, Object* item)
} }
// 0x429DB4 // 0x429DB4
static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender) static HitMode _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender)
{ {
if (weapon == nullptr) { if (weapon == nullptr) {
return HIT_MODE_PUNCH; return HIT_MODE_PUNCH;
@@ -2302,19 +2302,19 @@ static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender)
break; break;
case AREA_ATTACK_MODE_BE_SURE: case AREA_ATTACK_MODE_BE_SURE:
if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 85 if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 85
&& !_combat_safety_invalidate_weapon(attacker, weapon, 3, defender, nullptr)) { && !_combat_safety_invalidate_weapon(attacker, weapon, HIT_MODE_RIGHT_WEAPON_SECONDARY, defender, nullptr)) {
useSecondaryMode = true; useSecondaryMode = true;
} }
break; break;
case AREA_ATTACK_MODE_BE_CAREFUL: case AREA_ATTACK_MODE_BE_CAREFUL:
if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 50 if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 50
&& !_combat_safety_invalidate_weapon(attacker, weapon, 3, defender, nullptr)) { && !_combat_safety_invalidate_weapon(attacker, weapon, HIT_MODE_RIGHT_WEAPON_SECONDARY, defender, nullptr)) {
useSecondaryMode = true; useSecondaryMode = true;
} }
break; break;
case AREA_ATTACK_MODE_BE_ABSOLUTELY_SURE: case AREA_ATTACK_MODE_BE_ABSOLUTELY_SURE:
if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 95 if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 95
&& !_combat_safety_invalidate_weapon(attacker, weapon, 3, defender, nullptr)) { && !_combat_safety_invalidate_weapon(attacker, weapon, HIT_MODE_RIGHT_WEAPON_SECONDARY, defender, nullptr)) {
useSecondaryMode = true; useSecondaryMode = true;
} }
break; break;
@@ -2574,7 +2574,7 @@ static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetDa
// 0x42A518 // 0x42A518
static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance) static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance)
{ {
int hitMode = HIT_MODE_RIGHT_WEAPON_PRIMARY; HitMode hitMode = HIT_MODE_RIGHT_WEAPON_PRIMARY;
bool aiming = false; bool aiming = false;
if (attacker == gDude) { if (attacker == gDude) {
interfaceGetCurrentHitMode(&hitMode, &aiming); interfaceGetCurrentHitMode(&hitMode, &aiming);
@@ -2601,7 +2601,7 @@ static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object
} }
// 0x42A5B8 // 0x42A5B8
static int _ai_switch_weapons(Object* attacker, int* hitMode, Object** weapon, Object* defender) static int _ai_switch_weapons(Object* attacker, HitMode* hitMode, Object** weapon, Object* defender)
{ {
*weapon = nullptr; *weapon = nullptr;
*hitMode = HIT_MODE_PUNCH; *hitMode = HIT_MODE_PUNCH;
@@ -2639,9 +2639,9 @@ static int _ai_switch_weapons(Object* attacker, int* hitMode, Object** weapon, O
} }
// 0x42A670 // 0x42A670
static int _ai_called_shot(Object* attacker, Object* defender, int hitMode) static HitLocation _ai_called_shot(Object* attacker, Object* defender, HitMode hitMode)
{ {
int hitLocation = HIT_LOCATION_TORSO; HitLocation hitLocation = HIT_LOCATION_TORSO;
if (weaponGetActionPointCost(attacker, hitMode, true) <= attacker->data.critter.combat.ap) { if (weaponGetActionPointCost(attacker, hitMode, true) <= attacker->data.critter.combat.ap) {
if (critterCanAim(attacker, hitMode)) { if (critterCanAim(attacker, hitMode)) {
@@ -2661,8 +2661,8 @@ static int _ai_called_shot(Object* attacker, Object* defender, int hitMode)
} }
if (critterGetStat(attacker, STAT_INTELLIGENCE) >= intelligenceRequired) { if (critterGetStat(attacker, STAT_INTELLIGENCE) >= intelligenceRequired) {
hitLocation = randomBetween(0, HIT_LOCATION_SPECIFIC_COUNT); hitLocation = static_cast<HitLocation>(randomBetween(HIT_LOCATION_FIRST, HIT_LOCATION_SPECIFIC_COUNT));
int chanceToHit = _determine_to_hit(attacker, defender, hitMode, hitLocation); int chanceToHit = _determine_to_hit(attacker, defender, hitLocation, hitMode);
if (chanceToHit < ai->min_to_hit) { if (chanceToHit < ai->min_to_hit) {
hitLocation = HIT_LOCATION_TORSO; hitLocation = HIT_LOCATION_TORSO;
} }
@@ -2675,7 +2675,7 @@ static int _ai_called_shot(Object* attacker, Object* defender, int hitMode)
} }
// 0x42A748 // 0x42A748
static int _ai_attack(Object* attacker, Object* defender, int hitMode) static int _ai_attack(Object* attacker, Object* defender, HitMode hitMode)
{ {
if (attacker->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) { if (attacker->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) {
return -1; return -1;
@@ -2686,7 +2686,7 @@ static int _ai_attack(Object* attacker, Object* defender, int hitMode)
reg_anim_end(); reg_anim_end();
_combat_turn_run(); _combat_turn_run();
int hitLocation = _ai_called_shot(attacker, defender, hitMode); HitLocation hitLocation = _ai_called_shot(attacker, defender, hitMode);
if (_combat_attack(attacker, defender, hitMode, hitLocation)) { if (_combat_attack(attacker, defender, hitMode, hitLocation)) {
return -1; return -1;
} }
@@ -2709,7 +2709,7 @@ static int _ai_try_attack(Object* attacker, Object* defender)
weapon = nullptr; weapon = nullptr;
} }
int hitMode = _ai_pick_hit_mode(attacker, weapon, defender); HitMode hitMode = _ai_pick_hit_mode(attacker, weapon, defender);
int minToHit = aiGetPacket(attacker)->min_to_hit; int minToHit = aiGetPacket(attacker)->min_to_hit;
int actionPoints = attacker->data.critter.combat.ap; int actionPoints = attacker->data.critter.combat.ap;
@@ -2736,7 +2736,7 @@ static int _ai_try_attack(Object* attacker, Object* defender)
break; break;
} }
int reason = _combat_check_bad_shot(attacker, defender, hitMode, false); CombatBadShot reason = _combat_check_bad_shot(attacker, defender, hitMode, false);
if (reason == COMBAT_BAD_SHOT_NO_AMMO) { if (reason == COMBAT_BAD_SHOT_NO_AMMO) {
// If the secondary attack (for example, burst fire) does not have enough ammo, // If the secondary attack (for example, burst fire) does not have enough ammo,
// try the primary attack, which may have a lower ammo cost. // try the primary attack, which may have a lower ammo cost.
@@ -2821,9 +2821,6 @@ static int _ai_try_attack(Object* attacker, Object* defender)
} }
} }
} else if (reason == COMBAT_BAD_SHOT_NOT_ENOUGH_AP || reason == COMBAT_BAD_SHOT_ARM_CRIPPLED || reason == COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED) { } else if (reason == COMBAT_BAD_SHOT_NOT_ENOUGH_AP || reason == COMBAT_BAD_SHOT_ARM_CRIPPLED || reason == COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED) {
// 3 - not enough action points
// 6 - crippled one arm for two-handed weapon
// 7 - both hands crippled
if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1) { if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1) {
return -1; return -1;
} }
+1
View File
@@ -6,6 +6,7 @@
#include "db.h" #include "db.h"
#include "obj_types.h" #include "obj_types.h"
#include "sfall_script_hooks.h" #include "sfall_script_hooks.h"
#include "skill_defs.h"
namespace fallout { namespace fallout {
+119 -34
View File
@@ -5,20 +5,66 @@
#define EXPLOSION_TARGET_COUNT (6) #define EXPLOSION_TARGET_COUNT (6)
#define CRTICIAL_EFFECT_COUNT (6)
#define WEAPON_CRITICAL_FAILURE_TYPE_COUNT (7) #define WEAPON_CRITICAL_FAILURE_TYPE_COUNT (7)
#define WEAPON_CRITICAL_FAILURE_EFFECT_COUNT (5) #define WEAPON_CRITICAL_FAILURE_EFFECT_COUNT (5)
namespace fallout { namespace fallout {
typedef enum CombatState { enum CriticalEffect : int {
CRITICAL_EFFECT_1 = 0,
CRITICAL_EFFECT_2 = 1,
CRITICAL_EFFECT_3 = 2,
CRITICAL_EFFECT_4 = 3,
CRITICAL_EFFECT_5 = 4,
CRITICAL_EFFECT_6 = 5,
CRITICAL_EFFECT_COUNT = 6,
CRITICAL_EFFECT_FIRST = CRITICAL_EFFECT_1
};
inline CriticalEffect operator++(CriticalEffect& e, int)
{
CriticalEffect result = e;
e = static_cast<CriticalEffect>(static_cast<int>(e) + 1);
return result;
}
inline static bool criticalEffectIsValid(int effect)
{
return effect >= CRITICAL_EFFECT_FIRST && effect < CRITICAL_EFFECT_COUNT;
}
enum CombatState : unsigned int {
COMBAT_STATE_OUT_COMBAT = 0x00,
COMBAT_STATE_IN_COMBAT = 0x01, COMBAT_STATE_IN_COMBAT = 0x01,
COMBAT_STATE_PLAYER_TURN = 0x02, COMBAT_STATE_PLAYER_TURN = 0x02,
COMBAT_STATE_EXIT_REQUESTED = 0x08, COMBAT_STATE_EXIT_REQUESTED = 0x08,
} CombatState; };
typedef enum HitMode { inline fallout::CombatState& operator&=(fallout::CombatState& lhs, unsigned int rhs)
{
lhs = static_cast<fallout::CombatState>(static_cast<unsigned int>(lhs) & rhs);
return lhs;
}
inline constexpr CombatState operator~(CombatState rhs)
{
return static_cast<CombatState>(~static_cast<unsigned int>(rhs));
}
inline CombatState& operator&=(CombatState& lhs, CombatState rhs)
{
lhs = static_cast<CombatState>(static_cast<unsigned int>(lhs) & static_cast<unsigned int>(rhs));
return lhs;
}
inline CombatState& operator|=(CombatState& lhs, CombatState rhs)
{
lhs = static_cast<CombatState>(static_cast<unsigned int>(lhs) | static_cast<unsigned int>(rhs));
return lhs;
}
enum HitMode : int {
HIT_MODE_INVALID = -1,
HIT_MODE_LEFT_WEAPON_PRIMARY = 0, HIT_MODE_LEFT_WEAPON_PRIMARY = 0,
HIT_MODE_LEFT_WEAPON_SECONDARY = 1, HIT_MODE_LEFT_WEAPON_SECONDARY = 1,
HIT_MODE_RIGHT_WEAPON_PRIMARY = 2, HIT_MODE_RIGHT_WEAPON_PRIMARY = 2,
@@ -70,21 +116,47 @@ typedef enum HitMode {
LAST_ADVANCED_KICK_HIT_MODE = HIT_MODE_PIERCING_KICK, LAST_ADVANCED_KICK_HIT_MODE = HIT_MODE_PIERCING_KICK,
FIRST_ADVANCED_UNARMED_HIT_MODE = FIRST_ADVANCED_PUNCH_HIT_MODE, FIRST_ADVANCED_UNARMED_HIT_MODE = FIRST_ADVANCED_PUNCH_HIT_MODE,
LAST_ADVANCED_UNARMED_HIT_MODE = LAST_ADVANCED_KICK_HIT_MODE, LAST_ADVANCED_UNARMED_HIT_MODE = LAST_ADVANCED_KICK_HIT_MODE,
} HitMode; HIT_MODE_FIRST = HIT_MODE_LEFT_WEAPON_PRIMARY,
};
typedef enum HitLocation { inline static bool hitModeIsValid(int hitMode)
HIT_LOCATION_HEAD, {
HIT_LOCATION_LEFT_ARM, return hitMode >= HIT_MODE_FIRST && hitMode < HIT_MODE_COUNT;
HIT_LOCATION_RIGHT_ARM, }
HIT_LOCATION_TORSO,
HIT_LOCATION_RIGHT_LEG, inline HitMode operator++(HitMode& e, int)
HIT_LOCATION_LEFT_LEG, {
HIT_LOCATION_EYES, HitMode result = e;
HIT_LOCATION_GROIN, e = static_cast<HitMode>(static_cast<int>(e) + 1);
HIT_LOCATION_UNCALLED, return result;
HIT_LOCATION_COUNT, }
enum HitLocation : int {
HIT_LOCATION_HEAD = 0,
HIT_LOCATION_LEFT_ARM = 1,
HIT_LOCATION_RIGHT_ARM = 2,
HIT_LOCATION_TORSO = 3,
HIT_LOCATION_RIGHT_LEG = 4,
HIT_LOCATION_LEFT_LEG = 5,
HIT_LOCATION_EYES = 6,
HIT_LOCATION_GROIN = 7,
HIT_LOCATION_UNCALLED = 8,
HIT_LOCATION_COUNT = 9,
HIT_LOCATION_SPECIFIC_COUNT = HIT_LOCATION_COUNT - 1, HIT_LOCATION_SPECIFIC_COUNT = HIT_LOCATION_COUNT - 1,
} HitLocation; HIT_LOCATION_FIRST = HIT_LOCATION_HEAD,
};
inline static bool hitLocationIsValid(int hitLocation)
{
return hitLocation >= HIT_LOCATION_FIRST && hitLocation < HIT_LOCATION_COUNT;
}
inline HitLocation operator++(HitLocation& e, int)
{
HitLocation result = e;
e = static_cast<HitLocation>(static_cast<int>(e) + 1);
return result;
}
typedef struct CombatStartData { typedef struct CombatStartData {
Object* attacker; Object* attacker;
@@ -101,16 +173,16 @@ typedef struct CombatStartData {
typedef struct Attack { typedef struct Attack {
Object* attacker; Object* attacker;
int hitMode; HitMode hitMode;
Object* weapon; Object* weapon;
int attackHitLocation; // UNUSED? HitLocation attackHitLocation; // UNUSED?
int attackerDamage; int attackerDamage;
int attackerFlags; int attackerFlags;
int ammoQuantity; int ammoQuantity;
int criticalMessageId; int criticalMessageId;
Object* defender; Object* defender;
int tile; int tile;
int defenderHitLocation; HitLocation defenderHitLocation;
int defenderDamage; int defenderDamage;
int defenderFlags; int defenderFlags;
int defenderKnockback; int defenderKnockback;
@@ -123,16 +195,29 @@ typedef struct Attack {
int extrasKnockback[EXPLOSION_TARGET_COUNT]; int extrasKnockback[EXPLOSION_TARGET_COUNT];
} Attack; } Attack;
typedef enum CriticalHitDescriptionDataMember { enum CriticalHitDataMember : int {
CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER, CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER = 0,
CRIT_DATA_MEMBER_FLAGS, CRIT_DATA_MEMBER_FLAGS = 1,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT = 2,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER = 3,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS = 4,
CRIT_DATA_MEMBER_MESSAGE_ID, CRIT_DATA_MEMBER_MESSAGE_ID = 5,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID = 6,
CRIT_DATA_MEMBER_COUNT, CRIT_DATA_MEMBER_COUNT = 7,
} CriticalHitDescriptionDataMember; CRIT_DATA_MEMBER_FIRST = CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER,
};
inline static bool criticalHitDataMemberIsValid(int criticalHitDataMember)
{
return criticalHitDataMember >= CRIT_DATA_MEMBER_FIRST && criticalHitDataMember < CRIT_DATA_MEMBER_COUNT;
}
inline CriticalHitDataMember operator++(CriticalHitDataMember& e, int)
{
CriticalHitDataMember result = e;
e = static_cast<CriticalHitDataMember>(static_cast<int>(e) + 1);
return result;
}
// Provides metadata about critical hit effect. // Provides metadata about critical hit effect.
typedef union CriticalHitDescription { typedef union CriticalHitDescription {
@@ -160,7 +245,7 @@ typedef union CriticalHitDescription {
int values[CRIT_DATA_MEMBER_COUNT]; int values[CRIT_DATA_MEMBER_COUNT];
} CriticalHitDescription; } CriticalHitDescription;
typedef enum CombatBadShot { enum CombatBadShot : int {
COMBAT_BAD_SHOT_OK = 0, COMBAT_BAD_SHOT_OK = 0,
COMBAT_BAD_SHOT_NO_AMMO = 1, COMBAT_BAD_SHOT_NO_AMMO = 1,
COMBAT_BAD_SHOT_OUT_OF_RANGE = 2, COMBAT_BAD_SHOT_OUT_OF_RANGE = 2,
@@ -168,8 +253,8 @@ typedef enum CombatBadShot {
COMBAT_BAD_SHOT_ALREADY_DEAD = 4, COMBAT_BAD_SHOT_ALREADY_DEAD = 4,
COMBAT_BAD_SHOT_AIM_BLOCKED = 5, COMBAT_BAD_SHOT_AIM_BLOCKED = 5,
COMBAT_BAD_SHOT_ARM_CRIPPLED = 6, COMBAT_BAD_SHOT_ARM_CRIPPLED = 6,
COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED = 7, COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED = 7
} CombatBadShot; };
} // namespace fallout } // namespace fallout
+32 -15
View File
@@ -17,7 +17,7 @@
namespace fallout { namespace fallout {
#define CONFIG_FILE_MAX_LINE_LENGTH (1024) #define CONFIG_FILE_MAX_LINE_LENGTH (2048)
// The initial number of sections (or key-value) pairs in the config. // The initial number of sections (or key-value) pairs in the config.
#define CONFIG_INITIAL_CAPACITY (10) #define CONFIG_INITIAL_CAPACITY (10)
@@ -32,9 +32,10 @@ struct CaseInsensitiveLess {
typedef std::set<std::string, CaseInsensitiveLess> StringSet; typedef std::set<std::string, CaseInsensitiveLess> StringSet;
static bool configParseLine(Config* config, char* string); static bool configParseLine(Config* config, char* string);
static bool configParseKeyValue(char* string, char* key, char* value); static bool configParseKeyValue(char* string, std::string& key, std::string& value);
static bool configEnsureSectionExists(Config* config, const char* sectionKey); static bool configEnsureSectionExists(Config* config, const char* sectionKey);
static bool configTrimString(char* string); static bool configTrimString(char* string);
static std::string configGetTrimmedString(const char* string);
static bool configWriteDb(Config* config, const char* filePath); static bool configWriteDb(Config* config, const char* filePath);
static bool configWriteStandard(Config* config, const char* filePath); static bool configWriteStandard(Config* config, const char* filePath);
@@ -120,10 +121,10 @@ bool configParseCommandLineArguments(Config* config, int argc, char** argv)
*pch = '\0'; *pch = '\0';
char key[260]; std::string key;
char value[260]; std::string value;
if (configParseKeyValue(pch + 1, key, value)) { if (configParseKeyValue(pch + 1, key, value)) {
if (!configSetString(config, sectionKey, key, value)) { if (!configSetString(config, sectionKey, key.c_str(), value.c_str())) {
*pch = ']'; *pch = ']';
return false; return false;
} }
@@ -683,13 +684,13 @@ static bool configParseLine(Config* config, char* string)
} }
} }
char key[260]; std::string key;
char value[260]; std::string value;
if (!configParseKeyValue(string, key, value)) { if (!configParseKeyValue(string, key, value)) {
return false; return false;
} }
return configSetString(config, gConfigLastSectionKey, key, value); return configSetString(config, gConfigLastSectionKey, key.c_str(), value.c_str());
} }
// Splits "key=value" pair from [string] and copy appropriate parts into [key] // Splits "key=value" pair from [string] and copy appropriate parts into [key]
@@ -698,9 +699,9 @@ static bool configParseLine(Config* config, char* string)
// Both key and value are trimmed. // Both key and value are trimmed.
// //
// 0x42C594 // 0x42C594
static bool configParseKeyValue(char* string, char* key, char* value) static bool configParseKeyValue(char* string, std::string& key, std::string& value)
{ {
if (string == nullptr || key == nullptr || value == nullptr) { if (string == nullptr) {
return false; return false;
} }
@@ -712,14 +713,11 @@ static bool configParseKeyValue(char* string, char* key, char* value)
*pch = '\0'; *pch = '\0';
strcpy(key, string); key = configGetTrimmedString(string);
strcpy(value, pch + 1); value = configGetTrimmedString(pch + 1);
*pch = '='; *pch = '=';
configTrimString(key);
configTrimString(value);
return true; return true;
} }
@@ -791,6 +789,25 @@ static bool configTrimString(char* string)
return true; return true;
} }
static std::string configGetTrimmedString(const char* string)
{
if (string == nullptr) {
return std::string();
}
const char* start = string;
while (isspace(static_cast<unsigned char>(*start))) {
start++;
}
const char* end = start + strlen(start);
while (end > start && isspace(static_cast<unsigned char>(*(end - 1)))) {
end--;
}
return std::string(start, end);
}
// 0x42C718 // 0x42C718
bool configGetDouble(Config* config, const char* sectionKey, const char* key, double* valuePtr) bool configGetDouble(Config* config, const char* sectionKey, const char* key, double* valuePtr)
{ {
+1
View File
@@ -8,6 +8,7 @@ namespace fallout {
#define CONTENT_CONFIG_START_SECTION "start" #define CONTENT_CONFIG_START_SECTION "start"
#define CONTENT_CONFIG_KARMA_SECTION "karma" #define CONTENT_CONFIG_KARMA_SECTION "karma"
#define CONTENT_CONFIG_ITEMS_SECTION "items" #define CONTENT_CONFIG_ITEMS_SECTION "items"
#define CONTENT_CONFIG_MAPS_SECTION "maps"
#define CONTENT_CONFIG_DIALOG_SECTION "dialog" #define CONTENT_CONFIG_DIALOG_SECTION "dialog"
#define CONTENT_CONFIG_MAIN_MENU_SECTION "main_menu" #define CONTENT_CONFIG_MAIN_MENU_SECTION "main_menu"
#define CONTENT_CONFIG_MOVIES_SECTION "movies" #define CONTENT_CONFIG_MOVIES_SECTION "movies"
+36 -44
View File
@@ -80,7 +80,7 @@ static int _critterClearObjDrugs(Object* obj, void* data);
static char _aCorpse[] = "corpse"; static char _aCorpse[] = "corpse";
// 0x501494 // 0x501494
static char byte_501494[] = ""; static char empty[] = "";
// 0x51833C name_critter // 0x51833C name_critter
static char* _name_critter = _aCorpse; static char* _name_critter = _aCorpse;
@@ -152,7 +152,7 @@ static char gDudeName[DUDE_NAME_MAX_LENGTH];
static int _sneak_working; static int _sneak_working;
// 0x56D780 pc_kill_counts // 0x56D780 pc_kill_counts
static int gKillsByType[KILL_TYPE_COUNT]; static int gKillsByType[KILL_TYPE_DEFAULT_COUNT];
// Something with radiation. // Something with radiation.
// //
@@ -679,15 +679,15 @@ int radiationEventWrite(File* stream, void* data)
} }
// 0x42D82C critter_get_base_damage_type // 0x42D82C critter_get_base_damage_type
int critterGetDamageType(Object* obj) DamageType critterGetDamageType(Object* obj)
{ {
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return 0; return DAMAGE_TYPE_NORMAL;
} }
Proto* proto; Proto* proto;
if (protoGetProto(obj->pid, &proto) == -1) { if (protoGetProto(obj->pid, &proto) == -1) {
return 0; return DAMAGE_TYPE_NORMAL;
} }
return proto->critter.data.damageType; return proto->critter.data.damageType;
@@ -703,9 +703,9 @@ static int critter_kill_count_clear()
} }
// 0x42D878 critter_kill_count_inc // 0x42D878 critter_kill_count_inc
int killsIncByType(int killType) int killsIncByType(KillType killType)
{ {
if (killType != -1 && killType < KILL_TYPE_COUNT) { if (killTypeIsValid(killType)) {
gKillsByType[killType]++; gKillsByType[killType]++;
return 0; return 0;
} }
@@ -714,9 +714,9 @@ int killsIncByType(int killType)
} }
// 0x42D8A8 critter_kill_count // 0x42D8A8 critter_kill_count
int killsGetByType(int killType) int killsGetByType(KillType killType)
{ {
if (killType != -1 && killType < KILL_TYPE_COUNT) { if (killTypeIsValid(killType)) {
return gKillsByType[killType]; return gKillsByType[killType];
} }
@@ -726,7 +726,7 @@ int killsGetByType(int killType)
// 0x42D8C0 critter_kill_count_load // 0x42D8C0 critter_kill_count_load
int killsLoad(File* stream) int killsLoad(File* stream)
{ {
if (fileReadInt32List(stream, gKillsByType, KILL_TYPE_COUNT) == -1) { if (fileReadInt32List(stream, gKillsByType, KILL_TYPE_DEFAULT_COUNT) == -1) {
fileClose(stream); fileClose(stream);
return -1; return -1;
} }
@@ -737,7 +737,7 @@ int killsLoad(File* stream)
// 0x42D8F0 critter_kill_count_save // 0x42D8F0 critter_kill_count_save
int killsSave(File* stream) int killsSave(File* stream)
{ {
if (fileWriteInt32List(stream, gKillsByType, KILL_TYPE_COUNT) == -1) { if (fileWriteInt32List(stream, gKillsByType, KILL_TYPE_DEFAULT_COUNT) == -1) {
fileClose(stream); fileClose(stream);
return -1; return -1;
} }
@@ -746,7 +746,7 @@ int killsSave(File* stream)
} }
// 0x42D920 critter_kill_count_type // 0x42D920 critter_kill_count_type
int critterGetKillType(Object* obj) KillType critterGetKillType(Object* obj)
{ {
if (obj == gDude) { if (obj == gDude) {
int gender = critterGetStat(obj, STAT_GENDER); int gender = critterGetStat(obj, STAT_GENDER);
@@ -757,7 +757,7 @@ int critterGetKillType(Object* obj)
} }
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return -1; return KILL_TYPE_INVALID;
} }
Proto* proto; Proto* proto;
@@ -767,33 +767,25 @@ int critterGetKillType(Object* obj)
} }
// 0x42D974 critter_kill_name // 0x42D974 critter_kill_name
char* killTypeGetName(int killType) char* killTypeGetName(KillType killType)
{ {
if (killType != -1 && killType < KILL_TYPE_COUNT) { if (killTypeIsValid(killType)) {
if (killType >= 0 && killType < KILL_TYPE_COUNT) { MessageListItem messageListItem;
MessageListItem messageListItem; return getmsg(&gProtoMessageList, &messageListItem, 1450 + killType);
return getmsg(&gProtoMessageList, &messageListItem, 1450 + killType);
} else {
return nullptr;
}
} else {
return byte_501494;
} }
return empty;
} }
// 0x42D9B4 critter_kill_info // 0x42D9B4 critter_kill_info
char* killTypeGetDescription(int killType) char* killTypeGetDescription(KillType killType)
{ {
if (killType != -1 && killType < KILL_TYPE_COUNT) { if (killTypeIsValid(killType)) {
if (killType >= 0 && killType < KILL_TYPE_COUNT) { MessageListItem messageListItem;
MessageListItem messageListItem; return getmsg(&gProtoMessageList, &messageListItem, 1469 + killType);
return getmsg(&gProtoMessageList, &messageListItem, 1469 + killType);
} else {
return nullptr;
}
} else {
return byte_501494;
} }
return empty;
} }
// 0x42D9F4 critter_heal_hours // 0x42D9F4 critter_heal_hours
@@ -1008,15 +1000,15 @@ bool critterIsProne(Object* critter)
// critter_body_type // critter_body_type
// 0x42DDC4 critter_body_type // 0x42DDC4 critter_body_type
int critterGetBodyType(Object* critter) BodyType critterGetBodyType(Object* critter)
{ {
if (critter == nullptr) { if (critter == nullptr) {
debugPrint("\nError: critter_body_type: pobj was NULL!"); debugPrint("\nError: critter_body_type: pobj was NULL!");
return 0; return BODY_TYPE_BIPED;
} }
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) { if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return 0; return BODY_TYPE_BIPED;
} }
Proto* proto; Proto* proto;
@@ -1024,7 +1016,7 @@ int critterGetBodyType(Object* critter)
return proto->critter.data.bodyType; return proto->critter.data.bodyType;
} }
bool critterCanUseWeapon(Object* critter, Object* weapon, int hitMode) bool critterCanUseWeapon(Object* critter, Object* weapon, HitMode hitMode)
{ {
if (critter == nullptr || weapon == nullptr || itemGetType(weapon) != ITEM_TYPE_WEAPON) { if (critter == nullptr || weapon == nullptr || itemGetType(weapon) != ITEM_TYPE_WEAPON) {
return false; return false;
@@ -1091,9 +1083,9 @@ int gcdLoad(const char* path)
} }
proto->critter.data.baseStats[STAT_DAMAGE_RESISTANCE_EMP] = 100; proto->critter.data.baseStats[STAT_DAMAGE_RESISTANCE_EMP] = 100;
proto->critter.data.bodyType = 0; proto->critter.data.bodyType = BODY_TYPE_BIPED;
proto->critter.data.experience = 0; proto->critter.data.experience = 0;
proto->critter.data.killType = 0; proto->critter.data.killType = KILL_TYPE_MAN;
fileClose(stream); fileClose(stream);
return 0; return 0;
@@ -1105,10 +1097,10 @@ int protoCritterDataRead(File* stream, CritterProtoData* critterData)
if (fileReadInt32(stream, &(critterData->flags)) == -1) return -1; if (fileReadInt32(stream, &(critterData->flags)) == -1) return -1;
if (fileReadInt32List(stream, critterData->baseStats, SAVEABLE_STAT_COUNT) == -1) return -1; if (fileReadInt32List(stream, critterData->baseStats, SAVEABLE_STAT_COUNT) == -1) return -1;
if (fileReadInt32List(stream, critterData->bonusStats, SAVEABLE_STAT_COUNT) == -1) return -1; if (fileReadInt32List(stream, critterData->bonusStats, SAVEABLE_STAT_COUNT) == -1) return -1;
if (fileReadInt32List(stream, critterData->skills, SKILL_COUNT) == -1) return -1; if (fileReadInt32List(stream, reinterpret_cast<int*>(critterData->skills), SKILL_COUNT) == -1) return -1;
if (fileReadInt32(stream, &(critterData->bodyType)) == -1) return -1; if (fileReadInt32(stream, reinterpret_cast<int*>(&(critterData->bodyType))) == -1) return -1;
if (fileReadInt32(stream, &(critterData->experience)) == -1) return -1; if (fileReadInt32(stream, &(critterData->experience)) == -1) return -1;
if (fileReadInt32(stream, &(critterData->killType)) == -1) return -1; if (fileReadInt32(stream, reinterpret_cast<int*>(&(critterData->killType))) == -1) return -1;
// NOTE: For unknown reason damage type is not present in two protos: Sentry // NOTE: For unknown reason damage type is not present in two protos: Sentry
// Bot and Weak Brahmin. These two protos are 412 bytes, not 416. // Bot and Weak Brahmin. These two protos are 412 bytes, not 416.
@@ -1122,7 +1114,7 @@ int protoCritterDataRead(File* stream, CritterProtoData* critterData)
// //
// Regardless of the reason, damage type is considered optional by original // Regardless of the reason, damage type is considered optional by original
// code as seen at 0x42E01B. // code as seen at 0x42E01B.
if (fileReadInt32(stream, &(critterData->damageType)) == -1) { if (fileReadInt32(stream, reinterpret_cast<int*>(&(critterData->damageType))) == -1) {
critterData->damageType = DAMAGE_TYPE_NORMAL; critterData->damageType = DAMAGE_TYPE_NORMAL;
} }
@@ -1172,7 +1164,7 @@ int protoCritterDataWrite(File* stream, CritterProtoData* critterData)
if (fileWriteInt32(stream, critterData->flags) == -1) return -1; if (fileWriteInt32(stream, critterData->flags) == -1) return -1;
if (fileWriteInt32List(stream, critterData->baseStats, SAVEABLE_STAT_COUNT) == -1) return -1; if (fileWriteInt32List(stream, critterData->baseStats, SAVEABLE_STAT_COUNT) == -1) return -1;
if (fileWriteInt32List(stream, critterData->bonusStats, SAVEABLE_STAT_COUNT) == -1) return -1; if (fileWriteInt32List(stream, critterData->bonusStats, SAVEABLE_STAT_COUNT) == -1) return -1;
if (fileWriteInt32List(stream, critterData->skills, SKILL_COUNT) == -1) return -1; if (fileWriteInt32List(stream, reinterpret_cast<int*>(critterData->skills), SKILL_COUNT) == -1) return -1;
if (fileWriteInt32(stream, critterData->bodyType) == -1) return -1; if (fileWriteInt32(stream, critterData->bodyType) == -1) return -1;
if (fileWriteInt32(stream, critterData->experience) == -1) return -1; if (fileWriteInt32(stream, critterData->experience) == -1) return -1;
if (fileWriteInt32(stream, critterData->killType) == -1) return -1; if (fileWriteInt32(stream, critterData->killType) == -1) return -1;
+9 -8
View File
@@ -1,6 +1,7 @@
#ifndef CRITTER_H #ifndef CRITTER_H
#define CRITTER_H #define CRITTER_H
#include "combat_defs.h"
#include "db.h" #include "db.h"
#include "obj_types.h" #include "obj_types.h"
#include "proto_types.h" #include "proto_types.h"
@@ -38,14 +39,14 @@ void radiationProcess(Object* obj, int radiationLevel, bool direction);
int radiationEventProcess(Object* obj, void* data); int radiationEventProcess(Object* obj, void* data);
int radiationEventRead(File* stream, void** dataPtr); int radiationEventRead(File* stream, void** dataPtr);
int radiationEventWrite(File* stream, void* data); int radiationEventWrite(File* stream, void* data);
int critterGetDamageType(Object* critter); DamageType critterGetDamageType(Object* critter);
int killsIncByType(int killType); int killsIncByType(KillType killType);
int killsGetByType(int killType); int killsGetByType(KillType killType);
int killsLoad(File* stream); int killsLoad(File* stream);
int killsSave(File* stream); int killsSave(File* stream);
int critterGetKillType(Object* critter); KillType critterGetKillType(Object* critter);
char* killTypeGetName(int killType); char* killTypeGetName(KillType killType);
char* killTypeGetDescription(int killType); char* killTypeGetDescription(KillType killType);
int critterHealByHours(Object* obj, int hours); int critterHealByHours(Object* obj, int hours);
void critterKill(Object* critter, int anim, bool refreshRect); void critterKill(Object* critter, int anim, bool refreshRect);
int critterGetExp(Object* critter); int critterGetExp(Object* critter);
@@ -53,10 +54,10 @@ bool critterIsActive(Object* critter);
bool critterIsDead(Object* critter); bool critterIsDead(Object* critter);
bool critterIsCrippled(Object* critter); bool critterIsCrippled(Object* critter);
bool critterIsProne(Object* critter); bool critterIsProne(Object* critter);
int critterGetBodyType(Object* critter); BodyType critterGetBodyType(Object* critter);
// Checks physical/art capability only. Callers that expose weapon usability // Checks physical/art capability only. Callers that expose weapon usability
// decisions must still call scriptHooks_CanUseWeapon with the final result. // decisions must still call scriptHooks_CanUseWeapon with the final result.
bool critterCanUseWeapon(Object* critter, Object* weapon, int hitMode); bool critterCanUseWeapon(Object* critter, Object* weapon, HitMode hitMode);
int critterBuildGorisFid(Object* critter, int frmId); int critterBuildGorisFid(Object* critter, int frmId);
int gcdLoad(const char* path); int gcdLoad(const char* path);
int protoCritterDataRead(File* stream, CritterProtoData* critterData); int protoCritterDataRead(File* stream, CritterProtoData* critterData);
+6
View File
@@ -175,11 +175,17 @@ namespace {
constexpr SfallMigrationEntry kSfallMigrationEntries[] = { constexpr SfallMigrationEntry kSfallMigrationEntries[] = {
// [start] // [start]
{ kSfallMisc, "StartingMap", CONTENT_CONFIG_START_SECTION, "map", "" }, { kSfallMisc, "StartingMap", CONTENT_CONFIG_START_SECTION, "map", "" },
{ kSfallMisc, "StartXPos", CONTENT_CONFIG_START_SECTION, "worldmap_x", "-1" },
{ kSfallMisc, "StartYPos", CONTENT_CONFIG_START_SECTION, "worldmap_y", "-1" },
{ kSfallMisc, "ViewXPos", CONTENT_CONFIG_START_SECTION, "worldmap_view_x", "-1" },
{ kSfallMisc, "ViewYPos", CONTENT_CONFIG_START_SECTION, "worldmap_view_y", "-1" },
{ kSfallMisc, "MaleStartModel", CONTENT_CONFIG_START_SECTION, "model_male", "hmwarr" }, { kSfallMisc, "MaleStartModel", CONTENT_CONFIG_START_SECTION, "model_male", "hmwarr" },
{ kSfallMisc, "MaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_male_default", "hmjmps" }, { kSfallMisc, "MaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_male_default", "hmjmps" },
{ kSfallMisc, "FemaleStartModel", CONTENT_CONFIG_START_SECTION, "model_female", "hfprim" }, { kSfallMisc, "FemaleStartModel", CONTENT_CONFIG_START_SECTION, "model_female", "hfprim" },
{ kSfallMisc, "FemaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_female_default", "hfjmps" }, { kSfallMisc, "FemaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_female_default", "hfjmps" },
{ kSfallMisc, "PipBoyAvailableAtGameStart", CONTENT_CONFIG_START_SECTION, "pipboy", "0" }, { kSfallMisc, "PipBoyAvailableAtGameStart", CONTENT_CONFIG_START_SECTION, "pipboy", "0" },
// [maps]
{ kSfallMisc, "DisableSpecialMapIDs", CONTENT_CONFIG_MAPS_SECTION, "disable_special_map_ids", "0" },
// [karma] // [karma]
{ kSfallMisc, "KarmaFRMs", CONTENT_CONFIG_KARMA_SECTION, "frms" }, { kSfallMisc, "KarmaFRMs", CONTENT_CONFIG_KARMA_SECTION, "frms" },
{ kSfallMisc, "KarmaPoints", CONTENT_CONFIG_KARMA_SECTION, "points" }, { kSfallMisc, "KarmaPoints", CONTENT_CONFIG_KARMA_SECTION, "points" },

Some files were not shown because too many files have changed in this diff Show More