Beispiel #1
0
void FinishSpawningItem( gentity_t *ent ) {
	trace_t		tr;
	vec3_t		dest;
	gitem_t		*item;
	int			itemNum;

	itemNum=1;
	for ( item = bg_itemlist + 1 ; item->classname ; item++,itemNum++) 
	{
		if (!strcmp(item->classname,ent->classname))
		{
			break;
		}
	}

	// Set bounding box for item
	VectorSet( ent->mins, item->mins[0],item->mins[1] ,item->mins[2]);
	VectorSet( ent->maxs, item->maxs[0],item->maxs[1] ,item->maxs[2]);

	if ((!ent->mins[0] && !ent->mins[1] && !ent->mins[2]) && 
		(!ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2]))
	{
		VectorSet (ent->mins, -ITEM_RADIUS, -ITEM_RADIUS, -2);//to match the comments in the items.dat file!
		VectorSet (ent->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
	}

	if ((item->quantity) && (item->giType == IT_AMMO))
	{
		ent->count = item->quantity;
	}

	if ((item->quantity) && (item->giType == IT_BATTERY))
	{
		ent->count = item->quantity; 
	}


//	if ( item->giType == IT_WEAPON ) // NOTE: james thought it was ok to just always do this?
	{
		ent->s.radius = 20;
		VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f );
		gi.G2API_InitGhoul2Model( ent->ghoul2, ent->item->world_model, G_ModelIndex( ent->item->world_model ), NULL, NULL, 0, 0);
	}

	// Set crystal ammo amount based on skill level
/*	if ((itemNum == ITM_AMMO_CRYSTAL_BORG) ||
		(itemNum == ITM_AMMO_CRYSTAL_DN) ||
		(itemNum == ITM_AMMO_CRYSTAL_FORGE) ||
		(itemNum == ITM_AMMO_CRYSTAL_SCAVENGER) ||
		(itemNum == ITM_AMMO_CRYSTAL_STASIS))
	{
		CrystalAmmoSettings(ent);
	}
*/
	ent->s.eType = ET_ITEM;
	ent->s.modelindex = ent->item - bg_itemlist;		// store item number in modelindex
	ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item

	ent->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_BODY;//CONTENTS_TRIGGER|
	ent->e_TouchFunc = touchF_Touch_Item;
	// useing an item causes it to respawn
	ent->e_UseFunc = useF_Use_Item;
	ent->svFlags |= SVF_PLAYER_USABLE;//so player can pick it up

	// Hang in air?
	ent->s.origin[2] += 1;//just to get it off the damn ground because coplanar = insolid
	if ( ent->spawnflags & ITMSF_SUSPEND) 
	{
		// suspended
		G_SetOrigin( ent, ent->s.origin );
	} 
	else 
	{
		// drop to floor
		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], MIN_WORLD_COORD );
		gi.trace( &tr, ent->s.origin, ent->mins, ent->maxs, dest, ent->s.number, MASK_SOLID|CONTENTS_PLAYERCLIP, G2_NOCOLLIDE, 0 );
		if ( tr.startsolid ) 
		{
			if ( &g_entities[tr.entityNum] != NULL )
			{
				gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin), g_entities[tr.entityNum].classname );
			}
			else
			{
				gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin) );
			}
			assert( 0 && "item starting in solid");
#ifndef FINAL_BUILD
			if (!g_entities[ENTITYNUM_WORLD].s.radius){	//not a region
				delayedShutDown = level.time + 100;
			}
#endif
			G_FreeEntity( ent );
			return;
		}

		// allow to ride movers
		ent->s.groundEntityNum = tr.entityNum;

		G_SetOrigin( ent, tr.endpos );
	}

/* ? don't need this
	// team slaves and targeted items aren't present at start
	if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
		ent->s.eFlags |= EF_NODRAW;
		ent->contents = 0;
		return;
	}
*/
	if ( ent->spawnflags & ITMSF_INVISIBLE ) // invisible
	{
		ent->s.eFlags |= EF_NODRAW;
		ent->contents = 0;
	}

	if ( ent->spawnflags & ITMSF_NOTSOLID ) // not solid
	{
		ent->contents = 0;
	}

	gi.linkentity (ent);
}
Beispiel #2
0
/*QUAKED target_gravity_change (1 0 0) (-4 -4 -4) (4 4 4) GLOBAL

"gravity" - Normal = 800, Valid range: any

GLOBAL - Apply to entire world, not just the activator
*/
void SP_target_gravity_change( gentity_t *self )
{
	G_SetOrigin( self, self->s.origin );
	G_SpawnFloat( "gravity", "0", &self->speed );
	self->e_UseFunc = useF_target_gravity_change_use;
}
/*
================
FinishSpawningItem

Traces down to find where an item should rest, instead of letting them
free fall from their spawn points
================
*/
void FinishSpawningItem( gentity_t *ent ) {
	trace_t		tr;
	vec3_t		dest;

	VectorSet( ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS );
	VectorSet( ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );

	ent->s.eType = ET_ITEM;
	ent->s.modelindex = ent->item - bg_itemlist;		// store item number in modelindex
	ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item

	ent->r.contents = CONTENTS_TRIGGER;
	ent->touch = Touch_Item;
	// useing an item causes it to respawn
	ent->use = Use_Item;

	if ( ent->spawnflags & 1 ) {
		// suspended
		G_SetOrigin( ent, ent->s.origin );
	} else {
		// drop to floor
		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
		if ( tr.startsolid ) {
			G_Printf ("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
			G_FreeEntity( ent );
			return;
		}

		// allow to ride movers
		ent->s.groundEntityNum = tr.entityNum;

		G_SetOrigin( ent, tr.endpos );
	}

	// team slaves and targeted items aren't present at start
	if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) {
		ent->s.eFlags |= EF_NODRAW;
		ent->r.contents = 0;
		return;
	}

	// powerups don't spawn in for a while
	if ( ent->item->giType == IT_POWERUP ) {
		float	respawn;

		respawn = 45 + crandom() * 15;
		ent->s.eFlags |= EF_NODRAW;
		ent->r.contents = 0;
		ent->nextthink = level.time + respawn * 1000;
		ent->think = RespawnItem;
		return;
	}

	if ( ent->item->giType == IT_HOLDABLE ) {
		if ( ( ent->item->giTag == HI_BAMBAM ) && ( g_gametype.integer != GT_CTF ) ) {
			return;
		}
		else if ( ( ent->item->giTag == HI_BOOMIES ) &&
		          ( ( g_gametype.integer != GT_CTF ) && ( g_gametype.integer != GT_BALLOON ) ) ) {
			return;
		}
	}


	trap_LinkEntity (ent);
}
Beispiel #4
0
void	CRailTrack::Update()
{
	mNextUpdateTime = level.time + mNextUpdateDelay;


	// Now, Attempt To Add A Number Of Movers To The Track
	//-----------------------------------------------------
	int		attempt;
	int		startCol;
	int		stopCol;
	int		atCol;
	int		testColIndex;

	for (attempt=0; attempt<mNumMoversPerRow; attempt++)
	{
		// Randomly Select A Mover And Test To See If It Is Active
		//---------------------------------------------------------
		CRailMover*	mover = mMovers[Q_irand(0, mMovers.size()-1)];
		if (mover->Active())
		{
			continue;
		}

		// Don't Spawn Until Start Time Has Expired
		//------------------------------------------
		if (level.time < ((mover->mLane)?(mover->mLane->mStartTime):(mStartTime)))
		{
			continue;
		}

		// If Center Locked, Stop Spawning Center Track Movers
		//-----------------------------------------------------
		if (mover->mCenter && mCenterLocked)
		{
			continue;
		}
	

		// Restrict It To A Lane
		//-----------------------
		if (mover->mLane)
		{
			startCol	= mover->mLane->mMinCol;
			stopCol		= mover->mLane->mMaxCol+1;
		}

		// Or Let It Go Anywhere On The Track
		//------------------------------------
		else
		{
			startCol	= 0;
			stopCol		= mCols;
		}
		stopCol -= (mover->mCols-1);


		// If The Mover Is Too Big To Fit In The Lane, Go On To Next Attempt
		//-------------------------------------------------------------------
		if (stopCol<=startCol)
		{
			assert(0);	// Should Not Happen
			continue;
		}

		// Force It To Center
		//--------------------
		if (mover->mCenter && stopCol!=(startCol+1))
		{
			startCol	= ((mCols/2) - (mover->mCols/2));
			stopCol		= startCol+1;
		}


		// Construct A List Of Columns To Test For Insertion
		//---------------------------------------------------
		mTestCols.clear();
		for (int i=startCol; i<stopCol; i++)
		{
			mTestCols.push_back(i);
		}

		// Now Try All The Cols To See If The Building Can Fit
		//-----------------------------------------------------
		while (!mTestCols.empty())
		{
			// Randomly Pick A Column, Then Remove It From The Vector
			//--------------------------------------------------------
			testColIndex = Q_irand(0, mTestCols.size()-1);
			atCol = mTestCols[testColIndex];
			mTestCols.erase_swap(testColIndex);

			if (TestMoverInCells(mover, atCol))
			{
				// Ok, We've Found A Safe Column To Insert This Mover
				//----------------------------------------------------
				InsertMoverInCells(mover, atCol);

				// Now Transport The Actual Mover Entity Into Position, Link It & Send It Off
				//----------------------------------------------------------------------------
				CVec3	StartPos(mGridBottomLeftCorner);
				StartPos[mWAxis] += ((atCol * mGridCellSize) + ((mover->mCols/2.0f) * mGridCellSize));
				StartPos[mHAxis] += (((mover->mRows/2.0f) * mGridCellSize) * ((mNegative)?(1):(-1)));
				StartPos[2] = 0;

				// If Centered, Actually Put It At EXACTLY The Right Position On The Width Axis
				//------------------------------------------------------------------------------
				if (mover->mCenter)
				{
					StartPos[mWAxis] = mGridCenter[mWAxis];
					float	deltaOffset = mGridCenter[mWAxis] - mover->mOriginOffset[mWAxis];
					if (deltaOffset<(mGridCellSize*0.5f) )
					{
						StartPos[mWAxis] -= deltaOffset;
					}
				}

				StartPos -= mover->mOriginOffset;
				G_SetOrigin(mover->mEnt, StartPos.v);

				// Start It Moving
				//-----------------
				VectorCopy(StartPos.v, mover->mEnt->s.pos.trBase);
				VectorCopy(mVelocity.v, mover->mEnt->s.pos.trDelta);
				mover->mEnt->s.pos.trTime		= level.time;
				mover->mEnt->s.pos.trDuration	= mTravelTimeMilliseconds + (mNextUpdateDelay*mover->mRows);
				mover->mEnt->s.pos.trType		= TR_LINEAR_STOP;
				mover->mEnt->s.eFlags			&= ~EF_NODRAW;

				mover->mSoundPlayed				= false;


				// Successfully Inserted This Mover.  Now Move On To The Next Mover
				//------------------------------------------------------------------
				break;
			}
		}
	}

	// Incriment The Current Row
	//---------------------------
	mRow++;
	if (mRow>=mRows)
	{
		mRow = 0;
	}

	// Erase The Erase Row
	//---------------------
	int	EraseRow = mRow - MAX_ROW_HISTORY;
	if (EraseRow<0)
	{
		EraseRow += mRows;
	}
	for (int col=0; col<mCols; col++)
	{
		mCells.get(col, EraseRow) = 0;
	}
}
Beispiel #5
0
/*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
Used as a positional target for in-game calculation, like jumppad targets.
info_notnull does the same thing
*/
void SP_target_position( gentity_t *self ){
	G_SetOrigin( self, self->s.origin );
}
Beispiel #6
0
/**
 * @brief SP_target_smoke
 * @param[in,out] ent
 */
void SP_target_smoke(gentity_t *ent)
{
	char *buffer;

	if (G_SpawnString("shader", "", &buffer))
	{
		ent->s.modelindex2 = G_ShaderIndex(buffer);
	}
	else
	{
		ent->s.modelindex2 = 0;
	}

	// modified this a lot to be sent to the client as one entity and then is shown at the client
	if (ent->delay == 0.f)
	{
		ent->delay = 100;
	}

	ent->use = smoke_toggle;

	ent->think     = smoke_init;
	ent->nextthink = level.time + FRAMETIME;

	G_SetOrigin(ent, ent->s.origin);
	ent->r.svFlags = 0;
	ent->s.eType   = ET_SMOKER;

	if (ent->spawnflags & 2)
	{
		ent->s.density = 4;
	}
	else
	{
		ent->s.density = 0;
	}

	// using "time"
	ent->s.time = ent->speed;
	if (!ent->s.time)
	{
		ent->s.time = 5000; // 5 seconds

	}
	ent->s.time2 = ent->duration;
	if (!ent->s.time2)
	{
		ent->s.time2 = 2000;
	}

	ent->s.angles2[0] = ent->start_size;
	if (ent->s.angles2[0] == 0.f)
	{
		ent->s.angles2[0] = 24;
	}

	ent->s.angles2[1] = ent->end_size;
	if (ent->s.angles2[1] == 0.f)
	{
		ent->s.angles2[1] = 96;
	}

	ent->s.angles2[2] = ent->wait;
	if (ent->s.angles2[2] == 0.f)
	{
		ent->s.angles2[2] = 50;
	}

	// idiot check
	if (ent->s.time < ent->s.time2)
	{
		ent->s.time = ent->s.time2 + 100;
	}

	if (ent->spawnflags & 8)
	{
		ent->s.frame = 1;
	}

	ent->s.dl_intensity  = ent->health;
	ent->s.constantLight = ent->delay;

	if (ent->spawnflags & 4)
	{
		trap_LinkEntity(ent);
	}
}
Beispiel #7
0
gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) {
	trace_t		tr;
	vec3_t		dest;
	gentity_t	*ent;

	ent = G_Spawn();

	VectorCopy( origin, ent->s.origin );
	VectorCopy( origin, ent->s.pos.trBase );
	VectorCopy( origin, ent->r.currentOrigin );

	VectorSet( ent->r.mins, -15, -15, 0 );
	VectorSet( ent->r.maxs, 15, 15, 87 );

	ent->s.eType = ET_GENERAL;
	ent->flags = FL_NO_KNOCKBACK;

	if( g_gametype.integer == GT_OBELISK ) {
		ent->r.contents = CONTENTS_SOLID;
		ent->takedamage = qtrue;
		ent->health = g_obeliskHealth.integer;
		ent->die = ObeliskDie;
		ent->pain = ObeliskPain;
		ent->think = ObeliskRegen;
		ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000;
	}
	if( g_gametype.integer == GT_HARVESTER ) {
		ent->r.contents = CONTENTS_TRIGGER;
		ent->touch = ObeliskTouch;
	}

	if ( spawnflags & 1 ) {
		// suspended
		G_SetOrigin( ent, ent->s.origin );
	} else {
		// mappers like to put them exactly on the floor, but being coplanar
		// will sometimes show up as starting in solid, so lif it up one pixel
		ent->s.origin[2] += 1;

		// drop to floor
		VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
		trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
		if ( tr.startsolid ) {
			ent->s.origin[2] -= 1;
			G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) );

			ent->s.groundEntityNum = ENTITYNUM_NONE;
			G_SetOrigin( ent, ent->s.origin );
		}
		else {
			// allow to ride movers
			ent->s.groundEntityNum = tr.entityNum;
			G_SetOrigin( ent, tr.endpos );
		}
	}

	ent->spawnflags = team;

	trap_LinkEntity( ent );

	return ent;
}
Beispiel #8
0
/**
* @brief Traces down to find where an item should rest, instead of letting them
*        free fall from their spawn points.
*/
void FinishSpawningItem(gentity_t *ent)
{
    trace_t tr;
    vec3_t  dest;
    vec3_t  maxs;

    if (ent->spawnflags & 1)     // suspended
    {
        VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
        VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
        VectorCopy(ent->r.maxs, maxs);
    }
    else
    {
        // had to modify this so that items would spawn in shelves
        VectorSet(ent->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0);
        VectorSet(ent->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
        VectorCopy(ent->r.maxs, maxs);
        maxs[2] /= 2;
    }

    ent->r.contents   = CONTENTS_TRIGGER | CONTENTS_ITEM;
    ent->touch        = Touch_Item_Auto;
    ent->s.eType      = ET_ITEM;
    ent->s.modelindex = ent->item - bg_itemlist;        // store item number in modelindex

    ent->s.otherEntityNum2 = 0;     // takes modelindex2's place in signaling a dropped item
    // we don't use this (yet, anyway) so I'm taking it so you can specify a model for treasure items and clipboards
    //ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item
    if (ent->model)
    {
        ent->s.modelindex2 = G_ModelIndex(ent->model);
    }

    // using an item causes it to respawn
    ent->use = Use_Item;

    // moved this up so it happens for suspended items too (and made it a function)
    G_SetAngle(ent, ent->s.angles);

    if (ent->spawnflags & 1)        // suspended
    {
        G_SetOrigin(ent, ent->s.origin);
    }
    else
    {
        VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096);
        trap_Trace(&tr, ent->s.origin, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID);

        if (tr.startsolid)
        {
            vec3_t temp;

            VectorCopy(ent->s.origin, temp);
            temp[2] -= ITEM_RADIUS;

            VectorSet(dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096);
            trap_Trace(&tr, temp, ent->r.mins, maxs, dest, ent->s.number, MASK_SOLID);
        }

        if (tr.startsolid)
        {
            G_Printf("FinishSpawningItem: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin));
            G_FreeEntity(ent);
            return;
        }

        // allow to ride movers
        ent->s.groundEntityNum = tr.entityNum;

        G_SetOrigin(ent, tr.endpos);
    }

    if (ent->spawnflags & 2)          // spin
    {
        ent->s.eFlags |= EF_SPINNING;
    }

    // team slaves and targeted items aren't present at start
    if ((ent->flags & FL_TEAMSLAVE) || ent->targetname)
    {
        ent->flags |= FL_NODRAW;
        //ent->s.eFlags |= EF_NODRAW;
        ent->r.contents = 0;
        return;
    }

    // health/ammo can potentially be multi-stage (multiple use)
    if (ent->item->giType == IT_HEALTH || ent->item->giType == IT_AMMO)
    {
        int i;

        // having alternate models defined in bg_misc.c for a health or ammo item specify it as "multi-stage"
        // - left-hand operand of comma expression has no effect
        // initial line: for(i=0;i<4,ent->item->world_model[i];i++) {}
        for (i = 0; i < 4 && ent->item->world_model[i] ; i++)
        {
        }

        ent->s.density = i - 1;   // store number of stages in 'density' for client (most will have '1')
    }

    trap_LinkEntity(ent);
}
Beispiel #9
0
/**
* @brief Spawns an item and tosses it forward.
*/
gentity_t *LaunchItem(gitem_t *item, vec3_t origin, vec3_t velocity, int ownerNum)
{
    gentity_t *dropped = G_Spawn();
    trace_t   tr;
    vec3_t    vec, temp;

    dropped->s.eType           = ET_ITEM;
    dropped->s.modelindex      = item - bg_itemlist; // store item number in modelindex
    dropped->s.otherEntityNum2 = 1; // this is taking modelindex2's place for a dropped item

    dropped->classname = item->classname;
    dropped->item      = item;
    VectorSet(dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0);              // so items sit on the ground
    VectorSet(dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, 2 * ITEM_RADIUS);    // so items sit on the ground
    dropped->r.contents = CONTENTS_TRIGGER | CONTENTS_ITEM;

    dropped->clipmask = CONTENTS_SOLID | CONTENTS_MISSILECLIP;      // fix for items falling through grates

    dropped->touch = Touch_Item_Auto;

    trap_Trace(&tr, origin, dropped->r.mins, dropped->r.maxs, origin, ownerNum, MASK_SOLID);
    if (tr.startsolid)
    {
        int i;

        VectorSubtract(g_entities[ownerNum].s.origin, origin, temp);
        VectorNormalize(temp);

        for (i = 16; i <= 48; i += 16)
        {
            VectorScale(temp, i, vec);
            VectorAdd(origin, vec, origin);

            trap_Trace(&tr, origin, dropped->r.mins, dropped->r.maxs, origin, ownerNum, MASK_SOLID);
            if (!tr.startsolid)
            {
                break;
            }
        }
    }

    G_SetOrigin(dropped, origin);
    dropped->s.pos.trType = TR_GRAVITY;
    dropped->s.pos.trTime = level.time;
    VectorCopy(velocity, dropped->s.pos.trDelta);

    // set yaw to parent angles
    temp[PITCH] = 0;
    temp[YAW]   = g_entities[ownerNum].s.apos.trBase[YAW];
    temp[ROLL]  = 0;
    G_SetAngle(dropped, temp);

    dropped->s.eFlags |= EF_BOUNCE_HALF;

    if (item->giType == IT_TEAM)     // Special case for CTF flags
    {
        gentity_t *flag = &g_entities[g_entities[ownerNum].client->flagParent];

        dropped->s.otherEntityNum = g_entities[ownerNum].client->flagParent;    // store the entitynum of our original flag spawner
        dropped->s.density        = 1;
        dropped->think            = Team_DroppedFlagThink;
        dropped->nextthink        = level.time + 30000;

        if (level.gameManager)
        {
            G_Script_ScriptEvent(level.gameManager, "trigger", flag->item->giTag == PW_REDFLAG ? "allied_object_dropped" : "axis_object_dropped");
        }
        G_Script_ScriptEvent(flag, "trigger", "dropped");
    }
    else     // auto-remove after 30 seconds
    {
        dropped->think     = G_FreeEntity;
        dropped->nextthink = level.time + 30000;
    }

    dropped->flags = FL_DROPPED_ITEM;

    trap_LinkEntity(dropped);

    return dropped;
}
Beispiel #10
0
/*
=============
SpawnCorpse

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
void SpawnCorpse( gentity_t *ent )
{
  gentity_t   *body;
  int         contents;
  vec3_t      origin, dest;
  trace_t     tr;
  float       vDiff;

  //just return right away so bodies never appear and its cleaner + faster
  return;
  VectorCopy( ent->r.currentOrigin, origin );

  trap_UnlinkEntity( ent );

  // if client is in a nodrop area, don't leave the body
  contents = trap_PointContents( origin, -1 );
  if( contents & CONTENTS_NODROP )
    return;

  body = G_Spawn( );

  VectorCopy( ent->s.apos.trBase, body->s.angles );
  body->s.eFlags = EF_DEAD;
  body->s.eType = ET_CORPSE;
  body->s.number = body - g_entities;
  body->timestamp = level.time;
  body->s.event = 0;
  body->r.contents = CONTENTS_CORPSE;
  body->s.clientNum = ent->client->ps.stats[ STAT_PCLASS ];
  body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;

  if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
    body->classname = "humanCorpse";
  else
    body->classname = "alienCorpse";

  body->s.powerups = MAX_CLIENTS;

  body->think = BodySink;
  body->nextthink = level.time + 20000;

  body->s.legsAnim = ent->s.legsAnim;

  if( !body->nonSegModel )
  {
    switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
    {
      case BOTH_DEATH1:
      case BOTH_DEAD1:
        body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
        break;
      case BOTH_DEATH2:
      case BOTH_DEAD2:
        body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
        break;
      case BOTH_DEATH3:
      case BOTH_DEAD3:
      default:
        body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
        break;
    }
  }
  else
  {
    switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
    {
      case NSPA_DEATH1:
      case NSPA_DEAD1:
        body->s.legsAnim = NSPA_DEAD1;
        break;
      case NSPA_DEATH2:
      case NSPA_DEAD2:
        body->s.legsAnim = NSPA_DEAD2;
        break;
      case NSPA_DEATH3:
      case NSPA_DEAD3:
      default:
        body->s.legsAnim = NSPA_DEAD3;
        break;
    }
  }

  body->takedamage = qfalse;

  body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
  ent->health = 0;

  //change body dimensions
  BG_FindBBoxForClass( ent->client->ps.stats[ STAT_PCLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs );
  vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ];

  //drop down to match the *model* origins of ent and body
  VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff );
  trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask );
  VectorCopy( tr.endpos, origin );

  G_SetOrigin( body, origin );
  VectorCopy( origin, body->s.origin );
  body->s.pos.trType = TR_GRAVITY;
  body->s.pos.trTime = level.time;
  VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );

  VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
  trap_LinkEntity( body );
}
Beispiel #11
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gclient_t	*client;
	int		i;
	clientPersistant_t	saved;
	clientSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
//	char	userinfo[MAX_INFO_STRING];
	forcedata_t			savedForce;
	void		*ghoul2save;
	int		saveSaberNum = ENTITYNUM_NONE;
	int		wDisable = 0;

	index = ent - g_entities;
	client = ent->client;

	if (client->ps.fd.forceDoInit)
	{ //force a reread of force powers
		WP_InitForcePowers( ent );
		client->ps.fd.forceDoInit = 0;
	}
	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) {
		// all base oriented team games use the CTF spawn points
		spawnPoint = SelectCTFSpawnPoint ( 
						client->sess.sessionTeam, 
						client->pers.teamState.state, 
						spawn_origin, spawn_angles);
	}
	else if (g_gametype.integer == GT_SAGA)
	{
		spawnPoint = SelectSagaSpawnPoint ( 
						client->sess.sessionTeam, 
						client->pers.teamState.state, 
						spawn_origin, spawn_angles);
	}
	else {
		do {
			// the first spawn should be at a good looking spot
			if ( !client->pers.initialSpawn && client->pers.localClient ) {
				client->pers.initialSpawn = qtrue;
				spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles );
			} else {
				// don't spawn near existing origin if possible
				spawnPoint = SelectSpawnPoint ( 
					client->ps.origin, 
					spawn_origin, spawn_angles);
			}

			// Tim needs to prevent bots from spawning at the initial point
			// on q3dm0...
			if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}
			// just to be symetric, we have a nohumans option...
			if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}

			break;

		} while ( 1 );
	}
	client->pers.teamState.state = TEAM_ACTIVE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
//	savedAreaBits = client->areabits;
	accuracy_hits = client->accuracy_hits;
	accuracy_shots = client->accuracy_shots;
	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		persistant[i] = client->ps.persistant[i];
	}
	eventSequence = client->ps.eventSequence;

	savedForce = client->ps.fd;

	ghoul2save = client->ghoul2;

	saveSaberNum = client->ps.saberEntityNum;

	memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset?

	//rww - Don't wipe the ghoul2 instance or the animation data
	client->ghoul2 = ghoul2save;

	//or the saber ent num
	client->ps.saberEntityNum = saveSaberNum;

	client->ps.fd = savedForce;

	client->ps.duelIndex = ENTITYNUM_NONE;

	client->pers = saved;
	client->sess = savedSess;
	client->ps.ping = savedPing;
//	client->areabits = savedAreaBits;
	client->accuracy_hits = accuracy_hits;
	client->accuracy_shots = accuracy_shots;
	client->lastkilled_client = -1;

	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		client->ps.persistant[i] = persistant[i];
	}
	client->ps.eventSequence = eventSequence;
	// increment the spawncount so the client will detect the respawn
	client->ps.persistant[PERS_SPAWN_COUNT]++;
	client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;

	client->airOutTime = level.time + 12000;

//	trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
	// set max health
	client->pers.maxHealth = 100;//atoi( Info_ValueForKey( userinfo, "handicap" ) );
	if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
		client->pers.maxHealth = 100;
	}
	// clear entity values
	client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
	client->ps.eFlags = flags;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->r.contents = CONTENTS_BODY;
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	
	VectorCopy (playerMins, ent->r.mins);
	VectorCopy (playerMaxs, ent->r.maxs);

	client->ps.clientNum = index;
	//give default weapons
	client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );

	if (g_gametype.integer == GT_HOLOCRON)
	{
		//always get free saber level 1 in holocron
		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );	//these are precached in g_items, ClearRegisteredItems()
	}
	else
	{
		if (client->ps.fd.forcePowerLevel[FP_SABERATTACK])
		{
			client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );	//these are precached in g_items, ClearRegisteredItems()
		}
		else
		{ //if you don't have saber attack rank then you don't get a saber
			client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
		}
	}

	if (g_gametype.integer == GT_TOURNAMENT)
	{
		wDisable = g_duelWeaponDisable.integer;
	}
	else
	{
		wDisable = g_weaponDisable.integer;
	}

	if (!wDisable || !(wDisable & (1 << WP_BRYAR_PISTOL)))
	{
		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
	}
	else if (g_gametype.integer == GT_JEDIMASTER)
	{
		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
	}

	if (g_gametype.integer == GT_JEDIMASTER)
	{
		client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
		client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
	}

	if (client->ps.stats[STAT_WEAPONS] & (1 << WP_BRYAR_PISTOL))
	{
		client->ps.weapon = WP_BRYAR_PISTOL;
	}
	else if (client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER))
	{
		client->ps.weapon = WP_SABER;
	}
	else
	{
		client->ps.weapon = WP_STUN_BATON;
	}

	/*
	client->ps.stats[STAT_HOLDABLE_ITEMS] |= ( 1 << HI_BINOCULARS );
	client->ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_BINOCULARS, IT_HOLDABLE);
	*/

	client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
	client->ps.stats[STAT_HOLDABLE_ITEM] = 0;

	if ( client->sess.sessionTeam == TEAM_SPECTATOR )
	{
		client->ps.stats[STAT_WEAPONS] = 0;
		client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
		client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
	}

	client->ps.ammo[AMMO_BLASTER] = 100; //ammoData[AMMO_BLASTER].max; //100 seems fair.
//	client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
//	client->ps.ammo[AMMO_FORCE] = ammoData[AMMO_FORCE].max;
//	client->ps.ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max;
//	client->ps.ammo[AMMO_ROCKETS] = ammoData[AMMO_ROCKETS].max;
/*
	client->ps.stats[STAT_WEAPONS] = ( 1 << WP_BRYAR_PISTOL);
	if ( g_gametype.integer == GT_TEAM ) {
		client->ps.ammo[WP_BRYAR_PISTOL] = 50;
	} else {
		client->ps.ammo[WP_BRYAR_PISTOL] = 100;
	}
*/
	client->ps.rocketLockIndex = MAX_CLIENTS;
	client->ps.rocketLockTime = 0;

	//rww - Set here to initialize the circling seeker drone to off.
	//A quick note about this so I don't forget how it works again:
	//ps.genericEnemyIndex is kept in sync between the server and client.
	//When it gets set then an entitystate value of the same name gets
	//set along with an entitystate flag in the shared bg code. Which
	//is why a value needs to be both on the player state and entity state.
	//(it doesn't seem to just carry over the entitystate value automatically
	//because entity state value is derived from player state data or some
	//such)
	client->ps.genericEnemyIndex = -1;

	client->ps.isJediMaster = qfalse;

	client->ps.fallingToDeath = 0;

	//Do per-spawn force power initialization
	WP_SpawnInitForcePowers( ent );

	// health will count down towards max_health
	ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];// * 1.25;		Boot - no extra 25 hp on start.

	// Start with a small amount of armor as well.
	client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 0.25;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	client->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
	SetClientViewAngle( ent, spawn_angles );

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {

	} else {
		G_KillBox( ent );
		trap_LinkEntity (ent);

		// force the base weapon up
		client->ps.weapon = WP_BRYAR_PISTOL;
		client->ps.weaponstate = FIRST_WEAPON;

	}

	// don't allow full run speed for a bit
	client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	client->ps.pm_time = 100;

	client->respawnTime = level.time;
	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	client->latched_buttons = 0;

	// set default animations
	client->ps.torsoAnim = WeaponReadyAnim[client->ps.weapon];
	client->ps.legsAnim = WeaponReadyAnim[client->ps.weapon];

	if ( level.intermissiontime ) {
		MoveClientToIntermission( ent );
	} else {
		// fire the targets of the spawn point
		G_UseTargets( spawnPoint, ent );

		// select the highest weapon number available, after any
		// spawn given items have fired
		client->ps.weapon = 1;
		for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
			if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
				client->ps.weapon = i;
				break;
			}
		}
	}

	// run a client frame to drop exactly to the floor,
	// initialize animations and other things
	client->ps.commandTime = level.time - 100;
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink( ent-g_entities );

	// positively link the client, even if the command times are weird
	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
		VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
	}

	if (g_spawnInvulnerability.integer)
	{
		ent->client->ps.eFlags |= EF_INVULNERABLE;
		ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer;
	}

	// run the presend to set anything else
	ClientEndFrame( ent );

	// clear entity state values
	BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
Beispiel #12
0
/*
================
G_MissileImpact

================
*/
void G_MissileImpact( gentity_t *ent, trace_t *trace )
{
    gentity_t   *other, *attacker;
    qboolean    returnAfterDamage = qfalse;
    vec3_t      dir;

    other = &g_entities[ trace->entityNum ];
    attacker = &g_entities[ ent->r.ownerNum ];

    // check for bounce
    if( !other->takedamage &&
            ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) )
    {
        G_BounceMissile( ent, trace );

        //only play a sound if requested
        if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
            G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );

        return;
    }

    if( !strcmp( ent->classname, "grenade" ) )
    {
        //grenade doesn't explode on impact
        G_BounceMissile( ent, trace );

        //only play a sound if requested
        if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
            G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );

        return;
    }
    else if( !strcmp( ent->classname, "lockblob" ) )
    {
        if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
        {
            other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED;
            other->client->lastLockTime = level.time;
            AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
            other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
        }
    }
    else if( !strcmp( ent->classname, "slowblob" ) )
    {
        if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
        {
            other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED;
            other->client->lastSlowTime = level.time;
            AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
            other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
        }
    }
    else if( !strcmp( ent->classname, "hive" ) )
    {
        if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE )
        {
            if( !ent->parent )
                G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" );
            else
                ent->parent->active = qfalse;

            G_FreeEntity( ent );
            return;
        }
        else
        {
            //prevent collision with the client when returning
            ent->r.ownerNum = other->s.number;

            ent->think = G_ExplodeMissile;
            ent->nextthink = level.time + FRAMETIME;

            //only damage humans
            if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
                returnAfterDamage = qtrue;
            else
                return;
        }
    }

    // impact damage
    if( other->takedamage )
    {
        // FIXME: wrong damage direction?
        if( ent->damage )
        {
            vec3_t  velocity;

            BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
            if( VectorLength( velocity ) == 0 )
                velocity[ 2 ] = 1;  // stepped on a grenade

            G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage,
                      DAMAGE_NO_LOCDAMAGE, ent->methodOfDeath );
        }
    }

    if( returnAfterDamage )
        return;

    // is it cheaper in bandwidth to just remove this ent and create a new
    // one, rather than changing the missile into the explosion?

    if( other->takedamage &&
            ( other->s.eType == ET_PLAYER || other->s.eType == ET_BUILDABLE ) )
    {
        G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
        ent->s.otherEntityNum = other->s.number;
    }
    else if( trace->surfaceFlags & SURF_METALSTEPS )
        G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
    else
        G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );

    ent->freeAfterEvent = qtrue;

    // change over to a normal entity right at the point of impact
    ent->s.eType = ET_GENERAL;

    SnapVectorTowards( trace->endpos, ent->s.pos.trBase );  // save net bandwidth

    G_SetOrigin( ent, trace->endpos );

    // splash damage (doesn't apply to person directly hit)
    if( ent->splashDamage )
        G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
                        other, ent->splashMethodOfDeath );

    trap_LinkEntity( ent );
}
Beispiel #13
0
// links the trigger to it's objective, determining if it's a func_explosive
// of func_constructible and spawning the right indicator
void Think_SetupObjectiveInfo(gentity_t *ent)
{
	ent->target_ent = G_FindByTargetname(NULL, ent->target);

	if (!ent->target_ent)
	{
		G_Error("'trigger_objective_info' has a missing target '%s'\n", ent->target);
	}

	if (ent->target_ent->s.eType == ET_EXPLOSIVE)
	{
		// this is for compass usage
		if ((ent->spawnflags & AXIS_OBJECTIVE) || (ent->spawnflags & ALLIED_OBJECTIVE))
		{
			gentity_t *e = G_Spawn();

			e->r.svFlags = SVF_BROADCAST;
			e->classname = "explosive_indicator";
			if (ent->spawnflags & 8)
			{
				e->s.eType = ET_TANK_INDICATOR;
			}
			else
			{
				e->s.eType = ET_EXPLOSIVE_INDICATOR;
			}
			e->parent       = ent;
			e->s.pos.trType = TR_STATIONARY;

			if (ent->spawnflags & AXIS_OBJECTIVE)
			{
				e->s.teamNum = 1;
			}
			else if (ent->spawnflags & ALLIED_OBJECTIVE)
			{
				e->s.teamNum = 2;
			}

			G_SetOrigin(e, ent->r.currentOrigin);

			e->s.modelindex2 = ent->s.teamNum;
			e->r.ownerNum    = ent->s.number;
			e->think         = explosive_indicator_think;
			e->nextthink     = level.time + FRAMETIME;

			e->s.effect1Time = ent->target_ent->constructibleStats.weaponclass;

			if (ent->tagParent)
			{
				e->tagParent = ent->tagParent;
				Q_strncpyz(e->tagName, ent->tagName, MAX_QPATH);
			}
			else
			{
				VectorCopy(ent->r.absmin, e->s.pos.trBase);
				VectorAdd(ent->r.absmax, e->s.pos.trBase, e->s.pos.trBase);
				VectorScale(e->s.pos.trBase, 0.5, e->s.pos.trBase);
			}

			SnapVector(e->s.pos.trBase);

			trap_LinkEntity(e);

			ent->target_ent->parent = ent;
		}
	}
	else if (ent->target_ent->s.eType == ET_CONSTRUCTIBLE)
	{
		gentity_t *constructibles[2];
		int       team[2] = { 0 };

		ent->target_ent->parent = ent;

		constructibles[0] = ent->target_ent;
		constructibles[1] = G_FindByTargetname(constructibles[0], ent->target);     // see if we are targetting a 2nd one for two team constructibles

		team[0] = constructibles[0]->spawnflags & AXIS_CONSTRUCTIBLE ? TEAM_AXIS : TEAM_ALLIES;

		constructibles[0]->s.otherEntityNum2 = ent->s.teamNum;

		if (constructibles[1])
		{
			team[1] = constructibles[1]->spawnflags & AXIS_CONSTRUCTIBLE ? TEAM_AXIS : TEAM_ALLIES;

			if (constructibles[1]->s.eType != ET_CONSTRUCTIBLE)
			{
				G_Error("'trigger_objective_info' targets multiple entities with targetname '%s', the second one isn't a 'func_constructible' [%d]\n", ent->target, constructibles[1]->s.eType);
			}

			if (team[0] == team[1])
			{
				G_Error("'trigger_objective_info' targets two 'func_constructible' entities with targetname '%s' that are constructible by the same team\n", ent->target);
			}

			constructibles[1]->s.otherEntityNum2 = ent->s.teamNum;

			ent->chain         = constructibles[1];
			ent->chain->parent = ent;

			constructibles[0]->chain = constructibles[1];
			constructibles[1]->chain = constructibles[0];
		}
		else
		{
			constructibles[0]->chain = NULL;
		}

		// if already constructed (in case of START_BUILT)
		if (constructibles[0]->s.angles2[1] == 0)
		{
			// spawn a constructible icon - this is for compass usage
			gentity_t *e = G_Spawn();

			e->r.svFlags = SVF_BROADCAST;
			e->classname = "constructible_indicator";
			if (ent->spawnflags & 8)
			{
				e->s.eType = ET_TANK_INDICATOR_DEAD;
			}
			else
			{
				e->s.eType = ET_CONSTRUCTIBLE_INDICATOR;
			}
			e->s.pos.trType = TR_STATIONARY;

			if (constructibles[1])
			{
				// see if one of the two is still partially built (happens when a multistage destructible construction blows up for the first time)
				if (constructibles[0]->count2 && constructibles[0]->grenadeFired > 1)
				{
					e->s.teamNum = team[0];
				}
				else if (constructibles[1]->count2 && constructibles[1]->grenadeFired > 1)
				{
					e->s.teamNum = team[1];
				}
				else
				{
					e->s.teamNum = 3;   // both teams
				}
			}
			else
			{
				e->s.teamNum = team[0];
			}

			e->s.modelindex2 = ent->s.teamNum;
			e->r.ownerNum    = ent->s.number;
			ent->count2      = (e - g_entities);
			e->think         = constructible_indicator_think;
			e->nextthink     = level.time + FRAMETIME;

			e->parent = ent;

			if (ent->tagParent)
			{
				e->tagParent = ent->tagParent;
				Q_strncpyz(e->tagName, ent->tagName, MAX_QPATH);
			}
			else
			{
				VectorCopy(ent->r.absmin, e->s.pos.trBase);
				VectorAdd(ent->r.absmax, e->s.pos.trBase, e->s.pos.trBase);
				VectorScale(e->s.pos.trBase, 0.5, e->s.pos.trBase);
			}

			SnapVector(e->s.pos.trBase);

			trap_LinkEntity(e);     // moved down
		}
		ent->touch = Touch_ObjectiveInfo;

	}
	else if (ent->target_ent->s.eType == ET_COMMANDMAP_MARKER)
	{
		ent->target_ent->parent = ent;
	}

	trap_LinkEntity(ent);
}
// Set up snapshot merge based on this portal
qboolean G_smvRunCamera( gentity_t *ent ) {
	int id = ent->TargetFlag;
	int chargeTime, sprintTime, hintTime, weapHeat;
	playerState_t *tps, *ps;

	// Opt out if not a real MV portal
	if ( ent->tagParent == NULL || ent->tagParent->client == NULL ) {
		return( qfalse );
	}

	if ( ( ps = &ent->tagParent->client->ps ) == NULL ) {
		return( qfalse );
	}

	// If viewing client is no longer connected, delete this camera
	if ( ent->tagParent->client->pers.connected != CON_CONNECTED ) {
		G_FreeEntity( ent );
		return( qtrue );
	}

	// Also remove if the target player is no longer in the game playing
	if ( ent->target_ent->client->pers.connected != CON_CONNECTED ||
		 ent->target_ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
		G_smvLocateEntityInMVList( ent->tagParent, ent->target_ent - g_entities, qtrue );
		return( qtrue );
	}

	// Seems that depending on player's state, we pull from either r.currentOrigin, or s.origin
	//		if(!spec) then use: r.currentOrigin
	//		if(spec) then use:  s.origin
	//
	// This is true for both the portal origin and its target (origin2)
	//
	VectorCopy( ent->tagParent->s.origin, ent->s.origin );
	G_SetOrigin( ent, ent->s.origin );
	VectorCopy( ent->target_ent->r.currentOrigin, ent->s.origin2 );
	trap_LinkEntity( ent );

	// Only allow client ids 0 to (MAX_MVCLIENTS-1) to be updated with extra info
	if ( id >= MAX_MVCLIENTS ) {
		return( qtrue );
	}

	tps = &ent->target_ent->client->ps;

	if ( tps->stats[STAT_PLAYER_CLASS] == PC_ENGINEER ) {
		chargeTime = g_engineerChargeTime.value;
	} else if ( tps->stats[STAT_PLAYER_CLASS] == PC_MEDIC ) {
		chargeTime = g_medicChargeTime.value;
	} else if ( tps->stats[STAT_PLAYER_CLASS] == PC_FIELDOPS )                                                                                                {
		chargeTime = g_LTChargeTime.value;
	} else if ( tps->stats[STAT_PLAYER_CLASS] == PC_COVERTOPS )                                                                                                                                                                                               {
		chargeTime = g_covertopsChargeTime.value;
	} else { chargeTime = g_soldierChargeTime.value;}

	chargeTime = ( level.time - tps->classWeaponTime >= (int)chargeTime ) ? 0 : ( 1 + floor( 15.0f * (float)( level.time - tps->classWeaponTime ) / chargeTime ) );
	sprintTime = ( ent->target_ent->client->pmext.sprintTime >= 20000 ) ? 0.0f : ( 1 + floor( 7.0f * (float)ent->target_ent->client->pmext.sprintTime / 20000.0f ) );
	weapHeat   = floor( (float)tps->curWeapHeat * 15.0f / 255.0f );
	hintTime   = ( tps->serverCursorHint != HINT_BUILD && ( tps->serverCursorHintVal >= 255 || tps->serverCursorHintVal == 0 ) ) ?
				 0 : ( 1 + floor( 15.0f * (float)tps->serverCursorHintVal / 255.0f ) );

	// (Remaining bits)
	// ammo      : 0
	// ammo-1    : 0
	// ammiclip  : 0
	// ammoclip-1: 16
	id = MAX_WEAPONS - 1 - ( id * 2 );

	if ( tps->pm_flags & PMF_LIMBO ) {
		ps->ammo[id] = 0;
		ps->ammo[id - 1] = 0;
		ps->ammoclip[id - 1] = 0;
	} else {
		ps->ammo[id]  = ( ( ( ent->target_ent->health > 0 ) ? ent->target_ent->health : 0 ) & 0xFF ); // Meds up to 140 :(
		ps->ammo[id] |= ( hintTime & 0x0F ) << 8; // 4 bits for work on current item (dynamite, weapon repair, etc.)
		ps->ammo[id] |= ( weapHeat & 0x0F ) << 12; // 4 bits for weapon heat info

		ps->ammo[id - 1]  = tps->ammo[BG_FindAmmoForWeapon( tps->weapon )] & 0x3FF;   // 11 bits needed to cover 1500 Venom ammo
		ps->ammo[id - 1] |= ( BG_simpleWeaponState( tps->weaponstate ) & 0x03 ) << 11;  // 2 bits for current weapon state
		ps->ammo[id - 1] |= ( ( tps->persistant[PERS_HWEAPON_USE] ) ? 1 : 0 ) << 13;    // 1 bit for mg42 use
		ps->ammo[id - 1] |= ( BG_simpleHintsCollapse( tps->serverCursorHint, hintTime ) & 0x03 ) << 14; // 2 bits for cursor hints

//	G_Printf("tps->hint: %d, dr: %d, collapse: %d\n", tps->serverCursorHint, HINT_DOOR_ROTATING, G_simpleHintsCollapse(tps->serverCursorHint, hintTime));

		ps->ammoclip[id - 1]  = tps->ammoclip[BG_FindClipForWeapon( tps->weapon )] & 0x1FF;   // 9 bits to cover 500 Venom ammo clip
		ps->ammoclip[id - 1] |= ( chargeTime & 0x0F ) << 9;   // 4 bits for weapon charge time
		ps->ammoclip[id - 1] |= ( sprintTime & 0x07 ) << 13;  // 3 bits for fatigue
	}

	return( qtrue );
}
Beispiel #15
0
//------------------
void Cmd_Fx( gentity_t *ent )
{
	vec3_t		dir;
	gentity_t	*fx_ent = NULL;

	if ( Q_stricmp( gi.argv(1), "play" ) == 0 )
	{
		if ( gi.argc() == 3 )
		{
			// I guess, only allow one active at a time
			while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) 
			{
				G_FreeEntity( fx_ent );
			}

			fx_ent = G_Spawn();

			fx_ent->fxFile = gi.argv( 2 );

			// Move out in front of the person spawning the effect
			AngleVectors( ent->currentAngles, dir, NULL, NULL );
			VectorMA( ent->currentOrigin, 32, dir, fx_ent->s.origin );

extern void SP_fx_runner( gentity_t *ent );

			SP_fx_runner( fx_ent );
			fx_ent->delay = 2000;			// adjusting delay
			fx_ent->classname = "cmd_fx";	//	and classname

			return;
		}
	}
	else if ( Q_stricmp( gi.argv(1), "stop" ) == 0 )
	{
		while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) 
		{
			G_FreeEntity( fx_ent );
		}

		return;
	}
	else if ( Q_stricmp( gi.argv(1), "delay" ) == 0 )
	{
		while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) 
		{
			if ( gi.argc() == 3 )
			{
				fx_ent->delay = atoi( gi.argv( 2 ));
			}
			else
			{
				gi.Printf( S_COLOR_GREEN"FX: current delay is: %i\n", fx_ent->delay );
			}

			return;
		}
	}
	else if ( Q_stricmp( gi.argv(1), "random" ) == 0 )
	{
		while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) 
		{
			if ( gi.argc() == 3 )
			{
				fx_ent->random = atoi( gi.argv( 2 ));
			}
			else
			{
				gi.Printf( S_COLOR_GREEN"FX: current random is: %6.2f\n", fx_ent->random );
			}

			return;
		}
	}
	else if ( Q_stricmp( gi.argv(1), "origin" ) == 0 )
	{
		while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) 
		{
			if ( gi.argc() == 5 )
			{
				fx_ent->s.origin[0] = atof( gi.argv( 2 ));
				fx_ent->s.origin[1] = atof( gi.argv( 3 ));
				fx_ent->s.origin[2] = atof( gi.argv( 4 ));

				G_SetOrigin( fx_ent, fx_ent->s.origin );
			}
			else
			{
				gi.Printf( S_COLOR_GREEN"FX: current origin is: <%6.2f %6.2f %6.2f>\n", 
									fx_ent->currentOrigin[0], fx_ent->currentOrigin[1], fx_ent->currentOrigin[2] );
			}

			return;
		}
	}
	else if ( Q_stricmp( gi.argv(1), "dir" ) == 0 )
	{
		while (( fx_ent = G_Find( fx_ent, FOFS(classname), "cmd_fx")) != NULL ) 
		{
			if ( gi.argc() == 5 )
			{
				fx_ent->s.angles[0] = atof( gi.argv( 2 ));
				fx_ent->s.angles[1] = atof( gi.argv( 3 ));
				fx_ent->s.angles[2] = atof( gi.argv( 4 ));

				if ( !VectorNormalize( fx_ent->s.angles ))
				{
					// must have been zero length
					fx_ent->s.angles[2] = 1;
				}
			}
			else
			{
				gi.Printf( S_COLOR_GREEN"FX: current dir is: <%6.2f %6.2f %6.2f>\n", 
									fx_ent->s.angles[0], fx_ent->s.angles[1], fx_ent->s.angles[2] );
			}

			return;
		}
	}

	gi.Printf( S_COLOR_CYAN"Fx--------------------------------------------------------\n" );
	gi.Printf( S_COLOR_CYAN"commands:              sample usage:\n" );
	gi.Printf( S_COLOR_CYAN"----------------------------------------------------------\n" );
	gi.Printf( S_COLOR_CYAN"fx play <filename>     fx play sparks, fx play env/fire\n" );
	gi.Printf( S_COLOR_CYAN"fx stop                fx stop\n" );		
	gi.Printf( S_COLOR_CYAN"fx delay <#>           fx delay 1000\n" );
	gi.Printf( S_COLOR_CYAN"fx random <#>          fx random 200\n" );
	gi.Printf( S_COLOR_CYAN"fx origin <#><#><#>    fx origin 10 20 30\n" );
	gi.Printf( S_COLOR_CYAN"fx dir <#><#><#>       fx dir 0 0 -1\n\n" );
}
Beispiel #16
0
//Run physics on the object (purely origin-related) using custom epVelocity entity
//state value. Origin smoothing on the client is expected to compensate for choppy
//movement.
void G_RunExPhys( gentity_t *ent, float gravity, float mass, float bounce, qboolean autoKill, int *g2Bolts, int numG2Bolts ) {
	trace_t tr;
	vector3 projectedOrigin;
	vector3 vNorm;
	vector3 ground;
	float velScaling = 0.1f;
	float vTotal = 0.0f;

	assert( mass <= 1.0f && mass >= 0.01f );

	if ( gravity ) { //factor it in before we do anything.
		VectorCopy( &ent->r.currentOrigin, &ground );
		ground.z -= 0.1f;

		trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &ground, ent->s.number, ent->clipmask, qfalse, 0, 0 );

		if ( tr.fraction == 1.0f ) {
			ent->s.groundEntityNum = ENTITYNUM_NONE;
		}
		else {
			ent->s.groundEntityNum = tr.entityNum;
		}

		if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) {
			ent->epGravFactor += gravity;

			if ( ent->epGravFactor > MAX_GRAVITY_PULL ) { //cap it off if needed
				ent->epGravFactor = MAX_GRAVITY_PULL;
			}

			ent->epVelocity.z -= ent->epGravFactor;
		}
		else { //if we're sitting on something then reset the gravity factor.
			ent->epGravFactor = 0;
		}
	}

	if ( !ent->epVelocity.x && !ent->epVelocity.y && !ent->epVelocity.z ) { //nothing to do if we have no velocity even after gravity.
		if ( ent->touch ) { //call touch if we're in something
			trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &ent->r.currentOrigin, ent->s.number, ent->clipmask, qfalse, 0, 0 );
			if ( tr.startsolid || tr.allsolid ) {
				ent->touch( ent, &g_entities[tr.entityNum], &tr );
			}
		}
		return;
	}

	//get the projected origin based on velocity.
	VectorMA( &ent->r.currentOrigin, velScaling, &ent->epVelocity, &projectedOrigin );

	VectorScale( &ent->epVelocity, 1.0f - mass, &ent->epVelocity ); //scale it down based on mass

	VectorCopy( &ent->epVelocity, &vNorm );
	vTotal = VectorNormalize( &vNorm );

	if ( vTotal < 1 && ent->s.groundEntityNum != ENTITYNUM_NONE ) { //we've pretty much stopped moving anyway, just clear it out then.
		VectorClear( &ent->epVelocity );
		ent->epGravFactor = 0;
		trap->LinkEntity( (sharedEntity_t *)ent );
		return;
	}

	if ( ent->ghoul2 && g2Bolts ) { //Have we been passed a bolt index array to clip against points on the skeleton?
		vector3 tMins, tMaxs;
		vector3 trajDif;
		vector3 gbmAngles;
		vector3 boneOrg;
		vector3 projectedBoneOrg;
		vector3 collisionRootPos;
		mdxaBone_t matrix;
		trace_t bestCollision;
		qboolean hasFirstCollision = qfalse;
		int i = 0;

		//Maybe we could use a trap call and get the default radius for the bone specified,
		//but this will do at least for now.
		VectorSet( &tMins, -3, -3, -3 );
		VectorSet( &tMaxs, 3, 3, 3 );

		gbmAngles.pitch = gbmAngles.roll = 0;
		gbmAngles.yaw = ent->s.apos.trBase.yaw;

		//Get the difference relative to the entity origin and projected origin, to add to each bolt position.
		VectorSubtract( &ent->r.currentOrigin, &projectedOrigin, &trajDif );

		while ( i < numG2Bolts ) {
			//Get the position of the actual bolt for this frame
			trap->G2API_GetBoltMatrix( ent->ghoul2, 0, g2Bolts[i], &matrix, &gbmAngles, &ent->r.currentOrigin, level.time, NULL, &ent->modelScale );
			BG_GiveMeVectorFromMatrix( &matrix, ORIGIN, &boneOrg );

			//Now add the projected positional difference into the result
			VectorAdd( &boneOrg, &trajDif, &projectedBoneOrg );

			trap->Trace( &tr, &boneOrg, &tMins, &tMaxs, &projectedBoneOrg, ent->s.number, ent->clipmask, qfalse, 0, 0 );

			if ( tr.fraction != 1.0f || tr.startsolid || tr.allsolid ) { //we've hit something
				//Store the "deepest" collision we have
				if ( !hasFirstCollision ) { //don't have one yet so just use this one
					bestCollision = tr;
					VectorCopy( &boneOrg, &collisionRootPos );
					hasFirstCollision = qtrue;
				}
				else {
					if ( tr.allsolid && !bestCollision.allsolid ) { //If the whole trace is solid then this one is deeper
						bestCollision = tr;
						VectorCopy( &boneOrg, &collisionRootPos );
					}
					else if ( tr.startsolid && !bestCollision.startsolid && !bestCollision.allsolid ) { //Next deepest is if it's startsolid
						bestCollision = tr;
						VectorCopy( &boneOrg, &collisionRootPos );
					}
					else if ( !bestCollision.startsolid && !bestCollision.allsolid &&
						tr.fraction < bestCollision.fraction ) { //and finally, if neither is startsolid/allsolid, but the new one has a smaller fraction, then it's closer to an impact point so we will use it
						bestCollision = tr;
						VectorCopy( &boneOrg, &collisionRootPos );
					}
				}
			}

			i++;
		}

		if ( hasFirstCollision ) { //at least one bolt collided
			//We'll get the offset between the collided bolt and endpos, then trace there
			//from the origin so that our desired position becomes that point.
			VectorSubtract( &collisionRootPos, &bestCollision.endpos, &trajDif );

			VectorAdd( &ent->r.currentOrigin, &trajDif, &projectedOrigin );
		}
	}

	//If we didn't collide with any bolts projectedOrigin will still be the original desired
	//projected position so all is well. If we did then projectedOrigin will be modified
	//to provide us with a relative position which does not place the bolt in a solid.
	trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &projectedOrigin, ent->s.number, ent->clipmask, qfalse, 0, 0 );

	if ( tr.startsolid || tr.allsolid ) { //can't go anywhere from here
#ifdef _DEBUG
		Com_Printf( "ExPhys object in solid (%i)\n", ent->s.number );
#endif
		if ( autoKill ) {
			ent->think = G_FreeEntity;
			ent->nextthink = level.time;
		}
		return;
	}

	//Go ahead and set it to the trace endpoint regardless of what it hit
	G_SetOrigin( ent, &tr.endpos );
	trap->LinkEntity( (sharedEntity_t *)ent );

	if ( tr.fraction == 1.0f ) { //Nothing was in the way.
		return;
	}

	if ( bounce ) {
		vTotal *= bounce; //scale it by bounce

		VectorScale( &tr.plane.normal, vTotal, &vNorm ); //scale the trace plane normal by the bounce factor

		if ( vNorm.z > 0 ) {
			ent->epGravFactor -= vNorm.z*(1.0f - mass); //The lighter it is the more gravity will be reduced by bouncing vertically.
			if ( ent->epGravFactor < 0 ) {
				ent->epGravFactor = 0;
			}
		}

		//call touch first so we can check velocity upon impact if we want
		if ( tr.entityNum != ENTITYNUM_NONE && ent->touch ) { //then call the touch function
			ent->touch( ent, &g_entities[tr.entityNum], &tr );
		}

		VectorAdd( &ent->epVelocity, &vNorm, &ent->epVelocity ); //add it into the existing velocity.
	}
	else { //if no bounce, kill when it hits something.
		ent->epVelocity.x = 0;
		ent->epVelocity.y = 0;

		if ( !gravity ) {
			ent->epVelocity.z = 0;
		}
	}
}
Beispiel #17
0
/*
================
LaunchItem

Spawns an item and tosses it forward
================
*/
gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) {
	gentity_t	*dropped;

	dropped = G_Spawn();

	dropped->s.eType = ET_ITEM;
	dropped->s.modelindex = item - bg_itemlist;	// store item number in modelindex
	dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item

	dropped->classname = item->classname;
	dropped->item = item;
	VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
	VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);
	dropped->r.contents = CONTENTS_TRIGGER;

	dropped->touch = Touch_Item;

	G_SetOrigin( dropped, origin );
	dropped->s.pos.trType = TR_GRAVITY;
	dropped->s.pos.trTime = level.time;
	VectorCopy( velocity, dropped->s.pos.trDelta );

	dropped->s.eFlags |= EF_BOUNCE_HALF;
#ifdef MISSIONPACK
	if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF)			&& item->giType == IT_TEAM) { // Special case for CTF flags
#else
	if (g_gametype.integer == GT_CTF && item->giType == IT_TEAM) { // Special case for CTF flags
#endif
		dropped->think = Team_DroppedFlagThink;
		dropped->nextthink = level.time + 30000;
		Team_CheckDroppedItem( dropped );
	} else { // auto-remove after 30 seconds
		dropped->think = G_FreeEntity;
		dropped->nextthink = level.time + 30000;
	}

	dropped->flags = FL_DROPPED_ITEM;

	trap_LinkEntity (dropped);

	return dropped;
}

/*
================
Drop_Item

Spawns an item and tosses it forward
================
*/
gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) {
	vec3_t	velocity;
	vec3_t	angles;

	VectorCopy( ent->s.apos.trBase, angles );
	angles[YAW] += angle;
	angles[PITCH] = 0;	// always forward

	AngleVectors( angles, velocity, NULL, NULL );
	VectorScale( velocity, 150, velocity );
	velocity[2] += 200 + crandom() * 50;
	
	return LaunchItem( item, ent->s.pos.trBase, velocity );
}
Beispiel #18
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gclient_t	*client;
	int		i;
	clientPersistant_t	saved;
	clientSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
	char	userinfo[MAX_INFO_STRING];

	index = ent - g_entities;
	client = ent->client;

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else if (g_gametype.integer >= GT_CTF ) {
		// all base oriented team games use the CTF spawn points
		spawnPoint = SelectCTFSpawnPoint ( 
						client->sess.sessionTeam, 
						client->pers.teamState.state, 
						spawn_origin, spawn_angles);
	} else {
		do {
			// the first spawn should be at a good looking spot
			if ( !client->pers.initialSpawn && client->pers.localClient ) {
				client->pers.initialSpawn = qtrue;
				spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles );
			} else {
				// don't spawn near existing origin if possible
				spawnPoint = SelectSpawnPoint ( 
					client->ps.origin, 
					spawn_origin, spawn_angles);
			}

			// Tim needs to prevent bots from spawning at the initial point
			// on q3dm0...
			if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}
			// just to be symetric, we have a nohumans option...
			if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}

			break;

		} while ( 1 );
	}
	client->pers.teamState.state = TEAM_ACTIVE;

	// always clear the kamikaze flag
	ent->s.eFlags &= ~EF_KAMIKAZE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
//	savedAreaBits = client->areabits;
	accuracy_hits = client->accuracy_hits;
	accuracy_shots = client->accuracy_shots;
	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		persistant[i] = client->ps.persistant[i];
	}
	eventSequence = client->ps.eventSequence;

	memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset?

	client->pers = saved;
	client->sess = savedSess;
	client->ps.ping = savedPing;
//	client->areabits = savedAreaBits;
	client->accuracy_hits = accuracy_hits;
	client->accuracy_shots = accuracy_shots;
	client->lastkilled_client = -1;

	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		client->ps.persistant[i] = persistant[i];
	}
	client->ps.eventSequence = eventSequence;
	// increment the spawncount so the client will detect the respawn
	client->ps.persistant[PERS_SPAWN_COUNT]++;
	client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;

	client->airOutTime = level.time + 12000;

	trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
	// set max health
	client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) );
	if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
		client->pers.maxHealth = 100;
	}
	// clear entity values
	client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
	client->ps.eFlags = flags;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->r.contents = CONTENTS_BODY;
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	
	VectorCopy (playerMins, ent->r.mins);
	VectorCopy (playerMaxs, ent->r.maxs);

	client->ps.clientNum = index;

	client->ps.stats[STAT_WEAPONS] = ( 1 << WP_MACHINEGUN );
	if ( g_gametype.integer == GT_TEAM ) {
		client->ps.ammo[WP_MACHINEGUN] = 50;
	} else {
		client->ps.ammo[WP_MACHINEGUN] = 100;
	}

	client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
	client->ps.ammo[WP_GAUNTLET] = -1;
	client->ps.ammo[WP_GRAPPLING_HOOK] = -1;

	// health will count down towards max_health
	ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	client->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
	SetClientViewAngle( ent, spawn_angles );

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {

	} else {
		G_KillBox( ent );
		trap_LinkEntity (ent);

		// force the base weapon up
		client->ps.weapon = WP_MACHINEGUN;
		client->ps.weaponstate = WEAPON_READY;

	}

	// don't allow full run speed for a bit
	client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	client->ps.pm_time = 100;

	client->respawnTime = level.time;
	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	client->latched_buttons = 0;

	// set default animations
	client->ps.torsoAnim = TORSO_STAND;
	client->ps.legsAnim = LEGS_IDLE;

	if ( level.intermissiontime ) {
		MoveClientToIntermission( ent );
	} else {
		// fire the targets of the spawn point
		G_UseTargets( spawnPoint, ent );

		// select the highest weapon number available, after any
		// spawn given items have fired
		client->ps.weapon = 1;
		for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
			if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
				client->ps.weapon = i;
				break;
			}
		}
	}

	// run a client frame to drop exactly to the floor,
	// initialize animations and other things
	client->ps.commandTime = level.time - 100;
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink( ent-g_entities );

	// positively link the client, even if the command times are weird
	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
		VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
	}

	// run the presend to set anything else
	ClientEndFrame( ent );

	// clear entity state values
	BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
Beispiel #19
0
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION
SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health
AUTOANIMATE - Will cycle it's anim
DEADSOLID - Stay solid even when destroyed (in case damage model is rather large).
NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists
USE_MODEL - When used, will toggle to it's usemodel (model name + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked
USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage
PLAYER_USE - Player can use it with the use button
NO_EXPLOSION - By default, will explode when it dies...this is your override.

"model"		arbitrary .md3 file to display
"health"	how much health to have - default is zero (not breakable)  If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving
"targetname" when used, dies and displays damagemodel, if any (if not, removes itself)
"target" What to use when it dies
"target2" What to use when it's repaired
"target3" What to use when it's used while it's broken
"paintarget" target to fire when hit (but not destroyed)
"count"  the amount of armor/health/ammo given (default 50)
"gravity"	if set to 1, this will be affected by gravity
"radius"  Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1)  (.5) is half as many chunks, (2) is twice as many chunks

Damage: default is none
"splashDamage" - damage to do (will make it explode on death)
"splashRadius" - radius for above damage

"team" - This cannot take damage from members of this team:
	"player"
	"neutral"
	"enemy"

"material" - default is "8 - MAT_NONE" - choose from this list:
0 = MAT_METAL		(grey metal)
1 = MAT_GLASS		
2 = MAT_ELECTRICAL	(sparks only)
3 = MAT_ELEC_METAL	(METAL chunks and sparks)
4 =	MAT_DRK_STONE	(brown stone chunks)
5 =	MAT_LT_STONE	(tan stone chunks)
6 =	MAT_GLASS_METAL (glass and METAL chunks)
7 = MAT_METAL2		(blue/grey metal)
8 = MAT_NONE		(no chunks-DEFAULT)
9 = MAT_GREY_STONE	(grey colored stone)
10 = MAT_METAL3		(METAL and METAL2 chunk combo)
11 = MAT_CRATE1		(yellow multi-colored crate chunks)
12 = MAT_GRATE1		(grate chunks--looks horrible right now)
13 = MAT_ROPE		(for yavin_trial, no chunks, just wispy bits )
14 = MAT_CRATE2		(red multi-colored crate chunks)
15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout )
FIXME/TODO: 
set size better?
multiple damage models?
custom explosion effect/sound?
*/
void SP_misc_model_breakable( gentity_t *ent ) 
{
	char	damageModel[MAX_QPATH];
	char	chunkModel[MAX_QPATH];
	char	useModel[MAX_QPATH];
	int		len;
	
	// Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this.
	G_SpawnInt( "material", "8", (int*)&ent->material );
	G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer
	CacheChunkEffects( ent->material );

	misc_model_breakable_init( ent );

	len = strlen( ent->model ) - 4;
	strncpy( damageModel, ent->model, len );
	damageModel[len] = 0;	//chop extension
	strncpy( chunkModel, damageModel, sizeof(chunkModel));
	strncpy( useModel, damageModel, sizeof(useModel));
	
	if (ent->takedamage) {
		//Dead/damaged model
		if( !(ent->spawnflags & 8) ) {	//no dmodel
			strcat( damageModel, "_d1.md3" );
			ent->s.modelindex2 = G_ModelIndex( damageModel );
		}
		
		//Chunk model
		strcat( chunkModel, "_c1.md3" );
		ent->s.modelindex3 = G_ModelIndex( chunkModel );
	}

	//Use model
	if( ent->spawnflags & 32 ) {	//has umodel
		strcat( useModel, "_u1.md3" );
		ent->sound1to2 = G_ModelIndex( useModel );
	}
	if ( !ent->mins[0] && !ent->mins[1] && !ent->mins[2] )
	{
		VectorSet (ent->mins, -16, -16, -16);
	}
	if ( !ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2] )
	{
		VectorSet (ent->maxs, 16, 16, 16);
	}

	if ( ent->spawnflags & 2 )
	{
		ent->s.eFlags |= EF_ANIM_ALLFAST;
	}

	G_SetOrigin( ent, ent->s.origin );
	G_SetAngles( ent, ent->s.angles );
	gi.linkentity (ent);

	if ( ent->spawnflags & 128 )
	{//Can be used by the player's BUTTON_USE
		ent->svFlags |= SVF_PLAYER_USABLE;
	}

	if ( ent->team && ent->team[0] )
	{
		ent->noDamageTeam = TranslateTeamName( ent->team );
		if ( ent->noDamageTeam == TEAM_FREE )
		{
			G_Error("team name %s not recognized\n", ent->team);
		}
	}
	
	ent->team = NULL;

	//HACK
	if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 )
	{//run a think
		G_EffectIndex( "fighter_explosion2" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" );
		G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" );
		ent->e_ThinkFunc = thinkF_TieFighterThink;
		ent->nextthink = level.time + FRAMETIME;
	}
	float grav = 0;
	G_SpawnFloat( "gravity", "0", &grav );
	if ( grav )
	{//affected by gravity
		G_SetAngles( ent, ent->s.angles );
		G_SetOrigin( ent, ent->currentOrigin );
		misc_model_breakable_gravity_init( ent, qtrue );
	}
}
Beispiel #20
0
/*QUAKED target_deactivate (1 0 0) (-4 -4 -4) (4 4 4)
Will set the target(s) to be non-usable/triggerable
*/
void SP_target_deactivate( gentity_t *self )
{
	G_SetOrigin( self, self->s.origin );
	self->use = target_deactivate_use;
}
Beispiel #21
0
//-----------------------------------------------------
void finish_spawning_turretG2( gentity_t *base )
{
	vec3_t		fwd;
	int			t;

	if ( (base->spawnflags&2) )
	{
		base->s.angles[ROLL] += 180;
		base->s.origin[2] -= 22.0f;
	}

	G_SetAngles( base, base->s.angles );
	AngleVectors( base->r.currentAngles, fwd, NULL, NULL );

	G_SetOrigin(base, base->s.origin);

	base->s.eType = ET_GENERAL;

	if ( base->team && base->team[0] && //g_gametype.integer == GT_SIEGE &&
		!base->teamnodmg)
	{
		base->teamnodmg = atoi(base->team);
	}
	base->team = NULL;

	// Set up our explosion effect for the ExplodeDeath code....
	G_EffectIndex( "turret/explode" );
	G_EffectIndex( "sparks/spark_exp_nosnd" );

	base->use = turretG2_base_use;
	base->pain = TurretG2Pain;

	// don't start working right away
	base->think = turretG2_base_think;
	base->nextthink = level.time + FRAMETIME * 5;

	// this is really the pitch angle.....
	base->speed = 0;

	// respawn time defaults to 20 seconds
	if ( (base->spawnflags&SPF_TURRETG2_CANRESPAWN) && !base->count )
	{
		base->count = 20000;
	}

	G_SpawnFloat( "shotspeed", "0", &base->mass );
	if ( (base->spawnflags&SPF_TURRETG2_TURBO) )
	{
		if ( !base->random )
		{//error worked into projectile direction
			base->random = 2.0f;
		}

		if ( !base->mass )
		{//misnomer: speed of projectile
			base->mass = 20000;
		}

		if ( !base->health )
		{
			base->health = 2000;
		}

		// search radius
		if ( !base->radius )
		{
			base->radius = 32768;
		}

		// How quickly to fire
		if ( !base->wait )
		{
			base->wait = 1000;// + random() * 500;
		}

		if ( !base->splashDamage )
		{
			base->splashDamage = 200;
		}

		if ( !base->splashRadius )
		{
			base->splashRadius = 500;
		}

		// how much damage each shot does
		if ( !base->damage )
		{
			base->damage = 500;
		}

		if ( (base->spawnflags&SPF_TURRETG2_TURBO) )
		{
			VectorSet( base->r.maxs, 64.0f, 64.0f, 30.0f );
			VectorSet( base->r.mins, -64.0f, -64.0f, -30.0f );
		}
		//start in "off" anim
		TurboLaser_SetBoneAnim( base, 4, 5 );
	}
	else
	{
		if ( !base->random )
		{//error worked into projectile direction
			base->random = 2.0f;
		}

		if ( !base->mass )
		{//misnomer: speed of projectile
			base->mass = 1100;
		}

		if ( !base->health )
		{
			base->health = 100;
		}

		// search radius
		if ( !base->radius )
		{
			base->radius = 512;
		}

		// How quickly to fire
		if ( !base->wait )
		{
			base->wait = 150 + random() * 55;
		}

		if ( !base->splashDamage )
		{
			base->splashDamage = 10;
		}

		if ( !base->splashRadius )
		{
			base->splashRadius = 25;
		}

		// how much damage each shot does
		if ( !base->damage )
		{
			base->damage = 5;
		}

		if ( base->spawnflags & 2 )
		{//upside-down, invert r.mins and maxe
			VectorSet( base->r.maxs, 10.0f, 10.0f, 30.0f );
			VectorSet( base->r.mins, -10.0f, -10.0f, 0.0f );
		}
		else
		{
			VectorSet( base->r.maxs, 10.0f, 10.0f, 0.0f );
			VectorSet( base->r.mins, -10.0f, -10.0f, -30.0f );
		}
	}

	//stash health off for respawn.  NOTE: cannot use maxhealth because that might not be set if not showing the health bar
	base->genericValue6 = base->health;

	G_SpawnInt( "showhealth", "0", &t );
	if (t)
	{ //a non-0 maxhealth value will mean we want to show the health on the hud
		base->maxHealth = base->health;
		G_ScaleNetHealth(base);
		base->s.shouldtarget = qtrue;
		//base->s.owner = MAX_CLIENTS; //not owned by any client
	}

	if (base->s.iModelScale)
	{ //let's scale the bbox too...
		float fScale = base->s.iModelScale/100.0f;
		VectorScale(base->r.mins, fScale, base->r.mins);
		VectorScale(base->r.maxs, fScale, base->r.maxs);
	}

	// Precache special FX and moving sounds
	if ( (base->spawnflags&SPF_TURRETG2_TURBO) )
	{
		base->genericValue13 = G_EffectIndex( "turret/turb_muzzle_flash" );
		base->genericValue14 = G_EffectIndex( "turret/turb_shot" );
		base->genericValue15 = G_EffectIndex( "turret/turb_impact" );
		//FIXME: Turbo Laser Cannon sounds!
		G_SoundIndex( "sound/vehicles/weapons/turbolaser/turn.wav" );
	}
	else
	{
		G_SoundIndex( "sound/chars/turret/startup.wav" );
		G_SoundIndex( "sound/chars/turret/shutdown.wav" );
		G_SoundIndex( "sound/chars/turret/ping.wav" );
		G_SoundIndex( "sound/chars/turret/move.wav" );
	}

	base->r.contents = CONTENTS_BODY|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_SHOTCLIP;

	//base->max_health = base->health;
	base->takedamage = qtrue;
	base->die  = turretG2_die;

	base->material = MAT_METAL;
	//base->r.svFlags |= SVF_NO_TELEPORT|SVF_NONNPC_ENEMY|SVF_SELF_ANIMATING;

	// Register this so that we can use it for the missile effect
	RegisterItem( BG_FindItemForWeapon( WP_BLASTER ));

	// But set us as a turret so that we can be identified as a turret
	base->s.weapon = WP_TURRET;

	trap_LinkEntity( base );
}
Beispiel #22
0
/*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
*/
void SP_info_camp(gentity_t * self)
{
	G_SetOrigin(self, self->s.origin);
}
Beispiel #23
0
/*QUAKED target_autosave (1 0 0) (-4 -4 -4) (4 4 4)
Auto save the game in two frames.
Make sure it won't trigger during dialogue or cinematic or it will stutter!
*/
void SP_target_autosave( gentity_t *self )
{
	G_SetOrigin( self, self->s.origin );
	self->e_UseFunc = useF_target_autosave_use;
}
Beispiel #24
0
/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
Used as a positional target for in-game calculation, like jumppad targets.
target_position does the same thing
*/
void SP_info_notnull(gentity_t * self)
{
	G_SetOrigin(self, self->s.origin);
}
Beispiel #25
0
/*QUAKED target_location (0 0.5 0) (-8 -8 -8) (8 8 8)
Set "message" to the name of this location.
Set "count" to 0-7 for color.
0:white 1:red 2:green 3:yellow 4:blue 5:cyan 6:magenta 7:white

Closest target_location in sight used for the location, if none
in site, closest in distance
*/
void SP_target_location( gentity_t *self ){
	self->e_ThinkFunc = thinkF_target_location_linkup;
	self->nextthink = level.time + 1000;  // Let them all spawn first

	G_SetOrigin( self, self->s.origin );
}
Beispiel #26
0
/*
=============
SpawnCorpse

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
static void SpawnCorpse( gentity_t *ent )
{
	gentity_t *body;
	int       contents;
	vec3_t    origin, mins;

	VectorCopy( ent->r.currentOrigin, origin );

	trap_UnlinkEntity( ent );

	// if client is in a nodrop area, don't leave the body
	contents = trap_PointContents( origin, -1 );

	if ( contents & CONTENTS_NODROP )
	{
		return;
	}

	body = G_NewEntity();

	VectorCopy( ent->s.apos.trBase, body->s.angles );
	body->s.eFlags = EF_DEAD;
	body->s.eType = ET_CORPSE;
	body->timestamp = level.time;
	body->s.event = 0;
	body->r.contents = CONTENTS_CORPSE;
	body->clipmask = MASK_DEADSOLID;
	body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
	body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;

	if ( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
	{
		body->classname = "humanCorpse";
	}
	else
	{
		body->classname = "alienCorpse";
	}

	body->s.misc = MAX_CLIENTS;

	body->think = BodySink;
	body->nextthink = level.time + 20000;

	body->s.legsAnim = ent->s.legsAnim;

	if ( !body->nonSegModel )
	{
		switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT )
		{
			case BOTH_DEATH1:
			case BOTH_DEAD1:
				body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
				break;

			case BOTH_DEATH2:
			case BOTH_DEAD2:
				body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
				break;

			case BOTH_DEATH3:
			case BOTH_DEAD3:
			default:
				body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
				break;
		}
	}
	else
	{
		switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT )
		{
			case NSPA_DEATH1:
			case NSPA_DEAD1:
				body->s.legsAnim = NSPA_DEAD1;
				break;

			case NSPA_DEATH2:
			case NSPA_DEAD2:
				body->s.legsAnim = NSPA_DEAD2;
				break;

			case NSPA_DEATH3:
			case NSPA_DEAD3:
			default:
				body->s.legsAnim = NSPA_DEAD3;
				break;
		}
	}

	body->takedamage = qfalse;

	body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
	ent->health = 0;

	//change body dimensions
	BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], mins, NULL, NULL, body->r.mins, body->r.maxs );

	//drop down to match the *model* origins of ent and body
	origin[2] += mins[ 2 ] - body->r.mins[ 2 ];

	G_SetOrigin( body, origin );
	body->s.pos.trType = TR_GRAVITY;
	body->s.pos.trTime = level.time;
	VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );

	trap_LinkEntity( body );
}
Beispiel #27
0
/*QUAKED target_friction_change (1 0 0) (-4 -4 -4) (4 4 4)

"friction" Normal = 6, Valid range 0 - 10

*/
void SP_target_friction_change( gentity_t *self )
{
	G_SetOrigin( self, self->s.origin );
	self->e_UseFunc = useF_target_friction_change_use;
}
Beispiel #28
0
static gentity_t *SpawnModelOnVictoryPad( gentity_t *pad, vec3_t offset, gentity_t *ent, int place ) {
	gentity_t	*body;
	vec3_t		vec;
	vec3_t		f, r, u;

	body = G_Spawn();
	if ( !body ) {
		G_Printf( S_COLOR_RED "ERROR: out of gentities\n" );
		return NULL;
	}

	body->classname = ent->client->pers.netname;
	body->client = ent->client;
	body->s = ent->s;
	body->s.eType = ET_PLAYER;		// could be ET_INVISIBLE
	body->s.eFlags = 0;				// clear EF_TALK, etc
	body->s.powerups = 0;			// clear powerups
	body->s.loopSound = 0;			// clear lava burning
	body->s.number = body - g_entities;
	body->timestamp = level.time;
	body->physicsObject = qtrue;
	body->physicsBounce = 0;		// don't bounce
	body->s.event = 0;
	body->s.pos.trType = TR_STATIONARY;
	body->s.groundEntityNum = ENTITYNUM_WORLD;
	body->s.legsAnim = LEGS_IDLE;
	body->s.torsoAnim = TORSO_STAND;
	if( body->s.weapon == WP_NONE ) {
		body->s.weapon = WP_MACHINEGUN;
	}
	if( body->s.weapon == WP_GAUNTLET) {
		body->s.torsoAnim = TORSO_STAND2;
	}
	body->s.event = 0;
	body->r.svFlags = ent->r.svFlags;
	VectorCopy (ent->r.mins, body->r.mins);
	VectorCopy (ent->r.maxs, body->r.maxs);
	VectorCopy (ent->r.absmin, body->r.absmin);
	VectorCopy (ent->r.absmax, body->r.absmax);
	body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
	body->r.contents = CONTENTS_BODY;
	body->r.ownerNum = ent->r.ownerNum;
	body->takedamage = qfalse;

	VectorSubtract( level.intermission_origin, pad->r.currentOrigin, vec );
	vectoangles( vec, body->s.apos.trBase );
	body->s.apos.trBase[PITCH] = 0;
	body->s.apos.trBase[ROLL] = 0;

	AngleVectors( body->s.apos.trBase, f, r, u );
	VectorMA( pad->r.currentOrigin, offset[0], f, vec );
	VectorMA( vec, offset[1], r, vec );
	VectorMA( vec, offset[2], u, vec );

	G_SetOrigin( body, vec );

	trap_LinkEntity (body);

	body->count = place;

	return body;
}
Beispiel #29
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles )
{
  int                 index;
  vec3_t              spawn_origin, spawn_angles;
  gclient_t           *client;
  int                 i;
  clientPersistant_t  saved;
  clientSession_t     savedSess;
  int                 persistant[ MAX_PERSISTANT ];
  gentity_t           *spawnPoint = NULL;
  int                 flags;
  int                 savedPing;
  int                 teamLocal;
  int                 eventSequence;
  char                userinfo[ MAX_INFO_STRING ];
  vec3_t              up = { 0.0f, 0.0f, 1.0f };
  int                 maxAmmo, maxClips;
  weapon_t            weapon;

  index = ent - g_entities;
  client = ent->client;

  teamLocal = client->pers.teamSelection;

  //if client is dead and following teammate, stop following before spawning
  if( client->sess.spectatorClient != -1 )
  {
    client->sess.spectatorClient = -1;
    client->sess.spectatorState = SPECTATOR_FREE;
  }

  // only start client if chosen a class and joined a team
  if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE )
    client->sess.spectatorState = SPECTATOR_FREE;
  else if( client->pers.classSelection == PCL_NONE )
    client->sess.spectatorState = SPECTATOR_LOCKED;

  // if client is dead and following teammate, stop following before spawning
  if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
    G_StopFollowing( ent );

  if( origin != NULL )
    VectorCopy( origin, spawn_origin );

  if( angles != NULL )
    VectorCopy( angles, spawn_angles );

  // find a spawn point
  // do it before setting health back up, so farthest
  // ranging doesn't count this client
  if( client->sess.spectatorState != SPECTATOR_NOT )
  {
    if( teamLocal == TEAM_NONE )
      spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
    else if( teamLocal == TEAM_ALIENS )
      spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
    else if( teamLocal == TEAM_HUMANS )
      spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
  }
  else
  {
    if( spawn == NULL )
    {
      G_Error( "ClientSpawn: spawn is NULL\n" );
      return;
    }

    spawnPoint = spawn;

    if( ent != spawn )
    {
      //start spawn animation on spawnPoint
      G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue );

      if( spawnPoint->buildableTeam == TEAM_ALIENS )
        spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
      else if( spawnPoint->buildableTeam == TEAM_HUMANS )
        spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
    }
  }

  // toggle the teleport bit so the client knows to not lerp
  flags = ( ent->client->ps.eFlags & EF_TELEPORT_BIT ) ^ EF_TELEPORT_BIT;
  G_UnlaggedClear( ent );

  // clear everything but the persistant data

  saved = client->pers;
  savedSess = client->sess;
  savedPing = client->ps.ping;

  for( i = 0; i < MAX_PERSISTANT; i++ )
    persistant[ i ] = client->ps.persistant[ i ];

  eventSequence = client->ps.eventSequence;
  memset( client, 0, sizeof( *client ) );

  client->pers = saved;
  client->sess = savedSess;
  client->ps.ping = savedPing;
  client->lastkilled_client = -1;

  for( i = 0; i < MAX_PERSISTANT; i++ )
    client->ps.persistant[ i ] = persistant[ i ];

  client->ps.eventSequence = eventSequence;

  // increment the spawncount so the client will detect the respawn
  client->ps.persistant[ PERS_SPAWN_COUNT ]++;
  client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState;

  client->airOutTime = level.time + 12000;

  trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
  client->ps.eFlags = flags;

  //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );

  ent->s.groundEntityNum = ENTITYNUM_NONE;
  ent->client = &level.clients[ index ];
  ent->takedamage = qtrue;
  ent->inuse = qtrue;
  ent->classname = "player";
  ent->r.contents = CONTENTS_BODY;
  ent->clipmask = MASK_PLAYERSOLID;
  ent->die = player_die;
  ent->waterlevel = 0;
  ent->watertype = 0;
  ent->flags = 0;

  // calculate each client's acceleration
  ent->evaluateAcceleration = qtrue;

  client->ps.stats[ STAT_MISC ] = 0;

  client->ps.eFlags = flags;
  client->ps.clientNum = index;

  BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );

  if( client->sess.spectatorState == SPECTATOR_NOT )
    client->ps.stats[ STAT_MAX_HEALTH ] =
      BG_Class( ent->client->pers.classSelection )->health;
  else
    client->ps.stats[ STAT_MAX_HEALTH ] = 100;

  // clear entity values
  if( ent->client->pers.classSelection == PCL_HUMAN )
  {
    BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
    weapon = client->pers.humanItemSelection;
  }
  else if( client->sess.spectatorState == SPECTATOR_NOT )
    weapon = BG_Class( ent->client->pers.classSelection )->startWeapon;
  else
    weapon = WP_NONE;

  maxAmmo = BG_Weapon( weapon )->maxAmmo;
  maxClips = BG_Weapon( weapon )->maxClips;
  client->ps.stats[ STAT_WEAPON ] = weapon;
  client->ps.ammo = maxAmmo;
  client->ps.clips = maxClips;

  // We just spawned, not changing weapons
  client->ps.persistant[ PERS_NEWWEAPON ] = 0;

  ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
  ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;

  ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
  ent->client->ps.stats[ STAT_STATE ] = 0;
  VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );

  // health will count down towards max_health
  ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25;

  //if evolving scale health
  if( ent == spawn )
  {
    ent->health *= ent->client->pers.evolveHealthFraction;
    client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction;
  }

  //clear the credits array
  for( i = 0; i < MAX_CLIENTS; i++ )
    ent->credits[ i ] = 0;

  client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;

  G_SetOrigin( ent, spawn_origin );
  VectorCopy( spawn_origin, client->ps.origin );

#define UP_VEL  150.0f
#define F_VEL   50.0f

  //give aliens some spawn velocity
  if( client->sess.spectatorState == SPECTATOR_NOT &&
      client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
  {
    if( ent == spawn )
    {
      //evolution particle system
      G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
    }
    else
    {
      spawn_angles[ YAW ] += 180.0f;
      AngleNormalize360( spawn_angles[ YAW ] );

      if( spawnPoint->s.origin2[ 2 ] > 0.0f )
      {
        vec3_t  forward, dir;

        AngleVectors( spawn_angles, forward, NULL, NULL );
        VectorScale( forward, F_VEL, forward );
        VectorAdd( spawnPoint->s.origin2, forward, dir );
        VectorNormalize( dir );

        VectorScale( dir, UP_VEL, client->ps.velocity );
      }

      G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
    }
  }
  else if( client->sess.spectatorState == SPECTATOR_NOT &&
           client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
  {
    spawn_angles[ YAW ] += 180.0f;
    AngleNormalize360( spawn_angles[ YAW ] );
  }

  // the respawned flag will be cleared after the attack and jump keys come up
  client->ps.pm_flags |= PMF_RESPAWNED;

  trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
  G_SetClientViewAngle( ent, spawn_angles );

  if( client->sess.spectatorState == SPECTATOR_NOT )
  {
    trap_LinkEntity( ent );

    // force the base weapon up
    if( client->pers.teamSelection == TEAM_HUMANS )
      G_ForceWeaponChange( ent, weapon );

    client->ps.weaponstate = WEAPON_READY;
  }

  // don't allow full run speed for a bit
  client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  client->ps.pm_time = 100;

  client->respawnTime = level.time;
  ent->nextRegenTime = level.time;

  client->inactivityTime = level.time + g_inactivity.integer * 1000;
  client->latched_buttons = 0;

  // set default animations
  client->ps.torsoAnim = TORSO_STAND;
  client->ps.legsAnim = LEGS_IDLE;

  if( level.intermissiontime )
    MoveClientToIntermission( ent );
  else
  {
    // fire the targets of the spawn point
    if( !spawn )
      G_UseTargets( spawnPoint, ent );

    // select the highest weapon number available, after any
    // spawn given items have fired
    client->ps.weapon = 1;

    for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- )
    {
      if( BG_InventoryContainsWeapon( i, client->ps.stats ) )
      {
        client->ps.weapon = i;
        break;
      }
    }
  }

  // run a client frame to drop exactly to the floor,
  // initialize animations and other things
  client->ps.commandTime = level.time - 100;
  ent->client->pers.cmd.serverTime = level.time;
  ClientThink( ent-g_entities );

  // positively link the client, even if the command times are weird
  if( client->sess.spectatorState == SPECTATOR_NOT )
  {
    BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
    VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
    trap_LinkEntity( ent );
  }

  // must do this here so the number of active clients is calculated
  CalculateRanks( );

  // run the presend to set anything else
  ClientEndFrame( ent );

  // clear entity state values
  BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
Beispiel #30
0
/*
================
LaunchItem

Spawns an item and tosses it forward
================
*/
gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity, char *target ) {
	gentity_t	*dropped;

	dropped = G_Spawn();

	dropped->s.eType = ET_ITEM;
	dropped->s.modelindex = item - bg_itemlist;	// store item number in modelindex
	dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item

	dropped->classname = item->classname;
	dropped->item = item;

	// try using the "correct" mins/maxs first
	VectorSet( dropped->mins, item->mins[0], item->mins[1], item->mins[2] );
	VectorSet( dropped->maxs, item->maxs[0], item->maxs[1], item->maxs[2] );

	if ((!dropped->mins[0] && !dropped->mins[1] && !dropped->mins[2]) && 
		(!dropped->maxs[0] && !dropped->maxs[1] && !dropped->maxs[2]))
	{
		VectorSet( dropped->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS );
		VectorScale( dropped->maxs, -1, dropped->mins );
	}

	dropped->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_TRIGGER;//not CONTENTS_BODY for dropped items, don't need to ID them

	if ( target && target[0] )
	{
		dropped->target = G_NewString( target );
	}
	else
	{
		// if not targeting something, auto-remove after 30 seconds
		// only if it's NOT a security or goodie key
		if (dropped->item->giTag != INV_SECURITY_KEY )
		{
			dropped->e_ThinkFunc = thinkF_G_FreeEntity;
			dropped->nextthink = level.time + 30000;
		}

		if ( dropped->item->giType == IT_AMMO && dropped->item->giTag == AMMO_FORCE )
		{
			dropped->nextthink = -1;
			dropped->e_ThinkFunc = thinkF_NULL;
		}
	}

	dropped->e_TouchFunc = touchF_Touch_Item;

	if ( item->giType == IT_WEAPON )
	{
		// give weapon items zero pitch, a random yaw, and rolled onto their sides...but would be bad to do this for a bowcaster
		if ( item->giTag != WP_BOWCASTER
			&& item->giTag != WP_THERMAL
			&& item->giTag != WP_TRIP_MINE
			&& item->giTag != WP_DET_PACK )
		{
			VectorSet( dropped->s.angles, 0, crandom() * 180, 90.0f );
			G_SetAngles( dropped, dropped->s.angles );
		}
	}

	G_SetOrigin( dropped, origin );
	dropped->s.pos.trType = TR_GRAVITY;
	dropped->s.pos.trTime = level.time;
	VectorCopy( velocity, dropped->s.pos.trDelta );

	dropped->s.eFlags |= EF_BOUNCE_HALF;

	dropped->flags = FL_DROPPED_ITEM;

	gi.linkentity (dropped);

	return dropped;
}