void CSpaceObjectList::NotifyOnObjDocked (CSpaceObject *pDockingObj, CSpaceObject *pDockTarget) // NotifyOnObjDocked // // Notify that an object docked. { int i; if (GetCount() > 0) { TArray<CSpaceObject *> List(m_List); for (i = 0; i < List.GetCount(); i++) { CSpaceObject *pObj = List[i]; // NOTE: We do not notify the dock target because it got notified // separately. if (!pObj->IsDestroyed() && pObj != pDockTarget && pObj->HasOnObjDockedEvent()) pObj->FireOnObjDocked(pDockingObj, pDockTarget); } } }
void AddSystemData (CSystem *pSystem, bool bAll, SNodeDesc *retResult) { int i; // Loop over all objects for (i = 0; i < pSystem->GetObjectCount(); i++) { CStationType *pType; CSpaceObject *pObj = pSystem->GetObject(i); if (pObj == NULL || pObj->IsDestroyed() || (pType = pObj->GetEncounterInfo()) == NULL) continue; // Skip if we're not interested in this encounter if (!bAll && !pType->CanBeEncounteredRandomly()) continue; // Get table CCountTable *pTable = retResult->Table.SetAt(pSystem->GetType()->GetUNID()); // Add to count bool bNew; int *pCount = pTable->SetAt(pType->GetUNID(), &bNew); if (bNew) *pCount = 1; else *pCount += 1; } }
int GetValidObjCount (CSystem *pSystem) { int i; int iCount = 0; for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); if (pObj && !pObj->IsDestroyed()) iCount++; } return iCount; }
void CSpaceObjectList::NotifyOnObjEnteredGate (CSpaceObject *pGatingObj, CTopologyNode *pDestNode, const CString &sDestEntryPoint, CSpaceObject *pStargate) // NotifyOnObjEnteredGate // // Notify all objects in the list that the given object has entered a stargate. { int i; if (GetCount() > 0) { TArray<CSpaceObject *> List(m_List); for (i = 0; i < List.GetCount(); i++) { CSpaceObject *pObj = List[i]; if (!pObj->IsDestroyed()) pObj->FireOnObjEnteredGate(pGatingObj, pDestNode, sDestEntryPoint, pStargate); } } }
void CSpaceObjectList::NotifyOnPlayerBlacklisted (CSpaceObject *pBlacklistingObj) // NotifyOnPlayerBlacklisted // // Notify all objects that the player was blacklisted { int i; if (GetCount() > 0) { TArray<CSpaceObject *> List(m_List); for (i = 0; i < List.GetCount(); i++) { CSpaceObject *pObj = List[i]; if (!pObj->IsDestroyed()) pObj->FireOnObjBlacklistedPlayer(pBlacklistingObj); } } }
void CSpaceObjectList::NotifyOnObjReconned (CSpaceObject *pReconnedObj) // NotifyOnObjReconned // // Notify that an object was reconned { int i; if (GetCount() > 0) { TArray<CSpaceObject *> List(m_List); for (i = 0; i < List.GetCount(); i++) { CSpaceObject *pObj = List[i]; if (!pObj->IsDestroyed()) pObj->FireOnObjReconned(pReconnedObj); } } }
void CSpaceObjectList::NotifyOnObjDestroyed (SDestroyCtx &Ctx) // NotifyOnObjDestroyed // // Notify all objects in the list that another object was destroyed. { int i; if (GetCount() > 0) { // We make a copy of our list because an event might remove an object // from our list. TArray<CSpaceObject *> List(m_List); for (i = 0; i < List.GetCount(); i++) { CSpaceObject *pObj = List[i]; if (!pObj->IsDestroyed()) pObj->OnObjDestroyedNotify(Ctx); } } }
EResults RunEncounter (CUniverse &Universe, CSimViewer &Viewer, CStationType *pDefenderType, CShipClass *pAttackerClass, CSovereign *pAttackerSovereign) { int iTimeOut = DEFAULT_TIME_OUT; // Make sure the universe is clean CString sError; if (Universe.InitGame(0, &sError) != NOERROR) { printf("ERROR: %s", sError.GetASCIIZPointer()); return resultError; } // Create an empty system CSystem *pSystem; if (Universe.CreateEmptyStarSystem(&pSystem) != NOERROR) { printf("ERROR: Unable to create empty star system.\n"); return resultError; } // Create a station in the center of the system CSpaceObject *pStation; if (pSystem->CreateStation(pDefenderType, NULL, CVector(), &pStation) != NOERROR) { printf("ERROR: Unable to create station.\n"); return resultError; } // Set the POV Universe.SetPOV(pStation); pSystem->SetPOVLRS(pStation); // Prepare system Universe.UpdateExtended(); Universe.GarbageCollectLibraryBitmaps(); Universe.StartGame(true); // Now create an attacker some distance away CVector vPos = PolarToVector(mathRandom(0, 359), INITIAL_DISTANCE); CShip *pAttacker; if (pSystem->CreateShip(pAttackerClass->GetUNID(), NULL, NULL, pAttackerSovereign, vPos, CVector(), 0, NULL, NULL, &pAttacker) != NOERROR) { printf("ERROR: Unable to create attacking ship.\n"); return resultError; } // Set the attacker to attack the station IShipController *pController = pAttacker->GetController(); if (pController == NULL) { printf("ERROR: No controller for ship.\n"); return resultError; } pController->AddOrder(IShipController::orderAttackStation, pStation, IShipController::SData()); // Watch the attacker Universe.SetPOV(pAttacker); pSystem->SetPOVLRS(pAttacker); // Update context SSystemUpdateCtx Ctx; Ctx.bForceEventFiring = true; Ctx.bForcePainted = true; // Now keep updating until either the station is destroyed, the ship is destroyed, or we time-out int iTime = 0; int iDestroyedTime = (Viewer.IsEmpty() ? 0 : DESTROY_TIME); bool bDestroyed = false; EResults iResult = resultTimeout; while (iTime < iTimeOut && (!bDestroyed || iDestroyedTime > 0)) { iTime++; Universe.Update(Ctx); if (!Viewer.IsEmpty()) Viewer.PaintViewport(Universe); if (bDestroyed) iDestroyedTime--; else if (pStation->IsDestroyed() || pStation->IsAbandoned()) { bDestroyed = true; iResult = resultDefenderDestroyed; } else if (pAttacker->IsDestroyed()) { bDestroyed = true; iResult = resultAttackerDestroyed; } } // Done return iResult; }
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; }
void CDockingPorts::UpdateAll (SUpdateCtx &Ctx, CSpaceObject *pOwner) // UpdateAll // // UpdateAll { DEBUG_TRY int i; CSpaceObject *pPlayer = Ctx.pSystem->GetPlayer(); Metric rDist2 = (pPlayer ? pPlayer->GetDistance2(pOwner) : 0.0); Metric rMaxDist = m_iMaxDist * LIGHT_SECOND; Metric rMaxDist2 = rMaxDist * rMaxDist; // If owner is destroyed then don't bother checking for nearest player // port. if (pPlayer && (pOwner->IsDestroyed() || pOwner->IsInactive() || pOwner == pPlayer || pPlayer->IsDestroyed())) pPlayer = NULL; // Also, don't bother checking if the owner is an enemy of the player. if (pPlayer && pPlayer->IsEnemy(pOwner) && !pOwner->IsAbandoned()) pPlayer = NULL; // Don't bother checking if the station is too far if (pPlayer && rDist2 > rMaxDist2) pPlayer = NULL; // If this is a stargate and we are at the center (just came through) // then don't bother showing docking ports. if (pPlayer && pOwner->IsStargate() && rDist2 < GATE_DIST2) pPlayer = NULL; // Loop over all ports for (i = 0; i < m_iPortCount; i++) { // If a ship is docking with this port, then maneuver the ship towards // the docking port. if (m_pPort[i].iStatus == psDocking) UpdateDockingManeuvers(pOwner, m_pPort[i]); // Otherwise, if the port is open, see if this is the nearest port to // the current player position. else if (m_pPort[i].iStatus == psEmpty) { if (pPlayer) { // Compute the distance from the player to the port CVector vPortPos = pOwner->GetPos() + m_pPort[i].vPos; Metric rDist2 = (vPortPos - pPlayer->GetPos()).Length2(); // If this is a better port, then replace the existing // solution. if (Ctx.pDockingObj == NULL || rDist2 < Ctx.rDockingPortDist2) { Ctx.pDockingObj = pOwner; Ctx.iDockingPort = i; Ctx.rDockingPortDist2 = rDist2; Ctx.vDockingPort = vPortPos; } } } } DEBUG_CATCH }
void CParticleEffect::OnUpdate (SUpdateCtx &Ctx, Metric rSecondsPerTick) // OnUpdate // // Update the effect { int iTick = GetSystem()->GetTick() + GetDestiny(); // Do not bother updating everything if we are far from the POV bool bFarAway = false; if (g_pUniverse->GetPOV() && g_pUniverse->GetCurrentSystem() == GetSystem()) { Metric rPOVDist2 = (GetPos() - g_pUniverse->GetPOV()->GetPos()).Length2(); Metric rMaxUpdateDist2 = LIGHT_SECOND * LIGHT_SECOND * 3600; bFarAway = (rPOVDist2 > rMaxUpdateDist2); } // Update the particles SParticleArray *pGroup = m_pFirstGroup; while (pGroup) { SParticleType *pType = pGroup->pType; // Max distance for a particle in this group Metric rMaxDist2 = pType->rRadius * pType->rRadius; Metric rMinDist2 = pType->rHoleRadius * pType->rHoleRadius; // If the particle field causes damage then we need to // compute its average density int iDensity = 0; if (pType->pDamageDesc) { Metric rRadius2 = pType->rRadius * pType->rRadius; Metric rArea = rRadius2 / (LIGHT_SECOND * LIGHT_SECOND); iDensity = (int)(4 * pGroup->iCount / rArea); } // Get an array of objects in the particle field that // may influence the particles CSpaceObject *Objects[ctMaxObjsInField]; int iObjCount = 0; if (!bFarAway && (pType->m_fWake || pType->pDamageDesc)) { Metric rMaxInfluenceDist2 = rMaxDist2; for (int i = 0; i < GetSystem()->GetObjectCount(); i++) { CSpaceObject *pObj = GetSystem()->GetObject(i); if (pObj && pObj->GetCategory() == catShip && !pObj->IsInactive() && pObj->CanBeHit() && !pObj->IsDestroyed() && pObj != this) { CVector vDist = GetPos() - pObj->GetPos(); Metric rDist2 = vDist.Length2(); if (rDist2 < rMaxInfluenceDist2 && (pObj->GetVel().Length2() > g_KlicksPerPixel) && iObjCount < ctMaxObjsInField) { Objects[iObjCount++] = pObj; // See if the object should take damage if (pType->pDamageDesc) { CVector vDeltaV = pObj->GetVel() - GetVel(); int iSpeed = (int)(vDeltaV.Length() / g_KlicksPerPixel); if (iSpeed == 0) iSpeed = 1; if (mathRandom(1, 1000) < (iDensity * iSpeed)) { SDamageCtx Ctx; Ctx.pObj = pObj; Ctx.pDesc = pType->pDamageDesc; Ctx.Damage = pType->pDamageDesc->m_Damage; if (IsAutomatedWeapon()) Ctx.Damage.SetAutomatedWeapon(); Ctx.iDirection = VectorToPolar(vDeltaV); Ctx.vHitPos = pObj->GetPos(); Ctx.pCause = this; Ctx.Attacker = CDamageSource(this, killedByDamage); pObj->Damage(Ctx); } } } } } } // If we're computing drag then we need to compute the new velocity // of the whole particle system CVector vNewVel; if (pType->m_fDrag) vNewVel = GetVel() * g_SpaceDragFactor; // Iterate over all particles SParticle *pParticle = pGroup->pParticles; SParticle *pEnd = pParticle + pGroup->iCount; while (pParticle < pEnd) { if (pParticle->IsValid()) { // Lifespan. If we're far away and we're regenerating, // then don't bother to compute lifespan. if (pType->m_fLifespan && !(bFarAway && (pType->m_fRegenerate && pType->iRegenerationTimer))) { if (--pParticle->iLifeLeft == 0) { // Do we regenerate? if (pType->m_fRegenerate && pType->iRegenerationTimer) { pParticle->iLifeLeft = pType->iLifespan; pParticle->vPos = NullVector; // Speed Metric rSpeed = mathRandom(1, 100) * (pType->rAveSpeed / 100.0); if (pType->iDirection == -1) pParticle->vVel = PolarToVector(mathRandom(0, 359), rSpeed); else { int iAngle = (pType->iDirection + 360 + mathRandom(0, 2 * pType->iDirRange) - pType->iDirRange) % 360; pParticle->vVel = PolarToVector(iAngle, rSpeed); } } // Otherwise we die else { pParticle->iLifeLeft = -1; pGroup->iAlive--; pParticle++; continue; } } } // Update the position if (!bFarAway) { pParticle->vPos = pParticle->vPos + pParticle->vVel; // Change the velocity to keep the particles within // the radius if (pType->m_fMaxRadius) { Metric rDist2 = pParticle->vPos.Length2(); if (pType->m_fMaxRadius && rDist2 > rMaxDist2) { CVector vChange = pParticle->vPos + g_KlicksPerPixel * pParticle->vPos.Perpendicular().Normal(); pParticle->vVel = pParticle->vVel - (0.00005 * vChange); } else if (rDist2 < rMinDist2) { CVector vNormal = pParticle->vPos.Normal(); CVector vChange = g_KlicksPerPixel * (400 * vNormal - 50 * vNormal.Perpendicular()); pParticle->vVel = pParticle->vVel + (0.00005 * vChange); } else pParticle->vVel = pParticle->vVel * pType->rDampening; } if (pType->m_fDrag) { // Compute the new absolute velocity (after drag) CVector vAbsolute = pType->rDampening * (pParticle->vVel + GetVel()); // The particle velocity is the absolute vel minus the // system velocity. pParticle->vVel = vAbsolute - vNewVel; } // Change the velocity based on influences from other objects if (pType->m_fWake && (iTick % 4) == 0) { for (int i = 0; i < iObjCount; i++) { Metric rDist2 = (Objects[i]->GetPos() - (pParticle->vPos + GetPos())).Length2(); if (rDist2 < g_KlicksPerPixel * g_KlicksPerPixel * 1000) { if (Objects[i]->GetVel().Dot(pParticle->vVel) < Objects[i]->GetVel().Length2()) pParticle->vVel = pParticle->vVel + 0.2 * Objects[i]->GetVel(); } } } } } pParticle++; } // Regeneration timer if (pType->m_fRegenerate && pType->iRegenerationTimer) pType->iRegenerationTimer--; // If there are no more particles left alive in this group then kill // the group if (pGroup->iAlive == 0) { SParticleArray *pNext = pGroup->pNext; SParticleArray *pPrev = NULL; // Find the previous group SParticleArray *pFind = m_pFirstGroup; while (pFind != pGroup) { if (pPrev) pPrev = pPrev->pNext; else pPrev = m_pFirstGroup; pFind = pFind->pNext; } // Fix up the linked list if (pPrev) pPrev->pNext = pNext; else m_pFirstGroup = pNext; // Delete the group delete pGroup; pGroup = pNext; } // Otherwise, next group else pGroup = pGroup->pNext; } // If we have no more groups then we destroy ourselves if (m_pFirstGroup == NULL) { Destroy(removedFromSystem, CDamageSource()); return; } // If we're moving, slow down SetVel(CVector(GetVel().GetX() * g_SpaceDragFactor, GetVel().GetY() * g_SpaceDragFactor)); }