2022-09-18 15:37:21 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Copyright ( C ) 1999 - 2005 , Id Software , Inc .
Copyright ( C ) 2000 - 2013 , Raven Software , Inc .
Copyright ( C ) 2001 - 2013 , Activision , Inc .
Copyright ( C ) 2013 - 2015 , OpenJK contributors
This file is part of the OpenJK source code .
OpenJK is free software ; you can redistribute it and / or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , see < http : //www.gnu.org/licenses/>.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
// g_misc.c
# include "g_headers.h"
# include "g_local.h"
# include "g_functions.h"
# include "g_nav.h"
# include "g_items.h"
extern gentity_t * G_FindDoorTrigger ( gentity_t * door ) ;
extern void G_SetEnemy ( gentity_t * self , gentity_t * enemy ) ;
extern void SetMiscModelDefaults ( gentity_t * ent , useFunc_t use_func , char * material , int solid_mask , int animFlag ,
qboolean take_damage , qboolean damage_model ) ;
# define MAX_AMMO_GIVE 4
/*QUAKED func_group (0 0 0) ?
Used to group brushes together just for editor convenience . They are turned into normal brushes by the utilities .
q3map_onlyvertexlighting 1 = brush only gets vertex lighting ( reduces bsp size ! )
*/
/*QUAKED info_null (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_null ( gentity_t * self ) {
//FIXME: store targetname and vector (origin) in a list for further reference... remove after 1st second of game?
G_SetOrigin ( self , self - > s . origin ) ;
self - > e_ThinkFunc = thinkF_G_FreeEntity ;
//Give other ents time to link
self - > nextthink = level . time + START_TIME_REMOVE_ENTS ;
}
/*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 ) {
//FIXME: store in ref_tag system?
G_SetOrigin ( self , self - > s . origin ) ;
}
/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear noIncidence START_OFF
Non - displayed light .
" light " overrides the default 300 intensity . - affects size
a negative " light " will subtract the light ' s color
' Linear ' checkbox gives linear falloff instead of inverse square
' noIncidence ' checkbox makes lighting smoother
Lights pointed at a target will be spotlights .
" radius " overrides the default 64 unit radius of a spotlight at the target point .
" scale " multiplier for the light intensity - does not affect size ( default 1 )
greater than 1 is brighter , between 0 and 1 is dimmer .
" color " sets the light ' s color
" targetname " to indicate a switchable light - NOTE that all lights with the same targetname will be grouped together and act as one light ( ie : don ' t mix colors , styles or start_off flag )
" style " to specify a specify light style , even for switchable lights !
" style_off " light style to use when switched off ( Only for switchable lights )
1 FLICKER ( first variety )
2 SLOW STRONG PULSE
3 CANDLE ( first variety )
4 FAST STROBE
5 GENTLE PULSE 1
6 FLICKER ( second variety )
7 CANDLE ( second variety )
8 CANDLE ( third variety )
9 SLOW STROBE ( fourth variety )
10 FLUORESCENT FLICKER
11 SLOW PULSE NOT FADE TO BLACK
12 FAST PULSE FOR JEREMY
13 Test Blending
*/
static void misc_lightstyle_set ( gentity_t * ent )
{
const int mLightStyle = ent - > count ;
const int mLightSwitchStyle = ent - > bounceCount ;
const int mLightOffStyle = ent - > fly_sound_debounce_time ;
if ( ! ent - > misc_dlight_active )
{ //turn off
if ( mLightOffStyle ) //i have a light style i'd like to use when off
{
char lightstyle [ 32 ] ;
gi . GetConfigstring ( CS_LIGHT_STYLES + ( mLightOffStyle * 3 ) + 0 , lightstyle , 32 ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 0 , lightstyle ) ;
gi . GetConfigstring ( CS_LIGHT_STYLES + ( mLightOffStyle * 3 ) + 1 , lightstyle , 32 ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 1 , lightstyle ) ;
gi . GetConfigstring ( CS_LIGHT_STYLES + ( mLightOffStyle * 3 ) + 2 , lightstyle , 32 ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 2 , lightstyle ) ;
} else
{
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 0 , " a " ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 1 , " a " ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 2 , " a " ) ;
}
}
else
{ //Turn myself on now
if ( mLightSwitchStyle ) //i have a light style i'd like to use when on
{
char lightstyle [ 32 ] ;
gi . GetConfigstring ( CS_LIGHT_STYLES + ( mLightSwitchStyle * 3 ) + 0 , lightstyle , 32 ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 0 , lightstyle ) ;
gi . GetConfigstring ( CS_LIGHT_STYLES + ( mLightSwitchStyle * 3 ) + 1 , lightstyle , 32 ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 1 , lightstyle ) ;
gi . GetConfigstring ( CS_LIGHT_STYLES + ( mLightSwitchStyle * 3 ) + 2 , lightstyle , 32 ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 2 , lightstyle ) ;
}
else
{
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 0 , " z " ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 1 , " z " ) ;
gi . SetConfigstring ( CS_LIGHT_STYLES + ( mLightStyle * 3 ) + 2 , " z " ) ;
}
}
}
void SP_light ( gentity_t * self ) {
if ( ! self - > targetname )
{ //if i don't have a light style switch, the i go away
G_FreeEntity ( self ) ;
return ;
}
G_SpawnInt ( " style " , " 0 " , & self - > count ) ;
G_SpawnInt ( " switch_style " , " 0 " , & self - > bounceCount ) ;
G_SpawnInt ( " style_off " , " 0 " , & self - > fly_sound_debounce_time ) ;
G_SetOrigin ( self , self - > s . origin ) ;
gi . linkentity ( self ) ;
self - > e_UseFunc = useF_misc_dlight_use ;
self - > e_clThinkFunc = clThinkF_NULL ;
self - > s . eType = ET_GENERAL ;
self - > misc_dlight_active = qfalse ;
self - > svFlags | = SVF_NOCLIENT ;
if ( ! ( self - > spawnflags & 4 ) )
{ //turn myself on now
self - > misc_dlight_active = qtrue ;
}
misc_lightstyle_set ( self ) ;
}
void misc_dlight_use ( gentity_t * ent , gentity_t * other , gentity_t * activator )
{
G_ActivateBehavior ( ent , BSET_USE ) ;
ent - > misc_dlight_active = ( qboolean ) ! ent - > misc_dlight_active ; //toggle
misc_lightstyle_set ( ent ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
TELEPORTERS
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
void TeleportPlayer ( gentity_t * player , vec3_t origin , vec3_t angles )
{
if ( player - > NPC & & ( player - > NPC - > aiFlags & NPCAI_FORM_TELE_NAV ) )
{
//My leader teleported, I was trying to catch up, take this off
player - > NPC - > aiFlags & = ~ NPCAI_FORM_TELE_NAV ;
}
// unlink to make sure it can't possibly interfere with G_KillBox
gi . unlinkentity ( player ) ;
VectorCopy ( origin , player - > client - > ps . origin ) ;
player - > client - > ps . origin [ 2 ] + = 1 ;
VectorCopy ( player - > client - > ps . origin , player - > currentOrigin ) ;
// spit the player out
AngleVectors ( angles , player - > client - > ps . velocity , NULL , NULL ) ;
VectorScale ( player - > client - > ps . velocity , 0 , player - > client - > ps . velocity ) ;
//player->client->ps.pm_time = 160; // hold time
//player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
// toggle the teleport bit so the client knows to not lerp
player - > client - > ps . eFlags ^ = EF_TELEPORT_BIT ;
// set angles
SetClientViewAngle ( player , angles ) ;
// kill anything at the destination
G_KillBox ( player ) ;
// save results of pmove
PlayerStateToEntityState ( & player - > client - > ps , & player - > s ) ;
gi . linkentity ( player ) ;
}
void TeleportMover ( gentity_t * mover , vec3_t origin , vec3_t diffAngles , qboolean snapAngle )
{ //FIXME: need an effect
vec3_t oldAngle , newAngle ;
float speed ;
// unlink to make sure it can't possibly interfere with G_KillBox
gi . unlinkentity ( mover ) ;
//reposition it
VectorCopy ( origin , mover - > s . pos . trBase ) ;
VectorCopy ( origin , mover - > currentOrigin ) ;
//Maintain their previous speed, but adjusted for new direction
if ( snapAngle )
{ //not a diffAngle, actually an absolute angle
vec3_t dir ;
VectorCopy ( diffAngles , newAngle ) ;
AngleVectors ( newAngle , dir , NULL , NULL ) ;
VectorNormalize ( dir ) ; //necessary?
speed = VectorLength ( mover - > s . pos . trDelta ) ;
VectorScale ( dir , speed , mover - > s . pos . trDelta ) ;
mover - > s . pos . trTime = level . time ;
VectorSubtract ( newAngle , mover - > s . apos . trBase , diffAngles ) ;
VectorCopy ( newAngle , mover - > s . apos . trBase ) ;
}
else
{
speed = VectorNormalize ( mover - > s . pos . trDelta ) ;
vectoangles ( mover - > s . pos . trDelta , oldAngle ) ;
VectorAdd ( oldAngle , diffAngles , newAngle ) ;
AngleVectors ( newAngle , mover - > s . pos . trDelta , NULL , NULL ) ;
VectorNormalize ( mover - > s . pos . trDelta ) ;
VectorScale ( mover - > s . pos . trDelta , speed , mover - > s . pos . trDelta ) ;
mover - > s . pos . trTime = level . time ;
//Maintain their previous angles, but adjusted to new orientation
VectorAdd ( mover - > s . apos . trBase , diffAngles , mover - > s . apos . trBase ) ;
}
//Maintain their previous anglespeed, but adjusted to new orientation
speed = VectorNormalize ( mover - > s . apos . trDelta ) ;
VectorAdd ( mover - > s . apos . trDelta , diffAngles , mover - > s . apos . trDelta ) ;
VectorNormalize ( mover - > s . apos . trDelta ) ;
VectorScale ( mover - > s . apos . trDelta , speed , mover - > s . apos . trDelta ) ;
mover - > s . apos . trTime = level . time ;
//Tell them it was teleported this move
mover - > s . eFlags | = EF_TELEPORT_BIT ;
// kill anything at the destination
//G_KillBox (mover);
//FIXME: call touch func instead of killbox?
gi . linkentity ( mover ) ;
}
void teleporter_touch ( gentity_t * self , gentity_t * other , trace_t * trace )
{
gentity_t * dest ;
if ( ! other - > client )
return ;
dest = G_PickTarget ( self - > target ) ;
if ( ! dest ) {
gi . Printf ( " Couldn't find teleporter destination \n " ) ;
return ;
}
TeleportPlayer ( other , dest - > s . origin , dest - > s . angles ) ;
}
/*QUAK-D misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16)
Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object .
*/
void SP_misc_teleporter ( gentity_t * ent )
{
gentity_t * trig ;
if ( ! ent - > target )
{
gi . Printf ( " teleporter without a target. \n " ) ;
G_FreeEntity ( ent ) ;
return ;
}
ent - > s . modelindex = G_ModelIndex ( " models/objects/dmspot.md3 " ) ;
ent - > s . clientNum = 1 ;
// ent->s.loopSound = G_SoundIndex("sound/world/amb10.wav");
ent - > contents = CONTENTS_SOLID ;
G_SetOrigin ( ent , ent - > s . origin ) ;
VectorSet ( ent - > mins , - 32 , - 32 , - 24 ) ;
VectorSet ( ent - > maxs , 32 , 32 , - 16 ) ;
gi . linkentity ( ent ) ;
trig = G_Spawn ( ) ;
trig - > e_TouchFunc = touchF_teleporter_touch ;
trig - > contents = CONTENTS_TRIGGER ;
trig - > target = ent - > target ;
trig - > owner = ent ;
G_SetOrigin ( trig , ent - > s . origin ) ;
VectorSet ( trig - > mins , - 8 , - 8 , 8 ) ;
VectorSet ( trig - > maxs , 8 , 8 , 24 ) ;
gi . linkentity ( trig ) ;
}
/*QUAK-D misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) - - NODRAW
Point teleporters at these .
*/
void SP_misc_teleporter_dest ( gentity_t * ent ) {
if ( ent - > spawnflags & 4 ) {
return ;
}
G_SetOrigin ( ent , ent - > s . origin ) ;
gi . linkentity ( ent ) ;
}
//===========================================================
/*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
" model " arbitrary . md3 or . ase file to display
turns into map triangles - not solid
*/
void SP_misc_model ( gentity_t * ent ) {
G_FreeEntity ( ent ) ;
}
//===========================================================
void setCamera ( gentity_t * ent )
{
vec3_t dir ;
gentity_t * target = 0 ;
// frame holds the rotate speed
if ( ent - > owner - > spawnflags & 1 )
{
ent - > s . frame = 25 ;
}
else if ( ent - > owner - > spawnflags & 2 )
{
ent - > s . frame = 75 ;
}
// clientNum holds the rotate offset
ent - > s . clientNum = ent - > owner - > s . clientNum ;
VectorCopy ( ent - > owner - > s . origin , ent - > s . origin2 ) ;
// see if the portal_camera has a target
if ( ent - > owner - > target ) {
target = G_PickTarget ( ent - > owner - > target ) ;
}
if ( target )
{
VectorSubtract ( target - > s . origin , ent - > owner - > s . origin , dir ) ;
VectorNormalize ( dir ) ;
}
else
{
G_SetMovedir ( ent - > owner - > s . angles , dir ) ;
}
ent - > s . eventParm = DirToByte ( dir ) ;
}
void cycleCamera ( gentity_t * self )
{
self - > owner = G_Find ( self - > owner , FOFS ( targetname ) , self - > target ) ;
if ( self - > owner = = NULL )
{
//Uh oh! Not targeted at any ents! Or reached end of list? Which is it?
//for now assume reached end of list and are cycling
self - > owner = G_Find ( self - > owner , FOFS ( targetname ) , self - > target ) ;
if ( self - > owner = = NULL )
{ //still didn't find one
gi . Printf ( " Couldn't find target for misc_portal_surface \n " ) ;
G_FreeEntity ( self ) ;
return ;
}
}
setCamera ( self ) ;
if ( self - > e_ThinkFunc = = thinkF_cycleCamera )
{
if ( self - > owner - > wait > 0 )
{
self - > nextthink = level . time + self - > owner - > wait ;
}
else
{
self - > nextthink = level . time + self - > wait ;
}
}
}
void misc_portal_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
cycleCamera ( self ) ;
}
void locateCamera ( gentity_t * ent )
{ //FIXME: make this fadeout with distance from misc_camera_portal
ent - > owner = G_Find ( NULL , FOFS ( targetname ) , ent - > target ) ;
if ( ! ent - > owner )
{
gi . Printf ( " Couldn't find target for misc_portal_surface \n " ) ;
G_FreeEntity ( ent ) ;
return ;
}
setCamera ( ent ) ;
if ( ! ent - > targetname )
{ //not targetted, so auto-cycle
if ( G_Find ( ent - > owner , FOFS ( targetname ) , ent - > target ) ! = NULL )
{ //targeted at more than one thing
ent - > e_ThinkFunc = thinkF_cycleCamera ;
if ( ent - > owner - > wait > 0 )
{
ent - > nextthink = level . time + ent - > owner - > wait ;
}
else
{
ent - > nextthink = level . time + ent - > wait ;
}
}
}
}
/*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
The portal surface nearest this entity will show a view from the targeted misc_portal_camera , or a mirror view if untargeted .
This must be within 64 world units of the surface !
targetname - When used , cycles to the next misc_portal_camera it ' s targeted
wait - makes it auto - cycle between all cameras it ' s pointed at at intevervals of specified number of seconds .
cameras will be cycled through in the order they were created on the map .
*/
void SP_misc_portal_surface ( gentity_t * ent )
{
VectorClear ( ent - > mins ) ;
VectorClear ( ent - > maxs ) ;
gi . linkentity ( ent ) ;
ent - > svFlags = SVF_PORTAL ;
ent - > s . eType = ET_PORTAL ;
ent - > wait * = 1000 ;
if ( ! ent - > target )
{ //mirror?
VectorCopy ( ent - > s . origin , ent - > s . origin2 ) ;
}
else
{
ent - > e_ThinkFunc = thinkF_locateCamera ;
ent - > nextthink = level . time + 100 ;
if ( ent - > targetname )
{
ent - > e_UseFunc = useF_misc_portal_use ;
}
}
}
/*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate
The target for a misc_portal_surface . You can set either angles or target another entity ( NOT an info_null ) to determine the direction of view .
" roll " an angle modifier to orient the camera around the target vector ;
*/
void SP_misc_portal_camera ( gentity_t * ent ) {
float roll ;
VectorClear ( ent - > mins ) ;
VectorClear ( ent - > maxs ) ;
gi . linkentity ( ent ) ;
G_SpawnFloat ( " roll " , " 0 " , & roll ) ;
ent - > s . clientNum = roll / 360.0 * 256 ;
ent - > wait * = 1000 ;
}
extern qboolean G_ClearViewEntity ( gentity_t * ent ) ;
extern void G_SetViewEntity ( gentity_t * self , gentity_t * viewEntity ) ;
extern void SP_fx_runner ( gentity_t * ent ) ;
2023-03-05 08:51:50 +00:00
gentity_t * first_camera_view = NULL ;
2022-09-18 15:37:21 +00:00
void camera_die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int mod , int dFlags , int hitLoc )
{
if ( player & & player - > client & & player - > client - > ps . viewEntity = = self - > s . number )
{
2023-03-05 08:51:50 +00:00
first_camera_view = NULL ;
G_UseTargets2 ( self , player , self - > target4 ) ;
2022-09-18 15:37:21 +00:00
G_ClearViewEntity ( player ) ;
G_Sound ( player , self - > soundPos2 ) ;
}
G_UseTargets2 ( self , player , self - > closetarget ) ;
//FIXME: explosion fx/sound
//leave sparks at origin- where base's pole is still at?
gentity_t * sparks = G_Spawn ( ) ;
if ( sparks )
{
sparks - > fxFile = " spark " ;
sparks - > delay = 100 ;
sparks - > random = 500 ;
sparks - > s . angles [ 0 ] = 180 ; //point down
VectorCopy ( self - > s . origin , sparks - > s . origin ) ;
SP_fx_runner ( sparks ) ;
}
//bye!
self - > takedamage = qfalse ;
self - > contents = 0 ;
self - > s . eFlags | = EF_NODRAW ;
self - > s . modelindex = 0 ;
}
void camera_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( ! activator | | ! activator - > client | | activator - > s . number )
{ //really only usable by the player
return ;
}
self - > painDebounceTime = level . time + ( self - > wait * 1000 ) ; //FRAMETIME*5;//don't check for player buttons for 500 ms
// FIXME: I guess we are allowing them to switch to a dead camera. Maybe we should conditionally do this though?
if ( /*self->health <= 0 ||*/ ( player & & player - > client & & player - > client - > ps . viewEntity = = self - > s . number ) )
{ //I'm already viewEntity, or I'm destroyed, find next
gentity_t * next = NULL ;
if ( self - > target2 ! = NULL )
{
next = G_Find ( NULL , FOFS ( targetname ) , self - > target2 ) ;
}
2023-03-05 08:51:50 +00:00
if ( ! next & & first_camera_view )
{ // loop through camera views
next = first_camera_view ;
}
2022-09-18 15:37:21 +00:00
if ( next )
{ //found another one
2023-03-05 08:52:35 +00:00
if ( ! Q_stricmp ( " misc_camera " , next - > classname ) & & next ! = self )
2022-09-18 15:37:21 +00:00
{ //make sure it's another camera
camera_use ( next , other , activator ) ;
}
}
else //if ( self->health > 0 )
{ //I was the last (only?) one, clear out the viewentity
2023-03-05 08:51:50 +00:00
first_camera_view = NULL ;
2022-09-18 15:37:21 +00:00
G_UseTargets2 ( self , activator , self - > target4 ) ;
G_ClearViewEntity ( activator ) ;
G_Sound ( activator , self - > soundPos2 ) ;
}
}
else
{ //set me as view entity
2023-03-05 08:51:50 +00:00
if ( ! first_camera_view )
{
first_camera_view = self ;
}
2022-09-18 15:37:21 +00:00
G_UseTargets2 ( self , activator , self - > target3 ) ;
self - > s . eFlags | = EF_NODRAW ;
self - > s . modelindex = 0 ;
G_SetViewEntity ( activator , self ) ;
G_Sound ( activator , self - > soundPos1 ) ;
}
}
void camera_aim ( gentity_t * self )
{
self - > nextthink = level . time + FRAMETIME ;
if ( player & & player - > client & & player - > client - > ps . viewEntity = = self - > s . number )
{ //I am the viewEntity
2022-10-25 22:12:20 +00:00
if ( ( player - > client - > usercmd . buttons & BUTTON_BLOCKING ) | | player - > client - > usercmd . upmove ) // || player->client->usercmd.forwardmove || player->client->usercmd.rightmove )
2022-09-18 15:37:21 +00:00
{ //player wants to back out of camera
2023-03-05 08:51:50 +00:00
first_camera_view = NULL ;
2022-09-18 15:37:21 +00:00
G_UseTargets2 ( self , player , self - > target4 ) ;
G_ClearViewEntity ( player ) ;
G_Sound ( player , self - > soundPos2 ) ;
self - > painDebounceTime = level . time + ( self - > wait * 1000 ) ; //FRAMETIME*5;//don't check for player buttons for 500 ms
if ( player - > client - > usercmd . upmove > 0 )
{ //stop player from doing anything for a half second after
player - > aimDebounceTime = level . time + 500 ;
}
}
else if ( self - > painDebounceTime < level . time )
{ //check for use button
if ( ( player - > client - > usercmd . buttons & BUTTON_USE ) )
{ //player pressed use button, wants to cycle to next
camera_use ( self , player , player ) ;
}
}
else
{ //don't draw me when being looked through
self - > s . eFlags | = EF_NODRAW ;
self - > s . modelindex = 0 ;
}
}
else if ( self - > health > 0 )
{ //still alive, can draw me again
self - > s . eFlags & = ~ EF_NODRAW ;
self - > s . modelindex = self - > s . modelindex3 ;
}
//update my aim
if ( self - > target )
{
gentity_t * targ = G_Find ( NULL , FOFS ( targetname ) , self - > target ) ;
if ( targ )
{
vec3_t angles , dir ;
VectorSubtract ( targ - > currentOrigin , self - > currentOrigin , dir ) ;
vectoangles ( dir , angles ) ;
//FIXME: if a G2 model, do a bone override..???
VectorCopy ( self - > currentAngles , self - > s . apos . trBase ) ;
for ( int i = 0 ; i < 3 ; i + + )
{
angles [ i ] = AngleNormalize180 ( angles [ i ] ) ;
self - > s . apos . trDelta [ i ] = AngleNormalize180 ( ( angles [ i ] - self - > currentAngles [ i ] ) * 10 ) ;
}
//VectorSubtract( angles, self->currentAngles, self->s.apos.trDelta );
//VectorScale( self->s.apos.trDelta, 10, self->s.apos.trDelta );
self - > s . apos . trTime = level . time ;
self - > s . apos . trDuration = FRAMETIME ;
VectorCopy ( angles , self - > currentAngles ) ;
if ( DistanceSquared ( self - > currentAngles , self - > lastAngles ) > 0.01f ) // if it moved at all, start a loop sound? not exactly the "bestest" solution
{
self - > s . loopSound = G_SoundIndex ( " sound/movers/objects/cameramove_lp2 " ) ;
}
else
{
self - > s . loopSound = 0 ; // not moving so don't bother
}
VectorCopy ( self - > currentAngles , self - > lastAngles ) ;
//G_SetAngles( self, angles );
}
}
}
/*QUAKED misc_camera (0 0 1) (-8 -8 -12) (8 8 16) VULNERABLE
A model in the world that can be used by the player to look through it ' s viewpoint
There will be a video overlay instead of the regular HUD and the FOV will be wider
VULNERABLE - allow camera to be destroyed
" target " - camera will remain pointed at this entity ( if it ' s a train or some other moving object , it will keep following it )
" target2 " - when player is in this camera and hits the use button , it will cycle to this next camera ( if no target2 , returns to normal view )
" target3 " - thing to use when player enters this camera view
" target4 " - thing to use when player leaves this camera view
" closetarget " - ( sigh . . . ) yet another target , fired this when it ' s destroyed
" wait " - how long to wait between being used ( default 0.5 )
*/
void SP_misc_camera ( gentity_t * self )
{
G_SpawnFloat ( " wait " , " 0.5 " , & self - > wait ) ;
//FIXME: spawn base, too
gentity_t * base = G_Spawn ( ) ;
if ( base )
{
base - > s . modelindex = G_ModelIndex ( " models/map_objects/kejim/impcam_base.md3 " ) ;
VectorCopy ( self - > s . origin , base - > s . origin ) ;
base - > s . origin [ 2 ] + = 16 ;
G_SetOrigin ( base , base - > s . origin ) ;
G_SetAngles ( base , self - > s . angles ) ;
gi . linkentity ( base ) ;
}
self - > s . modelindex3 = self - > s . modelindex = G_ModelIndex ( " models/map_objects/kejim/impcam.md3 " ) ;
self - > soundPos1 = G_SoundIndex ( " sound/movers/camera_on.mp3 " ) ;
self - > soundPos2 = G_SoundIndex ( " sound/movers/camera_off.mp3 " ) ;
G_SoundIndex ( " sound/movers/objects/cameramove_lp2 " ) ;
G_SetOrigin ( self , self - > s . origin ) ;
G_SetAngles ( self , self - > s . angles ) ;
self - > s . apos . trType = TR_LINEAR_STOP ; //TR_INTERPOLATE;//
self - > alt_fire = qtrue ;
VectorSet ( self - > mins , - 8 , - 8 , - 12 ) ;
VectorSet ( self - > maxs , 8 , 8 , 0 ) ;
self - > contents = CONTENTS_SOLID ;
gi . linkentity ( self ) ;
self - > fxID = G_EffectIndex ( " spark " ) ;
if ( self - > spawnflags & 1 ) // VULNERABLE
{
self - > takedamage = qtrue ;
}
self - > health = 10 ;
self - > e_DieFunc = dieF_camera_die ;
self - > e_UseFunc = useF_camera_use ;
self - > e_ThinkFunc = thinkF_camera_aim ;
self - > nextthink = level . time + START_TIME_LINK_ENTS ;
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
SHOOTERS
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
void Use_Shooter ( gentity_t * ent , gentity_t * other , gentity_t * activator )
{
/* vec3_t dir;
float deg ;
vec3_t up , right ;
*/
G_ActivateBehavior ( ent , BSET_USE ) ;
/*
// see if we have a target
if ( ent - > enemy ) {
VectorSubtract ( ent - > enemy - > currentOrigin , ent - > s . origin , dir ) ;
VectorNormalize ( dir ) ;
} else {
VectorCopy ( ent - > movedir , dir ) ;
}
// randomize a bit
PerpendicularVector ( up , dir ) ;
CrossProduct ( up , dir , right ) ;
deg = Q_flrand ( - 1.0f , 1.0f ) * ent - > random ;
VectorMA ( dir , deg , up , dir ) ;
deg = Q_flrand ( - 1.0f , 1.0f ) * ent - > random ;
VectorMA ( dir , deg , right , dir ) ;
VectorNormalize ( dir ) ;
switch ( ent - > s . weapon )
{
case WP_GRENADE_LAUNCHER :
fire_grenade ( ent , ent - > s . origin , dir ) ;
break ;
case WP_ROCKET_LAUNCHER :
fire_rocket ( ent , ent - > s . origin , dir ) ;
break ;
case WP_PLASMAGUN :
fire_plasma ( ent , ent - > s . origin , dir ) ;
break ;
}
G_AddEvent ( ent , EV_FIRE_WEAPON , 0 ) ;
*/
}
void InitShooter ( gentity_t * ent , int weapon ) {
ent - > e_UseFunc = useF_Use_Shooter ;
ent - > s . weapon = weapon ;
RegisterItem ( FindItemForWeapon ( ( weapon_t ) weapon ) ) ;
G_SetMovedir ( ent - > s . angles , ent - > movedir ) ;
if ( ! ent - > random ) {
ent - > random = 1.0 ;
}
ent - > random = sin ( M_PI * ent - > random / 180 ) ;
// target might be a moving object, so we can't set movedir for it
if ( ent - > target ) {
G_SetEnemy ( ent , G_PickTarget ( ent - > target ) ) ;
}
gi . linkentity ( ent ) ;
}
/*QUAK-ED shooter_rocket (1 0 0) (-16 -16 -16) (16 16 16)
Fires at either the target or the current direction .
" random " the number of degrees of deviance from the taget . ( 1.0 default )
*/
void SP_shooter_rocket ( gentity_t * ent )
{
// InitShooter( ent, WP_TETRION_DISRUPTOR );
}
/*QUAK-ED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16)
Fires at either the target or the current direction .
" random " is the number of degrees of deviance from the taget . ( 1.0 default )
*/
void SP_shooter_plasma ( gentity_t * ent )
{
InitShooter ( ent , WP_BRYAR_PISTOL ) ;
}
/*QUAK-ED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16)
Fires at either the target or the current direction .
" random " is the number of degrees of deviance from the taget . ( 1.0 default )
*/
void SP_shooter_grenade ( gentity_t * ent )
{
// InitShooter( ent, WP_GRENADE_LAUNCHER);
}
/*QUAKED object_cargo_barrel1 (1 0 0) (-16 -16 -16) (16 16 29) SMALLER KLINGON NO_SMOKE POWDERKEG
Cargo Barrel
if given a targetname , using it makes it explode
SMALLER - ( - 8 , - 8 , - 16 ) ( 8 , 8 , 8 )
KLINGON - klingon style barrel
NO_SMOKE - will not leave lingering smoke cloud when killed
POWDERKEG - wooden explosive barrel
health default = 20
splashDamage default = 100
splashRadius default = 200
*/
void SP_object_cargo_barrel1 ( gentity_t * ent )
{
if ( ent - > spawnflags & 8 )
{
//FIXME: make an index into an external string table for localization
ent - > fullName = " Powderkeg Barrel " ;
ent - > s . modelindex = G_ModelIndex ( " /models/mapobjects/cargo/barrel_wood2.md3 " ) ;
// ent->sounds = G_SoundIndex("sound/weapons/explosions/explode3.wav");
}
else if ( ent - > spawnflags & 2 )
{
//FIXME: make an index into an external string table for localization
ent - > fullName = " Klingon Cargo Barrel " ;
ent - > s . modelindex = G_ModelIndex ( " /models/mapobjects/scavenger/k_barrel.md3 " ) ;
// ent->sounds = G_SoundIndex("sound/weapons/explosions/explode4.wav");
}
else
{
//FIXME: make an index into an external string table for localization
ent - > fullName = " Federation Cargo Barrel " ;
ent - > s . modelindex = G_ModelIndex ( va ( " /models/mapobjects/cargo/barrel%i.md3 " , Q_irand ( 0 , 2 ) ) ) ;
// ent->sounds = G_SoundIndex("sound/weapons/explosions/explode1.wav");
}
ent - > contents = CONTENTS_SOLID | CONTENTS_OPAQUE ;
if ( ent - > spawnflags & 1 )
{
VectorSet ( ent - > mins , - 8 , - 8 , - 16 ) ;
VectorSet ( ent - > maxs , 8 , 8 , 8 ) ;
}
else
{
VectorSet ( ent - > mins , - 16 , - 16 , - 16 ) ;
VectorSet ( ent - > maxs , 16 , 16 , 29 ) ;
}
G_SetOrigin ( ent , ent - > s . origin ) ;
VectorCopy ( ent - > s . angles , ent - > s . apos . trBase ) ;
if ( ! ent - > health )
ent - > health = 20 ;
if ( ! ent - > splashDamage )
ent - > splashDamage = 100 ;
if ( ! ent - > splashRadius )
ent - > splashRadius = 200 ;
ent - > takedamage = qtrue ;
ent - > e_DieFunc = dieF_ExplodeDeath_Wait ;
if ( ent - > targetname )
ent - > e_UseFunc = useF_GoExplodeDeath ;
gi . linkentity ( ent ) ;
}
/*QUAKED misc_dlight (0.2 0.8 0.2) (-4 -4 -4) (4 4 4) STARTOFF FADEON FADEOFF PULSE
Dynamic light , toggles on and off when used
STARTOFF - Starts off
FADEON - Fades from 0 Radius to start Radius
FADEOFF - Fades from current Radius to 0 Radius before turning off
PULSE - This flag must be checked if you want it to fade / switch between start and final RGBA , otherwise it will just sit at startRGBA
ownername - Will display the light at the origin of the entity with this targetname
startRGBA - Red Green Blue Radius to start with - This MUST be set or your light won ' t do anything
These next values are used only if you want to fade / switch between 2 values ( PULSE flag on )
finalRGBA - Red Green Blue Radius to end with
speed - how long to take to fade from start to final and final to start . Also how long to fade on and off if appropriate flags are checked ( seconds )
finaltime - how long to hold at final ( seconds )
starttime - how long to hold at start ( seconds )
TODO : Add random to speed / radius ?
*/
void SP_misc_dlight ( gentity_t * ent )
{
G_SetOrigin ( ent , ent - > s . origin ) ;
gi . linkentity ( ent ) ;
ent - > speed * = 1000 ;
ent - > wait * = 1000 ;
ent - > radius * = 1000 ;
//FIXME: attach self to a train or something?
ent - > e_UseFunc = useF_misc_dlight_use ;
ent - > misc_dlight_active = qfalse ;
ent - > e_clThinkFunc = clThinkF_NULL ;
ent - > s . eType = ET_GENERAL ;
//Delay first think so we can find owner
if ( ent - > ownername )
{
ent - > e_ThinkFunc = thinkF_misc_dlight_think ;
ent - > nextthink = level . time + START_TIME_LINK_ENTS ;
}
if ( ! ( ent - > spawnflags & 1 ) )
{ //Turn myself on now
GEntity_UseFunc ( ent , ent , ent ) ;
}
}
void misc_dlight_use_old ( gentity_t * ent , gentity_t * other , gentity_t * activator )
{
G_ActivateBehavior ( ent , BSET_USE ) ;
if ( ent - > misc_dlight_active )
{ //We're on, turn off
if ( ent - > spawnflags & 4 )
{ //fade off
ent - > pushDebounceTime = 3 ;
}
else
{
ent - > misc_dlight_active = qfalse ;
ent - > e_clThinkFunc = clThinkF_NULL ;
ent - > s . eType = ET_GENERAL ;
ent - > svFlags & = ~ SVF_BROADCAST ;
}
}
else
{
//Start at start regardless of when we were turned off
if ( ent - > spawnflags & 4 )
{ //fade on
ent - > pushDebounceTime = 2 ;
}
else
{ //Just start on
ent - > pushDebounceTime = 0 ;
}
ent - > painDebounceTime = level . time ;
ent - > misc_dlight_active = qtrue ;
ent - > e_ThinkFunc = thinkF_misc_dlight_think ;
ent - > nextthink = level . time + FRAMETIME ;
ent - > e_clThinkFunc = clThinkF_CG_DLightThink ;
ent - > s . eType = ET_THINKER ;
ent - > svFlags | = SVF_BROADCAST ; // Broadcast to all clients
}
}
void misc_dlight_think ( gentity_t * ent )
{
//Stay Attached to owner
if ( ent - > owner )
{
G_SetOrigin ( ent , ent - > owner - > currentOrigin ) ;
gi . linkentity ( ent ) ;
}
else if ( ent - > ownername )
{
ent - > owner = G_Find ( NULL , FOFS ( targetname ) , ent - > ownername ) ;
ent - > ownername = NULL ;
}
ent - > nextthink = level . time + FRAMETIME ;
}
void station_pain ( gentity_t * self , gentity_t * inflictor , gentity_t * other , vec3_t point , int damage , int mod , int hitLoc )
{
// self->s.modelindex = G_ModelIndex("/models/mapobjects/stasis/plugin2_in.md3");
// self->s.eFlags &= ~ EF_ANIM_ALLFAST;
// self->s.eFlags |= EF_ANIM_ONCE;
// gi.linkentity (self);
self - > s . modelindex = self - > s . modelindex2 ;
gi . linkentity ( self ) ;
}
// --------------------------------------------------------------------
//
// HEALTH/ARMOR plugin functions
//
// --------------------------------------------------------------------
void health_use ( gentity_t * self , gentity_t * other , gentity_t * activator ) ;
int ITM_AddArmor ( gentity_t * ent , int count ) ;
int ITM_AddHealth ( gentity_t * ent , int count ) ;
void health_shutdown ( gentity_t * self )
{
if ( ! ( self - > s . eFlags & EF_ANIM_ONCE ) )
{
self - > s . eFlags & = ~ EF_ANIM_ALLFAST ;
self - > s . eFlags | = EF_ANIM_ONCE ;
// Switch to and animate its used up model.
if ( ! Q_stricmp ( self - > model , " models/mapobjects/stasis/plugin2.md3 " ) )
{
self - > s . modelindex = self - > s . modelindex2 ;
}
else if ( ! Q_stricmp ( self - > model , " models/mapobjects/borg/plugin2.md3 " ) )
{
self - > s . modelindex = self - > s . modelindex2 ;
}
else if ( ! Q_stricmp ( self - > model , " models/mapobjects/stasis/plugin2_floor.md3 " ) )
{
self - > s . modelindex = self - > s . modelindex2 ;
// G_Sound(self, G_SoundIndex("sound/ambience/stasis/shrinkage1.wav") );
}
else if ( ! Q_stricmp ( self - > model , " models/mapobjects/forge/panels.md3 " ) )
{
self - > s . modelindex = self - > s . modelindex2 ;
}
gi . linkentity ( self ) ;
}
}
void health_think ( gentity_t * ent )
{
int dif ;
// He's dead, Jim. Don't give him health
if ( ent - > enemy - > health < 1 )
{
ent - > count = 0 ;
ent - > e_ThinkFunc = thinkF_NULL ;
}
// Still has power to give
if ( ent - > count > 0 )
{
// For every 3 points of health, you get 1 point of armor
// BUT!!! after health is filled up, you get the full energy going to armor
dif = ent - > enemy - > client - > ps . stats [ STAT_MAX_HEALTH ] - ent - > enemy - > health ;
if ( dif > 3 )
{
dif = 3 ;
}
else if ( dif < 0 )
{
dif = 0 ;
}
if ( dif > ent - > count ) // Can't give more than count
{
dif = ent - > count ;
}
if ( ( ITM_AddHealth ( ent - > enemy , dif ) ) & & ( dif > 0 ) )
{
ITM_AddArmor ( ent - > enemy , 1 ) ; // 1 armor for every 3 health
ent - > count - = dif ;
ent - > nextthink = level . time + 10 ;
}
else // User has taken all health he can hold, see about giving it all to armor
{
dif = ent - > enemy - > client - > ps . stats [ STAT_MAX_HEALTH ] -
ent - > enemy - > client - > ps . stats [ STAT_ARMOR ] ;
if ( dif > 3 )
{
dif = 3 ;
}
else if ( dif < 0 )
{
dif = 0 ;
}
if ( ent - > count < dif ) // Can't give more than count
{
dif = ent - > count ;
}
if ( ( ! ITM_AddArmor ( ent - > enemy , dif ) ) | | ( dif < = 0 ) )
{
ent - > e_UseFunc = useF_health_use ;
ent - > e_ThinkFunc = thinkF_NULL ;
}
else
{
ent - > count - = dif ;
ent - > nextthink = level . time + 10 ;
}
}
}
if ( ent - > count < 1 )
{
health_shutdown ( ent ) ;
}
}
void misc_model_useup ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
G_ActivateBehavior ( self , BSET_USE ) ;
self - > s . eFlags & = ~ EF_ANIM_ALLFAST ;
self - > s . eFlags | = EF_ANIM_ONCE ;
// Switch to and animate its used up model.
self - > s . modelindex = self - > s . modelindex2 ;
gi . linkentity ( self ) ;
// Use target when used
if ( self - > spawnflags & 8 )
{
G_UseTargets ( self , activator ) ;
}
self - > e_UseFunc = useF_NULL ;
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
}
void health_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{ //FIXME: Heal entire team? Or only those that are undying...?
int dif ;
int dif2 ;
int hold ;
G_ActivateBehavior ( self , BSET_USE ) ;
if ( self - > e_ThinkFunc ! = thinkF_NULL )
{
self - > e_ThinkFunc = thinkF_NULL ;
}
else
{
if ( other - > client )
{
// He's dead, Jim. Don't give him health
if ( other - > client - > ps . stats [ STAT_HEALTH ] < 1 )
{
dif = 1 ;
self - > count = 0 ;
}
else
{ // Health
dif = other - > client - > ps . stats [ STAT_MAX_HEALTH ] - other - > client - > ps . stats [ STAT_HEALTH ] ;
// Armor
dif2 = other - > client - > ps . stats [ STAT_MAX_HEALTH ] - other - > client - > ps . stats [ STAT_ARMOR ] ;
hold = ( dif2 - dif ) ;
// For every 3 points of health, you get 1 point of armor
// BUT!!! after health is filled up, you get the full energy going to armor
if ( hold > 0 ) // Need more armor than health
{
// Calculate total amount of station energy needed.
hold = dif / 3 ; // For every 3 points of health, you get 1 point of armor
dif2 - = hold ;
dif2 + = dif ;
dif = dif2 ;
}
}
}
else
{ // Being triggered to be used up
dif = 1 ;
self - > count = 0 ;
}
// Does player already have full health and full armor?
if ( dif > 0 )
{
// G_Sound(self, G_SoundIndex("sound/player/suithealth.wav") );
if ( ( dif > = self - > count ) | | ( self - > count < 1 ) ) // use it all up?
{
health_shutdown ( self ) ;
}
// Use target when used
if ( self - > spawnflags & 8 )
{
G_UseTargets ( self , activator ) ;
}
self - > e_UseFunc = useF_NULL ;
self - > enemy = other ;
self - > e_ThinkFunc = thinkF_health_think ;
self - > nextthink = level . time + 50 ;
}
else
{
// G_Sound(self, G_SoundIndex("sound/weapons/noammo.wav") );
}
}
}
// --------------------------------------------------------------------
//
// AMMO plugin functions
//
// --------------------------------------------------------------------
void ammo_use ( gentity_t * self , gentity_t * other , gentity_t * activator ) ;
int Add_Ammo2 ( gentity_t * ent , int ammoType , int count ) ;
void ammo_shutdown ( gentity_t * self )
{
if ( ! ( self - > s . eFlags & EF_ANIM_ONCE ) )
{
self - > s . eFlags & = ~ EF_ANIM_ALLFAST ;
self - > s . eFlags | = EF_ANIM_ONCE ;
gi . linkentity ( self ) ;
}
}
void ammo_think ( gentity_t * ent )
{
int dif ;
// Still has ammo to give
if ( ent - > count > 0 & & ent - > enemy )
{
dif = ammoData [ AMMO_BLASTER ] . max - ent - > enemy - > client - > ps . ammo [ AMMO_BLASTER ] ;
if ( dif > 2 )
{
dif = 2 ;
}
else if ( dif < 0 )
{
dif = 0 ;
}
if ( ent - > count < dif ) // Can't give more than count
{
dif = ent - > count ;
}
// Give player ammo
if ( Add_Ammo2 ( ent - > enemy , AMMO_BLASTER , dif ) & & ( dif ! = 0 ) )
{
ent - > count - = dif ;
ent - > nextthink = level . time + 10 ;
}
else // User has taken all ammo he can hold
{
ent - > e_UseFunc = useF_ammo_use ;
ent - > e_ThinkFunc = thinkF_NULL ;
}
}
if ( ent - > count < 1 )
{
ammo_shutdown ( ent ) ;
}
}
//------------------------------------------------------------
void ammo_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
int dif ;
G_ActivateBehavior ( self , BSET_USE ) ;
if ( self - > e_ThinkFunc ! = thinkF_NULL )
{
if ( self - > e_UseFunc ! = useF_NULL )
{
self - > e_ThinkFunc = thinkF_NULL ;
}
}
else
{
if ( other - > client )
{
dif = ammoData [ AMMO_BLASTER ] . max - other - > client - > ps . ammo [ AMMO_BLASTER ] ;
}
else
{ // Being triggered to be used up
dif = 1 ;
self - > count = 0 ;
}
// Does player already have full ammo?
if ( dif > 0 )
{
// G_Sound(self, G_SoundIndex("sound/player/suitenergy.wav") );
if ( ( dif > = self - > count ) | | ( self - > count < 1 ) ) // use it all up?
{
ammo_shutdown ( self ) ;
}
}
else
{
// G_Sound(self, G_SoundIndex("sound/weapons/noammo.wav") );
}
// Use target when used
if ( self - > spawnflags & 8 )
{
G_UseTargets ( self , activator ) ;
}
self - > e_UseFunc = useF_NULL ;
G_SetEnemy ( self , other ) ;
self - > e_ThinkFunc = thinkF_ammo_think ;
self - > nextthink = level . time + 50 ;
}
}
//------------------------------------------------------------
void mega_ammo_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
G_ActivateBehavior ( self , BSET_USE ) ;
// Use target when used
G_UseTargets ( self , activator ) ;
// first use, adjust the max ammo a person can hold for each type of ammo
ammoData [ AMMO_BLASTER ] . max = 999 ;
ammoData [ AMMO_POWERCELL ] . max = 999 ;
// Set up our count with whatever the max difference will be
if ( other - > client - > ps . ammo [ AMMO_POWERCELL ] > other - > client - > ps . ammo [ AMMO_BLASTER ] )
self - > count = ammoData [ AMMO_BLASTER ] . max - other - > client - > ps . ammo [ AMMO_BLASTER ] ;
else
self - > count = ammoData [ AMMO_POWERCELL ] . max - other - > client - > ps . ammo [ AMMO_POWERCELL ] ;
// G_Sound( self, G_SoundIndex("sound/player/superenergy.wav") );
// Clear our usefunc, then think until our ammo is full
self - > e_UseFunc = useF_NULL ;
G_SetEnemy ( self , other ) ;
self - > e_ThinkFunc = thinkF_mega_ammo_think ;
self - > nextthink = level . time + 50 ;
self - > s . frame = 0 ;
self - > s . eFlags | = EF_ANIM_ONCE ;
}
//------------------------------------------------------------
void mega_ammo_think ( gentity_t * self )
{
int ammo_add = 5 ;
// If the middle model is done animating, and we haven't switched to the last model yet...
// chuck up the last model.
if ( ! Q_stricmp ( self - > model , " models/mapobjects/forge/power_up_boss.md3 " ) ) // Because the normal forge_ammo model can use this too
{
if ( self - > s . frame > 16 & & self - > s . modelindex ! = self - > s . modelindex2 )
self - > s . modelindex = self - > s . modelindex2 ;
}
if ( self - > enemy & & self - > count > 0 )
{
// Add an equal ammount of ammo to each type
self - > enemy - > client - > ps . ammo [ AMMO_BLASTER ] + = ammo_add ;
self - > enemy - > client - > ps . ammo [ AMMO_POWERCELL ] + = ammo_add ;
// Now cap to prevent overflows
if ( self - > enemy - > client - > ps . ammo [ AMMO_BLASTER ] > ammoData [ AMMO_BLASTER ] . max )
self - > enemy - > client - > ps . ammo [ AMMO_BLASTER ] = ammoData [ AMMO_BLASTER ] . max ;
if ( self - > enemy - > client - > ps . ammo [ AMMO_POWERCELL ] > ammoData [ AMMO_POWERCELL ] . max )
self - > enemy - > client - > ps . ammo [ AMMO_POWERCELL ] = ammoData [ AMMO_POWERCELL ] . max ;
// Decrement the count given counter
self - > count - = ammo_add ;
// If we've given all we should, prevent giving any more, even if they player is no longer full
if ( self - > count < = 0 )
{
self - > count = 0 ;
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
}
else
self - > nextthink = 20 ;
}
}
//------------------------------------------------------------
void switch_models ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
// FIXME: need a sound here!!
if ( self - > s . modelindex2 )
self - > s . modelindex = self - > s . modelindex2 ;
}
//------------------------------------------------------------
void touch_ammo_crystal_tigger ( gentity_t * self , gentity_t * other , trace_t * trace )
{
if ( ! other - > client )
return ;
// dead people can't pick things up
if ( other - > health < 1 )
return ;
// Only player can pick it up
if ( other - > s . number ! = 0 )
{
return ;
}
if ( other - > client - > ps . ammo [ AMMO_POWERCELL ] > = ammoData [ AMMO_POWERCELL ] . max )
{
return ; // can't hold any more
}
// Add the ammo
other - > client - > ps . ammo [ AMMO_POWERCELL ] + = self - > owner - > count ;
if ( other - > client - > ps . ammo [ AMMO_POWERCELL ] > ammoData [ AMMO_POWERCELL ] . max )
{
other - > client - > ps . ammo [ AMMO_POWERCELL ] = ammoData [ AMMO_POWERCELL ] . max ;
}
// Trigger once only
self - > e_TouchFunc = touchF_NULL ;
// swap the model to the version without the crystal and ditch the infostring
self - > owner - > s . modelindex = self - > owner - > s . modelindex2 ;
// play the normal pickup sound
// G_AddEvent( other, EV_ITEM_PICKUP, ITM_AMMO_CRYSTAL_BORG );
// fire item targets
G_UseTargets ( self - > owner , other ) ;
}
//------------------------------------------------------------
void spawn_ammo_crystal_trigger ( gentity_t * ent )
{
gentity_t * other ;
vec3_t mins , maxs ;
// Set the base bounds
VectorCopy ( ent - > s . origin , mins ) ;
VectorCopy ( ent - > s . origin , maxs ) ;
// Now add an area of influence around the thing
for ( int i = 0 ; i < 3 ; i + + )
{
maxs [ i ] + = 48 ;
mins [ i ] - = 48 ;
}
// create a trigger with this size
other = G_Spawn ( ) ;
VectorCopy ( mins , other - > mins ) ;
VectorCopy ( maxs , other - > maxs ) ;
// set up the other bits that the engine needs to know
other - > owner = ent ;
other - > contents = CONTENTS_TRIGGER ;
other - > e_TouchFunc = touchF_touch_ammo_crystal_tigger ;
gi . linkentity ( other ) ;
}
void misc_replicator_item_remove ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
self - > s . eFlags | = EF_NODRAW ;
//self->contents = 0;
self - > s . modelindex = 0 ;
self - > e_UseFunc = useF_misc_replicator_item_spawn ;
//FIXME: pickup sound?
if ( activator - > client )
{
activator - > health + = 5 ;
if ( activator - > health > activator - > client - > ps . stats [ STAT_MAX_HEALTH ] ) // Past max health
{
activator - > health = activator - > client - > ps . stats [ STAT_MAX_HEALTH ] ;
}
}
}
void misc_replicator_item_finish_spawn ( gentity_t * self )
{
//self->contents = CONTENTS_ITEM;
//FIXME: blinks out for a couple frames when transporter effect is done?
self - > e_UseFunc = useF_misc_replicator_item_remove ;
}
void misc_replicator_item_spawn ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
switch ( Q_irand ( 1 , self - > count ) )
{
case 1 :
self - > s . modelindex = self - > bounceCount ;
break ;
case 2 :
self - > s . modelindex = self - > fly_sound_debounce_time ;
break ;
case 3 :
self - > s . modelindex = self - > painDebounceTime ;
break ;
case 4 :
self - > s . modelindex = self - > disconnectDebounceTime ;
break ;
case 5 :
self - > s . modelindex = self - > attackDebounceTime ;
break ;
case 6 : //max
self - > s . modelindex = self - > pushDebounceTime ;
break ;
}
self - > s . eFlags & = ~ EF_NODRAW ;
self - > e_ThinkFunc = thinkF_misc_replicator_item_finish_spawn ;
self - > nextthink = level . time + 4000 ; //shorter?
self - > e_UseFunc = useF_NULL ;
gentity_t * tent = G_TempEntity ( self - > currentOrigin , EV_REPLICATOR ) ;
tent - > owner = self ;
}
/*QUAK-ED misc_replicator_item (0.2 0.8 0.2) (-4 -4 0) (4 4 8)
When used . this will " spawn " an entity with a random model from the ones provided below . . .
Using it again removes the item as if it were picked up .
model - first random model key
model2 - second random model key
model3 - third random model key
model4 - fourth random model key
model5 - fifth random model key
model6 - sixth random model key
NOTE : do not skip one of these model names , start with the lowest and fill in each next highest one with a value . A gap will cause the item to not work correctly .
NOTE : if you use an invalid model , it will still try to use it and show the NULL axis model ( or nothing at all )
targetname - how you refer to it for using it
*/
void SP_misc_replicator_item ( gentity_t * self )
{
if ( self - > model )
{
self - > bounceCount = G_ModelIndex ( self - > model ) ;
self - > count + + ;
if ( self - > model2 )
{
self - > fly_sound_debounce_time = G_ModelIndex ( self - > model2 ) ;
self - > count + + ;
if ( self - > target )
{
self - > painDebounceTime = G_ModelIndex ( self - > target ) ;
self - > count + + ;
if ( self - > target2 )
{
self - > disconnectDebounceTime = G_ModelIndex ( self - > target2 ) ;
self - > count + + ;
if ( self - > target3 )
{
self - > attackDebounceTime = G_ModelIndex ( self - > target3 ) ;
self - > count + + ;
if ( self - > target4 )
{
self - > pushDebounceTime = G_ModelIndex ( self - > target4 ) ;
self - > count + + ;
}
}
}
}
}
}
// G_SoundIndex( "sound/movers/switches/replicator.wav" );
self - > e_UseFunc = useF_misc_replicator_item_spawn ;
self - > s . eFlags | = EF_NODRAW ;
//self->contents = 0;
VectorSet ( self - > mins , - 4 , - 4 , 0 ) ;
VectorSet ( self - > maxs , 4 , 4 , 8 ) ;
G_SetOrigin ( self , self - > s . origin ) ;
G_SetAngles ( self , self - > s . angles ) ;
gi . linkentity ( self ) ;
}
/*QUAKED misc_trip_mine (0.2 0.8 0.2) (-4 -4 -4) (4 4 4) START_ON BROADCAST
Place in a map and point the angles at whatever surface you want it to attach to .
START_ON - If you give it a targetname to make it toggle - able , but want it to start on , set this flag
BROADCAST - ONLY USE THIS IF YOU HAVE TO ! causes the trip wire and loop sound to always happen , use this if the beam drops out in certain situations
The trip mine will attach to that surface and fire it ' s beam away from the surface at an angle perpendicular to it .
targetname - starts off , when used , turns on ( toggles )
FIXME : sometimes we want these to not be shootable . . . maybe just put them behind a force field ?
*/
extern void touchLaserTrap ( gentity_t * ent , gentity_t * other , trace_t * trace ) ;
extern void CreateLaserTrap ( gentity_t * laserTrap , vec3_t start , gentity_t * owner ) ;
void misc_trip_mine_activate ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( self - > e_ThinkFunc = = thinkF_laserTrapThink )
{
self - > s . eFlags & = ~ EF_FIRING ;
self - > s . loopSound = 0 ;
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
}
else
{
self - > e_ThinkFunc = thinkF_laserTrapThink ;
self - > nextthink = level . time + FRAMETIME ;
}
}
void SP_misc_trip_mine ( gentity_t * self )
{
vec3_t forward , end ;
trace_t trace ;
AngleVectors ( self - > s . angles , forward , NULL , NULL ) ;
VectorMA ( self - > s . origin , 128 , forward , end ) ;
gi . trace ( & trace , self - > s . origin , vec3_origin , vec3_origin , end , self - > s . number , MASK_SHOT , G2_NOCOLLIDE , 0 ) ;
if ( trace . allsolid | | trace . startsolid )
{
Com_Error ( ERR_DROP , " misc_trip_mine at %s in solid \n " , vtos ( self - > s . origin ) ) ;
G_FreeEntity ( self ) ;
return ;
}
if ( trace . fraction = = 1.0 )
{
Com_Error ( ERR_DROP , " misc_trip_mine at %s pointed at no surface \n " , vtos ( self - > s . origin ) ) ;
G_FreeEntity ( self ) ;
return ;
}
RegisterItem ( FindItemForWeapon ( WP_TRIP_MINE ) ) ; //precache the weapon
self - > count = 2 /*TRIPWIRE_STYLE*/ ;
vectoangles ( trace . plane . normal , end ) ;
G_SetOrigin ( self , trace . endpos ) ;
G_SetAngles ( self , end ) ;
CreateLaserTrap ( self , trace . endpos , self ) ;
touchLaserTrap ( self , self , & trace ) ;
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
if ( ! self - > targetname | | ( self - > spawnflags & 1 ) )
{ //starts on
misc_trip_mine_activate ( self , self , self ) ;
}
if ( self - > targetname )
{
self - > e_UseFunc = useF_misc_trip_mine_activate ;
}
if ( ( self - > spawnflags & 2 ) ) // broadcast...should only be used in very rare cases. could fix networking, perhaps, but james suggested this because it's easier
{
self - > svFlags | = SVF_BROADCAST ;
}
gi . linkentity ( self ) ;
}
/*QUAKED misc_maglock (0 .5 .8) (-8 -8 -8) (8 8 8) x x x x x x x x
Place facing a door ( using the angle , not a targetname ) and it will lock that door . Can only be destroyed by lightsaber and will automatically unlock the door it ' s attached to
NOTE : place these half - way in the door to make it flush with the door ' s surface .
" target " thing to use when destoryed ( not doors - it automatically unlocks the door it was angled at )
" health " default is 10
*/
void maglock_die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int mod , int dFlags , int hitLoc )
{
//unlock our door if we're the last lock pointed at the door
if ( self - > activator )
{
self - > activator - > lockCount - - ;
if ( ! self - > activator - > lockCount )
{
self - > activator - > svFlags & = ~ SVF_INACTIVE ;
}
}
//use targets
G_UseTargets ( self , attacker ) ;
//die
WP_Explode ( self ) ;
}
void SP_misc_maglock ( gentity_t * self )
{
//NOTE: May have to make these only work on doors that are either untargeted
// or are targeted by a trigger, not doors fired off by scripts, counters
// or other such things?
self - > s . modelindex = G_ModelIndex ( " models/map_objects/imp_detention/door_lock.md3 " ) ;
self - > fxID = G_EffectIndex ( " maglock/explosion " ) ;
G_SetOrigin ( self , self - > s . origin ) ;
self - > e_ThinkFunc = thinkF_maglock_link ;
//FIXME: for some reason, when you re-load a level, these fail to find their doors...? Random? Testing an additional 200ms after the START_TIME_FIND_LINKS
self - > nextthink = level . time + START_TIME_FIND_LINKS + 200 ; //START_TIME_FIND_LINKS;//because we need to let the doors link up and spawn their triggers first!
}
void maglock_link ( gentity_t * self )
{
//find what we're supposed to be attached to
vec3_t forward , start , end ;
trace_t trace ;
AngleVectors ( self - > s . angles , forward , NULL , NULL ) ;
VectorMA ( self - > s . origin , 128 , forward , end ) ;
VectorMA ( self - > s . origin , - 4 , forward , start ) ;
gi . trace ( & trace , start , vec3_origin , vec3_origin , end , self - > s . number , MASK_SHOT , G2_NOCOLLIDE , 0 ) ;
if ( trace . allsolid | | trace . startsolid )
{
Com_Error ( ERR_DROP , " misc_maglock at %s in solid \n " , vtos ( self - > s . origin ) ) ;
G_FreeEntity ( self ) ;
return ;
}
if ( trace . fraction = = 1.0 )
{
self - > e_ThinkFunc = thinkF_maglock_link ;
self - > nextthink = level . time + 100 ;
/*
Com_Error ( ERR_DROP , " misc_maglock at %s pointed at no surface \n " , vtos ( self - > s . origin ) ) ;
G_FreeEntity ( self ) ;
*/
return ;
}
gentity_t * traceEnt = & g_entities [ trace . entityNum ] ;
if ( trace . entityNum > = ENTITYNUM_WORLD | | ! traceEnt | | Q_stricmp ( " func_door " , traceEnt - > classname ) )
{
self - > e_ThinkFunc = thinkF_maglock_link ;
self - > nextthink = level . time + 100 ;
//Com_Error( ERR_DROP,"misc_maglock at %s not pointed at a door\n", vtos(self->s.origin) );
//G_FreeEntity( self );
return ;
}
//check the traceEnt, make sure it's a door and give it a lockCount and deactivate it
//find the trigger for the door
self - > activator = G_FindDoorTrigger ( traceEnt ) ;
if ( ! self - > activator )
{
self - > activator = traceEnt ;
}
self - > activator - > lockCount + + ;
self - > activator - > svFlags | = SVF_INACTIVE ;
//now position and orient it
vectoangles ( trace . plane . normal , end ) ;
G_SetOrigin ( self , trace . endpos ) ;
G_SetAngles ( self , end ) ;
//make it hittable
//FIXME: if rotated/inclined this bbox may be off... but okay if we're a ghoul model?
//self->s.modelindex = G_ModelIndex( "models/map_objects/imp_detention/door_lock.md3" );
VectorSet ( self - > mins , - 8 , - 8 , - 8 ) ;
VectorSet ( self - > maxs , 8 , 8 , 8 ) ;
self - > contents = CONTENTS_CORPSE ;
//make it destroyable
self - > flags | = FL_SHIELDED ; //only damagable by lightsabers
self - > takedamage = qtrue ;
self - > health = 10 ;
self - > e_DieFunc = dieF_maglock_die ;
//self->fxID = G_EffectIndex( "maglock/explosion" );
gi . linkentity ( self ) ;
}
/*
= = = = = = = = = = = = = = = =
EnergyShieldStationSettings
= = = = = = = = = = = = = = = =
*/
void EnergyShieldStationSettings ( gentity_t * ent )
{
G_SpawnInt ( " count " , " 0 " , & ent - > count ) ;
if ( ! ent - > count )
{
switch ( g_spskill - > integer )
{
case 0 : // EASY
ent - > count = 100 ;
break ;
case 1 : // MEDIUM
ent - > count = 75 ;
break ;
default :
case 2 : // HARD
ent - > count = 50 ;
break ;
}
}
}
/*
= = = = = = = = = = = = = = = =
shield_power_converter_use
= = = = = = = = = = = = = = = =
*/
void shield_power_converter_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
int dif , add ;
if ( ! activator | | activator - > s . number ! = 0 )
{
//only the player gets to use these
return ;
}
G_ActivateBehavior ( self , BSET_USE ) ;
if ( self - > setTime < level . time )
{
self - > setTime = level . time + 100 ;
dif = 100 - activator - > client - > ps . stats [ STAT_ARMOR ] ; // FIXME: define for max armor?
if ( dif > 0 & & self - > count ) // Already at full armor?..and do I even have anything to give
{
if ( dif > MAX_AMMO_GIVE )
{
add = MAX_AMMO_GIVE ;
}
else
{
add = dif ;
}
if ( self - > count < add )
{
add = self - > count ;
}
self - > count - = add ;
activator - > client - > ps . stats [ STAT_ARMOR ] + = add ;
self - > s . loopSound = G_SoundIndex ( " sound/interface/shieldcon_run.wav " ) ;
}
if ( self - > count < = 0 )
{
// play empty sound
self - > setTime = level . time + 1000 ; // extra debounce so that the sounds don't overlap too much
G_Sound ( self , G_SoundIndex ( " sound/interface/shieldcon_empty.mp3 " ) ) ;
self - > s . loopSound = 0 ;
if ( self - > s . eFlags & EF_SHADER_ANIM )
{
self - > s . frame = 1 ;
}
}
else if ( activator - > client - > ps . stats [ STAT_ARMOR ] > = 100 ) // FIXME: define for max
{
// play full sound
G_Sound ( self , G_SoundIndex ( " sound/interface/shieldcon_done.mp3 " ) ) ;
self - > setTime = level . time + 1000 ; // extra debounce so that the sounds don't overlap too much
self - > s . loopSound = 0 ;
}
}
if ( self - > s . loopSound )
{
// we will have to shut of the loop sound, so I guess try and do it intelligently...NOTE: this could get completely stomped every time through the loop
// this is fine, since it just controls shutting off the sound when there are situations that could start the sound but not shut it off
self - > e_ThinkFunc = thinkF_poll_converter ;
self - > nextthink = level . time + 500 ;
}
else
{
// sound is already off, so we don't need to "think" about it.
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = 0 ;
}
if ( activator - > client - > ps . stats [ STAT_ARMOR ] > 0 )
{
activator - > client - > ps . powerups [ PW_BATTLESUIT ] = Q3_INFINITE ;
}
}
/*QUAKED misc_model_shield_power_converter (1 0 0) (-16 -16 -16) (16 16 16) x x x USETARGET
# MODELNAME="models / items / psd_big.md3"
Gives shield energy when used .
USETARGET - when used it fires off target
" health " - how much health the model has - default 60 ( zero makes non - breakable )
" targetname " - dies and displays damagemodel when used , if any ( if not , removes itself )
" target " - what to use when it dies
" paintarget " - target to fire when hit ( but not destroyed )
" count " - the amount of ammo given when used ( default 100 )
*/
//------------------------------------------------------------
void SP_misc_model_shield_power_converter ( gentity_t * ent )
{
SetMiscModelDefaults ( ent , useF_shield_power_converter_use , " 4 " , CONTENTS_SOLID , 0 , qfalse , qfalse ) ;
ent - > takedamage = qfalse ;
EnergyShieldStationSettings ( ent ) ;
G_SoundIndex ( " sound/interface/shieldcon_run.wav " ) ;
G_SoundIndex ( " sound/interface/shieldcon_done.mp3 " ) ;
G_SoundIndex ( " sound/interface/shieldcon_empty.mp3 " ) ;
ent - > s . modelindex2 = G_ModelIndex ( " /models/items/psd_big.md3 " ) ; // Precache model
}
/*QUAKED misc_shield_floor_unit (1 0 0) (-16 -16 0) (16 16 32) x x x USETARGET
# MODELNAME=" / models / items / a_shield_converter.md3"
Gives shield energy when used .
USETARGET - when used it fires off target
" health " - how much health the model has - default 60 ( zero makes non - breakable )
" targetname " - dies and displays damagemodel when used , if any ( if not , removes itself )
" target " - what to use when it dies
" paintarget " - target to fire when hit ( but not destroyed )
" count " - the amount of ammo given when used ( default 100 )
*/
//------------------------------------------------------------
void SP_misc_shield_floor_unit ( gentity_t * ent )
{
VectorSet ( ent - > mins , - 16 , - 16 , 0 ) ;
VectorSet ( ent - > maxs , 16 , 16 , 32 ) ;
SetMiscModelDefaults ( ent , useF_shield_power_converter_use , " 4 " , CONTENTS_SOLID , 0 , qfalse , qfalse ) ;
ent - > takedamage = qfalse ;
EnergyShieldStationSettings ( ent ) ;
G_SoundIndex ( " sound/interface/shieldcon_run.wav " ) ;
G_SoundIndex ( " sound/interface/shieldcon_done.mp3 " ) ;
G_SoundIndex ( " sound/interface/shieldcon_empty.mp3 " ) ;
ent - > s . modelindex = G_ModelIndex ( " models/items/a_shield_converter.md3 " ) ; // Precache model
ent - > s . eFlags | = EF_SHADER_ANIM ;
}
/*
= = = = = = = = = = = = = = = =
EnergyAmmoShieldStationSettings
= = = = = = = = = = = = = = = =
*/
void EnergyAmmoStationSettings ( gentity_t * ent )
{
G_SpawnInt ( " count " , " 0 " , & ent - > count ) ;
if ( ! ent - > count )
{
switch ( g_spskill - > integer )
{
case 0 : // EASY
ent - > count = 100 ;
break ;
case 1 : // MEDIUM
ent - > count = 75 ;
break ;
default :
case 2 : // HARD
ent - > count = 50 ;
break ;
}
}
}
// There has to be an easier way to turn off the looping sound...but
// it's the night before beta and my brain is fried
//------------------------------------------------------------------
void poll_converter ( gentity_t * self )
{
self - > s . loopSound = 0 ;
self - > nextthink = 0 ;
self - > e_ThinkFunc = thinkF_NULL ;
}
/*
= = = = = = = = = = = = = = = =
ammo_power_converter_use
= = = = = = = = = = = = = = = =
*/
void ammo_power_converter_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
int add ;
int difBlaster , difPowerCell , difMetalBolts ;
playerState_t * ps ;
if ( ! activator | | activator - > s . number ! = 0 )
{ //only the player gets to use these
return ;
}
G_ActivateBehavior ( self , BSET_USE ) ;
ps = & activator - > client - > ps ;
if ( self - > setTime < level . time )
{
difBlaster = ammoData [ AMMO_BLASTER ] . max - ps - > ammo [ AMMO_BLASTER ] ;
difPowerCell = ammoData [ AMMO_POWERCELL ] . max - ps - > ammo [ AMMO_POWERCELL ] ;
difMetalBolts = ammoData [ AMMO_METAL_BOLTS ] . max - ps - > ammo [ AMMO_METAL_BOLTS ] ;
// Has it got any power left...and can we even use any of it?
if ( self - > count & & ( difBlaster > 0 | | difPowerCell > 0 | | difMetalBolts > 0 ) )
{
// at least one of the ammo types could stand to take on a bit more ammo
self - > setTime = level . time + 100 ;
self - > s . loopSound = G_SoundIndex ( " sound/interface/ammocon_run.wav " ) ;
// dole out ammo in little packets
if ( self - > count > MAX_AMMO_GIVE )
{
add = MAX_AMMO_GIVE ;
}
else if ( self - > count < 0 )
{
add = 0 ;
}
else
{
add = self - > count ;
}
// all weapons fill at same rate...
ps - > ammo [ AMMO_BLASTER ] + = add ;
ps - > ammo [ AMMO_POWERCELL ] + = add ;
ps - > ammo [ AMMO_METAL_BOLTS ] + = add ;
// ...then get clamped to max
if ( ps - > ammo [ AMMO_BLASTER ] > ammoData [ AMMO_BLASTER ] . max )
{
ps - > ammo [ AMMO_BLASTER ] = ammoData [ AMMO_BLASTER ] . max ;
}
if ( ps - > ammo [ AMMO_POWERCELL ] > ammoData [ AMMO_POWERCELL ] . max )
{
ps - > ammo [ AMMO_POWERCELL ] = ammoData [ AMMO_POWERCELL ] . max ;
}
if ( ps - > ammo [ AMMO_METAL_BOLTS ] > ammoData [ AMMO_METAL_BOLTS ] . max )
{
ps - > ammo [ AMMO_METAL_BOLTS ] = ammoData [ AMMO_METAL_BOLTS ] . max ;
}
self - > count - = add ;
}
if ( self - > count < = 0 )
{
// play empty sound
self - > setTime = level . time + 1000 ; // extra debounce so that the sounds don't overlap too much
G_Sound ( self , G_SoundIndex ( " sound/interface/ammocon_empty.mp3 " ) ) ;
self - > s . loopSound = 0 ;
if ( self - > s . eFlags & EF_SHADER_ANIM )
{
self - > s . frame = 1 ;
}
}
else if ( ps - > ammo [ AMMO_BLASTER ] > = ammoData [ AMMO_BLASTER ] . max
& & ps - > ammo [ AMMO_POWERCELL ] > = ammoData [ AMMO_POWERCELL ] . max
& & ps - > ammo [ AMMO_METAL_BOLTS ] > = ammoData [ AMMO_METAL_BOLTS ] . max )
{
// play full sound
G_Sound ( self , G_SoundIndex ( " sound/interface/ammocon_done.wav " ) ) ;
self - > setTime = level . time + 1000 ; // extra debounce so that the sounds don't overlap too much
self - > s . loopSound = 0 ;
}
}
if ( self - > s . loopSound )
{
// we will have to shut of the loop sound, so I guess try and do it intelligently...NOTE: this could get completely stomped every time through the loop
// this is fine, since it just controls shutting off the sound when there are situations that could start the sound but not shut it off
self - > e_ThinkFunc = thinkF_poll_converter ;
self - > nextthink = level . time + 500 ;
}
else
{
// sound is already off, so we don't need to "think" about it.
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = 0 ;
}
}
/*QUAKED misc_model_ammo_power_converter (1 0 0) (-16 -16 -16) (16 16 16) x x x USETARGET
# MODELNAME="models / items / power_converter.md3"
Gives ammo energy when used .
USETARGET - when used it fires off target
" health " - how much health the model has - default 60 ( zero makes non - breakable )
" targetname " - dies and displays damagemodel when used , if any ( if not , removes itself )
" target " - what to use when it dies
" paintarget " - target to fire when hit ( but not destroyed )
" count " - the amount of ammo given when used ( default 100 )
*/
//------------------------------------------------------------
void SP_misc_model_ammo_power_converter ( gentity_t * ent )
{
SetMiscModelDefaults ( ent , useF_ammo_power_converter_use , " 4 " , CONTENTS_SOLID , 0 , qfalse , qfalse ) ;
ent - > takedamage = qfalse ;
EnergyAmmoStationSettings ( ent ) ;
G_SoundIndex ( " sound/interface/ammocon_run.wav " ) ;
G_SoundIndex ( " sound/interface/ammocon_done.mp3 " ) ;
G_SoundIndex ( " sound/interface/ammocon_empty.mp3 " ) ;
ent - > s . modelindex2 = G_ModelIndex ( " /models/items/power_converter.md3 " ) ; // Precache model
}
/*QUAKED misc_ammo_floor_unit (1 0 0) (-16 -16 0) (16 16 32) x x x USETARGET
# MODELNAME="models / items / a_pwr_converter.md3"
Gives ammo energy when used .
USETARGET - when used it fires off target
" health " - how much health the model has - default 60 ( zero makes non - breakable )
" targetname " - dies and displays damagemodel when used , if any ( if not , removes itself )
" target " - what to use when it dies
" paintarget " - target to fire when hit ( but not destroyed )
" count " - the amount of ammo given when used ( default 100 )
*/
//------------------------------------------------------------
void SP_misc_ammo_floor_unit ( gentity_t * ent )
{
VectorSet ( ent - > mins , - 16 , - 16 , 0 ) ;
VectorSet ( ent - > maxs , 16 , 16 , 32 ) ;
SetMiscModelDefaults ( ent , useF_ammo_power_converter_use , " 4 " , CONTENTS_SOLID , 0 , qfalse , qfalse ) ;
ent - > takedamage = qfalse ;
EnergyAmmoStationSettings ( ent ) ;
G_SoundIndex ( " sound/interface/ammocon_run.wav " ) ;
G_SoundIndex ( " sound/interface/ammocon_done.mp3 " ) ;
G_SoundIndex ( " sound/interface/ammocon_empty.mp3 " ) ;
ent - > s . modelindex = G_ModelIndex ( " models/items/a_pwr_converter.md3 " ) ; // Precache model
ent - > s . eFlags | = EF_SHADER_ANIM ;
}
/*
= = = = = = = = = = = = = = = =
welder_think
= = = = = = = = = = = = = = = =
*/
void welder_think ( gentity_t * self )
{
self - > nextthink = level . time + 200 ;
vec3_t org ,
dir ;
mdxaBone_t boltMatrix ;
if ( self - > svFlags & SVF_INACTIVE )
{
return ;
}
int newBolt ;
// could alternate between the two... or make it random... ?
newBolt = gi . G2API_AddBolt ( & self - > ghoul2 [ self - > playerModel ] , " *flash " ) ;
// newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*flash01" );
if ( newBolt ! = - 1 )
{
G_Sound ( self , self - > noise_index ) ;
// G_PlayEffect( "blueWeldSparks", self->playerModel, newBolt, self->s.number);
// The welder gets rotated around a lot, and since the origin is offset by 352 I have to make this super expensive call to position the hurt...
gi . G2API_GetBoltMatrix ( self - > ghoul2 , self - > playerModel , newBolt ,
& boltMatrix , self - > currentAngles , self - > currentOrigin , ( cg . time ? cg . time : level . time ) ,
NULL , self - > s . modelScale ) ;
gi . G2API_GiveMeVectorFromMatrix ( boltMatrix , ORIGIN , org ) ;
VectorSubtract ( self - > currentOrigin , org , dir ) ;
VectorNormalize ( dir ) ;
// we want the welder effect to face inwards....
G_PlayEffect ( " blueWeldSparks " , org , dir ) ;
G_RadiusDamage ( org , self , 10 , 45 , self , MOD_UNKNOWN ) ;
}
}
/*
= = = = = = = = = = = = = = = =
welder_use
= = = = = = = = = = = = = = = =
*/
void welder_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
// Toggle on and off
if ( self - > spawnflags & 1 )
{
self - > nextthink = level . time + FRAMETIME ;
}
else
{
self - > nextthink = - 1 ;
}
self - > spawnflags = ( self - > spawnflags ^ 1 ) ;
}
/*QUAKED misc_model_welder (1 0 0) (-16 -16 -16) (16 16 16) START_OFF
# MODELNAME="models / map_objects / cairn / welder.md3"
When ' on ' emits sparks from it ' s welding points
START_OFF - welder starts off , using it toggles it on / off
*/
//------------------------------------------------------------
void SP_misc_model_welder ( gentity_t * ent )
{
VectorSet ( ent - > mins , 336 , - 16 , 0 ) ;
VectorSet ( ent - > maxs , 368 , 16 , 32 ) ;
SetMiscModelDefaults ( ent , useF_welder_use , " 4 " , CONTENTS_SOLID , 0 , qfalse , qfalse ) ;
ent - > takedamage = qfalse ;
ent - > contents = 0 ;
G_EffectIndex ( " blueWeldSparks " ) ;
ent - > noise_index = G_SoundIndex ( " sound/movers/objects/welding.wav " ) ;
ent - > s . modelindex = G_ModelIndex ( " models/map_objects/cairn/welder.glm " ) ;
// ent->s.modelindex2 = G_ModelIndex( "models/map_objects/cairn/welder.md3" );
ent - > playerModel = gi . G2API_InitGhoul2Model ( ent - > ghoul2 , " models/map_objects/cairn/welder.glm " , ent - > s . modelindex , NULL_HANDLE , NULL_HANDLE , 0 , 0 ) ;
ent - > s . radius = 400.0f ; // the origin of the welder is offset by approximately 352, so we need the big radius
ent - > e_ThinkFunc = thinkF_welder_think ;
ent - > nextthink = level . time + 1000 ;
if ( ent - > spawnflags & 1 )
{
ent - > nextthink = - 1 ;
}
}
/*
= = = = = = = = = = = = = = = =
jabba_cam_use
= = = = = = = = = = = = = = = =
*/
void jabba_cam_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( self - > spawnflags & 1 )
{
self - > spawnflags & = ~ 1 ;
gi . G2API_SetBoneAnimIndex ( & self - > ghoul2 [ self - > playerModel ] , self - > rootBone , 15 , 0 , BONE_ANIM_OVERRIDE_FREEZE , - 1.5f , ( cg . time ? cg . time : level . time ) , - 1 , 0 ) ;
}
else
{
self - > spawnflags | = 1 ;
gi . G2API_SetBoneAnimIndex ( & self - > ghoul2 [ self - > playerModel ] , self - > rootBone , 0 , 15 , BONE_ANIM_OVERRIDE_FREEZE , 1.5f , ( cg . time ? cg . time : level . time ) , - 1 , 0 ) ;
}
}
/*QUAKED misc_model_jabba_cam (1 0 0) ( 0 -8 0) (60 8 16) EXTENDED
# MODELNAME="models / map_objects / nar_shaddar / jabacam.md3"
The eye camera that popped out of Jabba ' s front door
EXTENDED - Starts in the extended position
targetname - Toggles it on / off
*/
//-----------------------------------------------------
void SP_misc_model_jabba_cam ( gentity_t * ent )
//-----------------------------------------------------
{
VectorSet ( ent - > mins , - 60.0f , - 8.0f , 0.0f ) ;
VectorSet ( ent - > maxs , 60.0f , 8.0f , 16.0f ) ;
SetMiscModelDefaults ( ent , useF_jabba_cam_use , " 4 " , 0 , 0 , qfalse , qfalse ) ;
G_SetAngles ( ent , ent - > s . angles ) ;
ent - > s . modelindex = G_ModelIndex ( " models/map_objects/nar_shaddar/jabacam/jabacam.glm " ) ;
ent - > playerModel = gi . G2API_InitGhoul2Model ( ent - > ghoul2 , " models/map_objects/nar_shaddar/jabacam/jabacam.glm " , ent - > s . modelindex , NULL_HANDLE , NULL_HANDLE , 0 , 0 ) ;
ent - > s . radius = 150.0f ; //......
VectorSet ( ent - > s . modelScale , 1.0f , 1.0f , 1.0f ) ;
ent - > rootBone = gi . G2API_GetBoneIndex ( & ent - > ghoul2 [ ent - > playerModel ] , " model_root " , qtrue ) ;
ent - > e_UseFunc = useF_jabba_cam_use ;
ent - > takedamage = qfalse ;
// start extended..
if ( ent - > spawnflags & 1 )
{
gi . G2API_SetBoneAnimIndex ( & ent - > ghoul2 [ ent - > playerModel ] , ent - > rootBone , 0 , 15 , BONE_ANIM_OVERRIDE_FREEZE , 0.6f , cg . time , - 1 , - 1 ) ;
}
gi . linkentity ( ent ) ;
}
//------------------------------------------------------------------------
void misc_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
misc_model_breakable_die ( self , other , activator , 100 , MOD_UNKNOWN ) ;
}
/*QUAKED misc_exploding_crate (1 0 0.25) (-24 -24 0) (24 24 64)
# MODELNAME="models / map_objects / nar_shaddar / crate_xplode.md3"
Basic exploding crate
" health " - how much health the model has - default 40 ( zero makes non - breakable )
" splashRadius " - radius to do damage in - default 128
" splashDamage " - amount of damage to do when it explodes - default 50
" targetname " - auto - explodes
" target " - what to use when it dies
*/
//------------------------------------------------------------
void SP_misc_exploding_crate ( gentity_t * ent )
{
G_SpawnInt ( " health " , " 40 " , & ent - > health ) ;
G_SpawnInt ( " splashRadius " , " 128 " , & ent - > splashRadius ) ;
G_SpawnInt ( " splashDamage " , " 50 " , & ent - > splashDamage ) ;
ent - > s . modelindex = G_ModelIndex ( " models/map_objects/nar_shaddar/crate_xplode.md3 " ) ;
G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ;
G_EffectIndex ( " chunks/metalexplode " ) ;
VectorSet ( ent - > mins , - 24 , - 24 , 0 ) ;
VectorSet ( ent - > maxs , 24 , 24 , 64 ) ;
ent - > contents = CONTENTS_SOLID | CONTENTS_OPAQUE | CONTENTS_BODY | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ; //CONTENTS_SOLID;
ent - > takedamage = qtrue ;
G_SetOrigin ( ent , ent - > s . origin ) ;
VectorCopy ( ent - > s . angles , ent - > s . apos . trBase ) ;
gi . linkentity ( ent ) ;
if ( ent - > targetname )
{
ent - > e_UseFunc = useF_misc_use ;
}
ent - > material = MAT_CRATE1 ;
ent - > e_DieFunc = dieF_misc_model_breakable_die ; //ExplodeDeath;
}
/*QUAKED misc_gas_tank (1 0 0.25) (-4 -4 0) (4 4 40)
# MODELNAME="models / map_objects / imp_mine / tank.md3"
Basic exploding oxygen tank
" health " - how much health the model has - default 20 ( zero makes non - breakable )
" splashRadius " - radius to do damage in - default 48
" splashDamage " - amount of damage to do when it explodes - default 32
" targetname " - auto - explodes
" target " - what to use when it dies
*/
void gas_random_jet ( gentity_t * self )
{
vec3_t pt ;
VectorCopy ( self - > currentOrigin , pt ) ;
pt [ 2 ] + = 50 ;
G_PlayEffect ( " env/mini_gasjet " , pt ) ;
self - > nextthink = level . time + Q_flrand ( 0.0f , 1.0f ) * 16000 + 12000 ; // do this rarely
}
//------------------------------------------------------------
void GasBurst ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , vec3_t point , int damage , int mod , int hitLoc )
{
vec3_t pt ;
VectorCopy ( self - > currentOrigin , pt ) ;
pt [ 2 ] + = 46 ;
G_PlayEffect ( " env/mini_flamejet " , pt ) ;
// do some damage to anything that may be standing on top of it when it bursts into flame
pt [ 2 ] + = 32 ;
G_RadiusDamage ( pt , self , 32 , 32 , self , MOD_UNKNOWN ) ;
// only get one burst
self - > e_PainFunc = painF_NULL ;
}
//------------------------------------------------------------
void SP_misc_gas_tank ( gentity_t * ent )
{
G_SpawnInt ( " health " , " 20 " , & ent - > health ) ;
G_SpawnInt ( " splashRadius " , " 48 " , & ent - > splashRadius ) ;
G_SpawnInt ( " splashDamage " , " 32 " , & ent - > splashDamage ) ;
ent - > s . modelindex = G_ModelIndex ( " models/map_objects/imp_mine/tank.md3 " ) ;
G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ;
G_EffectIndex ( " chunks/metalexplode " ) ;
G_EffectIndex ( " env/mini_flamejet " ) ;
G_EffectIndex ( " env/mini_gasjet " ) ;
VectorSet ( ent - > mins , - 4 , - 4 , 0 ) ;
VectorSet ( ent - > maxs , 4 , 4 , 40 ) ;
ent - > contents = CONTENTS_SOLID ;
ent - > takedamage = qtrue ;
G_SetOrigin ( ent , ent - > s . origin ) ;
VectorCopy ( ent - > s . angles , ent - > s . apos . trBase ) ;
gi . linkentity ( ent ) ;
ent - > e_PainFunc = painF_GasBurst ;
if ( ent - > targetname )
{
ent - > e_UseFunc = useF_misc_use ;
}
ent - > material = MAT_METAL3 ;
ent - > e_DieFunc = dieF_misc_model_breakable_die ;
ent - > e_ThinkFunc = thinkF_gas_random_jet ;
ent - > nextthink = level . time + Q_flrand ( 0.0f , 1.0f ) * 12000 + 6000 ; // do this rarely
}
/*QUAKED misc_crystal_crate (1 0 0.25) (-34 -34 0) (34 34 44) NON_SOLID
# MODELNAME="models / map_objects / imp_mine / crate_open.md3"
Open crate of crystals that explode when shot
NON_SOLID - can only be shot
" health " - how much health the crate has , default 80
" splashRadius " - radius to do damage in , default 80
" splashDamage " - amount of damage to do when it explodes , default 40
" targetname " - auto - explodes
" target " - what to use when it dies
*/
//------------------------------------------------------------
void CrystalCratePain ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , vec3_t point , int damage , int mod , int hitLoc )
{
vec3_t pt ;
VectorCopy ( self - > currentOrigin , pt ) ;
pt [ 2 ] + = 36 ;
G_PlayEffect ( " env/crystal_crate " , pt ) ;
// do some damage, heh
pt [ 2 ] + = 32 ;
G_RadiusDamage ( pt , self , 16 , 32 , self , MOD_UNKNOWN ) ;
}
//------------------------------------------------------------
void SP_misc_crystal_crate ( gentity_t * ent )
{
G_SpawnInt ( " health " , " 80 " , & ent - > health ) ;
G_SpawnInt ( " splashRadius " , " 80 " , & ent - > splashRadius ) ;
G_SpawnInt ( " splashDamage " , " 40 " , & ent - > splashDamage ) ;
ent - > s . modelindex = G_ModelIndex ( " models/map_objects/imp_mine/crate_open.md3 " ) ;
ent - > fxID = G_EffectIndex ( " thermal/explosion " ) ; // FIXME: temp
G_EffectIndex ( " env/crystal_crate " ) ;
G_SoundIndex ( " sound/weapons/explosions/cargoexplode.wav " ) ;
VectorSet ( ent - > mins , - 34 , - 34 , 0 ) ;
VectorSet ( ent - > maxs , 34 , 34 , 44 ) ;
//Blocks movement
ent - > contents = CONTENTS_SOLID | CONTENTS_OPAQUE | CONTENTS_BODY | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ; //Was CONTENTS_SOLID, but only architecture should be this
if ( ent - > spawnflags & 1 ) // non-solid
{ // Override earlier contents flag...
//Can only be shot
ent - > contents = CONTENTS_SHOTCLIP ;
}
ent - > takedamage = qtrue ;
G_SetOrigin ( ent , ent - > s . origin ) ;
VectorCopy ( ent - > s . angles , ent - > s . apos . trBase ) ;
gi . linkentity ( ent ) ;
ent - > e_PainFunc = painF_CrystalCratePain ;
if ( ent - > targetname )
{
ent - > e_UseFunc = useF_misc_use ;
}
ent - > material = MAT_CRATE2 ;
ent - > e_DieFunc = dieF_misc_model_breakable_die ;
}
/*QUAKED misc_atst_drivable (1 0 0.25) (-40 -40 -24) (40 40 248)
# MODELNAME="models / players / atst / model.glm"
Drivable ATST , when used by player , they become the ATST . When the player hits use again , they get out .
" health " - how much health the atst has - default 800
" target " - what to use when it dies
*/
void misc_atst_setanim ( gentity_t * self , int bone , int anim )
{
if ( bone < 0 | | anim < 0 )
{
return ;
}
int firstFrame = - 1 ;
int lastFrame = - 1 ;
float animSpeed = 0 ;
//try to get anim ranges from the animation.cfg for an AT-ST
for ( int i = 0 ; i < level . numKnownAnimFileSets ; i + + )
{
if ( ! Q_stricmp ( " atst " , level . knownAnimFileSets [ i ] . filename ) )
{
firstFrame = level . knownAnimFileSets [ i ] . animations [ anim ] . firstFrame ;
lastFrame = firstFrame + level . knownAnimFileSets [ i ] . animations [ anim ] . numFrames ;
animSpeed = 50.0f / level . knownAnimFileSets [ i ] . animations [ anim ] . frameLerp ;
break ;
}
}
if ( firstFrame ! = - 1 & & lastFrame ! = - 1 & & animSpeed ! = 0 )
{
if ( ! gi . G2API_SetBoneAnimIndex ( & self - > ghoul2 [ self - > playerModel ] , bone , firstFrame ,
lastFrame , BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND , animSpeed ,
( cg . time ? cg . time : level . time ) , - 1 , 150 ) )
{
gi . G2API_SetBoneAnimIndex ( & self - > ghoul2 [ self - > playerModel ] , bone , firstFrame ,
lastFrame , BONE_ANIM_OVERRIDE_FREEZE , animSpeed ,
( cg . time ? cg . time : level . time ) , - 1 , 150 ) ;
}
}
}
void misc_atst_die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int mod , int dFlags , int hitLoc )
{ //ATST was destroyed while you weren't in it
//can't be used anymore
self - > e_UseFunc = useF_NULL ;
//sigh... remove contents so we don't block the player's path...
self - > contents = CONTENTS_CORPSE ;
self - > takedamage = qfalse ;
self - > maxs [ 2 ] = 48 ;
//FIXME: match to slope
vec3_t effectPos ;
VectorCopy ( self - > currentOrigin , effectPos ) ;
effectPos [ 2 ] - = 15 ;
G_PlayEffect ( " droidexplosion1 " , effectPos ) ;
// G_PlayEffect( "small_chunks", effectPos );
//set these to defaults that work in a worst-case scenario (according to current animation.cfg)
gi . G2API_StopBoneAnimIndex ( & self - > ghoul2 [ self - > playerModel ] , self - > craniumBone ) ;
misc_atst_setanim ( self , self - > rootBone , BOTH_DEATH1 ) ;
}
extern void G_DriveATST ( gentity_t * ent , gentity_t * atst ) ;
extern void SetClientViewAngle ( gentity_t * ent , vec3_t angle ) ;
extern qboolean PM_InSlopeAnim ( int anim ) ;
void misc_atst_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( ! activator | | activator - > s . number )
{ //only player can do this
return ;
}
int tempLocDmg [ HL_MAX ] ;
int hl , tempHealth ;
if ( activator - > client - > NPC_class ! = CLASS_ATST )
{ //get in the ATST
if ( activator - > client - > ps . groundEntityNum ! = self - > s . number )
{ //can only get in if on top of me...
//we *could* even check for the hatch surf...?
return ;
}
//copy origin
G_SetOrigin ( activator , self - > currentOrigin ) ;
//copy angles
VectorCopy ( self - > s . angles2 , self - > currentAngles ) ;
G_SetAngles ( activator , self - > currentAngles ) ;
SetClientViewAngle ( activator , self - > currentAngles ) ;
//set player to my g2 instance
gi . G2API_StopBoneAnimIndex ( & self - > ghoul2 [ self - > playerModel ] , self - > craniumBone ) ;
G_DriveATST ( activator , self ) ;
activator - > activator = self ;
self - > s . eFlags | = EF_NODRAW ;
self - > svFlags | = SVF_NOCLIENT ;
self - > contents = 0 ;
self - > takedamage = qfalse ;
//transfer armor
tempHealth = self - > health ;
self - > health = activator - > client - > ps . stats [ STAT_ARMOR ] ;
activator - > client - > ps . stats [ STAT_ARMOR ] = tempHealth ;
//transfer locationDamage
for ( hl = HL_NONE ; hl < HL_MAX ; hl + + )
{
tempLocDmg [ hl ] = activator - > locationDamage [ hl ] ;
activator - > locationDamage [ hl ] = self - > locationDamage [ hl ] ;
self - > locationDamage [ hl ] = tempLocDmg [ hl ] ;
}
if ( ! self - > s . number )
{
CG_CenterPrint ( " @INGAME_EXIT_VIEW " , SCREEN_HEIGHT * 0.95 ) ;
}
}
else
{ //get out of ATST
int legsAnim = activator - > client - > ps . legsAnim ;
if ( legsAnim ! = BOTH_STAND1
& & ! PM_InSlopeAnim ( legsAnim )
& & legsAnim ! = BOTH_TURN_RIGHT1 & & legsAnim ! = BOTH_TURN_LEFT1 )
{ //can't get out of it while it's still moving
return ;
}
//FIXME: after a load/save, this crashes, BAD... somewhere in G2
G_SetOrigin ( self , activator - > currentOrigin ) ;
VectorSet ( self - > currentAngles , 0 , activator - > client - > ps . legsYaw , 0 ) ;
//self->currentAngles[PITCH] = activator->currentAngles[ROLL] = 0;
G_SetAngles ( self , self - > currentAngles ) ;
VectorCopy ( activator - > currentAngles , self - > s . angles2 ) ;
//remove my G2
if ( self - > playerModel > = 0 )
{
gi . G2API_RemoveGhoul2Model ( self - > ghoul2 , self - > playerModel ) ;
self - > playerModel = - 1 ;
}
//copy player's
gi . G2API_CopyGhoul2Instance ( activator - > ghoul2 , self - > ghoul2 , - 1 ) ;
self - > playerModel = 0 ; //assumption
//reset player to kyle
G_DriveATST ( activator , NULL ) ;
activator - > activator = NULL ;
self - > s . eFlags & = ~ EF_NODRAW ;
self - > svFlags & = ~ SVF_NOCLIENT ;
self - > contents = CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ;
self - > takedamage = qtrue ;
//transfer armor
tempHealth = self - > health ;
self - > health = activator - > client - > ps . stats [ STAT_ARMOR ] ;
activator - > client - > ps . stats [ STAT_ARMOR ] = tempHealth ;
//transfer locationDamage
for ( hl = HL_NONE ; hl < HL_MAX ; hl + + )
{
tempLocDmg [ hl ] = self - > locationDamage [ hl ] ;
self - > locationDamage [ hl ] = activator - > locationDamage [ hl ] ;
activator - > locationDamage [ hl ] = tempLocDmg [ hl ] ;
}
//link me back in
gi . linkentity ( self ) ;
//put activator on top of me?
vec3_t newOrg = { activator - > currentOrigin [ 0 ] , activator - > currentOrigin [ 1 ] , activator - > currentOrigin [ 2 ] + ( self - > maxs [ 2 ] - self - > mins [ 2 ] ) + 1 } ;
G_SetOrigin ( activator , newOrg ) ;
//open the hatch
misc_atst_setanim ( self , self - > craniumBone , BOTH_STAND2 ) ;
gi . G2API_SetSurfaceOnOff ( & self - > ghoul2 [ self - > playerModel ] , " head_hatchcover_off " , 0 ) ;
G_Sound ( self , G_SoundIndex ( " sound/chars/atst/atst_hatch_open " ) ) ;
}
}
void SP_misc_atst_drivable ( gentity_t * ent )
{
extern void NPC_ATST_Precache ( void ) ;
extern void NPC_PrecacheAnimationCFG ( const char * NPC_type ) ;
ent - > s . modelindex = G_ModelIndex ( " models/players/atst/model.glm " ) ;
ent - > playerModel = gi . G2API_InitGhoul2Model ( ent - > ghoul2 , " models/players/atst/model.glm " , ent - > s . modelindex , NULL_HANDLE , NULL_HANDLE , 0 , 0 ) ;
ent - > rootBone = gi . G2API_GetBoneIndex ( & ent - > ghoul2 [ ent - > playerModel ] , " model_root " , qtrue ) ;
ent - > craniumBone = gi . G2API_GetBoneIndex ( & ent - > ghoul2 [ ent - > playerModel ] , " cranium " , qtrue ) ; //FIXME: need to somehow set the anim/frame to the equivalent of BOTH_STAND1... use to be that BOTH_STAND1 was the first frame of the glm, but not anymore
ent - > s . radius = 320 ;
VectorSet ( ent - > s . modelScale , 1.0f , 1.0f , 1.0f ) ;
//register my weapons, sounds and model
RegisterItem ( FindItemForWeapon ( WP_ATST_MAIN ) ) ; //precache the weapon
RegisterItem ( FindItemForWeapon ( WP_ATST_SIDE ) ) ; //precache the weapon
//HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
RegisterItem ( FindItemForWeapon ( WP_EMPLACED_GUN ) ) ; //precache the weapon
// RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER )); //precache the weapon
// RegisterItem( FindItemForWeapon( WP_BOWCASTER )); //precache the weapon
//HACKHACKHACKTEMP - until ATST gets real weapons of it's own?
G_SoundIndex ( " sound/chars/atst/atst_hatch_open " ) ;
G_SoundIndex ( " sound/chars/atst/atst_hatch_close " ) ;
NPC_ATST_Precache ( ) ;
ent - > NPC_type = " atst " ;
NPC_PrecacheAnimationCFG ( ent - > NPC_type ) ;
//open the hatch
misc_atst_setanim ( ent , ent - > rootBone , BOTH_STAND2 ) ;
gi . G2API_SetSurfaceOnOff ( & ent - > ghoul2 [ ent - > playerModel ] , " head_hatchcover_off " , 0 ) ;
VectorSet ( ent - > mins , ATST_MINS0 , ATST_MINS1 , ATST_MINS2 ) ;
VectorSet ( ent - > maxs , ATST_MAXS0 , ATST_MAXS1 , ATST_MAXS2 ) ;
ent - > contents = CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP ;
ent - > flags | = FL_SHIELDED ;
ent - > takedamage = qtrue ;
if ( ! ent - > health )
{
ent - > health = 800 ;
}
ent - > max_health = ent - > health ; // cg_draw needs this
G_SetOrigin ( ent , ent - > s . origin ) ;
G_SetAngles ( ent , ent - > s . angles ) ;
VectorCopy ( ent - > currentAngles , ent - > s . angles2 ) ;
gi . linkentity ( ent ) ;
//FIXME: test the origin to make sure I'm clear?
ent - > e_UseFunc = useF_misc_atst_use ;
ent - > svFlags | = SVF_PLAYER_USABLE ;
//make it able to take damage and die when you're not in it...
//do an explosion and play the death anim, remove use func.
ent - > e_DieFunc = dieF_misc_atst_die ;
}
void SP_misc_cubemap ( gentity_t * ent )
{
G_FreeEntity ( ent ) ;
}