mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
* to be in line with define_extra.h, the naming is from RE/CE. Minor textual edits to code.
334 lines
13 KiB
ObjectPascal
334 lines
13 KiB
ObjectPascal
/*
|
|
This script reimplements the combat damage calculation of the original game.
|
|
Use this to implement your own damage formula!
|
|
*/
|
|
|
|
#include "../sfall/define_lite.h"
|
|
#include "../sfall/define_extra.h"
|
|
#include "../sfall/sfall.h"
|
|
|
|
#define DAMAGE_FORMULA_VANILLA (0)
|
|
#define DAMAGE_FORMULA_GLOVZ (1)
|
|
#define DAMAGE_FORMULA_GLOVZ2 (2)
|
|
#define DAMAGE_FORMULA_YAAM (5)
|
|
|
|
procedure start;
|
|
procedure combatdamage_handler;
|
|
procedure itemdamage_handler;
|
|
procedure calc_damage_YAAM(variable weapon, variable rounds, variable armorDT, variable armorDR, variable bonusRangedDamage, variable multiplyDamage, variable difficulty);
|
|
procedure item_w_subtype(variable weapon, variable hit_mode);
|
|
procedure get_ammo_value(variable weapon, variable param);
|
|
|
|
variable
|
|
damage_formula,
|
|
item_damage_min,
|
|
item_damage_max,
|
|
item_damage_weapon,
|
|
item_damage_attacker;
|
|
|
|
procedure start begin
|
|
if game_loaded then begin
|
|
damage_formula := get_ini_setting("ddraw.ini|Misc|DamageFormula");
|
|
register_hook_proc(HOOK_COMBATDAMAGE, combatdamage_handler);
|
|
register_hook_proc(HOOK_ITEMDAMAGE, itemdamage_handler);
|
|
end
|
|
end
|
|
|
|
/*
|
|
HOOK_COMBATDAMAGE
|
|
|
|
Runs when:
|
|
1) Game calculates how much damage each target will get. This includes primary target as well as all extras (explosions and bursts). This happens BEFORE the actual attack animation.
|
|
2) AI decides whether it is safe to use area attack (burst, grenades), if he might hit friendlies.
|
|
|
|
Does not run for misses, or non-combat damage like dynamite explosions.
|
|
|
|
Critter arg0 - The target
|
|
Critter arg1 - The attacker
|
|
int arg2 - The amount of damage to the target
|
|
int arg3 - The amount of damage to the attacker
|
|
int arg4 - The special effect flags for the target (use bwand DAM_* to check specific flags)
|
|
int arg5 - The special effect flags for the attacker (use bwand DAM_* to check specific flags)
|
|
Item arg6 - The weapon used in the attack
|
|
int arg7 - The bodypart that was struck
|
|
int arg8 - Damage Multiplier (this is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage. Usually it's 2; for critical hits, the value is taken from the critical table; with Silent Death perk and the corresponding attack conditions, the value will be doubled)
|
|
int arg9 - Number of bullets actually hit the target (1 for melee attacks)
|
|
int arg10 - The amount of knockback to the target
|
|
int arg11 - Attack Type (see ATKTYPE_* constants)
|
|
mixed arg12 - computed attack data (see C_ATTACK_* for offsets and use get/set_object_data functions to get/set the data)
|
|
|
|
int ret0 - The damage to the target
|
|
int ret1 - The damage to the attacker
|
|
int ret2 - The special effect flags for the target
|
|
int ret3 - The special effect flags for the attacker
|
|
int ret4 - The amount of knockback to the target
|
|
*/
|
|
procedure combatdamage_handler begin
|
|
variable dmg_type, weapon_perk, dmg_thresh, dmg_resist, weapon_subtype, bonus_ranged, difficulty, i, dmg_mult, dmg_div, damage;
|
|
variable target, flags, knockback, amount;
|
|
|
|
variable
|
|
ctdTarget := get_sfall_arg,
|
|
ctdSource := get_sfall_arg,
|
|
amountTarget := get_sfall_arg,
|
|
amountSource := get_sfall_arg,
|
|
flagsTarget := get_sfall_arg,
|
|
flagsSource := get_sfall_arg,
|
|
weapon := get_sfall_arg,
|
|
bodypart := get_sfall_arg,
|
|
damageMultiplier := get_sfall_arg,
|
|
rounds := get_sfall_arg,
|
|
amountKnockback := get_sfall_arg,
|
|
hit_mode := get_sfall_arg;
|
|
|
|
if (ctdSource != item_damage_attacker or weapon != item_damage_weapon) then begin
|
|
debug_msg("! ERROR ! compute_damage: Expected attacker or weapon differs!");
|
|
return;
|
|
end
|
|
|
|
if (flagsSource bwand DAM_HIT) then begin
|
|
target := ctdTarget;
|
|
flags := flagsTarget;
|
|
knockback := 1;
|
|
end else begin
|
|
target := ctdSource;
|
|
flags := flagsSource;
|
|
knockback := 0;
|
|
end
|
|
|
|
if target and (obj_type(target) == OBJ_TYPE_CRITTER) then begin
|
|
if weapon then begin
|
|
dmg_type := weapon_dmg_type(weapon);
|
|
weapon_perk := get_proto_data(obj_pid(weapon), PROTO_WP_PERK);
|
|
end else begin
|
|
dmg_type := DMG_normal_dam;
|
|
weapon_perk := -1;
|
|
end
|
|
|
|
dmg_thresh := get_critter_stat(target, STAT_dmg_thresh + dmg_type);
|
|
dmg_resist := get_critter_stat(target, STAT_dmg_resist + dmg_type);
|
|
|
|
if (flags bwand DAM_BYPASS) and (dmg_type != DMG_emp) then begin
|
|
dmg_thresh := dmg_thresh * 20 / 100;
|
|
dmg_resist := dmg_resist * 20 / 100;
|
|
end else begin
|
|
if (weapon_perk == PERK_weapon_penetrate)
|
|
or (hit_mode == ATKTYPE_PALMSTRIKE or hit_mode == ATKTYPE_PIERCINGSTRIKE
|
|
or hit_mode == ATKTYPE_HOOKKICK or hit_mode == ATKTYPE_PIERCINGKICK) then
|
|
dmg_thresh := dmg_thresh * 20 / 100;
|
|
|
|
if ctdSource == dude_obj and has_trait(TRAIT_TRAIT, ctdSource, TRAIT_finesse) then
|
|
dmg_resist += 30;
|
|
end
|
|
|
|
weapon_subtype := item_w_subtype(weapon, hit_mode); // item_w_subtype_
|
|
if (ctdSource != dude_obj) or (weapon_subtype != WEAPON_TYPE_RANGED) then
|
|
bonus_ranged := 0;
|
|
else
|
|
bonus_ranged := has_trait(TRAIT_PERK, ctdSource, PERK_bonus_ranged_damage) * 2;
|
|
|
|
difficulty := 100;
|
|
if (has_trait(TRAIT_OBJECT, ctdSource, OBJECT_TEAM_NUM) != has_trait(TRAIT_OBJECT, dude_obj, OBJECT_TEAM_NUM)) then begin
|
|
if (combat_difficulty == 0) then
|
|
difficulty := 75;
|
|
else if (combat_difficulty == 2) then
|
|
difficulty := 125;
|
|
end
|
|
|
|
if (damage_formula == DAMAGE_FORMULA_YAAM) then begin
|
|
amount := calc_damage_YAAM(weapon, rounds, dmg_thresh, dmg_resist, bonus_ranged, damageMultiplier, difficulty);
|
|
end else begin
|
|
// F2 default start
|
|
// Damage = (1 - (DR_armor + DR_ammo_adjust) * (((raw_damage * (dmg_mult * damageMultiplier)) / dmg_div) - dmg_thresh)
|
|
dmg_resist += get_ammo_value(weapon, PROTO_AM_DR_MOD); // item_w_dr_adjust_ (DR Adjust %)
|
|
if (dmg_resist < 100) then begin
|
|
if (dmg_resist < 0) then dmg_resist := 0;
|
|
|
|
dmg_mult := damageMultiplier * get_ammo_value(weapon, PROTO_AM_DMG_MULT); // item_w_dam_mult_ (Dmg mod A)
|
|
dmg_div := get_ammo_value(weapon, PROTO_AM_DMG_DIV); // item_w_dam_div_ (Dmg mod B)
|
|
|
|
for (i := 1; i <= rounds; i++) begin
|
|
damage := (random(item_damage_min, item_damage_max) + bonus_ranged) * dmg_mult; // item_w_damage_ (raw_damage)
|
|
if dmg_div then damage /= dmg_div;
|
|
|
|
damage := (((damage / 2) * difficulty) / 100) - dmg_thresh;
|
|
if (damage > 0) then begin
|
|
damage := damage - ((damage * dmg_resist) / 100); // reduce damage by the percentage of DR_armor + DR_Ammo
|
|
if (damage > 0) then amount += damage;
|
|
end
|
|
end
|
|
end
|
|
// F2 default end
|
|
end
|
|
|
|
if (ctdSource == dude_obj) then begin
|
|
if has_trait(TRAIT_PERK, ctdSource, PERK_living_anatomy_perk) and (critter_kill_type(ctdTarget) != KILL_TYPE_robot_kills)
|
|
and (critter_kill_type(ctdTarget) != KILL_TYPE_alien_kills) then
|
|
amount += 5;
|
|
if has_trait(TRAIT_PERK, ctdSource, PERK_pyromaniac_perk) and (dmg_type == DMG_fire) then
|
|
amount += 5;
|
|
end
|
|
|
|
if knockback and ((get_flags(target) bwand FLAG_MULTIHEX) == 0)
|
|
and (dmg_type == DMG_explosion or weapon == 0 or weapon_subtype == WEAPON_TYPE_MELEE)
|
|
and ((get_proto_data(obj_pid(target), PROTO_CR_FLAGS) bwand CFLG_NOKNOCKDOWN) == 0) then begin // critter_flag_check_
|
|
damage := 0;
|
|
if (target == dude_obj) and has_trait(TRAIT_PERK, target, PERK_stonewall_perk) then begin
|
|
damage := 1;
|
|
if (random(0, 100) < 50) then knockback := 0;
|
|
end
|
|
if knockback then begin
|
|
if (weapon_perk == PERK_weapon_knockback) then
|
|
amountKnockback := amount / 5;
|
|
else
|
|
amountKnockback := amount / 10;
|
|
|
|
if damage then amountKnockback /= 2;
|
|
end
|
|
end
|
|
end
|
|
|
|
if (flagsSource bwand DAM_HIT) then begin
|
|
display_msg("COMBATDAMAGE amountTarget = " + amountTarget+ ", amount = " + amount);
|
|
amountTarget := amount;
|
|
flagsTarget := flags;
|
|
end else begin
|
|
display_msg("COMBATDAMAGE amountSource = " + amountSource+ ", amount = " + amount);
|
|
amountSource := amount;
|
|
flagsSource := flags;
|
|
end
|
|
|
|
set_sfall_return(amountTarget);
|
|
set_sfall_return(amountSource);
|
|
set_sfall_return(flagsTarget);
|
|
set_sfall_return(flagsSource);
|
|
set_sfall_return(amountKnockback);
|
|
|
|
set_sfall_arg(2, amountTarget);
|
|
set_sfall_arg(3, amountSource);
|
|
set_sfall_arg(4, flagsTarget);
|
|
set_sfall_arg(5, flagsSource);
|
|
set_sfall_arg(10, amountKnockback);
|
|
end
|
|
|
|
procedure calc_damage_YAAM(variable weapon, variable rounds, variable armorDT, variable armorDR, variable bonusRangedDamage, variable multiplyDamage, variable difficulty) begin
|
|
variable accumulatedDamage, ammoDiv, ammoMult, ammoDT, calcDT, _calcDT, calcDR, i, rawDamage, resistedDamage;
|
|
if (rounds <= 0) then begin
|
|
return 0;
|
|
end
|
|
|
|
ammoDiv := get_ammo_value(weapon, PROTO_AM_DMG_DIV);
|
|
ammoMult := get_ammo_value(weapon, PROTO_AM_DMG_MULT);
|
|
|
|
// Damage Multipler = Critical Multipler * Ammo Dividend
|
|
multiplyDamage *= ammoMult;
|
|
|
|
// Retrieve ammo DT (well, it's really Retrieve ammo DR, but since we're treating ammo DR as ammo DT...)
|
|
ammoDT := get_ammo_value(weapon, PROTO_AM_DR_MOD);
|
|
|
|
calcDT := armorDT - ammoDT;
|
|
_calcDT := calcDT;
|
|
|
|
if (calcDT >= 0) then begin
|
|
_calcDT := 0;
|
|
end else begin
|
|
// note that this should be a negative value
|
|
_calcDT *= 10;
|
|
calcDT := 0;
|
|
end
|
|
|
|
// DR = armor DR + DT (note that DT should be less than or equal to zero)
|
|
calcDR = armorDR + _calcDT;
|
|
if (calcDR < 0) then begin
|
|
calcDR = 0;
|
|
end else if (calcDR >= 100) then begin
|
|
return 0;
|
|
end
|
|
|
|
display_msg("YAAM: AmmoDT=" + ammoDT + ", DT=" + calcDT + "/" + armorDT + ", DR=" + calcDR + "/" + armorDR);
|
|
// Start of damage calculation loop
|
|
for (i := 0; i < rounds; i++) begin
|
|
rawDamage = random(item_damage_min, item_damage_max);
|
|
rawDamage += bonusRangedDamage;
|
|
|
|
rawDamage -= calcDT;
|
|
if (rawDamage <= 0) then
|
|
continue;
|
|
|
|
rawDamage *= multiplyDamage;
|
|
if (ammoDiv != 0) then begin
|
|
rawDamage /= ammoDiv;
|
|
end
|
|
rawDamage /= 2; // related to critical hit damage multiplier bonus
|
|
rawDamage *= difficulty; // combat difficulty setting (75 if wimpy, 100 if normal or if attacker is player, 125 if rough)
|
|
rawDamage /= 100;
|
|
|
|
resistedDamage = calcDR * rawDamage;
|
|
resistedDamage /= 100;
|
|
rawDamage -= resistedDamage;
|
|
|
|
if (rawDamage > 0) then begin
|
|
display_msg("YAAM: Bullet " + i + " dmg=" + rawDamage);
|
|
accumulatedDamage += rawDamage;
|
|
end
|
|
end
|
|
return accumulatedDamage;
|
|
end
|
|
|
|
/*
|
|
HOOK_ITEMDAMAGE
|
|
|
|
Runs when retrieving the damage rating of the player's used weapon. (Which may be their fists.)
|
|
|
|
int arg0 - The default min damage
|
|
int arg1 - The default max damage
|
|
Item arg2 - The weapon used (0 if unarmed)
|
|
Critter arg3 - The critter doing the attacking
|
|
int arg4 - The type of attack
|
|
int arg5 - non-zero if this is an attack using a melee weapon
|
|
|
|
int ret0 - Either the damage to be used, if ret1 isn't given, or the new minimum damage if it is
|
|
int ret1 - The new maximum damage
|
|
*/
|
|
procedure itemdamage_handler begin
|
|
item_damage_min := get_sfall_arg;
|
|
item_damage_max := get_sfall_arg;
|
|
item_damage_weapon := get_sfall_arg;
|
|
item_damage_attacker := get_sfall_arg;
|
|
//hit_mode := get_sfall_arg,
|
|
//isMelee := get_sfall_arg;
|
|
|
|
display_msg("itemdamage: " + item_damage_min + "-" + item_damage_max);
|
|
end
|
|
|
|
procedure item_w_subtype(variable weapon, variable hit_mode) begin
|
|
variable attack_mode, type := WEAPON_TYPE_UNARMED;
|
|
|
|
if weapon and (hit_mode <= ATKTYPE_RWEP2) then begin
|
|
attack_mode := weapon_attack_mode(obj_pid(weapon), hit_mode);
|
|
|
|
if (attack_mode >= ATTACK_MODE_SINGLE) then
|
|
type := WEAPON_TYPE_RANGED;
|
|
else if (attack_mode == ATTACK_MODE_THROW) then
|
|
type := WEAPON_TYPE_THROWN;
|
|
else if (attack_mode >= ATTACK_MODE_SWING) then
|
|
type := WEAPON_TYPE_MELEE;
|
|
else if (attack_mode == ATTACK_MODE_NONE) then
|
|
type := WEAPON_TYPE_NONE;
|
|
end
|
|
|
|
return type;
|
|
end
|
|
|
|
procedure get_ammo_value(variable weapon, variable param) begin
|
|
variable pid := -1, value := 0; // default DR value
|
|
|
|
if weapon then begin
|
|
pid := get_weapon_ammo_pid(weapon);
|
|
if (pid > -1) then value := get_proto_data(pid, param);
|
|
end
|
|
if (pid == -1 and param != PROTO_AM_DR_MOD) then value := 1; // default value for Mult/Div
|
|
|
|
return value;
|
|
end
|