Пример #1
0
ALERROR CLevelTableOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Loads from XML

	{
	int i;
	ALERROR error;

	m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB));
	if (m_Count.IsEmpty())
		m_Count.SetConstant(1);

	for (i = 0; i < pDesc->GetContentElementCount(); i++)
		{
		CXMLElement *pEntry = pDesc->GetContentElement(i);
		SEntry *pNewEntry = m_Table.Insert();

		pNewEntry->sLevelFrequency = pEntry->GetAttribute(LEVEL_FREQUENCY_ATTRIB);

		if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &pNewEntry->pDevice))
			{
			pNewEntry->pDevice = NULL;
			return error;
			}
		}

	m_iComputedLevel = -1;

	return NOERROR;
	}
Пример #2
0
void CParticleJetEffectPainter::CreateLinearParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel)

//	CreateLinearParticles
//
//	Creates new particles on a straight line

	{
	int i;

	//	Compute some basic stuff

	const Metric rJitterFactor = LIGHT_SPEED / 100000.0;
	Metric rCurRotation = AngleToRadians(180 + m_iXformRotation + m_iCurDirection);

	//	Compute the spread angle, in radians

	Metric rSpread = AngleToRadians(Max(0, m_SpreadAngle.Roll()));
	Metric rHalfSpread = 0.5 * rSpread;

	//	Calculate where last tick's particles would be based on the last rotation.

	Metric rAveSpeed = m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0;
	CVector vCurStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rCurRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos;

	//	Create particles

	for (i = 0; i < iCount; i++)
		{
		Metric rSlide = mathRandom(0, 9999) / 10000.0;

		//	We place the particle along the line betwen the current
		//	and last emit positions

		CVector vPos = vInitialPos + rSlide * vCurStart;

		//	Generate a random velocity backwards

		Metric rRotation = rCurRotation + (rHalfSpread * mathRandom(-1000, 1000) / 1000.0);
		Metric rSpeed = m_EmitSpeed.Roll() * LIGHT_SPEED / 100.0;
		CVector vVel = m_rXformTime * (vInitialVel + ::PolarToVectorRadians(rRotation, rSpeed + rJitterFactor * mathRandom(-500, 500)));

		//	Lifetime

		int iLifeLeft = m_ParticleLifetime.Roll();

		//	Add the particle

		m_Particles.AddParticle(vPos, vVel, iLifeLeft, AngleToDegrees(rRotation));
		}

	//	Remember the last position

	m_iLastDirection = m_iCurDirection;
	m_vLastEmitPos = vInitialPos;
	}
Пример #3
0
void CTableOfDeviceGenerators::AddDevices (SDeviceGenerateCtx &Ctx)

//	AddDevices
//
//	Add devices

	{
	int i, j;

	int iCount = m_Count.Roll();
	for (j = 0; j < iCount; j++)
		{
		int iRoll = mathRandom(1, m_iTotalChance);

		for (i = 0; i < m_Table.GetCount(); i++)
			{
			iRoll -= m_Table[i].iChance;

			if (iRoll <= 0)
				{
				m_Table[i].pDevice->AddDevices(Ctx);
				break;
				}
			}
		}
	}
Пример #4
0
ALERROR CGroupOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	int i;
	ALERROR error;

	m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB));
	if (m_Count.IsEmpty())
		m_Count.SetConstant(1);

	//	Load either a <DeviceSlot> element or another device generator.

	for (i = 0; i < pDesc->GetContentElementCount(); i++)
		{
		CXMLElement *pEntry = pDesc->GetContentElement(i);

		if (strEquals(pEntry->GetTag(), DEVICE_SLOT_TAG))
			{
			SSlotDesc *pSlotDesc = m_SlotDesc.Insert();

			CItem::ParseCriteria(pEntry->GetAttribute(CRITERIA_ATTRIB), &pSlotDesc->Criteria);

			if (error = IDeviceGenerator::InitDeviceDescFromXML(Ctx, pEntry, &pSlotDesc->DefaultDesc))
				return error;

			pSlotDesc->iMaxCount = pEntry->GetAttributeIntegerBounded(MAX_COUNT_ATTRIB, 0, -1, -1);
			}
		else
			{
			SEntry *pTableEntry = m_Table.Insert();

			pTableEntry->iChance = pEntry->GetAttributeIntegerBounded(CHANCE_ATTRIB, 0, -1, 100);
			if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &pTableEntry->pDevice))
				{
				pTableEntry->pDevice = NULL;
				return error;
				}
			}
		}

	return NOERROR;
	}
Пример #5
0
void CParticleJetEffectPainter::InitParticles (const CVector &vInitialPos)

//	Init
//
//	Make sure particle array is initialized

	{
	if (m_Particles.GetCount() == 0)
		{
		//	Compute the maximum number of particles that we could ever have

		int iNewParticleRate = m_EmitRate.GetMaxValue();
		int iParticleLifetime = m_ParticleLifetime.GetMaxValue();

		int iMaxParticleCount = Max(0, iParticleLifetime * iNewParticleRate);

		//	Initialize the array

		m_Particles.Init(iMaxParticleCount);
		}
	}
Пример #6
0
ALERROR CTableOfDeviceGenerators::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	int i;
	ALERROR error;

	m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB));
	if (m_Count.IsEmpty())
		m_Count.SetConstant(1);

	m_iTotalChance = 0;
	int iCount = pDesc->GetContentElementCount();
	if (iCount > 0)
		{
		m_Table.InsertEmpty(iCount);
		for (i = 0; i < iCount; i++)
			{
			CXMLElement *pEntry = pDesc->GetContentElement(i);
			
			m_Table[i].iChance = pEntry->GetAttributeInteger(CHANCE_ATTRIB);
			m_iTotalChance += m_Table[i].iChance;

			if (error = IDeviceGenerator::CreateFromXML(Ctx, pEntry, &m_Table[i].pDevice))
				{
				m_Table[i].pDevice = NULL;
				return error;
				}
			}
		}

	return NOERROR;
	}
Пример #7
0
void CLevelTableOfDeviceGenerators::AddDevices (SDeviceGenerateCtx &Ctx)

//	AddDevices
//
//	Adds devices

	{
	int i, j;

	//	Compute probabilities

	if (Ctx.iLevel != m_iComputedLevel)
		{
		m_iTotalChance = 0;
		for (i = 0; i < m_Table.GetCount(); i++)
			{
			m_Table[i].iChance = GetFrequencyByLevel(m_Table[i].sLevelFrequency, Ctx.iLevel);
			m_iTotalChance += m_Table[i].iChance;
			}

		m_iComputedLevel = Ctx.iLevel;
		}

	//	Generate

	if (m_iTotalChance)
		{
		int iCount = m_Count.Roll();

		for (i = 0; i < iCount; i++)
			{
			int iRoll = mathRandom(1, m_iTotalChance);

			for (j = 0; j < m_Table.GetCount(); j++)
				{
				iRoll -= m_Table[j].iChance;

				if (iRoll <= 0)
					{
					m_Table[j].pDevice->AddDevices(Ctx);
					break;
					}
				}
			}
		}
	}
Пример #8
0
void CGroupOfDeviceGenerators::AddDevices (SDeviceGenerateCtx &Ctx)

//	AddDevices
//
//	Add devices

	{
	int i, j;

	int iCount = m_Count.Roll();
	for (j = 0; j < iCount; j++)
		{
		for (i = 0; i < m_Table.GetCount(); i++)
			{
			if (mathRandom(1, 100) <= m_Table[i].iChance)
				m_Table[i].pDevice->AddDevices(Ctx);
			}
		}
	}
Пример #9
0
void CParticleJetEffectPainter::OnUpdate (SEffectUpdateCtx &Ctx)

//	OnUpdate
//
//	Update the painter

	{
	//	Update the single-particle painter

	if (m_pParticlePainter)
		m_pParticlePainter->OnUpdate(Ctx);

	//	LATER: Support damage

#if 0
	//	If we have a wake potential or if the particles do damage
	//	then we need to hit test against all objects in the system.

	Ctx.pDamageDesc = m_pCreator->GetDamageDesc();
	Ctx.iWakePotential = m_pCreator->GetWakePotential();

	if ((Ctx.pDamageDesc || Ctx.iWakePotential > 0) && Ctx.pSystem)
		{
		//	Update

		m_Particles.Update(Ctx);
		}
#endif

	//	Create new particles

	if (!Ctx.bFade)
		CreateNewParticles(Ctx.pObj, m_EmitRate.Roll(), Ctx.vEmitPos, CalcInitialVel(Ctx.pObj));
	else if (m_bUseObjectMotion && Ctx.pObj)
		m_vLastEmitPos = Ctx.pObj->GetPos();

	//	If we're fading, reset direction (otherwise, when painting thruster 
	//	effects we'll try to interpolate between stale directions).

	if (Ctx.bFade)
		m_iCurDirection = -1;
	}
Пример #10
0
ALERROR CSingleDevice::LoadFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	LoadFromXML
//
//	Load from XML

	{
	ALERROR error;

	//	Load the item type

	CString sUNID = pDesc->GetAttribute(DEVICE_ID_ATTRIB);
	if (sUNID.IsBlank())
		sUNID = pDesc->GetAttribute(ITEM_ATTRIB);

	if (error = m_pItemType.LoadUNID(Ctx, sUNID))
		return error;

	if (m_pItemType.GetUNID() == 0)
		{
		Ctx.sError = strPatternSubst(CONSTLIT("<%s> element missing item attribute."), pDesc->GetTag());
		return ERR_FAIL;
		}

	//	Load the count

	m_Count.LoadFromXML(pDesc->GetAttribute(COUNT_ATTRIB));
	if (m_Count.IsEmpty())
		m_Count.SetConstant(1);

	//	Load damage chance

	m_iDamaged = pDesc->GetAttributeInteger(DAMAGED_ATTRIB);

	//	Load enhancement chance

	if (error = m_Enhanced.InitFromXML(Ctx, pDesc))
		return error;

	//	Load device position attributes

	if (pDesc->FindAttributeInteger(POS_Z_ATTRIB, &m_iPosZ))
		{
		m_iPosAngle = pDesc->GetAttributeInteger(POS_ANGLE_ATTRIB);
		m_iPosRadius = pDesc->GetAttributeInteger(POS_RADIUS_ATTRIB);
		m_b3DPosition = true;
		m_bDefaultPos = false;
		}
	else if (pDesc->FindAttributeInteger(POS_ANGLE_ATTRIB, &m_iPosAngle))
		{
		m_iPosRadius = pDesc->GetAttributeInteger(POS_RADIUS_ATTRIB);
		m_iPosZ = 0;
		m_b3DPosition = false;
		m_bDefaultPos = false;
		}
	else
		{
		m_iPosAngle = 0;
		m_iPosRadius = 0;
		m_iPosZ = 0;
		m_b3DPosition = false;
		m_bDefaultPos = true;
		}

	//	Load fire arc attributes

	if (pDesc->FindAttributeInteger(MIN_FIRE_ARC_ATTRIB, &m_iMinFireArc))
		{
		m_bOmnidirectional = false;
		m_iMaxFireArc = pDesc->GetAttributeInteger(MAX_FIRE_ARC_ATTRIB);
		m_bDefaultFireArc = false;
		}
	else if (pDesc->FindAttributeBool(OMNIDIRECTIONAL_ATTRIB, &m_bOmnidirectional))
		{
		m_iMinFireArc = 0;
		m_iMaxFireArc = 0;
		m_bDefaultFireArc = false;
		}
	else
		{
		m_bOmnidirectional = false;
		m_iMinFireArc = 0;
		m_iMaxFireArc = 0;
		m_bDefaultFireArc = true;
		}

	//	Linked fire options

	CString sLinkedFire;
	if (pDesc->FindAttribute(LINKED_FIRE_ATTRIB, &sLinkedFire))
		{
		if (error = CDeviceClass::ParseLinkedFireOptions(Ctx, sLinkedFire, &m_dwLinkedFireOptions))
			return error;

		m_bDefaultLinkedFire = false;
		}
	else
		{
		m_dwLinkedFireOptions = 0;
		m_bDefaultLinkedFire = true;
		}

	m_bSecondary = pDesc->GetAttributeBool(SECONDARY_WEAPON_ATTRIB);

	//	Slot bonus

	if (pDesc->FindAttributeInteger(HP_BONUS_ATTRIB, &m_iSlotBonus))
		m_bDefaultSlotBonus = false;
	else
		{
		m_iSlotBonus = 0;
		m_bDefaultSlotBonus = true;
		}

	//	Load extra items

	CXMLElement *pItems = pDesc->GetContentElementByTag(ITEMS_TAG);
	if (pItems)
		{
		if (error = IItemGenerator::CreateFromXML(Ctx, pItems, &m_pExtraItems))
			return error;
		}

	return NOERROR;
	}
Пример #11
0
void CSingleDevice::AddDevices (SDeviceGenerateCtx &Ctx)

//	AddDevices
//
//	Add devices to list

	{
	int i;

	if (m_pItemType == NULL)
		return;

	int iCount = m_Count.Roll();
	for (i = 0; i < iCount; i++)
		{
		//	Initialize the desc

		SDeviceDesc Desc;
		Desc.Item = CItem(m_pItemType, 1);
		if (mathRandom(1, 100) <= m_iDamaged)
			Desc.Item.SetDamaged();
		else
			m_Enhanced.EnhanceItem(Desc.Item);

		//	Find the default settings for the device slot for this device

		SDeviceDesc SlotDesc;
		bool bUseSlotDesc = (Ctx.pRoot ? Ctx.pRoot->FindDefaultDesc(Desc.Item, &SlotDesc) : false);

		//	Set the device position appropriately, either from the <Device> element,
		//	from the slot descriptor at the root, or from defaults.

		if (!m_bDefaultPos)
			{
			Desc.iPosAngle = m_iPosAngle;
			Desc.iPosRadius = m_iPosRadius;
			Desc.iPosZ = m_iPosZ;
			Desc.b3DPosition = m_b3DPosition;
			}
		else if (bUseSlotDesc)
			{
			Desc.iPosAngle = SlotDesc.iPosAngle;
			Desc.iPosRadius = SlotDesc.iPosRadius;
			Desc.iPosZ = SlotDesc.iPosZ;
			Desc.b3DPosition = SlotDesc.b3DPosition;
			}

		//	Set the device fire arc appropriately.

		if (!m_bDefaultFireArc)
			{
			Desc.bOmnidirectional = m_bOmnidirectional;
			Desc.iMinFireArc = m_iMinFireArc;
			Desc.iMaxFireArc = m_iMaxFireArc;
			}
		else if (bUseSlotDesc)
			{
			Desc.bOmnidirectional = SlotDesc.bOmnidirectional;
			Desc.iMinFireArc = SlotDesc.iMinFireArc;
			Desc.iMaxFireArc = SlotDesc.iMaxFireArc;
			}

		//	Set linked fire

		if (!m_bDefaultLinkedFire)
			Desc.dwLinkedFireOptions = m_dwLinkedFireOptions;
		else if (bUseSlotDesc)
			Desc.dwLinkedFireOptions = SlotDesc.dwLinkedFireOptions;
		else
			Desc.dwLinkedFireOptions = 0;

		Desc.bSecondary = m_bSecondary;

		//	Slot bonus

		if (!m_bDefaultSlotBonus)
			Desc.iSlotBonus = m_iSlotBonus;
		else if (bUseSlotDesc)
			Desc.iSlotBonus = SlotDesc.iSlotBonus;
		else
			Desc.iSlotBonus = 0;

		//	Add extra items

		if (m_pExtraItems)
			{
			CItemListManipulator ItemList(Desc.ExtraItems);
			SItemAddCtx ItemCtx(ItemList);
			ItemCtx.iLevel = Ctx.iLevel;

			m_pExtraItems->AddItems(ItemCtx);
			}

		//	Done
		
		Ctx.pResult->AddDeviceDesc(Desc);
		}
	}
Пример #12
0
void CParticleJetEffectPainter::Paint (CG32bitImage &Dest, int x, int y, SViewportPaintCtx &Ctx)

//	Paint
//
//	Paint the effect

	{
	int iParticleLifetime = m_ParticleLifetime.GetMaxValue();

	//	Particles move the opposite direction from the shot

	int iTrailDirection = Ctx.iRotation;

	//	If we're using the object center then we paint at the object center.
	//	Otherwise we paint at the given position.

	int xPaint;
	int yPaint;
	if (m_bUseObjectCenter)
		{
		if (Ctx.pObj)
			Ctx.XForm.Transform(Ctx.pObj->GetPos(), &xPaint, &yPaint);
		else
			{
			//	If we don't have an object then we use the viewport center. This
			//	handles the case where we paint in TransData (where there is
			//	no object).

			xPaint = Ctx.xCenter;
			yPaint = Ctx.yCenter;
			}
		}
	else
		{
		xPaint = x;
		yPaint = y;
		}

	//	If we haven't created any particles yet, do it now

	if (m_iCurDirection == -1
			&& !Ctx.bFade)
		{
		m_iLastDirection = iTrailDirection;
		m_iCurDirection = iTrailDirection;

		//	Figure out the position where we create particles

		CVector vPos;

		//	If we're using the object center then it means that x,y is where
		//	we emit particles from. We need to convert from screen coordinates
		//	to object-relative coordinates.

		if (m_bUseObjectCenter)
			vPos = CVector((x - xPaint) * g_KlicksPerPixel, (yPaint - y) * g_KlicksPerPixel);

		//	Initialize last emit position

		m_vLastEmitPos = (m_bUseObjectMotion && Ctx.pObj ? Ctx.pObj->GetPos() + vPos : vPos);

		//	Create particles

		CreateNewParticles(Ctx.pObj, m_EmitRate.Roll(), vPos, CalcInitialVel(Ctx.pObj));
		}

	//	Paint with the painter

	if (m_pParticlePainter)
		{
		//	If we can get a paint descriptor, use that because it is faster

		SParticlePaintDesc Desc;
		if (m_pParticlePainter->GetParticlePaintDesc(&Desc))
			{
			Desc.iMaxLifetime = iParticleLifetime;
			m_Particles.Paint(Dest, xPaint, yPaint, Ctx, Desc);
			}

		//	Otherwise, we use the painter for each particle

		else
			m_Particles.Paint(Dest, xPaint, yPaint, Ctx, m_pParticlePainter);
		}

	//	Update

	m_iCurDirection = iTrailDirection;
	}
Пример #13
0
void CParticleJetEffectPainter::CreateInterpolatedParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel)

//	CreateInterpolatedParticles
//
//	Creates particles interpolated between to directions.

	{
	int i;

	//	Compute some basic stuff

	const Metric rJitterFactor = LIGHT_SPEED / 100000.0;
	Metric rLastRotation = AngleToRadians(180 + m_iXformRotation + m_iLastDirection);
	Metric rCurRotation = AngleToRadians(180 + m_iXformRotation + m_iCurDirection);

	//	Compute the spread angle, in radians

	Metric rSpread = AngleToRadians(Max(0, m_SpreadAngle.Roll()));
	Metric rHalfSpread = 0.5 * rSpread;

	//	Calculate where last tick's particles would be based on the last rotation.

	Metric rAveSpeed = m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0;
	CVector vLastStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rLastRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos;

	//	Calculate where last tick's particles would be IF we have used the current
	//	rotation. This allows us to interpolate a turn.

	CVector vCurStart = (m_vLastEmitPos + (m_rXformTime * ::PolarToVectorRadians(rCurRotation, rAveSpeed * g_SecondsPerUpdate))) - vInitialPos;

	//	Create particles

	for (i = 0; i < iCount; i++)
		{
		Metric rSlide = mathRandom(0, 9999) / 10000.0;

		//	Compute two points along the two slide vectors (last and current)

		CVector vSlide1 = rSlide * vLastStart;
		CVector vSlide2 = rSlide * vCurStart;
		CVector vAdj = (rSlide * vSlide1) + ((1.0 - rSlide) * vSlide2);

		//	We place the particle along the line betwen the current
		//	and last emit positions

		CVector vPos = vInitialPos + vAdj;

		//	We blend the rotation as well

		if (Absolute(rCurRotation - rLastRotation) > g_Pi)
			{
			if (rLastRotation < rCurRotation)
				rLastRotation += g_Pi * 2.0;
			else
				rCurRotation += g_Pi * 2.0;
			}

		Metric rSlideRotation = (rSlide * rLastRotation) + ((1.0 - rSlide) * rCurRotation);

		//	Generate a random velocity backwards

		Metric rRotation = rSlideRotation + (rHalfSpread * mathRandom(-1000, 1000) / 1000.0);
		Metric rSpeed = m_EmitSpeed.Roll() * LIGHT_SPEED / 100.0;
		CVector vVel = m_rXformTime * (vInitialVel + ::PolarToVectorRadians(rRotation, rSpeed + rJitterFactor * mathRandom(-500, 500)));

		//	Lifetime

		int iLifeLeft = m_ParticleLifetime.Roll();

		//	Add the particle

		m_Particles.AddParticle(vPos, vVel, iLifeLeft, AngleToDegrees(rRotation));
		}

	//	Remember the last position

	m_iLastDirection = m_iCurDirection;
	m_vLastEmitPos = vInitialPos;
	}
Пример #14
0
void CParticleJetEffectPainter::CreateFixedParticles (CSpaceObject *pObj, int iCount, const CVector &vInitialPos, const CVector &vInitialVel)

//	CreateFixedParticles
//
//	Creates particles along the objects path (e.g., missile exhaust).

	{
	int i;

	//	Calculate a vector to our previous position
	//
	//	NOTE: In this mode m_vLastEmitPos is the last position of the object.

	CVector vCurPos = (pObj ? pObj->GetPos() : CVector());
	CVector vToOldPos;
	if (m_bTrackingObject)
		{
		Metric rAveSpeed = m_rXformTime * m_EmitSpeed.GetAveValue() * LIGHT_SPEED / 100.0;
		vToOldPos = m_vLastEmitPos - (vCurPos + vInitialPos) + ::PolarToVector(180 + m_iLastDirection, rAveSpeed * g_SecondsPerUpdate);
		}
	else
		{
		Metric rSpeed = (pObj ? pObj->GetVel().Length() : 0.0);
		vToOldPos = ::PolarToVector(180 + m_iLastDirection, rSpeed * g_SecondsPerUpdate);
		}

	//	Compute two orthogonal coordinates

	CVector vAxis = ::PolarToVector(m_iCurDirection + 180, 1.0);
	CVector vTangent = ::PolarToVector(m_iCurDirection + 90, 1.0);

	//	Create particles

	for (i = 0; i < iCount; i++)
		{
		Metric rSlide = mathRandom(0, 9999) / 10000.0;

		//	Compute a position randomly along the line between the current and
		//	last emit positions.

		CVector vPos = vInitialPos + rSlide * vToOldPos;

		//	Generate a random velocity along the tangent

		Metric rTangentSlide = mathRandom(-9999, 9999) / 10000.0;
		Metric rAxisJitter = mathRandom(-50, 50) / 100.0;
		CVector vVel = (vTangent * rTangentSlide * m_rXformTime * m_TangentSpeed.Roll() * LIGHT_SPEED / 100.0)
				+ (vAxis * (m_EmitSpeed.Roll() + rAxisJitter) * LIGHT_SPEED / 100.0);

		//	Lifetime

		int iLifeLeft = m_ParticleLifetime.Roll();

		//	Add the particle

		m_Particles.AddParticle(vPos, vVel, iLifeLeft, m_iCurDirection);
		}

	//	Remember the last position

	m_iLastDirection = m_iCurDirection;
	m_vLastEmitPos = vCurPos + vInitialPos;
	}