openclonk/src/landscape/C4Particles.cpp

930 lines
25 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2002, 2004-2005 Sven Eberhardt
* Copyright (c) 2005, 2009-2010 Tobias Zwick
2013-01-09 23:23:06 +00:00
* Copyright (c) 2005-2006, 2008, 2010-2011 Günther Brammer
* Copyright (c) 2008 Peter Wortmann
* Copyright (c) 2010 Benjamin Herr
2009-05-08 13:28:41 +00:00
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
// newgfx particle system for smoke, sparks, ...
#include <C4Include.h>
#include <C4Particles.h>
#include <C4Config.h>
2009-05-08 13:28:41 +00:00
#include <C4Physics.h>
#include <C4Object.h>
#include <C4Random.h>
#include <C4Game.h>
#include <C4Components.h>
#include <C4Log.h>
2009-06-11 19:59:35 +00:00
#include <C4Weather.h>
#include <C4GameObjects.h>
2009-05-08 13:28:41 +00:00
void C4ParticleDefCore::CompileFunc(StdCompiler * pComp)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pComp->Value(mkNamingAdapt(toC4CStrBuf(Name), "Name", ""));
pComp->Value(mkNamingAdapt(MaxCount, "MaxCount", C4Px_MaxParticle));
pComp->Value(mkNamingAdapt(MinLifetime, "MinLifetime", 0));
pComp->Value(mkNamingAdapt(MaxLifetime, "MaxLifetime", 0));
pComp->Value(mkNamingAdapt(toC4CStrBuf(InitFn), "InitFn", ""));
pComp->Value(mkNamingAdapt(toC4CStrBuf(ExecFn), "ExecFn", ""));
pComp->Value(mkNamingAdapt(toC4CStrBuf(CollisionFn),"CollisionFn",""));
pComp->Value(mkNamingAdapt(toC4CStrBuf(DrawFn), "DrawFn", ""));
pComp->Value(mkNamingAdapt(GfxFace, "Face"));
pComp->Value(mkNamingAdapt(YOff, "YOff", 0));
pComp->Value(mkNamingAdapt(Delay, "Delay", 0));
pComp->Value(mkNamingAdapt(Repeats, "Repeats", 0));
pComp->Value(mkNamingAdapt(Reverse, "Reverse", 0));
pComp->Value(mkNamingAdapt(FadeOutLen, "FadeOutLen", 0));
pComp->Value(mkNamingAdapt(FadeOutDelay, "FadeOutDelay",0));
pComp->Value(mkNamingAdapt(RByV, "RByV", 0));
pComp->Value(mkNamingAdapt(GravityAcc, "GravityAcc", 0));
pComp->Value(mkNamingAdapt(WindDrift, "WindDrift", 0));
pComp->Value(mkNamingAdapt(VertexCount, "VertexCount", 0));
pComp->Value(mkNamingAdapt(VertexY, "VertexY", 0));
pComp->Value(mkNamingAdapt(Additive, "Additive", 0));
pComp->Value(mkNamingAdapt(AlphaFade, "AlphaFade", 0));
2009-07-15 13:41:03 +00:00
pComp->Value(mkNamingAdapt(FadeDelay, "FadeDelay", 0));
2009-05-08 13:28:41 +00:00
pComp->Value(mkNamingAdapt(mkArrayAdaptDM(Parallaxity,100),"Parallaxity"));
pComp->Value(mkNamingAdapt(Attach, "Attach", 0));
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleDefCore::C4ParticleDefCore():
2010-03-28 18:58:01 +00:00
MaxCount(C4Px_MaxParticle),
MinLifetime(0),MaxLifetime(0),
YOff(0),
Delay(0),Repeats(0),Reverse(0),
FadeOutLen(0),FadeOutDelay(0),
RByV(0),
Placement(0),
GravityAcc(0),
VertexCount(0),VertexY(0),
Additive(0),
Attach(0),
AlphaFade(0),
FadeDelay(0)
{
2009-05-08 13:28:41 +00:00
GfxFace.Default();
Parallaxity[0] = Parallaxity[1] = 100;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4ParticleDefCore::Compile(char *particle_source, const char *name)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "Particle"),
StdStrBuf(particle_source), name);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleDef::C4ParticleDef():
2010-03-28 18:58:01 +00:00
C4ParticleDefCore(),
InitProc(&fxStdInit),
ExecProc(&fxStdExec),
CollisionProc(NULL),
DrawProc(&fxStdDraw),
Count(0)
{
2009-05-08 13:28:41 +00:00
// zero fields
Gfx.Default();
// link into list
if (!ParticleSystem.pDef0)
2010-03-28 18:58:01 +00:00
{
pPrev = NULL;
ParticleSystem.pDef0 = this;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
{
pPrev = ParticleSystem.pDefL;
pPrev->pNext = this;
}
ParticleSystem.pDefL = this;
pNext = NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleDef::~C4ParticleDef()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// clear
Clear();
// unlink from list
if (pPrev) pPrev->pNext = pNext; else ParticleSystem.pDef0 = pNext;
if (pNext) pNext->pPrev = pPrev; else ParticleSystem.pDefL = pPrev;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleDef::Clear()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Name.Clear();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4ParticleDef::Load(C4Group &group)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// store file
Filename.Copy(group.GetFullName());
2009-05-08 13:28:41 +00:00
// load
char *particle_source;
if (group.LoadEntry(C4CFN_ParticleCore,&particle_source,NULL,1))
2010-03-28 18:58:01 +00:00
{
if (!Compile(particle_source, Filename.getData()))
2010-03-28 18:58:01 +00:00
{
DebugLogF("invalid particle def at '%s'", group.GetFullName().getData());
delete [] particle_source; return false;
2010-03-28 18:58:01 +00:00
}
delete [] particle_source;
2009-05-08 13:28:41 +00:00
// load graphics
2013-01-08 22:50:11 +00:00
if (!Gfx.Load(group, C4CFN_DefGraphics))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DebugLogF("particle %s has no valid graphics defined", Name.getData());
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// set facet, if assigned - otherwise, assume full surface
if (GfxFace.Wdt) Gfx.Set(Gfx.Surface, GfxFace.x, GfxFace.y, GfxFace.Wdt, GfxFace.Hgt);
2009-05-08 13:28:41 +00:00
// set phase num
int32_t Q; Gfx.GetPhaseNum(PhasesX, Q);
Length = PhasesX * Q;
2009-05-08 13:28:41 +00:00
if (!Length)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DebugLogF("invalid facet for particle '%s'", Name.getData());
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// case fadeout from length
if (FadeOutLen)
2010-03-28 18:58:01 +00:00
{
Length = Max<int32_t>(Length - FadeOutLen, 1);
2009-05-08 13:28:41 +00:00
if (!FadeOutDelay) FadeOutDelay=1;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// if phase num is 1, no reverse is allowed
if (Length == 1) Reverse = 0;
2009-05-08 13:28:41 +00:00
// calc aspect
2010-03-28 00:15:45 +00:00
Aspect=(float) Gfx.Hgt/Gfx.Wdt;
2009-05-08 13:28:41 +00:00
// get proc pointers
if (!(InitProc = ParticleSystem.GetProc(InitFn.getData())))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DebugLogF("init proc for particle '%s' not found: '%s'", Name.getData(), InitFn.getData());
InitProc = &fxStdInit;
2010-03-28 18:58:01 +00:00
}
if (!(ExecProc = ParticleSystem.GetProc(ExecFn.getData())))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DebugLogF("exec proc for particle '%s' not found: '%s'", Name.getData(), ExecFn.getData());
ExecProc = &fxStdExec;
2010-03-28 18:58:01 +00:00
}
if (CollisionFn && CollisionFn[0]) if (!(CollisionProc = ParticleSystem.GetProc(CollisionFn.getData())))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
DebugLogF("collision proc for particle '%s' not found: '%s'", Name.getData(), CollisionFn.getData());
return false;
2009-05-08 13:28:41 +00:00
}
if (!(DrawProc = ParticleSystem.GetDrawProc(DrawFn.getData())))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DebugLogF("draw proc for particle '%s' not found: '%s'", Name.getData(), DrawFn.getData());
DrawProc = &fxStdDraw;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// particle overloading
C4ParticleDef *def_overload;
if ((def_overload = ParticleSystem.GetDef(Name.getData(), this)))
2010-03-28 18:58:01 +00:00
{
if (Config.Graphics.VerboseObjectLoading >= 1)
{ char ostr[250]; sprintf(ostr,LoadResStr("IDS_PRC_DEFOVERLOAD"),def_overload->Name.getData(),"<particle>"); Log(ostr); }
delete def_overload;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// success
return true;
}
2010-03-28 18:58:01 +00:00
return false;
}
2009-05-08 13:28:41 +00:00
bool C4ParticleDef::Reload()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// no file?
if (!Filename[0]) return false;
// open group
C4Group group;
if (!group.Open(Filename.getData())) return false;
2009-05-08 13:28:41 +00:00
// reset class
Clear();
// load
return Load(group);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Particle::MoveList(C4ParticleList &from, C4ParticleList &to)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// remove from current list
if (pPrev)
pPrev->pNext = pNext;
2009-05-08 13:28:41 +00:00
else
// is it the first item in the list? then set this to the next one
if (from.pFirst == this) from.pFirst = pNext;
if (pNext) pNext->pPrev = pPrev;
2009-05-08 13:28:41 +00:00
// add to the other list - insert before first
if ((pNext = to.pFirst)) pNext->pPrev = this;
to.pFirst = this; pPrev = NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleChunk::C4ParticleChunk()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// zero linked list
pNext=NULL;
// zero buffer
Clear();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleChunk::~C4ParticleChunk()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// list stuff done by C4ParticleSystem
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleChunk::Clear()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// note that this method is called in ctor with uninitialized data!
// simply clear mem - this won't adjust any counts!
memset(Data, 0, sizeof(Data));
2009-05-08 13:28:41 +00:00
// init list
C4Particle *particle=Data;
for (int32_t i=0; i < C4Px_BufSize; ++i)
2010-03-28 18:58:01 +00:00
{
particle->pPrev = particle-1;
particle->pNext = particle+1;
++particle;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
Data[0].pPrev=Data[C4Px_BufSize-1].pNext=NULL;
// all free
NumFree = C4Px_BufSize;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleList::Exec(C4Object *object)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// execute all particles
C4Particle *next_particle = pFirst, *particle;
while ((particle = next_particle))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get next now, because destruction could corrupt the list
next_particle = particle->pNext;
2009-05-08 13:28:41 +00:00
// execute it
if (!particle->pDef->ExecProc(particle,object))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// sorry, life is over for you :P
--particle->pDef->Count;
particle->MoveList(*this, ::Particles.FreeParticles);
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
// done
}
2009-05-08 13:28:41 +00:00
void C4ParticleList::Draw(C4TargetFacet &cgo, C4Object *object)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// draw all particles
for (C4Particle *particle = pFirst; particle; particle = particle->pNext)
particle->pDef->DrawProc(particle, cgo, object);
2009-05-08 13:28:41 +00:00
// done
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleList::Clear()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// remove all particles
C4Particle *next_particle = pFirst, *particle;
while ((particle = next_particle))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get next now, because destruction could corrupt the list
next_particle = particle->pNext;
2009-05-08 13:28:41 +00:00
// sorry, life is over for you :P
--particle->pDef->Count;
particle->MoveList(*this, ::Particles.FreeParticles);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4ParticleList::Remove(C4ParticleDef *of_def)
2010-03-28 18:58:01 +00:00
{
int32_t num_removed = 0;
2009-05-08 13:28:41 +00:00
// check all particles for def
C4Particle *next_particle = pFirst, *particle;
while ((particle = next_particle))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get next now, because destruction could corrupt the list
next_particle = particle->pNext;
2009-05-08 13:28:41 +00:00
// execute it
if (!of_def || particle->pDef == of_def)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// sorry, life is over for you :P
--particle->pDef->Count;
particle->MoveList(*this, ::Particles.FreeParticles);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done
return num_removed;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleSystem::C4ParticleSystem()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// zero fields
pDef0=pDefL=NULL;
pSmoke=NULL;
pBlast=NULL;
pFSpark=NULL;
pFire1=NULL;
pFire2=NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleSystem::~C4ParticleSystem()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// clean up
Clear();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleChunk *C4ParticleSystem::AddChunk()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// add another chunk
C4ParticleChunk *new_chunk = new C4ParticleChunk();
new_chunk->pNext = Chunk.pNext;
Chunk.pNext = new_chunk;
2009-05-08 13:28:41 +00:00
// register into free-particle-list
if ((new_chunk->Data[C4Px_BufSize-1].pNext = FreeParticles.pFirst))
FreeParticles.pFirst->pPrev = &new_chunk->Data[C4Px_BufSize-1];
FreeParticles.pFirst = &new_chunk->Data[0];
2009-05-08 13:28:41 +00:00
// return it
return new_chunk;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleSystem::PruneChunks()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check all chunks, but not the first
// that cannot be removed anyway
C4ParticleChunk *chunk = Chunk.pNext, *next_chunk, **previous_chunk_p;
previous_chunk_p = &Chunk.pNext;
2009-05-08 13:28:41 +00:00
do
2010-03-28 18:58:01 +00:00
{
next_chunk = chunk->pNext;
2009-05-08 13:28:41 +00:00
// chunk empty?
if (chunk->NumFree == C4Px_BufSize)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// move out all particles
C4ParticleList tmp;
for (int32_t i = 0; i < C4Px_BufSize; ++i)
chunk->Data[i].MoveList(FreeParticles, tmp);
2009-05-08 13:28:41 +00:00
// and remove the chunk
*previous_chunk_p = next_chunk;
delete chunk;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// keep this chunk
previous_chunk_p = &chunk->pNext;
2009-05-08 13:28:41 +00:00
}
}
while ((chunk = next_chunk));
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleSystem::ClearParticles()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// clear particle lists
C4ObjectLink *link;
for (link = ::Objects.First; link; link = link->Next)
link->Obj->FrontParticles.pFirst = link->Obj->BackParticles.pFirst = NULL;
for (link = ::Objects.InactiveObjects.First; link; link = link->Next)
link->Obj->FrontParticles.pFirst = link->Obj->BackParticles.pFirst = NULL;
2009-05-08 13:28:41 +00:00
GlobalParticles.pFirst = NULL;
// reset chunks
C4ParticleChunk *next_chunk = Chunk.pNext, *chunk;
while ((chunk = next_chunk))
2010-03-28 18:58:01 +00:00
{
next_chunk = chunk->pNext;
delete chunk;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
Chunk.pNext = NULL;
Chunk.Clear();
FreeParticles.pFirst = Chunk.Data;
// adjust counts
for (C4ParticleDef *def=pDef0; def; def=def->pNext)
def->Count=0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleSystem::Clear()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// clear particles first
ClearParticles();
// clear defs
while (pDef0) delete pDef0;
// clear system particles
pSmoke = pBlast = pFSpark = pFire1 = pFire2 = NULL;
2009-05-08 13:28:41 +00:00
// done
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Particle *C4ParticleSystem::Create(C4ParticleDef *of_def,
2010-03-28 18:58:01 +00:00
float x, float y,
float xdir, float ydir,
float a, int32_t b, C4ParticleList *pxList,
C4Object *object)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// safety
if (!of_def) return NULL;
2009-05-08 13:28:41 +00:00
// default to global list
if (!pxList) pxList = &GlobalParticles;
2009-05-08 13:28:41 +00:00
// check count
int32_t max_count = of_def->MaxCount * (Config.Graphics.SmokeLevel + 20) / 150;
int32_t room = max_count - of_def->Count;
if (room <= 0) return NULL;
2009-05-08 13:28:41 +00:00
// reduce creation if limit is nearly reached
if (room < (max_count >> 1))
if (SafeRandom(room) < SafeRandom(max_count)) return NULL;
2009-05-08 13:28:41 +00:00
// get free particle
if (!FreeParticles.pFirst) AddChunk();
C4Particle *particle = FreeParticles.pFirst;
if (!particle) return NULL;
2009-05-08 13:28:41 +00:00
// set values
particle->x = x; particle->y = y;
particle->xdir = xdir; particle->ydir = ydir;
particle->a = a; particle->b = b;
particle->pDef = of_def;
if (particle->pDef->Attach && object != NULL)
2010-03-28 18:58:01 +00:00
{
particle->x -= fixtof(object->GetFixedX());
particle->y -= fixtof(object->GetFixedY());
}
2009-05-08 13:28:41 +00:00
// call initialization
if (!of_def->InitProc(particle,object))
2009-05-08 13:28:41 +00:00
// failed :(
return NULL;
// count particle
++of_def->Count;
2009-05-08 13:28:41 +00:00
// more to desired list
particle->MoveList(::Particles.FreeParticles, *pxList);
2009-05-08 13:28:41 +00:00
// return newly created particle
return particle;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4ParticleSystem::Cast(C4ParticleDef *of_def, int32_t amount,
2010-03-28 18:58:01 +00:00
float x, float y, int32_t level,
float a0, DWORD b0, float a1, DWORD b1, C4ParticleList *pxList, C4Object *object)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// safety
if (!of_def) return false;
2009-05-08 13:28:41 +00:00
// get range for a and b
int32_t iA0=(int32_t)(a0*100),iA1=(int32_t)(a1*100);
if (iA1<iA0) Swap(iA0, iA1);
int32_t iAd=iA1-iA0+1;
if (b1<b0) { DWORD dwX=b0; b0=b1; b1=dwX; }
DWORD db=b1-b0;
BYTE db1=BYTE(db>>24), db2=BYTE(db>>16), db3=BYTE(db>>8), db4=BYTE(db);
// create them
for (int32_t i=amount; i > 0; --i)
Create(of_def, x, y,
2010-03-28 18:58:01 +00:00
(float)(SafeRandom(level+1)-level/2)/10.0f,
(float)(SafeRandom(level+1)-level/2)/10.0f,
(float)(iA0+SafeRandom(iAd))/100.0f,
b0+(SafeRandom(db1)<<24)+(SafeRandom(db2)<<16)+(SafeRandom(db3)<<8)+SafeRandom(db4), pxList, object);
2009-05-08 13:28:41 +00:00
// success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleProc C4ParticleSystem::GetProc(const char *name)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// seek in map
for (int32_t i = 0; C4ParticleProcMap[i].Name[0]; ++i)
if (SEqual(C4ParticleProcMap[i].Name, name))
2009-05-08 13:28:41 +00:00
return C4ParticleProcMap[i].Proc;
// nothing found...
return NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleDrawProc C4ParticleSystem::GetDrawProc(const char *name)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// seek in map
for (int32_t i = 0; C4ParticleDrawProcMap[i].Name[0]; ++i)
if (SEqual(C4ParticleDrawProcMap[i].Name, name))
2009-05-08 13:28:41 +00:00
return C4ParticleDrawProcMap[i].Proc;
// nothing found...
return NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4ParticleDef *C4ParticleSystem::GetDef(const char *name, C4ParticleDef *exclude)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// seek list
for (C4ParticleDef *def = pDef0; def; def=def->pNext)
if (def != exclude && def->Name == name)
return def;
2009-05-08 13:28:41 +00:00
// nothing found
return NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4ParticleSystem::SetDefParticles()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get smoke
pSmoke = GetDef("Smoke");
2009-05-08 13:28:41 +00:00
// get blast
pBlast = GetDef("Blast");
pFSpark = GetDef("FSpark");
2009-05-08 13:28:41 +00:00
// get fire, if fire particles are desired
if (Config.Graphics.FireParticles)
2010-03-28 18:58:01 +00:00
{
pFire1 = GetDef("Fire");
pFire2 = GetDef("Fire2");
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
pFire1 = pFire2 = NULL;
2009-05-08 13:28:41 +00:00
// if fire is drawn w/o background fct: unload fire face if both fire particles are assigned
// but this is not done here
//if (IsFireParticleLoaded())
2010-03-28 18:58:01 +00:00
// ::GraphicsResource.fctFire.Clear();
}
2009-05-08 13:28:41 +00:00
int32_t C4ParticleSystem::Push(C4ParticleDef *of_def, float dxdir, float dydir)
2010-03-28 18:58:01 +00:00
{
int32_t num_pushed = 0;
2009-05-08 13:28:41 +00:00
// go through all particle chunks
for (C4ParticleChunk *a_chunk = &Chunk; a_chunk; a_chunk = a_chunk->pNext)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// go through all particles
C4Particle *particle = a_chunk->Data; int32_t i=C4Px_BufSize;
2009-05-08 13:28:41 +00:00
while (i--)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// def fits?
if (!of_def || particle->pDef == of_def)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// push it!
particle->xdir += dxdir;
particle->ydir += dydir;
2009-05-08 13:28:41 +00:00
// count pushed
++num_pushed;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// next particle
++particle;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done
return num_pushed;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxSmokeInit(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// init lifetime
particle->life = particle->pDef->MinLifetime;
int32_t lifetime = particle->pDef->MaxLifetime - particle->pDef->MinLifetime;
if (lifetime)
particle->life += SafeRandom(lifetime);
2009-05-08 13:28:41 +00:00
// use high-word of life to store init-status
particle->life |= (particle->life/17)<<16;
2009-05-08 13:28:41 +00:00
// set kind - ydir is unused anyway; set last kind reeeaaally seldom
particle->ydir = (float) SafeRandom(15) + SafeRandom(300)/299;
2009-05-08 13:28:41 +00:00
// set color
if (!particle->b)
particle->b = 0x004b4b4b;
else
particle->b &= ~0xff000000;
2009-05-08 13:28:41 +00:00
// always OK
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxSmokeExec(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// lifetime
if (!--particle->life) return false;
bool is_building = !!(particle->life&0x7fff0000);
2009-05-08 13:28:41 +00:00
// still building?
if (is_building)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// decrease init-time
particle->life -= 0x010000;
2009-05-08 13:28:41 +00:00
// increase color value
particle->b += 0x10000000;
2009-05-08 13:28:41 +00:00
// if full-grown, adjust to lifetime
if (!(particle->life&0x7fff0000))
particle->b = (particle->b&0xffffff)|((particle->life)<<24);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// color change
DWORD color = particle->b;
particle->b = (LightenClrBy(color, 1)&0xffffff) | Min<int32_t>((color>>24)-1, 255)<<24;
2009-05-08 13:28:41 +00:00
// wind to float
if (!(particle->b % 12) || is_building)
2010-03-28 18:58:01 +00:00
{
particle->xdir = 0.025f*::Weather.GetWind(int32_t(particle->x),int32_t(particle->y));
if (particle->xdir < -2.0f)
particle->xdir = -2.0f;
else if (particle->xdir > 2.0f)
particle->xdir = 2.0f;
particle->xdir += 0.1f * SafeRandom(41) - 2.0f;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// float
if (GBackSolid(int32_t(particle->x), int32_t(particle->y-particle->a)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// if stuck, decay; otherwise, move down
if (!GBackSolid(int32_t(particle->x), int32_t(particle->y)))
particle->y+=0.4f;
else
particle->a-=2;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
--particle->y;
particle->x += particle->xdir;
2009-05-08 13:28:41 +00:00
// increase in size
particle->a *= 1.01f;
2009-05-08 13:28:41 +00:00
// done, keep
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void fxSmokeDraw(C4Particle *particle, C4TargetFacet &cgo, C4Object *target)
2010-03-28 18:58:01 +00:00
{
C4ParticleDef *def = particle->pDef;
2009-05-08 13:28:41 +00:00
// apply parallaxity to target pos
int32_t tx = cgo.TargetX * def->Parallaxity[0]/100;
int32_t ty = cgo.TargetY * def->Parallaxity[1]/100;
2009-05-08 13:28:41 +00:00
// check if it's in screen range
if (!Inside(particle->x, tx-particle->a, tx+cgo.Wdt+particle->a)) return;
if (!Inside(particle->y, ty-particle->a, ty+cgo.Hgt+particle->a)) return;
2009-05-08 13:28:41 +00:00
// get pos
float cx = particle->x + cgo.X - tx;
float cy = particle->y + cgo.Y - ty;
2009-05-08 13:28:41 +00:00
// get phase by particle index
int32_t i = (int32_t) particle->ydir;
int32_t px = i/4;
int32_t py = i%4;
2009-05-08 13:28:41 +00:00
// draw at pos
2011-10-03 14:30:18 +00:00
pDraw->ActivateBlitModulation(particle->b);
float fx = float(def->Gfx.X + def->Gfx.Wdt * px);
float fy = float(def->Gfx.Y + def->Gfx.Hgt * py);
float fwdt = float(def->Gfx.Wdt);
float fhgt = float(def->Gfx.Hgt);
2011-10-03 14:30:18 +00:00
pDraw->Blit(def->Gfx.Surface,fx,fy,fwdt,fhgt,
cgo.Surface, cx - particle->a, cy - particle->a, particle->a * 2, particle->a * 2,
true);
2011-10-03 14:30:18 +00:00
pDraw->DeactivateBlitModulation();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxStdInit(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
if (particle->pDef->Delay)
2009-05-08 13:28:41 +00:00
// delay given: lifetime starts at zero
particle->life=0;
2009-05-08 13:28:41 +00:00
else
// init lifetime as phase
particle->life=SafeRandom(particle->pDef->Length);
2009-05-08 13:28:41 +00:00
// default color
if (!particle->b) particle->b=0xffffffff;
2009-05-08 13:28:41 +00:00
// always OK
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxStdExec(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
float dx = particle->x, dy = particle->y;
float dxdir = particle->xdir, dydir = particle->ydir;
// rel. position & movement
if (particle->pDef->Attach && target != NULL)
{
dx += fixtof(target->GetFixedX());
dy += fixtof(target->GetFixedY());
dxdir += fixtof(target->xdir);
dydir += fixtof(target->ydir);
}
2009-05-08 13:28:41 +00:00
// move
if (particle->xdir || particle->ydir)
2010-03-28 18:58:01 +00:00
{
if (particle->pDef->VertexCount && GBackSolid(int32_t( dx + particle->xdir),int32_t( dy + particle->ydir + particle->pDef->VertexY* particle->a/100.0f) ))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// collision
if (particle->pDef->CollisionProc)
if (!particle->pDef->CollisionProc(particle,target)) return false;
2010-03-28 18:58:01 +00:00
}
else if (particle->pDef->RByV != 2)
2010-03-28 18:58:01 +00:00
{
particle->x += particle->xdir;
particle->y += particle->ydir;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// With RByV=2, the V is only used for rotation, not for movement
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// apply gravity
if (particle->pDef->GravityAcc) particle->ydir+=fixtof(GravAccel * particle->pDef->GravityAcc)/100.0f;
2009-05-08 13:28:41 +00:00
// apply WindDrift
if (particle->pDef->WindDrift && !GBackSolid(int32_t(dx), int32_t(dy)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Air speed: Wind plus some random
int32_t wind_speed = GBackWind(int32_t(dx), int32_t(dy));
//C4Real txdir = itofix(wind_speed, 15) + C4REAL256(Random(1200) - 600);
float txdir = wind_speed / 15.0f;
//C4Real tydir = C4REAL256(Random(1200) - 600);
2009-05-08 13:28:41 +00:00
float tydir = 0;
// Air friction, based on WindDrift.
int32_t wind_drift = Max(particle->pDef->WindDrift - 20, 0);
particle->xdir += ((txdir - dxdir) * wind_drift) / 800;
particle->ydir += ((tydir - dydir) * wind_drift) / 800;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// fade out
int32_t fade = particle->pDef->AlphaFade;
if (fade < 0)
2010-03-28 18:58:01 +00:00
{
if (Game.FrameCounter % -fade == 0) fade = 1;
else fade = 0;
2010-03-28 18:58:01 +00:00
}
if (fade)
2010-03-28 18:58:01 +00:00
{
if (particle->pDef->FadeDelay == 0 || Game.FrameCounter % particle->pDef->FadeDelay == 0)
2009-07-15 13:41:03 +00:00
{
DWORD color = particle->b;
int32_t alpha = color>>24;
alpha -= particle->pDef->AlphaFade;
if (alpha <= 0x00) return false;
particle->b = (color&0xffffff) | (alpha<<24);
2009-07-15 13:41:03 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// if delay is given, advance lifetime
if (particle->pDef->Delay)
2010-03-28 18:58:01 +00:00
{
if (particle->life < 0)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// decay
return particle->life-- >= -particle->pDef->FadeOutLen * particle->pDef->FadeOutDelay;
2010-03-28 18:58:01 +00:00
}
++particle->life;
2009-05-08 13:28:41 +00:00
// check if still alive
int32_t phase = particle->life / particle->pDef->Delay;
int32_t length = particle->pDef->Length - particle->pDef->Reverse;
if (phase >= length * particle->pDef->Repeats + particle->pDef->Reverse)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do fadeout, if assigned
if (!particle->pDef->FadeOutLen) return false;
particle->life = -1;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return true;
}
2009-05-08 13:28:41 +00:00
// outside landscape range?
bool kp;
if (dxdir > 0)
2010-04-07 20:22:22 +00:00
kp = (dx - particle->a < GBackWdt);
else
2010-04-07 20:22:22 +00:00
kp = (dx + particle->a > 0);
if (dydir > 0)
kp = kp && (dy - particle->a < GBackHgt);
else
kp = kp && (dy + particle->a > particle->pDef->YOff);
2009-05-08 13:28:41 +00:00
return kp;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxBounce(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// reverse xdir/ydir
particle->xdir=-particle->xdir;
particle->ydir=-particle->ydir;
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxBounceY(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// reverse ydir only
particle->ydir = -particle->ydir;
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxStop(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// zero xdir/ydir
particle->xdir = particle->ydir = 0;
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool fxDie(C4Particle *particle, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// DIEEEEEE
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void fxStdDraw(C4Particle *particle, C4TargetFacet &cgo, C4Object *target)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get def
C4ParticleDef *def = particle->pDef;
2009-05-08 13:28:41 +00:00
// apply parallaxity to target pos
2012-12-28 18:28:39 +00:00
float tax = cgo.TargetX * def->Parallaxity[0] / 100;
float tay = cgo.TargetY * def->Parallaxity[1] / 100;
// get the phases per row
int32_t phases = def->PhasesX;
2009-05-08 13:28:41 +00:00
float dx = particle->x, dy = particle->y;
float dxdir = particle->xdir, dydir = particle->ydir;
// relative position & movement
if (def->Attach && target != NULL)
{
dx += fixtof(target->GetFixedX());
dy += fixtof(target->GetFixedY());
dxdir += fixtof(target->xdir);
dydir += fixtof(target->ydir);
}
2009-05-08 13:28:41 +00:00
// check if it's in screen range
if (!Inside(dx, tax-particle->a, tax + cgo.Wdt + particle->a)) return;
if (!Inside(dy, tay-particle->a, tay + cgo.Hgt + particle->a)) return;
2009-05-08 13:28:41 +00:00
// get pos
2012-12-28 18:28:39 +00:00
float cgox = cgo.X - tax, cgoy = cgo.Y - tay;
float cx = dx + cgox;
float cy = dy + cgoy;
2009-05-08 13:28:41 +00:00
// get phase
int32_t phase = particle->life;
if (def->Delay)
2010-03-28 18:58:01 +00:00
{
if (phase >= 0)
2010-03-28 18:58:01 +00:00
{
phase /= def->Delay;
int32_t length = def->Length;
if (def->Reverse)
2010-03-28 18:58:01 +00:00
{
--length;
phase %= length*2;
if (phase > length)
phase = length*2 + 1 - phase;
2009-05-08 13:28:41 +00:00
}
else phase %= length;
2010-01-25 04:00:59 +00:00
}
else phase = (phase+1) / -def->FadeOutDelay + def->Length;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// get rotation
float r=0;
if ((def->RByV == 1) || (def->RByV == 2)) // rotation by direction
r = Angle(0,0, (int32_t) (dxdir*10.0f), (int32_t) (dydir*10.0f));
if (def->RByV == 3) // random rotation - currently a pseudo random rotation by x/y position
r = fmodf(particle->x * 23 + particle->y * 12, 360.0f);
2009-05-08 13:28:41 +00:00
// draw at pos
2011-10-03 14:30:18 +00:00
pDraw->ActivateBlitModulation(particle->b);
pDraw->StorePrimaryClipper();
pDraw->SubPrimaryClipper(cgox, cgoy+def->YOff, 100000, 100000);
if (def->Additive)
2011-10-03 14:30:18 +00:00
pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
// draw
float draw_width = particle->a;
float draw_height = def->Aspect * draw_width;
int32_t phaseX = phase%phases;
int32_t phaseY = phase/phases;
float fx = float(def->Gfx.X + def->Gfx.Wdt * phaseX);
float fy = float(def->Gfx.Y + def->Gfx.Hgt * phaseY);
float fwdt = float(def->Gfx.Wdt);
float fhgt = float(def->Gfx.Hgt);
float tx = cx-draw_width;
float ty = cy-draw_height;
float twdt = draw_width*2;
float thgt = draw_height*2;
2009-05-08 13:28:41 +00:00
if (r)
{
C4BltTransform rot;
rot.SetRotate(r, (float) (tx+tx+twdt)/2, (float) (ty+ty+thgt)/2);
2011-10-03 14:30:18 +00:00
pDraw->Blit(def->Gfx.Surface,fx,fy,fwdt,fhgt,
cgo.Surface,tx,ty,twdt,thgt,
true,&rot);
}
2009-05-08 13:28:41 +00:00
else
{
2011-10-03 14:30:18 +00:00
pDraw->Blit(def->Gfx.Surface,fx,fy,fwdt,fhgt,
cgo.Surface,tx,ty,twdt,thgt,
true);
}
2011-10-03 14:30:18 +00:00
pDraw->ResetBlitMode();
pDraw->RestorePrimaryClipper();
pDraw->DeactivateBlitModulation();
2010-03-28 18:58:01 +00:00
}
C4ParticleProcRec C4ParticleProcMap[] =
{
{ "SmokeInit", fxSmokeInit },
{ "SmokeExec", fxSmokeExec },
{ "StdInit", fxStdInit },
{ "StdExec", fxStdExec },
{ "Bounce", fxBounce },
{ "BounceY", fxBounceY },
{ "Stop", fxStop },
{ "Die", fxDie },
{ "", 0 }
};
C4ParticleDrawProcRec C4ParticleDrawProcMap[] =
{
{ "Smoke", fxSmokeDraw },
{ "Std", fxStdDraw },
{ "", 0 }
};
2009-06-05 15:22:28 +00:00
C4ParticleSystem Particles;