ALERROR CEffectCreator::CreateBeamEffect (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID, CEffectCreator **retpCreator) // CreateBeamEffect // // Create a beam effect { ALERROR error; // Create the effect CEffectCreator *pCreator = new CBeamEffectCreator; pCreator->m_sUNID = sUNID; // Type-specific creation if (error = pCreator->OnEffectCreateFromXML(Ctx, pDesc, sUNID)) return error; // Done *retpCreator = pCreator; return NOERROR; }
ALERROR CEffectCreator::CreateSimpleFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID, CEffectCreator **retpCreator) // CreateSimpleFromXML // // Creates the creator from an XML element { ALERROR error; CEffectCreator *pCreator; // Create the effect based on the tag if (error = CreateFromTag(pDesc->GetTag(), &pCreator)) { Ctx.sError = strPatternSubst(CONSTLIT("Invalid painter tag: %s"), pDesc->GetTag()); return error; } if (pCreator == NULL) return ERR_MEMORY; pCreator->m_sUNID = sUNID; // Type-specific creation if (error = pCreator->OnEffectCreateFromXML(Ctx, pDesc, sUNID)) return error; // Done *retpCreator = pCreator; return NOERROR; }
void CWeaponFireDesc::CreateHitEffect (CSystem *pSystem, SDamageCtx &DamageCtx) // CreateHitEffect // // Creates an effect when the weapon hits an object { // See if this weapon has a hit effect CEffectCreator *pHitEffect = m_pHitEffect; // If not, compute a default hit effect depending on the weapon damage type if (pHitEffect == NULL) pHitEffect = g_pUniverse->FindDefaultHitEffect(m_Damage.GetDamageType()); // If we could not come up with a hit effect then we're done. if (pHitEffect == NULL) return; // Create the effect pHitEffect->CreateEffect(pSystem, ((DamageCtx.pObj && !DamageCtx.pObj->IsDestroyed()) ? DamageCtx.pObj : NULL), DamageCtx.vHitPos, (DamageCtx.pObj ? DamageCtx.pObj->GetVel() : CVector()), DamageCtx.iDirection, DamageCtx.iDamage); }
ALERROR CEffectCreator::CreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID, CEffectCreator **retpCreator) // CreateFromXML // // Creates the creator from an XML element { ALERROR error; CEffectCreator *pCreator; // Basic info CString sEffectUNID = sUNID; if (sEffectUNID.IsBlank()) { DWORD dwUNID = pDesc->GetAttributeInteger(UNID_ATTRIB); if (dwUNID) sEffectUNID = strFromInt(dwUNID, FALSE); else sEffectUNID = STR_NO_UNID; } // Create the effect based on the child tag if (pDesc->GetContentElementCount() == 0) { *retpCreator = NULL; return NOERROR; } else if (pDesc->GetContentElementCount() == 1) { if (error = CreateSimpleFromXML(Ctx, pDesc->GetContentElement(0), sEffectUNID, &pCreator)) return error; } else { pCreator = new CEffectGroupCreator; if (pCreator == NULL) return ERR_MEMORY; pCreator->m_sUNID = sEffectUNID; // Type-specific creation if (error = pCreator->OnEffectCreateFromXML(Ctx, pDesc, sEffectUNID)) return error; } // Sound Effect (resolved later) pCreator->m_dwSoundUNID = pDesc->GetAttributeInteger(SOUND_ATTRIB); pCreator->m_iSound = -1; // Done *retpCreator = pCreator; return NOERROR; }
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; }
ALERROR IEffectPainter::ValidateClass (SLoadCtx &Ctx, const CString &sOriginalClass) // ValidateClass // // Reads the class string. If the class does not match the current painter, // we read the old data and return ERR_FAIL. { if (Ctx.dwVersion >= 40) { CString sClass; sClass.ReadFromStream(Ctx.pStream); // If the original class doesn't match the current one, then it means // that the design changed. In that case, we load the painter using the // old class. if (!strEquals(sClass, sOriginalClass)) { // If sClass is blank, then it means that the original did not have // an effect painter (but the current design does) if (!sClass.IsBlank()) { // Get the original creator CEffectCreator *pOriginalCreator; if (CEffectCreator::CreateFromTag(sClass, &pOriginalCreator) != NOERROR) { kernelDebugLogMessage("Unable to find original effect creator: %s", sClass.GetASCIIZPointer()); return ERR_FAIL; } // Load the original painter IEffectPainter *pOriginalPainter = pOriginalCreator->CreatePainter(); pOriginalPainter->ReadFromStream(Ctx); // Discard pOriginalPainter->Delete(); delete pOriginalCreator; } // Done return ERR_FAIL; } } return NOERROR; }
void CMissile::CreateHitEffect (const CVector &vPos, int iRotation) // CreateHitEffect // // Create hit effect { CEffectCreator *pEffect; if (pEffect = m_pDesc->GetHitEffect()) pEffect->CreateEffect(GetSystem(), (m_iHitDir == -1 ? NULL : m_pHit), vPos, CVector(), iRotation); }
CSpaceObject::DamageResults CParticleEffect::Damage (CSpaceObject *pCause, const CVector &vHitPos, int iDirection, const DamageDesc &Damage) // Damage // // Damage the particle field { // Create hit effect CEffectCreator *pEffect = g_pUniverse->FindEffectType(g_HitEffectUNID); if (pEffect) pEffect->CreateEffect(GetSystem(), this, vHitPos, GetVel()); return damagePassthrough; }
void CEffectVariantPainter::Paint (CG16bitImage &Dest, int x, int y, SViewportPaintCtx &Ctx) // Paint // // Paint the effect { // Find the appropriate effect int iIndex = m_pCreator->GetVariantCreatorIndex(Ctx.iVariant); if (m_Cache[iIndex] == NULL) { CEffectCreator *pCreator = m_pCreator->GetVariantCreator(iIndex); m_Cache[iIndex] = pCreator->CreatePainter(CCreatePainterCtx()); } // Paint m_Cache[iIndex]->Paint(Dest, x, y, Ctx); }
CEffectCreator *CEffectCreator::OnFindEffectCreator (const CString &sUNID) // OnFindEffectCreator // // Finds the effect creator by UNID { char *pPos = sUNID.GetASCIIZPointer(); // If we're done, then we mean ourselves if (*pPos == '\0') return this; if (*pPos++ != '/') return NULL; // Damage selector? if (*pPos == 'd') { pPos++; if (m_pDamage == NULL) return NULL; return m_pDamage->FindEffectCreator(CString(pPos)); } // Sub effect int iSubEffect = strParseInt(pPos, -1, 0, &pPos); if (iSubEffect == -1) return NULL; CEffectCreator *pSubEffect = GetSubEffect(iSubEffect); if (pSubEffect == NULL) return NULL; return pSubEffect->FindEffectCreatorInType(CString(pPos)); }
CSmokeTrailPainter::CSmokeTrailPainter (CSmokeTrailEffectCreator *pCreator) : m_pCreator(pCreator), m_iLastDirection(-1), m_iTick(0) // CSmokeTrailPainter constructor { int iMaxParticleCount = m_pCreator->GetParticleLifetimeMax() * m_pCreator->GetNewParticleMax(); m_Particles.Init(iMaxParticleCount); int iTotalLifetime = m_pCreator->GetLifetime(); m_iEmitLifetime = (iTotalLifetime == -1 ? -1 : m_pCreator->GetEmitLifetime() * iTotalLifetime / 100); CEffectCreator *pEffect = pCreator->GetParticleEffect(); if (pEffect) m_pParticlePainter = pEffect->CreatePainter(); else m_pParticlePainter = NULL; }
void CCompositeImageDesc::InitDamagePainters (void) // InitDamagePainters // // Initializes station damage bitmaps { if (g_pMediumDamage == NULL) { CEffectCreator *pEffect = g_pUniverse->FindEffectType(MEDIUM_STATION_DAMAGE_UNID); if (pEffect) g_pMediumDamage = pEffect->CreatePainter(); } if (g_pLargeDamage == NULL) { CEffectCreator *pEffect = g_pUniverse->FindEffectType(LARGE_STATION_DAMAGE_UNID); if (pEffect) g_pLargeDamage = pEffect->CreatePainter(); } }
CParticleJetEffectPainter::CParticleJetEffectPainter (CCreatePainterCtx &Ctx, CParticleJetEffectCreator *pCreator) : m_pCreator(pCreator), m_iXformRotation(0), m_rXformTime(1.0), m_iLifetime(0), m_iCurDirection(-1), m_iLastDirection(-1), m_bUseObjectCenter(Ctx.UseObjectCenter()), m_bUseObjectMotion(false), m_bTrackingObject(Ctx.IsTracking()) // CParticleJetEffectPainter constructor { // Initialize the single particle painter CEffectCreator *pEffect = m_pCreator->GetParticleEffect(); if (pEffect) m_pParticlePainter = pEffect->CreatePainter(Ctx); else m_pParticlePainter = NULL; }
CWeaponFireDesc *CWeaponFireDesc::FindWeaponFireDescFromFullUNID (const CString &sUNID) // FindWeaponFireDesc // // Finds the descriptor by name { char *pPos = sUNID.GetPointer(); // Get the UNID of the type DWORD dwUNID = (DWORD)strParseInt(pPos, 0, &pPos); if (dwUNID == 0) return NULL; // Get the type CDesignType *pType = g_pUniverse->FindDesignType(dwUNID); if (pType == NULL) return NULL; // If this is an item, then it must be a weapon if (pType->GetType() == designItemType) { CItemType *pItemType = CItemType::AsType(pType); ASSERT(pItemType); CDeviceClass *pDevice = pItemType->GetDeviceClass(); if (pDevice == NULL) return NULL; CWeaponClass *pClass = pDevice->AsWeaponClass(); if (pClass == NULL) return NULL; // Get the ordinal ASSERT(*pPos == '/'); pPos++; int iOrdinal = strParseInt(pPos, 0, &pPos); // Get the weapon fire desc of the ordinal CWeaponFireDesc *pDesc = pClass->GetVariant(iOrdinal); if (pDesc == NULL) return NULL; // Continue parsing return pDesc->FindWeaponFireDesc(CString(pPos)); } // If this is an effect, then get it from that else if (pType->GetType() == designEffectType) { CEffectCreator *pEffectType = CEffectCreator::AsType(pType); ASSERT(pEffectType); // Expect /d ASSERT(*pPos == '/'); pPos++; ASSERT(*pPos == 'd'); pPos++; CWeaponFireDesc *pDesc = pEffectType->GetDamageDesc(); if (pDesc == NULL) return NULL; // Continue parsing return pDesc->FindWeaponFireDesc(CString(pPos)); } // Otherwise, we don't know else return NULL; }
ALERROR CParticleDamage::Create (CSystem *pSystem, CWeaponFireDesc *pDesc, CItemEnhancementStack *pEnhancements, DestructionTypes iCause, const CDamageSource &Source, const CVector &vPos, const CVector &vVel, int iDirection, CSpaceObject *pTarget, CParticleDamage **retpObj) // Create // // Create the object { ALERROR error; // Make sure we have a valid CWeaponFireDesc (otherwise we won't be // able to save the object). ASSERT(!pDesc->m_sUNID.IsBlank()); // Create the area CParticleDamage *pParticles = new CParticleDamage; if (pParticles == NULL) return ERR_MEMORY; pParticles->Place(vPos, vVel); // Get notifications when other objects are destroyed pParticles->SetObjectDestructionHook(); // Set non-linear move, meaning that we are responsible for // setting the position and velocity in OnMove pParticles->SetNonLinearMove(); pParticles->m_pDesc = pDesc; pParticles->m_pTarget = pTarget; pParticles->m_pEnhancements = (pEnhancements ? pEnhancements->AddRef() : NULL); pParticles->m_iCause = iCause; pParticles->m_iEmitDirection = iDirection; pParticles->m_vEmitSourcePos = vPos; pParticles->m_vEmitSourceVel = (Source.GetObj() ? Source.GetObj()->GetVel() : CVector()); pParticles->m_iEmitTime = Max(1, pDesc->GetParticleEmitTime()); pParticles->m_iLifeLeft = pDesc->GetMaxLifetime() + pParticles->m_iEmitTime; pParticles->m_Source = Source; pParticles->m_iTick = 0; pParticles->m_iDamage = pDesc->m_Damage.RollDamage(); // Friendly fire if (!pDesc->CanHitFriends()) pParticles->SetNoFriendlyFire(); // Painter CEffectCreator *pEffect; if (pEffect = pDesc->GetEffect()) { CCreatePainterCtx Ctx; Ctx.SetWeaponFireDesc(pDesc); pParticles->m_pPainter = pEffect->CreatePainter(Ctx); } // Remember the sovereign of the source (in case the source is destroyed) if (Source.GetObj()) pParticles->m_pSovereign = Source.GetObj()->GetSovereign(); else pParticles->m_pSovereign = NULL; // Compute the maximum number of particles that we might have int iMaxCount = pParticles->m_iEmitTime * pDesc->GetMaxParticleCount(); pParticles->m_Particles.Init(iMaxCount, vPos); // Create the initial particles int iInitCount = pDesc->GetParticleCount(); pParticles->InitParticles(iInitCount, CVector(), vVel, iDirection); // Figure out the number of particles that will cause full damage if (pParticles->m_iEmitTime > 1) pParticles->m_iParticleCount = pParticles->m_iEmitTime * pDesc->GetAveParticleCount(); else pParticles->m_iParticleCount = iInitCount; pParticles->m_iParticleCount = Max(1, pParticles->m_iParticleCount); // Add to system if (error = pParticles->AddToSystem(pSystem)) { delete pParticles; return error; } // Done if (retpObj) *retpObj = pParticles; return NOERROR; }
IEffectPainter *CEffectCreator::CreatePainterFromStream (SLoadCtx &Ctx, bool bNullCreator) // CreatePainterFromStream // // Load a painter from a stream { CEffectCreator *pCreator; // For previous versions, we only stored UNID if we had a creator if (Ctx.dwVersion < 43 && bNullCreator) return NULL; // At version 15 we started saving versions as string UNIDs. We need to do this // because sometimes the effect creator is inside a weapon fire desc // structure (also identified by string UNIDs). if (Ctx.dwVersion >= 15) { CString sUNID; sUNID.ReadFromStream(Ctx.pStream); pCreator = (sUNID.IsBlank() ? NULL : CEffectCreator::FindEffectCreator(sUNID)); // Load the creator class that saved the painter if (IEffectPainter::ValidateClass(Ctx, (pCreator ? pCreator->GetTag() : NULL_STR)) != NOERROR) return NULL; // Error if (pCreator == NULL) { if (!sUNID.IsBlank()) kernelDebugLogMessage("Invalid painter creator: %s", sUNID.GetASCIIZPointer()); return NULL; } } // Old style uses DWORD UNIDs else { // The first DWORD is the UNID of the creator DWORD dwUNID; Ctx.pStream->Read((char *)&dwUNID, sizeof(DWORD)); if (dwUNID == 0) return NULL; pCreator = g_pUniverse->FindEffectType(dwUNID); // Error if (pCreator == NULL) { kernelDebugLogMessage("Invalid painter creator: %x", dwUNID); return NULL; } } // Let the creator create the object IEffectPainter *pPainter = pCreator->CreatePainter(); // Load it pPainter->ReadFromStream(Ctx); // Done return pPainter; }
ALERROR CMissile::Create (CSystem *pSystem, CWeaponFireDesc *pDesc, int iBonus, DestructionTypes iCause, const CDamageSource &Source, const CVector &vPos, const CVector &vVel, int iRotation, CSpaceObject *pTarget, CMissile **retpMissile) // Create // // Creates a missile { ALERROR error; CMissile *pMissile; pMissile = new CMissile; if (pMissile == NULL) return ERR_MEMORY; pMissile->Place(vPos, vVel); // We can't save missiles without an UNID ASSERT(!pDesc->m_sUNID.IsBlank()); // Don't bother telling others when we are destroyed (Note that // if we do this then we also need to set the CannotBeHit flag; // otherwise we will crash when a beam hits us. This is because // m_pHit is setup in Move and the object can go away between then // and our Update event.) if (pDesc->GetInteraction() == 0) { pMissile->DisableObjectDestructionNotify(); pMissile->SetCannotBeHit(); } // Get notifications when other objects are destroyed pMissile->SetObjectDestructionHook(); pMissile->m_pDesc = pDesc; pMissile->m_iBonus = iBonus; pMissile->m_iCause = iCause; pMissile->m_iHitPoints = pDesc->GetHitPoints(); pMissile->m_iLifeLeft = pDesc->GetLifetime(); pMissile->m_iTick = 0; pMissile->m_Source = Source; pMissile->m_pHit = NULL; pMissile->m_iRotation = iRotation; pMissile->m_pTarget = pTarget; pMissile->m_fDestroyed = false; pMissile->m_fReflection = false; pMissile->m_fDetonate = false; pMissile->m_fPassthrough = false; pMissile->m_dwSpareFlags = 0; // Friendly fire if (!pDesc->CanHitFriends()) pMissile->SetNoFriendlyFire(); // Remember the sovereign of the source (in case the source is destroyed) pMissile->m_pSovereign = pMissile->m_Source.GetSovereign(); // Create a painter instance CEffectCreator *pEffect; if (pEffect = pDesc->GetEffect()) { pMissile->m_pPainter = pEffect->CreatePainter(); pMissile->SetBounds(pMissile->m_pPainter); } // Create exhaust trail, if necessary if (pDesc->m_iExhaustRate > 0) { int iCount = (pDesc->m_iExhaustLifetime / pDesc->m_iExhaustRate) + 1; pMissile->m_pExhaust = new TQueue<SExhaustParticle>(iCount); } else pMissile->m_pExhaust = NULL; // Create vapor trail, if necessary if (pDesc->GetVaporTrailWidth()) pMissile->SetBounds(2048.0 * g_KlicksPerPixel); // Add to system if (error = pMissile->AddToSystem(pSystem)) { delete pMissile; return error; } // Done if (retpMissile) *retpMissile = pMissile; return NOERROR; }