Compare commits

..
Author SHA1 Message Date
Mike Klaas 344f4e375e [Et tu] Movie overrides
Including movie1-movie32, mark_movie_played, set_movie_path

Verified that Fallout 1 movies play properly with this PR
2026-07-22 22:54:47 -07:00
67 changed files with 2257 additions and 2759 deletions
+3 -8
View File
@@ -42,13 +42,8 @@ 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 |
| --- | --- | --- | --- |
| `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` | `DisableSpecialMapIDs` | `maps` | `disable_special_map_ids` |
| `Misc` | `Movie1` - `Movie32` | `movies` | `movie1` - `movie32` |
## Opcodes / Metarules
@@ -65,7 +60,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 | - |
| 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 | - |
| 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 | 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 / 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. |
| Combat / Weapons and ammo | get/set_weapon_ammo_pid<br>get/set_weapon_ammo_count | âś… | - |
@@ -95,7 +90,7 @@ See [`https://sfall-team.github.io/sfall/`](https://sfall-team.github.io/sfall/)
| Locks | lock_is_jammed<br>unjam_lock<br>set_unjam_locks_time | not implemented | - |
| INI settings | get_ini_setting<br>get_ini_string<br>get_ini_section<br>get_ini_sections<br>get_ini_config<br>get_ini_config_db<br>set_ini_setting | âś… | `modified_ini` is intentionally omitted as deprecated. |
| Objects and scripts | set_self<br>set_dude_obj<br>real_dude_obj<br>remove_script<br>get/set_script<br>obj_is_carrying_obj<br>loot_obj<br>dialog_obj<br>obj_under_cursor<br>get/set_object_data<br>get/set_flags<br>set_unique_id<br>set_scr_name<br>obj_is_openable<br>get/set_proto_data<br>get_object_ai_data | âś… except set_dude_obj, set_scr_name | `get/set_object_data` maps supported object and `C_ATTACK_*` offsets instead of exposing arbitrary raw memory offsets. |
| Other / Game management | set_movie_path<br>stop/resume_game<br>mark_movie_played<br>game_loaded<br>get_game_mode<br>get_uptime<br>signal_close_game | implemented: game_loaded, get_game_mode, get_uptime, signal_close_game | - |
| Other / Game management | set_movie_path<br>stop/resume_game<br>mark_movie_played<br>game_loaded<br>get_game_mode<br>get_uptime<br>signal_close_game | âś… except stop/resume_game | - |
| Gameplay tweaks | set_pickpocket_max<br>set_hit_chance_max<br>set_xp_mod<br>set_critter_hit_chance_mod<br>set_base_hit_chance_mod<br>set_hp_per_level_mod<br>gdialog_get_barter_mod<br>get/set_unspent_ap_bonus<br>get/set_unspent_ap_perk_bonus<br>set_base_pickpocket_mod<br>set_critter_pickpocket_mod<br>get/set_inven_ap_cost<br>set_drugs_data<br>get_kill_counter<br>mod_kill_counter<br>set_pipboy_available | implemented: gdialog_get_barter_mod, get/set_unspent_ap{_perk}_bonus, get/set_inven_ap_cost | - |
| NPCs | inc_npc_level<br>get_npc_level<br>npc_engine_level_up | not implemented | - |
| Hero Appearance | set_dm/df_model<br>hero_select_win<br>set_hero_race<br>set_hero_style | not implemented | - |
+35 -11
View File
@@ -11,12 +11,6 @@ month=6
day=24
; To start a new game somewhere other than artemple.map, set this to the map file name.
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.
model_male=hmwarr
model_male_default=hmjmps
@@ -26,11 +20,6 @@ model_female_default=hfjmps
; Set to 2 to make the pipboy available by only skipping the vault suit movie check.
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]
; 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
@@ -71,6 +60,41 @@ credits_offset_x=0
credits_offset_y=0
[movies]
; Overrides for game movie file names in art\cuts\. These correspond to sfall's Movie1 through Movie32 settings.
; Movie IDs used by scripts are 0-indexed, so movie1 is script movie ID 0.
movie1=iplogo.mve
movie2=intro.mve
movie3=elder.mve
movie4=vsuit.mve
movie5=afailed.mve
movie6=adestroy.mve
movie7=car.mve
movie8=cartucci.mve
movie9=timeout.mve
movie10=tanker.mve
movie11=enclave.mve
movie12=derrick.mve
movie13=artimer1.mve
movie14=artimer2.mve
movie15=artimer3.mve
movie16=artimer4.mve
movie17=credits.mve
; Additional script-playable movies.
movie18=
movie19=
movie20=
movie21=
movie22=
movie23=
movie24=
movie25=
movie26=
movie27=
movie28=
movie29=
movie30=
movie31=
movie32=
; Number of days after game start when each Hakunin dream sequence becomes available.
artimer1=90
artimer2=180
-10
View File
@@ -165,16 +165,6 @@ 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.
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]
; Automatically open doors that are unlocked and has no script attached.
-27
View File
@@ -1,27 +0,0 @@
#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
+9 -9
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 _talk_to(Object* _, Object* critter);
static int _report_dmg(Attack* attack, Object* _);
static int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, DamageType damageType);
static int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, int damageType);
static int hideProjectile(void* _, void* projectile);
@@ -200,7 +200,7 @@ int pickDeathAnim(Object* attacker, Object* defender, Object* weapon, int damage
int normalViolenceLevelDamageThreshold = 15;
int maximumBloodViolenceLevelDamageThreshold = 45;
DamageType damageType = weaponGetDamageType(attacker, weapon);
int damageType = weaponGetDamageType(attacker, weapon);
if (weapon != nullptr && weapon->pid == PROTO_ID_MOLOTOV_COCKTAIL) {
normalViolenceLevelDamageThreshold = 5;
@@ -729,7 +729,7 @@ int _action_ranged(Attack* attack, int anim)
weaponGetRange(attack->attacker, attack->hitMode);
DamageType damageType = weaponGetDamageType(attack->attacker, attack->weapon);
int damageType = weaponGetDamageType(attack->attacker, attack->weapon);
tileGetTileInDirection(attack->attacker->tile, attack->attacker->rotation, 1);
@@ -1306,7 +1306,7 @@ int actionLootCritter(Object* critter, Object* target)
}
// 0x4124E0
int _action_skill_use(Skill skill)
int _action_skill_use(int skill)
{
if (skill == SKILL_SNEAK) {
reg_anim_clear(gDude);
@@ -1336,7 +1336,7 @@ static int _action_use_skill_in_combat_error(Object* critter)
// skill_use
// 0x41255C
int actionUseSkill(Object* user, Object* target, Skill skill)
int actionUseSkill(Object* user, Object* target, int skill)
{
UseSkillOnHookResult hookResult = scriptHooks_UseSkillOn(&user, target, skill);
if (!hookResult.shouldContinue) {
@@ -1907,7 +1907,7 @@ int _talk_to(Object* _, Object* critter)
}
// 0x413494
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, DamageType damageType, bool animated, bool bypassArmor)
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor)
{
Attack* attack = (Attack*)internal_malloc(sizeof(*attack));
if (attack == nullptr) {
@@ -1988,15 +1988,15 @@ int _report_dmg(Attack* attack, Object* _)
// Calculate damage by applying threshold and resistances.
//
// 0x413660
int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, DamageType damageType)
int _compute_dmg_damage(int min, int max, Object* obj, int* knockbackDistancePtr, int damageType)
{
if (!critterFlagCheck(obj->pid, CRITTER_NO_KNOCKBACK)) {
knockbackDistancePtr = nullptr;
}
int damage = randomBetween(min, max) - critterGetStat(obj, static_cast<Stat>(STAT_DAMAGE_THRESHOLD + damageType));
int damage = randomBetween(min, max) - critterGetStat(obj, STAT_DAMAGE_THRESHOLD + damageType);
if (damage > 0) {
damage -= critterGetStat(obj, static_cast<Stat>(STAT_DAMAGE_RESISTANCE + damageType)) * damage / 100;
damage -= critterGetStat(obj, STAT_DAMAGE_RESISTANCE + damageType) * damage / 100;
}
if (damage < 0) {
+3 -5
View File
@@ -3,8 +3,6 @@
#include "combat_defs.h"
#include "obj_types.h"
#include "proto_types.h"
#include "skill_defs.h"
namespace fallout {
@@ -15,14 +13,14 @@ int _action_use_an_item_on_object(Object* user, Object* targetObj, Object* item)
int _action_use_an_object(Object* user, Object* targetObj);
int actionPickUp(Object* critter, Object* item);
int actionLootCritter(Object* critter, Object* target);
int _action_skill_use(Skill skill);
int actionUseSkill(Object* user, Object* target, Skill skill);
int _action_skill_use(int skill);
int actionUseSkill(Object* user, Object* target, int skill);
bool _is_hit_from_front(const Object* attacker, const Object* defender);
bool _can_see(Object* source, Object* target);
bool _action_explode_running();
int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object* sourceObj, bool animate);
int actionTalk(Object* obj, Object* critter);
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, DamageType damageType, bool animated, bool bypassArmor);
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor);
bool actionCheckPush(Object* obj, Object* target);
int actionPush(Object* obj, Object* target);
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;
}
BodyType bodyType = critterGetBodyType(critter);
int bodyType = critterGetBodyType(critter);
if (bodyType != BODY_TYPE_BIPED && bodyType != BODY_TYPE_ROBOTIC) {
return false;
}
@@ -3019,7 +3019,7 @@ int _check_move(int* actionPointsPtr)
if (isInCombat()) {
if (*actionPointsPtr != -1) {
if (gPressedPhysicalKeys[SDL_SCANCODE_RCTRL] || gPressedPhysicalKeys[SDL_SCANCODE_LCTRL]) {
HitMode hitMode;
int hitMode;
bool aiming;
interfaceGetCurrentHitMode(&hitMode, &aiming);
+1 -3
View File
@@ -1789,13 +1789,11 @@ void FrmImage::resetInternal()
bool FrmImage::setFrame(const Art* art, int frame, int direction)
{
unsigned char* data = artGetFrameData(art, frame, direction, &_width, &_height, nullptr, nullptr);
unsigned char* data = artGetFrameData(art, frame, direction, &_width, &_height, &_xOffset, &_yOffset);
if (data == nullptr) {
return false;
}
_xOffset = art->xOffsets[direction];
_yOffset = art->yOffsets[direction];
_data = data;
return true;
}
+210 -239
View File
File diff suppressed because it is too large Load Diff
+9 -24
View File
@@ -67,27 +67,12 @@ namespace fallout {
#define CS_WINDOW_SECONDARY_STAT_MID_X (379)
#define CS_WINDOW_BIO_X (438)
enum PremadeCharacter : int {
typedef enum PremadeCharacter {
PREMADE_CHARACTER_NARG,
PREMADE_CHARACTER_CHITSA,
PREMADE_CHARACTER_MINGUN,
PREMADE_CHARACTER_COUNT,
PREMADE_CHARACTER_FIRST = PREMADE_CHARACTER_NARG
};
inline PremadeCharacter operator++(PremadeCharacter& e, int)
{
PremadeCharacter result = e;
e = static_cast<PremadeCharacter>(static_cast<int>(e) + 1);
return result;
}
inline PremadeCharacter operator--(PremadeCharacter& e, int)
{
PremadeCharacter result = e;
e = static_cast<PremadeCharacter>(static_cast<int>(e) - 1);
return result;
}
} PremadeCharacter;
typedef struct PremadeCharacterDescription {
char fileName[20];
@@ -106,7 +91,7 @@ static bool characterSelectorWindowFatalError(bool result);
static void premadeCharactersLocalizePath(char* path);
// 0x51C84C premade_index
static PremadeCharacter gCurrentPremadeCharacter = PREMADE_CHARACTER_NARG;
static int gCurrentPremadeCharacter = PREMADE_CHARACTER_NARG;
// 0x51C850 premade_characters
static PremadeCharacterDescription gPremadeCharacterDescriptions[PREMADE_CHARACTER_COUNT] = {
@@ -238,9 +223,9 @@ int characterSelectorOpen()
soundPlayFile("ib2p1xx1");
// FALLTHROUGH
case 500:
gCurrentPremadeCharacter--;
if (gCurrentPremadeCharacter < PREMADE_CHARACTER_FIRST) {
gCurrentPremadeCharacter--;
gCurrentPremadeCharacter -= 1;
if (gCurrentPremadeCharacter < 0) {
gCurrentPremadeCharacter = gPremadeCharacterCount - 1;
}
characterSelectorWindowRefresh();
@@ -249,9 +234,9 @@ int characterSelectorOpen()
soundPlayFile("ib2p1xx1");
// FALLTHROUGH
case 501:
gCurrentPremadeCharacter++;
gCurrentPremadeCharacter += 1;
if (gCurrentPremadeCharacter >= gPremadeCharacterCount) {
gCurrentPremadeCharacter = PREMADE_CHARACTER_FIRST;
gCurrentPremadeCharacter = 0;
}
characterSelectorWindowRefresh();
@@ -842,7 +827,7 @@ static bool characterSelectorWindowRenderStats()
y += vh; // blank line
// SKILLS
Skill skills[DEFAULT_TAGGED_SKILLS];
int skills[DEFAULT_TAGGED_SKILLS];
skillsGetTagged(skills, DEFAULT_TAGGED_SKILLS);
for (int index = 0; index < DEFAULT_TAGGED_SKILLS; index++) {
+912 -917
View File
File diff suppressed because it is too large Load Diff
+21 -21
View File
@@ -9,7 +9,7 @@
namespace fallout {
extern int _combatNumTurns;
extern CombatState gCombatState;
extern unsigned int gCombatState;
extern int _combat_free_move;
@@ -19,7 +19,7 @@ void combatExit();
int _find_cid(int a1, int a2, Object** a3, int a4);
int combatLoad(File* stream);
int combatSave(File* stream);
bool _combat_safety_invalidate_weapon(Object* attacker, Object* weapon, HitMode hitMode, Object* defender, int* safeDistancePtr);
bool _combat_safety_invalidate_weapon(Object* attacker, Object* weapon, int hitMode, Object* defender, int* safeDistancePtr);
bool _combatTestIncidentalHit(Object* attacker, Object* defender, Object* attackerFriend, Object* weapon);
Object* _combat_whose_turn();
void _combat_data_init(Object* obj);
@@ -34,19 +34,19 @@ void _combat_over_from_load();
void _combat_give_exps(int exp_points);
void _combat_turn_run();
void _combat(CombatStartData* csd);
void attackInit(Attack* attack, Object* attacker, Object* defender, HitMode hitMode, HitLocation hitLocation);
int _combat_attack(Object* attacker, Object* defender, HitMode hitMode, HitLocation hitLocation);
void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode, int hitLocation);
int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation);
int _combat_bullet_start(const Object* attacker, const Object* target);
void _compute_explosion_on_extras(Attack* attack, bool isFromAttacker, bool isGrenade, bool noDamage);
int _determine_to_hit(Object* a1, Object* a2, HitLocation hitLocation, HitMode hitMode);
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, HitLocation hitLocation, HitMode hitMode);
int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode);
int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5);
int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode);
void attackComputeDeathFlags(Attack* attack);
void _apply_damage(Attack* attack, bool animated);
void _combat_display(Attack* attack);
void _combat_anim_begin();
void _combat_anim_finished();
CombatBadShot _combat_check_bad_shot(Object* attacker, Object* defender, HitMode hitMode, bool aiming);
int _combat_check_bad_shot(Object* attacker, Object* defender, int hitMode, bool aiming);
bool _combat_to_hit(Object* target, int* accuracy);
void _combat_attack_this(Object* target);
void _combat_outline_on();
@@ -59,21 +59,21 @@ void _combat_delete_critter(Object* obj);
void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
int combatGetTargetHighlight();
int criticalsGetValue(KillType killType, HitLocation hitLocation, CriticalEffect effect, CriticalHitDataMember dataMember);
void criticalsSetValue(KillType killType, HitLocation hitLocation, CriticalEffect effect, CriticalHitDataMember dataMember, int value);
void criticalsResetValue(KillType killType, HitLocation hitLocation, CriticalEffect effect, CriticalHitDataMember dataMember);
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
bool criticalsNoTimeLimits();
int unarmedGetDamage(HitMode hitMode, int* minDamagePtr, int* maxDamagePtr);
int unarmedGetBonusCriticalChance(HitMode hitMode);
int unarmedGetActionPointCost(HitMode hitMode);
bool unarmedIsPenetrating(HitMode hitMode);
HitMode unarmedGetPunchHitMode(bool isSecondary);
HitMode unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(HitMode hitMode);
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr);
int unarmedGetBonusCriticalChance(int hitMode);
int unarmedGetActionPointCost(int hitMode);
bool unarmedIsPenetrating(int hitMode);
int unarmedGetPunchHitMode(bool isSecondary);
int unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(int hitMode);
bool damageModGetBonusHthDamageFix();
bool damageModGetDisplayBonusDamage();
int combat_get_hit_location_penalty(HitLocation hitLocation);
void combat_set_hit_location_penalty(HitLocation hitLocation, int penalty);
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();
Attack* combat_get_data();
@@ -82,7 +82,7 @@ static inline bool isInCombat()
return (gCombatState & COMBAT_STATE_IN_COMBAT) != 0;
}
static inline bool isUnarmedHitMode(HitMode hitMode)
static inline bool isUnarmedHitMode(int hitMode)
{
return hitMode == HIT_MODE_PUNCH
|| hitMode == HIT_MODE_KICK
+31 -28
View File
@@ -93,19 +93,19 @@ static Object* _ai_danger_source(Object* a1);
static bool aiHaveAmmo(Object* critter, Object* weapon, Object** ammoPtr);
static bool _caiHasWeapPrefType(AiPacket* ai, int attackType);
static Object* _ai_best_weapon(Object* a1, Object* a2, Object* a3, Object* a4);
static bool _ai_can_use_weapon(Object* critter, Object* weapon, HitMode hitMode);
static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode);
static bool aiCanUseItem(Object* obj, Object* a2);
static Object* _ai_search_environ(Object* critter, int itemType);
static Object* _ai_retrieve_object(Object* critter, Object* item);
static HitMode _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender);
static int _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_closer(Object* a1, Object* a2, bool taunt);
static int _cai_retargetTileFromFriendlyFire(Object* source, Object* target, int* tilePtr);
static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetData, int tile);
static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance);
static int _ai_switch_weapons(Object* a1, HitMode* hitMode, Object** weapon, Object* a4);
static HitLocation _ai_called_shot(Object* attacker, Object* defender, HitMode hitMode);
static int _ai_attack(Object* attacker, Object* defender, HitMode hitMode);
static int _ai_switch_weapons(Object* a1, int* hitMode, Object** weapon, Object* a4);
static int _ai_called_shot(Object* attacker, Object* defender, int hitMode);
static int _ai_attack(Object* attacker, Object* defender, int hitMode);
static int _ai_try_attack(Object* a1, Object* a2);
static int _cai_get_min_hp(AiPacket* ai);
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.end)) == -1) return -1;
for (int index = HIT_LOCATION_FIRST; index < HIT_LOCATION_SPECIFIC_COUNT; index++) {
for (int index = 0; index < HIT_LOCATION_SPECIFIC_COUNT; index++) {
AiMessageRange* range = &(ai->hit[index]);
if (fileReadInt32(stream, &(range->start)) == -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.end) == -1) return -1;
for (int index = HIT_LOCATION_FIRST; index < HIT_LOCATION_SPECIFIC_COUNT; index++) {
for (int index = 0; index < HIT_LOCATION_SPECIFIC_COUNT; index++) {
AiMessageRange* range = &(ai->hit[index]);
if (fileWriteInt32(stream, range->start) == -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
// range are ok results, since we can reload weapon
// move closer if necessary.
CombatBadShot badShot = _combat_check_bad_shot(a1, critter, HIT_MODE_RIGHT_WEAPON_PRIMARY, false);
int badShot = _combat_check_bad_shot(a1, critter, HIT_MODE_RIGHT_WEAPON_PRIMARY, false);
if (badShot != COMBAT_BAD_SHOT_OK
&& badShot != COMBAT_BAD_SHOT_NO_AMMO
&& badShot != COMBAT_BAD_SHOT_OUT_OF_RANGE) {
@@ -1975,13 +1975,13 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon
}
// 0x4298EC
static bool _ai_can_use_weapon(Object* critter, Object* weapon, HitMode hitMode)
static bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode)
{
bool result = critterCanUseWeapon(critter, weapon, hitMode);
AiPacket* ai = aiGetPacket(critter);
if (result) {
Skill skill = weaponGetSkillForHitMode(weapon, hitMode);
int skill = weaponGetSkillForHitMode(weapon, hitMode);
if (skillGetValue(critter, skill) < ai->min_to_hit) {
result = false;
}
@@ -1998,7 +1998,7 @@ static bool _ai_can_use_weapon(Object* critter, Object* weapon, HitMode hitMode)
// 0x4299A0
Object* _ai_search_inven_weap(Object* critter, bool checkRequiredActionPoints, Object* defender)
{
BodyType bodyType = critterGetBodyType(critter);
int bodyType = critterGetBodyType(critter);
if (bodyType != BODY_TYPE_BIPED
&& bodyType != BODY_TYPE_ROBOTIC
&& critter->pid != PROTO_ID_GORIS) {
@@ -2062,7 +2062,7 @@ Object* _ai_search_inven_armor(Object* critter)
if (armor != nullptr) {
armorScore = armorGetArmorClass(armor);
for (DamageType damageType = DAMAGE_TYPE_FIRST; damageType < DAMAGE_TYPE_COUNT; damageType++) {
for (int damageType = 0; damageType < DAMAGE_TYPE_COUNT; damageType++) {
armorScore += armorGetDamageResistance(armor, damageType);
armorScore += armorGetDamageThreshold(armor, damageType);
}
@@ -2081,7 +2081,7 @@ Object* _ai_search_inven_armor(Object* critter)
if (armor != candidate) {
int candidateScore = armorGetArmorClass(candidate);
for (DamageType damageType = DAMAGE_TYPE_FIRST; damageType < DAMAGE_TYPE_COUNT; damageType++) {
for (int damageType = 0; damageType < DAMAGE_TYPE_COUNT; damageType++) {
candidateScore += armorGetDamageResistance(candidate, damageType);
candidateScore += armorGetDamageThreshold(candidate, damageType);
}
@@ -2128,7 +2128,7 @@ static bool aiCanUseItem(Object* critter, Object* item)
return false;
}
KillType killType = critterGetKillType(critter);
int killType = critterGetKillType(critter);
if (killType != KILL_TYPE_MAN
&& killType != KILL_TYPE_WOMAN
&& killType != KILL_TYPE_SUPER_MUTANT
@@ -2260,7 +2260,7 @@ static Object* _ai_retrieve_object(Object* critter, Object* item)
}
// 0x429DB4
static HitMode _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender)
static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender)
{
if (weapon == nullptr) {
return HIT_MODE_PUNCH;
@@ -2302,19 +2302,19 @@ static HitMode _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defen
break;
case AREA_ATTACK_MODE_BE_SURE:
if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 85
&& !_combat_safety_invalidate_weapon(attacker, weapon, HIT_MODE_RIGHT_WEAPON_SECONDARY, defender, nullptr)) {
&& !_combat_safety_invalidate_weapon(attacker, weapon, 3, defender, nullptr)) {
useSecondaryMode = true;
}
break;
case AREA_ATTACK_MODE_BE_CAREFUL:
if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 50
&& !_combat_safety_invalidate_weapon(attacker, weapon, HIT_MODE_RIGHT_WEAPON_SECONDARY, defender, nullptr)) {
&& !_combat_safety_invalidate_weapon(attacker, weapon, 3, defender, nullptr)) {
useSecondaryMode = true;
}
break;
case AREA_ATTACK_MODE_BE_ABSOLUTELY_SURE:
if (_determine_to_hit(attacker, defender, HIT_LOCATION_TORSO, HIT_MODE_RIGHT_WEAPON_SECONDARY) >= 95
&& !_combat_safety_invalidate_weapon(attacker, weapon, HIT_MODE_RIGHT_WEAPON_SECONDARY, defender, nullptr)) {
&& !_combat_safety_invalidate_weapon(attacker, weapon, 3, defender, nullptr)) {
useSecondaryMode = true;
}
break;
@@ -2574,7 +2574,7 @@ static int _cai_retargetTileFromFriendlyFireSubFunc(AiRetargetData* aiRetargetDa
// 0x42A518
static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object* attackerFriend, int tile, int* distance)
{
HitMode hitMode = HIT_MODE_RIGHT_WEAPON_PRIMARY;
int hitMode = HIT_MODE_RIGHT_WEAPON_PRIMARY;
bool aiming = false;
if (attacker == gDude) {
interfaceGetCurrentHitMode(&hitMode, &aiming);
@@ -2601,7 +2601,7 @@ static bool _cai_attackWouldIntersect(Object* attacker, Object* defender, Object
}
// 0x42A5B8
static int _ai_switch_weapons(Object* attacker, HitMode* hitMode, Object** weapon, Object* defender)
static int _ai_switch_weapons(Object* attacker, int* hitMode, Object** weapon, Object* defender)
{
*weapon = nullptr;
*hitMode = HIT_MODE_PUNCH;
@@ -2639,9 +2639,9 @@ static int _ai_switch_weapons(Object* attacker, HitMode* hitMode, Object** weapo
}
// 0x42A670
static HitLocation _ai_called_shot(Object* attacker, Object* defender, HitMode hitMode)
static int _ai_called_shot(Object* attacker, Object* defender, int hitMode)
{
HitLocation hitLocation = HIT_LOCATION_TORSO;
int hitLocation = HIT_LOCATION_TORSO;
if (weaponGetActionPointCost(attacker, hitMode, true) <= attacker->data.critter.combat.ap) {
if (critterCanAim(attacker, hitMode)) {
@@ -2661,8 +2661,8 @@ static HitLocation _ai_called_shot(Object* attacker, Object* defender, HitMode h
}
if (critterGetStat(attacker, STAT_INTELLIGENCE) >= intelligenceRequired) {
hitLocation = static_cast<HitLocation>(randomBetween(HIT_LOCATION_FIRST, HIT_LOCATION_SPECIFIC_COUNT));
int chanceToHit = _determine_to_hit(attacker, defender, hitLocation, hitMode);
hitLocation = randomBetween(0, HIT_LOCATION_SPECIFIC_COUNT);
int chanceToHit = _determine_to_hit(attacker, defender, hitMode, hitLocation);
if (chanceToHit < ai->min_to_hit) {
hitLocation = HIT_LOCATION_TORSO;
}
@@ -2675,7 +2675,7 @@ static HitLocation _ai_called_shot(Object* attacker, Object* defender, HitMode h
}
// 0x42A748
static int _ai_attack(Object* attacker, Object* defender, HitMode hitMode)
static int _ai_attack(Object* attacker, Object* defender, int hitMode)
{
if (attacker->data.critter.combat.maneuver & CRITTER_MANUEVER_FLEEING) {
return -1;
@@ -2686,7 +2686,7 @@ static int _ai_attack(Object* attacker, Object* defender, HitMode hitMode)
reg_anim_end();
_combat_turn_run();
HitLocation hitLocation = _ai_called_shot(attacker, defender, hitMode);
int hitLocation = _ai_called_shot(attacker, defender, hitMode);
if (_combat_attack(attacker, defender, hitMode, hitLocation)) {
return -1;
}
@@ -2709,7 +2709,7 @@ static int _ai_try_attack(Object* attacker, Object* defender)
weapon = nullptr;
}
HitMode hitMode = _ai_pick_hit_mode(attacker, weapon, defender);
int hitMode = _ai_pick_hit_mode(attacker, weapon, defender);
int minToHit = aiGetPacket(attacker)->min_to_hit;
int actionPoints = attacker->data.critter.combat.ap;
@@ -2736,7 +2736,7 @@ static int _ai_try_attack(Object* attacker, Object* defender)
break;
}
CombatBadShot reason = _combat_check_bad_shot(attacker, defender, hitMode, false);
int reason = _combat_check_bad_shot(attacker, defender, hitMode, false);
if (reason == COMBAT_BAD_SHOT_NO_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.
@@ -2821,6 +2821,9 @@ 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) {
// 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) {
return -1;
}
-1
View File
@@ -6,7 +6,6 @@
#include "db.h"
#include "obj_types.h"
#include "sfall_script_hooks.h"
#include "skill_defs.h"
namespace fallout {
+35 -121
View File
@@ -2,70 +2,23 @@
#define COMBAT_DEFS_H
#include "obj_types.h"
#include "stat_defs.h"
#define EXPLOSION_TARGET_COUNT (6)
#define CRTICIAL_EFFECT_COUNT (6)
#define WEAPON_CRITICAL_FAILURE_TYPE_COUNT (7)
#define WEAPON_CRITICAL_FAILURE_EFFECT_COUNT (5)
namespace fallout {
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,
typedef enum CombatState {
COMBAT_STATE_IN_COMBAT = 0x01,
COMBAT_STATE_PLAYER_TURN = 0x02,
COMBAT_STATE_EXIT_REQUESTED = 0x08,
};
} CombatState;
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,
typedef enum HitMode {
HIT_MODE_LEFT_WEAPON_PRIMARY = 0,
HIT_MODE_LEFT_WEAPON_SECONDARY = 1,
HIT_MODE_RIGHT_WEAPON_PRIMARY = 2,
@@ -117,47 +70,21 @@ enum HitMode : int {
LAST_ADVANCED_KICK_HIT_MODE = HIT_MODE_PIERCING_KICK,
FIRST_ADVANCED_UNARMED_HIT_MODE = FIRST_ADVANCED_PUNCH_HIT_MODE,
LAST_ADVANCED_UNARMED_HIT_MODE = LAST_ADVANCED_KICK_HIT_MODE,
HIT_MODE_FIRST = HIT_MODE_LEFT_WEAPON_PRIMARY,
};
} HitMode;
inline static bool hitModeIsValid(int hitMode)
{
return hitMode >= HIT_MODE_FIRST && hitMode < HIT_MODE_COUNT;
}
inline HitMode operator++(HitMode& e, int)
{
HitMode result = e;
e = static_cast<HitMode>(static_cast<int>(e) + 1);
return result;
}
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,
typedef enum HitLocation {
HIT_LOCATION_HEAD,
HIT_LOCATION_LEFT_ARM,
HIT_LOCATION_RIGHT_ARM,
HIT_LOCATION_TORSO,
HIT_LOCATION_RIGHT_LEG,
HIT_LOCATION_LEFT_LEG,
HIT_LOCATION_EYES,
HIT_LOCATION_GROIN,
HIT_LOCATION_UNCALLED,
HIT_LOCATION_COUNT,
HIT_LOCATION_SPECIFIC_COUNT = HIT_LOCATION_COUNT - 1,
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;
}
} HitLocation;
typedef struct CombatStartData {
Object* attacker;
@@ -174,16 +101,16 @@ typedef struct CombatStartData {
typedef struct Attack {
Object* attacker;
HitMode hitMode;
int hitMode;
Object* weapon;
HitLocation attackHitLocation; // UNUSED?
int attackHitLocation; // UNUSED?
int attackerDamage;
int attackerFlags;
int ammoQuantity;
int criticalMessageId;
Object* defender;
int tile;
HitLocation defenderHitLocation;
int defenderHitLocation;
int defenderDamage;
int defenderFlags;
int defenderKnockback;
@@ -196,29 +123,16 @@ typedef struct Attack {
int extrasKnockback[EXPLOSION_TARGET_COUNT];
} Attack;
enum CriticalHitDataMember : int {
CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER = 0,
CRIT_DATA_MEMBER_FLAGS = 1,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT = 2,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER = 3,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS = 4,
CRIT_DATA_MEMBER_MESSAGE_ID = 5,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID = 6,
CRIT_DATA_MEMBER_COUNT = 7,
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;
}
typedef enum CriticalHitDescriptionDataMember {
CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER,
CRIT_DATA_MEMBER_FLAGS,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS,
CRIT_DATA_MEMBER_MESSAGE_ID,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID,
CRIT_DATA_MEMBER_COUNT,
} CriticalHitDescriptionDataMember;
// Provides metadata about critical hit effect.
typedef union CriticalHitDescription {
@@ -230,7 +144,7 @@ typedef union CriticalHitDescription {
// Stat to check to upgrade this critical hit to massive critical hit or
// -1 if there is no massive critical hit.
Stat massiveCriticalStat;
int massiveCriticalStat;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
@@ -246,7 +160,7 @@ typedef union CriticalHitDescription {
int values[CRIT_DATA_MEMBER_COUNT];
} CriticalHitDescription;
enum CombatBadShot : int {
typedef enum CombatBadShot {
COMBAT_BAD_SHOT_OK = 0,
COMBAT_BAD_SHOT_NO_AMMO = 1,
COMBAT_BAD_SHOT_OUT_OF_RANGE = 2,
@@ -254,8 +168,8 @@ enum CombatBadShot : int {
COMBAT_BAD_SHOT_ALREADY_DEAD = 4,
COMBAT_BAD_SHOT_AIM_BLOCKED = 5,
COMBAT_BAD_SHOT_ARM_CRIPPLED = 6,
COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED = 7
};
COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED = 7,
} CombatBadShot;
} // namespace fallout
-1
View File
@@ -8,7 +8,6 @@ namespace fallout {
#define CONTENT_CONFIG_START_SECTION "start"
#define CONTENT_CONFIG_KARMA_SECTION "karma"
#define CONTENT_CONFIG_ITEMS_SECTION "items"
#define CONTENT_CONFIG_MAPS_SECTION "maps"
#define CONTENT_CONFIG_DIALOG_SECTION "dialog"
#define CONTENT_CONFIG_MAIN_MENU_SECTION "main_menu"
#define CONTENT_CONFIG_MOVIES_SECTION "movies"
+45 -37
View File
@@ -80,7 +80,7 @@ static int _critterClearObjDrugs(Object* obj, void* data);
static char _aCorpse[] = "corpse";
// 0x501494
static char empty[] = "";
static char byte_501494[] = "";
// 0x51833C name_critter
static char* _name_critter = _aCorpse;
@@ -107,7 +107,7 @@ static const int gRadiationEnduranceModifiers[RADIATION_LEVEL_COUNT] = {
// [RADIATION_EFFECT_PRIMARY_STAT_COUNT] for more info.
//
// 0x518358 rad_stat
static const Stat gRadiationEffectStats[RADIATION_EFFECT_COUNT] = {
static const int gRadiationEffectStats[RADIATION_EFFECT_COUNT] = {
STAT_STRENGTH,
STAT_PERCEPTION,
STAT_ENDURANCE,
@@ -152,7 +152,7 @@ static char gDudeName[DUDE_NAME_MAX_LENGTH];
static int _sneak_working;
// 0x56D780 pc_kill_counts
static int gKillsByType[KILL_TYPE_DEFAULT_COUNT];
static int gKillsByType[KILL_TYPE_COUNT];
// Something with radiation.
//
@@ -679,15 +679,15 @@ int radiationEventWrite(File* stream, void* data)
}
// 0x42D82C critter_get_base_damage_type
DamageType critterGetDamageType(Object* obj)
int critterGetDamageType(Object* obj)
{
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return DAMAGE_TYPE_NORMAL;
return 0;
}
Proto* proto;
if (protoGetProto(obj->pid, &proto) == -1) {
return DAMAGE_TYPE_NORMAL;
return 0;
}
return proto->critter.data.damageType;
@@ -703,9 +703,9 @@ static int critter_kill_count_clear()
}
// 0x42D878 critter_kill_count_inc
int killsIncByType(KillType killType)
int killsIncByType(int killType)
{
if (killTypeIsValid(killType)) {
if (killType != -1 && killType < KILL_TYPE_COUNT) {
gKillsByType[killType]++;
return 0;
}
@@ -714,9 +714,9 @@ int killsIncByType(KillType killType)
}
// 0x42D8A8 critter_kill_count
int killsGetByType(KillType killType)
int killsGetByType(int killType)
{
if (killTypeIsValid(killType)) {
if (killType != -1 && killType < KILL_TYPE_COUNT) {
return gKillsByType[killType];
}
@@ -726,7 +726,7 @@ int killsGetByType(KillType killType)
// 0x42D8C0 critter_kill_count_load
int killsLoad(File* stream)
{
if (fileReadInt32List(stream, gKillsByType, KILL_TYPE_DEFAULT_COUNT) == -1) {
if (fileReadInt32List(stream, gKillsByType, KILL_TYPE_COUNT) == -1) {
fileClose(stream);
return -1;
}
@@ -737,7 +737,7 @@ int killsLoad(File* stream)
// 0x42D8F0 critter_kill_count_save
int killsSave(File* stream)
{
if (fileWriteInt32List(stream, gKillsByType, KILL_TYPE_DEFAULT_COUNT) == -1) {
if (fileWriteInt32List(stream, gKillsByType, KILL_TYPE_COUNT) == -1) {
fileClose(stream);
return -1;
}
@@ -746,7 +746,7 @@ int killsSave(File* stream)
}
// 0x42D920 critter_kill_count_type
KillType critterGetKillType(Object* obj)
int critterGetKillType(Object* obj)
{
if (obj == gDude) {
int gender = critterGetStat(obj, STAT_GENDER);
@@ -757,7 +757,7 @@ KillType critterGetKillType(Object* obj)
}
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return KILL_TYPE_INVALID;
return -1;
}
Proto* proto;
@@ -767,25 +767,33 @@ KillType critterGetKillType(Object* obj)
}
// 0x42D974 critter_kill_name
char* killTypeGetName(KillType killType)
char* killTypeGetName(int killType)
{
if (killTypeIsValid(killType)) {
MessageListItem messageListItem;
return getmsg(&gProtoMessageList, &messageListItem, 1450 + killType);
if (killType != -1 && killType < KILL_TYPE_COUNT) {
if (killType >= 0 && killType < KILL_TYPE_COUNT) {
MessageListItem messageListItem;
return getmsg(&gProtoMessageList, &messageListItem, 1450 + killType);
} else {
return nullptr;
}
} else {
return byte_501494;
}
return empty;
}
// 0x42D9B4 critter_kill_info
char* killTypeGetDescription(KillType killType)
char* killTypeGetDescription(int killType)
{
if (killTypeIsValid(killType)) {
MessageListItem messageListItem;
return getmsg(&gProtoMessageList, &messageListItem, 1469 + killType);
if (killType != -1 && killType < KILL_TYPE_COUNT) {
if (killType >= 0 && killType < KILL_TYPE_COUNT) {
MessageListItem messageListItem;
return getmsg(&gProtoMessageList, &messageListItem, 1469 + killType);
} else {
return nullptr;
}
} else {
return byte_501494;
}
return empty;
}
// 0x42D9F4 critter_heal_hours
@@ -1000,15 +1008,15 @@ bool critterIsProne(Object* critter)
// critter_body_type
// 0x42DDC4 critter_body_type
BodyType critterGetBodyType(Object* critter)
int critterGetBodyType(Object* critter)
{
if (critter == nullptr) {
debugPrint("\nError: critter_body_type: pobj was NULL!");
return BODY_TYPE_BIPED;
return 0;
}
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return BODY_TYPE_BIPED;
return 0;
}
Proto* proto;
@@ -1016,7 +1024,7 @@ BodyType critterGetBodyType(Object* critter)
return proto->critter.data.bodyType;
}
bool critterCanUseWeapon(Object* critter, Object* weapon, HitMode hitMode)
bool critterCanUseWeapon(Object* critter, Object* weapon, int hitMode)
{
if (critter == nullptr || weapon == nullptr || itemGetType(weapon) != ITEM_TYPE_WEAPON) {
return false;
@@ -1083,9 +1091,9 @@ int gcdLoad(const char* path)
}
proto->critter.data.baseStats[STAT_DAMAGE_RESISTANCE_EMP] = 100;
proto->critter.data.bodyType = BODY_TYPE_BIPED;
proto->critter.data.bodyType = 0;
proto->critter.data.experience = 0;
proto->critter.data.killType = KILL_TYPE_MAN;
proto->critter.data.killType = 0;
fileClose(stream);
return 0;
@@ -1097,10 +1105,10 @@ int protoCritterDataRead(File* stream, CritterProtoData* critterData)
if (fileReadInt32(stream, &(critterData->flags)) == -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, reinterpret_cast<int*>(critterData->skills), SKILL_COUNT) == -1) return -1;
if (fileReadInt32(stream, reinterpret_cast<int*>(&(critterData->bodyType))) == -1) return -1;
if (fileReadInt32List(stream, critterData->skills, SKILL_COUNT) == -1) return -1;
if (fileReadInt32(stream, &(critterData->bodyType)) == -1) return -1;
if (fileReadInt32(stream, &(critterData->experience)) == -1) return -1;
if (fileReadInt32(stream, reinterpret_cast<int*>(&(critterData->killType))) == -1) return -1;
if (fileReadInt32(stream, &(critterData->killType)) == -1) return -1;
// 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.
@@ -1114,7 +1122,7 @@ int protoCritterDataRead(File* stream, CritterProtoData* critterData)
//
// Regardless of the reason, damage type is considered optional by original
// code as seen at 0x42E01B.
if (fileReadInt32(stream, reinterpret_cast<int*>(&(critterData->damageType))) == -1) {
if (fileReadInt32(stream, &(critterData->damageType)) == -1) {
critterData->damageType = DAMAGE_TYPE_NORMAL;
}
@@ -1164,7 +1172,7 @@ int protoCritterDataWrite(File* stream, CritterProtoData* critterData)
if (fileWriteInt32(stream, critterData->flags) == -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, reinterpret_cast<int*>(critterData->skills), SKILL_COUNT) == -1) return -1;
if (fileWriteInt32List(stream, critterData->skills, SKILL_COUNT) == -1) return -1;
if (fileWriteInt32(stream, critterData->bodyType) == -1) return -1;
if (fileWriteInt32(stream, critterData->experience) == -1) return -1;
if (fileWriteInt32(stream, critterData->killType) == -1) return -1;
+8 -9
View File
@@ -1,7 +1,6 @@
#ifndef CRITTER_H
#define CRITTER_H
#include "combat_defs.h"
#include "db.h"
#include "obj_types.h"
#include "proto_types.h"
@@ -39,14 +38,14 @@ void radiationProcess(Object* obj, int radiationLevel, bool direction);
int radiationEventProcess(Object* obj, void* data);
int radiationEventRead(File* stream, void** dataPtr);
int radiationEventWrite(File* stream, void* data);
DamageType critterGetDamageType(Object* critter);
int killsIncByType(KillType killType);
int killsGetByType(KillType killType);
int critterGetDamageType(Object* critter);
int killsIncByType(int killType);
int killsGetByType(int killType);
int killsLoad(File* stream);
int killsSave(File* stream);
KillType critterGetKillType(Object* critter);
char* killTypeGetName(KillType killType);
char* killTypeGetDescription(KillType killType);
int critterGetKillType(Object* critter);
char* killTypeGetName(int killType);
char* killTypeGetDescription(int killType);
int critterHealByHours(Object* obj, int hours);
void critterKill(Object* critter, int anim, bool refreshRect);
int critterGetExp(Object* critter);
@@ -54,10 +53,10 @@ bool critterIsActive(Object* critter);
bool critterIsDead(Object* critter);
bool critterIsCrippled(Object* critter);
bool critterIsProne(Object* critter);
BodyType critterGetBodyType(Object* critter);
int critterGetBodyType(Object* critter);
// Checks physical/art capability only. Callers that expose weapon usability
// decisions must still call scriptHooks_CanUseWeapon with the final result.
bool critterCanUseWeapon(Object* critter, Object* weapon, HitMode hitMode);
bool critterCanUseWeapon(Object* critter, Object* weapon, int hitMode);
int critterBuildGorisFid(Object* critter, int frmId);
int gcdLoad(const char* path);
int protoCritterDataRead(File* stream, CritterProtoData* critterData);
+57 -6
View File
@@ -7,6 +7,7 @@
#include "content_config.h"
#include "debug.h"
#include "game_config.h"
#include "game_movie.h"
#include "platform_compat.h"
#include "settings.h"
#include "sfall_config.h"
@@ -175,17 +176,11 @@ namespace {
constexpr SfallMigrationEntry kSfallMigrationEntries[] = {
// [start]
{ 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, "MaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_male_default", "hmjmps" },
{ kSfallMisc, "FemaleStartModel", CONTENT_CONFIG_START_SECTION, "model_female", "hfprim" },
{ kSfallMisc, "FemaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_female_default", "hfjmps" },
{ kSfallMisc, "PipBoyAvailableAtGameStart", CONTENT_CONFIG_START_SECTION, "pipboy", "0" },
// [maps]
{ kSfallMisc, "DisableSpecialMapIDs", CONTENT_CONFIG_MAPS_SECTION, "disable_special_map_ids", "0" },
// [karma]
{ kSfallMisc, "KarmaFRMs", CONTENT_CONFIG_KARMA_SECTION, "frms" },
{ kSfallMisc, "KarmaPoints", CONTENT_CONFIG_KARMA_SECTION, "points" },
@@ -243,6 +238,58 @@ namespace {
{ kSfallMisc, "ExtraGameMsgFileList", CONTENT_CONFIG_TEXT_SECTION, "extra_msg_file_list" },
};
constexpr const char* kSfallDefaultMovies[MOVIE_COUNT] = {
"iplogo.mve",
"intro.mve",
"elder.mve",
"vsuit.mve",
"afailed.mve",
"adestroy.mve",
"car.mve",
"cartucci.mve",
"timeout.mve",
"tanker.mve",
"enclave.mve",
"derrick.mve",
"artimer1.mve",
"artimer2.mve",
"artimer3.mve",
"artimer4.mve",
"credits.mve",
};
static bool contentConfigMigrateSfallMovieOverrides(Config* sfallConfig, Config* migratedConfig)
{
assert(sfallConfig != nullptr && migratedConfig != nullptr);
bool migrated = false;
for (int index = 0; index < GAME_MOVIE_MAX_COUNT; index++) {
char sfallKey[16];
snprintf(sfallKey, sizeof(sfallKey), "Movie%d", index + 1);
char* value;
if (!configGetString(sfallConfig, kSfallMisc, sfallKey, &value) || value[0] == '\0') {
continue;
}
if (index < MOVIE_COUNT && strcmp(value, kSfallDefaultMovies[index]) == 0) {
continue;
}
char targetKey[16];
snprintf(targetKey, sizeof(targetKey), "movie%d", index + 1);
if (gameConfigHasKey(migratedConfig, CONTENT_CONFIG_MOVIES_SECTION, targetKey)) {
continue;
}
configSetString(migratedConfig, CONTENT_CONFIG_MOVIES_SECTION, targetKey, value);
migrated = true;
}
return migrated;
}
} // anonymous namespace
// Migrate sfall settings from ddraw.ini to game.cfg.
@@ -289,6 +336,10 @@ static bool contentConfigMigrateFromSfall(Config* sfallConfig, const char* conte
}
}
if (contentConfigMigrateSfallMovieOverrides(sfallConfig, &migratedConfig)) {
migrated = true;
}
if (migrated) {
// Ensure all directory components exist before writing.
char drive[COMPAT_MAX_DRIVE];
+1 -6
View File
@@ -3500,11 +3500,6 @@ int gameDialogGetWindow()
return windowGetWindow(gGameDialogWindow) != nullptr ? gGameDialogWindow : -1;
}
int gameDialogGetBackgroundWindow()
{
return windowGetWindow(gGameDialogBackgroundWindow) != nullptr ? gGameDialogBackgroundWindow : -1;
}
// 0x448660 gdialog_barter_cleanup_tables
void gameDialogBarterCleanupTables()
{
@@ -3789,7 +3784,7 @@ void partyMemberControlWindowUpdate()
fontDrawText(windowBuffer + windowWidth * 96 + 240, formattedText, 115, windowWidth, COLOR_GREEN);
// Render best skill.
Skill bestSkill = partyMemberGetBestSkill(gGameDialogSpeaker);
int bestSkill = partyMemberGetBestSkill(gGameDialogSpeaker);
text = skillGetName(bestSkill);
snprintf(formattedText, sizeof(formattedText), "%s", text);
fontDrawText(windowBuffer + windowWidth * 113 + 240, formattedText, 115, windowWidth, COLOR_GREEN);

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