// Copyright (C) 1999-2000 Id Software, Inc. // // g_misc.c #include "g_local.h" /*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 ){ if(!Q_stricmp(self->classname, "ref_tag") && !rpg_allowspmaps.integer) G_FreeEntity(self); G_SetOrigin( self, self->s.origin ); } /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear noIncidence Non-displayed light. "light" overrides the default 300 intensity. '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. */ void SP_light( gentity_t *self ) { G_FreeEntity( self ); } /* ================================================================================= TELEPORTERS ================================================================================= */ void TransportPlayer( gentity_t *player, vec3_t origin, vec3_t angles, int speed ) { gentity_t *tent = NULL; playerState_t *ps = &player->client->ps; clientSession_t *sess = &player->client->sess; // use temp events at source and destination to prevent the effect // from getting dropped by a second player event if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/ ) { vec3_t org; VectorCopy( ps->origin, org ); org[2] += (ps->viewheight >> 1); tent = G_TempEntity( 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, ps->origin ); ps->origin[2] += 1; // spit the player out AngleVectors( angles, ps->velocity, NULL, NULL ); VectorScale( ps->velocity, speed, ps->velocity ); ps->pm_time = 160; // hold time ps->pm_flags |= PMF_TIME_KNOCKBACK; // toggle the teleport bit so the client knows to not lerp ps->eFlags ^= EF_TELEPORT_BIT; // set angles SetClientViewAngle( player, angles ); // kill anything at the destination if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) { G_KillBox (player); } // save results of pmove BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue ); // use the precise origin for linking VectorCopy( ps->origin, player->r.currentOrigin ); if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) { trap_LinkEntity (player); } } void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles, tpType_t tpType ) { gentity_t *tent; playerState_t *ps = &player->client->ps; clientSession_t *sess = &player->client->sess; // unlink to make sure it can't possibly interfere with G_KillBox trap_UnlinkEntity (player); VectorCopy ( origin, ps->origin ); // use temp events at source and destination to prevent the effect // from getting dropped by a second player event if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/ ) { if ( tpType == TP_BORG ) { // ...we are borg...prepare to be... tent = G_TempEntity( origin, EV_BORG_TELEPORT ); tent->s.clientNum = player->s.clientNum; } //RPG-X: J2J Added to get fed trans effect without any traveling after beam in //TiM: Since the SP teleporter has been coded to only work with Jay's modification, //we'll add the second half of the client-side effects (ie materialization) here else if( tpType == TP_TRI_TP ) { //tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN ); //tent->s.clientNum = player->s.clientNum; // probably isn't necessary, but just in case, end the beam out powerup ps->powerups[PW_BEAM_OUT] = 0; //and add the beam in one ps->powerups[PW_QUAD] = level.time + 4000; tent = G_TempEntity( ps->origin, EV_PLAYER_TRANSPORT_IN ); tent->s.clientNum = player->s.clientNum; //Cheep hack, but toggle bit don't seem to work lol. //Nah we can predict this client side //trap_SendServerCommand( player-g_entities, "cg_flushAngles" ); } ////// else { //TiM: NO! THIS EFFECT IS TEH SUXXOR!!! /*tent = G_TempEntity( 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;*/ } } // spit the player out //TiM - If in a turbolift and moving, get their velocity, perform the rotation //calc on it, and then reset it to their velocity. if ( /*tpType != TP_BORG && tpType != TP_TRI_TP*/ tpType == TP_TURBO ) { vec3_t dir; vec3_t velAngles; float length; VectorCopy( ps->velocity, dir ); length = VectorLength( dir ); VectorNormalize( dir ); vectoangles( dir, velAngles ); velAngles[YAW] = AngleNormalize360( velAngles[YAW]+ (angles[YAW] - ps->viewangles[YAW] ) ); AngleVectors( velAngles, dir, NULL, NULL ); VectorScale( dir, length, ps->velocity); //TiM: No more spitting. That's for Q3 style teleporters. Not what we want for a slow-paced realism based RP. /*AngleVectors( angles, ps->velocity, NULL, NULL ); VectorScale( ps->velocity, 400, ps->velocity ); ps->pm_time = 160; // hold time ps->pm_flags |= PMF_TIME_KNOCKBACK;*/ } else { //TiM: Set the velocity to 0. So if they were moving b4 the transport, they'll be stopped when they come out. //It's a little something called the Heisenberg compensators. ;) //bug-fix. if the velocity is killed in a spectator door teleporter, the player //gets wedged in the door. >_< if ( sess->sessionTeam != TEAM_SPECTATOR ) VectorScale( ps->velocity, 0, ps->velocity ); else { AngleVectors( angles, ps->velocity, NULL, NULL ); VectorScale( ps->velocity, 400, ps->velocity ); ps->pm_time = 160; // hold time ps->pm_flags |= PMF_TIME_KNOCKBACK; } {//see if we can move the player up one vec3_t newOrg; trace_t tr; VectorCopy ( origin, newOrg ); newOrg[2] += 1; trap_Trace( &tr, ps->origin, player->r.mins, player->r.maxs, newOrg, ps->clientNum, player->clipmask ); if ( !tr.allsolid && !tr.startsolid && tr.fraction == 1.0 ) { ps->origin[2] += 1; } } } // toggle the teleport bit so the client knows to not lerp ps->eFlags ^= EF_TELEPORT_BIT; //Copy Yaw dif over to client for client update //player->s.angles2[0] = angles[YAW] - ps->viewangles[YAW]; // set angles SetClientViewAngle( player, angles ); // kill anything at the destination if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) { if ( G_MoveBox (player) ) player->r.contents = CONTENTS_NONE; } // save results of pmove BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue ); // use the precise origin for linking VectorCopy( ps->origin, player->r.currentOrigin ); if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) { 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) CAST_SHADOWS CLIP_MODEL FORCE_META "model" arbitrary .md3 file to display spawnflags only work when compiled with q3map2 */ void SP_misc_model( gentity_t *ent ) { #if 0 ent->s.modelindex = G_ModelIndex( ent->model ); VectorSet (ent->mins, -16, -16, -16); VectorSet (ent->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 setCamera ( gentity_t *ent, int ownernum ) { vec3_t dir; gentity_t *target = NULL; gentity_t *owner = NULL; ent->r.ownerNum = ownernum; owner = &g_entities[ownernum]; //frame holds the rotate speed //if ( owner->spawnflags & 1 ) { // ent->s.frame = 25; //} else if ( owner->spawnflags & 2 ) { // ent->s.frame = 75; //} ent->s.frame = 0; //TiM: 0 // 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 ); } void cycleCamera( gentity_t *self ) { gentity_t *orgOwner = NULL; gentity_t *owner = NULL; if ( self->r.ownerNum >= 0 && self->r.ownerNum < ENTITYNUM_WORLD ) { orgOwner = &g_entities[self->r.ownerNum]; } owner = G_Find( orgOwner, FOFS(targetname), self->target ); if ( owner == NULL ) { //Uh oh! Not targeted at any ents! Or reached end of list? Which is it? //for now assume reached end of list and are cycling owner = G_Find( owner, FOFS(targetname), self->target ); if ( owner == NULL ) {//still didn't find one G_Printf( "Couldn't find target for misc_portal_surface\n" ); G_FreeEntity( self ); return; } } setCamera( self, owner->s.number ); if ( self->think == cycleCamera ) { if ( owner->wait > 0 ) { self->nextthink = level.time + owner->wait; } else { self->nextthink = level.time + self->wait; } } } void misc_portal_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { cycleCamera( self ); } void locateCamera( gentity_t *ent ) { gentity_t *owner = NULL; owner = G_Find( NULL, FOFS(targetname), ent->target ); if ( owner == NULL ) { G_Printf( "Couldn't find target for misc_partal_surface\n" ); G_FreeEntity( ent ); return; } setCamera( ent, owner->s.number ); if ( G_Find( owner, FOFS(targetname), ent->target) != NULL ) {//targeted at more than one thing ent->think = cycleCamera; if ( owner->wait > 0 ) { ent->nextthink = level.time + owner->wait; } else { ent->nextthink = level.time + ent->wait; } } } /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8) The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted. This must be within 64 world units of the surface! targetname - When used, cycles to the next misc_portal_camera it's targeted wait - makes it auto-cycle between all cameras it's pointed at at intevervals of specified number of seconds. cameras will be cycled through in the order they were created on the map. */ void SP_misc_portal_surface(gentity_t *ent) { VectorClear( ent->r.mins ); VectorClear( ent->r.maxs ); trap_LinkEntity (ent); ent->r.svFlags = SVF_PORTAL; ent->s.eType = ET_PORTAL; ent->wait *= 1000; if ( !ent->target ) { VectorCopy( ent->s.origin, ent->s.origin2 ); } else { ent->think = locateCamera; ent->nextthink = level.time + 100; if ( ent->targetname ) { ent->use = misc_portal_use; } } } /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate The target for a misc_portal_surface. You can set either angles or target another entity (NOT an info_null) to determine the direction of view. "roll" an angle modifier to orient the camera around the target vector; */ void SP_misc_portal_camera(gentity_t *ent) { float roll; VectorClear( ent->r.mins ); VectorClear( ent->r.maxs ); trap_LinkEntity( ent ); G_SpawnFloat( "roll", "0", &roll ); ent->s.clientNum = roll/360.0 * 256; ent->wait *= 1000; } /* ====================================================================== 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_GRENADE_LAUNCHER: fire_grenade( ent, ent->s.origin, dir ); break; case WP_DISRUPTOR: fire_rocket( ent, ent->s.origin, dir ); break; case WP_COFFEE: fire_plasma( ent, ent->s.origin, dir ); break; case WP_QUANTUM_BURST: fire_quantum( ent, ent->s.origin, dir ); break; case WP_COMPRESSION_RIFLE: fire_comprifle( ent, ent->s.origin, dir ); break; } //RPG-X: RedTechie - Sorry causes run time errors in game... //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_rocket (1 0 0) (-16 -16 -16) (16 16 16) Fires at either the target or the current direction. "random" the number of degrees of deviance from the taget. (1.0 default) */ void SP_shooter_rocket( gentity_t *ent ) { InitShooter( ent, WP_DISRUPTOR ); } /*QUAKED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16) Fires at either the target or the current direction. "random" is the number of degrees of deviance from the taget. (1.0 default) */ void SP_shooter_plasma( gentity_t *ent ) { InitShooter( ent, WP_COMPRESSION_RIFLE ); //TiM : WP_COFFEE } /*QUAKED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16) Fires at either the target or the current direction. "random" is the number of degrees of deviance from the taget. (1.0 default) */ void SP_shooter_grenade( gentity_t *ent ) { InitShooter( ent, WP_GRENADE_LAUNCHER); } /*QUAKED shooter_torpedo (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_torpedo( gentity_t *ent ) { InitShooter( ent, WP_QUANTUM_BURST ); }