bool CNavigationPath::PathIsClear (CSystem *pSystem, CSovereign *pSovereign, const CVector &vFrom, const CVector &vTo, CSpaceObject **retpEnemy, CVector *retvAway) // PathIsClear // // Returns TRUE if the path from vFrom to vTo is free from enemy stations. // If FALSE, retpEnemy is initialized with the enemy that is blocking the // path and retvAway is a unit vector away from the enemy that avoids it { int i; // Loop over all objects in the system CSpaceObject *pNearestEnemy = NULL; Metric rNearestDist = MAX_SAFE_DIST; CVector vNearestAway; for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); CSovereign *pObjSovereign; if (pObj && (pObj->GetScale() == scaleStructure || ((pObj->GetScale() == scaleShip) && (pObj->GetVel().Length2() < MIN_SPEED2))) && (pObjSovereign = pObj->GetSovereign()) && (pObjSovereign->IsEnemy(pSovereign)) && pObj->CanAttack()) { CVector vAway; Metric rDist = CalcDistanceToPath(pObj->GetPos(), vFrom, vTo, NULL, &vAway); if (rDist < rNearestDist) { rNearestDist = rDist; pNearestEnemy = pObj; vNearestAway = vAway; } } } // If we found a threatening object, return it if (pNearestEnemy) { if (retpEnemy) *retpEnemy = pNearestEnemy; if (retvAway) *retvAway = vNearestAway; return false; } // Otherwise, the path is OK return true; }
bool CDockingPorts::ShipsNearPort (CSpaceObject *pOwner, CSpaceObject *pRequestingObj, const CVector &vPortPos) // ShipsNearPort // // Returns TRUE if there are ships near the given port { int i; CSystem *pSystem = pOwner->GetSystem(); for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); if (pObj && pObj->GetCategory() == CSpaceObject::catShip && !pObj->IsInactive() && pObj != pRequestingObj) { Metric rDist2 = (pObj->GetPos() - vPortPos).Length2(); if (rDist2 < MIN_PORT_DISTANCE2 && !IsDockedOrDocking(pObj)) return true; } } return false; }
void CAutoDefenseClass::Update (CInstalledDevice *pDevice, CSpaceObject *pSource, int iTick, bool *retbSourceDestroyed, bool *retbConsumedItems) // Update // // Update device { if (pDevice->IsReady() && pDevice->IsEnabled()) { int i; // Look for a target CSpaceObject *pBestTarget = NULL; Metric rBestDist2 = MAX_INTERCEPT_DISTANCE * MAX_INTERCEPT_DISTANCE; for (i = 0; i < pSource->GetSystem()->GetObjectCount(); i++) { CSpaceObject *pObj = pSource->GetSystem()->GetObject(i); if (pObj && pObj->GetCategory() == CSpaceObject::catMissile && pObj->GetSource() != pSource && (pObj->GetSource() == NULL || pSource->IsEnemy(pObj->GetSource()))) { CVector vRange = pObj->GetPos() - pSource->GetPos(); Metric rDistance2 = vRange.Dot(vRange); if (rDistance2 < rBestDist2) { pBestTarget = pObj; rBestDist2 = rDistance2; } } } // If we found a target, try to shoot at it if (pBestTarget) { CDeviceClass *pWeapon = GetWeapon(); if (pWeapon) { int iFireAngle; if (pWeapon->IsWeaponAligned(pSource, pDevice, pBestTarget, &iFireAngle)) { pDevice->SetFireAngle(iFireAngle); pWeapon->Activate(pDevice, pSource, pBestTarget, iFireAngle, retbSourceDestroyed, retbConsumedItems); pDevice->SetActivationDelay(m_iRechargeTicks); } } } } }
CSpaceObject *CFerianShipAI::FindRandomAsteroid (void) // FindRandomAsteroid // // Returns a random asteroid within 60 light-seconds { int i; Metric rMaxDist2 = MAX_MINING_RANGE2; Metric rCloseDist2 = CLOSE_MINING_RANGE2; CSpaceObject *Table[MAX_RANDOM_COUNT]; int iCount = 0; for (i = 0; (i < m_pShip->GetSystem()->GetObjectCount() && iCount < MAX_RANDOM_COUNT); i++) { CSpaceObject *pObj = m_pShip->GetSystem()->GetObject(i); if (pObj && pObj->HasAttribute(ATTRIBUTE_ASTEROID)) { CVector vRange = pObj->GetPos() - m_pShip->GetPos(); Metric rDistance2 = vRange.Dot(vRange); // If we're within the max dist, add it to the list if (rDistance2 < rMaxDist2) Table[iCount++] = pObj; // If we're within 10 light-seconds, add it twice more (to increase // the probability) if (rDistance2 < rCloseDist2) { Table[iCount++] = pObj; Table[iCount++] = pObj; } } } // Pick a random entry from the list if (iCount == 0) return NULL; else return Table[mathRandom(0, iCount-1)]; }
CVector CreateVectorFromList (CCodeChain &CC, ICCItem *pList) // CreateVectorFromList // // Creates a vector from a code chain list { CVector vVec; if (pList->IsList()) CreateBinaryFromList(CC, pList, &vVec); else if (pList->IsInteger()) { CSpaceObject *pObj = CreateObjFromItem(CC, pList); if (pObj) vVec = pObj->GetPos(); } return vVec; }
void GenerateSnapshot (CUniverse &Universe, CXMLElement *pCmdLine) { ALERROR error; int i; // Get some parameters int iInitialUpdateTime = pCmdLine->GetAttributeIntegerBounded(CONSTLIT("initialUpdate"), 0, -1, 10); int iUpdateTime = pCmdLine->GetAttributeInteger(CONSTLIT("wait")); bool bObjOnly = pCmdLine->GetAttributeBool(CONSTLIT("objOnly")); // Criteria CString sNode = pCmdLine->GetAttribute(CONSTLIT("node")); CString sCriteria = pCmdLine->GetAttribute(CONSTLIT("criteria")); // Number of snapshots int iTotalCount = pCmdLine->GetAttributeIntegerBounded(CONSTLIT("count"), 1, -1, 1); // Output int cxWidth; int cyHeight; if (pCmdLine->FindAttributeInteger(CONSTLIT("size"), &cxWidth)) { cyHeight = cxWidth; } else { cxWidth = 1024; cyHeight = 1024; } // Paint flags DWORD dwPaintFlags = 0; if (pCmdLine->GetAttributeBool(CONSTLIT("noStars"))) dwPaintFlags |= CSystem::VWP_NO_STAR_FIELD; // Output file CString sFilespec = pCmdLine->GetAttribute(CONSTLIT("output")); if (!sFilespec.IsBlank()) sFilespec = pathStripExtension(sFilespec); // Output image CG32bitImage Output; Output.Create(cxWidth, cyHeight); // Update context SSystemUpdateCtx Ctx; Ctx.bForceEventFiring = true; Ctx.bForcePainted = true; RECT rcViewport; rcViewport.left = 0; rcViewport.top = 0; rcViewport.right = cxWidth; rcViewport.bottom = cyHeight; // Loop over all systems until we find what we're looking for int iLoops = 20; int iNodeIndex = 0; int iSnapshotIndex = 0; CTopologyNode *pNode = Universe.GetTopologyNode(iNodeIndex); while (true) { // Create the system CSystem *pSystem; if (error = Universe.CreateStarSystem(pNode, &pSystem)) { printf("ERROR: Unable to create star system.\n"); return; } // If this is the node we want, then search CSpaceObject *pTarget; if (sNode.IsBlank() || strEquals(sNode, pNode->GetID())) { printf("Searching %s...\n", pNode->GetSystemName().GetASCIIZPointer()); // Set the POV CSpaceObject *pPOV = pSystem->GetObject(0); Universe.SetPOV(pPOV); pSystem->SetPOVLRS(pPOV); // Prepare system Universe.UpdateExtended(); Universe.GarbageCollectLibraryBitmaps(); // Update for a while for (i = 0; i < iInitialUpdateTime; i++) { Universe.Update(Ctx); Universe.PaintPOV(Output, rcViewport, 0); } // Compose the criteria CSpaceObject::Criteria Criteria; CSpaceObject::ParseCriteria(pPOV, sCriteria, &Criteria); // Get the list of all objects in the system that match the criteria CSpaceObject::SCriteriaMatchCtx Ctx(Criteria); TArray<CSpaceObject *> Results; for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); if (pObj && pObj->MatchesCriteria(Ctx, Criteria)) Results.Insert(pObj); } // Pick the appropriate object from the list if (Results.GetCount() == 0) pTarget = NULL; else if (Criteria.bNearestOnly || Criteria.bFarthestOnly) pTarget = Ctx.pBestObj; else pTarget = Results[mathRandom(0, Results.GetCount() - 1)]; } else pTarget = NULL; // If we found the target, then output if (pTarget) { Universe.SetPOV(pTarget); // Wait a bit // // NOTE: After we update, pTarget could be invalid (i.e., destroyed) // so we can't use it again. CString sTargetName = pTarget->GetNounPhrase(0); for (i = 0; i < iUpdateTime; i++) { if ((i % 100) == 99) printf("."); Universe.Update(Ctx); Universe.PaintPOV(Output, rcViewport, 0); } if (iUpdateTime >= 99) printf("\n"); // Paint if (bObjOnly) { SViewportPaintCtx Ctx; Ctx.pObj = Universe.GetPOV(); Ctx.XForm = ViewportTransform(Universe.GetPOV()->GetPos(), g_KlicksPerPixel, cxWidth / 2, cyHeight / 2); Ctx.XFormRel = Ctx.XForm; Ctx.fNoRecon = true; Ctx.fNoDockedShips = true; Ctx.fNoSelection = true; Ctx.fNoStarfield = true; CSpaceObject *pPOV = pSystem->GetObject(0); CSpaceObject::Criteria Criteria; CSpaceObject::ParseCriteria(pPOV, sCriteria, &Criteria); // Paint all objects that match the criteria CSpaceObject::SCriteriaMatchCtx CriteriaCtx(Criteria); for (i = 0; i < pSystem->GetObjectCount(); i++) { CSpaceObject *pObj = pSystem->GetObject(i); if (pObj && pObj->MatchesCriteria(CriteriaCtx, Criteria)) { Ctx.pObj = pObj; int xObj; int yObj; Ctx.XForm.Transform(pObj->GetPos(), &xObj, &yObj); pObj->Paint(Output, xObj, yObj, Ctx); } } } else { Universe.PaintPOV(Output, rcViewport, 0); } // Write to file if (!sFilespec.IsBlank()) { CString sBmpFilespec; if (iTotalCount > 100) sBmpFilespec = pathAddExtensionIfNecessary(strPatternSubst(CONSTLIT("%s%03d"), sFilespec, iSnapshotIndex + 1), CONSTLIT(".bmp")); else if (iTotalCount > 1) sBmpFilespec = pathAddExtensionIfNecessary(strPatternSubst(CONSTLIT("%s%02d"), sFilespec, iSnapshotIndex + 1), CONSTLIT(".bmp")); else sBmpFilespec = pathAddExtensionIfNecessary(sFilespec, CONSTLIT(".bmp")); CFileWriteStream OutputFile(sBmpFilespec); if (OutputFile.Create() != NOERROR) { printf("ERROR: Unable to create '%s'\n", sBmpFilespec.GetASCIIZPointer()); return; } Output.WriteToWindowsBMP(&OutputFile); OutputFile.Close(); printf("Found %s: Saved to %s\n", sTargetName.GetASCIIZPointer(), sBmpFilespec.GetASCIIZPointer()); } // Otherwise, clipboard else { if (error = Output.CopyToClipboard()) { printf("ERROR: Unable to copy image to clipboard.\n"); return; } printf("Found %s: Copied to clipboard.\n", sTargetName.GetASCIIZPointer()); } // Reset maximum loops iLoops = 20; // Done iSnapshotIndex++; if (iSnapshotIndex >= iTotalCount) break; } // Done with old system Universe.DestroySystem(pSystem); // Loop to the next node do { iNodeIndex = ((iNodeIndex + 1) % Universe.GetTopologyNodeCount()); pNode = Universe.GetTopologyNode(iNodeIndex); } while (pNode == NULL || pNode->IsEndGame()); // If we're back to the first node again, restart if (iNodeIndex == 0) { if (--iLoops > 0) { // Reinitialize Universe.Reinit(); CString sError; if (Universe.InitGame(0, &sError) != NOERROR) { printf("ERROR: %s\n", sError.GetASCIIZPointer()); return; } iNodeIndex = 0; pNode = Universe.GetTopologyNode(iNodeIndex); } else { printf("ERROR: Specified target could not be found.\n"); return; } } } }
int CNavigationPath::ComputePath (CSystem *pSystem, CSovereign *pSovereign, const CVector &vFrom, const CVector &vTo, int iDepth, CVector **retpPoints) // ComputePath // // This is a recursive function that returns a set of points that define a safe path // between the two given points. // // We return an allocated array of CVectors in retpPoints { int i; // If we're very close to our destination, then the best option is a direct path // If the path is clear, then a direct path is also the best option. CSpaceObject *pEnemy; CVector vAway; if (iDepth >= MAX_PATH_RECURSION || ((vTo - vFrom).Length2() <= MAX_SAFE_DIST2) || PathIsClear(pSystem, pSovereign, vFrom, vTo, &pEnemy, &vAway)) { (*retpPoints) = new CVector; (*retpPoints)[0] = vTo; return 1; } // Otherwise, we deflect the path at the enemy base and recurse for the // two path segments. else { // Compute the mid-point CVector vMidPoint = pEnemy->GetPos() + (AVOID_DIST * vAway); // Recurse CVector *pLeftPoints; int iLeftCount = ComputePath(pSystem, pSovereign, vFrom, vMidPoint, iDepth+1, &pLeftPoints); CVector *pRightPoints; int iRightCount = ComputePath(pSystem, pSovereign, vMidPoint, vTo, iDepth+1, &pRightPoints); // Compose the two paths together int iCount = iLeftCount + iRightCount; ASSERT(iCount > 0); (*retpPoints) = new CVector [iCount]; int iPos = 0; for (i = 0; i < iLeftCount; i++) (*retpPoints)[iPos++] = pLeftPoints[i]; for (i = 0; i < iRightCount; i++) (*retpPoints)[iPos++] = pRightPoints[i]; delete [] pLeftPoints; delete [] pRightPoints; return iCount; } }
void CMissile::OnMove (const CVector &vOldPos, Metric rSeconds) // OnMove // // Move our points { // Update the painter motion // Note that we do this even if we're destroyed because we might have // some fading particles. if (m_pPainter) { bool bBoundsChanged; m_pPainter->OnMove(&bBoundsChanged); // Set bounds, if they've changed if (bBoundsChanged) SetBounds(m_pPainter); } // If we're destroyed, then no further processing is needed if (m_fDestroyed) { // Clear m_pHit so that we can tell the difference between the single // frame in which we hit and any subsequent frames (when we're painting // the exhaust only). m_pHit = NULL; return; } // Compute threshold Metric rThreshold; if (m_pDesc->ProximityBlast() && m_iTick >= m_pDesc->GetProximityFailsafe()) rThreshold = 128 * g_KlicksPerPixel; else rThreshold = 0.0; // See if the beam hit anything after the move if (m_iTick > 1 || (!m_pDesc->m_bFragment && !m_fReflection)) m_pHit = HitTest(vOldPos, rThreshold, m_pDesc->m_Damage, &m_vHitPos, &m_iHitDir); // Make sure we are not too close to the source when we trigger // a proximity blast. CSpaceObject *pSource; if (m_pHit && m_iHitDir == -1 && (pSource = m_Source.GetObj())) { CVector vDist = m_vHitPos - pSource->GetPos(); Metric rDist2 = vDist.Length2(); if (rDist2 < (rThreshold * rThreshold) / 4.0) m_pHit = NULL; } // See if we pass through if (m_pHit && m_iHitDir != -1 && m_pDesc->GetPassthrough() > 0 && mathRandom(1, 100) <= m_pDesc->GetPassthrough() && m_pHit->GetPassthroughDefault() != damageNoDamageNoPassthrough) m_fPassthrough = true; else m_fPassthrough = false; }
void CFleetShipAI::Behavior (void) // Behavior { // Reset ResetBehavior(); // Use basic items UseItemsBehavior(); // Behave according to our state switch (m_State) { case stateNone: BehaviorStart(); break; case stateAttackAtWill: { // If we don't have a target, find one if (m_pTarget == NULL && m_pShip->IsDestinyTime(13)) m_pTarget = m_pShip->GetNearestEnemy(ATTACK_AT_WILL_RANGE, true); // If we have a target, attack if (m_pTarget) { ImplementAttackTarget(m_pTarget); ImplementFireOnTargetsOfOpportunity(m_pTarget); } // Otherwise, stay in formation else { ImplementKeepFormation(); ImplementFireOnTargetsOfOpportunity(); ImplementFireOnNearestTarget(); } break; } case stateAttackInFormation: { bool bInFormation; ImplementKeepFormation(&bInFormation); // Fire the primary weapon (most of the time) if (bInFormation && mathRandom(1, 3) > 1) ImplementFireWeapon(); // Decrement counter if (m_iCounter > 0) { if (--m_iCounter == 0) { SetState(stateKeepFormation); m_iCounter = 0; } } break; } case stateAttackOnPatrol: ASSERT(m_pTarget); ImplementAttackTarget(m_pTarget); ImplementFireOnTargetsOfOpportunity(m_pTarget); // Check to see if we've wandered outside our patrol zone if (m_pShip->IsDestinyTime(20)) { CSpaceObject *pCenter = GetCurrentOrderTarget(); Metric rMaxRange2 = PATROL_ORBIT_DIST + PATROL_SENSOR_RANGE; rMaxRange2 = rMaxRange2 * rMaxRange2; Metric rMinRange2 = std::max(0.0, PATROL_ORBIT_DIST - PATROL_SENSOR_RANGE); rMinRange2 = rMinRange2 * rMinRange2; int iTick = m_pShip->GetSystem()->GetTick(); CVector vRange = pCenter->GetPos() - m_pShip->GetPos(); Metric rDistance2 = vRange.Dot(vRange); // If we're outside of our patrol range then stop the attack if (rDistance2 > rMaxRange2 || rDistance2 < rMinRange2) SetState(stateNone); } break; case stateAttackTarget: { ASSERT(m_pTarget); if (m_pTarget->CanMove()) ImplementAttackTarget(m_pTarget); else { // Attack the target as best we can CVector vTarget = m_pTarget->GetPos() - m_pShip->GetPos(); Metric rTargetDist2 = vTarget.Dot(vTarget); ImplementFireWeaponOnTarget(-1, -1, m_pTarget, vTarget, rTargetDist2); // Maneuver to a proper position near the target Metric rRange; int iCounterAdj; if (m_pAISettings->iCombatStyle == aicombatStandOff) { rRange = m_rBestWeaponRange / 2.0; iCounterAdj = 2; } else { rRange = m_rBestWeaponRange / 6.0; iCounterAdj = 1; } if (m_iCounter == 0) { m_vVector = ComputeAttackPos(m_pTarget, rRange, &m_iAngle); m_iCounter = mathRandom(130, 160) * iCounterAdj; } else m_iCounter--; ImplementFormationManeuver(m_vVector, NullVector, m_iAngle); } ImplementFireOnTargetsOfOpportunity(m_pTarget); break; } case stateKeepFormation: ImplementKeepFormation(); ImplementFireOnTargetsOfOpportunity(); ImplementFireOnNearestTarget(); break; case stateOnCourseForDocking: ASSERT(m_pDest); ImplementDocking(m_pDest); ImplementFireOnTargetsOfOpportunity(); ImplementFireOnNearestTarget(); break; case stateOnCourseForStargate: ASSERT(m_pDest); ImplementGating(m_pDest); ImplementFireOnTargetsOfOpportunity(); ImplementFireOnNearestTarget(); break; case stateOnPatrolOrbit: ASSERT(m_pDest); ImplementOrbit(m_pDest, PATROL_ORBIT_DIST); ImplementAttackNearestTarget(m_rMaxWeaponRange, &m_pTarget, m_pDest); ImplementFireOnTargetsOfOpportunity(NULL, m_pDest); // Check to see if any enemy ships appear if (m_pShip->IsDestinyTime(30)) { CSpaceObject *pPrincipal = GetCurrentOrderTarget(); CSpaceObject *pTarget = CalcEnemyShipInRange(pPrincipal, PATROL_SENSOR_RANGE, m_pDest); if (pTarget) { SetState(stateAttackOnPatrol); m_pTarget = pTarget; ASSERT(m_pTarget->DebugIsValid() && m_pTarget->NotifyOthersWhenDestroyed()); } } break; } }
void CParticleEffect::OnUpdate (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->m_fDamage) { 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->m_fDamage)) { Metric rMaxInfluenceDist2 = rMaxDist2; for (int i = 0; i < GetSystem()->GetObjectCount(); i++) { CSpaceObject *pObj = GetSystem()->GetObject(i); if (pObj && pObj->GetCategory() == catShip && 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->m_fDamage) { CVector vDeltaV = pObj->GetVel() - GetVel(); int iSpeed = (int)(vDeltaV.Length() / g_KlicksPerPixel); if (iSpeed == 0) iSpeed = 1; if (mathRandom(1, 1000) < (iDensity * iSpeed)) { pObj->Damage(this, pObj->GetPos(), VectorToPolar(vDeltaV), pType->Damage); } } } } } } // 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, NULL); return; } // If we're moving, slow down SetVel(CVector(GetVel().GetX() * g_SpaceDragFactor, GetVel().GetY() * g_SpaceDragFactor)); }
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 }
bool GetPosOrObject (CEvalContext *pEvalCtx, ICCItem *pArg, CVector *retvPos, CSpaceObject **retpObj, int *retiLocID) // GetPosOrObject // // pArg is either a position or an object. We return a position and/or the object. { CCodeChain &CC(*pEvalCtx->pCC); CVector vPos; CSpaceObject *pObj = NULL; int iLocID = -1; if (pArg->IsNil()) NULL; else if (pArg->IsList()) { // Is this a location criteria? CString sTag = pArg->GetElement(0)->GetStringValue(); if (strEquals(sTag, CONSTLIT("location"))) { CSystem *pSystem = g_pUniverse->GetCurrentSystem(); if (pSystem == NULL) return false; // Get the criteria and parse it CString sCriteria = (pArg->GetCount() > 1 ? pArg->GetElement(1)->GetStringValue() : NULL_STR); if (sCriteria.IsBlank()) return false; CAttributeCriteria Criteria; if (Criteria.Parse(sCriteria) != NOERROR) return false; // Get a random location if (!pSystem->FindRandomLocation(Criteria, 0, NULL, &iLocID)) return false; // Return the position CLocationDef *pLoc = pSystem->GetLocation(iLocID); vPos = pLoc->GetOrbit().GetObjectPos(); } // Otherwise, we assume a vector else vPos = CreateVectorFromList(CC, pArg); } else { pObj = CreateObjFromItem(CC, pArg); if (pObj) vPos = pObj->GetPos(); } // Done if (retvPos) *retvPos = vPos; if (retpObj) *retpObj = pObj; if (retiLocID) *retiLocID = iLocID; return true; }