int CArmorClass::GetMaxHP (CItemCtx &ItemCtx) // GetMaxHP // // Returns the max HP for this kind of armor { // Start with hit points defined by the class int iHP = m_iHitPoints; // Fire event to compute HP, if necessary iHP = FireGetMaxHP(ItemCtx, iHP); // Add mods const CItemEnhancement &Mods = ItemCtx.GetMods(); if (Mods.IsNotEmpty()) iHP = iHP * Mods.GetHPAdj() / 100; // Add complete bonus CInstalledArmor *pSect = ItemCtx.GetArmor(); if (pSect && pSect->IsComplete()) iHP += m_iArmorCompleteBonus; // Done return iHP; }
int CArmorClass::FireGetMaxHP (CItemCtx &ItemCtx, int iMaxHP) const // FireGetMaxHP // // Fire GetMaxHP event { SEventHandlerDesc Event; if (FindEventHandlerArmorClass(evtGetMaxHP, &Event)) { // Setup arguments CCodeChainCtx Ctx; Ctx.SaveAndDefineSourceVar(ItemCtx.GetSource()); Ctx.SaveAndDefineItemVar(ItemCtx); Ctx.DefineInteger(CONSTLIT("aMaxHP"), iMaxHP); ICCItem *pResult = Ctx.Run(Event); if (pResult->IsError()) ItemCtx.GetSource()->ReportEventError(GET_MAX_HP_EVENT, pResult); else if (!pResult->IsNil()) iMaxHP = Max(0, pResult->GetIntegerValue()); Ctx.Discard(pResult); } return iMaxHP; }
bool CDeviceClass::AccumulateEnhancements (CItemCtx &Device, CInstalledDevice *pTarget, TArray<CString> &EnhancementIDs, CItemEnhancementStack *pEnhancements) // AccumulateEnhancements // // If this device can enhance pTarget, then we add to the list of enhancements. { int i; bool bEnhanced = false; CInstalledDevice *pDevice = Device.GetDevice(); CSpaceObject *pSource = Device.GetSource(); // See if we can enhance the target device if (pDevice == NULL || (pDevice->IsEnabled() && !pDevice->IsDamaged())) { for (i = 0; i < m_Enhancements.GetCount(); i++) { // If this type of enhancement has already been applied, skip it if (!m_Enhancements[i].sType.IsBlank() && EnhancementIDs.Find(m_Enhancements[i].sType)) continue; // If we don't match the criteria, skip it. if (pSource && pTarget && !pSource->GetItemForDevice(pTarget).MatchesCriteria(m_Enhancements[i].Criteria)) continue; // Add the enhancement pEnhancements->Insert(m_Enhancements[i].Enhancement); bEnhanced = true; // Remember that we added this enhancement class if (!m_Enhancements[i].sType.IsBlank()) EnhancementIDs.Insert(m_Enhancements[i].sType); } } // Let sub-classes add their own if (OnAccumulateEnhancements(Device, pTarget, EnhancementIDs, pEnhancements)) bEnhanced = true; // Done return bEnhanced; }
void CDeviceClass::AccumulateAttributes (CItemCtx &ItemCtx, int iVariant, TArray<SDisplayAttribute> *retList) // AccumulateAttributes // // Add display attributes to the list. { // Add general device attributes. If we have a variant, then it means // we're interested in the attributes for a missile/ammo of the device // (not the device itself). if (iVariant == -1) { CInstalledDevice *pDevice = ItemCtx.GetDevice(); // Linked-fire DWORD dwOptions = GetLinkedFireOptions(ItemCtx); if (dwOptions != 0) retList->Insert(SDisplayAttribute(attribPositive, CONSTLIT("linked-fire"))); } // Let our subclasses add their own attributes OnAccumulateAttributes(ItemCtx, iVariant, retList); }
void CArmorClass::CalcAdjustedDamage (CItemCtx &ItemCtx, SDamageCtx &Ctx) // CalcAdjustedDamage // // Modifies Ctx.iDamage to account for damage type adjustments, etc. { CInstalledArmor *pArmor = ItemCtx.GetArmor(); // Adjust for special armor damage: // // <0 = 2.5x damage // 0 = 2x damage // 1 = 1.5x damage // 2 = 1.25x damage // >2 = 1x damage int iDamageLevel = Ctx.Damage.GetArmorDamageLevel(); if (iDamageLevel > 0) Ctx.iDamage = (CalcArmorDamageAdj(Ctx.Damage) * Ctx.iDamage + 50) / 100; // Adjust for damage type int iDamageAdj = GetDamageAdj((pArmor ? pArmor->GetMods() : CItemEnhancement()), Ctx.Damage); Ctx.iDamage = (iDamageAdj * Ctx.iDamage + 50) / 100; }
void CShieldClass::CalcMinMaxHP (CItemCtx &Ctx, int iCharges, int iArmorSegs, int iTotalHP, int *retiMin, int *retiMax) const // CalcMinMaxHP // // Returns the min and max HP of this shield // // iCharges = m_iMaxCharges or the current charges on item // iArmorSegs = count of armor segments on ship (or 0) // iTotalHP = current total HP of all armor segments (or 0) { int iMax = m_iHitPoints; int iMin = iMax; if (m_iExtraHPPerCharge) iMax = Max(0, iMax + (m_iExtraHPPerCharge * iCharges)); if (m_iArmorShield) { iMin = m_iArmorShield; if (iArmorSegs > 0) iMax = Min(iMax, ((m_iArmorShield * iTotalHP / iArmorSegs) + 5) / 10); } // If we're installed, fire the custom event to get max HPs if (Ctx.GetSource() && Ctx.GetDevice()) iMax = FireGetMaxHP(Ctx.GetDevice(), Ctx.GetSource(), iMax); // Mods const CItemEnhancement &Mods = Ctx.GetMods(); if (Mods.IsNotEmpty()) iMax = iMax * Mods.GetHPAdj() / 100; // Done if (iMin > iMax) iMin = iMax; if (retiMin) *retiMin = iMin; if (retiMax) *retiMax = iMax; }
void CCodeChainCtx::DefineItem (const CString &sVar, CItemCtx &ItemCtx) // DefineItem // // Defines a CItem variable { ICCItem *pItem = ItemCtx.CreateItemVariable(m_CC); m_CC.DefineGlobal(sVar, pItem); pItem->Discard(&m_CC); }
CString CShieldClass::GetReference (CItemCtx &Ctx, int iVariant, DWORD dwFlags) // GetReference // // Returns a string that describes the basic attributes // of this shield // // Example: // // 20 hp (average regen); 100MW { int i; CString sReference; CString sRegeneration; const CItemEnhancement &Mods = Ctx.GetMods(); // Compute the strength string int iMin, iMax; CalcMinMaxHP(Ctx, m_iMaxCharges, 0, 0, &iMin, &iMax); // Compute the regeneration if (m_iRegenHP > 0) { int iRate = (int)((10.0 * g_TicksPerSecond * m_iRegenHP / m_iRegenRate) + 0.5); if (iRate == 0) sRegeneration = CONSTLIT("<0.1 hp/sec"); else if ((iRate % 10) == 0) sRegeneration = strPatternSubst(CONSTLIT("%d hp/sec"), iRate / 10); else sRegeneration = strPatternSubst(CONSTLIT("%d.%d hp/sec"), iRate / 10, iRate % 10); } else sRegeneration = CONSTLIT("none"); sReference = strPatternSubst("%s — regen @ %s", GetReferencePower(Ctx), sRegeneration); // Reflection for (i = 0; i < damageCount; i++) { if (m_Reflective.InSet((DamageTypes)i) || (Mods.IsReflective() && Mods.GetDamageType() == i)) sReference.Append(strPatternSubst(CONSTLIT(" — %s-reflecting"), GetDamageShortName((DamageTypes)i))); } return sReference; }
bool CArmorClass::IsReflective (CItemCtx &ItemCtx, const DamageDesc &Damage) // IsReflective // // Returns TRUE if the armor reflects this damage { const CItemEnhancement &Mods = ItemCtx.GetMods(); int iReflectChance = 0; // Base armor chance if (m_Reflective.InSet(Damage.GetDamageType())) iReflectChance = MAX_REFLECTION_CHANCE; // Mods int iModReflect; if (Mods.IsNotEmpty() && Mods.IsReflective(Damage, &iModReflect)) iReflectChance = Max(iReflectChance, iModReflect); // Done if (iReflectChance) { CInstalledArmor *pSect = ItemCtx.GetArmor(); int iMaxHP = GetMaxHP(ItemCtx); int iHP = (pSect ? pSect->GetHitPoints() : iMaxHP); // Adjust based on how damaged the armor is iReflectChance = (iMaxHP > 0 ? iHP * iReflectChance / iMaxHP : iReflectChance); return (mathRandom(1, 100) <= iReflectChance); } else return false; }
void CArmorClass::CalcAdjustedDamage (CItemCtx &ItemCtx, SDamageCtx &Ctx) // CalcAdjustedDamage // // Modifies Ctx.iDamage to account for damage type adjustments, etc. { CInstalledArmor *pArmor = ItemCtx.GetArmor(); // Adjust for special armor damage: // // <0 = 2.5x damage // 0 = 2x damage // 1 = 1.5x damage // 2 = 1.25x damage // >2 = 1x damage int iDamageLevel = Ctx.Damage.GetArmorDamageLevel(); if (iDamageLevel > 0) { int iDiff = m_pItemType->GetLevel() - iDamageLevel; int iAdj; switch (iDiff) { case 0: iAdj = 200; break; case 1: iAdj = 150; break; case 2: iAdj = 125; break; default: if (iDiff < 0) iAdj = 250; else iAdj = 100; } Ctx.iDamage = (iAdj * Ctx.iDamage + 50) / 100; } // Adjust for damage type int iDamageAdj = GetDamageAdj((pArmor ? pArmor->GetMods() : CItemEnhancement()), Ctx.Damage); Ctx.iDamage = (iDamageAdj * Ctx.iDamage + 50) / 100; }
int CItemEnhancementStack::CalcActivateDelay (CItemCtx &DeviceCtx) const // CalcActivateDelay // // Calculates the activation delay (in ticks) for the given device if we apply // this enhancement stack. { int i; CInstalledDevice *pDevice = DeviceCtx.GetDevice(); if (pDevice == NULL) return 0; Metric rDelay = -1.0; for (i = 0; i < m_Stack.GetCount(); i++) { int iMin, iMax; int iAdj = m_Stack[i].GetActivateRateAdj(&iMin, &iMax); if (iAdj != 100) { if (rDelay < 0.0) { pDevice->SetActivateDelayAdj(100); rDelay = pDevice->GetActivateDelay(DeviceCtx.GetSource()); } rDelay = iAdj * rDelay / 100.0; if (rDelay < (Metric)iMin) rDelay = (Metric)iMin; else if (iMax > 0 && rDelay > (Metric)iMax) rDelay = (Metric)iMax; } } return (rDelay < 0.0 ? 100 : (int)(rDelay + 0.5)); }
int CItemEnhancementStack::CalcActivateDelay (CItemCtx &DeviceCtx) const // CalcActivateDelay // // Calculates the activation delay (in ticks) for the given device if we apply // this enhancement stack. { int i; CInstalledDevice *pDevice = DeviceCtx.GetDevice(); if (pDevice == NULL) return 0; // Get the raw activation delay. NOTE: This DOES NOT include // any enhancements on the item. Metric rDelay = pDevice->GetClass()->GetActivateDelay(pDevice, DeviceCtx.GetSource()); // Apply enhancements (including on the item itself) for (i = 0; i < m_Stack.GetCount(); i++) { int iMin, iMax; int iAdj = m_Stack[i].GetActivateRateAdj(&iMin, &iMax); if (iAdj != 100) { rDelay = iAdj * rDelay / 100.0; if (rDelay < (Metric)iMin) rDelay = (Metric)iMin; else if (iMax > 0 && rDelay > (Metric)iMax) rDelay = (Metric)iMax; } } return (int)(rDelay + 0.5); }
void CArmorClass::FireOnArmorDamage (CItemCtx &ItemCtx, SDamageCtx &Ctx) // FireOnArmorDamage // // Fires OnArmorDamage event { SEventHandlerDesc Event; if (FindEventHandlerArmorClass(evtOnArmorDamage, &Event)) { // Setup arguments CCodeChainCtx CCCtx; CCCtx.SaveAndDefineSourceVar(ItemCtx.GetSource()); CCCtx.SaveAndDefineItemVar(ItemCtx); CCCtx.DefineInteger(CONSTLIT("aArmorHP"), Ctx.iHPLeft); CCCtx.DefineInteger(CONSTLIT("aArmorSeg"), Ctx.iSectHit); CCCtx.DefineSpaceObject(CONSTLIT("aAttacker"), Ctx.Attacker.GetObj()); CCCtx.DefineSpaceObject(CONSTLIT("aCause"), Ctx.pCause); CCCtx.DefineDamageEffects(CONSTLIT("aDamageEffects"), Ctx); CCCtx.DefineInteger(CONSTLIT("aDamageHP"), Ctx.iDamage); CCCtx.DefineString(CONSTLIT("aDamageType"), GetDamageShortName(Ctx.Damage.GetDamageType())); CCCtx.DefineInteger(CONSTLIT("aHitDir"), Ctx.iDirection); CCCtx.DefineVector(CONSTLIT("aHitPos"), Ctx.vHitPos); CCCtx.DefineSpaceObject(CONSTLIT("aOrderGiver"), (Ctx.Attacker.GetObj() ? Ctx.Attacker.GetObj()->GetOrderGiver(Ctx.Attacker.GetCause()) : NULL)); CCCtx.DefineItemType(CONSTLIT("aWeaponType"), Ctx.pDesc->GetWeaponType()); ICCItem *pResult = CCCtx.Run(Event); // If we return Nil, then nothing if (pResult->IsNil()) NULL; // If we return an integer, then this is the damage that armor should take else if (pResult->IsInteger()) Ctx.iDamage = pResult->GetIntegerValue(); // If we return a list, then we it to be a DamageEffects list (modifications to // aDamageEffects) else if (pResult->IsList()) LoadDamageEffectsFromItem(pResult, Ctx); CCCtx.Discard(pResult); } }
int CShieldClass::GetPowerRating (CItemCtx &Ctx) // GetPowerRating // // Returns the power rating of the item { int iPower = m_iPowerUse; const CItemEnhancement &Mods = Ctx.GetMods(); if (Mods.IsNotEmpty()) iPower = iPower * Mods.GetPowerAdj() / 100; return iPower; }
CString CShieldClass::GetReference (CItemCtx &Ctx, int iVariant, DWORD dwFlags) // GetReference // // Returns a string that describes the basic attributes // of this shield // // Example: // // 20 hp (average regen); 100MW { int i; CString sReference; const CItemEnhancement &Mods = Ctx.GetMods(); // Compute the strength string int iMin, iMax; CalcMinMaxHP(Ctx, m_iMaxCharges, 0, 0, &iMin, &iMax); // Compute the regeneration sReference = strPatternSubst("%s — regen @ %s", GetReferencePower(Ctx), m_Regen.GetReferenceRate(CONSTLIT("hp/sec"))); // Reflection for (i = 0; i < damageCount; i++) { if (m_Reflective.InSet((DamageTypes)i) || (Mods.IsReflective() && Mods.GetDamageType() == i)) sReference.Append(strPatternSubst(CONSTLIT(" — %s-reflecting"), GetDamageShortName((DamageTypes)i))); } return sReference; }
CString CDeviceClass::GetReference (CItemCtx &Ctx, int iVariant, DWORD dwFlags) // GetReference // // Returns reference string { CString sReference; // For a device we always add power and other properties. // (If iVariant != -1 then it means that we're looking for reference on a // missile or someting). if (iVariant == -1) { CInstalledDevice *pDevice = Ctx.GetDevice(); // Start with power requirements AppendReferenceString(&sReference, GetReferencePower(Ctx)); // Non-standard slots if (GetSlotsRequired() != 1) AppendReferenceString(&sReference, strPatternSubst(CONSTLIT("%d Slots"), GetSlotsRequired())); // External devices if (IsExternal() || (pDevice && pDevice->IsExternal())) AppendReferenceString(&sReference, CONSTLIT("External")); } // Combine with our subclass AppendReferenceString(&sReference, OnGetReference(Ctx, iVariant, dwFlags)); return sReference; }
void CShieldClass::FireOnShieldDamage (CItemCtx &ItemCtx, SDamageCtx &Ctx) // FireOnShieldDamage // // Fire OnShieldDamage { SEventHandlerDesc Event; if (FindEventHandlerShieldClass(evtOnShieldDamage, &Event)) { // Setup arguments CCodeChainCtx CCCtx; CCCtx.SaveAndDefineSourceVar(ItemCtx.GetSource()); CCCtx.SaveAndDefineItemVar(ItemCtx); CCCtx.DefineInteger(CONSTLIT("aArmorSeg"), Ctx.iSectHit); CCCtx.DefineSpaceObject(CONSTLIT("aCause"), Ctx.pCause); CCCtx.DefineSpaceObject(CONSTLIT("aAttacker"), Ctx.Attacker.GetObj()); CCCtx.DefineSpaceObject(CONSTLIT("aOrderGiver"), (Ctx.Attacker.GetObj() ? Ctx.Attacker.GetObj()->GetOrderGiver(Ctx.Attacker.GetCause()) : NULL)); CCCtx.DefineVector(CONSTLIT("aHitPos"), Ctx.vHitPos); CCCtx.DefineInteger(CONSTLIT("aHitDir"), Ctx.iDirection); CCCtx.DefineInteger(CONSTLIT("aDamageHP"), Ctx.iDamage); CCCtx.DefineString(CONSTLIT("aDamageType"), GetDamageShortName(Ctx.Damage.GetDamageType())); CCCtx.DefineItemType(CONSTLIT("aWeaponType"), Ctx.pDesc->GetWeaponType()); CCCtx.DefineInteger(CONSTLIT("aShieldHP"), Ctx.iHPLeft); CCCtx.DefineInteger(CONSTLIT("aShieldDamageHP"), Ctx.iShieldDamage); CCCtx.DefineInteger(CONSTLIT("aArmorDamageHP"), Ctx.iDamage - Ctx.iAbsorb); if (Ctx.bReflect) { CCCtx.DefineString(CONSTLIT("aShieldReflect"), STR_SHIELD_REFLECT); CCCtx.DefineInteger(CONSTLIT("aOriginalShieldDamageHP"), Ctx.iOriginalShieldDamage); CCCtx.DefineInteger(CONSTLIT("aOriginalArmorDamageHP"), Ctx.iDamage - Ctx.iOriginalAbsorb); } else { CCCtx.DefineNil(CONSTLIT("aShieldReflect")); CCCtx.DefineInteger(CONSTLIT("aOriginalShieldDamageHP"), Ctx.iShieldDamage); CCCtx.DefineInteger(CONSTLIT("aOriginalArmorDamageHP"), Ctx.iDamage - Ctx.iAbsorb); } ICCItem *pResult = CCCtx.Run(Event); // If we return Nil, then nothing if (pResult->IsNil()) NULL; // If an error, report it else if (pResult->IsError()) ItemCtx.GetSource()->ReportEventError(ON_SHIELD_DAMAGE_EVENT, pResult); // If we return a list, then modify variables else if (pResult->IsList()) { // A single value means we modified the damage to armor if (pResult->GetCount() == 1) { if (strEquals(pResult->GetElement(0)->GetStringValue(), STR_SHIELD_REFLECT)) { Ctx.bReflect = true; Ctx.iAbsorb = Ctx.iDamage; Ctx.iShieldDamage = 0; } else { Ctx.iShieldDamage = Max(0, Min(pResult->GetElement(0)->GetIntegerValue(), Ctx.iHPLeft)); if (Ctx.bReflect) { Ctx.bReflect = false; Ctx.iAbsorb = Ctx.iOriginalAbsorb; } } } // Two values mean we modified both damage to armor and shield damage else if (pResult->GetCount() == 2) { Ctx.bReflect = false; Ctx.iShieldDamage = Max(0, Min(pResult->GetElement(0)->GetIntegerValue(), Ctx.iHPLeft)); Ctx.iAbsorb = Max(0, Ctx.iDamage - Max(0, pResult->GetElement(1)->GetIntegerValue())); } // Otherwise, we deal with reflection else { Ctx.bReflect = strEquals(pResult->GetElement(0)->GetStringValue(), STR_SHIELD_REFLECT); Ctx.iShieldDamage = Max(0, Min(pResult->GetElement(1)->GetIntegerValue(), Ctx.iHPLeft)); Ctx.iAbsorb = Max(0, Ctx.iDamage - Max(0, pResult->GetElement(2)->GetIntegerValue())); } } CCCtx.Discard(pResult); } }
void CArmorClass::CalcDamageEffects (CItemCtx &ItemCtx, SDamageCtx &Ctx) // CalcDamageEffects // // Initialize the damage effects based on the damage and on this armor type. { CSpaceObject *pSource = ItemCtx.GetSource(); CInstalledArmor *pArmor = ItemCtx.GetArmor(); // Compute all the effects (if we don't have installed armor, then the // caller is responsible for setting this). if (pArmor) Ctx.iHPLeft = pArmor->GetHitPoints(); // Reflect Ctx.bReflect = (IsReflective(ItemCtx, Ctx.Damage) && Ctx.iDamage > 0); // Disintegration int iDisintegration = Ctx.Damage.GetDisintegrationDamage(); Ctx.bDisintegrate = (iDisintegration > 0 && !IsDisintegrationImmune(ItemCtx)); // Shatter int iShatter = Ctx.Damage.GetShatterDamage(); if (iShatter) { // Compute the threshold mass. Below this size, we shatter the object int iMassLimit = 10 * mathPower(5, iShatter); Ctx.bShatter = (pSource && pSource->GetMass() < iMassLimit); } else Ctx.bShatter = false; // Blinding int iBlinding = Ctx.Damage.GetBlindingDamage(); if (iBlinding && !IsBlindingDamageImmune(ItemCtx)) { // The chance of being blinded is dependent // on the rating. int iChance = 4 * iBlinding * iBlinding * GetBlindingDamageAdj() / 100; Ctx.bBlind = (mathRandom(1, 100) <= iChance); Ctx.iBlindTime = Ctx.iDamage * g_TicksPerSecond / 2; } else Ctx.bBlind = false; // EMP int iEMP = Ctx.Damage.GetEMPDamage(); if (iEMP && !IsEMPDamageImmune(ItemCtx)) { // The chance of being paralyzed is dependent // on the EMP rating. int iChance = 4 * iEMP * iEMP * GetEMPDamageAdj() / 100; Ctx.bParalyze = (mathRandom(1, 100) <= iChance); Ctx.iParalyzeTime = Ctx.iDamage * g_TicksPerSecond / 2; } else Ctx.bParalyze = false; // Device disrupt int iDeviceDisrupt = Ctx.Damage.GetDeviceDisruptDamage(); if (iDeviceDisrupt && !IsDeviceDamageImmune(ItemCtx)) { // The chance of damaging a device depends on the rating. int iChance = 4 * iDeviceDisrupt * iDeviceDisrupt * GetDeviceDamageAdj() / 100; Ctx.bDeviceDisrupt = (mathRandom(1, 100) <= iChance); Ctx.iDisruptTime = 2 * Ctx.iDamage * g_TicksPerSecond; } else Ctx.bDeviceDisrupt = false; // Device damage int iDeviceDamage = Ctx.Damage.GetDeviceDamage(); if (iDeviceDamage && !IsDeviceDamageImmune(ItemCtx)) { // The chance of damaging a device depends on the rating. int iChance = 4 * iDeviceDamage * iDeviceDamage * GetDeviceDamageAdj() / 100; Ctx.bDeviceDamage = (mathRandom(1, 100) <= iChance); } else Ctx.bDeviceDamage = false; // Radiation int iRadioactive = Ctx.Damage.GetRadiationDamage(); Ctx.bRadioactive = (iRadioactive > 0 && !IsRadiationImmune(ItemCtx)); // Some effects decrease damage if (iBlinding || iEMP) Ctx.iDamage = 0; else if (iDeviceDamage) Ctx.iDamage = Ctx.iDamage / 2; }
EDamageResults CArmorClass::AbsorbDamage (CItemCtx &ItemCtx, SDamageCtx &Ctx) // AbsorbDamage // // Handles getting hit by damage. // // Returns damageNoDamage if all the damage was absorbed and no further processing is necessary // Returns damageDestroyed if the source was destroyed // Returns damageArmorHit if source was damage and further processing (destroy check) is needed // // Sets Ctx.iDamage to the amount of hit points left after damage absorption. { CSpaceObject *pSource = ItemCtx.GetSource(); CInstalledArmor *pArmor = ItemCtx.GetArmor(); if (pSource == NULL || pArmor == NULL) return damageNoDamage; // Compute all the effects (this initializes elements in Ctx). CalcDamageEffects(ItemCtx, Ctx); // First give custom weapons a chance bool bCustomDamage = Ctx.pDesc->FireOnDamageArmor(Ctx); if (pSource->IsDestroyed()) return damageDestroyed; // Damage adjustment CalcAdjustedDamage(ItemCtx, Ctx); // If the armor has custom code to deal with damage, handle it here. FireOnArmorDamage(ItemCtx, Ctx); if (pSource->IsDestroyed()) return damageDestroyed; // If this armor section reflects this kind of damage then // send the damage on if (Ctx.bReflect) { if (Ctx.pCause) Ctx.pCause->CreateReflection(Ctx.vHitPos, (Ctx.iDirection + 120 + mathRandom(0, 120)) % 360); return damageNoDamage; } // If this is a disintegration attack, then disintegrate the ship if (Ctx.bDisintegrate) { if (!pSource->OnDestroyCheck(killedByDisintegration, Ctx.Attacker)) return damageNoDamage; pSource->Destroy(killedByDisintegration, Ctx.Attacker); return damageDestroyed; } // If this is a shatter attack, see if the ship is destroyed if (Ctx.bShatter) { if (!pSource->OnDestroyCheck(killedByShatter, Ctx.Attacker)) return damageNoDamage; pSource->Destroy(killedByShatter, Ctx.Attacker); return damageDestroyed; } // If this is a paralysis attack and we've gotten past the shields // then freeze the ship. if (Ctx.bParalyze) pSource->MakeParalyzed(Ctx.iParalyzeTime); // If this is blinding damage then our sensors are disabled if (Ctx.bBlind) pSource->MakeBlind(Ctx.iBlindTime); // If this attack is radioactive, then contaminate the ship if (Ctx.bRadioactive) pSource->OnHitByRadioactiveDamage(Ctx); // If this is device damage, then see if any device is damaged if (Ctx.bDeviceDamage) pSource->OnHitByDeviceDamage(); if (Ctx.bDeviceDisrupt) pSource->OnHitByDeviceDisruptDamage(Ctx.iDisruptTime); // Create a hit effect. (Many weapons show an effect even if no damage was // done.) Ctx.pDesc->CreateHitEffect(pSource->GetSystem(), Ctx); // If no damage has reached us, then we're done if (Ctx.iDamage == 0 && !bCustomDamage) return damageNoDamage; // Give source events a chance to change the damage before we // subtract from armor. if (pSource->HasOnDamageEvent()) { pSource->FireOnDamage(Ctx); if (pSource->IsDestroyed()) return damageDestroyed; } // Take damage if (Ctx.iDamage <= pArmor->GetHitPoints()) { pArmor->IncHitPoints(-Ctx.iDamage); Ctx.iDamage = 0; } else { Ctx.iDamage -= pArmor->GetHitPoints(); pArmor->SetHitPoints(0); } return damageArmorHit; }
CString CArmorClass::GetReference (CItemCtx &Ctx, int iVariant) // GetReference // // Returns a string that describes the basic attributes // of this armor. // // Example: // // 30 hp; laser-resistant; impact-resistant { int i; CString sReference; // Get modifications int iLevel = m_pItemType->GetLevel(); const CItemEnhancement &Mods = Ctx.GetMods(); // Radiation if (m_fRadiationImmune || Mods.IsRadiationImmune()) { if (iLevel < RADIATION_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("radiation-immune")); } else if (iLevel >= RADIATION_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("radiation-vulnerable")); // If we're immune to blinding/EMP/device damage, then collapse // it all under a single entry bool bCheckedBlind = false; bool bCheckedEMP = false; bool bCheckedDevice = false; if ((m_iBlindingDamageAdj == 0 || Mods.IsBlindingImmune()) && (m_iEMPDamageAdj == 0 || Mods.IsEMPImmune()) && (m_iDeviceDamageAdj < 100 || Mods.IsDeviceDamageImmune())) { if (iLevel < DEVICE_DAMAGE_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("ion effect-immune")); bCheckedBlind = true; bCheckedEMP = true; bCheckedDevice = true; } // Collapse blind and EMP resistance else if ((m_iBlindingDamageAdj == 0 || Mods.IsBlindingImmune()) && (m_iEMPDamageAdj == 0 || Mods.IsEMPImmune())) { if (iLevel < EMP_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("blind-, EMP-immune")); bCheckedBlind = true; bCheckedEMP = true; } else if ((m_iBlindingDamageAdj < 100) && (iLevel < BLIND_IMMUNE_LEVEL) && (m_iEMPDamageAdj < 100)) { AppendReferenceString(&sReference, CONSTLIT("blind-, EMP-resistant")); bCheckedBlind = true; bCheckedEMP = true; } // Otherwise, treat each separate // // Blindness if (!bCheckedBlind) { if (m_iBlindingDamageAdj == 0 || Mods.IsBlindingImmune()) { if (iLevel < BLIND_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("blind-immune")); } else if (m_iBlindingDamageAdj < 100) { if (iLevel < BLIND_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("blind-resistant")); else AppendReferenceString(&sReference, CONSTLIT("blind-vulnerable")); } else if (iLevel >= BLIND_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("blind-vulnerable")); } // EMP if (!bCheckedEMP) { if (m_iEMPDamageAdj == 0 || Mods.IsEMPImmune()) { if (iLevel < EMP_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("EMP-immune")); } else if (m_iEMPDamageAdj < 100) { if (iLevel < EMP_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("EMP-resistant")); else AppendReferenceString(&sReference, CONSTLIT("EMP-vulnerable")); } else if (iLevel >= EMP_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("EMP-vulnerable")); } // Device damage if (!bCheckedDevice) { if (m_iDeviceDamageAdj < 100 || Mods.IsDeviceDamageImmune()) { if (iLevel < DEVICE_DAMAGE_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("device-protect")); } else if (iLevel >= DEVICE_DAMAGE_IMMUNE_LEVEL) AppendReferenceString(&sReference, CONSTLIT("device-vulnerable")); } // Disintegration if (m_fDisintegrationImmune || Mods.IsDisintegrationImmune()) AppendReferenceString(&sReference, CONSTLIT("disintegrate-immune")); // Shatter if (IsShatterImmune(Ctx)) AppendReferenceString(&sReference, CONSTLIT("shatter-immune")); // Shield interference if (m_fShieldInterference || Mods.IsShieldInterfering()) AppendReferenceString(&sReference, CONSTLIT("no-shields")); // Photo repair if (m_fPhotoRepair || Mods.IsPhotoRegenerating()) AppendReferenceString(&sReference, CONSTLIT("photo-repair")); // Solar power if (m_fPhotoRecharge || Mods.IsPhotoRecharge()) AppendReferenceString(&sReference, CONSTLIT("solar")); // Regeneration if ((!m_Regen.IsEmpty() && !m_fPhotoRepair) || Mods.IsRegenerating()) AppendReferenceString(&sReference, CONSTLIT("regenerate")); // Decay if (!m_Decay.IsEmpty() || Mods.IsDecaying()) AppendReferenceString(&sReference, CONSTLIT("decay")); // Reflection for (i = 0; i < damageCount; i++) { if (m_Reflective.InSet((DamageTypes)i) || (Mods.IsReflective() && Mods.GetDamageType() == i)) AppendReferenceString(&sReference, strPatternSubst(CONSTLIT("%s-reflecting"), GetDamageShortName((DamageTypes)i))); } // Done return sReference; }
bool CDeviceClass::SetItemProperty (CItemCtx &Ctx, const CString &sName, ICCItem *pValue, CString *retsError) // SetItemProperty // // Sets a device property. Subclasses should call this if they do not // understand the property. { CCodeChain &CC = g_pUniverse->GetCC(); // Get the installed device. We need this to set anything. CInstalledDevice *pDevice = Ctx.GetDevice(); if (pDevice == NULL) { *retsError = CONSTLIT("Item is not an installed device on object."); return false; } // Figure out what to set if (strEquals(sName, PROPERTY_FIRE_ARC)) { // A value of nil means no fire arc (and no omni) if (pValue == NULL || pValue->IsNil()) { pDevice->SetOmniDirectional(false); pDevice->SetFireArc(0, 0); } // A value of "omnidirectional" counts else if (strEquals(pValue->GetStringValue(), PROPERTY_OMNIDIRECTIONAL)) { pDevice->SetOmniDirectional(true); pDevice->SetFireArc(0, 0); } // A single value means that we just point in a direction else if (pValue->GetCount() == 1) { int iMinFireArc = AngleMod(pValue->GetElement(0)->GetIntegerValue()); pDevice->SetOmniDirectional(false); pDevice->SetFireArc(iMinFireArc, iMinFireArc); } // Otherwise we expect a list with two elements else if (pValue->GetCount() >= 2) { int iMinFireArc = AngleMod(pValue->GetElement(0)->GetIntegerValue()); int iMaxFireArc = AngleMod(pValue->GetElement(1)->GetIntegerValue()); pDevice->SetOmniDirectional(false); pDevice->SetFireArc(iMinFireArc, iMaxFireArc); } // Invalid else { *retsError = CONSTLIT("Invalid fireArc parameter."); return false; } } else if (strEquals(sName, PROPERTY_LINKED_FIRE_OPTIONS)) { // Parse the options DWORD dwOptions; if (!::GetLinkedFireOptions(pValue, &dwOptions, retsError)) return false; // Set pDevice->SetLinkedFireOptions(dwOptions); } else if (strEquals(sName, PROPERTY_POS)) { // Get the parameters. We accept a single list parameter with angle/radius/z. // (The latter is compatible with the return of objGetDevicePos.) int iPosAngle; int iPosRadius; int iZ; if (pValue == NULL || pValue->IsNil()) { iPosAngle = 0; iPosRadius = 0; iZ = 0; } else if (pValue->GetCount() >= 2) { iPosAngle = pValue->GetElement(0)->GetIntegerValue(); iPosRadius = pValue->GetElement(1)->GetIntegerValue(); if (pValue->GetCount() >= 3) iZ = pValue->GetElement(2)->GetIntegerValue(); else iZ = 0; } else { *retsError = CONSTLIT("Invalid angle and radius"); return false; } // Set it pDevice->SetPosAngle(iPosAngle); pDevice->SetPosRadius(iPosRadius); pDevice->SetPosZ(iZ); } else if (strEquals(sName, PROPERTY_SECONDARY)) { if (pValue == NULL || !pValue->IsNil()) pDevice->SetSecondary(true); else pDevice->SetSecondary(false); } else { *retsError = strPatternSubst(CONSTLIT("Unknown item property: %s."), sName); return false; } return true; }