//======== (C) Copyright 2002 Charles G. Cleveland All rights reserved. ========= // // The copyright to the contents herein is the property of Charles G. Cleveland. // The contents may be used and/or copied only with the written permission of // Charles G. Cleveland, or in accordance with the terms and conditions stipulated in // the agreement/contract under which the contents have been supplied. // // Purpose: // // $Workfile: AvHParticleSystemEntity.cpp $ // $Date: 2002/11/22 21:28:16 $ // //------------------------------------------------------------------------------- // $Log: AvHParticleSystemEntity.cpp,v $ // Revision 1.13 2002/11/22 21:28:16 Flayra // - mp_consistency changes // // Revision 1.12 2002/05/23 02:33:20 Flayra // - Post-crash checkin. Restored @Backup from around 4/16. Contains changes for last four weeks of development. // //=============================================================================== #include "../util/nowarnings.h" #include "AvHParticleSystemEntity.h" #include "AvHParticleTemplateServer.h" #include "AvHParticleSystemManager.h" #include "AvHConstants.h" #include "AvHMarineEquipmentConstants.h" #include "AvHSpecials.h" #include "AvHParticleTemplate.h" #include "AvHParticleConstants.h" extern AvHParticleTemplateListServer gParticleTemplateList; uint32 AvHParticleSystemEntity::sCurrentHandle = 1; const float kDefaultParticleSystemThinkRate = 0.05f; LINK_ENTITY_TO_CLASS( keParticles, AvHParticleSystemEntity ); LINK_ENTITY_TO_CLASS( keParticlesCustom, AvHCustomParticleSystemEntity ); AvHParticleSystemEntity::AvHParticleSystemEntity() { this->mTemplateIndex = -1; this->mIsOn = false; //this->mClientIsOn = false; this->mUseState = false; this->mHandle = 0; this->mCreatedTemplate = false; this->mTimeParticlesCreated = 0; this->mCustomData = 0; } void AvHParticleSystemEntity::SetTemplateIndex(int inTemplateIndex) { this->mTemplateIndex = inTemplateIndex; } uint16 AvHParticleSystemEntity::GetCustomData() const { return this->mCustomData; } void AvHParticleSystemEntity::SetCustomData(uint16 inCustomData) { this->mCustomData = inCustomData; this->pev->weaponmodel = this->mCustomData; } // Fetch template if we have a valid template index, create a new custom template if we don't AvHParticleTemplate* AvHParticleSystemEntity::GetCustomTemplate() { AvHParticleTemplate* theTemplate = NULL; if(!this->mCreatedTemplate) { if(this->mTemplateIndex == -1) { // Create a new template, none was specified in the name field ASSERT(!this->mCreatedTemplate); this->mTemplateIndex = gParticleTemplateList.CreateTemplateFromIndex(); } //else //{ // // Create a template from our existing base template // this->mTemplateIndex = gParticleTemplateList.CreateTemplateFromIndex(this->mTemplateIndex); //} // Set the name of our new custom particle system theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mTemplateIndex); ASSERT(theTemplate); string theName = STRING(this->pev->targetname); theTemplate->SetName(theName); this->mCreatedTemplate = true; } ASSERT(this->mTemplateIndex != -1); if(!theTemplate) theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mTemplateIndex); return theTemplate; } void AvHParticleSystemEntity::KeyValue( KeyValueData* inPkvd ) { if(FStrEq(inPkvd->szKeyName, kpscSystemName)) { uint32 theIndex; if(gParticleTemplateList.GetTemplateIndexWithName(inPkvd->szValue, theIndex)) { this->mTemplateIndex = theIndex; inPkvd->fHandled = TRUE; } } else { // Call down to base class CBaseEntity::KeyValue(inPkvd); } } void AvHParticleSystemEntity::ParticleThink() { this->UpdateClientData(); // Look up particle system AvHParticleSystem* theParticleSystem = AvHParticleSystemManager::Instance()->GetParticleSystem(this->mHandle); if(theParticleSystem) { // Call UpdatePhysics() theParticleSystem->UpdatePhysics(this->pev); // The actual entity's position is the regular position of the entity, unless it's using a generaton entity, then use it instead //theParticleSystem->GetEffectiveOrigin(this->pev->origin); ////UTIL_SetOrigin(this->pev, theGenerationEntityOrigin); } this->pev->nextthink = gpGlobals->time + kDefaultParticleSystemThinkRate; } void AvHParticleSystemEntity::ParticleTouch( CBaseEntity* /*pOther*/ ) { //EMIT_SOUND(ENT(pev), CHAN_VOICE, "buttons/spark1.wav", 1.0f, ATTN_NORM); } void AvHParticleSystemEntity::ParticleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) { switch(useType) { case USE_OFF: this->mUseState = false; break; case USE_ON: this->mUseState = true; break; case USE_SET: // Handle this? break; case USE_TOGGLE: this->mUseState = !this->mUseState; break; } } void AvHParticleSystemEntity::Precache(void) { CBaseEntity::Precache(); PRECACHE_UNMODIFIED_MODEL(kNullModel); } void AvHParticleSystemEntity::SetUseState(USE_TYPE inUseType) { this->ParticleUse(NULL, NULL, inUseType, 0.0f); } void AvHParticleSystemEntity::Spawn( void ) { // Just in case our class derives off of something else in the future CBaseEntity::Spawn(); this->Precache(); this->pev->classname = MAKE_STRING(kesParticlesCustom); this->pev->solid = SOLID_NOT; this->pev->movetype = MOVETYPE_NONE; //this->pev->movetype = MOVETYPE_PUSH; //this->pev->effects = EF_BRIGHTLIGHT; this->SetTouch(&AvHParticleSystemEntity::ParticleTouch); this->SetUse(&AvHParticleSystemEntity::ParticleUse); SET_MODEL(ENT(this->pev), kNullModel); UTIL_SetOrigin(this->pev, this->pev->origin); // Set the other flags AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mTemplateIndex); //ASSERT(theTemplate); if(theTemplate) { //theTemplate->SetFlags(theSpawnFlags); string theTargetName = STRING(this->pev->targetname); if(!FStrEq(theTargetName.c_str(), "")) { theTemplate->SetName(theTargetName); } this->SetThink(&AvHParticleSystemEntity::ParticleThink); pev->nextthink = gpGlobals->time + kDefaultParticleSystemThinkRate; // The spawn flags field is the only one that doesn't get set through KeyValue(), so // we set it in the template this way int theSpawnFlags = this->pev->spawnflags; if(!theSpawnFlags) { theSpawnFlags = theTemplate->GetFlags(); } // Did they check the "start on" flag? if(theSpawnFlags & 1) { this->mUseState = true; } theTemplate->SetFlags(theSpawnFlags); } else { ALERT(at_logged, "Couldn't find particle system template: %d\n", this->mTemplateIndex); UTIL_Remove(this); } } void AvHParticleSystemEntity::UpdateClientData() { // Turn the system on or off if(this->mIsOn != this->mUseState) { this->mIsOn = this->mUseState; // set up entity so it's propagated correctly if(this->mIsOn) { // TODO: Reset sCurrentHandle to 1 every time a new game starts? this->mHandle = sCurrentHandle++; // Create server side system for collision only int theEntIndex = this->entindex(); AvHParticleSystemManager::Instance()->CreateParticleSystemIfNotCreated(theEntIndex, this->mTemplateIndex, this->mHandle); // Replicate action to everyone this->pev->iuser3 = AVH_USER3_PARTICLE_ON; const AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mTemplateIndex); ASSERT(theTemplate); int theGenEntityIndex = theTemplate->GetGenerationEntityIndex(); if(theGenEntityIndex == -1) { theGenEntityIndex = 0; } //this->pev->fuser1 = /*this->mTemplateIndex;*/ (theGenEntityIndex << 16) | this->mTemplateIndex; ASSERT(this->mTemplateIndex < 256); this->pev->fuser1 = (theGenEntityIndex << 16) | ((this->mTemplateIndex & 0xFF) << 8); this->pev->fuser2 = this->mHandle; // Store our custom data this->SetCustomData(this->mCustomData); this->mTimeParticlesCreated = gpGlobals->time; } else { // Destroy server side particle system //AvHParticleSystemManager::Instance()->DestroyParticleSystemIfNotDestroyed(this->entindex(), this->mHandle); AvHParticleSystemManager::Instance()->MarkParticleSystemForDeletion(this->entindex(), this->mHandle); // Replicate action to everyone this->pev->iuser3 = AVH_USER3_PARTICLE_OFF; this->pev->fuser1 = this->mHandle; } } if(this->mIsOn) { AvHParticleTemplate* theTemplate = gParticleTemplateList.GetTemplateAtIndex(this->mTemplateIndex); ASSERT(theTemplate); // If particle system was set to expire, make sure to realize when it turns off int theParticleSystemLifetime = theTemplate->GetParticleSystemLifetime(); if(theParticleSystemLifetime != -1) { if(gpGlobals->time - this->mTimeParticlesCreated >= theParticleSystemLifetime) { //this->mIsOn = false; this->mUseState = false; } } } } // Create a custom particle system. Create a new template instead of reading // one off disk. void AvHCustomParticleSystemEntity::KeyValue( KeyValueData* inPkvd ) { // Read tag with ps name and create a new template using it if(FStrEq(inPkvd->szKeyName, kpscSystemName)) { // Custom particle systems shouldn't be specifying a system in the .ps to use ASSERT(false); } else if(FStrEq(inPkvd->szKeyName, kpscGenSource) || FStrEq(inPkvd->szKeyName, "particleGenerationSource")) { char* theEntityName = inPkvd->szValue; this->GetCustomTemplate()->SetGenerationEntityName(theEntityName); inPkvd->fHandled = TRUE; } else if(FStrEq(inPkvd->szKeyName, kpscGenShape) || FStrEq(inPkvd->szKeyName, "particleGenerationShape")) { int theGenerationShape = 0; if(sscanf(inPkvd->szValue, "%d", &theGenerationShape)) { ShapeType theShape = PS_Point; switch(theGenerationShape) { case 4: theShape = PS_Box; break; case 5: theShape = PS_Sphere; break; case 8: theShape = PS_Blob; break; } this->GetCustomTemplate()->SetGenerationShape(theShape); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscSprite) || FStrEq(inPkvd->szKeyName, "particleSprite")) { // relative path to sprite string theSpriteName(inPkvd->szValue); this->GetCustomTemplate()->SetSprite(theSpriteName); inPkvd->fHandled = TRUE; } else if(FStrEq(inPkvd->szKeyName, kpscSpriteNumFrames) || FStrEq(inPkvd->szKeyName, "particleSpriteNumFrames")) { // int number of frames int theNumFrames = 0; if(sscanf(inPkvd->szValue, "%d", &theNumFrames) == 1) { this->GetCustomTemplate()->SetNumSpriteFrames(theNumFrames); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscGenShapeParams) || FStrEq(inPkvd->szKeyName, "particleGenerationShapeParams")) { float theParameter = 0; if(sscanf(inPkvd->szValue, "%f", &theParameter) == 1) { this->GetCustomTemplate()->SetGenerationEntityParameter(theParameter); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscNumParticles) || FStrEq(inPkvd->szKeyName, "particleNumParticles")) { // max particles, or density int theNumParticles = 0; if(sscanf(inPkvd->szValue, "%d", &theNumParticles) == 1) { this->GetCustomTemplate()->SetMaxParticles(theNumParticles); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscGenRate) || FStrEq(inPkvd->szKeyName, "particleGenerationRate")) { // num particles per second int theNumParticles = 0; if(sscanf(inPkvd->szValue, "%d", &theNumParticles) == 1) { this->GetCustomTemplate()->SetGenerationRate(theNumParticles); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscSize) || FStrEq(inPkvd->szKeyName, "particleSize")) { // float particle size float theParticleSize = 0; if(sscanf(inPkvd->szValue, "%f", &theParticleSize) == 1) { this->GetCustomTemplate()->SetParticleSize(theParticleSize); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscSystemLifetime) || FStrEq(inPkvd->szKeyName, "particleSystemLifetime")) { // string system lifetime float theLifetime = -1; if(sscanf(inPkvd->szValue, "%f", &theLifetime) == 1) { this->GetCustomTemplate()->SetParticleSystemLifetime(theLifetime); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscAnimationSpeed) || FStrEq(inPkvd->szKeyName, "particleAnimationSpeed")) { // Sprite animation speed float theAnimSpeed = 1.0f; if(sscanf(inPkvd->szValue, "%f", &theAnimSpeed) == 1) { this->GetCustomTemplate()->SetAnimationSpeed(theAnimSpeed); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscParticleLifetime) || FStrEq(inPkvd->szKeyName, "particleLifetime")) { // string particle lifetime float theLifetime = -1; if(sscanf(inPkvd->szValue, "%f", &theLifetime) == 1) { this->GetCustomTemplate()->SetParticleLifetime(theLifetime); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscVelocityShape) || FStrEq(inPkvd->szKeyName, "particleStartingVelocityShape")) { int theVelocityShape = 0; if(sscanf(inPkvd->szValue, "%d", &theVelocityShape)) { ShapeType theShape = PS_Point; switch(theVelocityShape) { case 1: theShape = PS_Point; break; case 2: theShape = PS_Box; break; case 3: theShape = PS_Sphere; break; case 4: theShape = PS_Blob; break; } this->GetCustomTemplate()->SetStartingVelocityShape(theShape); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscVelocityParams) || FStrEq(inPkvd->szKeyName, "particleStartingVelParams")) { // string, 8 comma-delimited parms ParticleParams theVelParms; if(sscanf(inPkvd->szValue, "%d,%d,%d,%d,%d,%d,%d,%d", theVelParms + 0, theVelParms + 1, theVelParms + 2, theVelParms + 3, theVelParms + 4, theVelParms + 5, theVelParms + 6, theVelParms + 7) == 8) { this->GetCustomTemplate()->SetStartingVelocityParams(theVelParms); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscScale) || FStrEq(inPkvd->szKeyName, "particleScaling")) { // float/string particle scaling float theScaling = 1.0f; if(sscanf(inPkvd->szValue, "%f", &theScaling) == 1) { this->GetCustomTemplate()->SetParticleScaling(theScaling); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscRendermode) || FStrEq(inPkvd->szKeyName, "particleRenderMode")) { // 0-5 render mode int theRenderMode = 0; if(sscanf(inPkvd->szValue, "%d", &theRenderMode) == 1) { this->GetCustomTemplate()->SetRenderMode(theRenderMode); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscMaxAlpha) || FStrEq(inPkvd->szKeyName, "particleMaxAlpha")) { float theMaxAlpha = 1.0f; if(sscanf(inPkvd->szValue, "%f", &theMaxAlpha) == 1) { this->GetCustomTemplate()->SetMaxAlpha(theMaxAlpha); inPkvd->fHandled = TRUE; } } else if(FStrEq(inPkvd->szKeyName, kpscSystemToGen) || FStrEq(inPkvd->szKeyName, "particleSystemToGenerate")) { string theSystemToGenerate = inPkvd->szValue; this->GetCustomTemplate()->SetParticleSystemToGenerate(theSystemToGenerate); inPkvd->fHandled = TRUE; } else { // Call down to base class AvHParticleSystemEntity::KeyValue(inPkvd); } }