/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
Large files files are truncated, but you can click here to view the full file
- // 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-…
Large files files are truncated, but you can click here to view the full file