// ----------------------------------------------------------------------------------------------------------- // 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); }
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); } } }
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 ()); }
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; }
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; }
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; }
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); }
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); }
// -------------------------------------------------------------------------------------------------------------------- // 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; }
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; }
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 }
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); } }
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; }
//------------------------------------------------------------------------------ //Tries to find a CSegment for an CObject, using FindSegByPos () int CObject::FindSegment (void) { return FindSegByPos (info.position.vPos, info.nSegment, 1, 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; }
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)); }
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; }
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); }
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; }
// -------------------------------------------------------------------------------------------------------------------- // 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); }
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; } }