mirror of
https://bitbucket.org/seefoe/dsrc.git
synced 2026-07-31 01:15:55 -04:00
487 lines
14 KiB
Plaintext
487 lines
14 KiB
Plaintext
// Heavy weapons library
|
|
include library.utils;
|
|
include library.trace;
|
|
include library.combat;
|
|
include library.vehicle;
|
|
include library.dot;
|
|
include library.ai_lib;
|
|
|
|
const int TYPE_LAUNCHER_PISTOL = 1;
|
|
const int TYPE_ROCKET_LAUNCHER = 2;
|
|
const int TYPE_FLAME_THROWER = 3;
|
|
const int TYPE_HEAVY_PARTICLE_BEAM = 4;
|
|
const int TYPE_ACID_RIFLE = 5;
|
|
const int TYPE_HEAVY_ACID_BEAM = 6;
|
|
const int TYPE_LIGHTNING_BEAM_CANON = 7;
|
|
const int TYPE_PROTON_RIFLE = 8;
|
|
const int TYPE_HEAVY_FLAME_THROWER = 9;
|
|
const int TYPE_LAVA_CANNON = 10;
|
|
//*********************************************************************************************************
|
|
const int TYPE_MAX = 11; // this needs to get incremented if another weapon type is added
|
|
//*********************************************************************************************************
|
|
|
|
// Balance Knobs
|
|
const float LAUNCHER_PISTOL_AOE_RADIUS = 10.0f;
|
|
const float HVY_ACID_BEAM_AOE_RADIUS = 15.0f;
|
|
const int HVY_ACID_BEAM_AOE_CHANCE = 25;// %
|
|
const float HVY_ACID_BEAM_BLIND_DURATION = 20.0f;
|
|
const float HVY_LIGHTNING_BEAM_AOE_RADIUS = 10.0f;
|
|
const float FLAME_THROWER_CONE_ANGLE = 20.0f;
|
|
const float HEAVY_FLAME_THROWER_CONE_ANGLE = 25.0f;
|
|
const float LAVA_CANNON_AOE_RADIUS = 5.0f;
|
|
const int FLAME_THROWER_DOT_CHANCE = 30;// %
|
|
const float PARTICLE_BEAM_CONE_ANGLE = 1.0f;
|
|
const int ACID_RIFLE_PUNISHMENT_CHANCE = 40;// %
|
|
const float ACID_RIFLE_PUNISHMENT_DURATION = 15.0f;
|
|
const int ACID_RIFLE_MID_BLIND_CHANCE = 40;//%
|
|
const int ACID_RIFLE_MAX_BLIND_CHANCE = 20;//%
|
|
const int LIGHTNING_BEAM_CANON_DOT_CHANCE = 20;//%
|
|
const int LIGHTNING_BEAM_VEHICLE_SLOW_CHANCE = 100;//%
|
|
const float LIGHTNING_BEAM_VEHICLE_SLOW_DURATION = 20.0f; // seconds
|
|
const float LIGHTNING_BEAM_VEHICLE_SLOW_FACTOR = 0.1f; // 10% of normal speed
|
|
const float FOCUS_FIRE_CONE_REDUCTION = .75f; // 75% smaller cone
|
|
const float DEAD_BANG_RANGE_REDUCTION = .85f; // 85% range reduction
|
|
const float DEMOLITION_DAM_MULTIPLIER = 1.4f;
|
|
const float DEMOLITION_1_DAM_MULTIPLIER = 1.6f;
|
|
const float DEMOLITION_2_DAM_MULTIPLIER = 1.8f;
|
|
const string ATTACK_NAME_BASE_SINGLE = "co_hw_dot_";
|
|
const string ATTACK_NAME_BASE_AREA = "co_ae_hw_dot_";
|
|
|
|
//
|
|
// returns the weapon type based on @weapon's template
|
|
//
|
|
int getHeavyWeaponType(obj_id weapon)
|
|
{
|
|
if(!hasObjVar(weapon, "intWeaponType"))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
int type = getIntObjVar(weapon, "intWeaponType");
|
|
if(type >= TYPE_MAX)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
//
|
|
// fills the weapon_data class with the weapon data from the weapon object so we dont need to constantly query the weapon
|
|
//
|
|
boolean fillHeavyWeaponData(weapon_data dat)
|
|
{
|
|
int weaponType = getHeavyWeaponType(dat.id);
|
|
if(weaponType == -1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float AOEDamagePercent = 0.0f;
|
|
if(hasObjVar(dat.id, "intAOEDamagePercent"))
|
|
{
|
|
AOEDamagePercent = getFloatObjVar(dat.id, "intAOEDamagePercent");
|
|
}
|
|
|
|
if(AOEDamagePercent > 1.0f)
|
|
{
|
|
AOEDamagePercent = 1.0f;
|
|
}
|
|
else if(AOEDamagePercent < 0.0f)
|
|
{
|
|
AOEDamagePercent = 0.0f;
|
|
}
|
|
|
|
dat.hvyWeaponType = weaponType;
|
|
dat.hvyWeaponAeDamageMod = AOEDamagePercent;
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Checks restrictions of firing heavy weapons. Currently only checks to ensure that your posture is
|
|
// kneeling
|
|
//
|
|
boolean canFireHeavyWeapon(obj_id player, weapon_data weapon)
|
|
{
|
|
if (cybernetic.hasCommandoLegs( player ))
|
|
return true;//with these legs, you don't have to kneel, so.
|
|
|
|
// put posture restriction here, if any
|
|
return true;
|
|
}
|
|
|
|
float getDemolitionShotDamageMod(obj_id attacker, obj_id defender, string actionName)
|
|
{
|
|
float mult = 1.05f;
|
|
|
|
if(isPlayer(defender) || ai_lib.isNpc(defender) || ai_lib.isAnimal(defender))
|
|
{
|
|
return mult;
|
|
}
|
|
|
|
actionName = toLower(actionName);
|
|
if(actionName.equals("demolitionshot_2"))
|
|
{
|
|
mult = DEMOLITION_2_DAM_MULTIPLIER;
|
|
}
|
|
else if(actionName.equals("demolitionshot"))
|
|
{
|
|
mult = DEMOLITION_DAM_MULTIPLIER;
|
|
}
|
|
else if(actionName.equals("demolitionshot_1"))
|
|
{
|
|
mult = DEMOLITION_1_DAM_MULTIPLIER;
|
|
}
|
|
|
|
return mult;
|
|
}
|
|
|
|
//
|
|
// constructs the target list for the heavy weapon, depending on the weapon. Does not include the
|
|
// defender, as the defender is always an implicit target in the attack. The targeted defender needs
|
|
// to be in the array at the first index.
|
|
//
|
|
obj_id[] getAllHeavyWeaponTargets(obj_id attacker, obj_id defender, weapon_data weapon, dictionary actionData)
|
|
{
|
|
float rangeModifier = 1f;
|
|
float areaModifier = 1f;
|
|
string actionName = toLower(actionData.getString("actionName"));
|
|
|
|
if(actionName != null && actionName.length() > 0)
|
|
{
|
|
if(actionName.startsWith("demolition") || actionName.startsWith("overkill"))
|
|
{
|
|
return new obj_id[]{defender};
|
|
}
|
|
if(actionName.startsWith("deadbang"))
|
|
{
|
|
rangeModifier = DEAD_BANG_RANGE_REDUCTION;
|
|
}
|
|
else if(actionName.startsWith("focusfire"))
|
|
{
|
|
areaModifier = FOCUS_FIRE_CONE_REDUCTION;
|
|
}
|
|
}
|
|
|
|
obj_id[] defenders = null;
|
|
switch(weapon.hvyWeaponType)
|
|
{
|
|
case TYPE_LAUNCHER_PISTOL:
|
|
defenders = pvpGetTargetsInRange(attacker, defender, LAUNCHER_PISTOL_AOE_RADIUS * areaModifier);
|
|
break;
|
|
case TYPE_LAVA_CANNON:
|
|
defenders = pvpGetTargetsInRange(attacker, defender, LAVA_CANNON_AOE_RADIUS * areaModifier);
|
|
break;
|
|
case TYPE_FLAME_THROWER:
|
|
defenders = pvpGetTargetsInCone(attacker, attacker, defender, weapon.maxRange * rangeModifier, FLAME_THROWER_CONE_ANGLE * areaModifier);
|
|
break;
|
|
case TYPE_HEAVY_FLAME_THROWER:
|
|
defenders = pvpGetTargetsInCone(attacker, attacker, defender, weapon.maxRange * rangeModifier, HEAVY_FLAME_THROWER_CONE_ANGLE * areaModifier);
|
|
break;
|
|
case TYPE_HEAVY_PARTICLE_BEAM:
|
|
defenders = pvpGetTargetsInCone(attacker, attacker, defender, weapon.maxRange * rangeModifier, PARTICLE_BEAM_CONE_ANGLE * areaModifier);
|
|
// switch the first and last elements so that the animation looks right (punch-through illusion)
|
|
// assuming that the last element in the array is the furthest target
|
|
if(defenders != null && defenders.length > 1)
|
|
{
|
|
obj_id temp = defenders[0];
|
|
defenders[0] = defenders[defenders.length-1];
|
|
defenders[defenders.length-1] = temp;
|
|
}
|
|
break;
|
|
case TYPE_HEAVY_ACID_BEAM:
|
|
defenders = pvpGetTargetsInRange(attacker, defender, HVY_ACID_BEAM_AOE_RADIUS * areaModifier);
|
|
break;
|
|
case TYPE_LIGHTNING_BEAM_CANON:
|
|
defenders = pvpGetTargetsInRange(attacker, defender, HVY_LIGHTNING_BEAM_AOE_RADIUS * areaModifier);
|
|
break;
|
|
default:
|
|
defenders = new obj_id[]{defender};
|
|
break;
|
|
}
|
|
|
|
return defenders;
|
|
}
|
|
|
|
//
|
|
// This method is called by combat_heavy_weapon.script successful attack message handler as a result
|
|
// of a successful heavy weapon attack. here we can perform any cleanup or perform any penalty actions
|
|
// if necessary
|
|
//
|
|
void finalizeHeavyWeaponAttack(attacker_data attacker, defender_data[] defenders, weapon_data weapon)
|
|
{
|
|
effect_data dat = new effect_data();
|
|
defender_data defender = defenders[0];
|
|
switch(weapon.hvyWeaponType)
|
|
{
|
|
case TYPE_ACID_RIFLE:
|
|
float distance = getDistance(attacker.id, defender.id);
|
|
if(distance > (weapon.maxRange - weapon.minRange)/2)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(rand(1, 100) <= ACID_RIFLE_PUNISHMENT_CHANCE)
|
|
{
|
|
|
|
sendSystemMessage(attacker.id, new string_id("cbt_spam", "acid_rifle_selfblind"));
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Gets called by the combat system after a hit @defender has been established and damage has been dealt.
|
|
// This method will apply any special weapon effects.
|
|
//
|
|
effect_data doHeavyWeaponDefenderEffect(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
effect_data rslt = new effect_data();
|
|
switch(weapon.hvyWeaponType)
|
|
{
|
|
case TYPE_LAUNCHER_PISTOL:
|
|
rslt = _doLauncherPistolAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_ROCKET_LAUNCHER:
|
|
rslt = _doRocketLauncherAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_FLAME_THROWER:
|
|
case TYPE_HEAVY_FLAME_THROWER:
|
|
rslt = _doFlameThrowerAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_LAVA_CANNON:
|
|
rslt = _doLavaCannonAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_HEAVY_PARTICLE_BEAM:
|
|
rslt = _doHeavyParticleBeamAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_ACID_RIFLE:
|
|
rslt = _doAcidRifleAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_HEAVY_ACID_BEAM:
|
|
rslt = _doHeavyAcidBeamAttack(attacker, defender, weapon);
|
|
break;
|
|
case TYPE_LIGHTNING_BEAM_CANON:
|
|
rslt = _doLightningBeamCanonAttack(attacker, defender, weapon);
|
|
break;
|
|
}
|
|
|
|
return rslt;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
// For all of the _do* below functions, we are only applying the special effects,
|
|
// that is, any effect that is not damage - damage will have already been applied
|
|
// by the time these functions get called via the combat loops
|
|
/////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
effect_data _doLauncherPistolAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
// launcher pistol currently only does AOE damage, which is handled in the core combat loop
|
|
return new effect_data();
|
|
}
|
|
effect_data _doLavaCannonAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
// only does AOE damage, which is handled in the core combat loop
|
|
return new effect_data();
|
|
}
|
|
effect_data _doRocketLauncherAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
effect_data dat = new effect_data();
|
|
dat.posture = POSTURE_KNOCKED_DOWN;
|
|
return dat;
|
|
}
|
|
|
|
effect_data _doFlameThrowerAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
if(rand(1, 100) <= FLAME_THROWER_DOT_CHANCE)
|
|
{
|
|
_applyHealthStatFireDot(attacker, defender, weapon);
|
|
}
|
|
return new effect_data();
|
|
}
|
|
|
|
effect_data _doHeavyParticleBeamAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
return new effect_data();
|
|
}
|
|
|
|
effect_data _doAcidRifleAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
effect_data dat = new effect_data();
|
|
dat.states = new int[]{STATE_BLINDED};
|
|
dat.durations = new float[]{ACID_RIFLE_PUNISHMENT_DURATION};
|
|
|
|
float distance = getDistance(defender.id, attacker.id);
|
|
if(distance <= (weapon.maxRange-weapon.minRange)/2)
|
|
{
|
|
dat.stateChance = ACID_RIFLE_MID_BLIND_CHANCE;
|
|
}
|
|
else
|
|
{
|
|
dat.stateChance = ACID_RIFLE_MAX_BLIND_CHANCE;
|
|
}
|
|
|
|
return dat;
|
|
}
|
|
|
|
effect_data _doHeavyAcidBeamAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
effect_data dat = new effect_data();
|
|
dat.states = new int[]{STATE_BLINDED};
|
|
dat.durations = new float[]{HVY_ACID_BEAM_BLIND_DURATION};
|
|
dat.stateChance = HVY_ACID_BEAM_AOE_CHANCE;
|
|
return dat;
|
|
}
|
|
|
|
effect_data _doLightningBeamCanonAttack(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
if(rand(1, 100) <= LIGHTNING_BEAM_CANON_DOT_CHANCE)
|
|
{
|
|
_applyHealthStatFireDot(attacker, defender, weapon);
|
|
if(vehicle.isRidingVehicle(defender.id) && rand(1, 100) <= LIGHTNING_BEAM_VEHICLE_SLOW_CHANCE)
|
|
{
|
|
obj_id objVehicle = getMountId(defender.id);
|
|
vehicle.doTempMaxSpeedReduction(objVehicle, LIGHTNING_BEAM_VEHICLE_SLOW_FACTOR, LIGHTNING_BEAM_VEHICLE_SLOW_DURATION);
|
|
sendSystemMessage(defender.id, new string_id("cbt_spam", "vehicle_slow_lightning"));
|
|
}
|
|
}
|
|
|
|
return new effect_data();
|
|
}
|
|
|
|
void _applyHealthStatFireDot(attacker_data attacker, defender_data defender, weapon_data weapon)
|
|
{
|
|
int duration = 0;
|
|
int potency = 0;
|
|
int strength = 0;
|
|
|
|
switch(weapon.hvyWeaponType)
|
|
{
|
|
case TYPE_FLAME_THROWER:
|
|
case TYPE_LIGHTNING_BEAM_CANON:
|
|
{
|
|
duration = rand(15, 45);
|
|
potency = rand(125, 225);
|
|
strength = rand(200, 400);
|
|
}
|
|
break;
|
|
case TYPE_HEAVY_FLAME_THROWER:
|
|
{
|
|
duration = rand(15, 60);
|
|
potency = rand(150, 275);
|
|
strength = rand(300, 500);
|
|
}
|
|
break;
|
|
}
|
|
|
|
dot.applyDotEffect(defender.id, attacker.id, dot.DOT_FIRE, "hvyWeaponDot_" + HEALTH + "_" + attacker.id, HEALTH, potency, strength, duration, true, null);
|
|
}
|
|
|
|
string getHeavyWeaponDotName(obj_id player, obj_id weapon, boolean singleTarget)
|
|
{
|
|
int elementalDamageType = getWeaponElementalType(weapon);
|
|
|
|
return getHeavyWeaponDotName(player, elementalDamageType, singleTarget);
|
|
}
|
|
|
|
string getHeavyWeaponDotName(obj_id player, weapon_data weaponData, boolean singleTarget)
|
|
{
|
|
int elementalDamageType = weaponData.elementalType;
|
|
|
|
return getHeavyWeaponDotName(player, elementalDamageType, singleTarget);
|
|
}
|
|
|
|
string getHeavyWeaponDotName(obj_id player, int elementalDamageType, boolean singleTarget)
|
|
{
|
|
int playerLevel = getLevel(player);
|
|
|
|
//bail out if there is no elemental weapon type
|
|
if(elementalDamageType <= 0)
|
|
return null;
|
|
|
|
string attackNameBase= "";
|
|
string attackNameType = "";
|
|
string attackNameLevel = "";
|
|
string finalAttackName = "";
|
|
|
|
if (singleTarget)
|
|
attackNameBase = ATTACK_NAME_BASE_SINGLE;
|
|
else
|
|
attackNameBase = ATTACK_NAME_BASE_AREA;
|
|
|
|
switch(elementalDamageType)
|
|
{ case DAMAGE_ENERGY:
|
|
attackNameType = "energy_";
|
|
break;
|
|
case DAMAGE_KINETIC:
|
|
attackNameType = "kinetic_";
|
|
break;
|
|
case DAMAGE_ELEMENTAL_HEAT:
|
|
attackNameType = "fire_";
|
|
break;
|
|
case DAMAGE_ELEMENTAL_COLD:
|
|
attackNameType = "cold_";
|
|
break;
|
|
case DAMAGE_ELEMENTAL_ACID:
|
|
attackNameType = "acid_";
|
|
break;
|
|
case DAMAGE_ELEMENTAL_ELECTRICAL:
|
|
attackNameType = "electrical_";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(playerLevel == 90)
|
|
attackNameLevel = "5";
|
|
else if (playerLevel >= 80)
|
|
attackNameLevel = "4";
|
|
else if (playerLevel >= 70)
|
|
attackNameLevel = "3";
|
|
else if(playerLevel >= 60)
|
|
attackNameLevel = "2";
|
|
else if(playerLevel >= 50)
|
|
attackNameLevel = "1";
|
|
//string should look like this co_hw_dot_fire_5 = level 90 fire dot
|
|
finalAttackName = attackNameBase + attackNameType + attackNameLevel;
|
|
|
|
return finalAttackName;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|