コード例 #1
0
//	-----------------------------------------------------------------------------------------------------------
//	Determine if two OBJECTS are on a line of sight.  If so, return true, else return false.
//	Calls fvi.
int ObjectToObjectVisibility (CObject *objP1, CObject *objP2, int transType)
{
	CHitQuery	fq;
	CHitData		hitData;
	int			fate, nTries = 0, bSpectate = SPECTATOR (objP1);

do {
	if (nTries++) {
		fq.startSeg		= bSpectate ? FindSegByPos (gameStates.app.playerPos.vPos, gameStates.app.nPlayerSegment, 1, 0) :
							  FindSegByPos (objP1->info.position.vPos, objP1->info.nSegment, 1, 0);
		if (fq.startSeg < 0) {
			fate = HIT_BAD_P0;
			return false;
			}
		}
	else
		fq.startSeg		= bSpectate ? gameStates.app.nPlayerSegment : objP1->info.nSegment;
	fq.p0					= bSpectate ? &gameStates.app.playerPos.vPos : &objP1->info.position.vPos;
	fq.p1					= SPECTATOR (objP2) ? &gameStates.app.playerPos.vPos : &objP2->info.position.vPos;
	fq.radP0				=
	fq.radP1				= 0x10;
	fq.thisObjNum		= OBJ_IDX (objP1);
	fq.ignoreObjList	= NULL;
	fq.flags				= transType;
	fq.bCheckVisibility = false;
	fate = FindHitpoint (&fq, &hitData);
	}
while ((fate == HIT_BAD_P0) && (nTries < 2));
return (fate == HIT_NONE) || (fate == HIT_BAD_P0);
}
コード例 #2
0
void CreateSmallFireballOnObject (CObject *objP, fix size_scale, int bSound)
{
	fix			size;
	CFixVector	vPos, vRand;
	short			nSegment;

vPos = objP->info.position.vPos;
vRand = CFixVector::Random();
vRand *= (objP->info.xSize / 2);
vPos += vRand;
size = FixMul (size_scale, I2X (1) / 2 + d_rand () * 4 / 2);
nSegment = FindSegByPos (vPos, objP->info.nSegment, 1, 0);
if (nSegment != -1) {
	CObject *explObjP = /*Object*/CreateExplosion (nSegment, vPos, size, VCLIP_SMALL_EXPLOSION);
	if (!explObjP)
		return;
	AttachObject (objP, explObjP);
	if (bSound || (d_rand () < 8192)) {
		fix vol = I2X (1) / 2;
		if (objP->info.nType == OBJ_ROBOT)
			vol *= 2;
		audio.CreateObjectSound (SOUND_EXPLODING_WALL, SOUNDCLASS_EXPLOSION, objP->Index (), 0, vol);
		}
	}
}
コード例 #3
0
ファイル: object.cpp プロジェクト: paud/d2x-xl
void CObject::LinkToSeg (int nSegment)
{
if ((nSegment < 0) || (nSegment >= gameData.segs.nSegments)) {
	nSegment = FindSegByPos (*GetPos (), 0, 0, 0);
	if (nSegment < 0)
		return;
	}
SetSegment (nSegment);
#if DBG
if (IsLinkedToSeg (nSegment))
	UnlinkFromSeg ();
#else
if (SEGMENTS [nSegment].m_objects == Index ())
	return;
#endif
SetNextInSeg (SEGMENTS [nSegment].m_objects);
#if DBG
if ((info.nNextInSeg < -1) || (info.nNextInSeg >= LEVEL_OBJECTS))
	SetNextInSeg (-1);
#endif
SetPrevInSeg (-1);
SEGMENTS [nSegment].m_objects = Index ();
if (info.nNextInSeg != -1)
	OBJECTS [info.nNextInSeg].SetPrevInSeg (Index ());
}
コード例 #4
0
int CParticle::UpdateDrift (int t, int nThread)
{
    m_vPos += m_vDrift * t; //(I2X (t) / 1000);

#if DBG
    CFixVector vDrift = m_vDrift;
    CFixVector::Normalize (vDrift);
    if (CFixVector::Dot (vDrift, m_vDir) < 0)
        t = t;
#endif

    if ((m_nType <= SMOKE_PARTICLES) || (m_nType == FIRE_PARTICLES)) {
        m_vDrift [X] = ChangeDir (m_vDrift [X]);
        m_vDrift [Y] = ChangeDir (m_vDrift [Y]);
        m_vDrift [Z] = ChangeDir (m_vDrift [Z]);
    }

    if (m_bHaveDir) {
        CFixVector vi = m_vDrift, vj = m_vDir;
        CFixVector::Normalize (vi);
        CFixVector::Normalize (vj);
        fix drag = Drag ();
        if (CFixVector::Dot (vi, vj) < 0)
            drag = -drag;
        m_vPos += m_vDir * drag;
    }

    int nSegment = FindSegByPos (m_vPos, m_nSegment, m_nSegment < 0, 1, (m_nType == BUBBLE_PARTICLES) ? 0 : fix (m_nRad), nThread);
    if ((0 > nSegment) && ((m_nType != WATERFALL_PARTICLES) || m_bChecked)) {
        if (m_nType == BUBBLE_PARTICLES) {
            if (SEGMENTS [nSegment].m_nType != SEGMENT_IS_WATER) {
                m_nLife = -1;
                return 0;
            }
        }
        else if (m_nType == WATERFALL_PARTICLES) {
            CFixVector vDir = m_vPos - m_vStartPos;
            if ((CFixVector::Normalize (vDir) >= I2X (1)) && (CFixVector::Dot (vDir, m_vDir) < I2X (1) / 2)) {
                m_nLife = -1;
                return 0;
            }
            if (SEGMENTS [nSegment].m_nType == SEGMENT_IS_WATER) {
                m_bChecked = 1;
                m_nLife = 500;
            }
        }
        else if (m_nTTL - m_nLife > 500) {
            m_nLife = -1;
            return 0;
        }
    }
    m_nSegment = nSegment;

    if (!Bounce (nThread))
        return 0;

    return 1;
}
コード例 #5
0
ファイル: particles.cpp プロジェクト: paud/d2x-xl
void CParticleEmitter::SetPos (CFixVector *vPos, CFixMatrix *mOrient, short nSegment)
{
if ((nSegment < 0) && gameOpts->render.particles.bCollisions)
	nSegment = FindSegByPos (*vPos, m_nSegment, 1, 0, 1);
m_vPos = *vPos;
if (mOrient)
	m_mOrient = *mOrient;
if (nSegment >= 0)
	m_nSegment = nSegment;
}
コード例 #6
0
ファイル: aipath.cpp プロジェクト: paud/d2x-xl
tPointSeg *InsertTransitPoint (tPointSeg *curSegP, tPointSeg *predSegP, tPointSeg *succSegP, ubyte nConnSide)
{
	CFixVector	vCenter, vPoint;
	short			nSegment;

vCenter = SEGMENTS [predSegP->nSegment].SideCenter (nConnSide);
vPoint = predSegP->point - vCenter;
vPoint[X] /= 16;
vPoint[Y] /= 16;
vPoint[Z] /= 16;
curSegP->point = vCenter - vPoint;
nSegment = FindSegByPos (curSegP->point, succSegP->nSegment, 1, 0);
if (nSegment == -1) {
#if TRACE
	console.printf (1, "Warning: point not in ANY CSegment in aipath.c/InsertCenterPoints().\n");
#endif
	curSegP->point = vCenter;
	FindSegByPos (curSegP->point, succSegP->nSegment, 1, 0);
	}
curSegP->nSegment = succSegP->nSegment;
return curSegP;
}
コード例 #7
0
ファイル: ailib.cpp プロジェクト: paud/d2x-xl
int AICanFireAtPlayer (CObject *objP, CFixVector *vGun, CFixVector *vPlayer)
{
	tFVIQuery	fq;
	fix			nSize, h;
	short			nModel, ignoreObjs [2] = {OBJ_IDX (gameData.objs.consoleP), -1};

//	Assume that robot's gun tip is in same CSegment as robot's center.
if (vGun->IsZero())
	return 0;
if (!extraGameInfo [IsMultiGame].bRobotsHitRobots)
	return 1;
objP->cType.aiInfo.SUB_FLAGS &= ~SUB_FLAGS_GUNSEG;
if (((*vGun) [X] == objP->info.position.vPos [X]) &&
	 ((*vGun) [Y] == objP->info.position.vPos [Y]) &&
	 ((*vGun) [Z] == objP->info.position.vPos [Z]))
	fq.startSeg	= objP->info.nSegment;
else {
	short nSegment = FindSegByPos (*vGun, objP->info.nSegment, 1, 0);
	if (nSegment == -1)
		return -1;
	if (nSegment != objP->info.nSegment)
		objP->cType.aiInfo.SUB_FLAGS |= SUB_FLAGS_GUNSEG;
	fq.startSeg = nSegment;
	}
h = CFixVector::Dist (*vGun, objP->info.position.vPos);
h = CFixVector::Dist (*vGun, *vPlayer);
nModel = objP->rType.polyObjInfo.nModel;
nSize = objP->info.xSize;
objP->rType.polyObjInfo.nModel = -1;	//make sure sphere/hitbox and not hitbox/hitbox collisions get tested
objP->info.xSize = I2X (2);						//chose some meaningful small size to simulate a weapon
fq.p0					= vGun;
fq.p1					= vPlayer;
fq.radP0				=
fq.radP1				= I2X (1);
fq.thisObjNum		= objP->Index ();
fq.ignoreObjList	= ignoreObjs;
fq.flags				= FQ_CHECK_OBJS | FQ_ANY_OBJECT | FQ_IGNORE_POWERUPS;		//what about trans walls???
fq.bCheckVisibility = true;
gameData.ai.nHitType = FindVectorIntersection (&fq, &gameData.ai.hitData);
#if DBG
if (gameData.ai.nHitType == 0)
	FindVectorIntersection (&fq, &gameData.ai.hitData);
#endif
gameData.ai.vHitPos = gameData.ai.hitData.hit.vPoint;
gameData.ai.nHitSeg = gameData.ai.hitData.hit.nSegment;
objP->rType.polyObjInfo.nModel = nModel;
objP->info.xSize = nSize;
return (gameData.ai.nHitType == HIT_NONE);
}
コード例 #8
0
void CParticle::UpdateColor (float fBrightness, int nThread)
{
    if (m_nType <= SMOKE_PARTICLES) {
        if (m_nFadeState > 0) {
            if (m_color [0].green < m_color [1].green) {
#if SMOKE_SLOWMO
                m_color [0].green += 1.0f / 20.0f / (float) gameStates.gameplay.slowmo [0].fSpeed;
#else
                m_color [0].green += 1.0f / 20.0f;
#endif
                if (m_color [0].green > m_color [1].green) {
                    m_color [0].green = m_color [1].green;
                    m_nFadeState--;
                }
            }
            if (m_color [0].blue < m_color [1].blue) {
#if SMOKE_SLOWMO
                m_color [0].blue += 1.0f / 10.0f / (float) gameStates.gameplay.slowmo [0].fSpeed;
#else
                m_color [0].blue += 1.0f / 10.0f;
#endif
                if (m_color [0].blue > m_color [1].blue) {
                    m_color [0].blue = m_color [1].blue;
                    m_nFadeState--;
                }
            }
        }
        else if (m_nFadeState == 0) {
            m_color [0].red = m_color [1].red * RANDOM_FADE;
            m_color [0].green = m_color [1].green * RANDOM_FADE;
            m_color [0].blue = m_color [1].blue * RANDOM_FADE;
            m_nFadeState = -1;
        }
#if SMOKE_LIGHTING //> 1
        if (gameOpts->render.particles.nQuality == 3) {
            if (0 <= (m_nSegment = FindSegByPos (m_vPos, m_nSegment, 0, 0, 0, nThread))) {
                tFaceColor* colorP = lightManager.AvgSgmColor (m_nSegment, NULL, nThread);
                m_color [0].red *= colorP->color.red;
                m_color [0].green *= colorP->color.green;
                m_color [0].blue *= colorP->color.blue;
            }
        }
#endif
    }

    SetupColor (fBrightness);
}
コード例 #9
0
ファイル: ailib.cpp プロジェクト: paud/d2x-xl
// --------------------------------------------------------------------------------------------------------------------
//	Returns:
//		0		Player is not visible from CObject, obstruction or something.
//		1		Player is visible, but not in field of view.
//		2		Player is visible and in field of view.
//	Note: Uses gameData.ai.vBelievedPlayerPos as CPlayerData's position for cloak effect.
//	NOTE: Will destructively modify *pos if *pos is outside the mine.
int ObjectCanSeePlayer (CObject *objP, CFixVector *pos, fix fieldOfView, CFixVector *vVecToPlayer)
{
	fix			dot;
	tFVIQuery	fq;

	//	Assume that robot's gun tip is in same CSegment as robot's center.
objP->cType.aiInfo.SUB_FLAGS &= ~SUB_FLAGS_GUNSEG;
fq.p0	= pos;
if (((*pos)[X] != objP->info.position.vPos [X]) ||
	 ((*pos)[Y] != objP->info.position.vPos [Y]) ||
	 ((*pos)[Z] != objP->info.position.vPos [Y])) {
	short nSegment = FindSegByPos (*pos, objP->info.nSegment, 1, 0);
	if (nSegment == -1) {
		fq.startSeg = objP->info.nSegment;
		*pos = objP->info.position.vPos;
#if TRACE
		console.printf (1, "Object %i, gun is outside mine, moving towards center.\n", objP->Index ());
#endif
		MoveTowardsSegmentCenter (objP);
		}
	else {
		if (nSegment != objP->info.nSegment)
			objP->cType.aiInfo.SUB_FLAGS |= SUB_FLAGS_GUNSEG;
		fq.startSeg = nSegment;
		}
	}
else
	fq.startSeg	= objP->info.nSegment;
fq.p1					= &gameData.ai.vBelievedPlayerPos;
fq.radP0				=
fq.radP1				= I2X (1) / 4;
fq.thisObjNum		= objP->Index ();
fq.ignoreObjList	= NULL;
fq.flags				= FQ_TRANSWALL | FQ_CHECK_OBJS | FQ_CHECK_PLAYER;
fq.bCheckVisibility = true;
gameData.ai.nHitType = FindVectorIntersection (&fq, &gameData.ai.hitData);
gameData.ai.vHitPos = gameData.ai.hitData.hit.vPoint;
gameData.ai.nHitSeg = gameData.ai.hitData.hit.nSegment;
if ((gameData.ai.nHitType != HIT_OBJECT) || (gameData.ai.hitData.hit.nObject != LOCALPLAYER.nObject))
	return 0;
dot = CFixVector::Dot (*vVecToPlayer, objP->info.position.mOrient.FVec ());
return (dot > fieldOfView - (gameData.ai.nOverallAgitation << 9)) ? 2 : 1;
}
コード例 #10
0
ファイル: network_lib.cpp プロジェクト: paud/d2x-xl
int GotTeamSpawnPos (void)
{
	int	i, j;

for (i = 0; i < gameData.multiplayer.nPlayerPositions; i++) {
	j = FindSegByPos (PlayerSpawnPos (i), -1, 1, 0);
	gameData.multiplayer.playerInit [i].nSegType = (j < 0) ? SEGMENT_IS_NOTHING : gameData.segs.segment2s [j].special;
	switch (gameData.multiplayer.playerInit [i].nSegType) {
		case SEGMENT_IS_GOAL_BLUE:
		case SEGMENT_IS_TEAM_BLUE:
		case SEGMENT_IS_GOAL_RED:
		case SEGMENT_IS_TEAM_RED:
			break;
		default:
			return 0;
		}
	}
return 1;
}
コード例 #11
0
bool CParticle::IsVisible (int nThread)
{
#if 0
    return gameData.render.mine.bVisible [m_nSegment] == gameData.render.mine.nVisible;
#else
    if ((m_nSegment < 0) || (m_nSegment >= gameData.segs.nSegments))
        return false;
    if (gameData.render.mine.bVisible [m_nSegment] == gameData.render.mine.nVisible)
        return true;
    short* childP = SEGMENTS [m_nSegment].m_children;
    for (int i = 6; i; i--, childP++)
        if ((*childP >= 0) && (gameData.render.mine.bVisible [*childP] == gameData.render.mine.nVisible))
            return true;
    int nSegment = FindSegByPos (m_vPos, m_nSegment, 0, 0, 0, nThread);
    if (nSegment < 0)
        return false;
    m_nSegment = nSegment;
    return gameData.render.mine.bVisible [nSegment] == gameData.render.mine.nVisible;
#endif
}
コード例 #12
0
void CreateVClipOnObject (CObject *objP, fix xScale, ubyte nVClip)
{
	fix			xSize;
	CFixVector	vPos, vRand;
	short			nSegment;

vPos = objP->info.position.vPos;
vRand = CFixVector::Random();
vRand *= (objP->info.xSize / 2);
vPos += vRand;
xSize = FixMul (xScale, I2X (1) + d_rand ()*4);
nSegment = FindSegByPos (vPos, objP->info.nSegment, 1, 0);
if (nSegment != -1) {
	CObject *explObjP = /*Object*/CreateExplosion (nSegment, vPos, xSize, nVClip);
	if (!explObjP)
		return;

	explObjP->info.movementType = MT_PHYSICS;
	explObjP->mType.physInfo.velocity = objP->mType.physInfo.velocity * (I2X (1) / 2);
	}
}
コード例 #13
0
int CanSeePoint (CObject *objP, CFixVector *vSource, CFixVector *vDest, short nSegment, fix xRad)
{
	CHitQuery	fq;
	int					nHitType;
	CHitData		hitData;

	//see if we can see this CPlayerData

fq.p0 = vSource;
fq.p1 = vDest;
fq.radP0 = 1;
fq.radP1 = xRad;
fq.thisObjNum = objP ? objP->Index () : -1;
fq.flags = FQ_TRANSWALL;
if (SPECTATOR (objP))
	fq.startSeg = FindSegByPos (objP->info.position.vPos, objP->info.nSegment, 1, 0);
else
	fq.startSeg = objP ? objP->info.nSegment : nSegment;
fq.ignoreObjList = NULL;
fq.bCheckVisibility = false;
nHitType = FindHitpoint (&fq, &hitData);
return nHitType != HIT_WALL;
}
コード例 #14
0
ファイル: object.cpp プロジェクト: paud/d2x-xl
//------------------------------------------------------------------------------
//Tries to find a CSegment for an CObject, using FindSegByPos ()
int CObject::FindSegment (void)
{
return FindSegByPos (info.position.vPos, info.nSegment, 1, 0);
}
コード例 #15
0
int CreateObject (ubyte nType, ubyte nId, short nCreator, short nSegment, const CFixVector& vPos, const CFixMatrix& mOrient,
						fix xSize, ubyte cType, ubyte mType, ubyte rType)
{
	short		nObject;
	CObject	*objP;

#if DBG
if (nType == OBJ_WEAPON) {
	nType = nType;
	if ((nCreator >= 0) && (OBJECTS [nCreator].info.nType == OBJ_ROBOT)) {
		nType = nType;
		if ((nDbgSeg >= 0) && (nSegment == nDbgSeg))
			nDbgSeg = nDbgSeg;
		}
	if (nId == FLARE_ID)
		nType = nType;
	if (gameData.objs.bIsMissile [(int) nId])
		nType = nType;
	}
else if (nType == OBJ_ROBOT) {
#if 0
	if (ROBOTINFO ((int) nId).bossFlag && (BOSS_COUNT >= MAX_BOSS_COUNT))
		return -1;
#endif
	}
else if (nType == OBJ_HOSTAGE)
	nType = nType;
else if (nType == OBJ_FIREBALL)
	nType = nType;
else if (nType == OBJ_REACTOR)
	nType = nType;
else if (nType == OBJ_DEBRIS)
	nType = nType;
else if (nType == OBJ_MARKER)
	nType = nType;
else if (nType == OBJ_PLAYER)
	nType = nType;
else if (nType == OBJ_POWERUP) {
	nType = nType;
	if (nId == POW_MONSTERBALL)
		nId = nId;
	}
#endif

//if (GetSegMasks (vPos, nSegment, 0).m_center))
if (nSegment < -1)
	nSegment = -nSegment - 2;
else
	nSegment = FindSegByPos (vPos, nSegment, 1, 0);
if ((nSegment < 0) || (nSegment > gameData.segs.nLastSegment))
	return -1;

if (nType == OBJ_DEBRIS) {
	if (gameData.objs.nDebris >= gameStates.render.detail.nMaxDebrisObjects)
		return -1;
	}

// Find next free object
if (0 > (nObject = AllocObject ()))
	return -1;
objP = OBJECTS + nObject;
objP->SetId (nObject);
// Zero out object structure to keep weird bugs from happening in uninitialized fields.
objP->info.nSignature = gameData.objs.nNextSignature++;
objP->info.nType = nType;
objP->info.nId = nId;
objP->info.vLastPos =
objP->info.position.vPos = vPos;
objP->SetOrigin (vPos);
objP->info.xSize = xSize;
objP->info.nCreator = sbyte (nCreator);
objP->info.xLifeLeft = IMMORTAL_TIME;
if (IsMultiGame && (gameData.app.nGameMode & GM_ENTROPY) && (nType == OBJ_POWERUP) && (nId == POW_ENTROPY_VIRUS)) {
	if ((nCreator >= 0) && (OBJECTS [nCreator].info.nType == OBJ_PLAYER))
		objP->info.nCreator = sbyte (GetTeam (OBJECTS [nCreator].info.nId) + 1);
	if (extraGameInfo [1].entropy.nVirusLifespan > 0)
		objP->info.xLifeLeft = I2X (extraGameInfo [1].entropy.nVirusLifespan);
	}
objP->info.position.mOrient = mOrient;
objP->info.controlType = cType;
objP->info.movementType = mType;
objP->info.renderType = rType;
objP->info.contains.nType = -1;
objP->info.nAttachedObj = -1;
if (objP->info.controlType == CT_POWERUP)
	objP->cType.powerupInfo.nCount = 1;

// Init physics info for this CObject
if (objP->info.movementType == MT_PHYSICS)
	objP->SetStartVel ((CFixVector*) &CFixVector::ZERO);
if (objP->info.renderType == RT_POLYOBJ)
	objP->rType.polyObjInfo.nTexOverride = -1;
objP->SetCreationTime (gameData.time.xGame);

if (objP->info.nType == OBJ_WEAPON) {
	Assert (objP->info.controlType == CT_WEAPON);
	objP->mType.physInfo.flags |= WI_persistent (objP->info.nId) * PF_PERSISTENT;
	objP->cType.laserInfo.xCreationTime = gameData.time.xGame;
	objP->cType.laserInfo.nLastHitObj = 0;
	objP->cType.laserInfo.xScale = I2X (1);
	}
else if (objP->info.nType == OBJ_DEBRIS)
	gameData.objs.nDebris++;
if (objP->info.controlType == CT_POWERUP)
	objP->cType.powerupInfo.xCreationTime = gameData.time.xGame;
else if (objP->info.controlType == CT_EXPLOSION)
	objP->cType.explInfo.attached.nNext =
	objP->cType.explInfo.attached.nPrev =
	objP->cType.explInfo.attached.nParent = -1;

objP->Link ();
objP->LinkToSeg (nSegment);

memset (&objP->HitInfo (), 0, sizeof (CObjHitInfo));
#if 1
if (IsMultiGame && IsCoopGame && 
	 (nType == OBJ_WEAPON) && gameData.objs.bIsMissile [int (nId)] && 
	 (nCreator >= 0) && (OBJECTS [nCreator].info.nType == OBJ_PLAYER)) {
	extern char powerupToObject [MAX_POWERUP_TYPES];

	for (int i = 0; i < MAX_POWERUP_TYPES; i++) {
		if (powerupToObject [i] == nId)
			gameData.multiplayer.maxPowerupsAllowed [i]--;
		}
	}
#endif
OBJECTS [nObject].ResetDamage ();
OBJECTS [nObject].SetTarget (NULL);
return nObject;
}
コード例 #16
0
ファイル: aipath.cpp プロジェクト: paud/d2x-xl
void MoveTowardsOutside (tPointSeg *ptSegs, int *nPoints, CObject *objP, int bRandom)
{
	int			i, j;
	int			nNewSeg;
	fix			xSegSize;
	int			nSegment;
	CFixVector	a, b, c, d, e;
	CFixVector	vGoalPos;
	int			count;
	int			nTempSeg;
	tFVIQuery	fq;
	tFVIData		hitData;
	int			nHitType;

j = *nPoints;
if (j > LEVEL_SEGMENTS)
	j = LEVEL_SEGMENTS;
for (i = 1, --j; i < j; i++) {
	nTempSeg = FindSegByPos (ptSegs [i].point, ptSegs [i].nSegment, 1, 0);
	if (nTempSeg < 0)
		break;
	ptSegs [i].nSegment = nTempSeg;
	nSegment = ptSegs [i].nSegment;

	if (i == 1) {
		a = ptSegs [i].point - ptSegs [i-1].point;
		CFixVector::Normalize (a);
		}
	else
		a = b;
	b = ptSegs [i + 1].point - ptSegs [i].point;
	c = ptSegs [i + 1].point - ptSegs [i-1].point;
	CFixVector::Normalize (b);
	if (abs (CFixVector::Dot (a, b)) > I2X (3)/4) {
		if (abs (a[Z]) < I2X (1)/2) {
			if (bRandom) {
				e [X] = (d_rand ()- 16384) / 2;
				e [Y] = (d_rand ()- 16384) / 2;
				e [Z] = abs (e [X]) + abs (e [Y]) + 1;
				CFixVector::Normalize (e);
				}
			else {
				e [X] =
				e [Y] = 0;
				e [Z] = I2X (1);
				}
			}
		else {
			if (bRandom) {
				e [Y] = (d_rand ()-16384)/2;
				e [Z] = (d_rand ()-16384)/2;
				e [X] = abs (e [Y]) + abs (e [Z]) + 1;
				CFixVector::Normalize (e);
				}
			else {
				e [X] = I2X (1);
				e [Y] =
				e [Z] = 0;
				}
			}
		}
	else {
		d = CFixVector::Cross(a, b);
		e = CFixVector::Cross(c, d);
		CFixVector::Normalize (e);
		}
#if DBG
	if (e.Mag () < I2X (1)/2)
		Int3 ();
#endif
	xSegSize = CFixVector::Dist (gameData.segs.vertices [SEGMENTS [nSegment].m_verts [0]], 
										 gameData.segs.vertices [SEGMENTS [nSegment].m_verts [6]]);
	if (xSegSize > I2X (40))
		xSegSize = I2X (40);
	vGoalPos = ptSegs [i].point + e * (xSegSize/4);
	count = 3;
	while (count) {
		fq.p0					= &ptSegs [i].point;
		fq.startSeg			= ptSegs [i].nSegment;
		fq.p1					= &vGoalPos;
		fq.radP0				=
		fq.radP1				= objP->info.xSize;
		fq.thisObjNum		= objP->Index ();
		fq.ignoreObjList	= NULL;
		fq.flags				= 0;
		fq.bCheckVisibility = false;
		nHitType = FindVectorIntersection (&fq, &hitData);
		if (nHitType == HIT_NONE)
			count = 0;
		else {
			if ((count == 3) && (nHitType == HIT_BAD_P0))
				return;
			vGoalPos[X] = ((*fq.p0)[X] + hitData.hit.vPoint[X])/2;
			vGoalPos[Y] = ((*fq.p0)[Y] + hitData.hit.vPoint[Y])/2;
			vGoalPos[Z] = ((*fq.p0)[Z] + hitData.hit.vPoint[Z])/2;
			if (!--count)	//	Couldn't move towards outside, that's ok, sometimes things can't be moved.
				vGoalPos = ptSegs [i].point;
			}
		}
	//	Only move towards outside if remained inside CSegment.
	nNewSeg = FindSegByPos (vGoalPos, ptSegs [i].nSegment, 1, 0);
	if (nNewSeg == ptSegs [i].nSegment) {
		newPtSegs [i].point = vGoalPos;
		newPtSegs [i].nSegment = nNewSeg;
		}
	else {
		newPtSegs [i].point = ptSegs [i].point;
		newPtSegs [i].nSegment = ptSegs [i].nSegment;
		}
	}
if (j > 1)
	memcpy (ptSegs + 1, newPtSegs + 1, (j - 1) * sizeof (tPointSeg));
}
コード例 #17
0
ファイル: particles.cpp プロジェクト: paud/d2x-xl
int CParticle::Update (int nCurTime)
{
	int			j, nRad;
	short			nSegment;
	fix			t, dot;
	CFixVector	vPos, drift;
	fix			drag = (m_nType == BUBBLE_PARTICLES) ? I2X (1) : F2X ((float) m_nLife / (float) m_nTTL);

if ((m_nLife <= 0) /*|| (m_color [0].alpha < 0.01f)*/)
	return 0;
t = nCurTime - m_nMoved;
if (m_nDelay > 0)
	m_nDelay -= t;
else {
	vPos = m_vPos;
	drift = m_vDrift;
	if ((m_nType == SMOKE_PARTICLES) /*|| (m_nType == BUBBLE_PARTICLES)*/) {
		drift [X] = ChangeDir (drift [X]);
		drift [Y] = ChangeDir (drift [Y]);
		drift [Z] = ChangeDir (drift [Z]);
		}
	for (j = 0; j < 2; j++) {
		if (t < 0)
			t = -t;
		m_vPos = vPos + drift * t; //(I2X (t) / 1000);
		if (m_bHaveDir) {
			CFixVector vi = drift, vj = m_vDir;
			CFixVector::Normalize (vi);
			CFixVector::Normalize (vj);
//				if (CFixVector::Dot (drift, m_vDir) < 0)
			if (CFixVector::Dot (vi, vj) < 0)
				drag = -drag;
//				VmVecScaleInc (&drift, &m_vDir, drag);
			m_vPos += m_vDir * drag;
			}
		if ((m_nType == BUBBLE_PARTICLES) || (m_nTTL - m_nLife > I2X (1) / 16)) {
			nSegment = FindSegByPos (m_vPos, m_nSegment, 0, 0, 1);
			if (nSegment < 0) {
#if DBG
				if (m_nSegment == nDbgSeg)
					nSegment = FindSegByPos (m_vPos, m_nSegment, 1, 0, 1);
#endif
				nSegment = FindSegByPos (m_vPos, m_nSegment, 0, 1, 1);
				if (nSegment < 0)
					return 0;
				}
			if ((m_nType == BUBBLE_PARTICLES) && (SEGMENTS [nSegment].m_nType != SEGMENT_IS_WATER))
				return 0;
			m_nSegment = nSegment;
			}
		if (gameOpts->render.particles.bCollisions && CollideWithWall ()) {	//Reflect the particle
			if (m_nType == BUBBLE_PARTICLES)
				return 0;
			if (j)
				return 0;
			else if (!(dot = CFixVector::Dot (drift, *wallNorm)))
				return 0;
			else {
				drift = m_vDrift + *wallNorm * (-2 * dot);
				//VmVecScaleAdd (&m_vPos, &vPos, &drift, 2 * t);
				m_nBounce = 3;
				continue;
				}
			}
		else if (m_nBounce)
			m_nBounce--;
		else {
			break;
			}
		}
	m_vDrift = drift;
	if (m_nTTL >= 0) {
#if SMOKE_SLOWMO
		m_nLife -= (int) (t / gameStates.gameplay.slowmo [0].fSpeed);
#else
		m_nLife -= t;
#endif
		if ((m_nType == SMOKE_PARTICLES) && (nRad = m_nRad)) {
			if (m_bBlowUp) {
				if (m_nWidth >= nRad)
					m_nRad = 0;
				else {
					m_nWidth += nRad / 10 / m_bBlowUp;
					m_nHeight += nRad / 10 / m_bBlowUp;
					if (m_nWidth > nRad)
						m_nWidth = nRad;
					if (m_nHeight > nRad)
						m_nHeight = nRad;
					m_color [0].alpha *= (1.0f + 0.0725f / m_bBlowUp);
					if (m_color [0].alpha > 1)
						m_color [0].alpha = 1;
					}
				}
			else {
				if (m_nWidth <= nRad)
					m_nRad = 0;
				else {
					m_nRad += nRad / 5;
					m_color [0].alpha *= 1.0725f;
					if (m_color [0].alpha > 1)
						m_color [0].alpha = 1;
					}
				}
			}
		}
	}
m_nMoved = nCurTime;
return 1;
}
コード例 #18
0
ファイル: rendermine.cpp プロジェクト: paud/d2x-xl
void SetRenderView (fix nEyeOffset, short *pnStartSeg, int bOglScale)
{
	short nStartSeg;

gameData.render.mine.viewerEye = gameData.objs.viewerP->info.position.vPos;
if (nEyeOffset) {
	gameData.render.mine.viewerEye += gameData.objs.viewerP->info.position.mOrient.RVec () * nEyeOffset;
	}

externalView.SetPos (NULL);
if (gameStates.render.cameras.bActive) {
	nStartSeg = gameData.objs.viewerP->info.nSegment;
	G3SetViewMatrix (gameData.render.mine.viewerEye, gameData.objs.viewerP->info.position.mOrient, gameStates.render.xZoom, bOglScale);
	}
else {
	if (!pnStartSeg)
		nStartSeg = gameStates.render.nStartSeg;
	else {
		nStartSeg = FindSegByPos (gameData.render.mine.viewerEye, gameData.objs.viewerP->info.nSegment, 1, 0);
		if (!gameStates.render.nWindow && (gameData.objs.viewerP == gameData.objs.consoleP)) {
			externalView.SetPoint (gameData.objs.viewerP);
			if (nStartSeg == -1)
				nStartSeg = gameData.objs.viewerP->info.nSegment;
			}
		}
	if ((gameData.objs.viewerP == gameData.objs.consoleP) && transformation.m_info.bUsePlayerHeadAngles) {
		CFixMatrix mHead, mView;
		mHead = CFixMatrix::Create(transformation.m_info.playerHeadAngles);
		mView = gameData.objs.viewerP->info.position.mOrient * mHead;
		G3SetViewMatrix (gameData.render.mine.viewerEye, mView, gameStates.render.xZoom, bOglScale);
		}
	else if (gameStates.render.bRearView && (gameData.objs.viewerP == gameData.objs.consoleP)) {
#if 1
		CFixMatrix mView;

		mView = gameData.objs.viewerP->info.position.mOrient;
		mView.FVec ().Neg ();
		mView.RVec ().Neg ();
#else
		CFixMatrix mHead, mView;

		transformation.m_info.playerHeadAngles [PA] = 0;
		transformation.m_info.playerHeadAngles [BA] = 0x7fff;
		transformation.m_info.playerHeadAngles [HA] = 0x7fff;
		VmAngles2Matrix (&mHead, &transformation.m_info.playerHeadAngles);
		VmMatMul (&mView, &gameData.objs.viewerP->info.position.mOrient, &mHead);
#endif
		G3SetViewMatrix (gameData.render.mine.viewerEye, mView,  //gameStates.render.xZoom, bOglScale);
							  FixDiv (gameStates.render.xZoom, gameStates.zoom.nFactor), bOglScale);
		}
	else if ((gameData.objs.viewerP == gameData.objs.consoleP) && (!IsMultiGame || gameStates.app.bHaveExtraGameInfo [1])) {
		gameStates.zoom.nMinFactor = I2X (gameStates.render.glAspect); 
		gameStates.zoom.nMaxFactor = gameStates.zoom.nMinFactor * 5;
		HandleZoom ();
		if ((gameData.objs.viewerP == gameData.objs.consoleP) &&
#if DBG
			 gameStates.render.bChaseCam) {
#else
			 gameStates.render.bChaseCam && (!IsMultiGame || IsCoopGame || EGI_FLAG (bEnableCheats, 0, 0, 0))) {
#endif
			externalView.GetViewPoint ();
			G3SetViewMatrix (gameData.render.mine.viewerEye,
								  externalView.GetPos () ? externalView.GetPos ()->mOrient : gameData.objs.viewerP->info.position.mOrient,
								  gameStates.render.xZoom, bOglScale);
			}
		else
			G3SetViewMatrix (gameData.render.mine.viewerEye, gameData.objs.viewerP->info.position.mOrient,
								  FixDiv (gameStates.render.xZoom, gameStates.zoom.nFactor), bOglScale);
		}
コード例 #19
0
int CObject::Create (ubyte nType, ubyte nId, short nCreator, short nSegment,
							const CFixVector& vPos, const CFixMatrix& mOrient,
							fix xSize, ubyte cType, ubyte mType, ubyte rType)
{
#if DBG
if (nType == OBJ_WEAPON) {
	nType = nType;
	if ((nCreator >= 0) && (OBJECTS [nCreator].info.nType == OBJ_ROBOT))
		nType = nType;
	if (nId == FLARE_ID)
		nType = nType;
	if (gameData.objs.bIsMissile [(int) nId])
		nType = nType;
	}
else if (nType == OBJ_ROBOT) {
#if 0
	if (ROBOTINFO ((int) nId).bossFlag && (BOSS_COUNT >= MAX_BOSS_COUNT))
		return -1;
#endif
	}
else if (nType == OBJ_HOSTAGE)
	nType = nType;
else if (nType == OBJ_FIREBALL)
	nType = nType;
else if (nType == OBJ_REACTOR)
	nType = nType;
else if (nType == OBJ_DEBRIS)
	nType = nType;
else if (nType == OBJ_MARKER)
	nType = nType;
else if (nType == OBJ_PLAYER)
	nType = nType;
else if (nType == OBJ_POWERUP)
	nType = nType;
#endif

SetSegment (FindSegByPos (vPos, nSegment, 1, 0));
if ((Segment () < 0) || (Segment () > gameData.segs.nLastSegment))
	return -1;

if (nType == OBJ_DEBRIS) {
	if (gameData.objs.nDebris >= gameStates.render.detail.nMaxDebrisObjects)
		return -1;
	}

// Zero out object structure to keep weird bugs from happening in uninitialized fields.
m_nId = OBJ_IDX (this);
SetSignature (gameData.objs.nNextSignature++);
SetType (nType);
SetId (nId);
SetLastPos (vPos);
SetPos (&vPos);
SetSize (xSize);
SetCreator ((sbyte) nCreator);
SetOrient (&mOrient);
SetControlType (cType);
SetMovementType (mType);
SetRenderType (rType);
SetContainsType (-1);
SetLifeLeft (
	((gameData.app.nGameMode & GM_ENTROPY) && (nType == OBJ_POWERUP) && (nId == POW_HOARD_ORB) && (extraGameInfo [1].entropy.nVirusLifespan > 0)) ?
	I2X (extraGameInfo [1].entropy.nVirusLifespan) : IMMORTAL_TIME);
SetAttachedObj (-1);
m_xCreationTime = gameData.time.xGame;
#if 0
if (GetControlType () == CT_POWERUP)
	CPowerupInfo::SetCount (1);
// Init physics info for this CObject
if (GetMovementType () == MT_PHYSICS)
	m_vStartVel.SetZero ();
if (GetRenderType () == RT_POLYOBJ)
	CPolyObjInfo::SetTexOverride (-1);

if (GetType () == OBJ_WEAPON) {
	CPhysicsInfo::SetFlags (CPhysInfo.GetFlags () | WI_persistent (m_info.nId) * PF_PERSISTENT);
	CLaserInfo::SetCreationTime (gameData.time.xGame);
	CLaserInfo::SetLastHitObj (0);
	CLaserInfo::SetScale (I2X (1));
	}
else if (GetType () == OBJ_DEBRIS)
	gameData.objs.nDebris++;
if (GetControlType () == CT_POWERUP)
	CPowerupInfo::SetCreationTime (gameData.time.xGame);
else if (GetControlType () == CT_EXPLOSION) {
	CAttachedInfo::SetPrev (-1);
	CAttachedInfo::SetNext (-1);
	CAttachedInfo::SetParent (-1);
	}
#endif
Link ();
LinkToSeg (nSegment);
return m_nId;
}
コード例 #20
0
ファイル: aifire.cpp プロジェクト: paud/d2x-xl
// --------------------------------------------------------------------------------------------------------------------
//	Note: Parameter gameData.ai.vVecToPlayer is only passed now because guns which aren't on the forward vector from the
//	center of the robot will not fire right at the player.  We need to aim the guns at the player.  Barring that, we cheat.
//	When this routine is complete, the parameter gameData.ai.vVecToPlayer should not be necessary.
void AIFireLaserAtPlayer (CObject *objP, CFixVector *vFirePoint, int nGun, CFixVector *vBelievedPlayerPos)
{
	short				nShot, nObject = objP->Index ();
	tAILocalInfo	*ailP = gameData.ai.localInfo + nObject;
	tRobotInfo		*botInfoP = &ROBOTINFO (objP->info.nId);
	CFixVector		vFire;
	CFixVector		bpp_diff;
	short				nWeaponType;
	fix				aim, dot;
	int				count, i;

Assert (nObject >= 0);
//	If this robot is only awake because a camera woke it up, don't fire.
if (objP->cType.aiInfo.SUB_FLAGS & SUB_FLAGS_CAMERA_AWAKE)
	return;
if (!gameStates.app.cheats.bRobotsFiring)
	return;
if (objP->info.controlType == CT_MORPH)
	return;
//	If player is exploded, stop firing.
if (gameStates.app.bPlayerExploded)
	return;
if (objP->cType.aiInfo.xDyingStartTime)
	return;		//	No firing while in death roll.
//	Don't let the boss fire while in death roll.  Sorry, this is the easiest way to do this.
//	If you try to key the boss off objP->cType.aiInfo.xDyingStartTime, it will hose the endlevel stuff.
if (ROBOTINFO (objP->info.nId).bossFlag) {
	i = gameData.bosses.Find (nObject);
	if ((i < 0) || (gameData.bosses [i].m_nDyingStartTime))
		return;
	}
//	If CPlayerData is cloaked, maybe don't fire based on how long cloaked and randomness.
if (LOCALPLAYER.flags & PLAYER_FLAGS_CLOAKED) {
	fix	xCloakTime = gameData.ai.cloakInfo [nObject % MAX_AI_CLOAK_INFO].lastTime;
	if ((gameData.time.xGame - xCloakTime > CLOAK_TIME_MAX/4) &&
		 (d_rand () > FixDiv (gameData.time.xGame - xCloakTime, CLOAK_TIME_MAX)/2)) {
		SetNextFireTime (objP, ailP, botInfoP, nGun);
		return;
		}
	}
//	Handle problem of a robot firing through a CWall because its gun tip is on the other
//	CSide of the CWall than the robot's center.  For speed reasons, we normally only compute
//	the vector from the gun point to the player.  But we need to know whether the gun point
//	is separated from the robot's center by a CWall.  If so, don't fire!
if (objP->cType.aiInfo.SUB_FLAGS & SUB_FLAGS_GUNSEG) {
	//	Well, the gun point is in a different CSegment than the robot's center.
	//	This is almost always ok, but it is not ok if something solid is in between.
	int	nGunSeg = FindSegByPos (*vFirePoint, objP->info.nSegment, 1, 0);
	//	See if these segments are connected, which should almost always be the case.
	short nConnSide = SEGMENTS [nGunSeg].ConnectedSide (&SEGMENTS [objP->info.nSegment]);
	if (nConnSide != -1) {
		//	They are connected via nConnSide in CSegment objP->info.nSegment.
		//	See if they are unobstructed.
		if (!(SEGMENTS [objP->info.nSegment].IsDoorWay (nConnSide, NULL) & WID_FLY_FLAG)) {
			//	Can't fly through, so don't let this bot fire through!
			return;
			}
		}
	else {
		//	Well, they are not directly connected, so use FindVectorIntersection to see if they are unobstructed.
		tFVIQuery	fq;
		tFVIData		hit_data;
		int			fate;

		fq.startSeg			= objP->info.nSegment;
		fq.p0					= &objP->info.position.vPos;
		fq.p1					= vFirePoint;
		fq.radP0				=
		fq.radP1				= 0;
		fq.thisObjNum		= objP->Index ();
		fq.ignoreObjList	= NULL;
		fq.flags				= FQ_TRANSWALL;

		fate = FindVectorIntersection (&fq, &hit_data);
		if (fate != HIT_NONE) {
			Int3 ();		//	This bot's gun is poking through a CWall, so don't fire.
			MoveTowardsSegmentCenter (objP);		//	And decrease chances it will happen again.
			return;
			}
		}
	}
//	Set position to fire at based on difficulty level and robot's aiming ability
aim = I2X (FIRE_K) - (FIRE_K-1)* (botInfoP->aim << 8);	//	I2X (1) in bitmaps.tbl = same as used to be.  Worst is 50% more error.
//	Robots aim more poorly during seismic disturbance.
if (gameStates.gameplay.seismic.nMagnitude) {
	fix temp = I2X (1) - abs (gameStates.gameplay.seismic.nMagnitude);
	if (temp < I2X (1)/2)
		temp = I2X (1)/2;
	aim = FixMul (aim, temp);
	}
//	Lead the CPlayerData half the time.
//	Note that when leading the CPlayerData, aim is perfect.  This is probably acceptable since leading is so hacked in.
//	Problem is all robots will lead equally badly.
if (d_rand () < 16384) {
	if (LeadPlayer (objP, vFirePoint, vBelievedPlayerPos, nGun, &vFire))		//	Stuff direction to fire at in vFirePoint.
		goto player_led;
}

dot = 0;
count = 0;			//	Don't want to sit in this loop foreverd:\temp\dm_test.
i = (NDL - gameStates.app.nDifficultyLevel - 1) * 4;
while ((count < 4) && (dot < I2X (1)/4)) {
	bpp_diff[X] = (*vBelievedPlayerPos)[X] + FixMul ((d_rand ()-16384) * i, aim);
	bpp_diff[Y] = (*vBelievedPlayerPos)[Y] + FixMul ((d_rand ()-16384) * i, aim);
	bpp_diff[Z] = (*vBelievedPlayerPos)[Z] + FixMul ((d_rand ()-16384) * i, aim);
	CFixVector::NormalizedDir(vFire, bpp_diff, *vFirePoint);
	dot = CFixVector::Dot (objP->info.position.mOrient.FVec (), vFire);
	count++;
	}

player_led:

nWeaponType = botInfoP->nWeaponType;
if ((botInfoP->nSecWeaponType != -1) && ((nWeaponType < 0) || !nGun))
	nWeaponType = botInfoP->nSecWeaponType;
if (nWeaponType < 0)
	return;
if (0 > (nShot = CreateNewLaserEasy (&vFire, vFirePoint, objP->Index (), (ubyte) nWeaponType, 1)))
	return;

lightClusterManager.AddForAI (objP, nObject, nShot);
objP->Shots ().nObject = nShot;
objP->Shots ().nSignature = OBJECTS [nShot].info.nSignature;

if (IsMultiGame) {
	AIMultiSendRobotPos (nObject, -1);
	MultiSendRobotFire (nObject, objP->cType.aiInfo.CURRENT_GUN, &vFire);
	}
#if 1
if (++(objP->cType.aiInfo.CURRENT_GUN) >= botInfoP->nGuns) {
	if ((botInfoP->nGuns == 1) || (botInfoP->nSecWeaponType == -1))
		objP->cType.aiInfo.CURRENT_GUN = 0;
	else
		objP->cType.aiInfo.CURRENT_GUN = 1;
	}
#endif
CreateAwarenessEvent (objP, PA_NEARBY_ROBOT_FIRED);
SetNextFireTime (objP, ailP, botInfoP, nGun);
}
コード例 #21
0
ファイル: omega.cpp プロジェクト: paud/d2x-xl
void CreateOmegaBlobs (short nFiringSeg, CFixVector *vMuzzle, CFixVector *vTargetPos, CObject *parentObjP, CObject *targetObjP)
{
	short			nLastSeg, nLastCreatedObj = -1;
	CFixVector	vGoal;
	fix			xGoalDist;
	int			nOmegaBlobs;
	fix			xOmegaBlobDist;
	CFixVector	vOmegaDelta;
	CFixVector	vBlobPos, vPerturb;
	fix			xPerturbArray [MAX_OMEGA_BLOBS];
	int			i;

if (IsMultiGame)
	DeleteOldOmegaBlobs (parentObjP);
omegaLightnings.Create (vTargetPos, parentObjP, targetObjP);
vGoal = *vTargetPos - *vMuzzle;
xGoalDist = CFixVector::Normalize (vGoal);
if (xGoalDist < MIN_OMEGA_BLOBS * MIN_OMEGA_DIST) {
	xOmegaBlobDist = MIN_OMEGA_DIST;
	nOmegaBlobs = xGoalDist / xOmegaBlobDist;
	if (nOmegaBlobs == 0)
		nOmegaBlobs = 1;
	}
else {
	xOmegaBlobDist = DESIRED_OMEGA_DIST;
	nOmegaBlobs = xGoalDist / xOmegaBlobDist;
	if (nOmegaBlobs > MAX_OMEGA_BLOBS) {
		nOmegaBlobs = MAX_OMEGA_BLOBS;
		xOmegaBlobDist = xGoalDist / nOmegaBlobs;
		}
	else if (nOmegaBlobs < MIN_OMEGA_BLOBS) {
		nOmegaBlobs = MIN_OMEGA_BLOBS;
		xOmegaBlobDist = xGoalDist / nOmegaBlobs;
		}
	}
vOmegaDelta = vGoal;
vOmegaDelta *= xOmegaBlobDist;
//	Now, create all the blobs
vBlobPos = *vMuzzle;
nLastSeg = nFiringSeg;

//	If nearby, don't perturb vector.  If not nearby, start halfway out.
if (xGoalDist < MIN_OMEGA_DIST * 4) {
	for (i = 0; i < nOmegaBlobs; i++)
		xPerturbArray [i] = 0;
	}
else {
	vBlobPos += vOmegaDelta * (I2X (1) / 2);	//	Put first blob half way out.
	for (i = 0; i < nOmegaBlobs / 2; i++) {
		xPerturbArray [i] = I2X (i) + I2X (1) / 4;
		xPerturbArray [nOmegaBlobs - 1 - i] = I2X (i);
		}
	}

//	Create Random perturbation vector, but favor _not_ going up in CPlayerData's reference.
vPerturb = CFixVector::Random ();
vPerturb += parentObjP->info.position.mOrient.UVec () * (-I2X (1) / 2);
for (i = 0; i < nOmegaBlobs; i++) {
	CFixVector	vTempPos;
	short			nBlobObj, nSegment;

	//	This will put the last blob right at the destination CObject, causing damage.
	if (i == nOmegaBlobs - 1)
		vBlobPos += vOmegaDelta * (I2X (15) / 32);	//	Move last blob another (almost) half section
	//	Every so often, re-perturb blobs
	if (i % 4 == 3) {
		CFixVector	vTemp;

		vTemp = CFixVector::Random ();
		vPerturb += vTemp * (I2X (1) / 4);
		}
	vTempPos = vBlobPos + vPerturb * xPerturbArray[i];
	nSegment = FindSegByPos (vTempPos, nLastSeg, 1, 0);
	if (nSegment != -1) {
		CObject		*objP;

		nLastSeg = nSegment;
		nBlobObj = CreateWeapon (OMEGA_ID, -1, nSegment, vTempPos, 0, RT_WEAPON_VCLIP);
		if (nBlobObj == -1)
			break;
		nLastCreatedObj = nBlobObj;
		objP = OBJECTS + nBlobObj;
		objP->info.xLifeLeft = ONE_FRAME_TIME;
		objP->mType.physInfo.velocity = vGoal;
		//	Only make the last one move fast, else multiple blobs might collide with target.
		objP->mType.physInfo.velocity *= (I2X (4));
		objP->info.xSize = gameData.weapons.info [objP->info.nId].blob_size;
		objP->info.xShields = FixMul (OMEGA_DAMAGE_SCALE*gameData.time.xFrame, WI_strength (objP->info.nId,gameStates.app.nDifficultyLevel));
		objP->cType.laserInfo.parent.nType = parentObjP->info.nType;
		objP->cType.laserInfo.parent.nSignature = parentObjP->info.nSignature;
		objP->cType.laserInfo.parent.nObject = OBJ_IDX (parentObjP);
		objP->info.movementType = MT_NONE;	//	Only last one moves, that will get bashed below.
		}
	vBlobPos += vOmegaDelta;
	}

	//	Make last one move faster, but it's already moving at speed = I2X (4).
if (nLastCreatedObj != -1) {
	OBJECTS [nLastCreatedObj].mType.physInfo.velocity *=
		gameData.weapons.info [OMEGA_ID].speed [gameStates.app.nDifficultyLevel]/4;
	OBJECTS [nLastCreatedObj].info.movementType = MT_PHYSICS;
	}
}