summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Angus <tim@ngus.net>2004-06-29 22:20:59 +0000
committerTim Angus <tim@ngus.net>2004-06-29 22:20:59 +0000
commit6348ee0e272b0d662ec19462ad46dfc1446e639e (patch)
tree2478cfd2b62200c0fd5606e8f8801937ac444992
parentb4227be79cccc641a4dd471ff8fb367b79ac84b1 (diff)
* Added a bunch of map entities: trigger_buildable, trigger_class,
trigger_equipment, trigger_heal, trigger_ammo, triggerable func_trains, target_rumble, trigger_gravity
-rw-r--r--entities.def87
-rw-r--r--src/cgame/cg_players.c5
-rw-r--r--src/game/g_active.c8
-rw-r--r--src/game/g_cmds.c2
-rw-r--r--src/game/g_local.h7
-rw-r--r--src/game/g_spawn.c14
-rw-r--r--src/game/g_target.c70
-rw-r--r--src/game/g_trigger.c609
-rw-r--r--src/game/tremulous.h4
9 files changed, 798 insertions, 8 deletions
diff --git a/entities.def b/entities.def
index 979152f7..1254ae7a 100644
--- a/entities.def
+++ b/entities.def
@@ -1045,6 +1045,15 @@ TARGET_* ENTITIES
//=============================================================================
+/*QUAKED target_rumble (1 0 0) (-8 -8 -8) (8 8 8)
+When triggered, this initiates a level-wide rumble effect. All players are affected.
+
+-------- KEYS --------
+speed: severity of the quake (default: 100)
+
+count: duration of the quake (default: 10)
+*/
+
/*QUAKED target_delay (0 .7 .7) (-8 -8 -8) (8 8 8)
Time delay trigger intermediary. Like a target_relay, this can only be fired by other triggers which will cause it in turn to fire its own targets.
@@ -1421,6 +1430,84 @@ TRIGGER_* ENTITIES
//=============================================================================
+/*QUAKED trigger_heal (.5 .5 .5) ? START_OFF SLOW
+Any player that touches this will be healed. It does heal points of healage each server frame. Targeting the trigger will toggle its effects.
+
+-------- SPAWNFLAGS --------
+START_OFF: needs to be triggered (toggle) to activate.
+
+SLOW: changes the heal rate to once per second.
+
+-------- KEYS --------
+heal: Health points to heal (default 5)
+*/
+
+/*QUAKED trigger_ammo (.5 .5 .5) ? SLOW NOENERGY NOCASE
+Any player that touches this will have the ammo for his held weapon restored. It gives ammo in chunks reflected by the key "ammo" each server frame.
+
+-------- SPAWNFLAGS --------
+SLOW: changes the ammo rate to once per second.
+
+NOENERGY: disables this entity for energy weapons.
+
+NOCASE: disables this entity for case based weapons.
+
+-------- KEYS --------
+ammo: Amount of ammo to give (default 1)
+*/
+
+/*QUAKED trigger_gravity (.5 .5 .5) ?
+The gravity (for players) within this trigger is determined by the gravity key. Targetting this entity toggles its effects.
+NOT THROUGHLY TESTED: please report whether or not this works for you.
+
+-------- KEYS --------
+gravity: The gravity within this trigger (default 800).
+*/
+
+/*QUAKED trigger_buildable (.5 .5 .5) ?
+Triggered by a buildable or subset of buildables. If no buildables key is supplied every buildable will trigger this entity.
+NOT THROUGHLY TESTED: please report whether or not this works for you.
+
+-------- KEYS --------
+target: this points to the entity to activate.
+
+buildables: a comma delimited list of buildables which will trigger this entity.
+
+wait: time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once).
+
+random: random time variance in seconds added or subtracted from "wait" delay (default 0 - see Notes).
+*/
+
+/*QUAKED trigger_class (.5 .5 .5) ?
+Triggered by a specific class or subset of classes. If no classes key is supplied every class will trigger this entity.
+NOT THROUGHLY TESTED: please report whether or not this works for you.
+
+-------- KEYS --------
+target: this points to the entity to activate.
+
+classes: a comma delimited list of classes which will trigger this entity.
+
+wait: time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once).
+
+random: random time variance in seconds added or subtracted from "wait" delay (default 0 - see Notes).
+*/
+
+/*QUAKED trigger_equipment (.5 .5 .5) ?
+Triggered by a player carrying some item (weapon or upgrade) or subset of items. If no equipment key is supplied every human will trigger this entity.
+NOT THROUGHLY TESTED: please report whether or not this works for you.
+
+-------- KEYS --------
+target: this points to the entity to activate.
+
+equipment: a comma delimited list of equipment which will trigger this entity.
+
+wait: time in seconds until trigger becomes re-triggerable after it's been touched (default 0.2, -1 = trigger once).
+
+random: random time variance in seconds added or subtracted from "wait" delay (default 0 - see Notes).
+*/
+
+//=============================================================================
+
/*QUAKED trigger_stage (.5 .5 .5) (-8 -8 -8) (8 8 8)
Fires its targets when the team key reaches stage key.
UNTESTED: please report whether or not this works for you.
diff --git a/src/cgame/cg_players.c b/src/cgame/cg_players.c
index 9aba27a4..5b6bfc3a 100644
--- a/src/cgame/cg_players.c
+++ b/src/cgame/cg_players.c
@@ -480,13 +480,14 @@ static qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *modelN
return qfalse;
}
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/icon_%s.tga", modelName, skinName );
+ //FIXME: skins do not load without icon present. do we want icons anyway?
+/* Com_sprintf( filename, sizeof( filename ), "models/players/%s/icon_%s.tga", modelName, skinName );
ci->modelIcon = trap_R_RegisterShaderNoMip( filename );
if( !ci->modelIcon )
{
Com_Printf( "Failed to load icon file: %s\n", filename );
return qfalse;
- }
+ }*/
return qtrue;
}
diff --git a/src/game/g_active.c b/src/game/g_active.c
index f01e2379..acd6a4f1 100644
--- a/src/game/g_active.c
+++ b/src/game/g_active.c
@@ -1096,6 +1096,11 @@ void ClientThink_real( gentity_t *ent )
VectorCopy( client->ps.origin, client->oldOrigin );
+ // moved from after Pmove -- potentially the cause of
+ // future triggering bugs
+ if( !ent->client->noclip )
+ G_TouchTriggers( ent );
+
Pmove( &pm );
// save results of pmove
@@ -1129,9 +1134,6 @@ void ClientThink_real( gentity_t *ent )
// link entity now, after any personal teleporters have been used
trap_LinkEntity( ent );
- if( !ent->client->noclip )
- G_TouchTriggers( ent );
-
// NOTE: now copy the exact origin over otherwise clients can be snapped into solid
VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c
index 27341b94..cbd10555 100644
--- a/src/game/g_cmds.c
+++ b/src/game/g_cmds.c
@@ -1382,7 +1382,7 @@ void Cmd_ToggleItem_f( gentity_t *ent )
if( ent->client->pers.teamSelection != PTE_HUMANS )
return;
- if( weapon == WP_NONE )
+ if( weapon != WP_NONE )
{
//special case to allow switching between
//the blaster and the primary weapon
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 33379dfd..5972c356 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -217,6 +217,13 @@ struct gentity_s
gentity_t *builder; //TA: occupant of this hovel
qboolean nonSegModel; //TA: this entity uses a nonsegmented player model
+
+ buildable_t bTriggers[ BA_NUM_BUILDABLES ]; //TA: which buildables are triggers
+ pClass_t cTriggers[ PCL_NUM_CLASSES ]; //TA: which classes are triggers
+ weapon_t wTriggers[ WP_NUM_WEAPONS ]; //TA: which weapons are triggers
+ upgrade_t uTriggers[ UP_NUM_UPGRADES ]; //TA: which upgrades are triggers
+
+ int triggerGravity; //TA: gravity for this trigger
};
typedef enum
diff --git a/src/game/g_spawn.c b/src/game/g_spawn.c
index 29967e1c..ebd6dc45 100644
--- a/src/game/g_spawn.c
+++ b/src/game/g_spawn.c
@@ -179,6 +179,12 @@ void SP_trigger_push( gentity_t *ent );
void SP_trigger_teleport( gentity_t *ent );
void SP_trigger_hurt( gentity_t *ent );
void SP_trigger_stage( gentity_t *ent );
+void SP_trigger_buildable( gentity_t *ent );
+void SP_trigger_class( gentity_t *ent );
+void SP_trigger_equipment( gentity_t *ent );
+void SP_trigger_gravity( gentity_t *ent );
+void SP_trigger_heal( gentity_t *ent );
+void SP_trigger_ammo( gentity_t *ent );
void SP_target_delay( gentity_t *ent );
void SP_target_speaker( gentity_t *ent );
@@ -191,6 +197,7 @@ void SP_target_kill( gentity_t *ent );
void SP_target_position( gentity_t *ent );
void SP_target_location( gentity_t *ent );
void SP_target_push( gentity_t *ent );
+void SP_target_rumble( gentity_t *ent );
void SP_light( gentity_t *self );
void SP_info_null( gentity_t *self );
@@ -252,6 +259,12 @@ spawn_t spawns[ ] =
{ "trigger_teleport", SP_trigger_teleport },
{ "trigger_hurt", SP_trigger_hurt },
{ "trigger_stage", SP_trigger_stage },
+ { "trigger_buildable", SP_trigger_buildable },
+ { "trigger_class", SP_trigger_class },
+ { "trigger_equipment", SP_trigger_equipment },
+ { "trigger_gravity", SP_trigger_gravity },
+ { "trigger_heal", SP_trigger_heal },
+ { "trigger_ammo", SP_trigger_ammo },
// targets perform no action by themselves, but must be triggered
// by another entity
@@ -265,6 +278,7 @@ spawn_t spawns[ ] =
{ "target_position", SP_target_position },
{ "target_location", SP_target_location },
{ "target_push", SP_target_push },
+ { "target_rumble", SP_target_rumble },
{ "light", SP_light },
{ "path_corner", SP_path_corner },
diff --git a/src/game/g_target.c b/src/game/g_target.c
index ee2d096f..64a3f33e 100644
--- a/src/game/g_target.c
+++ b/src/game/g_target.c
@@ -320,3 +320,73 @@ void SP_target_location( gentity_t *self )
G_SetOrigin( self, self->s.origin );
}
+
+/*
+===============
+target_rumble_think
+===============
+*/
+void target_rumble_think( gentity_t *self )
+{
+ int i;
+ gentity_t *ent;
+
+ if( self->last_move_time < level.time )
+ self->last_move_time = level.time + 0.5;
+
+ for( i = 0, ent = g_entities + i; i < level.num_entities; i++, ent++ )
+ {
+ if( !ent->inuse )
+ continue;
+
+ if( !ent->client )
+ continue;
+
+ if( ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
+ continue;
+
+ ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
+ ent->client->ps.velocity[ 0 ] += crandom( ) * 150;
+ ent->client->ps.velocity[ 1 ] += crandom( ) * 150;
+ ent->client->ps.velocity[ 2 ] = self->speed;
+ }
+
+ if( level.time < self->timestamp )
+ self->nextthink = level.time + FRAMETIME;
+}
+
+/*
+===============
+target_rumble_use
+===============
+*/
+void target_rumble_use( gentity_t *self, gentity_t *other, gentity_t *activator )
+{
+ self->timestamp = level.time + ( self->count * FRAMETIME );
+ self->nextthink = level.time + FRAMETIME;
+ self->activator = activator;
+ self->last_move_time = 0;
+}
+
+/*
+===============
+SP_target_rumble
+===============
+*/
+void SP_target_rumble( gentity_t *self )
+{
+ if( !self->targetname )
+ {
+ G_Printf( S_COLOR_YELLOW "WARNING: untargeted %s at %s\n", self->classname,
+ vtos( self->s.origin ) );
+ }
+
+ if( !self->count )
+ self->count = 10;
+
+ if( !self->speed )
+ self->speed = 100;
+
+ self->think = target_rumble_think;
+ self->use = target_rumble_use;
+}
diff --git a/src/game/g_trigger.c b/src/game/g_trigger.c
index cffa0e66..01279cd6 100644
--- a/src/game/g_trigger.c
+++ b/src/game/g_trigger.c
@@ -503,6 +503,11 @@ void G_Checktrigger_stages( pTeam_t team, stage_t stage )
}
+/*
+===============
+trigger_stage_use
+===============
+*/
void trigger_stage_use( gentity_t *self, gentity_t *other, gentity_t *activator )
{
G_UseTargets( self, self );
@@ -517,3 +522,607 @@ void SP_trigger_stage( gentity_t *self )
self->r.svFlags = SVF_NOCLIENT;
}
+
+
+/*
+===============
+trigger_buildable_trigger
+===============
+*/
+void trigger_buildable_trigger( gentity_t *self, gentity_t *activator )
+{
+ int i = 0;
+
+ self->activator = activator;
+ if( self->nextthink )
+ return; // can't retrigger until the wait is over
+
+ //if there is no buildable list every buildable triggers
+ if( self->bTriggers[ i ] == BA_NONE )
+ G_UseTargets( self, activator );
+ else
+ {
+ //otherwise check against the list
+ for( i = 0; self->bTriggers[ i ] != BA_NONE; i++ )
+ {
+ if( activator->s.modelindex == self->bTriggers[ i ] )
+ {
+ G_UseTargets( self, activator );
+ return;
+ }
+ }
+ }
+
+ if( self->wait > 0 )
+ {
+ self->think = multi_wait;
+ self->nextthink = level.time + ( self->wait + self->random * crandom( ) ) * 1000;
+ }
+ else
+ {
+ // we can't just remove (self) here, because this is a touch function
+ // called while looping through area links...
+ self->touch = 0;
+ self->nextthink = level.time + FRAMETIME;
+ self->think = G_FreeEntity;
+ }
+}
+
+/*
+===============
+trigger_buildable_touch
+===============
+*/
+void trigger_buildable_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
+{
+ //only triggered by buildables
+ if( other->s.eType != ET_BUILDABLE )
+ return;
+
+ trigger_buildable_trigger( ent, other );
+}
+
+/*
+===============
+trigger_buildable_use
+===============
+*/
+void trigger_buildable_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
+{
+ trigger_buildable_trigger( ent, activator );
+}
+
+/*
+===============
+SP_trigger_buildable
+===============
+*/
+void SP_trigger_buildable( gentity_t *self )
+{
+ char *buffer;
+ int i = 0;
+ char *p, *q;
+ qboolean EOS = qfalse;
+
+ G_SpawnFloat( "wait", "0.5", &self->wait );
+ G_SpawnFloat( "random", "0", &self->random );
+
+ if( self->random >= self->wait && self->wait >= 0 )
+ {
+ self->random = self->wait - FRAMETIME;
+ G_Printf( S_COLOR_YELLOW "WARNING: trigger_buildable has random >= wait\n" );
+ }
+
+ G_SpawnString( "buildables", "", &buffer );
+
+ p = q = buffer;
+
+ while( *p != '\0' )
+ {
+ //skip to first , or EOS
+ while( *p != ',' && *p != '\0' )
+ p++;
+
+ if( *p == '\0' )
+ EOS = qtrue;
+
+ *p = '\0';
+
+ //strip leading whitespace
+ while( *q == ' ' )
+ q++;
+
+ self->bTriggers[ i ] = BG_FindBuildNumForName( q );
+ if( self->bTriggers[ i ] == BA_NONE )
+ G_Printf( S_COLOR_YELLOW "WARNING: unknown buildable %s in trigger_buildable\n", q );
+ else
+ i++;
+
+ if( !EOS )
+ {
+ p++;
+ q = p;
+ }
+ else
+ break;
+ }
+
+ self->bTriggers[ i ] = BA_NONE;
+
+ self->touch = trigger_buildable_touch;
+ self->use = trigger_buildable_use;
+
+ InitTrigger( self );
+ trap_LinkEntity( self );
+}
+
+
+/*
+===============
+trigger_class_trigger
+===============
+*/
+void trigger_class_trigger( gentity_t *self, gentity_t *activator )
+{
+ int i = 0;
+
+ //sanity check
+ if( !activator->client )
+ return;
+
+ if( activator->client->ps.stats[ STAT_PTEAM ] != PTE_ALIENS )
+ return;
+
+ self->activator = activator;
+ if( self->nextthink )
+ return; // can't retrigger until the wait is over
+
+ //if there is no class list every class triggers (stupid case)
+ if( self->cTriggers[ i ] == PCL_NONE )
+ G_UseTargets( self, activator );
+ else
+ {
+ //otherwise check against the list
+ for( i = 0; self->cTriggers[ i ] != PCL_NONE; i++ )
+ {
+ if( activator->client->ps.stats[ STAT_PCLASS ] == self->cTriggers[ i ] )
+ {
+ G_UseTargets( self, activator );
+ return;
+ }
+ }
+ }
+
+ if( self->wait > 0 )
+ {
+ self->think = multi_wait;
+ self->nextthink = level.time + ( self->wait + self->random * crandom( ) ) * 1000;
+ }
+ else
+ {
+ // we can't just remove (self) here, because this is a touch function
+ // called while looping through area links...
+ self->touch = 0;
+ self->nextthink = level.time + FRAMETIME;
+ self->think = G_FreeEntity;
+ }
+}
+
+/*
+===============
+trigger_class_touch
+===============
+*/
+void trigger_class_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
+{
+ //only triggered by clients
+ if( !other->client )
+ return;
+
+ trigger_class_trigger( ent, other );
+}
+
+/*
+===============
+trigger_class_use
+===============
+*/
+void trigger_class_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
+{
+ trigger_class_trigger( ent, activator );
+}
+
+/*
+===============
+SP_trigger_class
+===============
+*/
+void SP_trigger_class( gentity_t *self )
+{
+ char *buffer;
+ int i = 0;
+ char *p, *q;
+ qboolean EOS = qfalse;
+
+ G_SpawnFloat( "wait", "0.5", &self->wait );
+ G_SpawnFloat( "random", "0", &self->random );
+
+ if( self->random >= self->wait && self->wait >= 0 )
+ {
+ self->random = self->wait - FRAMETIME;
+ G_Printf( S_COLOR_YELLOW "WARNING: trigger_class has random >= wait\n" );
+ }
+
+ G_SpawnString( "classes", "", &buffer );
+
+ p = q = buffer;
+
+ while( *p != '\0' )
+ {
+ //skip to first , or EOS
+ while( *p != ',' && *p != '\0' )
+ p++;
+
+ if( *p == '\0' )
+ EOS = qtrue;
+
+ *p = '\0';
+
+ //strip leading whitespace
+ while( *q == ' ' )
+ q++;
+
+ self->cTriggers[ i ] = BG_FindClassNumForName( q );
+ if( self->cTriggers[ i ] == PCL_NONE )
+ G_Printf( S_COLOR_YELLOW "WARNING: unknown class %s in trigger_class\n", q );
+ else
+ i++;
+
+ if( !EOS )
+ {
+ p++;
+ q = p;
+ }
+ else
+ break;
+ }
+
+ self->cTriggers[ i ] = PCL_NONE;
+
+ self->touch = trigger_class_touch;
+ self->use = trigger_class_use;
+
+ InitTrigger( self );
+ trap_LinkEntity( self );
+}
+
+
+/*
+===============
+trigger_equipment_trigger
+===============
+*/
+void trigger_equipment_trigger( gentity_t *self, gentity_t *activator )
+{
+ int i = 0;
+
+ //sanity check
+ if( !activator->client )
+ return;
+
+ if( activator->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
+ return;
+
+ self->activator = activator;
+ if( self->nextthink )
+ return; // can't retrigger until the wait is over
+
+ //if there is no equipment list all equipment triggers (stupid case)
+ if( self->wTriggers[ i ] == WP_NONE && self->wTriggers[ i ] == UP_NONE )
+ G_UseTargets( self, activator );
+ else
+ {
+ //otherwise check against the lists
+ for( i = 0; self->wTriggers[ i ] != WP_NONE; i++ )
+ {
+ if( BG_gotWeapon( self->wTriggers[ i ], activator->client->ps.stats ) )
+ {
+ G_UseTargets( self, activator );
+ return;
+ }
+ }
+
+ for( i = 0; self->uTriggers[ i ] != UP_NONE; i++ )
+ {
+ if( BG_gotItem( self->uTriggers[ i ], activator->client->ps.stats ) )
+ {
+ G_UseTargets( self, activator );
+ return;
+ }
+ }
+ }
+
+ if( self->wait > 0 )
+ {
+ self->think = multi_wait;
+ self->nextthink = level.time + ( self->wait + self->random * crandom( ) ) * 1000;
+ }
+ else
+ {
+ // we can't just remove (self) here, because this is a touch function
+ // called while looping through area links...
+ self->touch = 0;
+ self->nextthink = level.time + FRAMETIME;
+ self->think = G_FreeEntity;
+ }
+}
+
+/*
+===============
+trigger_equipment_touch
+===============
+*/
+void trigger_equipment_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
+{
+ //only triggered by clients
+ if( !other->client )
+ return;
+
+ trigger_equipment_trigger( ent, other );
+}
+
+/*
+===============
+trigger_equipment_use
+===============
+*/
+void trigger_equipment_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
+{
+ trigger_equipment_trigger( ent, activator );
+}
+
+/*
+===============
+SP_trigger_equipment
+===============
+*/
+void SP_trigger_equipment( gentity_t *self )
+{
+ char *buffer;
+ int i = 0, j = 0;
+ char *p, *q;
+ qboolean EOS = qfalse;
+
+ G_SpawnFloat( "wait", "0.5", &self->wait );
+ G_SpawnFloat( "random", "0", &self->random );
+
+ if( self->random >= self->wait && self->wait >= 0 )
+ {
+ self->random = self->wait - FRAMETIME;
+ G_Printf( S_COLOR_YELLOW "WARNING: trigger_equipment has random >= wait\n" );
+ }
+
+ G_SpawnString( "equipment", "", &buffer );
+
+ p = q = buffer;
+
+ while( *p != '\0' )
+ {
+ //skip to first , or EOS
+ while( *p != ',' && *p != '\0' )
+ p++;
+
+ if( *p == '\0' )
+ EOS = qtrue;
+
+ *p = '\0';
+
+ //strip leading whitespace
+ while( *q == ' ' )
+ q++;
+
+ self->wTriggers[ i ] = BG_FindWeaponNumForName( q );
+ self->uTriggers[ j ] = BG_FindUpgradeNumForName( q );
+
+ if( self->wTriggers[ i ] == WP_NONE && self->uTriggers[ j ] == UP_NONE )
+ G_Printf( S_COLOR_YELLOW "WARNING: unknown equipment %s in trigger_class\n", q );
+ else if( self->wTriggers[ i ] != WP_NONE )
+ i++;
+ else if( self->uTriggers[ j ] != UP_NONE )
+ j++;
+
+ if( !EOS )
+ {
+ p++;
+ q = p;
+ }
+ else
+ break;
+ }
+
+ self->wTriggers[ i ] = WP_NONE;
+ self->uTriggers[ j ] = UP_NONE;
+
+ self->touch = trigger_equipment_touch;
+ self->use = trigger_equipment_use;
+
+ InitTrigger( self );
+ trap_LinkEntity( self );
+}
+
+
+/*
+===============
+trigger_gravity_touch
+===============
+*/
+void trigger_gravity_touch( gentity_t *ent, gentity_t *other, trace_t *trace )
+{
+ //only triggered by clients
+ if( !other->client )
+ return;
+
+ other->client->ps.gravity = ent->triggerGravity;
+}
+
+/*
+===============
+trigger_gravity_use
+===============
+*/
+void trigger_gravity_use( gentity_t *ent, gentity_t *other, gentity_t *activator )
+{
+ if( ent->r.linked )
+ trap_UnlinkEntity( ent );
+ else
+ trap_LinkEntity( ent );
+}
+
+
+/*
+===============
+SP_trigger_gravity
+===============
+*/
+void SP_trigger_gravity( gentity_t *self )
+{
+ G_SpawnInt( "gravity", "800", &self->triggerGravity );
+
+ self->touch = trigger_gravity_touch;
+ self->use = trigger_gravity_use;
+
+ InitTrigger( self );
+ trap_LinkEntity( self );
+}
+
+
+/*
+===============
+trigger_heal_use
+===============
+*/
+void trigger_heal_use( gentity_t *self, gentity_t *other, gentity_t *activator )
+{
+ if( self->r.linked )
+ trap_UnlinkEntity( self );
+ else
+ trap_LinkEntity( self );
+}
+
+/*
+===============
+trigger_heal_touch
+===============
+*/
+void trigger_heal_touch( gentity_t *self, gentity_t *other, trace_t *trace )
+{
+ int max;
+
+ if( !other->client )
+ return;
+
+ if( self->timestamp > level.time )
+ return;
+
+ if( self->spawnflags & 2 )
+ self->timestamp = level.time + 1000;
+ else
+ self->timestamp = level.time + FRAMETIME;
+
+ max = other->client->ps.stats[ STAT_MAX_HEALTH ];
+
+ other->health += self->damage;
+
+ if( other->health > max )
+ other->health = max;
+
+ other->client->ps.stats[ STAT_HEALTH ] = other->health;
+}
+
+/*
+===============
+SP_trigger_heal
+===============
+*/
+void SP_trigger_heal( gentity_t *self )
+{
+ G_SpawnInt( "heal", "5", &self->damage );
+
+ self->touch = trigger_heal_touch;
+ self->use = trigger_heal_use;
+
+ InitTrigger( self );
+
+ // link in to the world if starting active
+ if( !( self->spawnflags & 1 ) )
+ trap_LinkEntity( self );
+}
+
+
+/*
+===============
+trigger_ammo_touch
+===============
+*/
+void trigger_ammo_touch( gentity_t *self, gentity_t *other, trace_t *trace )
+{
+ int ammo, clips, maxClips, maxAmmo;
+
+ if( !other->client )
+ return;
+
+ if( other->client->ps.stats[ STAT_PTEAM ] != PTE_HUMANS )
+ return;
+
+ if( self->timestamp > level.time )
+ return;
+
+ if( other->client->ps.weaponstate != WEAPON_READY )
+ return;
+
+ if( BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 2 )
+ return;
+
+ if( !BG_FindUsesEnergyForWeapon( other->client->ps.weapon ) && self->spawnflags & 4 )
+ return;
+
+ if( self->spawnflags & 1 )
+ self->timestamp = level.time + 1000;
+ else
+ self->timestamp = level.time + FRAMETIME;
+
+ BG_FindAmmoForWeapon( other->client->ps.weapon, &maxAmmo, NULL, NULL );
+ BG_unpackAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups,
+ &ammo, &clips, &maxClips );
+
+ if( ( ammo + self->damage ) > maxAmmo )
+ {
+ if( clips < maxClips )
+ {
+ clips++;
+ ammo = 1;
+ }
+ else
+ ammo = maxAmmo;
+ }
+ else
+ ammo += self->damage;
+
+ BG_packAmmoArray( other->client->ps.weapon, other->client->ps.ammo, other->client->ps.powerups,
+ ammo, clips, maxClips );
+}
+
+/*
+===============
+SP_trigger_ammo
+===============
+*/
+void SP_trigger_ammo( gentity_t *self )
+{
+ G_SpawnInt( "ammo", "1", &self->damage );
+
+ self->touch = trigger_ammo_touch;
+
+ InitTrigger( self );
+ trap_LinkEntity( self );
+}
diff --git a/src/game/tremulous.h b/src/game/tremulous.h
index a384852f..3c8f1cd5 100644
--- a/src/game/tremulous.h
+++ b/src/game/tremulous.h
@@ -371,9 +371,9 @@
#define FLAMER_PRICE 450
#define FLAMER_GAS 80
#define FLAMER_REPEAT 300
-#define FLAMER_DMG HDM(30)
+#define FLAMER_DMG HDM(35)
#define FLAMER_RADIUS 50
-#define FLAMER_LIFETIME 1000.0f
+#define FLAMER_LIFETIME 500.0f
#define FLAMER_SPEED 200.0f
#define FLAMER_LAG 0.65f //the amount of player velocity that is added to the fireball