Files
2018-07-17 14:15:37 +02:00

515 lines
14 KiB
ActionScript

import statuses;
uint typeMask = DF_Flag1 | DF_Flag2;
enum DamageTypes {
DT_Generic = 0,
DT_Projectile,
DT_Energy,
DT_Explosive,
};
DamageFlags DF_IgnoreDR = DF_Flag3;
DamageFlags ReachedInternals = DF_Flag4;
DamageFlags DF_FullDR = DF_Flag5;
void Damage(Event& evt, double Amount) {
DamageEvent dmg;
dmg.damage = Amount * double(evt.efficiency) * double(evt.partiality);
dmg.partiality = evt.partiality;
dmg.impact = evt.impact;
@dmg.obj = evt.obj;
@dmg.target = evt.target;
dmg.source_index = evt.source_index;
evt.target.damage(dmg, -1.0, evt.direction);
}
void EnergyDamage(Event& evt, double Amount) {
DamageEvent dmg;
dmg.damage = Amount * double(evt.efficiency) * double(evt.partiality);
dmg.partiality = evt.partiality;
dmg.impact = evt.impact;
@dmg.obj = evt.obj;
@dmg.target = evt.target;
dmg.source_index = evt.source_index;
dmg.flags |= DT_Energy | DF_FullDR | ReachedInternals;
evt.target.damage(dmg, -1.0, evt.direction);
//if(dmg.flags & ReachedInternals != 0 && evt.target.isShip)
// cast<Ship>(evt.target).startFire();
}
void ExplDamage(Event& evt, double Amount) {
DamageEvent dmg;
dmg.damage = Amount * double(evt.efficiency) * double(evt.partiality);
dmg.partiality = evt.partiality;
dmg.impact = evt.impact;
@dmg.obj = evt.obj;
@dmg.target = evt.target;
dmg.source_index = evt.source_index;
dmg.flags |= DT_Explosive | ReachedInternals;
evt.target.damage(dmg, -1.0, evt.direction);
//if(dmg.flags & ReachedInternals != 0 && evt.target.isShip)
// cast<Ship>(evt.target).mangle(Amount);
}
void SelfDestruct(Event& evt, double Amount, double Radius, double Hits) {
if(evt.obj.inCombat)
AreaExplDamage(evt, Amount, Radius, Hits, 0);
}
void AreaExplDamage(Event& evt, double Amount, double Radius, double Hits) {
AreaExplDamage(evt, Amount, Radius, Hits, 0);
}
void AreaExplDamage(Event& evt, double Amount, double Radius, double Hits, double Spillable) {
Object@ targ = evt.target !is null ? evt.target : evt.obj;
vec3d center = targ.position + evt.impact.normalize(targ.radius);
array<Object@>@ objs = findInBox(center - vec3d(Radius), center + vec3d(Radius), evt.obj.owner.hostileMask);
playParticleSystem("TorpExplosionRed", center, quaterniond(), Radius / 3.0, targ.visibleMask);
uint hits = round(Hits);
double maxDSq = Radius * Radius;
for(uint i = 0, cnt = objs.length; i < cnt; ++i) {
Object@ target = objs[i];
vec3d off = target.position - center;
double dist = off.length - target.radius;
if(dist > Radius)
continue;
double deal = Amount;
if(dist > 0.0)
deal *= 1.0 - (dist / Radius);
//Rock the boat
if(target.hasMover) {
double amplitude = deal * 0.2 / (target.radius * target.radius);
target.impulse(off.normalize(min(amplitude,8.0)));
target.rotate(quaterniond_fromAxisAngle(off.cross(off.cross(target.rotation * vec3d_front())).normalize(), (randomi(0,1) == 0 ? 1.0 : -1.0) * atan(amplitude * 0.2) * 2.0));
}
DamageEvent dmg;
@dmg.obj = evt.obj;
@dmg.target = target;
dmg.source_index = evt.source_index;
dmg.flags |= DT_Projectile;
dmg.impact = off.normalized(target.radius);
dmg.spillable = Spillable != 0;
vec2d dir = vec2d(off.x, off.z).normalized();
for(uint n = 0; n < hits; ++n) {
dmg.partiality = evt.partiality / double(hits);
dmg.damage = deal * double(evt.efficiency) * double(dmg.partiality);
target.damage(dmg, -1.0, dir);
}
}
}
void ProjDamage(Event& evt, double Amount, double Pierce, double Suppression) {
ProjDamage(evt, Amount, Pierce, Suppression, 0);
}
void ProjDamage(Event& evt, double Amount, double Pierce, double Suppression, double IgnoreDR) {
DamageEvent dmg;
dmg.damage = Amount * double(evt.efficiency) * double(evt.partiality);
dmg.partiality = evt.partiality;
dmg.pierce = Pierce;
dmg.impact = evt.impact;
@dmg.obj = evt.obj;
@dmg.target = evt.target;
dmg.source_index = evt.source_index;
dmg.flags |= DT_Projectile;
if(IgnoreDR != 0)
dmg.flags |= DF_IgnoreDR;
evt.target.damage(dmg, -1.0, evt.direction);
if(Suppression > 0 && evt.target.isShip) {
double r = evt.target.radius;
double suppress = Suppression * double(evt.efficiency) * double(evt.partiality) / (r*r*r);
cast<Ship>(evt.target).suppress(suppress);
}
}
void ProjImpact(Event& evt, double Amount, double Pierce, double IgnoreDR, double Impulse) {
DamageEvent dmg;
dmg.damage = Amount * double(evt.efficiency) * double(evt.partiality);
dmg.partiality = evt.partiality;
dmg.pierce = Pierce;
dmg.impact = evt.impact;
@dmg.obj = evt.obj;
@dmg.target = evt.target;
dmg.source_index = evt.source_index;
dmg.flags |= DT_Projectile;
if(IgnoreDR != 0)
dmg.flags |= DF_IgnoreDR;
evt.target.damage(dmg, -1.0, evt.direction);
if(Impulse != 0) {
Ship@ ship = cast<Ship>(evt.target);
if(ship !is null)
ship.impulse((ship.position - evt.obj.position).normalize(Impulse / ship.getMass()));
}
}
void BombardDamage(Event& evt, double Amount) {
Planet@ planet = cast<Planet>(evt.target);
if(planet !is null)
planet.removePopulation(Amount);
}
void SurfaceBombard(Event& evt, double Duration, double Stacks) {
int stacks = int(Stacks);
Planet@ planet = cast<Planet>(evt.target);
if(planet !is null) {
Duration /= double(planet.level) * 0.5 + 1.0;
int status = getStatusID("Devastation");
for(int i = 0; i < stacks; ++i)
planet.addStatus(status, Duration);
}
}
bool WeaponFire(const Effector& efftr, Object& obj, Object& target, float& efficiency, double supply) {
Ship@ ship = cast<Ship>(obj);
if(ship is null)
return true;
ship.consumeSupply(supply);
return true;
}
bool RequiresSupply(const Effector& efftr, Object& obj, Object& target, float& efficiency, double supply) {
Ship@ ship = cast<Ship>(obj);
if(ship is null)
return true;
return ship.consumeMinSupply(supply);
}
DamageEventStatus CapDamage(DamageEvent& evt, const vec2u& position,
double maxDamage, double MinimumPercent)
{
if(evt.flags & DF_IgnoreDR != 0)
return DE_Continue;
if(evt.flags & DF_FullDR != 0)
MinimumPercent = 0.01;
if(evt.damage > maxDamage * evt.partiality)
evt.damage = max(maxDamage * evt.partiality, evt.damage * MinimumPercent);
return DE_Continue;
}
DamageEventStatus ReduceDamage(DamageEvent& evt, const vec2u& position,
double ProjResist, double EnergyResist, double ExplResist, double MinPct)
{
if(evt.flags & DF_IgnoreDR != 0)
return DE_Continue;
if(evt.flags & DF_FullDR != 0)
MinPct = 0.01;
//Prevent internal-only effects
evt.flags &= ~ReachedInternals;
double dmg = evt.damage;
double dr;
switch(evt.flags & typeMask) {
case DT_Projectile:
dr = ProjResist; break;
case DT_Energy:
dr = EnergyResist; break;
case DT_Explosive:
dr = ExplResist; break;
case DT_Generic:
default:
dr = (ProjResist + EnergyResist + ExplResist) / 3.0; break;
}
dmg -= dr * evt.partiality;
double minDmg = evt.damage * MinPct;
if(dmg < minDmg)
dmg = minDmg;
evt.damage = dmg;
return DE_Continue;
}
DamageEventStatus DamageResist(DamageEvent& evt, const vec2u& position, double Amount, double MinPct)
{
if(evt.flags & DF_IgnoreDR != 0)
return DE_Continue;
if(evt.flags & DF_FullDR != 0)
MinPct = 0.01;
//Prevent internal-only effects
evt.flags &= ~ReachedInternals;
double dmg = evt.damage - (Amount * evt.partiality);
double minDmg = evt.damage * MinPct;
if(dmg < minDmg)
dmg = minDmg;
evt.damage = dmg;
return DE_Continue;
}
DamageEventStatus DamageDistributeHealth(DamageEvent& evt, const vec2u& position)
{
evt.blueprint.damage(evt.target, evt, position);
distributeHealth(evt.blueprint, evt.target, position);
return DE_SkipHex;
}
void DistributeHealth(Event& evt) {
const Subsystem@ sys = evt.source;
if(sys is null)
return;
for(uint n = 0, cnt = ceil(evt.time * 4.0); n < cnt; ++n) {
vec2u hex = sys.hexagon(randomi(0, sys.hexCount-1));
distributeHealth(evt.blueprint, evt.obj, hex);
}
}
//This is ugly as shit to prevent allocation. Macros would've been nice but ah well.
void distributeHealth(Blueprint& bp, Object& obj, const vec2u& position) {
auto@ curSys = bp.design.subsystem(position);
if(curSys is null)
return;
double totHealth = 0;
double totMax = 0;
uint hexes = 1;
bool offset = (position.x % 2) != 0;
//Collect health variables for all nearby hexes
vec2u pos = position;
double h0 = bp.design.variable(pos, HV_HP);
totMax += h0;
HexStatus@ stat0 = bp.getHexStatus(pos.x, pos.y);
totHealth += h0 * double(stat0.hp) / 255.0;
bp.quadrantHP(bp.design.getQuadrant(pos)) -= h0 * double(stat0.hp) / 255.0;
double h1 = 0;
HexStatus@ stat1;
vec2u pos1 = position; pos1.y -= 1;
if(bp.design.subsystem(pos1) is curSys) {
h1 = bp.design.variable(pos1, HV_HP);
totMax += h1;
@stat1 = bp.getHexStatus(pos1.x, pos1.y);
double mod = h1 * double(stat1.hp) / 255.0;
totHealth += mod;
bp.quadrantHP(bp.design.getQuadrant(pos1)) -= mod;
hexes += 1;
}
double h2 = 0;
HexStatus@ stat2;
vec2u pos2 = position; pos2.x -= 1; if(!offset) pos2.y -= 1;
if(bp.design.subsystem(pos2) is curSys) {
h2 = bp.design.variable(pos2, HV_HP);
totMax += h2;
@stat2 = bp.getHexStatus(pos2.x, pos2.y);
double mod = h2 * double(stat2.hp) / 255.0;
totHealth += mod;
bp.quadrantHP(bp.design.getQuadrant(pos2)) -= mod;
hexes += 1;
}
double h3 = 0;
HexStatus@ stat3;
vec2u pos3 = position; pos3.x += 1; if(!offset) pos3.y -= 1;
if(bp.design.subsystem(pos3) is curSys) {
h3 = bp.design.variable(pos3, HV_HP);
totMax += h3;
@stat3 = bp.getHexStatus(pos3.x, pos3.y);
double mod = h3 * double(stat3.hp) / 255.0;
totHealth += mod;
bp.quadrantHP(bp.design.getQuadrant(pos3)) -= mod;
hexes += 1;
}
double h4 = 0;
HexStatus@ stat4;
vec2u pos4 = position; pos4.x -= 1; if(offset) pos4.y += 1;
if(bp.design.subsystem(pos4) is curSys) {
h4 = bp.design.variable(pos4, HV_HP);
totMax += h4;
@stat4 = bp.getHexStatus(pos4.x, pos4.y);
double mod = h4 * double(stat4.hp) / 255.0;
totHealth += mod;
bp.quadrantHP(bp.design.getQuadrant(pos4)) -= mod;
hexes += 1;
}
double h5 = 0;
HexStatus@ stat5;
vec2u pos5 = position; pos5.y += 1;
if(bp.design.subsystem(pos5) is curSys) {
h5 = bp.design.variable(pos5, HV_HP);
totMax += h5;
@stat5 = bp.getHexStatus(pos5.x, pos5.y);
double mod = h5 * double(stat5.hp) / 255.0;
totHealth += mod;
bp.quadrantHP(bp.design.getQuadrant(pos5)) -= mod;
hexes += 1;
}
double h6 = 0;
HexStatus@ stat6;
vec2u pos6 = position; pos6.x += 1; if(offset) pos6.y += 1;
if(bp.design.subsystem(pos6) is curSys) {
h6 = bp.design.variable(pos6, HV_HP);
totMax += h6;
@stat6 = bp.getHexStatus(pos6.x, pos6.y);
double mod = h6 * double(stat6.hp) / 255.0;
totHealth += mod;
bp.quadrantHP(bp.design.getQuadrant(pos6)) -= mod;
hexes += 1;
}
//Set all hexes to the same health value
double targPct = totHealth / totMax;
uint targTake = ceil(targPct * 255);
uint take;
{
take = min(targTake, uint(floor(totHealth / h0 * 255.0)));
stat0.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos)) += h0 * double(stat0.hp) / 255.0;
totHealth -= stat0.hp * h0 / 255.0;
}
if(stat1 !is null) {
take = min(targTake, uint(floor(totHealth / h1 * 255.0)));
stat1.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos1)) += h1 * double(stat1.hp) / 255.0;
totHealth -= stat1.hp * h1 / 255.0;
}
if(stat2 !is null) {
take = min(targTake, uint(floor(totHealth / h2 * 255.0)));
stat2.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos2)) += h2 * double(stat2.hp) / 255.0;
totHealth -= stat2.hp * h2 / 255.0;
}
if(stat3 !is null) {
take = min(targTake, uint(floor(totHealth / h3 * 255.0)));
stat3.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos3)) += h3 * double(stat3.hp) / 255.0;
totHealth -= stat3.hp * h3 / 255.0;
}
if(stat4 !is null) {
take = min(targTake, uint(floor(totHealth / h4 * 255.0)));
stat4.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos4)) += h4 * double(stat4.hp) / 255.0;
totHealth -= stat4.hp * h4 / 255.0;
}
if(stat5 !is null) {
take = min(targTake, uint(floor(totHealth / h5 * 255.0)));
stat5.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos5)) += h5 * double(stat5.hp) / 255.0;
totHealth -= stat5.hp * h5 / 255.0;
}
if(stat6 !is null) {
take = min(targTake, uint(floor(totHealth / h6 * 255.0)));
stat6.hp = take;
bp.quadrantHP(bp.design.getQuadrant(pos6)) += h6 * double(stat6.hp) / 255.0;
totHealth -= stat6.hp * h6 / 255.0;
}
if(totHealth > 0.001)
bp.currentHP -= totHealth;
}
DamageEventStatus ShieldDamage(DamageEvent& evt, vec2u& position, vec2d& direction) {
Ship@ ship = cast<Ship>(evt.target);
if(ship !is null && ship.Shield > 0) {
double maxShield = ship.MaxShield;
if(maxShield <= 0.0)
maxShield = ship.Shield;
double dmgScale = (evt.damage * ship.Shield) / (maxShield * maxShield);
if(dmgScale < 0.01) {
//TODO: Simulate this effect on the client
if(randomd() < dmgScale / 0.001)
playParticleSystem("ShieldImpactLight", ship.position + evt.impact.normalized(ship.radius * 0.9), quaterniond_fromVecToVec(vec3d_front(), evt.impact), ship.radius, ship.visibleMask, networked=false);
}
else if(dmgScale < 0.05) {
playParticleSystem("ShieldImpactMedium", ship.position + evt.impact.normalized(ship.radius * 0.9), quaterniond_fromVecToVec(vec3d_front(), evt.impact), ship.radius, ship.visibleMask);
}
else {
playParticleSystem("ShieldImpactHeavy", ship.position + evt.impact.normalized(ship.radius * 0.9), quaterniond_fromVecToVec(vec3d_front(), evt.impact), ship.radius, ship.visibleMask, networked=false);
}
double block;
if(ship.MaxShield > 0)
block = min(ship.Shield * min(ship.Shield / maxShield, 1.0), evt.damage);
else
block = min(ship.Shield, evt.damage);
ship.Shield -= block;
evt.damage -= block;
if(evt.damage <= 0.0)
return DE_EndDamage;
}
return DE_Continue;
}
DamageEventStatus ShieldBlock(DamageEvent& evt, vec2u& position, vec2d& direction, double Chance) {
Ship@ ship = cast<Ship>(evt.target);
//The lower the shield strength, the lower the chance
if(ship !is null) {
double maxShield = ship.MaxShield;
double shield = ship.Shield;
if(maxShield <= 0.0001 || shield <= 0.0001)
return DE_Continue;
Chance *= (shield / maxShield);
}
//The more damaged the hardener, the lower the chance
Chance *= double(evt.destination_status.workingHexes) / double(evt.destination.hexCount);
//Deal with partiality in the chance
if(evt.partiality != 1.0)
Chance = pow(Chance, 1.0 / double(evt.partiality));
//Fully block hits with a particular chance
if(Chance > 0.001 && randomd() < Chance) {
evt.damage = 0.0;
return DE_EndDamage;
}
return DE_Continue;
}