void InitTrig (void) // InitTrig // // Initializes sine and cosine tables { if (!g_bTrigInit) { for (int i = 0; i < 360; i++) { Metric rRadian = 2 * g_Pi * i / 360; g_Sine[i] = sin(rRadian); g_Cosine[i] = cos(rRadian); } g_bTrigInit = true; } #if 0 for (int i = 0; i < 360; i++) { CVector vTest = PolarToVector(i, 100); kernelDebugLogMessage("Angle %d = (%d,%d) = %d", i, (int)vTest.GetX(), (int)vTest.GetY(), VectorToPolar(vTest, NULL)); } #endif }
bool CCyberDeckClass::IsWeaponAligned (CSpaceObject *pShip, CInstalledDevice *pDevice, CSpaceObject *pTarget, int *retiAimAngle, int *retiFireAngle) // IsWeaponAligned // // Return TRUE if weapon is aligned on target. // // Note: If the weapon is invalid, we return an aim angle of -1 { // We must return an aim angle, if we are asked for it. int iAim = VectorToPolar(pTarget->GetPos() - pDevice->GetPos(pShip)); if (retiAimAngle) *retiAimAngle = iAim; if (retiFireAngle) *retiFireAngle = iAim; // CyberDecks are always aligned return true; }
void CDockingPorts::ReadFromStream (CSpaceObject *pOwner, SLoadCtx &Ctx) // ReadFromStream // // ReadFromStream { Ctx.pStream->Read((char *)&m_iPortCount, sizeof(DWORD)); if (m_iPortCount > 0) { m_pPort = new DockingPort[m_iPortCount]; for (int i = 0; i < m_iPortCount; i++) { Ctx.pStream->Read((char *)&m_pPort[i].iStatus, sizeof(DWORD)); Ctx.pSystem->ReadObjRefFromStream(Ctx, &m_pPort[i].pObj); Ctx.pStream->Read((char *)&m_pPort[i].vPos, sizeof(CVector)); if (Ctx.dwVersion >= 24) Ctx.pStream->Read((char *)&m_pPort[i].iRotation, sizeof(DWORD)); // In previous versions we did not set rotation, so set it now if (m_pPort[i].iRotation == -1) m_pPort[i].iRotation = (VectorToPolar(m_pPort[i].vPos) + 180) % 360; } } if (Ctx.dwVersion >= 81) Ctx.pStream->Read((char *)&m_iMaxDist, sizeof(DWORD)); else m_iMaxDist = DEFAULT_DOCK_DISTANCE_LS; }
void CDockingPorts::InitPorts (CSpaceObject *pOwner, int iCount, CVector *pPos) // InitPorts // // Initialize from count and array { ASSERT(m_pPort == NULL); if (iCount > 0) { m_iPortCount = iCount; m_pPort = new DockingPort[iCount]; for (int i = 0; i < iCount; i++) { m_pPort[i].iStatus = psEmpty; m_pPort[i].pObj = NULL; m_pPort[i].vPos = pPos[i]; m_pPort[i].iRotation = (VectorToPolar(pPos[i]) + 180) % 360; } } m_iMaxDist = DEFAULT_DOCK_DISTANCE_LS; }
void CParticleDamage::InitParticles (int iCount, const CVector &vSource, const CVector &vInitVel, int iDirection) // InitParticles // // Initialize particles { int i; // Generate the number of particles if (iCount > 0) { // Calculate a few temporaries Metric rRadius = (6.0 * m_pDesc->GetRatedSpeed()); int iSpreadAngle = m_pDesc->GetParticleSpreadAngle(); if (iSpreadAngle > 0) iSpreadAngle = (iSpreadAngle / 2) + 1; bool bSpreadAngle = (iSpreadAngle > 0); CVector vTemp = PolarToVector(iSpreadAngle, m_pDesc->GetRatedSpeed()); Metric rTangentV = (3.0 * vTemp.GetY()); int iTangentAngle = (iDirection + 90) % 360; int iSpreadWidth = m_pDesc->GetParticleSpreadWidth(); Metric rSpreadWidth = iSpreadWidth * g_KlicksPerPixel; bool bSpreadWidth = (iSpreadWidth > 0); // Create the particles with appropriate velocity for (i = 0; i < iCount; i++) { Metric rPlace = ((mathRandom(0, 25) + mathRandom(0, 25) + mathRandom(0, 25) + mathRandom(0, 25)) - 50.0) / 100.0; Metric rTangentPlace = ((mathRandom(0, 25) + mathRandom(0, 25) + mathRandom(0, 25) + mathRandom(0, 25)) - 50.0) / 100.0; CVector vPlace = PolarToVector(iDirection, rRadius * rPlace); CVector vVel = vInitVel + (0.05 * vPlace) + PolarToVector(iTangentAngle, rTangentV * rTangentPlace); // Compute the spread width CVector vPos = vSource + vPlace; if (bSpreadWidth) vPos = vPos + PolarToVector(iTangentAngle, rSpreadWidth * rTangentPlace); // Compute the travel rotation for these particles int iRotation = (bSpreadAngle ? VectorToPolar(GetVel() + vVel) : iDirection); // Create the particle m_Particles.AddParticle(vPos, vVel, m_pDesc->GetLifetime(), iRotation); } } }
void CBeamEffectCreator::DrawBeamHeavyBlaster (CG16bitImage &Dest, SLineDesc &Line, SViewportPaintCtx &Ctx) // DrawBeamHeavyBlaster // // Draws the appropriate beam { // Convert to an angle relative to xTo, yTo CVector vVec(Line.xFrom - Line.xTo, Line.yFrom - Line.yTo); Metric rRadius; int iAngle = VectorToPolar(vVec, &rRadius); int iRadius = (int)rRadius; // Can't deal with 0 sized lines if (iRadius == 0) return; CG16bitRegion Region; SPoint Poly[8]; // Compute some metrics int iLengthUnit = iRadius * (10 + m_iIntensity) / 40; int iWidthUnit = Max(1, iRadius * m_iIntensity / 60); // Paint the outer-most glow WORD wColor = CG16bitImage::BlendPixel(Ctx.wSpaceColor, m_wSecondaryColor, 100); CreateBlasterShape(iAngle, 4 * iLengthUnit, 3 * iWidthUnit / 2, Poly); Region.CreateFromConvexPolygon(8, Poly); Region.Fill(Dest, Line.xTo, Line.yTo, wColor); // Paint the inner transition wColor = CG16bitImage::BlendPixel(m_wSecondaryColor, m_wPrimaryColor, 128); wColor = CG16bitImage::BlendPixel(Ctx.wSpaceColor, wColor, 200); CreateBlasterShape(iAngle, 3 * iLengthUnit, iWidthUnit, Poly); Region.CreateFromConvexPolygon(8, Poly); Region.Fill(Dest, Line.xTo, Line.yTo, wColor); // Paint the inner core CreateBlasterShape(iAngle, iLengthUnit, iWidthUnit - 1, Poly); Region.CreateFromConvexPolygon(8, Poly); Region.Fill(Dest, Line.xTo, Line.yTo, m_wPrimaryColor); }
void CDockingPorts::InitPortsFromXML (CSpaceObject *pOwner, CXMLElement *pElement) // InitPortsFromXML // // InitPortsFromXML { // See if we've got a special element with docking port geometry CXMLElement *pDockingPorts = pElement->GetContentElementByTag(DOCKING_PORTS_TAG); if (pDockingPorts) { m_iPortCount = pDockingPorts->GetContentElementCount(); if (m_iPortCount > 0) { m_pPort = new DockingPort[m_iPortCount]; for (int i = 0; i < m_iPortCount; i++) { CXMLElement *pPort = pDockingPorts->GetContentElement(i); CVector vDockPos((pPort->GetAttributeInteger(X_ATTRIB) * g_KlicksPerPixel), (pPort->GetAttributeInteger(Y_ATTRIB) * g_KlicksPerPixel)); m_pPort[i].iStatus = psEmpty; m_pPort[i].pObj = NULL; m_pPort[i].vPos = vDockPos; m_pPort[i].iRotation = (VectorToPolar(vDockPos) + 180) % 360; } } } // Otherwise, initialize ports based on a count else InitPorts(pOwner, pElement->GetAttributeInteger(DOCKING_PORTS_ATTRIB), 64 * g_KlicksPerPixel); }
void CDockingPorts::InitPorts (CSpaceObject *pOwner, const TArray<CVector> &Desc) // InitPorts // // Initialize from array { ASSERT(m_pPort == NULL); if (Desc.GetCount() > 0) { m_iPortCount = Desc.GetCount(); m_pPort = new SDockingPort[m_iPortCount]; for (int i = 0; i < m_iPortCount; i++) { m_pPort[i].vPos = Desc[i]; m_pPort[i].iRotation = (VectorToPolar(Desc[i]) + 180) % 360; } } m_iMaxDist = DEFAULT_DOCK_DISTANCE_LS; }
void CMissile::OnUpdate (SUpdateCtx &Ctx, 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(); // LATER: We shouldn't have to update bounds here because // it is set in OnMove. SetBounds(m_pPainter); } // Done? if (--m_iLifeLeft <= 0) { Destroy(removedFromSystem, CDamageSource()); 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->IsTrackingTime(iTick) && m_pTarget) { // 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 int iFireAngle; Metric rCurrentSpeed = GetVel().Length(); Metric rTimeToIntercept = CalcInterceptTime(vTarget, vTargetVel, rCurrentSpeed); if (rTimeToIntercept > 0.0) { CVector vInterceptPoint = vTarget + vTargetVel * rTimeToIntercept; iFireAngle = VectorToPolar(vInterceptPoint, NULL); } else iFireAngle = VectorToPolar(vTarget); // Turn to desired direction. if (!AreAnglesAligned(iFireAngle, m_iRotation, 1)) { int iTurn = (iFireAngle + 360 - m_iRotation) % 360; if (iTurn >= 180) { int iTurnAngle = Min((360 - iTurn), m_pDesc->GetManeuverRate()); m_iRotation = (m_iRotation + 360 - iTurnAngle) % 360; } else { int iTurnAngle = Min(iTurn, m_pDesc->GetManeuverRate()); m_iRotation = (m_iRotation + iTurnAngle) % 360; } } 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(); // LATER: We shouldn't have to update bounds here because // it is set in OnMove. SetBounds(m_pPainter); } // If we have a vapor trail and need to save rotation, do it if (m_pDesc->GetVaporTrailLength() && m_pDesc->IsTracking()) { // Compute the current rotation int 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->GetVaporTrailLength()]; m_iSavedRotationsCount = 0; } int iStart = Min(m_iSavedRotationsCount, m_pDesc->GetVaporTrailLength() - 1); for (i = iStart; i > 0; i--) m_pSavedRotations[i] = m_pSavedRotations[i - 1]; m_pSavedRotations[0] = iDirection; if (m_iSavedRotationsCount < m_pDesc->GetVaporTrailLength()) m_iSavedRotationsCount++; } // See if the missile hit anything if (m_fDetonate && m_pDesc->ProximityBlast()) { CreateFragments(GetPos()); bDestroy = true; } else if (m_pHit) { // If we have fragments, then explode now if (m_iHitDir == -1 && m_pDesc->ProximityBlast() && m_iTick >= m_pDesc->GetProximityFailsafe()) { CreateFragments(m_vHitPos); bDestroy = true; } // Otherwise, if this was a direct hit, then we do damage else if (m_iHitDir != -1) { SDamageCtx DamageCtx; DamageCtx.pObj = m_pHit; DamageCtx.pDesc = m_pDesc; DamageCtx.Damage = m_pDesc->m_Damage; DamageCtx.Damage.AddBonus(m_iBonus); DamageCtx.Damage.SetCause(m_iCause); if (IsAutomatedWeapon()) DamageCtx.Damage.SetAutomatedWeapon(); DamageCtx.iDirection = (m_iHitDir + 360 + mathRandom(0, 30) - 15) % 360; DamageCtx.vHitPos = m_vHitPos; DamageCtx.pCause = this; DamageCtx.Attacker = m_Source; EDamageResults result = m_pHit->Damage(DamageCtx); // If we hit another missile (or some small object) there is a chance // that we continue if (result == damagePassthrough || result == damagePassthroughDestroyed) { m_iHitPoints = m_iHitPoints / 2; bDestroy = (m_iHitPoints == 0); } // Set the missile to destroy itself after a hit, if we did not // pass through else if (!m_fPassthrough) bDestroy = true; } } // 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->ProximityBlast()) CreateFragments(GetPos()); // If we've got a vapor trail effect, then keep the missile object alive // but mark it destroyed int iFadeLife; if (m_pDesc->GetVaporTrailLength()) { m_fDestroyed = true; m_iLifeLeft = m_pDesc->GetVaporTrailLength(); } // 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, CDamageSource()); return; } } } m_iTick++; }
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::InitPortsFromXML (CSpaceObject *pOwner, CXMLElement *pElement) // InitPortsFromXML // // InitPortsFromXML { int i; // See if we've got a special element with docking port geometry CXMLElement *pDockingPorts = pElement->GetContentElementByTag(DOCKING_PORTS_TAG); if (pDockingPorts) { // Initialize max dist // NOTE: pOwner can be NULL because sometimes we init ports from a ship class // (without an object). int iDefaultDist = Max(DEFAULT_DOCK_DISTANCE_LS, (pOwner ? 8 + (int)((pOwner->GetBoundsRadius() / LIGHT_SECOND) + 0.5) : 0)); m_iMaxDist = pDockingPorts->GetAttributeIntegerBounded(MAX_DIST_ATTRIB, 1, -1, iDefaultDist); // If we have sub-elements then these are port definitions. m_iPortCount = pDockingPorts->GetContentElementCount(); if (m_iPortCount > 0) { m_pPort = new SDockingPort[m_iPortCount]; for (i = 0; i < m_iPortCount; i++) { CXMLElement *pPort = pDockingPorts->GetContentElement(i); CVector vDockPos((pPort->GetAttributeInteger(X_ATTRIB) * g_KlicksPerPixel), (pPort->GetAttributeInteger(Y_ATTRIB) * g_KlicksPerPixel)); m_pPort[i].vPos = vDockPos; if (pPort->FindAttributeInteger(ROTATION_ATTRIB, &m_pPort[i].iRotation)) m_pPort[i].iRotation = (m_pPort[i].iRotation % 360); else m_pPort[i].iRotation = (VectorToPolar(vDockPos) + 180) % 360; if (pPort->GetAttributeBool(BRING_TO_FRONT_ATTRIB)) m_pPort[i].iLayer = plBringToFront; else if (pPort->GetAttributeBool(SEND_TO_BACK_ATTRIB)) m_pPort[i].iLayer = plSendToBack; } } // Otherwise, we expect a port count and radius else if ((m_iPortCount = pDockingPorts->GetAttributeIntegerBounded(PORT_COUNT_ATTRIB, 0, -1, 0)) > 0) { int iRadius = pDockingPorts->GetAttributeIntegerBounded(PORT_RADIUS_ATTRIB, 0, -1, DEFAULT_PORT_POS_RADIUS); int iAngle = 360 / m_iPortCount; // We need the image scale to adjust coordinates int iScale = (pOwner ? pOwner->GetImage().GetImageViewportSize() : 512); // Initialize ports m_pPort = new SDockingPort[m_iPortCount]; for (i = 0; i < m_iPortCount; i++) { C3DConversion::CalcCoord(iScale, i * iAngle, iRadius, 0, &m_pPort[i].vPos); m_pPort[i].iRotation = ((i * iAngle) + 180) % 360; } } // Otherwise, no ports else { m_iPortCount = 0; m_pPort = NULL; } } // Otherwise, initialize ports based on a count else InitPorts(pOwner, pElement->GetAttributeInteger(DOCKING_PORTS_ATTRIB), 64 * g_KlicksPerPixel); }
void CDockingPorts::UpdateDockingManeuvers (CSpaceObject *pOwner, DockingPort &Port) // UpdateDockingManeuvers // // Updates the motion of a ship docking with a port { CShip *pShip = Port.pObj->AsShip(); ASSERT(pShip); if (pShip == NULL) return; CVector vDest = pOwner->GetPos() + Port.vPos; CVector vDestVel = pOwner->GetVel(); // Figure out how far we are from where we want to be CVector vDelta = vDest - pShip->GetPos(); // Figure out if we're aligned int iFinalRotation = pShip->AlignToRotationAngle(Port.iRotation); // If the docking object is within the appropriate threshold // of the port, then complete the docking sequence. Metric rDelta2 = vDelta.Length2(); if (rDelta2 < DOCKING_THRESHOLD2 && (pShip == g_pUniverse->GetPlayer() || iFinalRotation == pShip->GetRotation())) { pShip->Place(vDest); pShip->UnfreezeControls(); IShipController *pController = pShip->GetController(); pController->SetManeuver(IShipController::NoRotation); Port.iStatus = psInUse; // Tell the owner that somone has docked with it first // (We do this because sometimes we want to handle stuff // in OnObjDocked before we show the player a dock screen) if (pOwner && pOwner->HasOnObjDockedEvent() && pOwner != pShip && !pOwner->IsDestroyed() && pShip->IsSubscribedToEvents(pOwner)) pOwner->FireOnObjDocked(pShip, pOwner); // Set the owner as "explored" (if the ship is the player) if (pShip->IsPlayer()) pOwner->SetExplored(); // Dock pShip->OnDocked(pOwner); // Notify other subscribers pShip->NotifyOnObjDocked(pOwner); } // Otherwise accelerate the ship towards the docking port else { Metric rMaxSpeed = pShip->GetMaxSpeed(); Metric rMinSpeed = rMaxSpeed / 10.0; // We slow down as we get closer Metric rSpeed; if (rDelta2 < FINAL_DOCKING2) rSpeed = rMinSpeed; else if (rDelta2 < FINAL_APPROACH2) { Metric rSpeedRange = rMaxSpeed - rMinSpeed; Metric rDelta = sqrt(rDelta2); rSpeed = rMinSpeed + (rSpeedRange * (rDelta - FINAL_DOCKING) / (FINAL_APPROACH - FINAL_DOCKING)); } else rSpeed = rMaxSpeed; // Figure out the ideal velocity vector that we want to // be following. CVector vIdealVel = vDelta.Normal() * rSpeed; // Calculate the delta v that we need CVector vDeltaV = vIdealVel - pShip->GetVel(); // Rotate if (!pShip->IsPlayer()) { IShipController *pController = pShip->GetController(); // If we're close enough, align to rotation angle if (rDelta2 < FINAL_APPROACH2) pController->SetManeuver(CalcTurnManeuver(iFinalRotation, pShip->GetRotation(), pShip->GetRotationAngle())); // Otherwise, align along delta v else pController->SetManeuver(CalcTurnManeuver(VectorToPolar(vDeltaV), pShip->GetRotation(), pShip->GetRotationAngle())); // Don't let the AI thrust pController->SetThrust(false); } // Accelerate pShip->Accelerate(vDeltaV * pShip->GetMass() / 10000.0, g_SecondsPerUpdate); pShip->ClipSpeed(rSpeed); } }
void CDockingPorts::InitPortsFromXML (CSpaceObject *pOwner, CXMLElement *pElement) // InitPortsFromXML // // InitPortsFromXML { int i; // See if we've got a special element with docking port geometry CXMLElement *pDockingPorts = pElement->GetContentElementByTag(DOCKING_PORTS_TAG); if (pDockingPorts) { // Initialize max dist m_iMaxDist = pDockingPorts->GetAttributeIntegerBounded(MAX_DIST_ATTRIB, 1, -1, DEFAULT_DOCK_DISTANCE_LS); // If we have sub-elements then these are port definitions. m_iPortCount = pDockingPorts->GetContentElementCount(); if (m_iPortCount > 0) { m_pPort = new DockingPort[m_iPortCount]; for (i = 0; i < m_iPortCount; i++) { CXMLElement *pPort = pDockingPorts->GetContentElement(i); CVector vDockPos((pPort->GetAttributeInteger(X_ATTRIB) * g_KlicksPerPixel), (pPort->GetAttributeInteger(Y_ATTRIB) * g_KlicksPerPixel)); m_pPort[i].iStatus = psEmpty; m_pPort[i].pObj = NULL; m_pPort[i].vPos = vDockPos; if (pPort->FindAttributeInteger(ROTATION_ATTRIB, &m_pPort[i].iRotation)) m_pPort[i].iRotation = (m_pPort[i].iRotation % 360); else m_pPort[i].iRotation = (VectorToPolar(vDockPos) + 180) % 360; } } // Otherwise, we expect a port count and radius else if ((m_iPortCount = pDockingPorts->GetAttributeIntegerBounded(PORT_COUNT_ATTRIB, 0, -1, 0)) > 0) { m_pPort = new DockingPort[m_iPortCount]; int iRadius = pDockingPorts->GetAttributeIntegerBounded(PORT_RADIUS_ATTRIB, 0, -1, DEFAULT_PORT_POS_RADIUS); Metric rRadius = g_KlicksPerPixel * iRadius; int iAngle = 360 / m_iPortCount; for (i = 0; i < m_iPortCount; i++) { m_pPort[i].iStatus = psEmpty; m_pPort[i].pObj = NULL; m_pPort[i].vPos = PolarToVector(i * iAngle, rRadius); m_pPort[i].iRotation = ((i * iAngle) + 180) % 360; } } // Otherwise, no ports else { m_iPortCount = 0; m_pPort = NULL; } } // Otherwise, initialize ports based on a count else InitPorts(pOwner, pElement->GetAttributeInteger(DOCKING_PORTS_ATTRIB), 64 * g_KlicksPerPixel); }
void CBeamEffectCreator::DrawBeamLightning (CG16bitImage &Dest, SLineDesc &Line, SViewportPaintCtx &Ctx) // DrawBeamLightning // // Draws the appropriate beam { // The central part of the beam is different depending on the // intensity. if (m_iIntensity < 4) { WORD wStart = CG16bitImage::BlendPixel(Ctx.wSpaceColor, m_wPrimaryColor, 128); Dest.DrawBiColorLine(Line.xFrom, Line.yFrom, Line.xTo, Line.yTo, 3, Ctx.wSpaceColor, wStart); WORD wEnd = CG16bitImage::BlendPixel(Ctx.wSpaceColor, m_wPrimaryColor, 155); Dest.DrawBiColorLine(Line.xFrom, Line.yFrom, Line.xTo, Line.yTo, 1, wEnd, m_wPrimaryColor); } else if (m_iIntensity < 10) { WORD wStart = CG16bitImage::BlendPixel(Ctx.wSpaceColor, m_wSecondaryColor, 155); Dest.DrawBiColorLine(Line.xFrom, Line.yFrom, Line.xTo, Line.yTo, 5, Ctx.wSpaceColor, wStart); Dest.DrawBiColorLine(Line.xFrom, Line.yFrom, Line.xTo, Line.yTo, 3, Ctx.wSpaceColor, m_wSecondaryColor); WORD wEnd = CG16bitImage::BlendPixel(Ctx.wSpaceColor, m_wPrimaryColor, 155); Dest.DrawBiColorLine(Line.xFrom, Line.yFrom, Line.xTo, Line.yTo, 1, wEnd, m_wPrimaryColor); } else { // Convert to an angle relative to xTo, yTo CVector vVec(Line.xFrom - Line.xTo, Line.yFrom - Line.yTo); Metric rRadius; int iAngle = VectorToPolar(vVec, &rRadius); int iRadius = (int)rRadius; // Can't deal with 0 sized lines if (iRadius == 0) return; CG16bitRegion Region; SPoint Poly[8]; // Paint the outer-most glow WORD wColor = CG16bitImage::BlendPixel(Ctx.wSpaceColor, m_wSecondaryColor, 100); CreateBlasterShape(iAngle, iRadius, iRadius / 6, Poly); Region.CreateFromConvexPolygon(8, Poly); Region.Fill(Dest, Line.xTo, Line.yTo, wColor); // Paint the inner transition wColor = CG16bitImage::BlendPixel(m_wSecondaryColor, m_wPrimaryColor, 128); wColor = CG16bitImage::BlendPixel(Ctx.wSpaceColor, wColor, 200); CreateBlasterShape(iAngle, iRadius * 2 / 3, iRadius / 7, Poly); Region.CreateFromConvexPolygon(8, Poly); Region.Fill(Dest, Line.xTo, Line.yTo, wColor); // Paint the inner core CreateBlasterShape(iAngle, iRadius / 2, iRadius / 8, Poly); Region.CreateFromConvexPolygon(8, Poly); Region.Fill(Dest, Line.xTo, Line.yTo, m_wPrimaryColor); } // Compute the half-way point int xHalf = (Line.xFrom + Line.xTo) / 2; int yHalf = (Line.yFrom + Line.yTo) / 2; // Draw lightning int iCount = m_iIntensity + mathRandom(0, 2); for (int j = 0; j < iCount; j++) if (mathRandom(1, 2) == 1) DrawLightning(Dest, xHalf, yHalf, Line.xTo, Line.yTo, m_wPrimaryColor, LIGHTNING_POINT_COUNT, 0.5); else DrawLightning(Dest, Line.xFrom, Line.yFrom, Line.xTo, Line.yTo, m_wSecondaryColor, LIGHTNING_POINT_COUNT, 0.3); }
void CDockingPorts::UpdateAll (CSpaceObject *pOwner) // UpdateAll // // UpdateAll { int i, j; for (i = 0; i < m_iPortCount; i++) { if (m_pPort[i].iStatus == psDocking) { CShip *pShip = m_pPort[i].pObj->AsShip(); ASSERT(pShip); if (pShip == NULL) continue; CVector vDest = pOwner->GetPos() + m_pPort[i].vPos; CVector vDestVel = pOwner->GetVel(); // Figure out how far we are from where we want to be CVector vDelta = vDest - pShip->GetPos(); // Figure out if we're aligned int iFinalRotation = pShip->AlignToRotationAngle(m_pPort[i].iRotation); // If the docking object is within the appropriate threshold // of the port, then complete the docking sequence. Metric rDelta2 = vDelta.Length2(); if (rDelta2 < DOCKING_THRESHOLD2 && (pShip == g_pUniverse->GetPlayer() || iFinalRotation == pShip->GetRotation())) { pShip->Place(vDest); pShip->UnfreezeControls(); IShipController *pController = pShip->GetController(); pController->SetManeuver(IShipController::NoRotation); m_pPort[i].iStatus = psInUse; // Tell the owner that somone has docked with it first // (We do this because sometimes we want to handle stuff // in OnObjDocked before we show the player a dock screen) if (pOwner && pOwner->HasOnObjDockedEvent() && pOwner != pShip) pOwner->OnObjDocked(pShip, pOwner); // Dock pShip->OnDocked(pOwner); // Tell all objects in the system that a ship has docked CSystem *pSystem = pShip->GetSystem(); for (j = 0; j < pSystem->GetObjectCount(); j++) { CSpaceObject *pObj = pSystem->GetObject(j); if (pObj && pObj->HasOnObjDockedEvent() && pObj != pShip && pObj != pOwner) pObj->OnObjDocked(pShip, pOwner); } } // Otherwise accelerate the ship towards the docking port else { Metric rMaxSpeed = pShip->GetMaxSpeed(); Metric rMinSpeed = rMaxSpeed / 10.0; // We slow down as we get closer Metric rSpeed; if (rDelta2 < FINAL_DOCKING2) rSpeed = rMinSpeed; else if (rDelta2 < FINAL_APPROACH2) { Metric rSpeedRange = rMaxSpeed - rMinSpeed; Metric rDelta = sqrt(rDelta2); rSpeed = rMinSpeed + (rSpeedRange * (rDelta - FINAL_DOCKING) / (FINAL_APPROACH - FINAL_DOCKING)); } else rSpeed = rMaxSpeed; // Figure out the ideal velocity vector that we want to // be following. CVector vIdealVel = vDelta.Normal() * rSpeed; // Calculate the delta v that we need CVector vDeltaV = vIdealVel - pShip->GetVel(); // Rotate if (pShip != g_pUniverse->GetPlayer()) { IShipController *pController = pShip->GetController(); // If we're close enough, align to rotation angle if (rDelta2 < FINAL_APPROACH2) pController->SetManeuver(CalcTurnManeuver(iFinalRotation, pShip->GetRotation(), pShip->GetRotationAngle())); // Otherwise, align along delta v else pController->SetManeuver(CalcTurnManeuver(VectorToPolar(vDeltaV), pShip->GetRotation(), pShip->GetRotationAngle())); } // Accelerate pShip->Accelerate(vDeltaV * pShip->GetMass() / 10000.0, g_SecondsPerUpdate); pShip->ClipSpeed(rSpeed); } } } }