int CShieldClass::GetDamageAdj (CItemEnhancement Mods, const DamageDesc &Damage) const // GetDamageAdj // // Damage adjustment { // The adjustment varies by shield class int iAdj = m_DamageAdj.GetAdj(Damage.GetDamageType()); // Adjust based on difference in level (negative numbers means the shield // is lower than the damage level): // // ... // -3 = 4.5x damage // -2 = 4x damage // -1 = 3.5x damage // 0 = 3x damage // 1 = 2.5x damage // 2 = 2x damage // 3 = 1.5x damage // >3 = 1x damage if (Damage.GetShieldDamageLevel()) iAdj = iAdj * Max(100, 300 + (50 * (Damage.GetShieldDamageLevel() - GetLevel()))) / 100; // Adjust based on enhancements if (Mods.IsNotEmpty()) iAdj = iAdj * Mods.GetDamageAdj(Damage) / 100; return iAdj; }
CSpaceObject::DamageResults CMissile::Damage (CSpaceObject *pCause, const CVector &vHitPos, int iDirection, const DamageDesc &Damage) // Damage // // Object takes damage from the given source { CSpaceObject *pAttacker = pCause->GetDamageCause(); // Compute damage bool bDestroy = false; int iDamage = Damage.RollDamage(); if (iDamage == 0) return damageNoDamage; // If this is a momentum attack then we are pushed int iMomentum; if (iMomentum = Damage.GetMomentumDamage()) { CVector vAccel = PolarToVector(iDirection, -10 * iMomentum * iMomentum); Accelerate(vAccel, g_MomentumConstant); ClipSpeed(GetMaxSpeed()); } // Create a hit effect CEffectCreator *pEffect = g_pUniverse->FindEffectType(g_HitEffectUNID); if (pEffect) pEffect->CreateEffect(GetSystem(), this, vHitPos, GetVel()); // Take damage if (iDamage < m_iHitPoints) { m_iHitPoints -= iDamage; return damageArmorHit; } // We are destroyed m_iHitPoints = 0; if (m_pDesc->m_iVaporTrailLength) { m_fDestroyed = true; m_iLifeLeft = m_pDesc->m_iVaporTrailLength; } else Destroy(killedByDamage, pAttacker); // A missile might be able to pass through after hitting us return damagePassthrough; }
bool CParticleEffect::CanBeHitBy (const DamageDesc &Damage) // CanBeHitBy // // Returns TRUE if the given type of damage can hit this object { // Only massless particles can hit a particle field return (Damage.GetDamageType() == damageLaser || Damage.GetDamageType() == damageParticle || Damage.GetDamageType() == damageIonRadiation || Damage.GetDamageType() == damagePositron); }
bool CItemEnhancement::IsReflective (const DamageDesc &Damage, int *retiReflectChance) const // IsReflective // // Returns TRUE if we reflect the given damage { switch (GetType()) { case etReflect: { if (!IsDisadvantage() && Damage.GetDamageType() == GetDamageType()) { if (retiReflectChance) *retiReflectChance = 50 + (GetLevel() * 5); return true; } else return false; } default: return false; } }
int CArmorClass::GetDamageAdj (CItemEnhancement Mods, const DamageDesc &Damage) // GetDamageAdj // // Returns the damage adjustment for the given damage type { int iDamageAdj = GetDamageAdj(Damage.GetDamageType()); if (Mods.IsNotEmpty()) return iDamageAdj * Mods.GetDamageAdj(Damage) / 100; else return iDamageAdj; }
int CShieldClass::GetDamageAdj (CItemEnhancement Mods, const DamageDesc &Damage) const // GetDamageAdj // // Damage adjustment { // The adjustment varies by shield class int iAdj = (Damage.GetDamageType() == damageGeneric ? 100 : m_iDamageAdj[Damage.GetDamageType()]); // Adjust based on the type of damage if (Damage.GetShieldDamage()) iAdj = iAdj * Max(100, 300 + (50 * (Damage.GetShieldDamageLevel() - GetLevel()))) / 100; // Adjust based on enhancements if (Mods.IsNotEmpty()) iAdj = iAdj * Mods.GetDamageAdj(Damage) / 100; return iAdj; }
int CItemEnhancement::GetDamageAdj (const DamageDesc &Damage) const // GetDamageAdj // // Returns the damage adjustment confered by this mod { switch (GetType()) { case etResist: return Level2DamageAdj(GetLevel(), IsDisadvantage()); case etResistEnergy: return (Damage.IsEnergyDamage() ? Level2DamageAdj(GetLevel(), IsDisadvantage()) : 100); case etResistMatter: return (Damage.IsMatterDamage() ? Level2DamageAdj(GetLevel(), IsDisadvantage()) : 100); case etResistByLevel: { if (Damage.GetDamageType() == GetDamageType() || Damage.GetDamageType() == GetDamageType() + 1) return Level2DamageAdj(GetLevel(), IsDisadvantage()); else return 100; } case etResistByDamage: return (Damage.GetDamageType() == GetDamageType() ? Level2DamageAdj(GetLevel(), IsDisadvantage()) : 100); case etResistByDamage2: { if (Damage.GetDamageType() == GetDamageType()) // 0 = 100 100 // 1 = 90 111 // 2 = 80 125 // 3 = 70 143 return Level2DamageAdj(GetLevel(), IsDisadvantage()); else if (Damage.GetDamageType() == GetDamageType() + 2) // 0 = 100 100 // 1 = 95 105 // 2 = 90 112 // 3 = 85 121 return 100 + ((Level2DamageAdj(GetLevel(), IsDisadvantage()) - 100) / 2); else return 100; } default: return 100; } }
int CItemEnhancement::GetAbsorbAdj (const DamageDesc &Damage) const // GetAbsorbAdj // // Returns damage absorbed { switch (GetType()) { case etReflect: { if (IsDisadvantage() && Damage.GetDamageType() == GetDamageType()) return Level2DamageAdj(GetLevel()); else return 100; } default: return 100; } }
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; }
int CArmorClass::CalcArmorDamageAdj (const DamageDesc &Damage) const // CalcArmorDamageAdj // // 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 = Damage.GetArmorDamageLevel(); if (iDamageLevel <= 0) return 100; int iDiff = m_pItemType->GetLevel() - iDamageLevel; switch (iDiff) { case 0: return 200; case 1: return 150; case 2: return 125; default: if (iDiff < 0) return 250; else return 100; } }
ALERROR CParticleEffect::Create (CSystem *pSystem, CXMLElement *pDesc, const CVector &vPos, const CVector &vVel, CParticleEffect **retpEffect) // Create // // Create a new particle effect in the system { ALERROR error; CParticleEffect *pParticles; pParticles = new CParticleEffect; if (pParticles == NULL) return ERR_MEMORY; pParticles->Place(vPos, vVel); pParticles->SetObjectDestructionHook(); pParticles->m_sName = pDesc->GetAttribute(NAME_ATTRIB); // Create the type based on the descriptor SParticleType *pType = new SParticleType; pType->m_fWake = true; pType->m_fMaxRadius = true; pType->rRadius = pDesc->GetAttributeInteger(RADIUS_ATTRIB) * LIGHT_SECOND; pType->rHoleRadius = pDesc->GetAttributeInteger(MIN_RADIUS_ATTRIB) * LIGHT_SECOND; int iDampening = pDesc->GetAttributeInteger(DAMPENING_ATTRIB); if (iDampening > 0) pType->rDampening = iDampening / 1000.0; else pType->rDampening = 0.9999; pType->rAveSpeed = LIGHT_SPEED * 0.1; // Load damage CString sDamage; if (pDesc->FindAttribute(DAMAGE_ATTRIB, &sDamage)) { SDesignLoadCtx Ctx; DamageDesc Damage; if (error = Damage.LoadFromXML(Ctx, sDamage)) return error; pType->pDamageDesc = new CWeaponFireDesc; pType->pDamageDesc->InitFromDamage(Damage); pType->m_fFreeDesc = true; } // Set the image pType->wColor = CG16bitImage::RGBValue(0, 255, 0); CXMLElement *pImage = pDesc->GetContentElementByTag(IMAGE_TAG); if (pImage) { pType->iPaintStyle = paintImage; pType->Image.InitFromXML(pImage); } // Miscelleanous settings pParticles->SetBounds(pType->rRadius); // Create the group SParticleArray *pGroup; int iCount = pDesc->GetAttributeInteger(COUNT_ATTRIB); pParticles->CreateGroup(pType, iCount, &pGroup); // Add to system if (error = pParticles->AddToSystem(pSystem)) { delete pParticles; return error; } // Done if (retpEffect) *retpEffect = pParticles; return NOERROR; }
void CParticleEffect::OnReadFromStream (SLoadCtx &Ctx) // OnReadFromStream // // Read object data from a stream // // CString m_sName // DWORD m_pAnchor (CSpaceObject ref) // // DWORD type: iPaintStyle (or 0xffffffff if no more groups) // Image type: Image // DWORD type: wColor // DWORD type: iRegenerationTimer // DWORD type: iLifespan // Metric type: rAveSpeed // DWORD type: iDirection // DWORD type: iDirRange // Metric type: rRadius // Metric type: rHoleRadius // Metric type: rDampening // DamageDesc type: Damage // DWORD type: flags // // DWORD array: iAlive // // DWORD particle: iDestiny // DWORD particle: iLifeLeft // Vector particle: vPos // Vector particle: vVel { #ifdef DEBUG_LOAD ::OutputDebugString("CParticleEffect::OnReadFromStream\n"); #endif m_sName.ReadFromStream(Ctx.pStream); Ctx.pSystem->ReadObjRefFromStream(Ctx, &m_pAnchor); DWORD dwLoad; DWORD dwNext; Ctx.pStream->Read((char *)&dwNext, sizeof(DWORD)); SParticleArray *pLastGroup = NULL; while (dwNext != 0xffffffff) { // Create a new group SParticleArray *pGroup = new SParticleArray; if (pLastGroup == NULL) m_pFirstGroup = pGroup; else pLastGroup->pNext = pGroup; // Create a new type pGroup->pType = new SParticleType; pGroup->pType->iPaintStyle = dwNext; pGroup->pType->Image.ReadFromStream(Ctx); Ctx.pStream->Read((char *)&dwLoad, sizeof(DWORD)); pGroup->pType->wColor = (WORD)dwLoad; Ctx.pStream->Read((char *)&pGroup->pType->iRegenerationTimer, sizeof(DWORD)); Ctx.pStream->Read((char *)&pGroup->pType->iLifespan, sizeof(DWORD)); Ctx.pStream->Read((char *)&pGroup->pType->rAveSpeed, sizeof(Metric)); Ctx.pStream->Read((char *)&pGroup->pType->iDirection, sizeof(DWORD)); Ctx.pStream->Read((char *)&pGroup->pType->iDirRange, sizeof(DWORD)); Ctx.pStream->Read((char *)&pGroup->pType->rRadius, sizeof(Metric)); Ctx.pStream->Read((char *)&pGroup->pType->rHoleRadius, sizeof(Metric)); Ctx.pStream->Read((char *)&pGroup->pType->rDampening, sizeof(Metric)); DamageDesc Damage; Damage.ReadFromStream(Ctx); // Load type flags Ctx.pStream->Read((char *)&dwLoad, sizeof(DWORD)); pGroup->pType->m_fMaxRadius = ((dwLoad & 0x00000001) ? true : false); pGroup->pType->m_fLifespan = ((dwLoad & 0x00000002) ? true : false); pGroup->pType->m_fWake = ((dwLoad & 0x00000004) ? true : false); pGroup->pType->m_fRegenerate = ((dwLoad & 0x00000008) ? true : false); pGroup->pType->m_fDrag = ((dwLoad & 0x00000010) ? true : false); bool bHasDamage = ((dwLoad & 0x00000020) ? true : false); // Init damage desc if (bHasDamage) { pGroup->pType->pDamageDesc = new CWeaponFireDesc; pGroup->pType->pDamageDesc->InitFromDamage(Damage); pGroup->pType->m_fFreeDesc = true; } // Load array of particles Ctx.pStream->Read((char *)&dwLoad, sizeof(DWORD)); pGroup->iAlive = dwLoad; pGroup->iCount = dwLoad; pGroup->pParticles = new SParticle [pGroup->iCount]; for (int i = 0; i < pGroup->iAlive; i++) { Ctx.pStream->Read((char *)&pGroup->pParticles[i].iDestiny, sizeof(DWORD)); Ctx.pStream->Read((char *)&pGroup->pParticles[i].iLifeLeft, sizeof(DWORD)); Ctx.pStream->Read((char *)&pGroup->pParticles[i].vPos, sizeof(CVector)); Ctx.pStream->Read((char *)&pGroup->pParticles[i].vVel, sizeof(CVector)); } // Next pGroup->pNext = NULL; pLastGroup = pGroup; Ctx.pStream->Read((char *)&dwNext, sizeof(DWORD)); } }
void CParticleEffect::OnWriteToStream (IWriteStream *pStream) // OnWriteToStream // // Write the object's data to stream // // CString m_sName // DWORD m_pAnchor (CSpaceObject ref) // // DWORD type: iPaintStyle (or 0xffffffff if no more groups) // Image type: Image // DWORD type: wColor // DWORD type: iRegenerationTimer // DWORD type: iLifespan // Metric type: rAveSpeed // DWORD type: iDirection // DWORD type: iDirRange // Metric type: rRadius // Metric type: rHoleRadius // Metric type: rDampening // DamageDesc type: Damage // DWORD type: flags // // DWORD array: iAlive // // DWORD particle: iDestiny // DWORD particle: iLifeLeft // Vector particle: vPos // Vector particle: vVel { DWORD dwSave; m_sName.WriteToStream(pStream); WriteObjRefToStream(m_pAnchor, pStream); // Save each group SParticleArray *pGroup = m_pFirstGroup; while (pGroup) { SParticleType *pType = pGroup->pType; // Save the type information pStream->Write((char *)&pType->iPaintStyle, sizeof(DWORD)); pType->Image.WriteToStream(pStream); dwSave = pType->wColor; pStream->Write((char *)&dwSave, sizeof(DWORD)); pStream->Write((char *)&pType->iRegenerationTimer, sizeof(DWORD)); pStream->Write((char *)&pType->iLifespan, sizeof(DWORD)); pStream->Write((char *)&pType->rAveSpeed, sizeof(Metric)); pStream->Write((char *)&pType->iDirection, sizeof(DWORD)); pStream->Write((char *)&pType->iDirRange, sizeof(DWORD)); pStream->Write((char *)&pType->rRadius, sizeof(Metric)); pStream->Write((char *)&pType->rHoleRadius, sizeof(Metric)); pStream->Write((char *)&pType->rDampening, sizeof(Metric)); if (pType->pDamageDesc) pType->pDamageDesc->m_Damage.WriteToStream(pStream); else { DamageDesc Dummy; Dummy.WriteToStream(pStream); } DWORD dwSave = 0; dwSave |= (pType->m_fMaxRadius ? 0x00000001 : 0); dwSave |= (pType->m_fLifespan ? 0x00000002 : 0); dwSave |= (pType->m_fWake ? 0x00000004 : 0); dwSave |= (pType->m_fRegenerate ? 0x00000008 : 0); dwSave |= (pType->m_fDrag ? 0x00000010 : 0); dwSave |= (pType->pDamageDesc ? 0x00000020 : 0); pStream->Write((char *)&dwSave, sizeof(DWORD)); // Save the array of particles pStream->Write((char *)&pGroup->iAlive, sizeof(DWORD)); int iCount = pGroup->iAlive; SParticle *pParticle = pGroup->pParticles; SParticle *pEnd = pParticle + pGroup->iCount; while (pParticle < pEnd) { if (pParticle->IsValid()) { pStream->Write((char *)&pParticle->iDestiny, sizeof(DWORD)); pStream->Write((char *)&pParticle->iLifeLeft, sizeof(DWORD)); pStream->Write((char *)&pParticle->vPos, sizeof(CVector)); pStream->Write((char *)&pParticle->vVel, sizeof(CVector)); iCount--; } pParticle++; } // iAlive better match the actual count ASSERT(iCount == 0); pGroup = pGroup->pNext; } // Mark the end with 0xffffffff dwSave = 0xffffffff; pStream->Write((char *)&dwSave, sizeof(DWORD)); }
void CMissile::OnUpdate (Metric rSecondsPerTick) // OnUpdate // // Update the beam { // If we're already destroyed, then just update the timer until the // vapor trail fades out if (m_fDestroyed) { // Update the painter if (m_pPainter) { m_pPainter->OnUpdate(); RECT rcRect; m_pPainter->GetRect(&rcRect); SetBounds(rcRect); } // Done? if (--m_iLifeLeft <= 0) { Destroy(removedFromSystem, NULL); return; } } // Otherwise, update else { int i; CSystem *pSystem = GetSystem(); int iTick = m_iTick + GetDestiny(); bool bDestroy = false; // Accelerate, if necessary if (m_pDesc->m_iAccelerationFactor > 0 && (iTick % 10 ) == 0) { if (m_pDesc->m_iAccelerationFactor < 100 || GetVel().Length() < m_pDesc->m_rMaxMissileSpeed) SetVel(GetVel() * (Metric)(m_pDesc->m_iAccelerationFactor / 100.0)); } // If we can choose new targets, see if we need one now if (m_pDesc->CanAutoTarget() && m_pTarget == NULL) m_pTarget = GetNearestEnemy(MAX_TARGET_RANGE, false); // If this is a tracking missile, change direction to face the target if ((m_pDesc->m_iManeuverability > 0) && m_pTarget && ((iTick % m_pDesc->m_iManeuverability) == 0)) { // Get the position and velocity of the target CVector vTarget = m_pTarget->GetPos() - GetPos(); CVector vTargetVel = m_pTarget->GetVel() - GetVel(); // Figure out which direction to move in Metric rCurrentSpeed = GetVel().Length(); Metric rTimeToIntercept = CalcInterceptTime(vTarget, vTargetVel, rCurrentSpeed); if (rTimeToIntercept > 0.0) { CVector vInterceptPoint = vTarget + vTargetVel * rTimeToIntercept; int iFireAngle = VectorToPolar(vInterceptPoint, NULL); // If we are directional, then we are constrained to specific angles if (m_pDesc->m_bDirectional) { if (!AreAnglesAligned(iFireAngle, m_iRotation, g_RotationAngle / 2)) { int iTurn = (iFireAngle + 360 - m_iRotation) % 360; if (iTurn >= 180) m_iRotation = (m_iRotation + 360 - g_RotationAngle) % 360; else m_iRotation = (m_iRotation + g_RotationAngle) % 360; } } else { if (!AreAnglesAligned(iFireAngle, m_iRotation, 1)) { int iTurn = (iFireAngle + 360 - m_iRotation) % 360; if (iTurn >= 180) { int iTurnAngle = Min((360 - iTurn), g_RotationAngle); m_iRotation = (m_iRotation + 360 - iTurnAngle) % 360; } else { int iTurnAngle = Min(iTurn, g_RotationAngle); m_iRotation = (m_iRotation + iTurnAngle) % 360; } SetVel(PolarToVector(m_iRotation, rCurrentSpeed)); } } SetVel(PolarToVector(m_iRotation, rCurrentSpeed)); } } // Update exhaust if (m_pExhaust) { if (iTick % m_pDesc->m_iExhaustRate) { if (m_pExhaust->GetCount() == m_pExhaust->GetMaxCount()) m_pExhaust->Dequeue(); SExhaustParticle &New = m_pExhaust->GetAt(m_pExhaust->Queue()); New.vPos = GetPos(); New.vVel = GetVel(); } for (int i = 0; i < m_pExhaust->GetCount(); i++) { SExhaustParticle &Particle = m_pExhaust->GetAt(i); Particle.vVel = m_pDesc->m_rExhaustDrag * Particle.vVel; Particle.vPos = Particle.vPos + Particle.vVel * g_SecondsPerUpdate; } } // Update the painter if (m_pPainter) { m_pPainter->OnUpdate(); RECT rcRect; m_pPainter->GetRect(&rcRect); SetBounds(rcRect); } // If we have a vapor trail and need to save rotation, do it if (m_pDesc->m_iVaporTrailLength && m_pDesc->m_iManeuverability) { // Compute the current rotation int iDirection; if (m_pDesc->m_bDirectional) iDirection = (AlignToRotationAngle(m_iRotation) + 180) % 360; else iDirection = (m_iRotation + 180) % 360; // Add the current rotation to the list of saved rotations if (m_pSavedRotations == NULL) { m_pSavedRotations = new int [m_pDesc->m_iVaporTrailLength]; m_iSavedRotationsCount = 0; } int iStart = Min(m_iSavedRotationsCount, m_pDesc->m_iVaporTrailLength - 1); for (i = iStart; i > 0; i--) m_pSavedRotations[i] = m_pSavedRotations[i - 1]; m_pSavedRotations[0] = iDirection; if (m_iSavedRotationsCount < m_pDesc->m_iVaporTrailLength) m_iSavedRotationsCount++; } // See if the missile hit anything if (m_fDetonate && m_pDesc->HasFragments()) { CreateFragments(GetPos()); CreateHitEffect(GetPos()); bDestroy = true; } else if (m_pHit) { // If we have fragments, then explode now if (m_iHitDir == -1 && m_pDesc->HasFragments() && m_iTick >= m_pDesc->m_iProximityFailsafe) { CreateFragments(m_vHitPos); CreateHitEffect(m_vHitPos); bDestroy = true; } // Otherwise, if this was a direct hit, then we do damage else if (m_iHitDir != -1) { DamageResults result; DamageDesc Damage = m_pDesc->m_Damage; Damage.AddBonus(m_iBonus); Damage.SetCause(m_iCause); if (IsAutomatedWeapon()) Damage.SetAutomatedWeapon(); result = m_pHit->Damage(this, m_vHitPos, (m_iHitDir + 360 + mathRandom(0, 30) - 15) % 360, Damage); // If we hit another missile (or some small object) there is a chance // that we continue if (result == damagePassthrough) { m_iHitPoints = m_iHitPoints / 2; bDestroy = (m_iHitPoints == 0); } // Set the missile to destroy itself after a hit else if (m_pDesc->m_iPassthrough == 0 || result == damageNoDamage || result == damageAbsorbedByShields || mathRandom(1, 100) > m_pDesc->m_iPassthrough) bDestroy = true; CreateHitEffect(m_vHitPos); } } // See if the missile has faded out if (bDestroy || --m_iLifeLeft <= 0) { // If this is a fragmentation weapon, then we explode at the end of life if (!bDestroy && m_pDesc->HasFragments()) { CreateFragments(GetPos()); CreateHitEffect(GetPos()); } // If we've got a vapor trail effect, then keep the missile object alive // but mark it destroyed int iFadeLife; if (m_pDesc->m_iVaporTrailLength) { m_fDestroyed = true; m_iLifeLeft = m_pDesc->m_iVaporTrailLength; } // If we've got an effect that needs time to fade out, then keep // the missile object alive else if (m_pPainter && (iFadeLife = m_pPainter->GetFadeLifetime())) { m_pPainter->OnBeginFade(); m_fDestroyed = true; m_iLifeLeft = iFadeLife; } // Otherwise, destroy the missile else { Destroy(removedFromSystem, NULL); return; } } } m_iTick++; }