ALERROR CLevelTableOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) // LoadFromXML // // Loads from XML { int i; ALERROR error; m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB)); if (m_Count.IsEmpty()) m_Count.SetConstant(1); for (i = 0; i < pDesc->GetContentElementCount(); i++) { CXMLElement *pEntry = pDesc->GetContentElement(i); SEntry *pNewEntry = m_Table.Insert(); pNewEntry->sLevelFrequency = pEntry->GetAttribute(LEVEL_FREQUENCY_ATTRIB); if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &pNewEntry->pDevice)) { pNewEntry->pDevice = NULL; return error; } } m_iComputedLevel = -1; return NOERROR; }
void CParticleJetEffectPainter::CreateLinearParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel) // CreateLinearParticles // // Creates new particles on a straight line { int i; // Compute some basic stuff const Metric rJitterFactor = LIGHT_SPEED / 100000.0; Metric rCurRotation = AngleToRadians(180 + m_iXformRotation + m_iCurDirection); // Compute the spread angle, in radians Metric rSpread = AngleToRadians(Max(0, m_SpreadAngle.Roll())); Metric rHalfSpread = 0.5 * rSpread; // Calculate where last tick's particles would be based on the last rotation. Metric rAveSpeed = m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0; CVector vCurStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rCurRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos; // Create particles for (i = 0; i < iCount; i++) { Metric rSlide = mathRandom(0, 9999) / 10000.0; // We place the particle along the line betwen the current // and last emit positions CVector vPos = vInitialPos + rSlide * vCurStart; // Generate a random velocity backwards Metric rRotation = rCurRotation + (rHalfSpread * mathRandom(-1000, 1000) / 1000.0); Metric rSpeed = m_EmitSpeed.Roll() * LIGHT_SPEED / 100.0; CVector vVel = m_rXformTime * (vInitialVel + ::PolarToVectorRadians(rRotation, rSpeed + rJitterFactor * mathRandom(-500, 500))); // Lifetime int iLifeLeft = m_ParticleLifetime.Roll(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft, AngleToDegrees(rRotation)); } // Remember the last position m_iLastDirection = m_iCurDirection; m_vLastEmitPos = vInitialPos; }
void CTableOfDeviceGenerators::AddDevices (SDeviceGenerateCtx &Ctx) // AddDevices // // Add devices { int i, j; int iCount = m_Count.Roll(); for (j = 0; j < iCount; j++) { int iRoll = mathRandom(1, m_iTotalChance); for (i = 0; i < m_Table.GetCount(); i++) { iRoll -= m_Table[i].iChance; if (iRoll <= 0) { m_Table[i].pDevice->AddDevices(Ctx); break; } } } }
ALERROR CGroupOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) // LoadFromXML // // Load from XML { int i; ALERROR error; m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB)); if (m_Count.IsEmpty()) m_Count.SetConstant(1); // Load either a <DeviceSlot> element or another device generator. for (i = 0; i < pDesc->GetContentElementCount(); i++) { CXMLElement *pEntry = pDesc->GetContentElement(i); if (strEquals(pEntry->GetTag(), DEVICE_SLOT_TAG)) { SSlotDesc *pSlotDesc = m_SlotDesc.Insert(); CItem::ParseCriteria(pEntry->GetAttribute(CRITERIA_ATTRIB), &pSlotDesc->Criteria); if (error = IDeviceGenerator::InitDeviceDescFromXML(Ctx, pEntry, &pSlotDesc->DefaultDesc)) return error; pSlotDesc->iMaxCount = pEntry->GetAttributeIntegerBounded(MAX_COUNT_ATTRIB, 0, -1, -1); } else { SEntry *pTableEntry = m_Table.Insert(); pTableEntry->iChance = pEntry->GetAttributeIntegerBounded(CHANCE_ATTRIB, 0, -1, 100); if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &pTableEntry->pDevice)) { pTableEntry->pDevice = NULL; return error; } } } return NOERROR; }
void CParticleJetEffectPainter::InitParticles (const CVector &vInitialPos) // Init // // Make sure particle array is initialized { if (m_Particles.GetCount() == 0) { // Compute the maximum number of particles that we could ever have int iNewParticleRate = m_EmitRate.GetMaxValue(); int iParticleLifetime = m_ParticleLifetime.GetMaxValue(); int iMaxParticleCount = Max(0, iParticleLifetime * iNewParticleRate); // Initialize the array m_Particles.Init(iMaxParticleCount); } }
ALERROR CTableOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) // LoadFromXML // // Load from XML { int i; ALERROR error; m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB)); if (m_Count.IsEmpty()) m_Count.SetConstant(1); m_iTotalChance = 0; int iCount = pDesc->GetContentElementCount(); if (iCount > 0) { m_Table.InsertEmpty(iCount); for (i = 0; i < iCount; i++) { CXMLElement *pEntry = pDesc->GetContentElement(i); m_Table[i].iChance = pEntry->GetAttributeInteger(CHANCE_ATTRIB); m_iTotalChance += m_Table[i].iChance; if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &m_Table[i].pDevice)) { m_Table[i].pDevice = NULL; return error; } } } return NOERROR; }
void CLevelTableOfDeviceGenerators::AddDevices (SDeviceGenerateCtx &Ctx) // AddDevices // // Adds devices { int i, j; // Compute probabilities if (Ctx.iLevel != m_iComputedLevel) { m_iTotalChance = 0; for (i = 0; i < m_Table.GetCount(); i++) { m_Table[i].iChance = GetFrequencyByLevel(m_Table[i].sLevelFrequency, Ctx.iLevel); m_iTotalChance += m_Table[i].iChance; } m_iComputedLevel = Ctx.iLevel; } // Generate if (m_iTotalChance) { int iCount = m_Count.Roll(); for (i = 0; i < iCount; i++) { int iRoll = mathRandom(1, m_iTotalChance); for (j = 0; j < m_Table.GetCount(); j++) { iRoll -= m_Table[j].iChance; if (iRoll <= 0) { m_Table[j].pDevice->AddDevices(Ctx); break; } } } } }
void CGroupOfDeviceGenerators::AddDevices (SDeviceGenerateCtx &Ctx) // AddDevices // // Add devices { int i, j; int iCount = m_Count.Roll(); for (j = 0; j < iCount; j++) { for (i = 0; i < m_Table.GetCount(); i++) { if (mathRandom(1, 100) <= m_Table[i].iChance) m_Table[i].pDevice->AddDevices(Ctx); } } }
void CParticleJetEffectPainter::OnUpdate (SEffectUpdateCtx &Ctx) // OnUpdate // // Update the painter { // Update the single-particle painter if (m_pParticlePainter) m_pParticlePainter->OnUpdate(Ctx); // LATER: Support damage #if 0 // If we have a wake potential or if the particles do damage // then we need to hit test against all objects in the system. Ctx.pDamageDesc = m_pCreator->GetDamageDesc(); Ctx.iWakePotential = m_pCreator->GetWakePotential(); if ((Ctx.pDamageDesc || Ctx.iWakePotential > 0) && Ctx.pSystem) { // Update m_Particles.Update(Ctx); } #endif // Create new particles if (!Ctx.bFade) CreateNewParticles(Ctx.pObj, m_EmitRate.Roll(), Ctx.vEmitPos, CalcInitialVel(Ctx.pObj)); else if (m_bUseObjectMotion && Ctx.pObj) m_vLastEmitPos = Ctx.pObj->GetPos(); // If we're fading, reset direction (otherwise, when painting thruster // effects we'll try to interpolate between stale directions). if (Ctx.bFade) m_iCurDirection = -1; }
ALERROR CSingleDevice::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc) // LoadFromXML // // Load from XML { ALERROR error; // Load the item type CString sUNID = pDesc->GetAttribute(DEVICE_ID_ATTRIB); if (sUNID.IsBlank()) sUNID = pDesc->GetAttribute(ITEM_ATTRIB); if (error = m_pItemType.LoadUNID(Ctx, sUNID)) return error; if (m_pItemType.GetUNID() == 0) { Ctx.sError = strPatternSubst(CONSTLIT("<%s> element missing item attribute."), pDesc->GetTag()); return ERR_FAIL; } // Load the count m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB)); if (m_Count.IsEmpty()) m_Count.SetConstant(1); // Load damage chance m_iDamaged = pDesc->GetAttributeInteger(DAMAGED_ATTRIB); // Load enhancement chance if (error = m_Enhanced.InitFromXML(Ctx, pDesc)) return error; // Load device position attributes if (pDesc->FindAttributeInteger(POS_Z_ATTRIB, &m_iPosZ)) { m_iPosAngle = pDesc->GetAttributeInteger(POS_ANGLE_ATTRIB); m_iPosRadius = pDesc->GetAttributeInteger(POS_RADIUS_ATTRIB); m_b3DPosition = true; m_bDefaultPos = false; } else if (pDesc->FindAttributeInteger(POS_ANGLE_ATTRIB, &m_iPosAngle)) { m_iPosRadius = pDesc->GetAttributeInteger(POS_RADIUS_ATTRIB); m_iPosZ = 0; m_b3DPosition = false; m_bDefaultPos = false; } else { m_iPosAngle = 0; m_iPosRadius = 0; m_iPosZ = 0; m_b3DPosition = false; m_bDefaultPos = true; } // Load fire arc attributes if (pDesc->FindAttributeInteger(MIN_FIRE_ARC_ATTRIB, &m_iMinFireArc)) { m_bOmnidirectional = false; m_iMaxFireArc = pDesc->GetAttributeInteger(MAX_FIRE_ARC_ATTRIB); m_bDefaultFireArc = false; } else if (pDesc->FindAttributeBool(OMNIDIRECTIONAL_ATTRIB, &m_bOmnidirectional)) { m_iMinFireArc = 0; m_iMaxFireArc = 0; m_bDefaultFireArc = false; } else { m_bOmnidirectional = false; m_iMinFireArc = 0; m_iMaxFireArc = 0; m_bDefaultFireArc = true; } // Linked fire options CString sLinkedFire; if (pDesc->FindAttribute(LINKED_FIRE_ATTRIB, &sLinkedFire)) { if (error = CDeviceClass::ParseLinkedFireOptions(Ctx, sLinkedFire, &m_dwLinkedFireOptions)) return error; m_bDefaultLinkedFire = false; } else { m_dwLinkedFireOptions = 0; m_bDefaultLinkedFire = true; } m_bSecondary = pDesc->GetAttributeBool(SECONDARY_WEAPON_ATTRIB); // Slot bonus if (pDesc->FindAttributeInteger(HP_BONUS_ATTRIB, &m_iSlotBonus)) m_bDefaultSlotBonus = false; else { m_iSlotBonus = 0; m_bDefaultSlotBonus = true; } // Load extra items CXMLElement *pItems = pDesc->GetContentElementByTag(ITEMS_TAG); if (pItems) { if (error = IItemGenerator::CreateFromXML(Ctx, pItems, &m_pExtraItems)) return error; } return NOERROR; }
void CSingleDevice::AddDevices (SDeviceGenerateCtx &Ctx) // AddDevices // // Add devices to list { int i; if (m_pItemType == NULL) return; int iCount = m_Count.Roll(); for (i = 0; i < iCount; i++) { // Initialize the desc SDeviceDesc Desc; Desc.Item = CItem(m_pItemType, 1); if (mathRandom(1, 100) <= m_iDamaged) Desc.Item.SetDamaged(); else m_Enhanced.EnhanceItem(Desc.Item); // Find the default settings for the device slot for this device SDeviceDesc SlotDesc; bool bUseSlotDesc = (Ctx.pRoot ? Ctx.pRoot->FindDefaultDesc(Desc.Item, &SlotDesc) : false); // Set the device position appropriately, either from the <Device> element, // from the slot descriptor at the root, or from defaults. if (!m_bDefaultPos) { Desc.iPosAngle = m_iPosAngle; Desc.iPosRadius = m_iPosRadius; Desc.iPosZ = m_iPosZ; Desc.b3DPosition = m_b3DPosition; } else if (bUseSlotDesc) { Desc.iPosAngle = SlotDesc.iPosAngle; Desc.iPosRadius = SlotDesc.iPosRadius; Desc.iPosZ = SlotDesc.iPosZ; Desc.b3DPosition = SlotDesc.b3DPosition; } // Set the device fire arc appropriately. if (!m_bDefaultFireArc) { Desc.bOmnidirectional = m_bOmnidirectional; Desc.iMinFireArc = m_iMinFireArc; Desc.iMaxFireArc = m_iMaxFireArc; } else if (bUseSlotDesc) { Desc.bOmnidirectional = SlotDesc.bOmnidirectional; Desc.iMinFireArc = SlotDesc.iMinFireArc; Desc.iMaxFireArc = SlotDesc.iMaxFireArc; } // Set linked fire if (!m_bDefaultLinkedFire) Desc.dwLinkedFireOptions = m_dwLinkedFireOptions; else if (bUseSlotDesc) Desc.dwLinkedFireOptions = SlotDesc.dwLinkedFireOptions; else Desc.dwLinkedFireOptions = 0; Desc.bSecondary = m_bSecondary; // Slot bonus if (!m_bDefaultSlotBonus) Desc.iSlotBonus = m_iSlotBonus; else if (bUseSlotDesc) Desc.iSlotBonus = SlotDesc.iSlotBonus; else Desc.iSlotBonus = 0; // Add extra items if (m_pExtraItems) { CItemListManipulator ItemList(Desc.ExtraItems); SItemAddCtx ItemCtx(ItemList); ItemCtx.iLevel = Ctx.iLevel; m_pExtraItems->AddItems(ItemCtx); } // Done Ctx.pResult->AddDeviceDesc(Desc); } }
void CParticleJetEffectPainter::Paint (CG32bitImage &Dest, int x, int y, SViewportPaintCtx &Ctx) // Paint // // Paint the effect { int iParticleLifetime = m_ParticleLifetime.GetMaxValue(); // Particles move the opposite direction from the shot int iTrailDirection = Ctx.iRotation; // If we're using the object center then we paint at the object center. // Otherwise we paint at the given position. int xPaint; int yPaint; if (m_bUseObjectCenter) { if (Ctx.pObj) Ctx.XForm.Transform(Ctx.pObj->GetPos(), &xPaint, &yPaint); else { // If we don't have an object then we use the viewport center. This // handles the case where we paint in TransData (where there is // no object). xPaint = Ctx.xCenter; yPaint = Ctx.yCenter; } } else { xPaint = x; yPaint = y; } // If we haven't created any particles yet, do it now if (m_iCurDirection == -1 && !Ctx.bFade) { m_iLastDirection = iTrailDirection; m_iCurDirection = iTrailDirection; // Figure out the position where we create particles CVector vPos; // If we're using the object center then it means that x,y is where // we emit particles from. We need to convert from screen coordinates // to object-relative coordinates. if (m_bUseObjectCenter) vPos = CVector((x - xPaint) * g_KlicksPerPixel, (yPaint - y) * g_KlicksPerPixel); // Initialize last emit position m_vLastEmitPos = (m_bUseObjectMotion && Ctx.pObj ? Ctx.pObj->GetPos() + vPos : vPos); // Create particles CreateNewParticles(Ctx.pObj, m_EmitRate.Roll(), vPos, CalcInitialVel(Ctx.pObj)); } // Paint with the painter if (m_pParticlePainter) { // If we can get a paint descriptor, use that because it is faster SParticlePaintDesc Desc; if (m_pParticlePainter->GetParticlePaintDesc(&Desc)) { Desc.iMaxLifetime = iParticleLifetime; m_Particles.Paint(Dest, xPaint, yPaint, Ctx, Desc); } // Otherwise, we use the painter for each particle else m_Particles.Paint(Dest, xPaint, yPaint, Ctx, m_pParticlePainter); } // Update m_iCurDirection = iTrailDirection; }
void CParticleJetEffectPainter::CreateInterpolatedParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel) // CreateInterpolatedParticles // // Creates particles interpolated between to directions. { int i; // Compute some basic stuff const Metric rJitterFactor = LIGHT_SPEED / 100000.0; Metric rLastRotation = AngleToRadians(180 + m_iXformRotation + m_iLastDirection); Metric rCurRotation = AngleToRadians(180 + m_iXformRotation + m_iCurDirection); // Compute the spread angle, in radians Metric rSpread = AngleToRadians(Max(0, m_SpreadAngle.Roll())); Metric rHalfSpread = 0.5 * rSpread; // Calculate where last tick's particles would be based on the last rotation. Metric rAveSpeed = m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0; CVector vLastStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rLastRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos; // Calculate where last tick's particles would be IF we have used the current // rotation. This allows us to interpolate a turn. CVector vCurStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rCurRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos; // Create particles for (i = 0; i < iCount; i++) { Metric rSlide = mathRandom(0, 9999) / 10000.0; // Compute two points along the two slide vectors (last and current) CVector vSlide1 = rSlide * vLastStart; CVector vSlide2 = rSlide * vCurStart; CVector vAdj = (rSlide * vSlide1) + ((1.0 - rSlide) * vSlide2); // We place the particle along the line betwen the current // and last emit positions CVector vPos = vInitialPos + vAdj; // We blend the rotation as well if (Absolute(rCurRotation - rLastRotation) > g_Pi) { if (rLastRotation < rCurRotation) rLastRotation += g_Pi * 2.0; else rCurRotation += g_Pi * 2.0; } Metric rSlideRotation = (rSlide * rLastRotation) + ((1.0 - rSlide) * rCurRotation); // Generate a random velocity backwards Metric rRotation = rSlideRotation + (rHalfSpread * mathRandom(-1000, 1000) / 1000.0); Metric rSpeed = m_EmitSpeed.Roll() * LIGHT_SPEED / 100.0; CVector vVel = m_rXformTime * (vInitialVel + ::PolarToVectorRadians(rRotation, rSpeed + rJitterFactor * mathRandom(-500, 500))); // Lifetime int iLifeLeft = m_ParticleLifetime.Roll(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft, AngleToDegrees(rRotation)); } // Remember the last position m_iLastDirection = m_iCurDirection; m_vLastEmitPos = vInitialPos; }
void CParticleJetEffectPainter::CreateFixedParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel) // CreateFixedParticles // // Creates particles along the objects path (e.g., missile exhaust). { int i; // Calculate a vector to our previous position // // NOTE: In this mode m_vLastEmitPos is the last position of the object. CVector vCurPos = (pObj ? pObj->GetPos() : CVector()); CVector vToOldPos; if (m_bTrackingObject) { Metric rAveSpeed = m_rXformTime * m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0; vToOldPos = m_vLastEmitPos - (vCurPos + vInitialPos) + ::PolarToVector(180 + m_iLastDirection, rAveSpeed * g_SecondsPerUpdate); } else { Metric rSpeed = (pObj ? pObj->GetVel().Length() : 0.0); vToOldPos = ::PolarToVector(180 + m_iLastDirection, rSpeed * g_SecondsPerUpdate); } // Compute two orthogonal coordinates CVector vAxis = ::PolarToVector(m_iCurDirection + 180, 1.0); CVector vTangent = ::PolarToVector(m_iCurDirection + 90, 1.0); // Create particles for (i = 0; i < iCount; i++) { Metric rSlide = mathRandom(0, 9999) / 10000.0; // Compute a position randomly along the line between the current and // last emit positions. CVector vPos = vInitialPos + rSlide * vToOldPos; // Generate a random velocity along the tangent Metric rTangentSlide = mathRandom(-9999, 9999) / 10000.0; Metric rAxisJitter = mathRandom(-50, 50) / 100.0; CVector vVel = (vTangent * rTangentSlide * m_rXformTime * m_TangentSpeed.Roll() * LIGHT_SPEED / 100.0) + (vAxis * (m_EmitSpeed.Roll() + rAxisJitter) * LIGHT_SPEED / 100.0); // Lifetime int iLifeLeft = m_ParticleLifetime.Roll(); // Add the particle m_Particles.AddParticle(vPos, vVel, iLifeLeft, m_iCurDirection); } // Remember the last position m_iLastDirection = m_iCurDirection; m_vLastEmitPos = vCurPos + vInitialPos; }