Code readability: _determine_to_hit, sfxBuildWeaponName

This commit is contained in:
phobos2077 2023-07-06 23:41:04 +02:00
parent cd4bf88fcf
commit b18b2471c3
5 changed files with 200 additions and 203 deletions

View File

@ -1711,6 +1711,7 @@ int _make_path(Object* object, int from, int to, unsigned char* rotations, int a
return pathfinderFindPath(object, from, to, rotations, a5, _obj_blocking_at); return pathfinderFindPath(object, from, to, rotations, a5, _obj_blocking_at);
} }
// TODO: move pathfinding into another unit
// 0x415EFC // 0x415EFC
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback) int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback)
{ {

View File

@ -116,7 +116,7 @@ static int attackComputeCriticalHit(Attack* a1);
static int _attackFindInvalidFlags(Object* a1, Object* a2); static int _attackFindInvalidFlags(Object* a1, Object* a2);
static int attackComputeCriticalFailure(Attack* attack); static int attackComputeCriticalFailure(Attack* attack);
static void _do_random_cripple(int* flagsPtr); static void _do_random_cripple(int* flagsPtr);
static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool a6); static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool useDistance);
static void attackComputeDamage(Attack* attack, int ammoQuantity, int a3); static void attackComputeDamage(Attack* attack, int ammoQuantity, int a3);
static void _check_for_death(Object* a1, int a2, int* a3); static void _check_for_death(Object* a1, int a2, int* a3);
static void _set_new_results(Object* a1, int a2); static void _set_new_results(Object* a1, int a2);
@ -3476,16 +3476,16 @@ void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode,
} }
// 0x422F3C // 0x422F3C
int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation) int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation)
{ {
if (a1 != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) { if (attacker != gDude && hitMode == HIT_MODE_PUNCH && randomBetween(1, 4) == 1) {
int fid = buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_KICK_LEG, (a1->fid & 0xF000) >> 12, (a1->fid & 0x70000000) >> 28); int fid = buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_KICK_LEG, (attacker->fid & 0xF000) >> 12, (attacker->fid & 0x70000000) >> 28);
if (artExists(fid)) { if (artExists(fid)) {
hitMode = HIT_MODE_KICK; hitMode = HIT_MODE_KICK;
} }
} }
attackInit(&_main_ctd, a1, a2, hitMode, hitLocation); attackInit(&_main_ctd, attacker, defender, hitMode, hitLocation);
debugPrint("computing attack...\n"); debugPrint("computing attack...\n");
if (attackCompute(&_main_ctd) == -1) { if (attackCompute(&_main_ctd) == -1) {
@ -3513,7 +3513,7 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
bool aiming; bool aiming;
if (_main_ctd.defenderHitLocation == HIT_LOCATION_TORSO || _main_ctd.defenderHitLocation == HIT_LOCATION_UNCALLED) { if (_main_ctd.defenderHitLocation == HIT_LOCATION_TORSO || _main_ctd.defenderHitLocation == HIT_LOCATION_UNCALLED) {
if (a1 == gDude) { if (attacker == gDude) {
interfaceGetCurrentHitMode(&hitMode, &aiming); interfaceGetCurrentHitMode(&hitMode, &aiming);
} else { } else {
aiming = false; aiming = false;
@ -3522,22 +3522,22 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
aiming = true; aiming = true;
} }
int actionPoints = weaponGetActionPointCost(a1, _main_ctd.hitMode, aiming); int actionPoints = weaponGetActionPointCost(attacker, _main_ctd.hitMode, aiming);
debugPrint("sequencing attack...\n"); debugPrint("sequencing attack...\n");
if (_action_attack(&_main_ctd) == -1) { if (_action_attack(&_main_ctd) == -1) {
return -1; return -1;
} }
if (actionPoints > a1->data.critter.combat.ap) { if (actionPoints > attacker->data.critter.combat.ap) {
a1->data.critter.combat.ap = 0; attacker->data.critter.combat.ap = 0;
} else { } else {
a1->data.critter.combat.ap -= actionPoints; attacker->data.critter.combat.ap -= actionPoints;
} }
if (a1 == gDude) { if (attacker == gDude) {
interfaceRenderActionPoints(a1->data.critter.combat.ap, _combat_free_move); interfaceRenderActionPoints(attacker->data.critter.combat.ap, _combat_free_move);
_critter_set_who_hit_me(a1, a2); _critter_set_who_hit_me(attacker, defender);
} }
// SFALL // SFALL
@ -3545,19 +3545,19 @@ int _combat_attack(Object* a1, Object* a2, int hitMode, int hitLocation)
_combat_call_display = 1; _combat_call_display = 1;
_combat_cleanup_enabled = 1; _combat_cleanup_enabled = 1;
aiInfoSetLastTarget(a1, a2); aiInfoSetLastTarget(attacker, defender);
debugPrint("running attack...\n"); debugPrint("running attack...\n");
return 0; return 0;
} }
// Returns tile one step closer from [a1] to [a2] // Returns tile one step closer from [attacker] to [target]
// //
// 0x423104 // 0x423104
int _combat_bullet_start(const Object* a1, const Object* a2) int _combat_bullet_start(const Object* attacker, const Object* target)
{ {
int rotation = tileGetRotationTo(a1->tile, a2->tile); int rotation = tileGetRotationTo(attacker->tile, target->tile);
return tileGetTileInDirection(a1->tile, rotation, 1); return tileGetTileInDirection(attacker->tile, rotation, 1);
} }
// 0x423128 // 0x423128
@ -4282,26 +4282,26 @@ static void _do_random_cripple(int* flagsPtr)
} }
// 0x42436C // 0x42436C
int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode) int _determine_to_hit(Object* attacker, Object* defender, int hitLocation, int hitMode)
{ {
return attackDetermineToHit(a1, a1->tile, a2, hitLocation, hitMode, true); return attackDetermineToHit(attacker, attacker->tile, defender, hitLocation, hitMode, true);
} }
// 0x424380 // 0x424380
int _determine_to_hit_no_range(Object* a1, Object* a2, int hitLocation, int hitMode, unsigned char* a5) int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5)
{ {
return attackDetermineToHit(a1, a1->tile, a2, hitLocation, hitMode, false); return attackDetermineToHit(attacker, attacker->tile, defender, hitLocation, hitMode, false);
} }
// 0x424394 // 0x424394
int _determine_to_hit_from_tile(Object* a1, int tile, Object* a3, int hitLocation, int hitMode) int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode)
{ {
return attackDetermineToHit(a1, tile, a3, hitLocation, hitMode, true); return attackDetermineToHit(attacker, tile, defender, hitLocation, hitMode, true);
} }
// determine_to_hit // determine_to_hit
// 0x4243A8 // 0x4243A8
static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool a6) static int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, bool useDistance)
{ {
Object* weapon = critterGetWeaponForHitMode(attacker, hitMode); Object* weapon = critterGetWeaponForHitMode(attacker, hitMode);
@ -4311,32 +4311,30 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
bool isRangedWeapon = false; bool isRangedWeapon = false;
int accuracy; int toHit;
if (weapon == NULL || isUnarmedHitMode(hitMode)) { if (weapon == NULL || isUnarmedHitMode(hitMode)) {
accuracy = skillGetValue(attacker, SKILL_UNARMED); toHit = skillGetValue(attacker, SKILL_UNARMED);
} else { } else {
accuracy = weaponGetSkillValue(attacker, hitMode); toHit = weaponGetSkillValue(attacker, hitMode);
int modifier = 0;
int attackType = weaponGetAttackTypeForHitMode(weapon, hitMode); int attackType = weaponGetAttackTypeForHitMode(weapon, hitMode);
if (attackType == ATTACK_TYPE_RANGED || attackType == ATTACK_TYPE_THROW) { if (attackType == ATTACK_TYPE_RANGED || attackType == ATTACK_TYPE_THROW) {
isRangedWeapon = true; isRangedWeapon = true;
int v29 = 0; int perceptionBonusMult = 0;
int v25 = 0; int minEffectiveDist = 0;
int weaponPerk = weaponGetPerk(weapon); int weaponPerk = weaponGetPerk(weapon);
switch (weaponPerk) { switch (weaponPerk) {
case PERK_WEAPON_LONG_RANGE: case PERK_WEAPON_LONG_RANGE:
v29 = 4; perceptionBonusMult = 4;
break; break;
case PERK_WEAPON_SCOPE_RANGE: case PERK_WEAPON_SCOPE_RANGE:
v29 = 5; perceptionBonusMult = 5;
v25 = 8; minEffectiveDist = 8;
break; break;
default: default:
v29 = 2; perceptionBonusMult = 2;
break; break;
} }
@ -4347,71 +4345,72 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
perception += 2 * perkGetRank(gDude, PERK_SHARPSHOOTER); perception += 2 * perkGetRank(gDude, PERK_SHARPSHOOTER);
} }
int distanceMod = 0;
// SFALL: Fix for `determine_to_hit_func` function taking distance // SFALL: Fix for `determine_to_hit_func` function taking distance
// into account when called from `determine_to_hit_no_range`. // into account when called from `determine_to_hit_no_range`.
if (defender != NULL && a6) { if (defender != NULL && useDistance) {
modifier = objectGetDistanceBetweenTiles(attacker, tile, defender, defender->tile); distanceMod = objectGetDistanceBetweenTiles(attacker, tile, defender, defender->tile);
} else { } else {
modifier = 0; distanceMod = 0;
} }
if (modifier >= v25) { if (distanceMod >= minEffectiveDist) {
int penalty = attacker == gDude int perceptionBonus = attacker == gDude
? v29 * (perception - 2) ? perceptionBonusMult * (perception - 2)
: v29 * perception; : perceptionBonusMult * perception;
modifier -= penalty; distanceMod -= perceptionBonus;
} else { } else {
modifier += v25; distanceMod += minEffectiveDist;
} }
if (-2 * perception > modifier) { if (distanceMod < -2 * perception) {
modifier = -2 * perception; distanceMod = -2 * perception;
} }
if (modifier >= 0) { if (distanceMod >= 0) {
if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) { if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) {
modifier *= -12; distanceMod *= -12;
} else { } else {
modifier *= -4; distanceMod *= -4;
} }
} else { } else {
modifier *= -4; distanceMod *= -4;
} }
if (a6 || modifier > 0) { if (useDistance || distanceMod > 0) {
accuracy += modifier; toHit += distanceMod;
} }
modifier = 0; int numCrittersInLof = 0;
if (defender != NULL && a6) { if (defender != NULL && useDistance) {
_combat_is_shot_blocked(attacker, tile, defender->tile, defender, &modifier); _combat_is_shot_blocked(attacker, tile, defender->tile, defender, &numCrittersInLof);
} }
accuracy -= 10 * modifier; toHit -= 10 * numCrittersInLof;
} }
if (attacker == gDude && traitIsSelected(TRAIT_ONE_HANDER)) { if (attacker == gDude && traitIsSelected(TRAIT_ONE_HANDER)) {
if (weaponIsTwoHanded(weapon)) { if (weaponIsTwoHanded(weapon)) {
accuracy -= 40; toHit -= 40;
} else { } else {
accuracy += 20; toHit += 20;
} }
} }
int minStrength = weaponGetMinStrengthRequired(weapon); int minStrength = weaponGetMinStrengthRequired(weapon);
modifier = minStrength - critterGetStat(attacker, STAT_STRENGTH); int minStrengthMod = minStrength - critterGetStat(attacker, STAT_STRENGTH);
if (attacker == gDude && perkGetRank(gDude, PERK_WEAPON_HANDLING) != 0) { if (attacker == gDude && perkGetRank(gDude, PERK_WEAPON_HANDLING) != 0) {
modifier -= 3; minStrengthMod -= 3;
} }
if (modifier > 0) { if (minStrengthMod > 0) {
accuracy -= 20 * modifier; toHit -= 20 * minStrengthMod;
} }
if (weaponGetPerk(weapon) == PERK_WEAPON_ACCURATE) { if (weaponGetPerk(weapon) == PERK_WEAPON_ACCURATE) {
accuracy += 20; toHit += 20;
} }
} }
@ -4422,17 +4421,17 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
armorClass = 0; armorClass = 0;
} }
accuracy -= armorClass; toHit -= armorClass;
} }
if (isRangedWeapon) { if (isRangedWeapon) {
accuracy += hit_location_penalty[hitLocation]; toHit += hit_location_penalty[hitLocation];
} else { } else {
accuracy += hit_location_penalty[hitLocation] / 2; toHit += hit_location_penalty[hitLocation] / 2;
} }
if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) { if (defender != NULL && (defender->flags & OBJECT_MULTIHEX) != 0) {
accuracy += 15; toHit += 15;
} }
if (attacker == gDude) { if (attacker == gDude) {
@ -4447,45 +4446,45 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
} }
if (lightIntensity <= 26214) if (lightIntensity <= 26214)
accuracy -= 40; toHit -= 40;
else if (lightIntensity <= 39321) else if (lightIntensity <= 39321)
accuracy -= 25; toHit -= 25;
else if (lightIntensity <= 52428) else if (lightIntensity <= 52428)
accuracy -= 10; toHit -= 10;
} }
if (_gcsd != NULL) { if (_gcsd != NULL) {
accuracy += _gcsd->accuracyBonus; toHit += _gcsd->accuracyBonus;
} }
if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) { if ((attacker->data.critter.combat.results & DAM_BLIND) != 0) {
accuracy -= 25; toHit -= 25;
} }
if (targetIsCritter && defender != NULL && (defender->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0) { if (targetIsCritter && defender != NULL && (defender->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0) {
accuracy += 40; toHit += 40;
} }
if (attacker->data.critter.combat.team != gDude->data.critter.combat.team) { if (attacker->data.critter.combat.team != gDude->data.critter.combat.team) {
switch (settings.preferences.combat_difficulty) { switch (settings.preferences.combat_difficulty) {
case 0: case 0:
accuracy -= 20; toHit -= 20;
break; break;
case 2: case 2:
accuracy += 20; toHit += 20;
break; break;
} }
} }
if (accuracy > 95) { if (toHit > 95) {
accuracy = 95; toHit = 95;
} }
if (accuracy < -100) { if (toHit < -100) {
debugPrint("Whoa! Bad skill value in determine_to_hit!\n"); debugPrint("Whoa! Bad skill value in determine_to_hit!\n");
} }
return accuracy; return toHit;
} }
// 0x4247B8 // 0x4247B8
@ -5880,39 +5879,35 @@ void _combat_highlight_change()
_combat_highlight = targetHighlight; _combat_highlight = targetHighlight;
} }
// Probably calculates line of sight or determines if object can see other object. // Checks if line of fire to the target object is blocked or not. Optionally calculate number of critters on the line of fire.
// //
// 0x426CC4 // 0x426CC4
bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5) bool _combat_is_shot_blocked(Object* sourceObj, int from, int to, Object* targetObj, int* numCrittersOnLof)
{ {
if (a5 != NULL) { if (numCrittersOnLof != NULL) {
*a5 = 0; *numCrittersOnLof = 0;
} }
Object* obstacle = a1; Object* obstacle = sourceObj;
int current = from; int current = from;
while (obstacle != NULL && current != to) { while (obstacle != NULL && current != to) {
_make_straight_path_func(a1, current, to, 0, &obstacle, 32, _obj_shoot_blocking_at); _make_straight_path_func(sourceObj, current, to, 0, &obstacle, 32, _obj_shoot_blocking_at);
if (obstacle != NULL) { if (obstacle != NULL) {
if (FID_TYPE(obstacle->fid) != OBJ_TYPE_CRITTER && obstacle != a4) { if (FID_TYPE(obstacle->fid) != OBJ_TYPE_CRITTER && obstacle != targetObj) {
return true; return true;
} }
if (a5 != NULL) { if (numCrittersOnLof != NULL && obstacle != targetObj && targetObj != NULL) {
if (obstacle != a4) { // SFALL: Fix for combat_is_shot_blocked_ engine
if (a4 != NULL) { // function not taking the flags of critters in the
// SFALL: Fix for combat_is_shot_blocked_ engine // line of fire into account when calculating the hit
// function not taking the flags of critters in the // chance penalty of ranged attacks in
// line of fire into account when calculating the hit // determine_to_hit_func_ engine function.
// chance penalty of ranged attacks in if ((obstacle->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT)) == 0) {
// determine_to_hit_func_ engine function. *numCrittersOnLof += 1;
if ((obstacle->data.critter.combat.results & (DAM_DEAD | DAM_KNOCKED_DOWN | DAM_KNOCKED_OUT)) == 0) {
*a5 += 1;
if ((obstacle->flags & OBJECT_MULTIHEX) != 0) { if ((obstacle->flags & OBJECT_MULTIHEX) != 0) {
*a5 += 1; *numCrittersOnLof += 1;
}
}
} }
} }
} }

View File

@ -34,13 +34,13 @@ 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(STRUCT_664980* attack); void _combat(STRUCT_664980* attack);
void attackInit(Attack* attack, Object* a2, Object* a3, int a4, int a5); void attackInit(Attack* attack, Object* attacker, Object* defender, int hitMode, int hitLocation);
int _combat_attack(Object* a1, Object* a2, int a3, int a4); int _combat_attack(Object* attacker, Object* defender, int hitMode, int hitLocation);
int _combat_bullet_start(const Object* a1, const Object* a2); 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, int hitLocation, int hitMode);
int _determine_to_hit_no_range(Object* a1, Object* a2, int a3, int a4, unsigned char* a5); int _determine_to_hit_no_range(Object* attacker, Object* defender, int hitLocation, int hitMode, unsigned char* a5);
int _determine_to_hit_from_tile(Object* a1, int a2, Object* a3, int a4, int a5); int _determine_to_hit_from_tile(Object* attacker, int tile, Object* defender, int hitLocation, int 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);
@ -52,7 +52,7 @@ void _combat_attack_this(Object* a1);
void _combat_outline_on(); void _combat_outline_on();
void _combat_outline_off(); void _combat_outline_off();
void _combat_highlight_change(); void _combat_highlight_change();
bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5); bool _combat_is_shot_blocked(Object* sourceObj, int from, int to, Object* targetObj, int* numCrittersOnLof);
int _combat_player_knocked_out_by(); int _combat_player_knocked_out_by();
int _combat_explode_scenery(Object* a1, Object* a2); int _combat_explode_scenery(Object* a1, Object* a2);
void _combat_delete_critter(Object* obj); void _combat_delete_critter(Object* obj);

View File

@ -2327,61 +2327,61 @@ static int _ai_pick_hit_mode(Object* attacker, Object* weapon, Object* defender)
} }
// 0x429FC8 // 0x429FC8
static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool taunt) static int _ai_move_steps_closer(Object* critter, Object* target, int actionPoints, bool taunt)
{ {
if (actionPoints <= 0) { if (actionPoints <= 0) {
return -1; return -1;
} }
int distance = aiGetDistance(a1); int distance = aiGetDistance(critter);
if (distance == DISTANCE_STAY) { if (distance == DISTANCE_STAY) {
return -1; return -1;
} }
if (distance == DISTANCE_STAY_CLOSE) { if (distance == DISTANCE_STAY_CLOSE) {
if (a2 != gDude) { if (target != gDude) {
int currentDistance = objectGetDistanceBetween(a1, gDude); int currentDistance = objectGetDistanceBetween(critter, gDude);
if (currentDistance > 5 if (currentDistance > 5
&& objectGetDistanceBetween(a2, gDude) > 5 && objectGetDistanceBetween(target, gDude) > 5
&& currentDistance + actionPoints > 5) { && currentDistance + actionPoints > 5) {
return -1; return -1;
} }
} }
} }
if (objectGetDistanceBetween(a1, a2) <= 1) { if (objectGetDistanceBetween(critter, target) <= 1) {
return -1; return -1;
} }
reg_anim_begin(ANIMATION_REQUEST_RESERVED); reg_anim_begin(ANIMATION_REQUEST_RESERVED);
if (taunt) { if (taunt) {
_combatai_msg(a1, NULL, AI_MESSAGE_TYPE_MOVE, 0); _combatai_msg(critter, NULL, AI_MESSAGE_TYPE_MOVE, 0);
} }
Object* v18 = a2; Object* initialTarget = target;
bool shouldUnhide; bool shouldUnhide;
if ((a2->flags & OBJECT_MULTIHEX) != 0) { if ((target->flags & OBJECT_MULTIHEX) != 0) {
shouldUnhide = true; shouldUnhide = true;
a2->flags |= OBJECT_HIDDEN; target->flags |= OBJECT_HIDDEN;
} else { } else {
shouldUnhide = false; shouldUnhide = false;
} }
if (pathfinderFindPath(a1, a1->tile, a2->tile, NULL, 0, _obj_blocking_at) == 0) { if (pathfinderFindPath(critter, critter->tile, target->tile, NULL, 0, _obj_blocking_at) == 0) {
_moveBlockObj = NULL; _moveBlockObj = NULL;
if (pathfinderFindPath(a1, a1->tile, a2->tile, NULL, 0, _obj_ai_blocking_at) == 0 if (pathfinderFindPath(critter, critter->tile, target->tile, NULL, 0, _obj_ai_blocking_at) == 0
&& _moveBlockObj != NULL && _moveBlockObj != NULL
&& PID_TYPE(_moveBlockObj->pid) == OBJ_TYPE_CRITTER) { && PID_TYPE(_moveBlockObj->pid) == OBJ_TYPE_CRITTER) {
if (shouldUnhide) { if (shouldUnhide) {
a2->flags &= ~OBJECT_HIDDEN; target->flags &= ~OBJECT_HIDDEN;
} }
a2 = _moveBlockObj; target = _moveBlockObj;
if ((a2->flags & OBJECT_MULTIHEX) != 0) { if ((target->flags & OBJECT_MULTIHEX) != 0) {
shouldUnhide = true; shouldUnhide = true;
a2->flags |= OBJECT_HIDDEN; target->flags |= OBJECT_HIDDEN;
} else { } else {
shouldUnhide = false; shouldUnhide = false;
} }
@ -2389,25 +2389,25 @@ static int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, bool
} }
if (shouldUnhide) { if (shouldUnhide) {
a2->flags &= ~OBJECT_HIDDEN; target->flags &= ~OBJECT_HIDDEN;
} }
int tile = a2->tile; int tile = target->tile;
if (a2 == v18) { if (target == initialTarget) {
_cai_retargetTileFromFriendlyFire(a1, a2, &tile); _cai_retargetTileFromFriendlyFire(critter, target, &tile);
} }
if (actionPoints >= critterGetStat(a1, STAT_MAXIMUM_ACTION_POINTS) / 2 && artCritterFidShouldRun(a1->fid)) { if (actionPoints >= critterGetStat(critter, STAT_MAXIMUM_ACTION_POINTS) / 2 && artCritterFidShouldRun(critter->fid)) {
if ((a2->flags & OBJECT_MULTIHEX) != 0) { if ((target->flags & OBJECT_MULTIHEX) != 0) {
animationRegisterRunToObject(a1, a2, actionPoints, 0); animationRegisterRunToObject(critter, target, actionPoints, 0);
} else { } else {
animationRegisterRunToTile(a1, tile, a1->elevation, actionPoints, 0); animationRegisterRunToTile(critter, tile, critter->elevation, actionPoints, 0);
} }
} else { } else {
if ((a2->flags & OBJECT_MULTIHEX) != 0) { if ((target->flags & OBJECT_MULTIHEX) != 0) {
animationRegisterMoveToObject(a1, a2, actionPoints, 0); animationRegisterMoveToObject(critter, target, actionPoints, 0);
} else { } else {
animationRegisterMoveToTile(a1, tile, a1->elevation, actionPoints, 0); animationRegisterMoveToTile(critter, tile, critter->elevation, actionPoints, 0);
} }
} }
@ -2665,35 +2665,35 @@ static int _ai_attack(Object* attacker, Object* defender, int hitMode)
} }
// 0x42A7D8 // 0x42A7D8
static int _ai_try_attack(Object* a1, Object* a2) static int _ai_try_attack(Object* attacker, Object* defender)
{ {
_critter_set_who_hit_me(a1, a2); _critter_set_who_hit_me(attacker, defender);
CritterCombatData* combatData = &(a1->data.critter.combat); CritterCombatData* combatData = &(attacker->data.critter.combat);
bool taunt = true; bool taunt = true;
Object* weapon = critterGetItem2(a1); Object* weapon = critterGetItem2(attacker);
if (weapon != NULL && itemGetType(weapon) != ITEM_TYPE_WEAPON) { if (weapon != NULL && itemGetType(weapon) != ITEM_TYPE_WEAPON) {
weapon = NULL; weapon = NULL;
} }
int hitMode = _ai_pick_hit_mode(a1, weapon, a2); int hitMode = _ai_pick_hit_mode(attacker, weapon, defender);
int minToHit = aiGetPacket(a1)->min_to_hit; int minToHit = aiGetPacket(attacker)->min_to_hit;
int actionPoints = a1->data.critter.combat.ap; int actionPoints = attacker->data.critter.combat.ap;
int safeDistance = 0; int safeDistance = 0;
int v42 = 0; int actionPointsToUse = 0;
if (weapon != NULL if (weapon != NULL
|| (critterGetBodyType(a2) == BODY_TYPE_BIPED || (critterGetBodyType(defender) == BODY_TYPE_BIPED
&& ((a2->fid & 0xF000) >> 12 == 0) && ((defender->fid & 0xF000) >> 12 == 0)
&& artExists(buildFid(OBJ_TYPE_CRITTER, a1->fid & 0xFFF, ANIM_THROW_PUNCH, 0, a1->rotation + 1)))) { && artExists(buildFid(OBJ_TYPE_CRITTER, attacker->fid & 0xFFF, ANIM_THROW_PUNCH, 0, attacker->rotation + 1)))) {
// SFALL: Check the safety of weapons based on the selected attack mode // SFALL: Check the safety of weapons based on the selected attack mode
// instead of always the primary weapon hit mode. // instead of always the primary weapon hit mode.
if (_combat_safety_invalidate_weapon(a1, weapon, hitMode, a2, &safeDistance)) { if (_combat_safety_invalidate_weapon(attacker, weapon, hitMode, defender, &safeDistance)) {
_ai_switch_weapons(a1, &hitMode, &weapon, a2); _ai_switch_weapons(attacker, &hitMode, &weapon, defender);
} }
} else { } else {
_ai_switch_weapons(a1, &hitMode, &weapon, a2); _ai_switch_weapons(attacker, &hitMode, &weapon, defender);
} }
unsigned char rotations[800]; unsigned char rotations[800];
@ -2704,37 +2704,37 @@ static int _ai_try_attack(Object* a1, Object* a2)
break; break;
} }
int reason = _combat_check_bad_shot(a1, a2, hitMode, false); int reason = _combat_check_bad_shot(attacker, defender, hitMode, false);
if (reason == COMBAT_BAD_SHOT_NO_AMMO) { if (reason == COMBAT_BAD_SHOT_NO_AMMO) {
// out of ammo // out of ammo
if (aiHaveAmmo(a1, weapon, &ammo)) { if (aiHaveAmmo(attacker, weapon, &ammo)) {
int remainingAmmoQuantity = weaponReload(weapon, ammo); int remainingAmmoQuantity = weaponReload(weapon, ammo);
if (remainingAmmoQuantity == 0 && ammo != NULL) { if (remainingAmmoQuantity == 0 && ammo != NULL) {
_obj_destroy(ammo); _obj_destroy(ammo);
} }
if (remainingAmmoQuantity != -1) { if (remainingAmmoQuantity != -1) {
int volume = _gsound_compute_relative_volume(a1); int volume = _gsound_compute_relative_volume(attacker);
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL); const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL);
_gsound_play_sfx_file_volume(sfx, volume); _gsound_play_sfx_file_volume(sfx, volume);
_ai_magic_hands(a1, weapon, 5002); _ai_magic_hands(attacker, weapon, 5002);
// SFALL: Fix incorrect AP cost when AI reloads a weapon. // SFALL: Fix incorrect AP cost when AI reloads a weapon.
// CE: There is a commented out code which checks // CE: There is a commented out code which checks
// available action points before performing reload. Not // available action points before performing reload. Not
// sure why it was commented, probably needs additional // sure why it was commented, probably needs additional
// testing. // testing.
int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false); int actionPointsRequired = weaponGetActionPointCost(attacker, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
if (a1->data.critter.combat.ap >= actionPointsRequired) { if (attacker->data.critter.combat.ap >= actionPointsRequired) {
a1->data.critter.combat.ap -= actionPointsRequired; attacker->data.critter.combat.ap -= actionPointsRequired;
} else { } else {
a1->data.critter.combat.ap = 0; attacker->data.critter.combat.ap = 0;
} }
} }
} else { } else {
ammo = _ai_search_environ(a1, ITEM_TYPE_AMMO); ammo = _ai_search_environ(attacker, ITEM_TYPE_AMMO);
if (ammo != NULL) { if (ammo != NULL) {
ammo = _ai_retrieve_object(a1, ammo); ammo = _ai_retrieve_object(attacker, ammo);
if (ammo != NULL) { if (ammo != NULL) {
int remainingAmmoQuantity = weaponReload(weapon, ammo); int remainingAmmoQuantity = weaponReload(weapon, ammo);
if (remainingAmmoQuantity == 0) { if (remainingAmmoQuantity == 0) {
@ -2742,62 +2742,63 @@ static int _ai_try_attack(Object* a1, Object* a2)
} }
if (remainingAmmoQuantity != -1) { if (remainingAmmoQuantity != -1) {
int volume = _gsound_compute_relative_volume(a1); int volume = _gsound_compute_relative_volume(attacker);
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL); const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_READY, weapon, hitMode, NULL);
_gsound_play_sfx_file_volume(sfx, volume); _gsound_play_sfx_file_volume(sfx, volume);
_ai_magic_hands(a1, weapon, 5002); _ai_magic_hands(attacker, weapon, 5002);
// SFALL: Fix incorrect AP cost when AI reloads a // SFALL: Fix incorrect AP cost when AI reloads a
// weapon. // weapon.
// CE: See note above, probably need to check // CE: See note above, probably need to check
// available action points before performing // available action points before performing
// reload. // reload.
int actionPointsRequired = weaponGetActionPointCost(a1, HIT_MODE_RIGHT_WEAPON_RELOAD, false); int actionPointsRequired = weaponGetActionPointCost(attacker, HIT_MODE_RIGHT_WEAPON_RELOAD, false);
if (a1->data.critter.combat.ap >= actionPointsRequired) { if (attacker->data.critter.combat.ap >= actionPointsRequired) {
a1->data.critter.combat.ap -= actionPointsRequired; attacker->data.critter.combat.ap -= actionPointsRequired;
} else { } else {
a1->data.critter.combat.ap = 0; attacker->data.critter.combat.ap = 0;
} }
} }
} }
} else { } else {
int volume = _gsound_compute_relative_volume(a1); int volume = _gsound_compute_relative_volume(attacker);
const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_OUT_OF_AMMO, weapon, hitMode, NULL); const char* sfx = sfxBuildWeaponName(WEAPON_SOUND_EFFECT_OUT_OF_AMMO, weapon, hitMode, NULL);
_gsound_play_sfx_file_volume(sfx, volume); _gsound_play_sfx_file_volume(sfx, volume);
_ai_magic_hands(a1, weapon, 5001); _ai_magic_hands(attacker, weapon, 5001);
if (_inven_unwield(a1, 1) == 0) { if (_inven_unwield(attacker, 1) == 0) {
_combat_turn_run(); _combat_turn_run();
} }
_ai_switch_weapons(a1, &hitMode, &weapon, a2); _ai_switch_weapons(attacker, &hitMode, &weapon, 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 // 3 - not enough action points
// 6 - crippled one arm for two-handed weapon // 6 - crippled one arm for two-handed weapon
// 7 - both hands crippled // 7 - both hands crippled
if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1) { if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1) {
return -1; return -1;
} }
} else if (reason == COMBAT_BAD_SHOT_OUT_OF_RANGE) { } else if (reason == COMBAT_BAD_SHOT_OUT_OF_RANGE) {
// target out of range // target out of range
int accuracy = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, rotations); int toHitNoRange = _determine_to_hit_no_range(attacker, defender, HIT_LOCATION_UNCALLED, hitMode, rotations);
if (accuracy < minToHit) { if (toHitNoRange < minToHit) {
debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(a1)); // hit chance is too low even at point blank range (not taking range into account)
_ai_run_away(a1, a2); debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(attacker));
_ai_run_away(attacker, defender);
return 0; return 0;
} }
if (weapon != NULL) { if (weapon != NULL) {
if (_ai_move_steps_closer(a1, a2, actionPoints, taunt) == -1) { if (_ai_move_steps_closer(attacker, defender, actionPoints, taunt) == -1) {
return -1; return -1;
} }
taunt = false; taunt = false;
} else { } else {
if (_ai_switch_weapons(a1, &hitMode, &weapon, a2) == -1 || weapon == NULL) { if (_ai_switch_weapons(attacker, &hitMode, &weapon, defender) == -1 || weapon == NULL) {
// NOTE: Uninline. // NOTE: Uninline.
if (_ai_move_closer(a1, a2, taunt) == -1) { if (_ai_move_closer(attacker, defender, taunt) == -1) {
return -1; return -1;
} }
} }
@ -2805,66 +2806,66 @@ static int _ai_try_attack(Object* a1, Object* a2)
} }
} else if (reason == COMBAT_BAD_SHOT_AIM_BLOCKED) { } else if (reason == COMBAT_BAD_SHOT_AIM_BLOCKED) {
// aim is blocked // aim is blocked
if (_ai_move_steps_closer(a1, a2, a1->data.critter.combat.ap, taunt) == -1) { if (_ai_move_steps_closer(attacker, defender, attacker->data.critter.combat.ap, taunt) == -1) {
return -1; return -1;
} }
taunt = false; taunt = false;
} else if (reason == COMBAT_BAD_SHOT_OK) { } else if (reason == COMBAT_BAD_SHOT_OK) {
int accuracy = _determine_to_hit(a1, a2, HIT_LOCATION_UNCALLED, hitMode); int accuracy = _determine_to_hit(attacker, defender, HIT_LOCATION_UNCALLED, hitMode);
if (safeDistance != 0) { if (safeDistance != 0) {
if (_ai_move_away(a1, a2, safeDistance) == -1) { if (_ai_move_away(attacker, defender, safeDistance) == -1) {
return -1; return -1;
} }
} }
if (accuracy < minToHit) { if (accuracy < minToHit) {
int accuracyNoRange = _determine_to_hit_no_range(a1, a2, HIT_LOCATION_UNCALLED, hitMode, rotations); int toHitNoRange = _determine_to_hit_no_range(attacker, defender, HIT_LOCATION_UNCALLED, hitMode, rotations);
if (accuracyNoRange < minToHit) { if (toHitNoRange < minToHit) {
debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(a1)); debugPrint("%s: FLEEING: Can't possibly Hit Target!", critterGetName(attacker));
_ai_run_away(a1, a2); _ai_run_away(attacker, defender);
return 0; return 0;
} }
if (actionPoints > 0) { if (actionPoints > 0) {
int v24 = pathfinderFindPath(a1, a1->tile, a2->tile, rotations, 0, _obj_blocking_at); int pathLength = pathfinderFindPath(attacker, attacker->tile, defender->tile, rotations, 0, _obj_blocking_at);
if (v24 == 0) { if (pathLength == 0) {
v42 = actionPoints; actionPointsToUse = actionPoints;
} else { } else {
if (v24 < actionPoints) { if (pathLength < actionPoints) {
actionPoints = v24; actionPoints = pathLength;
} }
int tile = a1->tile; int tile = attacker->tile;
int index; int index;
for (index = 0; index < actionPoints; index++) { for (index = 0; index < actionPoints; index++) {
tile = tileGetTileInDirection(tile, rotations[index], 1); tile = tileGetTileInDirection(tile, rotations[index], 1);
v42++; actionPointsToUse++;
int v27 = _determine_to_hit_from_tile(a1, tile, a2, HIT_LOCATION_UNCALLED, hitMode); int toHit = _determine_to_hit_from_tile(attacker, tile, defender, HIT_LOCATION_UNCALLED, hitMode);
if (v27 >= minToHit) { if (toHit >= minToHit) {
break; break;
} }
} }
if (index == actionPoints) { if (index == actionPoints) {
v42 = actionPoints; actionPointsToUse = actionPoints;
} }
} }
} }
if (_ai_move_steps_closer(a1, a2, v42, taunt) == -1) { if (_ai_move_steps_closer(attacker, defender, actionPointsToUse, taunt) == -1) {
debugPrint("%s: FLEEING: Can't possibly get closer to Target!", critterGetName(a1)); debugPrint("%s: FLEEING: Can't possibly get closer to Target!", critterGetName(attacker));
_ai_run_away(a1, a2); _ai_run_away(attacker, defender);
return 0; return 0;
} }
taunt = false; taunt = false;
if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) { if (_ai_attack(attacker, defender, hitMode) == -1 || weaponGetActionPointCost(attacker, hitMode, 0) > attacker->data.critter.combat.ap) {
return -1; return -1;
} }
} else { } else {
if (_ai_attack(a1, a2, hitMode) == -1 || weaponGetActionPointCost(a1, hitMode, 0) > a1->data.critter.combat.ap) { if (_ai_attack(attacker, defender, hitMode) == -1 || weaponGetActionPointCost(attacker, hitMode, 0) > attacker->data.critter.combat.ap) {
return -1; return -1;
} }
} }

View File

@ -1370,7 +1370,7 @@ char* gameSoundBuildInterfaceName(const char* a1)
// 0x451760 // 0x451760
char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* target) char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* target)
{ {
int v6; int soundVariant;
char weaponSoundCode; char weaponSoundCode;
char effectTypeCode; char effectTypeCode;
char materialCode; char materialCode;
@ -1384,12 +1384,12 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta
if (hitMode != HIT_MODE_LEFT_WEAPON_PRIMARY if (hitMode != HIT_MODE_LEFT_WEAPON_PRIMARY
&& hitMode != HIT_MODE_RIGHT_WEAPON_PRIMARY && hitMode != HIT_MODE_RIGHT_WEAPON_PRIMARY
&& hitMode != HIT_MODE_PUNCH) { && hitMode != HIT_MODE_PUNCH) {
v6 = 2; soundVariant = 2;
} else { } else {
v6 = 1; soundVariant = 1;
} }
} else { } else {
v6 = 1; soundVariant = 1;
} }
int damageType = weaponGetDamageType(NULL, weapon); int damageType = weaponGetDamageType(NULL, weapon);
@ -1438,7 +1438,7 @@ char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* ta
} }
} }
snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, v6, materialCode, 1); snprintf(_sfx_file_name, sizeof(_sfx_file_name), "W%c%c%1d%cXX%1d", effectTypeCode, weaponSoundCode, soundVariant, materialCode, 1);
compat_strupr(_sfx_file_name); compat_strupr(_sfx_file_name);
return _sfx_file_name; return _sfx_file_name;
} }