/codemp/game/g_misc.c
C | 3661 lines | 2211 code | 579 blank | 871 comment | 403 complexity | 026239744d1ac742a3b4818d6d229b2e MD5 | raw file
Possible License(s): GPL-2.0
- // Copyright (C) 1999-2000 Id Software, Inc.
- //
- // g_misc.c
- #include "g_local.h"
- #include "ghoul2/G2.h"
- #include "ai_main.h" //for the g2animents
- #define HOLOCRON_RESPAWN_TIME 30000
- #define MAX_AMMO_GIVE 2
- #define STATION_RECHARGE_TIME 100
- void HolocronThink(gentity_t *ent);
- /*QUAKED func_group (0 0 0) ?
- Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
- */
- /*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
- Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
- */
- void SP_info_camp( gentity_t *self ) {
- G_SetOrigin( self, self->s.origin );
- }
- /*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 ) {
- G_FreeEntity( self );
- }
- /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
- Used as a positional target for in-game calculation, like jumppad targets.
- target_position does the same thing
- */
- void SP_info_notnull( gentity_t *self ){
- G_SetOrigin( self, self->s.origin );
- }
- /*QUAKED lightJunior (0 0.7 0.3) (-8 -8 -8) (8 8 8) nonlinear angle negative_spot negative_point
- Non-displayed light that only affects dynamic game models, but does not contribute to lightmaps
- "light" overrides the default 300 intensity.
- Nonlinear checkbox gives inverse square falloff instead of linear
- Angle adds light:surface angle calculations (only valid for "Linear" lights) (wolf)
- Lights pointed at a target will be spotlights.
- "radius" overrides the default 64 unit radius of a spotlight at the target point.
- "fade" falloff/radius adjustment value. multiply the run of the slope by "fade" (1.0f default) (only valid for "Linear" lights) (wolf)
- */
- /*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->alt_fire)
- { //turn off
- if (mLightOffStyle) //i have a light style i'd like to use when off
- {
- char lightstyle[32];
- trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+0, lightstyle, 32);
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
- trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+1, lightstyle, 32);
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
- trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+2, lightstyle, 32);
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
- }else
- {
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "a");
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "a");
- trap_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];
- trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+0, lightstyle, 32);
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
- trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+1, lightstyle, 32);
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
- trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+2, lightstyle, 32);
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
- }
- else
- {
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "z");
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "z");
- trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "z");
- }
- }
- }
- void misc_dlight_use ( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- G_ActivateBehavior(ent,BSET_USE);
- ent->alt_fire = !ent->alt_fire; //toggle
- misc_lightstyle_set (ent);
- }
- 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 );
- trap_LinkEntity( self );
- self->use = misc_dlight_use;
- self->s.eType = ET_GENERAL;
- self->alt_fire = qfalse;
- self->r.svFlags |= SVF_NOCLIENT;
- if ( !(self->spawnflags & 4) )
- { //turn myself on now
- self->alt_fire = qtrue;
- }
- misc_lightstyle_set (self);
- }
- /*
- =================================================================================
- TELEPORTERS
- =================================================================================
- */
- void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
- gentity_t *tent;
- qboolean isNPC = qfalse;
- if (player->s.eType == ET_NPC)
- {
- isNPC = qtrue;
- }
- // use temp events at source and destination to prevent the effect
- // from getting dropped by a second player event
- if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
- tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
- tent->s.clientNum = player->s.clientNum;
- tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
- tent->s.clientNum = player->s.clientNum;
- }
- // unlink to make sure it can't possibly interfere with G_KillBox
- trap_UnlinkEntity (player);
- VectorCopy ( origin, player->client->ps.origin );
- player->client->ps.origin[2] += 1;
- // spit the player out
- AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
- VectorScale( player->client->ps.velocity, 400, 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
- if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
- G_KillBox (player);
- }
- // save results of pmove
- BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
- if (isNPC)
- {
- player->s.eType = ET_NPC;
- }
- // use the precise origin for linking
- VectorCopy( player->client->ps.origin, player->r.currentOrigin );
- if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
- trap_LinkEntity (player);
- }
- }
- /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
- Point teleporters at these.
- Now that we don't have teleport destination pads, this is just
- an info_notnull
- */
- void SP_misc_teleporter_dest( gentity_t *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 ) {
- #if 0
- ent->s.modelindex = G_ModelIndex( ent->model );
- VectorSet (ent->r.mins, -16, -16, -16);
- VectorSet (ent->r.maxs, 16, 16, 16);
- trap_LinkEntity (ent);
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- #else
- G_FreeEntity( ent );
- #endif
- }
- /*QUAKED misc_model_static (1 0 0) (-16 -16 0) (16 16 16)
- "model" arbitrary .md3 file to display
- "zoffset" units to offset vertical culling position by, can be
- negative or positive. This does not affect the actual
- position of the model, only the culling position. Use
- it for models with stupid origins that go below the
- ground and whatnot.
- "modelscale" scale on all axis
- "modelscale_vec" scale difference axis
- loaded as a model in the renderer - does not take up precious
- bsp space!
- */
- void SP_misc_model_static(gentity_t *ent)
- {
- G_FreeEntity( ent );
- }
- /*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION
- SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health
- AUTOANIMATE - Will cycle it's anim
- DEADSOLID - Stay solid even when destroyed (in case damage model is rather large).
- NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists
- USE_MODEL - When used, will toggle to it's usemodel (model name + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked
- USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage
- PLAYER_USE - Player can use it with the use button
- NO_EXPLOSION - By default, will explode when it dies...this is your override.
- "model" arbitrary .md3 file to display
- "health" how much health to have - default is zero (not breakable) If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving
- "targetname" when used, dies and displays damagemodel, if any (if not, removes itself)
- "target" What to use when it dies
- "target2" What to use when it's repaired
- "target3" What to use when it's used while it's broken
- "paintarget" target to fire when hit (but not destroyed)
- "count" the amount of armor/health/ammo given (default 50)
- "gravity" if set to 1, this will be affected by gravity
- "radius" Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1) (.5) is half as many chunks, (2) is twice as many chunks
- Damage: default is none
- "splashDamage" - damage to do (will make it explode on death)
- "splashRadius" - radius for above damage
- "team" - This cannot take damage from members of this team:
- "player"
- "neutral"
- "enemy"
- "material" - default is "8 - MAT_NONE" - choose from this list:
- 0 = MAT_METAL (grey metal)
- 1 = MAT_GLASS
- 2 = MAT_ELECTRICAL (sparks only)
- 3 = MAT_ELEC_METAL (METAL chunks and sparks)
- 4 = MAT_DRK_STONE (brown stone chunks)
- 5 = MAT_LT_STONE (tan stone chunks)
- 6 = MAT_GLASS_METAL (glass and METAL chunks)
- 7 = MAT_METAL2 (blue/grey metal)
- 8 = MAT_NONE (no chunks-DEFAULT)
- 9 = MAT_GREY_STONE (grey colored stone)
- 10 = MAT_METAL3 (METAL and METAL2 chunk combo)
- 11 = MAT_CRATE1 (yellow multi-colored crate chunks)
- 12 = MAT_GRATE1 (grate chunks--looks horrible right now)
- 13 = MAT_ROPE (for yavin_trial, no chunks, just wispy bits )
- 14 = MAT_CRATE2 (red multi-colored crate chunks)
- 15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout )
- FIXME/TODO:
- set size better?
- multiple damage models?
- custom explosion effect/sound?
- */
- void misc_model_breakable_gravity_init( gentity_t *ent, qboolean dropToFloor );
- void misc_model_breakable_init( gentity_t *ent );
- void SP_misc_model_breakable( gentity_t *ent )
- {
- float grav;
- G_SpawnInt( "material", "8", (int*)&ent->material );
- G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer
- misc_model_breakable_init( ent );
- if ( !ent->r.mins[0] && !ent->r.mins[1] && !ent->r.mins[2] )
- {
- VectorSet (ent->r.mins, -16, -16, -16);
- }
- if ( !ent->r.maxs[0] && !ent->r.maxs[1] && !ent->r.maxs[2] )
- {
- VectorSet (ent->r.maxs, 16, 16, 16);
- }
- G_SetOrigin( ent, ent->s.origin );
- G_SetAngles( ent, ent->s.angles );
- trap_LinkEntity (ent);
- if ( ent->spawnflags & 128 )
- {//Can be used by the player's BUTTON_USE
- ent->r.svFlags |= SVF_PLAYER_USABLE;
- }
- ent->s.teamowner = 0;
- G_SpawnFloat( "gravity", "0", &grav );
- if ( grav )
- {//affected by gravity
- G_SetAngles( ent, ent->s.angles );
- G_SetOrigin( ent, ent->r.currentOrigin );
- misc_model_breakable_gravity_init( ent, qtrue );
- }
- }
- void misc_model_breakable_gravity_init( gentity_t *ent, qboolean dropToFloor )
- {
- trace_t tr;
- vec3_t top, bottom;
- ent->s.eType = ET_GENERAL;
- //ent->s.eFlags |= EF_BOUNCE_HALF; // FIXME
- ent->clipmask = MASK_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//?
- ent->physicsBounce = ent->mass = VectorLength( ent->r.maxs ) + VectorLength( ent->r.mins );
- //drop to floor
- if ( dropToFloor )
- {
- VectorCopy( ent->r.currentOrigin, top );
- top[2] += 1;
- VectorCopy( ent->r.currentOrigin, bottom );
- bottom[2] = MIN_WORLD_COORD;
- trap_Trace( &tr, top, ent->r.mins, ent->r.maxs, bottom, ent->s.number, MASK_NPCSOLID );
- if ( !tr.allsolid && !tr.startsolid && tr.fraction < 1.0 )
- {
- G_SetOrigin( ent, tr.endpos );
- trap_LinkEntity( ent );
- }
- }
- else
- {
- G_SetOrigin( ent, ent->r.currentOrigin );
- trap_LinkEntity( ent );
- }
- //set up for object thinking
- if ( VectorCompare( ent->s.pos.trDelta, vec3_origin ) )
- {//not moving
- ent->s.pos.trType = TR_STATIONARY;
- }
- else
- {
- ent->s.pos.trType = TR_GRAVITY;
- }
- VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
- VectorClear( ent->s.pos.trDelta );
- ent->s.pos.trTime = level.time;
- if ( VectorCompare( ent->s.apos.trDelta, vec3_origin ) )
- {//not moving
- ent->s.apos.trType = TR_STATIONARY;
- }
- else
- {
- ent->s.apos.trType = TR_LINEAR;
- }
- VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
- VectorClear( ent->s.apos.trDelta );
- ent->s.apos.trTime = level.time;
- }
- void misc_model_breakable_init( gentity_t *ent )
- {
- if (!ent->model) {
- G_Error("no model set on %s at (%.1f %.1f %.1f)\n", ent->classname, ent->s.origin[0],ent->s.origin[1],ent->s.origin[2]);
- }
- //Main model
- ent->s.modelindex = ent->sound2to1 = G_ModelIndex( ent->model );
- if ( ent->spawnflags & 1 )
- {//Blocks movement
- ent->r.contents = CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//Was CONTENTS_SOLID, but only architecture should be this
- }
- else if ( ent->health )
- {//Can only be shot
- ent->r.contents = CONTENTS_SHOTCLIP;
- }
- // TODO: fix using
- // TODO: fix health/dying funcs
- }
- /*QUAKED misc_G2model (1 0 0) (-16 -16 -16) (16 16 16)
- "model" arbitrary .glm file to display
- */
- void SP_misc_G2model( gentity_t *ent ) {
- #if 0
- char name1[200] = "models/players/kyle/modelmp.glm";
- trap_G2API_InitGhoul2Model(&ent->s, name1, G_ModelIndex( name1 ), 0, 0, 0, 0);
- trap_G2API_SetBoneAnim(ent->s.ghoul2, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, level.time, -1, -1);
- ent->s.radius = 150;
- // VectorSet (ent->r.mins, -16, -16, -16);
- // VectorSet (ent->r.maxs, 16, 16, 16);
- trap_LinkEntity (ent);
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- #else
- G_FreeEntity( ent );
- #endif
- }
- //===========================================================
- void locateCamera( gentity_t *ent ) {
- vec3_t dir;
- gentity_t *target;
- gentity_t *owner;
- owner = G_PickTarget( ent->target );
- if ( !owner ) {
- G_Printf( "Couldn't find target for misc_partal_surface\n" );
- G_FreeEntity( ent );
- return;
- }
- ent->r.ownerNum = owner->s.number;
- // frame holds the rotate speed
- if ( owner->spawnflags & 1 ) {
- ent->s.frame = 25;
- } else if ( owner->spawnflags & 2 ) {
- ent->s.frame = 75;
- }
- // swing camera ?
- if ( owner->spawnflags & 4 ) {
- // set to 0 for no rotation at all
- ent->s.powerups = 0;
- }
- else {
- ent->s.powerups = 1;
- }
- // clientNum holds the rotate offset
- ent->s.clientNum = owner->s.clientNum;
- VectorCopy( owner->s.origin, ent->s.origin2 );
- // see if the portal_camera has a target
- target = G_PickTarget( owner->target );
- if ( target ) {
- VectorSubtract( target->s.origin, owner->s.origin, dir );
- VectorNormalize( dir );
- } else {
- G_SetMovedir( owner->s.angles, dir );
- }
- ent->s.eventParm = DirToByte( dir );
- }
- /*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!
- */
- void SP_misc_portal_surface(gentity_t *ent) {
- VectorClear( ent->r.mins );
- VectorClear( ent->r.maxs );
- trap_LinkEntity (ent);
- ent->r.svFlags = SVF_PORTAL;
- ent->s.eType = ET_PORTAL;
- if ( !ent->target ) {
- VectorCopy( ent->s.origin, ent->s.origin2 );
- } else {
- ent->think = locateCamera;
- ent->nextthink = level.time + 100;
- }
- }
- /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
- The target for a misc_portal_director. You can set either angles or target another entity 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->r.mins );
- VectorClear( ent->r.maxs );
- trap_LinkEntity (ent);
- G_SpawnFloat( "roll", "0", &roll );
- ent->s.clientNum = roll/360.0 * 256;
- }
- /*QUAKED misc_bsp (1 0 0) (-16 -16 -16) (16 16 16)
- "bspmodel" arbitrary .bsp file to display
- */
- void SP_misc_bsp(gentity_t *ent)
- {
- char temp[MAX_QPATH];
- char *out;
- float newAngle;
- int tempint;
- G_SpawnFloat( "angle", "0", &newAngle );
- if (newAngle != 0.0)
- {
- ent->s.angles[1] = newAngle;
- }
- // don't support rotation any other way
- ent->s.angles[0] = 0.0;
- ent->s.angles[2] = 0.0;
-
- G_SpawnString("bspmodel", "", &out);
- ent->s.eFlags = EF_PERMANENT;
- // Mainly for debugging
- G_SpawnInt( "spacing", "0", &tempint);
- ent->s.time2 = tempint;
- G_SpawnInt( "flatten", "0", &tempint);
- ent->s.time = tempint;
- Com_sprintf(temp, MAX_QPATH, "#%s", out);
- trap_SetBrushModel( ent, temp ); // SV_SetBrushModel -- sets mins and maxs
- G_BSPIndex(temp);
- level.mNumBSPInstances++;
- Com_sprintf(temp, MAX_QPATH, "%d-", level.mNumBSPInstances);
- VectorCopy(ent->s.origin, level.mOriginAdjust);
- level.mRotationAdjust = ent->s.angles[1];
- level.mTargetAdjust = temp;
- //level.hasBspInstances = qtrue; //rww - also not referenced anywhere.
- level.mBSPInstanceDepth++;
- /*
- G_SpawnString("filter", "", &out);
- strcpy(level.mFilter, out);
- */
- G_SpawnString("teamfilter", "", &out);
- strcpy(level.mTeamFilter, out);
- VectorCopy( ent->s.origin, ent->s.pos.trBase );
- VectorCopy( ent->s.origin, ent->r.currentOrigin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- VectorCopy( ent->s.angles, ent->r.currentAngles );
- ent->s.eType = ET_MOVER;
- trap_LinkEntity (ent);
- trap_SetActiveSubBSP(ent->s.modelindex);
- G_SpawnEntitiesFromString(qtrue);
- trap_SetActiveSubBSP(-1);
- level.mBSPInstanceDepth--;
- //level.mFilter[0] = level.mTeamFilter[0] = 0;
- level.mTeamFilter[0] = 0;
- /*
- if ( g_debugRMG.integer )
- {
- G_SpawnDebugCylinder ( ent->s.origin, ent->s.time2, &g_entities[0], 2000, COLOR_WHITE );
- if ( ent->s.time )
- {
- G_SpawnDebugCylinder ( ent->s.origin, ent->s.time, &g_entities[0], 2000, COLOR_RED );
- }
- }
- */
- }
- /*QUAKED terrain (1.0 1.0 1.0) ? NOVEHDMG
- NOVEHDMG - don't damage vehicles upon impact with this terrain
- Terrain entity
- It will stretch to the full height of the brush
- numPatches - integer number of patches to split the terrain brush into (default 200)
- terxels - integer number of terxels on a patch side (default 4) (2 <= count <= 8)
- seed - integer seed for random terrain generation (default 0)
- textureScale - float scale of texture (default 0.005)
- heightmap - name of heightmap data image to use, located in heightmaps/*.png. (must be PNG format)
- terrainDef - defines how the game textures the terrain (file is base/ext_data/rmg/*.terrain - default is grassyhills)
- instanceDef - defines which bsp instances appear
- miscentDef - defines which client models spawn on the terrain (file is base/ext_data/rmg/*.miscents)
- densityMap - how dense the client models are packed
- */
- void AddSpawnField(char *field, char *value);
- #define MAX_INSTANCE_TYPES 16
- void SP_terrain(gentity_t *ent)
- {
- char temp[MAX_INFO_STRING];
- char final[MAX_QPATH];
- char seed[MAX_QPATH];
- char missionType[MAX_QPATH];
- //char soundSet[MAX_QPATH];
- int shaderNum, i;
- char *value;
- int terrainID;
- //Force it to 1 when there is terrain on the level.
- trap_Cvar_Set("RMG", "1");
- RMG.integer = 1;
- VectorClear (ent->s.angles);
- trap_SetBrushModel( ent, ent->model );
- // Get the shader from the top of the brush
- // shaderNum = gi.CM_GetShaderNum(s.modelindex);
- shaderNum = 0;
- if (RMG.integer)
- {
- /*
- // Grab the default terrain file from the RMG cvar
- trap_Cvar_VariableStringBuffer("RMG_terrain", temp, MAX_QPATH);
- Com_sprintf(final, MAX_QPATH, "%s", temp);
- AddSpawnField("terrainDef", temp);
-
- trap_Cvar_VariableStringBuffer("RMG_instances", temp, MAX_QPATH);
- Com_sprintf(final, MAX_QPATH, "%s", temp);
- AddSpawnField("instanceDef", temp);
- trap_Cvar_VariableStringBuffer("RMG_miscents", temp, MAX_QPATH);
- Com_sprintf(final, MAX_QPATH, "%s", temp);
- AddSpawnField("miscentDef", temp);
- */
- //rww - disabled for now, don't want cvar overrides.
- trap_Cvar_VariableStringBuffer("RMG_seed", seed, MAX_QPATH);
- trap_Cvar_VariableStringBuffer("RMG_mission", missionType, MAX_QPATH);
- //rww - May want to implement these at some point.
- //trap_Cvar_VariableStringBuffer("RMG_soundset", soundSet, MAX_QPATH);
- //trap_SetConfigstring(CS_AMBIENT_SOUNDSETS, soundSet );
- }
- // Get info required for the common init
- temp[0] = 0;
- G_SpawnString("heightmap", "", &value);
- Info_SetValueForKey(temp, "heightMap", value);
- G_SpawnString("numpatches", "400", &value);
- Info_SetValueForKey(temp, "numPatches", va("%d", atoi(value)));
- G_SpawnString("terxels", "4", &value);
- Info_SetValueForKey(temp, "terxels", va("%d", atoi(value)));
- Info_SetValueForKey(temp, "seed", seed);
- Info_SetValueForKey(temp, "minx", va("%f", ent->r.mins[0]));
- Info_SetValueForKey(temp, "miny", va("%f", ent->r.mins[1]));
- Info_SetValueForKey(temp, "minz", va("%f", ent->r.mins[2]));
- Info_SetValueForKey(temp, "maxx", va("%f", ent->r.maxs[0]));
- Info_SetValueForKey(temp, "maxy", va("%f", ent->r.maxs[1]));
- Info_SetValueForKey(temp, "maxz", va("%f", ent->r.maxs[2]));
- Info_SetValueForKey(temp, "modelIndex", va("%d", ent->s.modelindex));
- G_SpawnString("terraindef", "grassyhills", &value);
- Info_SetValueForKey(temp, "terrainDef", value);
- G_SpawnString("instancedef", "", &value);
- Info_SetValueForKey(temp, "instanceDef", value);
- G_SpawnString("miscentdef", "", &value);
- Info_SetValueForKey(temp, "miscentDef", value);
- Info_SetValueForKey(temp, "missionType", missionType);
-
- for(i = 0; i < MAX_INSTANCE_TYPES; i++)
- {
- trap_Cvar_VariableStringBuffer(va("RMG_instance%d", i), final, MAX_QPATH);
- if(strlen(final))
- {
- Info_SetValueForKey(temp, va("inst%d", i), final);
- }
- }
- // Set additional data required on the client only
- G_SpawnString("densitymap", "", &value);
- Info_SetValueForKey(temp, "densityMap", value);
- Info_SetValueForKey(temp, "shader", va("%d", shaderNum));
- G_SpawnString("texturescale", "0.005", &value);
- Info_SetValueForKey(temp, "texturescale", va("%f", atof(value)));
- // Initialise the common aspects of the terrain
- terrainID = trap_CM_RegisterTerrain(temp);
- // SetCommon(common);
- Info_SetValueForKey(temp, "terrainId", va("%d", terrainID));
- // Let the entity know if it is random generated or not
- // SetIsRandom(common->GetIsRandom());
- // Let the game remember everything
- //level.landScapes[terrainID] = ent; //rww - also not referenced
- // Send all the data down to the client
- trap_SetConfigstring(CS_TERRAINS + terrainID, temp);
- // Make sure the contents are properly set
- ent->r.contents = CONTENTS_TERRAIN;
- ent->r.svFlags = SVF_NOCLIENT;
- ent->s.eFlags = EF_PERMANENT;
- ent->s.eType = ET_TERRAIN;
- // Hook into the world so physics will work
- trap_LinkEntity(ent);
- // If running RMG then initialize the terrain and handle team skins
- if ( RMG.integer )
- {
- trap_RMG_Init(terrainID);
- /*
- if ( level.gametypeData->teams )
- {
- char temp[MAX_QPATH];
- // Red team change from RMG ?
- trap_GetConfigstring ( CS_GAMETYPE_REDTEAM, temp, MAX_QPATH );
- if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_RED] ) )
- {
- level.gametypeTeam[TEAM_RED] = trap_VM_LocalStringAlloc ( temp );
- }
- // Blue team change from RMG ?
- trap_GetConfigstring ( CS_GAMETYPE_BLUETEAM, temp, MAX_QPATH );
- if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_BLUE] ) )
- {
- level.gametypeTeam[TEAM_BLUE] = trap_VM_LocalStringAlloc ( temp );
- }
- }
- */
- }
- }
- //rww - Called by skyportal entities. This will check through entities and flag them
- //as portal ents if they are in the same pvs as a skyportal entity and pass
- //a direct point trace check between origins. I really wanted to use an eFlag for
- //flagging portal entities, but too many entities like to reset their eFlags.
- //Note that this was not part of the original wolf sky portal stuff.
- void G_PortalifyEntities(gentity_t *ent)
- {
- int i = 0;
- gentity_t *scan = NULL;
- while (i < MAX_GENTITIES)
- {
- scan = &g_entities[i];
- if (scan && scan->inuse && scan->s.number != ent->s.number && trap_InPVS(ent->s.origin, scan->r.currentOrigin))
- {
- trace_t tr;
- trap_Trace(&tr, ent->s.origin, vec3_origin, vec3_origin, scan->r.currentOrigin, ent->s.number, CONTENTS_SOLID);
- if (tr.fraction == 1.0 || (tr.entityNum == scan->s.number && tr.entityNum != ENTITYNUM_NONE && tr.entityNum != ENTITYNUM_WORLD))
- {
- if (!scan->client || scan->s.eType == ET_NPC)
- { //making a client a portal entity would be bad.
- scan->s.isPortalEnt = qtrue; //he's flagged now
- }
- }
- }
- i++;
- }
- ent->think = G_FreeEntity; //the portal entity is no longer needed because its information is stored in a config string.
- ent->nextthink = level.time;
- }
- /*QUAKED misc_skyportal_orient (.6 .7 .7) (-8 -8 0) (8 8 16)
- point from which to orient the sky portal cam in relation
- to the regular view position.
- "modelscale" the scale at which to scale positions
- */
- void SP_misc_skyportal_orient (gentity_t *ent)
- {
- G_FreeEntity(ent);
- }
- /*QUAKED misc_skyportal (.6 .7 .7) (-8 -8 0) (8 8 16)
- "fov" for the skybox default is 80
- To have the portal sky fogged, enter any of the following values:
- "onlyfoghere" if non-0 allows you to set a global fog, but will only use that fog within this sky portal.
- Also note that entities in the same PVS and visible (via point trace) from this
- object will be flagged as portal entities. This means they will be sent and
- updated from the server for every client every update regardless of where
- they are, and they will essentially be added to the scene twice if the client
- is in the same PVS as them (only once otherwise, but still once no matter
- where the client is). In other words, don't go overboard with it or everything
- will explode.
- */
- void SP_misc_skyportal (gentity_t *ent)
- {
- char *fov;
- vec3_t fogv; //----(SA)
- int fogn; //----(SA)
- int fogf; //----(SA)
- int isfog = 0; // (SA)
- float fov_x;
- G_SpawnString ("fov", "80", &fov);
- fov_x = atof (fov);
- isfog += G_SpawnVector ("fogcolor", "0 0 0", fogv);
- isfog += G_SpawnInt ("fognear", "0", &fogn);
- isfog += G_SpawnInt ("fogfar", "300", &fogf);
- trap_SetConfigstring( CS_SKYBOXORG, va("%.2f %.2f %.2f %.1f %i %.2f %.2f %.2f %i %i", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], fov_x, (int)isfog, fogv[0], fogv[1], fogv[2], fogn, fogf ) );
- ent->think = G_PortalifyEntities;
- ent->nextthink = level.time + 1050; //give it some time first so that all other entities are spawned.
- }
- /*QUAKED misc_holocron (0 0 1) (-8 -8 -8) (8 8 8)
- count Set to type of holocron (based on force power value)
- HEAL = 0
- JUMP = 1
- SPEED = 2
- PUSH = 3
- PULL = 4
- TELEPATHY = 5
- GRIP = 6
- LIGHTNING = 7
- RAGE = 8
- PROTECT = 9
- ABSORB = 10
- TEAM HEAL = 11
- TEAM FORCE = 12
- DRAIN = 13
- SEE = 14
- SABERATTACK = 15
- SABERDEFEND = 16
- SABERTHROW = 17
- */
- /*char *holocronTypeModels[] = {
- "models/chunks/rock/rock_big.md3",//FP_HEAL,
- "models/chunks/rock/rock_big.md3",//FP_LEVITATION,
- "models/chunks/rock/rock_big.md3",//FP_SPEED,
- "models/chunks/rock/rock_big.md3",//FP_PUSH,
- "models/chunks/rock/rock_big.md3",//FP_PULL,
- "models/chunks/rock/rock_big.md3",//FP_TELEPATHY,
- "models/chunks/rock/rock_big.md3",//FP_GRIP,
- "models/chunks/rock/rock_big.md3",//FP_LIGHTNING,
- "models/chunks/rock/rock_big.md3",//FP_RAGE,
- "models/chunks/rock/rock_big.md3",//FP_PROTECT,
- "models/chunks/rock/rock_big.md3",//FP_ABSORB,
- "models/chunks/rock/rock_big.md3",//FP_TEAM_HEAL,
- "models/chunks/rock/rock_big.md3",//FP_TEAM_FORCE,
- "models/chunks/rock/rock_big.md3",//FP_DRAIN,
- "models/chunks/rock/rock_big.md3",//FP_SEE
- "models/chunks/rock/rock_big.md3",//FP_SABER_OFFENSE
- "models/chunks/rock/rock_big.md3",//FP_SABER_DEFENSE
- "models/chunks/rock/rock_big.md3"//FP_SABERTHROW
- };*/
- void HolocronRespawn(gentity_t *self)
- {
- self->s.modelindex = (self->count - 128);
- }
- void HolocronPopOut(gentity_t *self)
- {
- if (Q_irand(1, 10) < 5)
- {
- self->s.pos.trDelta[0] = 150 + Q_irand(1, 100);
- }
- else
- {
- self->s.pos.trDelta[0] = -150 - Q_irand(1, 100);
- }
- if (Q_irand(1, 10) < 5)
- {
- self->s.pos.trDelta[1] = 150 + Q_irand(1, 100);
- }
- else
- {
- self->s.pos.trDelta[1] = -150 - Q_irand(1, 100);
- }
- self->s.pos.trDelta[2] = 150 + Q_irand(1, 100);
- }
- void HolocronTouch(gentity_t *self, gentity_t *other, trace_t *trace)
- {
- int i = 0;
- int othercarrying = 0;
- float time_lowest = 0;
- int index_lowest = -1;
- int hasall = 1;
- int forceReselect = WP_NONE;
- if (trace)
- {
- self->s.groundEntityNum = trace->entityNum;
- }
- if (!other || !other->client || other->health < 1)
- {
- return;
- }
- if (!self->s.modelindex)
- {
- return;
- }
- if (self->enemy)
- {
- return;
- }
- if (other->client->ps.holocronsCarried[self->count])
- {
- return;
- }
- if (other->client->ps.holocronCantTouch == self->s.number && other->client->ps.holocronCantTouchTime > level.time)
- {
- return;
- }
- while (i < NUM_FORCE_POWERS)
- {
- if (other->client->ps.holocronsCarried[i])
- {
- othercarrying++;
- if (index_lowest == -1 || other->client->ps.holocronsCarried[i] < time_lowest)
- {
- index_lowest = i;
- time_lowest = other->client->ps.holocronsCarried[i];
- }
- }
- else if (i != self->count)
- {
- hasall = 0;
- }
- i++;
- }
- if (hasall)
- { //once we pick up this holocron we'll have all of them, so give us super special best prize!
- //G_Printf("You deserve a pat on the back.\n");
- }
- if (!(other->client->ps.fd.forcePowersActive & (1 << other->client->ps.fd.forcePowerSelected)))
- { //If the player isn't using his currently selected force power, select this one
- if (self->count != FP_SABER_OFFENSE && self->count != FP_SABER_DEFENSE && self->count != FP_SABERTHROW && self->count != FP_LEVITATION)
- {
- other->client->ps.fd.forcePowerSelected = self->count;
- }
- }
- if (g_maxHolocronCarry.integer && othercarrying >= g_maxHolocronCarry.integer)
- { //make the oldest holocron carried by the player pop out to make room for this one
- other->client->ps.holocronsCarried[index_lowest] = 0;
- /*
- if (index_lowest == FP_SABER_OFFENSE && !HasSetSaberOnly())
- { //you lost your saberattack holocron, so no more saber for you
- other->client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
- other->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
- if (other->client->ps.weapon == WP_SABER)
- {
- forceReselect = WP_SABER;
- }
- }
- */
- //NOTE: No longer valid as we are now always giving a force level 1 saber attack level in holocron
- }
- //G_Sound(other, CHAN_AUTO, G_SoundIndex("sound/weapons/w_pkup.wav"));
- G_AddEvent( other, EV_ITEM_PICKUP, self->s.number );
- other->client->ps.holocronsCarried[self->count] = level.time;
- self->s.modelindex = 0;
- self->enemy = other;
- self->pos2[0] = 1;
- self->pos2[1] = level.time + HOLOCRON_RESPAWN_TIME;
- /*
- if (self->count == FP_SABER_OFFENSE && !HasSetSaberOnly())
- { //player gets a saber
- other->client->ps.stats[STAT_WEAPONS] |= (1 << WP_SABER);
- other->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_STUN_BATON);
- if (other->client->ps.weapon == WP_STUN_BATON)
- {
- forceReselect = WP_STUN_BATON;
- }
- }
- */
- if (forceReselect != WP_NONE)
- {
- G_AddEvent(other, EV_NOAMMO, forceReselect);
- }
- //G_Printf("DON'T TOUCH ME\n");
- }
- void HolocronThink(gentity_t *ent)
- {
- if (ent->pos2[0] && (!ent->enemy || !ent->enemy->client || ent->enemy->health < 1))
- {
- if (ent->enemy && ent->enemy->client)
- {
- HolocronRespawn(ent);
- VectorCopy(ent->enemy->client->ps.origin, ent->s.pos.trBase);
- VectorCopy(ent->enemy->client->ps.origin, ent->s.origin);
- VectorCopy(ent->enemy->client->ps.origin, ent->r.currentOrigin);
- //copy to person carrying's origin before popping out of them
- HolocronPopOut(ent);
- ent->enemy->client->ps.holocronsCarried[ent->count] = 0;
- ent->enemy = NULL;
-
- goto justthink;
- }
- }
- else if (ent->pos2[0] && ent->enemy && ent->enemy->client)
- {
- ent->pos2[1] = level.time + HOLOCRON_RESPAWN_TIME;
- }
- if (ent->enemy && ent->enemy->client)
- {
- if (!ent->enemy->client->ps.holocronsCarried[ent->count])
- {
- ent->enemy->client->ps.holocronCantTouch = ent->s.number;
- ent->enemy->client->ps.holocronCantTouchTime = level.time + 5000;
- HolocronRespawn(ent);
- VectorCopy(ent->enemy->client->ps.origin, ent->s.pos.trBase);
- VectorCopy(ent->enemy->client->ps.origin, ent->s.origin);
- VectorCopy(ent->enemy->client->ps.origin, ent->r.currentOrigin);
- //copy to person carrying's origin before popping out of them
- HolocronPopOut(ent);
- ent->enemy = NULL;
- goto justthink;
- }
- if (!ent->enemy->inuse || (ent->enemy->client && ent->enemy->client->ps.fallingToDeath))
- {
- if (ent->enemy->inuse && ent->enemy->client)
- {
- ent->enemy->client->ps.holocronBits &= ~(1 << ent->count);
- ent->enemy->client->ps.holocronsCarried[ent->count] = 0;
- }
- ent->enemy = NULL;
- HolocronRespawn(ent);
- VectorCopy(ent->s.origin2, ent->s.pos.trBase);
- VectorCopy(ent->s.origin2, ent->s.origin);
- VectorCopy(ent->s.origin2, ent->r.currentOrigin);
- ent->s.pos.trTime = level.time;
- ent->pos2[0] = 0;
- trap_LinkEntity(ent);
- goto justthink;
- }
- }
- if (ent->pos2[0] && ent->pos2[1] < level.time)
- { //isn't in original place and has been there for (HOLOCRON_RESPAWN_TIME) seconds without being picked up, so respawn
- VectorCopy(ent->s.origin2, ent->s.pos.trBase);
- VectorCopy(ent->s.origin2, ent->s.origin);
- VectorCopy(ent->s.origin2, ent->r.currentOrigin);
- ent->s.pos.trTime = level.time;
- ent->pos2[0] = 0;
- trap_LinkEntity(ent);
- }
- justthink:
- ent->nextthink = level.time + 50;
- if (ent->s.pos.trDelta[0] || ent->s.pos.trDelta[1] || ent->s.pos.trDelta[2])
- {
- G_RunObject(ent);
- }
- }
- void SP_misc_holocron(gentity_t *ent)
- {
- vec3_t dest;
- trace_t tr;
- if (level.gametype != GT_HOLOCRON)
- {
- G_FreeEntity(ent);
- return;
- }
- if (HasSetSaberOnly())
- {
- if (ent->count == FP_SABER_OFFENSE ||
- ent->count == FP_SABER_DEFENSE ||
- ent->count == FP_SABERTHROW)
- { //having saber holocrons in saber only mode is pointless
- G_FreeEntity(ent);
- return;
- }
- }
- ent->s.isJediMaster = qtrue;
- VectorSet( ent->r.maxs, 8, 8, 8 );
- VectorSet( ent->r.mins, -8, -8, -8 );
- ent->s.origin[2] += 0.1f;
- ent->r.maxs[2] -= 0.1f;
- VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
- trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
- if ( tr.startsolid )
- {
- G_Printf ("SP_misc_holocron: misc_holocron startsolid at %s\n", vtos(ent->s.origin));
- G_FreeEntity( ent );
- return;
- }
- //add the 0.1 back after the trace
- ent->r.maxs[2] += 0.1f;
- // allow to ride movers
- // ent->s.groundEntityNum = tr.entityNum;
- G_SetOrigin( ent, tr.endpos );
- if (ent->count < 0)
- {
- ent->count = 0;
- }
- if (ent->count >= NUM_FORCE_POWERS)
- {
- ent->count = NUM_FORCE_POWERS-1;
- }
- /*
- if (g_forcePowerDisable.integer &&
- (g_forcePowerDisable.integer & (1 << ent->count)))
- {
- G_FreeEntity(ent);
- return;
- }
- */
- //No longer doing this, causing too many complaints about accidentally setting no force powers at all
- //and starting a holocron game (making it basically just FFA)
- ent->enemy = NULL;
- ent->flags = FL_BOUNCE_HALF;
- ent->s.modelindex = (ent->count - 128);//G_ModelIndex(holocronTypeModels[ent->count]);
- ent->s.eType = ET_HOLOCRON;
- ent->s.pos.trType = TR_GRAVITY;
- ent->s.pos.trTime = level.time;
- ent->r.contents = CONTENTS_TRIGGER;
- ent->clipmask = MASK_SOLID;
- ent->s.trickedentindex4 = ent->count;
- if (forcePowerDarkLight[ent->count] == FORCE_DARKSIDE)
- {
- ent->s.trickedentindex3 = 1;
- }
- else if (forcePowerDarkLight[ent->count] == FORCE_LIGHTSIDE)
- {
- ent->s.trickedentindex3 = 2;
- }
- else
- {
- ent->s.trickedentindex3 = 3;
- }
- ent->physicsObject = qtrue;
- VectorCopy(ent->s.pos.trBase, ent->s.origin2); //remember the spawn spot
- ent->touch = HolocronTouch;
- trap_LinkEntity(ent);
- ent->think = HolocronThink;
- ent->nextthink = level.time + 50;
- }
- /*
- ======================================================================
- SHOOTERS
- ======================================================================
- */
- void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
- vec3_t dir;
- float deg;
- vec3_t up, right;
- // see if we have a target
- if ( ent->enemy ) {
- VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir );
- VectorNormalize( dir );
- } else {
- VectorCopy( ent->movedir, dir );
- }
- // randomize a bit
- PerpendicularVector( up, dir );
- CrossProduct( up, dir, right );
- deg = crandom() * ent->random;
- VectorMA( dir, deg, up, dir );
- deg = crandom() * ent->random;
- VectorMA( dir, deg, right, dir );
- VectorNormalize( dir );
- switch ( ent->s.weapon ) {
- case WP_BLASTER:
- WP_FireBlasterMissile( ent, ent->s.origin, dir, qfalse );
- break;
- }
- G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
- }
- static void InitShooter_Finish( gentity_t *ent ) {
- ent->enemy = G_PickTarget( ent->target );
- ent->think = 0;
- ent->nextthink = 0;
- }
- void InitShooter( gentity_t *ent, int weapon ) {
- ent->use = Use_Shooter;
- ent->s.weapon = weapon;
- RegisterItem( BG_FindItemForWeapon( 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 ) {
- ent->think = InitShooter_Finish;
- ent->nextthink = level.time + 500;
- }
- trap_LinkEntity( ent );
- }
- /*QUAKED shooter_blaster (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_blaster( gentity_t *ent ) {
- InitShooter( ent, WP_BLASTER);
- }
- void check_recharge(gentity_t *ent)
- {
- if (ent->fly_sound_debounce_time < level.time ||
- !ent->activator ||
- !ent->activator->client ||
- !(ent->activator->client->pers.cmd.buttons & BUTTON_USE))
- {
- if (ent->activator)
- {
- G_Sound(ent, CHAN_AUTO, ent->genericValue7);
- }
- ent->s.loopSound = 0;
- ent->s.loopIsSoundset = qfalse;
- ent->activator = NULL;
- ent->fly_sound_debounce_time = 0;
- }
-
- if (!ent->activator)
- { //don't recharge during use
- if (ent->genericValue8 < level.time)
- {
- if (ent->count < ent->genericValue4)
- {
- ent->count++;
- }
- ent->genericValue8 = level.time + ent->genericValue5;
- }
- }
- ent->s.health = ent->count; //the "health bar" is gonna be how full we are
- ent->nextthink = level.time;
- }
- /*
- ================
- EnergyShieldStationSettings
- ================
- */
- void EnergyShieldStationSettings(gentity_t *ent)
- {
- G_SpawnInt( "count", "200", &ent->count );
- G_SpawnInt("chargerate", "0", &ent->genericValue5);
- if (!ent->genericValue5)
- {
- ent->genericValue5 = STATION_RECHARGE_TIME;
- }
- }
- /*
- ================
- shield_power_converter_use
- ================
- */
- void shield_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int dif,add;
- int stop = 1;
- if (!activator || !activator->client)
- {
- return;
- }
- if ( level.gametype == GT_SIEGE
- && other
- && other->client
- && other->client->siegeClass )
- {
- if ( !bgSiegeClasses[other->client->siegeClass].maxarmor )
- {//can't use it!
- G_Sound(self, CHAN_AUTO, G_SoundIndex("sound/interface/shieldcon_empty"));
- return;
- }
- }
- if (self->setTime < level.time)
- {
- int maxArmor;
- if (!self->s.loopSound)
- {
- self->s.loopSound = G_SoundIndex("sound/interface/shieldcon_run");
- self->s.loopIsSoundset = qfalse;
- }
- self->setTime = level.time + 100;
- if ( level.gametype == GT_SIEGE
- && other
- && other->client
- && other->client->siegeClass != -1 )
- {
- maxArmor = bgSiegeClasses[other->client->siegeClass].maxarmor;
- }
- else
- {
- maxArmor = activator->client->ps.stats[STAT_MAX_HEALTH];
- }
- dif = maxArmor - activator->client->ps.stats[STAT_ARMOR];
- if (dif > 0) // Already at full armor?
- {
- if (dif >MAX_AMMO_GIVE)
- {
- add = MAX_AMMO_GIVE;
- }
- else
- {
- add = dif;
- }
- if (self->count<add)
- {
- add = self->count;
- }
- if (!self->genericValue12)
- {
- self->count -= add;
- }
- if (self->count <= 0)
- {
- self->setTime = 0;
- }
- stop = 0;
- self->fly_sound_debounce_time = level.time + 500;
- self->activator = activator;
- activator->client->ps.stats[STAT_ARMOR] += add;
- }
- }
- if (stop || self->count <= 0)
- {
- if (self->s.loopSound && self->setTime < level.time)
- {
- if (self->count <= 0)
- {
- G_Sound(self, CHAN_AUTO, G_SoundIndex("sound/interface/shieldcon_empty"));
- }
- else
- {
- G_Sound(self, CHAN_AUTO, self->genericValue7);
- }
- }
- self->s.loopSound = 0;
- self->s.loopIsSoundset = qfalse;
- if (self->setTime < level.time)
- {
- self->setTime = level.time + self->genericValue5+100;
- }
- }
- }
- //dispense generic ammo
- void ammo_generic_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int /*dif,*/ add;
- //int ammoType;
- int stop = 1;
- if (!activator || !activator->client)
- {
- return;
- }
- if (self->setTime < level.time)
- {
- qboolean gaveSome = qfalse;
- /*
- while (i < 3)
- {
- if (!self->s.loopSound)
- {
- self->s.loopSound = G_SoundIndex("sound/interface/ammocon_run");
- self->s.loopIsSoundset = qfalse;
- }
- self->setTime = level.time + 100;
- //dif = activator->client->ps.stats[STAT_MAX_HEALTH] - activator->client->ps.stats[STAT_ARMOR];
- switch (i)
- { //don't give rockets I guess
- case 0:
- ammoType = AMMO_BLASTER;
- break;
- case 1:
- ammoType = AMMO_POWERCELL;
- break;
- case 2:
- ammoType = AMMO_METAL_BOLTS;
- break;
- default:
- ammoType = -1;
- break;
- }
- if (ammoType != -1)
- {
- dif = ammoData[ammoType].max - activator->client->ps.ammo[ammoType];
- }
- else
- {
- dif = 0;
- }
- if (dif > 0)
- { //only give if not full
- if (dif > MAX_AMMO_GIVE)
- {
- add = MAX_AMMO_GIVE;
- }
- else
- {
- add = dif;
- }
- if (self->count<add)
- {
- add = self->count;
- }
- self->count -= add;
- if (self->count <= 0)
- {
- self->setTime = 0;
- break;
- }
- stop = 0;
- self->fly_sound_debounce_time = level.time + 500;
- self->activator = activator;
- activator->client->ps.ammo[ammoType] += add;
- }
- i++;
- }
- */
- int i = AMMO_BLASTER;
- if (!self->s.loopSound)
- {
- self->s.loopSound = G_SoundIndex("sound/interface/ammocon_run");
- self->s.loopIsSoundset = qfalse;
- }
- //self->setTime = level.time + 100;
- self->fly_sound_debounce_time = level.time + 500;
- self->activator = activator;
- while (i < AMMO_MAX)
- {
- add = ammoData[i].max*0.05;
- if (add < 1)
- {
- add = 1;
- }
- if ( ( (activator->client->ps.eFlags & EF_DOUBLE_AMMO) && (activator->client->ps.ammo[i] < ammoData[i].max*2)) ||
- ( activator->client->ps.ammo[i] < ammoData[i].max ) )
- {
- gaveSome = qtrue;
- if ( level.gametype == GT_SIEGE && i == AMMO_ROCKETS && activator->client->ps.ammo[i] >= 10 )
- { //this stuff is already a freaking mess, so..
- gaveSome = qfalse;
- }
- activator->client->ps.ammo[i] += add;
- if ( level.gametype == GT_SIEGE && i == AMMO_ROCKETS && activator->client->ps.ammo[i] >= 10 )
- { // fixme - this should SERIOUSLY be externed.
- activator->client->ps.ammo[i] = 10;
- }
- else if ( activator->client->ps.eFlags & EF_DOUBLE_AMMO )
- {
- if (activator->client->ps.ammo[i] >= ammoData[i].max * 2)
- { // yuck.
- activator->client->ps.ammo[i] = ammoData[i].max * 2;
- }
- else
- {
- stop = 0;
- }
- }
- else
- {
- if (activator->client->ps.ammo[i] >= ammoData[i].max)
- {
- activator->client->ps.ammo[i] = ammoData[i].max;
- }
- else
- {
- stop = 0;
- }
- }
- }
- i++;
- if (!self->genericValue12 && gaveSome)
- {
- int sub = (add*0.2);
- if (sub < 1)
- {
- sub = 1;
- }
- self->count -= sub;
- if (self->count <= 0)
- {
- self->count = 0;
- stop = 1;
- break;
- }
- }
- }
- }
- if (stop || self->count <= 0)
- {
- if (self->s.loopSound && self->setTime < level.time)
- {
- if (self->count <= 0)
- {
- G_Sound(self, CHAN_AUTO, G_SoundIndex("sound/interface/ammocon_empty"));
- }
- else
- {
- G_Sound(self, CHAN_AUTO, self->genericValue7);
- }
- }
- self->s.loopSound = 0;
- self->s.loopIsSoundset = qfalse;
- if (self->setTime < level.time)
- {
- self->setTime = level.time + self->genericValue5+100;
- }
- }
- }
- /*QUAKED misc_ammo_floor_unit (1 0 0) (-16 -16 0) (16 16 40)
- model="/models/items/a_pwr_converter.md3"
- Gives generic ammo when used
- "count" - max charge value (default 200)
- "chargerate" - rechage 1 point every this many milliseconds (default 2000)
- "nodrain" - don't drain power from station if 1
- */
- void SP_misc_ammo_floor_unit(gentity_t *ent)
- {
- vec3_t dest;
- trace_t tr;
- VectorSet( ent->r.mins, -16, -16, 0 );
- VectorSet( ent->r.maxs, 16, 16, 40 );
- ent->s.origin[2] += 0.1f;
- ent->r.maxs[2] -= 0.1f;
- VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
- trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
- if ( tr.startsolid )
- {
- G_Printf ("SP_misc_ammo_floor_unit: misc_ammo_floor_unit startsolid at %s\n", vtos(ent->s.origin));
- G_FreeEntity( ent );
- return;
- }
- //add the 0.1 back after the trace
- ent->r.maxs[2] += 0.1f;
- // allow to ride movers
- ent->s.groundEntityNum = tr.entityNum;
- G_SetOrigin( ent, tr.endpos );
- if (!ent->health)
- {
- ent->health = 60;
- }
- if (!ent->model || !ent->model[0])
- {
- ent->model = "/models/items/a_pwr_converter.md3";
- }
- ent->s.modelindex = G_ModelIndex( ent->model );
- ent->s.eFlags = 0;
- ent->r.svFlags |= SVF_PLAYER_USABLE;
- ent->r.contents = CONTENTS_SOLID;
- ent->clipmask = MASK_SOLID;
- EnergyShieldStationSettings(ent);
- ent->genericValue4 = ent->count; //initial value
- ent->think = check_recharge;
- G_SpawnInt("nodrain", "0", &ent->genericValue12);
- if (!ent->genericValue12)
- {
- ent->s.maxhealth = ent->s.health = ent->count;
- }
- ent->s.shouldtarget = qtrue;
- ent->s.teamowner = 0;
- ent->s.owner = ENTITYNUM_NONE;
- ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
- ent->use = ammo_generic_power_converter_use;
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- trap_LinkEntity (ent);
- G_SoundIndex("sound/interface/ammocon_run");
- ent->genericValue7 = G_SoundIndex("sound/interface/ammocon_done");
- G_SoundIndex("sound/interface/ammocon_empty");
- if (level.gametype == GT_SIEGE)
- { //show on radar from everywhere
- ent->r.svFlags |= SVF_BROADCAST;
- ent->s.eFlags |= EF_RADAROBJECT;
- ent->s.genericenemyindex = G_IconIndex("gfx/mp/siegeicons/desert/weapon_recharge");
- }
- }
- /*QUAKED misc_shield_floor_unit (1 0 0) (-16 -16 0) (16 16 40)
- model="/models/items/a_shield_converter.md3"
- Gives shield energy when used.
- "count" - max charge value (default 50)
- "chargerate" - rechage 1 point every this many milliseconds (default 3000)
- "nodrain" - don't drain power from me
- */
- void SP_misc_shield_floor_unit( gentity_t *ent )
- {
- vec3_t dest;
- trace_t tr;
- if (level.gametype != GT_CTF &&
- level.gametype != GT_CTY &&
- level.gametype != GT_SIEGE)
- {
- G_FreeEntity( ent );
- return;
- }
- VectorSet( ent->r.mins, -16, -16, 0 );
- VectorSet( ent->r.maxs, 16, 16, 40 );
- ent->s.origin[2] += 0.1f;
- ent->r.maxs[2] -= 0.1f;
- VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
- trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
- if ( tr.startsolid )
- {
- G_Printf ("SP_misc_shield_floor_unit: misc_shield_floor_unit startsolid at %s\n", vtos(ent->s.origin));
- G_FreeEntity( ent );
- return;
- }
- //add the 0.1 back after the trace
- ent->r.maxs[2] += 0.1f;
- // allow to ride movers
- ent->s.groundEntityNum = tr.entityNum;
- G_SetOrigin( ent, tr.endpos );
- if (!ent->health)
- {
- ent->health = 60;
- }
- if (!ent->model || !ent->model[0])
- {
- ent->model = "/models/items/a_shield_converter.md3";
- }
- ent->s.modelindex = G_ModelIndex( ent->model );
- ent->s.eFlags = 0;
- ent->r.svFlags |= SVF_PLAYER_USABLE;
- ent->r.contents = CONTENTS_SOLID;
- ent->clipmask = MASK_SOLID;
- EnergyShieldStationSettings(ent);
- ent->genericValue4 = ent->count; //initial value
- ent->think = check_recharge;
- G_SpawnInt("nodrain", "0", &ent->genericValue12);
- if (!ent->genericValue12)
- {
- ent->s.maxhealth = ent->s.health = ent->count;
- }
- ent->s.shouldtarget = qtrue;
- ent->s.teamowner = 0;
- ent->s.owner = ENTITYNUM_NONE;
- ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
- ent->use = shield_power_converter_use;
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- trap_LinkEntity (ent);
- G_SoundIndex("sound/interface/shieldcon_run");
- ent->genericValue7 = G_SoundIndex("sound/interface/shieldcon_done");
- G_SoundIndex("sound/interface/shieldcon_empty");
- if (level.gametype == GT_SIEGE)
- { //show on radar from everywhere
- ent->r.svFlags |= SVF_BROADCAST;
- ent->s.eFlags |= EF_RADAROBJECT;
- ent->s.genericenemyindex = G_IconIndex("gfx/mp/siegeicons/desert/shield_recharge");
- }
- }
- /*QUAKED misc_model_shield_power_converter (1 0 0) (-16 -16 -16) (16 16 16)
- model="models/items/psd_big.md3"
- Gives shield energy when used.
- "count" - the amount of ammo given when used (default 200)
- */
- //------------------------------------------------------------
- void SP_misc_model_shield_power_converter( gentity_t *ent )
- {
- if (!ent->health)
- {
- ent->health = 60;
- }
- VectorSet (ent->r.mins, -16, -16, -16);
- VectorSet (ent->r.maxs, 16, 16, 16);
- ent->s.modelindex = G_ModelIndex( ent->model );
- ent->s.eFlags = 0;
- ent->r.svFlags |= SVF_PLAYER_USABLE;
- ent->r.contents = CONTENTS_SOLID;
- ent->clipmask = MASK_SOLID;
- EnergyShieldStationSettings(ent);
- ent->genericValue4 = ent->count; //initial value
- ent->think = check_recharge;
- ent->s.maxhealth = ent->s.health = ent->count;
- ent->s.shouldtarget = qtrue;
- ent->s.teamowner = 0;
- ent->s.owner = ENTITYNUM_NONE;
- ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
- ent->use = shield_power_converter_use;
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- trap_LinkEntity (ent);
- //G_SoundIndex("sound/movers/objects/useshieldstation.wav");
- ent->s.modelindex2 = G_ModelIndex("/models/items/psd_big.md3"); // Precache model
- }
- /*
- ================
- EnergyAmmoShieldStationSettings
- ================
- */
- void EnergyAmmoStationSettings(gentity_t *ent)
- {
- G_SpawnInt( "count", "200", &ent->count );
- }
- /*
- ================
- ammo_power_converter_use
- ================
- */
- void ammo_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int add = 0.0f;//,highest;
- qboolean overcharge;
- // int difBlaster,difPowerCell,difMetalBolts;
- int stop = 1;
- if (!activator || !activator->client)
- {
- return;
- }
- if (self->setTime < level.time)
- {
- overcharge = qfalse;
- if (!self->s.loopSound)
- {
- self->s.loopSound = G_SoundIndex("sound/player/pickupshield.wav");
- }
- self->setTime = level.time + 100;
- if (self->count) // Has it got any power left?
- {
- int i = AMMO_BLASTER;
- while (i < AMMO_MAX)
- {
- add = ammoData[i].max*0.1;
- if (add < 1)
- {
- add = 1;
- }
- if (activator->client->ps.ammo[i] < ammoData[i].max)
- {
- activator->client->ps.ammo[i] += add;
- if (activator->client->ps.ammo[i] > ammoData[i].max)
- {
- activator->client->ps.ammo[i] = ammoData[i].max;
- }
- }
- i++;
- }
- if (!self->genericValue12)
- {
- self->count -= add;
- }
- stop = 0;
- self->fly_sound_debounce_time = level.time + 500;
- self->activator = activator;
- /*
- if (self->count > MAX_AMMO_GIVE)
- {
- add = MAX_AMMO_GIVE;
- }
- else if (self->count<0)
- {
- add = 0;
- }
- else
- {
- add = self->count;
- }
- activator->client->ps.ammo[AMMO_BLASTER] += add;
- activator->client->ps.ammo[AMMO_POWERCELL] += add;
- activator->client->ps.ammo[AMMO_METAL_BOLTS] += add;
- self->count -= add;
- stop = 0;
- self->fly_sound_debounce_time = level.time + 500;
- self->activator = activator;
- difBlaster = activator->client->ps.ammo[AMMO_BLASTER] - ammoData[AMMO_BLASTER].max;
- difPowerCell = activator->client->ps.ammo[AMMO_POWERCELL] - ammoData[AMMO_POWERCELL].max;
- difMetalBolts = activator->client->ps.ammo[AMMO_METAL_BOLTS] - ammoData[AMMO_METAL_BOLTS].max;
- // Find the highest one
- highest = difBlaster;
- if (difPowerCell>difBlaster)
- {
- highest = difPowerCell;
- }
- if (difMetalBolts > highest)
- {
- highest = difMetalBolts;
- }
- */
- }
- }
- if (stop)
- {
- self->s.loopSound = 0;
- self->s.loopIsSoundset = qfalse;
- }
- }
- /*QUAKED misc_model_ammo_power_converter (1 0 0) (-16 -16 -16) (16 16 16)
- model="models/items/power_converter.md3"
- Gives ammo energy when used.
- "count" - the amount of ammo given when used (default 200)
- "nodrain" - don't drain power from me
- */
- //------------------------------------------------------------
- void SP_misc_model_ammo_power_converter( gentity_t *ent )
- {
- if (!ent->health)
- {
- ent->health = 60;
- }
- VectorSet (ent->r.mins, -16, -16, -16);
- VectorSet (ent->r.maxs, 16, 16, 16);
- ent->s.modelindex = G_ModelIndex( ent->model );
- ent->s.eFlags = 0;
- ent->r.svFlags |= SVF_PLAYER_USABLE;
- ent->r.contents = CONTENTS_SOLID;
- ent->clipmask = MASK_SOLID;
- G_SpawnInt("nodrain", "0", &ent->genericValue12);
- ent->use = ammo_power_converter_use;
- EnergyAmmoStationSettings(ent);
- ent->genericValue4 = ent->count; //initial value
- ent->think = check_recharge;
- if (!ent->genericValue12)
- {
- ent->s.maxhealth = ent->s.health = ent->count;
- }
- ent->s.shouldtarget = qtrue;
- ent->s.teamowner = 0;
- ent->s.owner = ENTITYNUM_NONE;
- ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- trap_LinkEntity (ent);
- //G_SoundIndex("sound/movers/objects/useshieldstation.wav");
- }
- /*
- ================
- EnergyHealthStationSettings
- ================
- */
- void EnergyHealthStationSettings(gentity_t *ent)
- {
- G_SpawnInt( "count", "200", &ent->count );
- }
- /*
- ================
- health_power_converter_use
- ================
- */
- void health_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
- {
- int dif,add;
- int stop = 1;
- if (!activator || !activator->client)
- {
- return;
- }
- if (self->setTime < level.time)
- {
- if (!self->s.loopSound)
- {
- self->s.loopSound = G_SoundIndex("sound/player/pickuphealth.wav");
- }
- self->setTime = level.time + 100;
- dif = activator->client->ps.stats[STAT_MAX_HEALTH] - activator->health;
- if (dif > 0) // Already at full armor?
- {
- if (dif >/*MAX_AMMO_GIVE*/5)
- {
- add = 5;//MAX_AMMO_GIVE;
- }
- else
- {
- add = dif;
- }
- if (self->count<add)
- {
- add = self->count;
- }
- //self->count -= add;
- stop = 0;
- self->fly_sound_debounce_time = level.time + 500;
- self->activator = activator;
- activator->health += add;
- }
- }
- if (stop)
- {
- self->s.loopSound = 0;
- self->s.loopIsSoundset = qfalse;
- }
- }
- /*QUAKED misc_model_health_power_converter (1 0 0) (-16 -16 -16) (16 16 16)
- model="models/items/power_converter.md3"
- Gives ammo energy when used.
- "count" - the amount of ammo given when used (default 200)
- */
- //------------------------------------------------------------
- void SP_misc_model_health_power_converter( gentity_t *ent )
- {
- if (!ent->health)
- {
- ent->health = 60;
- }
- VectorSet (ent->r.mins, -16, -16, -16);
- VectorSet (ent->r.maxs, 16, 16, 16);
- ent->s.modelindex = G_ModelIndex( ent->model );
- ent->s.eFlags = 0;
- ent->r.svFlags |= SVF_PLAYER_USABLE;
- ent->r.contents = CONTENTS_SOLID;
- ent->clipmask = MASK_SOLID;
- ent->use = health_power_converter_use;
- EnergyHealthStationSettings(ent);
- ent->genericValue4 = ent->count; //initial value
- ent->think = check_recharge;
- //ent->s.maxhealth = ent->s.health = ent->count;
- ent->s.shouldtarget = qtrue;
- ent->s.teamowner = 0;
- ent->s.owner = ENTITYNUM_NONE;
- ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
- G_SetOrigin( ent, ent->s.origin );
- VectorCopy( ent->s.angles, ent->s.apos.trBase );
- trap_LinkEntity (ent);
- //G_SoundIndex("sound/movers/objects/useshieldstation.wav");
- G_SoundIndex("sound/player/pickuphealth.wav");
- ent->genericValue7 = G_SoundIndex("sound/interface/shieldcon_done");
- if (level.gametype == GT_SIEGE)
- { //show on radar from everywhere
- ent->r.svFlags |= SVF_BROADCAST;
- ent->s.eFlags |= EF_RADAROBJECT;
- ent->s.genericenemyindex = G_IconIndex("gfx/mp/siegeicons/desert/bacta");
- }
- }
- #if 0 //damage box stuff
- void DmgBoxHit( gentity_t *self, gentity_t *other, trace_t *trace )
- {
- return;
- }
- void DmgBoxUpdateSelf(gentity_t *self)
- {
- gentity_t *owner = &g_entities[self->r.ownerNum];
- if (!owner || !owner->client || !owner->inuse)
- {
- goto killMe;
- }
- if (self->damageRedirect == DAMAGEREDIRECT_HEAD &&
- owner->client->damageBoxHandle_Head != self->s.number)
- {
- goto killMe;
- }
- if (self->damageRedirect == DAMAGEREDIRECT_RLEG &&
- owner->client->damageBoxHandle_RLeg != self->s.number)
- {
- goto killMe;
- }
- if (self->damageRedirect == DAMAGEREDIRECT_LLEG &&
- owner->client->damageBoxHandle_LLeg != self->s.number)
- {
- goto killMe;
- }
- if (owner->health < 1)
- {
- goto killMe;
- }
- //G_TestLine(self->r.currentOrigin, owner->client->ps.origin, 0x0000ff, 100);
- trap_LinkEntity(self);
- self->nextthink = level.time;
- return;
- killMe:
- self->think = G_FreeEntity;
- self->nextthink = level.time;
- }
- void DmgBoxAbsorb_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
- {
- self->health = 1;
- }
- void DmgBoxAbsorb_Pain(gentity_t *self, gentity_t *attacker, int damage)
- {
- self->health = 1;
- }
- gentity_t *CreateNewDamageBox( gentity_t *ent )
- {
- gentity_t *dmgBox;
- //We do not want the client to have any real knowledge of the entity whatsoever. It will only
- //ever be used on the server.
- dmgBox = G_Spawn();
- dmgBox->classname = "dmg_box";
-
- dmgBox->r.svFlags = SVF_USE_CURRENT_ORIGIN;
- dmgBox->r.ownerNum = ent->s.number;
- dmgBox->clipmask = 0;
- dmgBox->r.contents = MASK_PLAYERSOLID;
- dmgBox->mass = 5000;
- dmgBox->s.eFlags |= EF_NODRAW;
- dmgBox->r.svFlags |= SVF_NOCLIENT;
- dmgBox->touch = DmgBoxHit;
- dmgBox->takedamage = qtrue;
- dmgBox->health = 1;
- dmgBox->pain = DmgBoxAbsorb_Pain;
- dmgBox->die = DmgBoxAbsorb_Die;
- dmgBox->think = DmgBoxUpdateSelf;
- dmgBox->nextthink = level.time + 50;
- return dmgBox;
- }
- void ATST_ManageDamageBoxes(gentity_t *ent)
- {
- vec3_t headOrg, lLegOrg, rLegOrg;
- vec3_t fwd, right, up, flatAngle;
- if (!ent->client->damageBoxHandle_Head)
- {
- gentity_t *dmgBox = CreateNewDamageBox(ent);
- if (dmgBox)
- {
- VectorSet( dmgBox->r.mins, ATST_MINS0, ATST_MINS1, ATST_MINS2 );
- VectorSet( dmgBox->r.maxs, ATST_MAXS0, ATST_MAXS1, ATST_HEADSIZE );
- ent->client->damageBoxHandle_Head = dmgBox->s.number;
- dmgBox->damageRedirect = DAMAGEREDIRECT_HEAD;
- dmgBox->damageRedirectTo = ent->s.number;
- }
- }
- if (!ent->client->damageBoxHandle_RLeg)
- {
- gentity_t *dmgBox = CreateNewDamageBox(ent);
- if (dmgBox)
- {
- VectorSet( dmgBox->r.mins, ATST_MINS0/4, ATST_MINS1/4, ATST_MINS2 );
- VectorSet( dmgBox->r.maxs, ATST_MAXS0/4, ATST_MAXS1/4, ATST_MAXS2-ATST_HEADSIZE );
- ent->client->damageBoxHandle_RLeg = dmgBox->s.number;
- dmgBox->damageRedirect = DAMAGEREDIRECT_RLEG;
- dmgBox->damageRedirectTo = ent->s.number;
- }
- }
- if (!ent->client->damageBoxHandle_LLeg)
- {
- gentity_t *dmgBox = CreateNewDamageBox(ent);
- if (dmgBox)
- {
- VectorSet( dmgBox->r.mins, ATST_MINS0/4, ATST_MINS1/4, ATST_MINS2 );
- VectorSet( dmgBox->r.maxs, ATST_MAXS0/4, ATST_MAXS1/4, ATST_MAXS2-ATST_HEADSIZE );
- ent->client->damageBoxHandle_LLeg = dmgBox->s.number;
- dmgBox->damageRedirect = DAMAGEREDIRECT_LLEG;
- dmgBox->damageRedirectTo = ent->s.number;
- }
- }
- if (!ent->client->damageBoxHandle_Head ||
- !ent->client->damageBoxHandle_LLeg ||
- !ent->client->damageBoxHandle_RLeg)
- {
- return;
- }
- VectorCopy(ent->client->ps.origin, headOrg);
- headOrg[2] += (ATST_MAXS2-ATST_HEADSIZE);
- VectorCopy(ent->client->ps.viewangles, flatAngle);
- flatAngle[PITCH] = 0;
- flatAngle[ROLL] = 0;
- AngleVectors(flatAngle, fwd, right, up);
- VectorCopy(ent->client->ps.origin, lLegOrg);
- VectorCopy(ent->client->ps.origin, rLegOrg);
- lLegOrg[0] -= right[0]*32;
- lLegOrg[1] -= right[1]*32;
- lLegOrg[2] -= right[2]*32;
- rLegOrg[0] += right[0]*32;
- rLegOrg[1] += right[1]*32;
- rLegOrg[2] += right[2]*32;
- G_SetOrigin(&g_entities[ent->client->damageBoxHandle_Head], headOrg);
- G_SetOrigin(&g_entities[ent->client->damageBoxHandle_LLeg], lLegOrg);
- G_SetOrigin(&g_entities[ent->client->damageBoxHandle_RLeg], rLegOrg);
- }
- int G_PlayerBecomeATST(gentity_t *ent)
- {
- if (!ent || !ent->client)
- {
- return 0;
- }
- if (ent->client->ps.weaponTime > 0)
- {
- return 0;
- }
- if (ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
- {
- return 0;
- }
- if (ent->client->ps.zoomMode)
- {
- return 0;
- }
- if (ent->client->ps.usingATST)
- {
- ent->client->ps.usingATST = qfalse;
- ent->client->ps.forceHandExtend = HANDEXTEND_WEAPONREADY;
- }
- else
- {
- ent->client->ps.usingATST = qtrue;
- }
- ent->client->ps.weaponTime = 1000;
- return 1;
- }
- #endif
- //----------------------------------------------------------
- /*QUAKED fx_runner (0 0 1) (-8 -8 -8) (8 8 8) STARTOFF ONESHOT DAMAGE
- Runs the specified effect, can also be targeted at an info_notnull to orient the effect
- STARTOFF - effect starts off, toggles on/off when used
- ONESHOT - effect fires only when used
- DAMAGE - does radius damage around effect every "delay" milliseonds
- "fxFile" - name of the effect file to play
- "target" - direction to aim the effect in, otherwise defaults to up
- "target2" - uses its target2 when the fx gets triggered
- "delay" - how often to call the effect, don't over-do this ( default 200 )
- "random" - random amount of time to add to delay, ( default 0, 200 = 0ms to 200ms )
- "splashRadius" - only works when damage is checked ( default 16 )
- "splashDamage" - only works when damage is checked ( default 5 )
- "soundset" - bmodel set to use, plays start sound when toggled on, loop sound while on ( doesn't play on a oneshot), and a stop sound when turned off
- */
- #define FX_RUNNER_RESERVED 0x800000
- #define FX_ENT_RADIUS 32
- extern int BMS_START;
- extern int BMS_MID;
- extern int BMS_END;
- //----------------------------------------------------------
- void fx_runner_think( gentity_t *ent )
- {
- BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
- BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles );
- // call the effect with the desired position and orientation
- if (ent->s.isPortalEnt)
- {
- // G_AddEvent( ent, EV_PLAY_PORTAL_EFFECT_ID, ent->genericValue5 );
- }
- else
- {
- // G_AddEvent( ent, EV_PLAY_EFFECT_ID, ent->genericValue5 );
- }
- // start the fx on the client (continuous)
- ent->s.modelindex2 = FX_STATE_CONTINUOUS;
- VectorCopy(ent->r.currentAngles, ent->s.angles);
- VectorCopy(ent->r.currentOrigin, ent->s.origin);
- ent->nextthink = level.time + ent->delay + random() * ent->random;
- if ( ent->spawnflags & 4 ) // damage
- {
- G_RadiusDamage( ent->r.currentOrigin, ent, ent->splashDamage, ent->splashRadius, ent, ent, MOD_UNKNOWN );
- }
- if ( ent->target2 && ent->target2[0] )
- {
- // let our target know that we have spawned an effect
- G_UseTargets2( ent, ent, ent->target2 );
- }
- if ( !(ent->spawnflags & 2 ) && !ent->s.loopSound ) // NOT ONESHOT...this is an assy thing to do
- {
- if ( ent->soundSet && ent->soundSet[0] )
- {
- ent->s.soundSetIndex = G_SoundSetIndex(ent->soundSet);
- ent->s.loopIsSoundset = qtrue;
- ent->s.loopSound = BMS_MID;
- }
- }
- }
- //----------------------------------------------------------
- void fx_runner_use( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if (self->s.isPortalEnt)
- { //rww - mark it as broadcast upon first use if it's within the area of a skyportal
- self->r.svFlags |= SVF_BROADCAST;
- }
- if ( self->spawnflags & 2 ) // ONESHOT
- {
- // call the effect with the desired position and orientation, as a safety thing,
- // make sure we aren't thinking at all.
- int saveState = self->s.modelindex2 + 1;
- fx_runner_think( self );
- self->nextthink = -1;
- // one shot indicator
- self->s.modelindex2 = saveState;
- if (self->s.modelindex2 > FX_STATE_ONE_SHOT_LIMIT)
- {
- self->s.modelindex2 = FX_STATE_ONE_SHOT;
- }
- if ( self->target2 )
- {
- // let our target know that we have spawned an effect
- G_UseTargets2( self, self, self->target2 );
- }
- if ( self->soundSet && self->soundSet[0] )
- {
- self->s.soundSetIndex = G_SoundSetIndex(self->soundSet);
- G_AddEvent( self, EV_BMODEL_SOUND, BMS_START);
- }
- }
- else
- {
- // ensure we are working with the right think function
- self->think = fx_runner_think;
- // toggle our state
- if ( self->nextthink == -1 )
- {
- // NOTE: we fire the effect immediately on use, the fx_runner_think func will set
- // up the nextthink time.
- fx_runner_think( self );
- if ( self->soundSet && self->soundSet[0] )
- {
- self->s.soundSetIndex = G_SoundSetIndex(self->soundSet);
- G_AddEvent( self, EV_BMODEL_SOUND, BMS_START);
- self->s.loopSound = BMS_MID;
- self->s.loopIsSoundset = qtrue;
- }
- }
- else
- {
- // turn off for now
- self->nextthink = -1;
- // turn off fx on client
- self->s.modelindex2 = FX_STATE_OFF;
- if ( self->soundSet && self->soundSet[0] )
- {
- self->s.soundSetIndex = G_SoundSetIndex(self->soundSet);
- G_AddEvent( self, EV_BMODEL_SOUND, BMS_END );
- self->s.loopSound = 0;
- self->s.loopIsSoundset = qfalse;
- }
- }
- }
- }
- //----------------------------------------------------------
- void fx_runner_link( gentity_t *ent )
- {
- vec3_t dir;
- if ( ent->target && ent->target[0] )
- {
- // try to use the target to override the orientation
- gentity_t *target = NULL;
- target = G_Find( target, FOFS(targetname), ent->target );
- if ( !target )
- {
- // Bah, no good, dump a warning, but continue on and use the UP vector
- Com_Printf( "fx_runner_link: target specified but not found: %s\n", ent->target );
- Com_Printf( " -assuming UP orientation.\n" );
- }
- else
- {
- // Our target is valid so let's override the default UP vector
- VectorSubtract( target->s.origin, ent->s.origin, dir );
- VectorNormalize( dir );
- vectoangles( dir, ent->s.angles );
- }
- }
- // don't really do anything with this right now other than do a check to warn the designers if the target2 is bogus
- if ( ent->target2 && ent->target2[0] )
- {
- gentity_t *target = NULL;
- target = G_Find( target, FOFS(targetname), ent->target2 );
- if ( !target )
- {
- // Target2 is bogus, but we can still continue
- Com_Printf( "fx_runner_link: target2 was specified but is not valid: %s\n", ent->target2 );
- }
- }
- G_SetAngles( ent, ent->s.angles );
- if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT
- {
- // We won't even consider thinking until we are used
- ent->nextthink = -1;
- }
- else
- {
- if ( ent->soundSet && ent->soundSet[0] )
- {
- ent->s.soundSetIndex = G_SoundSetIndex(ent->soundSet);
- ent->s.loopSound = BMS_MID;
- ent->s.loopIsSoundset = qtrue;
- }
- // Let's get to work right now!
- ent->think = fx_runner_think;
- ent->nextthink = level.time + 200; // wait a small bit, then start working
- }
- // make us useable if we can be targeted
- if ( ent->targetname && ent->targetname[0] )
- {
- ent->use = fx_runner_use;
- }
- }
- //----------------------------------------------------------
- void SP_fx_runner( gentity_t *ent )
- {
- char *fxFile;
- G_SpawnString( "fxFile", "", &fxFile );
- // Get our defaults
- G_SpawnInt( "delay", "200", &ent->delay );
- G_SpawnFloat( "random", "0", &ent->random );
- G_SpawnInt( "splashRadius", "16", &ent->splashRadius );
- G_SpawnInt( "splashDamage", "5", &ent->splashDamage );
- if (!ent->s.angles[0] && !ent->s.angles[1] && !ent->s.angles[2])
- {
- // didn't have angles, so give us the default of up
- VectorSet( ent->s.angles, -90, 0, 0 );
- }
- if ( !fxFile || !fxFile[0] )
- {
- Com_Printf( S_COLOR_RED"ERROR: fx_runner %s at %s has no fxFile specified\n", ent->targetname, vtos(ent->s.origin) );
- G_FreeEntity( ent );
- return;
- }
- // Try and associate an effect file, unfortunately we won't know if this worked or not
- // until the CGAME trys to register it...
- ent->s.modelindex = G_EffectIndex( fxFile );
- // important info transmitted
- ent->s.eType = ET_FX;
- ent->s.speed = ent->delay;
- ent->s.time = ent->random;
- ent->s.modelindex2 = FX_STATE_OFF;
- // Give us a bit of time to spawn in the other entities, since we may have to target one of 'em
- ent->think = fx_runner_link;
- ent->nextthink = level.time + 400;
- // Save our position and link us up!
- G_SetOrigin( ent, ent->s.origin );
- VectorSet( ent->r.maxs, FX_ENT_RADIUS, FX_ENT_RADIUS, FX_ENT_RADIUS );
- VectorScale( ent->r.maxs, -1, ent->r.mins );
- trap_LinkEntity( ent );
- }
- /*QUAKED fx_spacedust (1 0 0) (-16 -16 -16) (16 16 16)
- This world effect will spawn space dust globally into the level.
- "count" the number of snow particles (default of 1000)
- */
- //----------------------------------------------------------
- void SP_CreateSpaceDust( gentity_t *ent )
- {
- G_EffectIndex(va("*spacedust %i", ent->count));
- //G_EffectIndex("*constantwind ( 10 -10 0 )");
- }
- /*QUAKED fx_snow (1 0 0) (-16 -16 -16) (16 16 16)
- This world effect will spawn snow globally into the level.
- "count" the number of snow particles (default of 1000)
- */
- //----------------------------------------------------------
- void SP_CreateSnow( gentity_t *ent )
- {
- G_EffectIndex("*snow");
- G_EffectIndex("*fog");
- G_EffectIndex("*constantwind (100 100 -100)");
- }
- /*QUAKED fx_rain (1 0 0) (-16 -16 -16) (16 16 16)
- This world effect will spawn rain globally into the level.
- "count" the number of rain particles (default of 500)
- */
- //----------------------------------------------------------
- void SP_CreateRain( gentity_t *ent )
- {
- G_EffectIndex(va("*rain init %i", ent->count));
- }
- qboolean gEscaping = qfalse;
- int gEscapeTime = 0;
- void Use_Target_Screenshake( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- qboolean bGlobal = qfalse;
- if (ent->genericValue6)
- {
- bGlobal = qtrue;
- }
- G_ScreenShake(ent->s.origin, NULL, ent->speed, ent->genericValue5, bGlobal);
- }
- void SP_target_screenshake(gentity_t *ent)
- {
- G_SpawnFloat( "intensity", "10", &ent->speed );
- //intensity of the shake
- G_SpawnInt( "duration", "800", &ent->genericValue5 );
- //duration of the shake
- G_SpawnInt( "globalshake", "1", &ent->genericValue6 );
- //non-0 if shake should be global (all clients). Otherwise, only in the PVS.
- ent->use = Use_Target_Screenshake;
- }
- void LogExit( const char *string );
- void Use_Target_Escapetrig( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- if (!ent->genericValue6)
- {
- gEscaping = qtrue;
- gEscapeTime = level.time + ent->genericValue5;
- }
- else if (gEscaping)
- {
- int i = 0;
- gEscaping = qfalse;
- while (i < MAX_CLIENTS)
- { //all of the survivors get 100 points!
- if (g_entities[i].inuse && g_entities[i].client && g_entities[i].health > 0 &&
- g_entities[i].client->sess.sessionTeam != TEAM_SPECTATOR &&
- !(g_entities[i].client->ps.pm_flags & PMF_FOLLOW))
- {
- AddScore(&g_entities[i], g_entities[i].client->ps.origin, 100);
- }
- i++;
- }
- if (activator && activator->inuse && activator->client)
- { //the one who escaped gets 500
- AddScore(activator, activator->client->ps.origin, 500);
- }
- LogExit("Escaped!");
- }
- }
- void SP_target_escapetrig(gentity_t *ent)
- {
- if (level.gametype != GT_SINGLE_PLAYER)
- {
- G_FreeEntity(ent);
- return;
- }
- G_SpawnInt( "escapetime", "60000", &ent->genericValue5);
- //time given (in ms) for the escape
- G_SpawnInt( "escapegoal", "0", &ent->genericValue6);
- //if non-0, when used, will end an ongoing escape instead of start it
- ent->use = Use_Target_Escapetrig;
- }
- /*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)
- {
- //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->flags &= ~FL_INACTIVE;
- }
- }
-
- //use targets
- G_UseTargets( self, attacker );
- //die
- //rwwFIXMEFIXME - weap expl func
- // WP_Explode( self );
- }
- void maglock_link( gentity_t *self );
- gentity_t *G_FindDoorTrigger( gentity_t *ent );
- 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->genericValue1 = G_EffectIndex( "maglock/explosion" );
- G_SetOrigin( self, self->s.origin );
- self->think = 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;
- gentity_t *traceEnt;
- AngleVectors( self->s.angles, forward, NULL, NULL );
- VectorMA( self->s.origin, 128, forward, end );
- VectorMA( self->s.origin, -4, forward, start );
- trap_Trace( &trace, start, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT );
- 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->think = 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;
- }
- traceEnt = &g_entities[trace.entityNum];
- if ( trace.entityNum >= ENTITYNUM_WORLD || !traceEnt || Q_stricmp( "func_door", traceEnt->classname ) )
- {
- self->think = 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->flags |= FL_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->r.mins, -8, -8, -8 );
- VectorSet( self->r.maxs, 8, 8, 8 );
- self->r.contents = CONTENTS_CORPSE;
- //make it destroyable
- self->flags |= FL_SHIELDED;//only damagable by lightsabers
- self->takedamage = qtrue;
- self->health = 10;
- self->die = maglock_die;
- //self->fxID = G_EffectIndex( "maglock/explosion" );
- trap_LinkEntity( self );
- }
- void faller_touch(gentity_t *self, gentity_t *other, trace_t *trace)
- {
- if (self->epVelocity[2] < -100 && self->genericValue7 < level.time)
- {
- int r = Q_irand(1, 3);
- if (r == 1)
- {
- self->genericValue11 = G_SoundIndex("sound/chars/stofficer1/misc/pain25");
- }
- else if (r == 2)
- {
- self->genericValue11 = G_SoundIndex("sound/chars/stofficer1/misc/pain50");
- }
- else
- {
- self->genericValue11 = G_SoundIndex("sound/chars/stofficer1/misc/pain75");
- }
- G_EntitySound(self, CHAN_VOICE, self->genericValue11);
- G_EntitySound(self, CHAN_AUTO, self->genericValue10);
- self->genericValue6 = level.time + 3000;
- self->genericValue7 = level.time + 200;
- }
- }
- void faller_think(gentity_t *ent)
- {
- float gravity = 3.0f;
- float mass = 0.09f;
- float bounce = 1.1f;
- if (ent->genericValue6 < level.time)
- {
- ent->think = G_FreeEntity;
- ent->nextthink = level.time;
- return;
- }
- if (ent->epVelocity[2] < -100)
- {
- if (!ent->genericValue8)
- {
- G_EntitySound(ent, CHAN_VOICE, ent->genericValue9);
- ent->genericValue8 = 1;
- }
- }
- else
- {
- ent->genericValue8 = 0;
- }
- G_RunExPhys(ent, gravity, mass, bounce, qtrue, NULL, 0);
- VectorScale(ent->epVelocity, 10.0f, ent->s.pos.trDelta);
- ent->nextthink = level.time + 25;
- }
- void misc_faller_create( gentity_t *ent, gentity_t *other, gentity_t *activator )
- {
- gentity_t *faller = G_Spawn();
- faller->genericValue10 = G_SoundIndex("sound/player/fallsplat");
- faller->genericValue9 = G_SoundIndex("sound/chars/stofficer1/misc/falling1");
- faller->genericValue8 = 0;
- faller->genericValue7 = 0;
- faller->genericValue6 = level.time + 15000;
- G_SetOrigin(faller, ent->s.origin);
- faller->s.modelGhoul2 = 1;
- faller->s.modelindex = G_ModelIndex("models/players/stormtrooper/model.glm");
- faller->s.g2radius = 100;
-
- faller->s.customRGBA[0]=Q_irand(1,255);
- faller->s.customRGBA[1]=Q_irand(1,255);
- faller->s.customRGBA[2]=Q_irand(1,255);
- faller->s.customRGBA[3]=255;
- VectorSet(faller->r.mins, -15, -15, DEFAULT_MINS_2);
- VectorSet(faller->r.maxs, 15, 15, DEFAULT_MAXS_2);
-
- faller->clipmask = MASK_PLAYERSOLID;
- faller->r.contents = MASK_PLAYERSOLID;
- faller->s.eFlags = (EF_RAG|EF_CLIENTSMOOTH);
- faller->think = faller_think;
- faller->nextthink = level.time;
- faller->touch = faller_touch;
- faller->epVelocity[0] = flrand(-256.0f, 256.0f);
- faller->epVelocity[1] = flrand(-256.0f, 256.0f);
- trap_LinkEntity(faller);
- }
- void misc_faller_think(gentity_t *ent)
- {
- misc_faller_create(ent, ent, ent);
- ent->nextthink = level.time + ent->genericValue1 + Q_irand(0, ent->genericValue2);
- }
- /*QUAKED misc_faller (1 0 0) (-8 -8 -8) (8 8 8)
- Falling stormtrooper - spawned every interval+random fudgefactor,
- or if specified, when used.
- targetname - if specified, will only spawn when used
- interval - spawn every so often (milliseconds)
- fudgefactor - milliseconds between 0 and this number randomly added to interval
- */
- void SP_misc_faller(gentity_t *ent)
- {
- G_ModelIndex("models/players/stormtrooper/model.glm");
- G_SoundIndex("sound/chars/stofficer1/misc/pain25");
- G_SoundIndex("sound/chars/stofficer1/misc/pain50");
- G_SoundIndex("sound/chars/stofficer1/misc/pain75");
- G_SoundIndex("sound/chars/stofficer1/misc/falling1");
- G_SoundIndex("sound/player/fallsplat");
- G_SpawnInt("interval", "500", &ent->genericValue1);
- G_SpawnInt("fudgefactor", "0", &ent->genericValue2);
- if (!ent->targetname || !ent->targetname[0])
- {
- ent->think = misc_faller_think;
- ent->nextthink = level.time + ent->genericValue1 + Q_irand(0, ent->genericValue2);
- }
- else
- {
- ent->use = misc_faller_create;
- }
- }
- //rww - ref tag stuff ported from SP (and C-ified)
- #define TAG_GENERIC_NAME "__WORLD__" //If a designer chooses this name, cut a finger off as an example to the others
- //MAX_TAG_OWNERS is 16 for now in order to not use too much VM memory.
- //Each tag owner has preallocated space for tags up to MAX_TAGS.
- //As is this means 16*256 sizeof(reference_tag_t)'s in addition to name+inuse*16.
- #define MAX_TAGS 256
- #define MAX_TAG_OWNERS 16
- //Maybe I should use my trap_TrueMalloc/trap_TrueFree stuff with this.
- //But I am not yet confident that it can be used without exploding at some point.
- typedef struct tagOwner_s
- {
- char name[MAX_REFNAME];
- reference_tag_t tags[MAX_TAGS];
- qboolean inuse;
- } tagOwner_t;
- tagOwner_t refTagOwnerMap[MAX_TAG_OWNERS];
- tagOwner_t *FirstFreeTagOwner(void)
- {
- int i = 0;
- while (i < MAX_TAG_OWNERS)
- {
- if (!refTagOwnerMap[i].inuse)
- {
- return &refTagOwnerMap[i];
- }
- i++;
- }
- Com_Printf("WARNING: MAX_TAG_OWNERS (%i) REF TAG LIMIT HIT\n", MAX_TAG_OWNERS);
- return NULL;
- }
- reference_tag_t *FirstFreeRefTag(tagOwner_t *tagOwner)
- {
- int i = 0;
- assert(tagOwner);
- while (i < MAX_TAGS)
- {
- if (!tagOwner->tags[i].inuse)
- {
- return &tagOwner->tags[i];
- }
- i++;
- }
- Com_Printf("WARNING: MAX_TAGS (%i) REF TAG LIMIT HIT\n", MAX_TAGS);
- return NULL;
- }
- /*
- -------------------------
- TAG_Init
- -------------------------
- */
- void TAG_Init( void )
- {
- int i = 0;
- int x = 0;
- while (i < MAX_TAG_OWNERS)
- {
- while (x < MAX_TAGS)
- {
- memset(&refTagOwnerMap[i].tags[x], 0, sizeof(refTagOwnerMap[i].tags[x]));
- x++;
- }
- memset(&refTagOwnerMap[i], 0, sizeof(refTagOwnerMap[i]));
- i++;
- }
- }
- /*
- -------------------------
- TAG_FindOwner
- -------------------------
- */
- tagOwner_t *TAG_FindOwner( const char *owner )
- {
- int i = 0;
- while (i < MAX_TAG_OWNERS)
- {
- if (refTagOwnerMap[i].inuse && !Q_stricmp(refTagOwnerMap[i].name, owner))
- {
- return &refTagOwnerMap[i];
- }
- i++;
- }
- return NULL;
- }
- /*
- -------------------------
- TAG_Find
- -------------------------
- */
- reference_tag_t *TAG_Find( const char *owner, const char *name )
- {
- tagOwner_t *tagOwner = NULL;
- int i = 0;
- if (owner && owner[0])
- {
- tagOwner = TAG_FindOwner(owner);
- }
- if (!tagOwner)
- {
- tagOwner = TAG_FindOwner(TAG_GENERIC_NAME);
- }
- //Not found...
- if (!tagOwner)
- {
- tagOwner = TAG_FindOwner( TAG_GENERIC_NAME );
- if (!tagOwner)
- {
- return NULL;
- }
- }
- while (i < MAX_TAGS)
- {
- if (tagOwner->tags[i].inuse && !Q_stricmp(tagOwner->tags[i].name, name))
- {
- return &tagOwner->tags[i];
- }
- i++;
- }
- //Try the generic owner instead
- tagOwner = TAG_FindOwner( TAG_GENERIC_NAME );
- if (!tagOwner)
- {
- return NULL;
- }
- i = 0;
- while (i < MAX_TAGS)
- {
- if (tagOwner->tags[i].inuse && !Q_stricmp(tagOwner->tags[i].name, name))
- {
- return &tagOwner->tags[i];
- }
- i++;
- }
- return NULL;
- }
- /*
- -------------------------
- TAG_Add
- -------------------------
- */
- reference_tag_t *TAG_Add( const char *name, const char *owner, vec3_t origin, vec3_t angles, int radius, int flags )
- {
- reference_tag_t *tag = NULL;
- tagOwner_t *tagOwner = NULL;
- //Make sure this tag's name isn't alread in use
- if ( TAG_Find( owner, name ) )
- {
- Com_Printf(S_COLOR_RED"Duplicate tag name \"%s\"\n", name );
- return NULL;
- }
- //Attempt to add this to the owner's list
- if ( !owner || !owner[0] )
- {
- //If the owner isn't found, use the generic world name
- owner = TAG_GENERIC_NAME;
- }
- tagOwner = TAG_FindOwner( owner );
-
- if (!tagOwner)
- {
- //Create a new owner list
- tagOwner = FirstFreeTagOwner();//new tagOwner_t;
- if (!tagOwner)
- {
- assert(0);
- return 0;
- }
- }
-
- //This is actually reverse order of how SP does it because of the way we're storing/allocating.
- //Now that we have the owner, we want to get the first free reftag on the owner itself.
- tag = FirstFreeRefTag(tagOwner);
- if (!tag)
- {
- assert(0);
- return NULL;
- }
- //Copy the information
- VectorCopy( origin, tag->origin );
- VectorCopy( angles, tag->angles );
- tag->radius = radius;
- tag->flags = flags;
- if ( !name || !name[0] )
- {
- Com_Printf(S_COLOR_RED"ERROR: Nameless ref_tag found at (%i %i %i)\n", (int)origin[0], (int)origin[1], (int)origin[2]);
- return NULL;
- }
- //Copy the name
- Q_strncpyz( (char *) tagOwner->name, owner, MAX_REFNAME );
- Q_strlwr( (char *) tagOwner->name ); //NOTENOTE: For case insensitive searches on a map
- //Copy the name
- Q_strncpyz( (char *) tag->name, name, MAX_REFNAME );
- Q_strlwr( (char *) tag->name ); //NOTENOTE: For case insensitive searches on a map
- tagOwner->inuse = qtrue;
- tag->inuse = qtrue;
- return tag;
- }
- /*
- -------------------------
- TAG_GetOrigin
- -------------------------
- */
- int TAG_GetOrigin( const char *owner, const char *name, vec3_t origin )
- {
- reference_tag_t *tag = TAG_Find( owner, name );
- if (!tag)
- {
- VectorClear(origin);
- return 0;
- }
- VectorCopy( tag->origin, origin );
- return 1;
- }
- /*
- -------------------------
- TAG_GetOrigin2
- Had to get rid of that damn assert for dev
- -------------------------
- */
- int TAG_GetOrigin2( const char *owner, const char *name, vec3_t origin )
- {
- reference_tag_t *tag = TAG_Find( owner, name );
- if( tag == NULL )
- {
- return 0;
- }
- VectorCopy( tag->origin, origin );
- return 1;
- }
- /*
- -------------------------
- TAG_GetAngles
- -------------------------
- */
- int TAG_GetAngles( const char *owner, const char *name, vec3_t angles )
- {
- reference_tag_t *tag = TAG_Find( owner, name );
- if (!tag)
- {
- assert(0);
- return 0;
- }
- VectorCopy( tag->angles, angles );
-
- return 1;
- }
- /*
- -------------------------
- TAG_GetRadius
- -------------------------
- */
- int TAG_GetRadius( const char *owner, const char *name )
- {
- reference_tag_t *tag = TAG_Find( owner, name );
- if (!tag)
- {
- assert(0);
- return 0;
- }
- return tag->radius;
- }
- /*
- -------------------------
- TAG_GetFlags
- -------------------------
- */
- int TAG_GetFlags( const char *owner, const char *name )
- {
- reference_tag_t *tag = TAG_Find( owner, name );
- if (!tag)
- {
- assert(0);
- return 0;
- }
- return tag->flags;
- }
- /*
- ==============================================================================
- Spawn functions
- ==============================================================================
- */
- /*QUAKED ref_tag_huge (0.5 0.5 1) (-128 -128 -128) (128 128 128)
- SAME AS ref_tag, JUST BIGGER SO YOU CAN SEE THEM IN EDITOR ON HUGE MAPS!
- Reference tags which can be positioned throughout the level.
- These tags can later be refered to by the scripting system
- so that their origins and angles can be referred to.
- If you set angles on the tag, these will be retained.
- If you target a ref_tag at an entity, that will set the ref_tag's
- angles toward that entity.
- If you set the ref_tag's ownername to the ownername of an entity,
- it makes that entity is the owner of the ref_tag. This means
- that the owner, and only the owner, may refer to that tag.
- Tags may not have the same name as another tag with the same
- owner. However, tags with different owners may have the same
- name as one another. In this way, scripts can generically
- refer to tags by name, and their owners will automatically
- specifiy which tag is being referred to.
- targetname - the name of this tag
- ownername - the owner of this tag
- target - use to point the tag at something for angles
- */
- /*QUAKED ref_tag (0.5 0.5 1) (-8 -8 -8) (8 8 8)
- Reference tags which can be positioned throughout the level.
- These tags can later be refered to by the scripting system
- so that their origins and angles can be referred to.
- If you set angles on the tag, these will be retained.
- If you target a ref_tag at an entity, that will set the ref_tag's
- angles toward that entity.
- If you set the ref_tag's ownername to the ownername of an entity,
- it makes that entity is the owner of the ref_tag. This means
- that the owner, and only the owner, may refer to that tag.
- Tags may not have the same name as another tag with the same
- owner. However, tags with different owners may have the same
- name as one another. In this way, scripts can generically
- refer to tags by name, and their owners will automatically
- specifiy which tag is being referred to.
- targetname - the name of this tag
- ownername - the owner of this tag
- target - use to point the tag at something for angles
- */
- void ref_link ( gentity_t *ent )
- {
- reference_tag_t *tag;
- if ( ent->target )
- {
- //TODO: Find the target and set our angles to that direction
- gentity_t *target = G_Find( NULL, FOFS(targetname), ent->target );
- vec3_t dir;
- if ( target )
- {
- //Find the direction to the target
- VectorSubtract( target->s.origin, ent->s.origin, dir );
- VectorNormalize( dir );
- vectoangles( dir, ent->s.angles );
-
- //FIXME: Does pitch get flipped?
- }
- else
- {
- Com_Printf( S_COLOR_RED"ERROR: ref_tag (%s) has invalid target (%s)\n", ent->targetname, ent->target );
- }
- }
-
- //Add the tag
- tag = TAG_Add( ent->targetname, ent->ownername, ent->s.origin, ent->s.angles, 16, 0 );
- //Delete immediately, cannot be refered to as an entity again
- //NOTE: this means if you wanted to link them in a chain for, say, a path, you can't
- G_FreeEntity( ent );
- }
- void SP_reference_tag ( gentity_t *ent )
- {
- if ( ent->target )
- {
- //Init cannot occur until all entities have been spawned
- ent->think = ref_link;
- ent->nextthink = level.time + START_TIME_LINK_ENTS;
- }
- else
- {
- ref_link( ent );
- }
- }
- /*QUAKED misc_weapon_shooter (1 0 0) (-8 -8 -8) (8 8 8) ALTFIRE TOGGLE
- ALTFIRE - fire the alt-fire of the chosen weapon
- TOGGLE - keep firing until used again (fires at intervals of "wait")
- "wait" - debounce time between refires (defaults to 500)
- "target" - what to aim at (will update aim every frame if it's a moving target)
- "weapon" - specify the weapon to use (default is WP_BLASTER)
- WP_BRYAR_PISTOL
- WP_BLASTER
- WP_DISRUPTOR
- WP_BOWCASTER
- WP_REPEATER
- WP_DEMP2
- WP_FLECHETTE
- WP_ROCKET_LAUNCHER
- WP_THERMAL
- WP_TRIP_MINE
- WP_DET_PACK
- WP_STUN_BATON
- WP_EMPLACED_GUN
- WP_BOT_LASER
- WP_TURRET
- WP_ATST_MAIN
- WP_ATST_SIDE
- WP_TIE_FIGHTER
- WP_RAPID_FIRE_CONC
- WP_BLASTER_PISTOL
- */
- //kind of hacky, but we have to do this with no dynamic allocation
- #define MAX_SHOOTERS 16
- typedef struct shooterClient_s
- {
- gclient_t cl;
- qboolean inuse;
- } shooterClient_t;
- static shooterClient_t g_shooterClients[MAX_SHOOTERS];
- static qboolean g_shooterClientInit = qfalse;
- gclient_t *G_ClientForShooter(void)
- {
- int i = 0;
- if (!g_shooterClientInit)
- { //in theory it should be initialized to 0 on the stack, but just in case.
- memset(g_shooterClients, 0, sizeof(shooterClient_t)*MAX_SHOOTERS);
- g_shooterClientInit = qtrue;
- }
- while (i < MAX_SHOOTERS)
- {
- if (!g_shooterClients[i].inuse)
- {
- return &g_shooterClients[i].cl;
- }
- i++;
- }
- Com_Error(ERR_DROP, "No free shooter clients - hit MAX_SHOOTERS");
- return NULL;
- }
- void G_FreeClientForShooter(gclient_t *cl)
- {
- int i = 0;
- while (i < MAX_SHOOTERS)
- {
- if (&g_shooterClients[i].cl == cl)
- {
- g_shooterClients[i].inuse = qfalse;
- return;
- }
- i++;
- }
- }
- void misc_weapon_shooter_fire( gentity_t *self )
- {
- FireWeapon( self, (self->spawnflags&1) );
- if ( (self->spawnflags&2) )
- {//repeat
- self->think = misc_weapon_shooter_fire;
- self->nextthink = level.time + self->wait;
- }
- }
- void misc_weapon_shooter_use ( gentity_t *self, gentity_t *other, gentity_t *activator )
- {
- if ( self->think == misc_weapon_shooter_fire )
- {//repeating fire, stop
- /*
- G_FreeClientForShooter(self->client);
- self->think = G_FreeEntity;
- self->nextthink = level.time;
- */
- self->nextthink = 0;
- return;
- }
- //otherwise, fire
- misc_weapon_shooter_fire( self );
- }
- void misc_weapon_shooter_aim( gentity_t *self )
- {
- //update my aim
- if ( self->target )
- {
- gentity_t *targ = G_Find( NULL, FOFS(targetname), self->target );
- if ( targ )
- {
- self->enemy = targ;
- VectorSubtract( targ->r.currentOrigin, self->r.currentOrigin, self->pos1 );
- VectorCopy( targ->r.currentOrigin, self->pos1 );
- vectoangles( self->pos1, self->client->ps.viewangles );
- SetClientViewAngle( self, self->client->ps.viewangles );
- //FIXME: don't keep doing this unless target is a moving target?
- self->nextthink = level.time + FRAMETIME;
- }
- else
- {
- self->enemy = NULL;
- }
- }
- }
- extern stringID_table_t WPTable[];
- void SP_misc_weapon_shooter( gentity_t *self )
- {
- char *s;
- //alloc a client just for the weapon code to use
- self->client = G_ClientForShooter();//(gclient_s *)gi.Malloc(sizeof(gclient_s), TAG_G_ALLOC, qtrue);
- G_SpawnString("weapon", "", &s);
- //set weapon
- self->s.weapon = self->client->ps.weapon = WP_BLASTER;
- if ( s && s[0] )
- {//use a different weapon
- self->s.weapon = self->client->ps.weapon = GetIDForString( WPTable, s );
- }
- RegisterItem(BG_FindItemForWeapon(self->s.weapon));
- //set where our muzzle is
- VectorCopy( self->s.origin, self->client->renderInfo.muzzlePoint );
- //permanently updated (don't need for MP)
- //self->client->renderInfo.mPCalcTime = Q3_INFINITE;
- //set up to link
- if ( self->target )
- {
- self->think = misc_weapon_shooter_aim;
- self->nextthink = level.time + START_TIME_LINK_ENTS;
- }
- else
- {//just set aim angles
- VectorCopy( self->s.angles, self->client->ps.viewangles );
- AngleVectors( self->s.angles, self->pos1, NULL, NULL );
- }
- //set up to fire when used
- self->use = misc_weapon_shooter_use;
- if ( !self->wait )
- {
- self->wait = 500;
- }
- }
- /*QUAKED misc_weather_zone (0 .5 .8) ?
- Determines a region to check for weather contents - will significantly reduce load time
- */
- void SP_misc_weather_zone( gentity_t *ent )
- {
- G_FreeEntity(ent);
- }