Beispiel #1
0
/*QUAKED func_rotating (0 .5 .8) ? START_ON - X_AXIS Y_AXIS
You need to have an origin brush as part of this entity.  The center of that brush will be
the point around which it is rotated. It will rotate around the Z axis by default.  You can
check either the X_AXIS or Y_AXIS box to change that.

"model2"	.md3 model to also draw
"speed"		determines how fast it moves; default value is 100.
"dmg"		damage to inflict when blocked (2 default)
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_rotating (gentity_t *ent) {
	if ( !ent->speed ) {
		ent->speed = 100;
	}

	// set the axis of rotation
	ent->s.apos.trType = TR_LINEAR;
	if ( ent->spawnflags & 4 ) {
		ent->s.apos.trDelta[2] = ent->speed;
	} else if ( ent->spawnflags & 8 ) {
		ent->s.apos.trDelta[0] = ent->speed;
	} else {
		ent->s.apos.trDelta[1] = ent->speed;
	}

	if (!ent->damage) {
		ent->damage = 2;
	}

	trap_SetBrushModel( ent, ent->model );
	InitMover( ent );

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

	trap_LinkEntity( ent );
}
Beispiel #2
0
/**
QUAKED func_static (0 .5 .8) ?
A bmodel that just sits there, doing nothing.  Can be used for conditional walls and models.
"model2"	.md3 model to also draw
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_static(gentity_t *ent)
{
	trap_SetBrushModel(ent, ent->model);
	InitMover(ent);
	VectorCopy(ent->s.origin, ent->s.pos.trBase);
	VectorCopy(ent->s.origin, ent->r.currentOrigin);
}
Beispiel #3
0
/*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS
A train is a mover that moves between path_corner target points.
Trains MUST HAVE AN ORIGIN BRUSH.
The train spawns at the first target it is pointing at.
"model2"	.md3 model to also draw
"speed"		default 100
"dmg"		default	2
"noise"		looping sound to play when the train is in motion
"target"	next path corner
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_train (gentity_t *self) {
	VectorClear (self->s.angles);

	if (self->spawnflags & TRAIN_BLOCK_STOPS) {
		self->damage = 0;
	} else {
		if (!self->damage) {
			self->damage = 2;
		}
	}

	if ( !self->speed ) {
		self->speed = 100;
	}

	if ( !self->target ) {
		G_Printf ("func_train without a target at %s\n", vtos(self->r.absmin));
		G_FreeEntity( self );
		return;
	}

	trap_SetBrushModel( self, self->model );
	InitMover( self );

	self->reached = Reached_Train;

	// start trains on the second frame, to make sure their targets have had
	// a chance to spawn
	self->nextthink = level.time + FRAMETIME;
	self->think = Think_SetupTrainTargets;
}
Beispiel #4
0
/*QUAKED func_pendulum (0 .5 .8) ?
You need to have an origin brush as part of this entity.
Pendulums always swing north / south on unrotated models.  Add an angles field to the model to allow rotation in other directions.
Pendulum frequency is a physical constant based on the length of the beam and gravity.
"model2"	.md3 model to also draw
"speed"		the number of degrees each way the pendulum swings, (30 default)
"phase"		the 0.0 to 1.0 offset in the cycle to start at
"dmg"		damage to inflict when blocked (2 default)
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_pendulum(gentity_t *ent) {
	float		freq;
	float		length;
	float		phase;
	float		speed;

	G_SpawnFloat( "speed", "30", &speed );
	G_SpawnInt( "dmg", "2", &ent->damage );
	G_SpawnFloat( "phase", "0", &phase );

	trap_SetBrushModel( ent, ent->model );

	// find pendulum length
	length = fabs( ent->r.mins[2] );
	if ( length < 8 ) {
		length = 8;
	}

	freq = 1 / ( M_PI * 2 ) * sqrt( g_gravity.value / ( 3 * length ) );

	ent->s.pos.trDuration = ( 1000 / freq );

	InitMover( ent );

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

	VectorCopy( ent->s.angles, ent->s.apos.trBase );

	ent->s.apos.trDuration = 1000 / freq;
	ent->s.apos.trTime = ent->s.apos.trDuration * phase;
	ent->s.apos.trType = TR_SINE;
	ent->s.apos.trDelta[2] = speed;
}
Beispiel #5
0
/*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
Normally bobs on the Z axis
"model2"	.md3 model to also draw
"height"	amplitude of bob (32 default)
"speed"		seconds to complete a bob cycle (4 default)
"phase"		the 0.0 to 1.0 offset in the cycle to start at
"dmg"		damage to inflict when blocked (2 default)
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_bobbing (gentity_t *ent) {
	float		height;
	float		phase;

	G_SpawnFloat( "speed", "4", &ent->speed );
	G_SpawnFloat( "height", "32", &height );
	G_SpawnInt( "dmg", "2", &ent->damage );
	G_SpawnFloat( "phase", "0", &phase );

	trap_SetBrushModel( ent, ent->model );
	InitMover( ent );

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

	ent->s.pos.trDuration = ent->speed * 1000;
	ent->s.pos.trTime = ent->s.pos.trDuration * phase;
	ent->s.pos.trType = TR_SINE;

	// set the axis of bobbing
	if ( ent->spawnflags & 1 ) {
		ent->s.pos.trDelta[0] = height;
	} else if ( ent->spawnflags & 2 ) {
		ent->s.pos.trDelta[1] = height;
	} else {
		ent->s.pos.trDelta[2] = height;
	}
}
Beispiel #6
0
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY
Scripted brush entity. A simplified means of moving brushes around based on events.

"modelscale" - Scale multiplier (defaults to 1, and scales uniformly)
"modelscale_vec" - Set scale per-axis.  Overrides "modelscale", so if you have both the "modelscale" is ignored
"model2" optional md3 to draw over the solid clip brush
"scriptname" name used for scripting purposes (like aiName in AI scripting)
"health" optionally make this entity damagable
*/
void SP_script_mover( gentity_t *ent ) {

	float scale[3] = {1,1,1};
	vec3_t scalevec;

	if ( !ent->model ) {
		G_Error( "script_model_med must have a \"model\"\n" );
	}
	if ( !ent->scriptName ) {
		G_Error( "script_model_med must have a \"scriptname\"\n" );
	}

	ent->blocked = script_mover_blocked;

	// first position at start
	VectorCopy( ent->s.origin, ent->pos1 );

//	VectorCopy( ent->r.currentOrigin, ent->pos1 );
	VectorCopy( ent->pos1, ent->pos2 ); // don't go anywhere just yet

	trap_SetBrushModel( ent, ent->model );

	InitMover( ent );
	ent->reached = NULL;

	if ( ent->spawnflags & 1 ) {
		ent->use = script_mover_use;
		trap_UnlinkEntity( ent ); // make sure it's not visible
		return;
	}

	G_SetAngle( ent, ent->s.angles );

	G_SpawnInt( "health", "0", &ent->health );
	if ( ent->health ) {
		ent->takedamage = qtrue;
	}

	ent->die = script_mover_die;
	ent->pain = script_mover_pain;

	// look for general scaling
	if ( G_SpawnFloat( "modelscale", "1", &scale[0] ) ) {
		scale[2] = scale[1] = scale[0];
	}

	// look for axis specific scaling
	if ( G_SpawnVector( "modelscale_vec", "1 1 1", &scalevec[0] ) ) {
		VectorCopy( scalevec, scale );
	}

	if ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) {
//		ent->s.eType		= ET_MOVERSCALED;
		ent->s.density      = ET_MOVERSCALED;
		// scale is stored in 'angles2'
		VectorCopy( scale, ent->s.angles2 );
	}

	script_mover_spawn( ent );
}
Beispiel #7
0
void SP_func_usable( gentity_t *self )
{
	gi.SetBrushModel( self, self->model );
	InitMover( self );
	VectorCopy( self->s.origin, self->s.pos.trBase );
	VectorCopy( self->s.origin, self->currentOrigin );
	VectorCopy( self->s.origin, self->pos1 );

	self->count = 1;
	if (self->spawnflags & 1)
	{
		self->spawnContents = self->contents;	// It Navs can temporarly turn it "on"
		self->s.solid = 0;
		self->contents = 0;
		self->clipmask = 0;
		self->svFlags |= SVF_NOCLIENT;
		self->s.eFlags |= EF_NODRAW;
		self->count = 0;
	}

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

	if (self->spawnflags & 4)
	{//FIXME: need to be able to do change to something when it's done?  Or not be usable until it's done?
		self->s.eFlags |= EF_ANIM_ONCE;
	}

	self->e_UseFunc = useF_func_usable_use;

	if ( self->health )
	{
		self->takedamage = qtrue;
		self->e_DieFunc = dieF_func_usable_die;
		self->e_PainFunc = painF_func_usable_pain;
	}

	if ( self->endFrame > 0 )
	{
		self->s.frame = self->startFrame = 0;
		self->s.eFlags |= EF_SHADER_ANIM;
	}

	gi.linkentity (self);

	int forceVisible = 0;
	G_SpawnInt( "forcevisible", "0", &forceVisible );
	if ( forceVisible )
	{//can see these through walls with force sight, so must be broadcast
		if ( VectorCompare( self->s.origin, vec3_origin ) )
		{//no origin brush
			self->svFlags |= SVF_BROADCAST;
		}
		self->s.eFlags |= EF_FORCE_VISIBLE;
	}
}
Beispiel #8
0
static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) {
	const FILM *pfilm;
	const MULTI_INIT *pmi;
	const FRAME *pFrame;
	IMAGE *pim;


	assert(BgPal()); // Can't start actor without a background palette
	assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels

	InitMover(pMover);
	InitialPathChecks(pMover, X, Y);

	pfilm = (const FILM *)LockMem(pMover->walkReels[0][FORWARD]);
	pmi = (const MULTI_INIT *)LockMem(FROM_32(pfilm->reels[0].mobj));

//---
	pFrame = (const FRAME *)LockMem(FROM_32(pmi->hMulFrame));

	// get pointer to image
	pim = (IMAGE *)LockMem(READ_32(pFrame));	// handle to image
	pim->hImgPal = TO_32(BgPal());
//---
	pMover->actorObj = MultiInitObject(pmi);

/**/	assert(pMover->actorID == id);
	pMover->actorID = id;

	// add it to display list
	MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj);
	storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0);

	InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate));
	pMover->stepCount = 0;

	MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);

	// If no path, just use first path in the scene
	if (pMover->hCpath != NOPOLY)
		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
	else
		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));

	// Make him the right size
	SetMoverStanding(pMover);

//**** if added 18/11/94, am
	if (X != MAGICX && Y != MAGICY) {
		HideMover(pMover, 0);		// Allows a play to come in before this appears
		pMover->bHidden = false;	// ...but don't stay hidden
	}

	pMover->bActive = true;
}
Beispiel #9
0
void SP_func_usable( gentity_t *self ) 
{
	gi.SetBrushModel( self, self->model );
	InitMover( self );
	VectorCopy( self->s.origin, self->s.pos.trBase );
	VectorCopy( self->s.origin, self->currentOrigin );
	VectorCopy( self->s.origin, self->pos1 );

	self->count = 1;
	if (self->spawnflags & 1)
	{
		self->s.solid = 0;
		self->contents = 0;
		self->clipmask = 0;
		self->svFlags |= SVF_NOCLIENT;
		self->s.eFlags |= EF_NODRAW;
		self->count = 0;
	}

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

	if (self->spawnflags & 4)
	{//FIXME: need to be able to do change to something when it's done?  Or not be usable until it's done?
		self->s.eFlags |= EF_ANIM_ONCE;
	}

	self->e_UseFunc = useF_func_usable_use;

	if ( self->health )
	{
		self->takedamage = qtrue;
		self->e_DieFunc = dieF_func_usable_die;
		self->e_PainFunc = painF_func_usable_pain;
	}

	if ( self->endFrame > 0 )
	{
		self->s.frame = self->startFrame = 0;
		self->s.eFlags |= EF_SHADER_ANIM;
	}

	gi.linkentity (self);
}
Beispiel #10
0
/**
QUAKED func_button (0 .5 .8) ?
When a button is touched, it moves some distance in the direction of its angle, triggers all of its targets, waits some time, then returns to its original position where it can be triggered again.

"model2"	.md3 model to also draw
"angle"		determines the opening direction
"target"	all entities with a matching targetname will be used
"speed"		override the default 40 speed
"wait"		override the default 1 second wait (-1 = never return)
"lip"		override the default 4 pixel lip remaining at end of move
"health"	if set, the button must be killed instead of touched
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_button(gentity_t *ent)
{
	vec3_t		abs_movedir;
	float		distance;
	vec3_t		size;
	float		lip;

	ent->sound1to2 = G_SoundIndex("sound/movers/switches/butn2.wav");

	if (!ent->speed) {
		ent->speed = 40;
	}

	if (!ent->wait) {
		ent->wait = 1;
	}
	ent->wait *= 1000;

	// first position
	VectorCopy(ent->s.origin, ent->pos1);

	// calculate second position
	trap_SetBrushModel(ent, ent->model);

	G_SpawnFloat("lip", "4", &lip);

	G_SetMovedir(ent->s.angles, ent->movedir);
	abs_movedir[0] = fabs(ent->movedir[0]);
	abs_movedir[1] = fabs(ent->movedir[1]);
	abs_movedir[2] = fabs(ent->movedir[2]);
	VectorSubtract(ent->r.maxs, ent->r.mins, size);
	distance = abs_movedir[0] * size[0] + abs_movedir[1] * size[1] + abs_movedir[2] * size[2] - lip;
	VectorMA(ent->pos1, distance, ent->movedir, ent->pos2);

	if (ent->health) {
		// shootable button
		ent->takedamage = qtrue;
	} else {
		// touchable button
		ent->touch = Touch_Button;
	}

	InitMover(ent);
}
Beispiel #11
0
/*QUAKED func_button (0 .5 .8) ?
When a button is touched, it moves some distance in the direction of its angle, triggers all of its targets, waits some time, then returns to its original position where it can be triggered again.

"model2"	.md3 model to also draw
"angle"		determines the opening direction
"target"	all entities with a matching targetname will be used
"speed"		override the default 40 speed
"wait"		override the default 1 second wait (-1 = never return)
"lip"		override the default 4 pixel lip remaining at end of move
"health"	if set, the button must be killed instead of touched
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_button( gentity_t *ent ) {
	vector3		abs_movedir;
	float		distance;
	vector3		size;
	float		lip;

	ent->sound1to2 = G_SoundIndex("sound/movers/switches/butn2.wav");
	
	if ( !ent->speed ) {
		ent->speed = 40;
	}

	if ( !ent->wait ) {
		ent->wait = 1;
	}
	ent->wait *= 1000;

	// first position
	VectorCopy( &ent->s.origin, &ent->pos1 );

	// calculate second position
	trap->SV_SetBrushModel( (sharedEntity_t *)ent, ent->model );

	G_SpawnFloat( "lip", "4", &lip );

	G_SetMovedir( &ent->s.angles, &ent->movedir );
	abs_movedir.x = fabsf(ent->movedir.x);
	abs_movedir.y = fabsf(ent->movedir.y);
	abs_movedir.z = fabsf(ent->movedir.z);
	VectorSubtract( &ent->r.maxs, &ent->r.mins, &size );
	distance = abs_movedir.x * size.x + abs_movedir.y * size.y + abs_movedir.z * size.z - lip;
	VectorMA (&ent->pos1, distance, &ent->movedir, &ent->pos2);

	if (ent->health) {
		// shootable button
		ent->takedamage = qtrue;
	} else {
		// touchable button
		ent->touch = Touch_Button;
	}

	InitMover( ent );
}
Beispiel #12
0
/**
QUAKED func_plat (0 .5 .8) ?
Plats are always drawn in the extended position so they will light correctly.

"lip"		default 8, protrusion above rest position
"height"	total height of movement, defaults to model height
"speed"		overrides default 200.
"dmg"		overrides default 2
"model2"	.md3 model to also draw
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_plat(gentity_t *ent)
{
	float		lip, height;

	ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/plats/pt1_strt.wav");
	ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/plats/pt1_end.wav");

	VectorClear (ent->s.angles);

	G_SpawnFloat("speed", "200", &ent->speed);
	G_SpawnInt("dmg", "2", &ent->damage);
	G_SpawnFloat("wait", "1", &ent->wait);
	G_SpawnFloat("lip", "8", &lip);

	ent->wait = 1000;

	// create second position
	trap_SetBrushModel(ent, ent->model);

	if (!G_SpawnFloat("height", "0", &height)) {
		height = (ent->r.maxs[2] - ent->r.mins[2]) - lip;
	}

	// pos1 is the rest (bottom) position, pos2 is the top
	VectorCopy(ent->s.origin, ent->pos2);
	VectorCopy(ent->pos2, ent->pos1);
	ent->pos1[2] -= height;

	InitMover(ent);

	// touch function keeps the plat from returning while
	// a live player is standing on it
	ent->touch = Touch_Plat;

	ent->blocked = Blocked_Door;

	ent->parent = ent;	// so it can be treated as a door

	// spawn the trigger if one hasn't been custom made
	if (!ent->targetname) {
		SpawnPlatTrigger(ent);
	}
}
Beispiel #13
0
void func_wait_return_solid( gentity_t *self )
{
	//once a frame, see if it's clear.
	self->clipmask = CONTENTS_BODY;//|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;
	if ( !(self->spawnflags&16) || G_TestEntityPosition( self ) == NULL )
	{
		gi.SetBrushModel( self, self->model );
		VectorCopy( self->currentOrigin, self->pos1 );
		InitMover( self );
		/*
		VectorCopy( self->s.origin, self->s.pos.trBase );
		VectorCopy( self->s.origin, self->currentOrigin );
		*/
		//if we moved, we want the *current* origin, not our start origin!
		VectorCopy( self->currentOrigin, self->s.pos.trBase );
		gi.linkentity( self );
		self->svFlags &= ~SVF_NOCLIENT;
		self->s.eFlags &= ~EF_NODRAW;
		self->e_UseFunc = useF_func_usable_use;
		self->clipmask = 0;
		if ( self->target2 && self->target2[0] )
		{
			G_UseTargets2( self, self->activator, self->target2 );
		}
		if ( self->s.eFlags & EF_ANIM_ONCE )
		{//Start our anim
			self->s.frame = 0;
		}
		//NOTE: be sure to reset the brushmodel before doing this or else CONTENTS_OPAQUE may not be on when you call this
		if ( !(self->spawnflags&1) )
		{//START_OFF doesn't effect area portals
			gi.AdjustAreaPortalState( self, qfalse );
		}
	}
	else
	{
		self->clipmask = 0;
		self->e_ThinkFunc = thinkF_func_wait_return_solid;
		self->nextthink = level.time + FRAMETIME;
	}
}
Beispiel #14
0
/*QUAKED func_rotating (0 .5 .8) ? START_ON - X_AXIS Y_AXIS
You need to have an origin brush as part of this entity.  The center of that brush will be
the point around which it is rotated. It will rotate around the Z axis by default.  You can
check either the X_AXIS or Y_AXIS box to change that.

"model2"	.md3 model to also draw
"speed"		determines how fast it moves; default value is 100.
"dmg"		damage to inflict when blocked (2 default)
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_rotating (gentity_t *ent) {
	if ( !ent->speed ) {
		ent->speed = 100;
	}

	// set the axis of rotation
	ent->s.apos.trType = TR_LINEAR;
		 if ( ent->spawnflags & 4 )		ent->s.apos.trDelta.roll	= ent->speed;
	else if ( ent->spawnflags & 8 )		ent->s.apos.trDelta.pitch	= ent->speed;
	else								ent->s.apos.trDelta.yaw		= ent->speed;

	if (!ent->damage)
		ent->damage = 2;

	trap->SV_SetBrushModel( (sharedEntity_t *)ent, ent->model );
	InitMover( ent );

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

	trap->SV_LinkEntity( (sharedEntity_t *)ent );
}
Beispiel #15
0
/*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS
Normally bobs on the Z axis
"model2"	.md3 model to also draw
"height"	amplitude of bob (32 default)
"speed"		seconds to complete a bob cycle (4 default)
"phase"		the 0.0 to 1.0 offset in the cycle to start at
"dmg"		damage to inflict when blocked (2 default)
"color"		constantLight color
"light"		constantLight radius
*/
void SP_func_bobbing (gentity_t *ent) {
	float		height;
	float		phase;

	G_SpawnFloat( "speed", "4", &ent->speed );
	G_SpawnFloat( "height", "32", &height );
	G_SpawnInt( "dmg", "2", &ent->damage );
	G_SpawnFloat( "phase", "0", &phase );

	trap->SV_SetBrushModel( (sharedEntity_t *)ent, ent->model );
	InitMover( ent );

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

	ent->s.pos.trDuration = (int)(ent->speed * 1000);
	ent->s.pos.trTime = (int)(ent->s.pos.trDuration * phase);
	ent->s.pos.trType = TR_SINE;

	// set the axis of bobbing
		 if ( ent->spawnflags & 1 )		ent->s.pos.trDelta.x = height;
	else if ( ent->spawnflags & 2 )		ent->s.pos.trDelta.y = height;
	else								ent->s.pos.trDelta.z = height;
}
Beispiel #16
0
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY RESURECTABLE COMPASS ALLIED AXIS MOUNTED_GUN
Scripted brush entity. A simplified means of moving brushes around based on events.

"modelscale" - Scale multiplier (defaults to 1, and scales uniformly)
"modelscale_vec" - Set scale per-axis.  Overrides "modelscale", so if you have both the "modelscale" is ignored
"model2" optional md3 to draw over the solid clip brush
"scriptname" name used for scripting purposes (like aiName in AI scripting)
"health" optionally make this entity damagable
"description" used with health, if the entity is damagable, it draws a healthbar with this description above it.
*/
void SP_script_mover(gentity_t *ent) {
	float  scale[3] = { 1, 1, 1 };
	vec3_t scalevec;
	char   *modelname;
	char   *tagent;
	char   *s;

	if (!ent->model) {
		G_Error("script_mover must have a \"model\"\n");
	}
	if (!ent->scriptName) {
		G_Error("script_mover must have a \"scriptname\"\n");
	}

	ent->blocked = script_mover_blocked;

	// first position at start
	VectorCopy(ent->s.origin, ent->pos1);

	VectorCopy(ent->pos1, ent->pos2);   // don't go anywhere just yet

	trap_SetBrushModel(ent, ent->model);

	InitMover(ent);
	ent->reached        = NULL;
	ent->s.animMovetype = 0;

	ent->s.density = 0;

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

	if (ent->spawnflags & 8) {
		ent->use = script_mover_use;
	}

	if (ent->spawnflags & 16) {
		ent->s.time2 = 1;
	} else {
		ent->s.time2 = 0;
	}

	if (ent->spawnflags & 32) {
		ent->s.teamNum = TEAM_ALLIES;
	} else if (ent->spawnflags & 64) {
		ent->s.teamNum = TEAM_AXIS;
	} else {
		ent->s.teamNum = TEAM_FREE;
	}

	if (ent->spawnflags & 1) {
		ent->use = script_mover_use;
		trap_UnlinkEntity(ent);   // make sure it's not visible
		return;
	}

	G_SetAngle(ent, ent->s.angles);

	G_SpawnInt("health", "0", &ent->health);
	if (ent->health) {
		ent->takedamage = qtrue;
		ent->count      = ent->health;

		// client needs to know about it as well
		ent->s.effect1Time  = ent->count;
		ent->s.dl_intensity = 255;

		if (G_SpawnString("description", "", &s)) {
			char cs[MAX_INFO_STRING];

			trap_GetConfigstring(CS_SCRIPT_MOVER_NAMES, cs, sizeof (cs));
			Info_SetValueForKey(cs, va("%d", (int)(ent - g_entities)), s);
			trap_SetConfigstring(CS_SCRIPT_MOVER_NAMES, cs);
		}
	} else {
		ent->count = 0;
	}

	ent->die = script_mover_die;

	// look for general scaling
	if (G_SpawnFloat("modelscale", "1", &scale[0])) {
		scale[2] = scale[1] = scale[0];
	}

	if (G_SpawnString("model2", "", &modelname)) {
		char tagname[MAX_QPATH];

		COM_StripExtension(modelname, tagname);
		Q_strcat(tagname, MAX_QPATH, ".tag");

		ent->tagNumber = trap_LoadTag(tagname);
	}

	// look for axis specific scaling
	if (G_SpawnVector("modelscale_vec", "1 1 1", &scalevec[0])) {
		VectorCopy(scalevec, scale);
	}

	if (scale[0] != 1 || scale[1] != 1 || scale[2] != 1) {
		ent->s.density |= 1;
		// scale is stored in 'angles2'
		VectorCopy(scale, ent->s.angles2);
	}

	if (ent->spawnflags & 128) {
		ent->s.density |= 4;
		ent->waterlevel = 0;

		if (G_SpawnString("gun", "", &modelname) && !Q_stricmp(modelname, "browning")) {
			ent->s.density |= 8;
		}

		G_SpawnString("tagent", "", &tagent);
		Q_strncpyz(ent->tagBuffer, tagent, 16);
		ent->s.powerups = -1;
	}

	ent->think     = script_mover_spawn;
	ent->nextthink = level.time + FRAMETIME;
}
Beispiel #17
0
/**
 * Get it into our heads that there's nothing doing.
 * Called at the end of a scene.
 */
void DropMovers() {
	for (int i = 0; i < MAX_MOVERS; i++)
		InitMover(&g_Movers[i]);
}
Beispiel #18
0
/*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER
TOGGLE		wait in both the start and end states for a trigger event.
START_OPEN	the door to moves to its destination when spawned, and operate in reverse.  It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
NOMONSTER	monsters will not trigger this door

"model2"	.md3 model to also draw
"angle"		determines the opening direction
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
"speed"		movement speed (100 default)
"wait"		wait before returning (3 default, -1 = never return)
"lip"		lip remaining at end of move (8 default)
"dmg"		damage to inflict when blocked (2 default)
"color"		constantLight color
"light"		constantLight radius
"health"	if set, the door must be shot open
*/
void SP_func_door (gentity_t *ent) {
	vec3_t	abs_movedir;
	float	distance;
	vec3_t	size;
	float	lip;

	ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/doors/dr1_strt.wav");
	ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/doors/dr1_end.wav");

	ent->blocked = Blocked_Door;

	// default speed of 400
	if (!ent->speed)
		ent->speed = 400;

	// default wait of 2 seconds
	if (!ent->wait)
		ent->wait = 2;
	ent->wait *= 1000;

	// default lip of 8 units
	G_SpawnFloat( "lip", "8", &lip );

	// default damage of 2 points
	G_SpawnInt( "dmg", "2", &ent->damage );

	// first position at start
	VectorCopy( ent->s.origin, ent->pos1 );

	// calculate second position
	trap_SetBrushModel( ent, ent->model );
	G_SetMovedir (ent->s.angles, ent->movedir);
	abs_movedir[0] = fabs(ent->movedir[0]);
	abs_movedir[1] = fabs(ent->movedir[1]);
	abs_movedir[2] = fabs(ent->movedir[2]);
	VectorSubtract( ent->r.maxs, ent->r.mins, size );
	distance = DotProduct( abs_movedir, size ) - lip;
	VectorMA( ent->pos1, distance, ent->movedir, ent->pos2 );

	// if "start_open", reverse position 1 and 2
	if ( ent->spawnflags & 1 ) {
		vec3_t	temp;

		VectorCopy( ent->pos2, temp );
		VectorCopy( ent->s.origin, ent->pos2 );
		VectorCopy( temp, ent->pos1 );
	}

	InitMover( ent );

	ent->nextthink = level.time + FRAMETIME;

	if ( ! (ent->flags & FL_TEAMSLAVE ) ) {
		int health;

		G_SpawnInt( "health", "0", &health );
		if ( health ) {
			ent->takedamage = qtrue;
		}
		if ( ent->targetname || health ) {
			// non touch/shoot doors
			ent->think = Think_MatchTeam;
		} else {
			ent->think = Think_SpawnNewDoorTrigger;
		}
	}


}
Beispiel #19
0
/**
 * Tinsel 2 Moving actor process
 * - 1 per moving actor in current scene.
 */
void T2MoverProcess(CORO_PARAM, const void *param) {
	CORO_BEGIN_CONTEXT;
	CORO_END_CONTEXT(_ctx);

	// Get the co-ordinates - copied to process when it was created
	const MAINIT *rpos = (const MAINIT *)param;
	PMOVER pMover = rpos->pMover;
	int i;
	FILM *pFilm;
	PMULTI_INIT pmi;

	CORO_BEGIN_CODE(_ctx);

	for (i = 0; i < TOTAL_SCALES; i++) {
		if (pMover->walkReels[i][FORWARD])
			break;
	}
	assert(i < TOTAL_SCALES);

	InitMover(pMover);
	InitialPathChecks(pMover, rpos->X, rpos->Y);

	pFilm = (FILM *)LockMem(pMover->walkReels[i][FORWARD]);	// Any old reel
	pmi = (PMULTI_INIT)LockMem(FROM_32(pFilm->reels[0].mobj));

	// Poke in the background palette
	PokeInPalette(pmi);

	pMover->actorObj = MultiInitObject(pmi);
	// FIXME: This is what the original did. A bug, perhaps?
	// pMover->actorID = pMover->actorID;
	pMover->bActive = true;

	// add it to display list
	MultiInsertObject( GetPlayfieldList(FIELD_WORLD), pMover->actorObj );

	InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, pFilm->reels[0].script, ONE_SECOND/pFilm->frate);
	pMover->stepCount = 0;

	MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY);

	// If no path, just use first path in the scene
	if (pMover->hCpath != NOPOLY)
		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath));
	else
		SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly()));

	// Make him the right size
	SetMoverStanding(pMover);

	HideMover(pMover);		// Allows a play to come in before this appears
	pMover->bHidden = false;	// ...but don't stay hidden

	for (;;) {
		if (pMover->bSpecReel) {
			if (!pMover->bHidden)
				StepAnimScript(&pMover->actorAnim);
		} else
			DoMoveActor(pMover);

		CheckBrightness(pMover);

		CORO_SLEEP(1);
	}

	CORO_END_CODE;
}
/*
===============
G_ScriptAction_GotoMarker

  syntax: gotomarker <targetname> <speed> [accel/deccel] [turntotarget] [wait]

  NOTE: speed may be modified to round the duration to the next 50ms for smooth
  transitions
===============
*/
qboolean G_ScriptAction_GotoMarker( gentity_t *ent, char *params ) {
    char    *pString, *token;
    gentity_t *target;
    vec3_t vec;
    float speed, dist;
    qboolean wait = qfalse, turntotarget = qfalse;
    int trType;
    int duration, i;
    vec3_t diff;
    vec3_t angles;

    if ( params && ( ent->scriptStatus.scriptFlags & SCFL_GOING_TO_MARKER ) ) {
        // we can't process a new movement until the last one has finished
        return qfalse;
    }

    if ( !params || ent->scriptStatus.scriptStackChangeTime < level.time ) {          // we are waiting for it to reach destination
        if ( ent->s.pos.trTime + ent->s.pos.trDuration <= level.time ) {  // we made it
            ent->scriptStatus.scriptFlags &= ~SCFL_GOING_TO_MARKER;

            // set the angles at the destination
            BG_EvaluateTrajectory( &ent->s.apos, ent->s.apos.trTime + ent->s.apos.trDuration, ent->s.angles );
            VectorCopy( ent->s.angles, ent->s.apos.trBase );
            VectorCopy( ent->s.angles, ent->r.currentAngles );
            ent->s.apos.trTime = level.time;
            ent->s.apos.trDuration = 0;
            ent->s.apos.trType = TR_STATIONARY;
            VectorClear( ent->s.apos.trDelta );

            // stop moving
            BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin );
            VectorCopy( ent->s.origin, ent->s.pos.trBase );
            VectorCopy( ent->s.origin, ent->r.currentOrigin );
            ent->s.pos.trTime = level.time;
            ent->s.pos.trDuration = 0;
            ent->s.pos.trType = TR_STATIONARY;
            VectorClear( ent->s.pos.trDelta );

            script_linkentity( ent );

            return qtrue;
        }
    } else {    // we have just started this command

        pString = params;
        token = COM_ParseExt( &pString, qfalse );
        if ( !token[0] ) {
            G_Error( "G_Scripting: gotomarker must have an targetname\n" );
        }

        // find the entity with the given "targetname"
        target = G_Find( NULL, FOFS( targetname ), token );

        if ( !target ) {
            G_Error( "G_Scripting: can't find entity with \"targetname\" = \"%s\"\n", token );
        }

        VectorSubtract( target->r.currentOrigin, ent->r.currentOrigin, vec );

        token = COM_ParseExt( &pString, qfalse );
        if ( !token[0] ) {
            G_Error( "G_Scripting: gotomarker must have a speed\n" );
        }

        speed = atof( token );
        trType = TR_LINEAR_STOP;

        while ( token[0] ) {
            token = COM_ParseExt( &pString, qfalse );
            if ( token[0] ) {
                if ( !Q_stricmp( token, "accel" ) ) {
                    trType = TR_ACCELERATE;
                } else if ( !Q_stricmp( token, "deccel" ) )      {
                    trType = TR_DECCELERATE;
                } else if ( !Q_stricmp( token, "wait" ) )      {
                    wait = qtrue;
                } else if ( !Q_stricmp( token, "turntotarget" ) )      {
                    turntotarget = qtrue;
                }
            }
        }

        // start the movement
        if ( ent->s.eType == ET_MOVER ) {

            VectorCopy( vec, ent->movedir );
            VectorCopy( ent->r.currentOrigin, ent->pos1 );
            VectorCopy( target->r.currentOrigin, ent->pos2 );
            ent->speed = speed;
            dist = VectorDistance( ent->pos1, ent->pos2 );
            // setup the movement with the new parameters
            InitMover( ent );

            // start the movement

            SetMoverState( ent, MOVER_1TO2, level.time );
            if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration
                ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 );
                ent->s.pos.trType = trType;
            }
            ent->reached = NULL;

            if ( turntotarget ) {
                duration = ent->s.pos.trDuration;
                VectorCopy( target->s.angles, angles );

                for ( i = 0; i < 3; i++ ) {
                    diff[i] = AngleDifference( angles[i], ent->s.angles[i] );
                    while ( diff[i] > 180 )
                        diff[i] -= 360;
                    while ( diff[i] < -180 )
                        diff[i] += 360;
                }
                VectorCopy( ent->s.angles, ent->s.apos.trBase );
                if ( duration ) {
                    VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta );
                } else {
                    VectorClear( ent->s.apos.trDelta );
                }
                ent->s.apos.trDuration = duration;
                ent->s.apos.trTime = level.time;
                ent->s.apos.trType = TR_LINEAR_STOP;
                if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration
                    ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 );
                    ent->s.pos.trType = trType;
                }
            }

        } else {
            // calculate the trajectory
            ent->s.pos.trType = TR_LINEAR_STOP;
            ent->s.pos.trTime = level.time;
            VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
            dist = VectorNormalize( vec );
            VectorScale( vec, speed, ent->s.pos.trDelta );
            ent->s.pos.trDuration = 1000 * ( dist / speed );

            if ( turntotarget ) {
                duration = ent->s.pos.trDuration;
                VectorCopy( target->s.angles, angles );

                for ( i = 0; i < 3; i++ ) {
                    diff[i] = AngleDifference( angles[i], ent->s.angles[i] );
                    while ( diff[i] > 180 )
                        diff[i] -= 360;
                    while ( diff[i] < -180 )
                        diff[i] += 360;
                }
                VectorCopy( ent->s.angles, ent->s.apos.trBase );
                if ( duration ) {
                    VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta );
                } else {
                    VectorClear( ent->s.apos.trDelta );
                }
                ent->s.apos.trDuration = duration;
                ent->s.apos.trTime = level.time;
                ent->s.apos.trType = TR_LINEAR_STOP;
            }

        }

        if ( !wait ) {
            // round the duration to the next 50ms
            if ( ent->s.pos.trDuration % 50 ) {
                float frac;

                frac = (float)( ( ( ent->s.pos.trDuration / 50 ) * 50 + 50 ) - ent->s.pos.trDuration ) / (float)( ent->s.pos.trDuration );
                if ( frac < 1 ) {
                    VectorScale( ent->s.pos.trDelta, 1.0 / ( 1.0 + frac ), ent->s.pos.trDelta );
                    ent->s.pos.trDuration = ( ent->s.pos.trDuration / 50 ) * 50 + 50;
                }
            }

            // set the goto flag, so we can keep processing the move until we reach the destination
            ent->scriptStatus.scriptFlags |= SCFL_GOING_TO_MARKER;
            return qtrue;   // continue to next command
        }

    }

    BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
    BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles );
    script_linkentity( ent );

    return qfalse;
}