2009-05-08 13:28:41 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
2009-06-05 13:41:20 +00:00
|
|
|
* Copyright (c) 2002, 2004-2005 Sven Eberhardt
|
2010-12-23 00:01:24 +00:00
|
|
|
* 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
|
2009-06-05 13:41:20 +00:00
|
|
|
* Copyright (c) 2008 Peter Wortmann
|
2010-12-23 00:01:24 +00:00
|
|
|
* 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>
|
|
|
|
|
2011-01-24 00:15:58 +00:00
|
|
|
#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>
|
2009-06-12 18:52:21 +00:00
|
|
|
#include <C4Log.h>
|
2009-06-11 19:59:35 +00:00
|
|
|
#include <C4Weather.h>
|
2009-06-15 21:47:26 +00:00
|
|
|
#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
|
|
|
|
2010-04-01 17:58:58 +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"),
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
pPrev = NULL;
|
|
|
|
ParticleSystem.pDef0 = this;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
2010-04-01 17:58:58 +00:00
|
|
|
{
|
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
Filename.Copy(group.GetFullName());
|
2009-05-08 13:28:41 +00:00
|
|
|
// load
|
2010-04-01 17:58:58 +00:00
|
|
|
char *particle_source;
|
|
|
|
if (group.LoadEntry(C4CFN_ParticleCore,&particle_source,NULL,1))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
if (!Compile(particle_source, Filename.getData()))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
DebugLogF("invalid particle def at '%s'", group.GetFullName().getData());
|
|
|
|
delete [] particle_source; return false;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +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
|
2011-03-28 16:36:16 +00:00
|
|
|
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
|
2010-03-27 22:46:43 +00:00
|
|
|
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
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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());
|
2013-10-22 14:21:31 +00:00
|
|
|
InitProc = &fxStdInit;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +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());
|
2013-10-22 14:21:31 +00:00
|
|
|
ExecProc = &fxStdExec;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +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
|
|
|
}
|
2010-04-01 17:58:58 +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());
|
2013-10-22 14:21:31 +00:00
|
|
|
DrawProc = &fxStdDraw;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// particle overloading
|
2010-04-01 17:58:58 +00:00
|
|
|
C4ParticleDef *def_overload;
|
|
|
|
if ((def_overload = ParticleSystem.GetDef(Name.getData(), this)))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
C4Group group;
|
|
|
|
if (!group.Open(Filename.getData())) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// reset class
|
|
|
|
Clear();
|
|
|
|
// load
|
2010-04-01 17:58:58 +00:00
|
|
|
return Load(group);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-01 17:58:58 +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)
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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!
|
2011-03-12 13:59:41 +00:00
|
|
|
memset(Data, 0, sizeof(Data));
|
2009-05-08 13:28:41 +00:00
|
|
|
// init list
|
2010-04-01 17:58:58 +00:00
|
|
|
C4Particle *particle=Data;
|
|
|
|
for (int32_t i=0; i < C4Px_BufSize; ++i)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
NumFree = C4Px_BufSize;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
next_particle = particle->pNext;
|
2009-05-08 13:28:41 +00:00
|
|
|
// execute it
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
--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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
next_particle = particle->pNext;
|
2009-05-08 13:28:41 +00:00
|
|
|
// sorry, life is over for you :P
|
2010-04-01 17:58:58 +00:00
|
|
|
--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
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t C4ParticleList::Remove(C4ParticleDef *of_def)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t num_removed = 0;
|
2009-05-08 13:28:41 +00:00
|
|
|
// check all particles for def
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
next_particle = particle->pNext;
|
2009-05-08 13:28:41 +00:00
|
|
|
// execute it
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
--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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
next_chunk = chunk->pNext;
|
2009-05-08 13:28:41 +00:00
|
|
|
// chunk empty?
|
2010-04-01 17:58:58 +00:00
|
|
|
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;
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
*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
|
2010-04-01 17:58:58 +00:00
|
|
|
previous_chunk_p = &chunk->pNext;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
C4ParticleChunk *next_chunk = Chunk.pNext, *chunk;
|
|
|
|
while ((chunk = next_chunk))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
C4Particle *C4ParticleSystem::Create(C4ParticleDef *of_def,
|
2010-03-28 18:58:01 +00:00
|
|
|
float x, float y,
|
|
|
|
float xdir, float ydir,
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
if (!of_def) return NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
// default to global list
|
2010-04-01 17:58:58 +00:00
|
|
|
if (!pxList) pxList = &GlobalParticles;
|
2009-05-08 13:28:41 +00:00
|
|
|
// check count
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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();
|
2010-04-01 17:58:58 +00:00
|
|
|
C4Particle *particle = FreeParticles.pFirst;
|
|
|
|
if (!particle) return NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
// set values
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->x -= fixtof(object->GetFixedX());
|
|
|
|
particle->y -= fixtof(object->GetFixedY());
|
2010-03-27 16:05:02 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// call initialization
|
2010-04-01 17:58:58 +00:00
|
|
|
if (!of_def->InitProc(particle,object))
|
2009-05-08 13:28:41 +00:00
|
|
|
// failed :(
|
|
|
|
return NULL;
|
|
|
|
// count particle
|
2010-04-01 17:58:58 +00:00
|
|
|
++of_def->Count;
|
2009-05-08 13:28:41 +00:00
|
|
|
// more to desired list
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->MoveList(::Particles.FreeParticles, *pxList);
|
2009-05-08 13:28:41 +00:00
|
|
|
// return newly created particle
|
2010-04-01 17:58:58 +00:00
|
|
|
return particle;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-01 17:58:58 +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,
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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,
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
pSmoke = GetDef("Smoke");
|
2009-05-08 13:28:41 +00:00
|
|
|
// get blast
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t C4ParticleSystem::Push(C4ParticleDef *of_def, float dxdir, float dydir)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t num_pushed = 0;
|
2009-05-08 13:28:41 +00:00
|
|
|
// go through all particle chunks
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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?
|
2010-04-01 17:58:58 +00:00
|
|
|
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!
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->xdir += dxdir;
|
|
|
|
particle->ydir += dydir;
|
2009-05-08 13:28:41 +00:00
|
|
|
// count pushed
|
2010-04-01 17:58:58 +00:00
|
|
|
++num_pushed;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// next particle
|
2010-04-01 17:58:58 +00:00
|
|
|
++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
|
2010-04-01 17:58:58 +00:00
|
|
|
return num_pushed;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->ydir = (float) SafeRandom(15) + SafeRandom(300)/299;
|
2009-05-08 13:28:41 +00:00
|
|
|
// set color
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
if (!--particle->life) return false;
|
|
|
|
bool is_building = !!(particle->life&0x7fff0000);
|
2009-05-08 13:28:41 +00:00
|
|
|
// still building?
|
2010-04-01 17:58:58 +00:00
|
|
|
if (is_building)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// decrease init-time
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->life -= 0x010000;
|
2009-05-08 13:28:41 +00:00
|
|
|
// increase color value
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->b += 0x10000000;
|
2009-05-08 13:28:41 +00:00
|
|
|
// if full-grown, adjust to lifetime
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
if (!(particle->b % 12) || is_building)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
--particle->y;
|
|
|
|
particle->x += particle->xdir;
|
2009-05-08 13:28:41 +00:00
|
|
|
// increase in size
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
void fxSmokeDraw(C4Particle *particle, C4TargetFacet &cgo, C4Object *target)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
C4ParticleDef *def = particle->pDef;
|
2009-05-08 13:28:41 +00:00
|
|
|
// apply parallaxity to target pos
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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);
|
2010-04-01 17:58:58 +00:00
|
|
|
|
|
|
|
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,
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
bool fxStdInit(C4Particle *particle, C4Object *target)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->pDef->Delay)
|
2009-05-08 13:28:41 +00:00
|
|
|
// delay given: lifetime starts at zero
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->life=0;
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
|
|
|
// init lifetime as phase
|
2010-04-01 17:58:58 +00:00
|
|
|
particle->life=SafeRandom(particle->pDef->Length);
|
2009-05-08 13:28:41 +00:00
|
|
|
// default color
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
bool fxStdExec(C4Particle *particle, C4Object *target)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
float dx = particle->x, dy = particle->y;
|
|
|
|
float dxdir = particle->xdir, dydir = particle->ydir;
|
2010-03-27 16:05:02 +00:00
|
|
|
// rel. position & movement
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->pDef->Attach && target != NULL)
|
2010-03-27 16:05:02 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
dx += fixtof(target->GetFixedX());
|
|
|
|
dy += fixtof(target->GetFixedY());
|
|
|
|
dxdir += fixtof(target->xdir);
|
|
|
|
dydir += fixtof(target->ydir);
|
2010-03-27 16:05:02 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// move
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->xdir || particle->ydir)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->pDef->CollisionProc)
|
|
|
|
if (!particle->pDef->CollisionProc(particle,target)) return false;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +00:00
|
|
|
else if (particle->pDef->RByV != 2)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->pDef->GravityAcc) particle->ydir+=fixtof(GravAccel * particle->pDef->GravityAcc)/100.0f;
|
2009-05-08 13:28:41 +00:00
|
|
|
// apply WindDrift
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t wind_speed = GBackWind(int32_t(dx), int32_t(dy));
|
2010-05-19 03:19:49 +00:00
|
|
|
//C4Real txdir = itofix(wind_speed, 15) + C4REAL256(Random(1200) - 600);
|
2010-04-01 17:58:58 +00:00
|
|
|
float txdir = wind_speed / 15.0f;
|
2010-05-19 03:19:49 +00:00
|
|
|
//C4Real tydir = C4REAL256(Random(1200) - 600);
|
2009-05-08 13:28:41 +00:00
|
|
|
float tydir = 0;
|
|
|
|
|
|
|
|
// Air friction, based on WindDrift.
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t fade = particle->pDef->AlphaFade;
|
|
|
|
if (fade < 0)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
if (Game.FrameCounter % -fade == 0) fade = 1;
|
|
|
|
else fade = 0;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +00:00
|
|
|
if (fade)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->pDef->FadeDelay == 0 || Game.FrameCounter % particle->pDef->FadeDelay == 0)
|
2009-07-15 13:41:03 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->pDef->Delay)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
if (particle->life < 0)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// decay
|
2010-04-01 17:58:58 +00:00
|
|
|
return particle->life-- >= -particle->pDef->FadeOutLen * particle->pDef->FadeOutDelay;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +00:00
|
|
|
++particle->life;
|
2009-05-08 13:28:41 +00:00
|
|
|
// check if still alive
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
2010-04-01 17:58:58 +00:00
|
|
|
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;
|
2010-04-01 17:58:58 +00:00
|
|
|
if (dxdir > 0)
|
2010-04-07 20:22:22 +00:00
|
|
|
kp = (dx - particle->a < GBackWdt);
|
2010-04-01 17:58:58 +00:00
|
|
|
else
|
2010-04-07 20:22:22 +00:00
|
|
|
kp = (dx + particle->a > 0);
|
2010-04-01 17:58:58 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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
|
|
|
|
2010-04-01 17:58:58 +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
|
|
|
|
2010-04-01 17:58:58 +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
|
2010-04-01 17:58:58 +00:00
|
|
|
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;
|
2010-04-01 17:58:58 +00:00
|
|
|
|
|
|
|
// get the phases per row
|
|
|
|
int32_t phases = def->PhasesX;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-01 17:58:58 +00:00
|
|
|
float dx = particle->x, dy = particle->y;
|
|
|
|
float dxdir = particle->xdir, dydir = particle->ydir;
|
2010-03-27 22:46:43 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
// relative position & movement
|
2010-04-01 17:58:58 +00:00
|
|
|
if (def->Attach && target != NULL)
|
2010-03-27 16:05:02 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
dx += fixtof(target->GetFixedX());
|
|
|
|
dy += fixtof(target->GetFixedY());
|
|
|
|
dxdir += fixtof(target->xdir);
|
|
|
|
dydir += fixtof(target->ydir);
|
2010-03-27 16:05:02 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// check if it's in screen range
|
2010-04-01 17:58:58 +00:00
|
|
|
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;
|
2010-04-01 17:58:58 +00:00
|
|
|
float cx = dx + cgox;
|
|
|
|
float cy = dy + cgoy;
|
|
|
|
|
2009-05-08 13:28:41 +00:00
|
|
|
// get phase
|
2010-04-01 17:58:58 +00:00
|
|
|
int32_t phase = particle->life;
|
|
|
|
if (def->Delay)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
if (phase >= 0)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
phase /= def->Delay;
|
|
|
|
int32_t length = def->Length;
|
|
|
|
if (def->Reverse)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-01 17:58:58 +00:00
|
|
|
--length;
|
|
|
|
phase %= length*2;
|
|
|
|
if (phase > length)
|
|
|
|
phase = length*2 + 1 - phase;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +00:00
|
|
|
else phase %= length;
|
2010-01-25 04:00:59 +00:00
|
|
|
}
|
2010-04-01 17:58:58 +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
|
2013-11-10 23:17:57 +00:00
|
|
|
float r=0;
|
2010-04-01 17:58:58 +00:00
|
|
|
if ((def->RByV == 1) || (def->RByV == 2)) // rotation by direction
|
2013-11-10 23:17:57 +00:00
|
|
|
r = Angle(0,0, (int32_t) (dxdir*10.0f), (int32_t) (dydir*10.0f));
|
2010-04-01 17:58:58 +00:00
|
|
|
if (def->RByV == 3) // random rotation - currently a pseudo random rotation by x/y position
|
2013-11-10 23:17:57 +00:00
|
|
|
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);
|
2010-04-01 17:58:58 +00:00
|
|
|
if (def->Additive)
|
2011-10-03 14:30:18 +00:00
|
|
|
pDraw->SetBlitMode(C4GFXBLIT_ADDITIVE);
|
2010-04-01 17:58:58 +00:00
|
|
|
|
|
|
|
// 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)
|
2010-04-01 17:58:58 +00:00
|
|
|
{
|
2011-09-30 20:15:51 +00:00
|
|
|
C4BltTransform rot;
|
2010-04-01 17:58:58 +00:00
|
|
|
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,
|
2010-04-01 17:58:58 +00:00
|
|
|
cgo.Surface,tx,ty,twdt,thgt,
|
|
|
|
true,&rot);
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
2010-04-01 17:58:58 +00:00
|
|
|
{
|
2011-10-03 14:30:18 +00:00
|
|
|
pDraw->Blit(def->Gfx.Surface,fx,fy,fwdt,fhgt,
|
2010-04-01 17:58:58 +00:00
|
|
|
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;
|