/* ============= SpawnCorpse A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ void SpawnCorpse( gentity_t *ent ) { gentity_t *body; int contents; vec3_t origin, dest; trace_t tr; float vDiff; VectorCopy( ent->r.currentOrigin, origin ); trap_UnlinkEntity( ent ); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( origin, -1 ); if( contents & CONTENTS_NODROP ) return; body = G_Spawn( ); VectorCopy( ent->s.apos.trBase, body->s.angles ); body->s.eFlags = EF_DEAD; body->s.eType = ET_CORPSE; body->s.number = body - g_entities; body->timestamp = level.time; body->s.event = 0; body->r.contents = CONTENTS_CORPSE; body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ]; body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL; if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) body->classname = "humanCorpse"; else body->classname = "alienCorpse"; body->s.misc = MAX_CLIENTS; body->think = BodySink; body->nextthink = level.time + 20000; body->s.legsAnim = ent->s.legsAnim; if( !body->nonSegModel ) { switch( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } } else { switch( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case NSPA_DEATH1: case NSPA_DEAD1: body->s.legsAnim = NSPA_DEAD1; break; case NSPA_DEATH2: case NSPA_DEAD2: body->s.legsAnim = NSPA_DEAD2; break; case NSPA_DEATH3: case NSPA_DEAD3: default: body->s.legsAnim = NSPA_DEAD3; break; } } body->takedamage = qfalse; body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ]; ent->health = 0; //change body dimensions BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs ); vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ]; //drop down to match the *model* origins of ent and body VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff ); trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask ); VectorCopy( tr.endpos, origin ); G_SetOrigin( body, origin ); VectorCopy( origin, body->s.origin ); body->s.pos.trType = TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); trap_LinkEntity( body ); }
/* ============================== G_UseTargets the global "activator" should be set to the entity that initiated the firing. If self.delay is set, a DelayedUse entity will be created that will actually do the SUB_UseTargets after that many seconds have passed. Centerprints any self.message to the activator. Search for (string)targetname in all entities that match (string)self.target and call their .use function ============================== */ void G_UseTargets (edict_t *ent, edict_t *activator) { edict_t *t; // // check for a delay // if (ent->delay) { // create a temp object to fire at a later time t = G_Spawn(); t->classname = "DelayedUse"; t->nextthink = level.time + ent->delay; t->think = Think_Delay; t->activator = activator; if (!activator) gi.dprintf ("Think_Delay with no activator\n"); t->message = ent->message; t->target = ent->target; t->killtarget = ent->killtarget; return; } // // print the message // if ((ent->message) && !(activator->svflags & SVF_MONSTER)) { gi.centerprintf (activator, "%s", ent->message); if (ent->noise_index) gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0); else gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); } // // kill killtargets // if (ent->killtarget) { t = NULL; while ((t = G_Find (t, FOFS(targetname), ent->killtarget))) { G_FreeEdict (t); if (!ent->inuse) { gi.dprintf("entity was removed while using killtargets\n"); return; } } } // gi.dprintf("TARGET: activating %s\n", ent->target); // // fire targets // if (ent->target) { t = NULL; while ((t = G_Find (t, FOFS(targetname), ent->target))) { // doors fire area portals in a specific way if (!Q_stricmp(t->classname, "func_areaportal") && (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating"))) continue; if (t == ent) { gi.dprintf ("WARNING: Entity used itself.\n"); } else { if (t->use) t->use (t, ent, activator); } if (!ent->inuse) { gi.dprintf("entity was removed while using targets\n"); return; } } } }
//---------------------------------------------------------- void eweb_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { vec3_t org; // turn off any firing animations it may have been doing self->s.frame = self->startFrame = self->endFrame = 0; self->svFlags &= ~(SVF_ANIMATING|SVF_PLAYER_USABLE); self->health = 0; // self->s.weapon = WP_EMPLACED_GUN; // we need to be able to switch back to the old weapon self->takedamage = qfalse; self->lastEnemy = attacker; if ( self->activator && self->activator->client ) { if ( self->activator->NPC ) { vec3_t right; // radius damage seems to throw them, but add an extra bit to throw them away from the weapon AngleVectors( self->currentAngles, NULL, right, NULL ); VectorMA( self->activator->client->ps.velocity, 140, right, self->activator->client->ps.velocity ); self->activator->client->ps.velocity[2] = -100; // kill them self->activator->health = 0; self->activator->client->ps.stats[STAT_HEALTH] = 0; } // kill the players emplaced ammo, cheesy way to keep the gun from firing self->activator->client->ps.ammo[weaponData[WP_EMPLACED_GUN].ammoIndex] = 0; } self->e_PainFunc = painF_NULL; if ( self->target ) { G_UseTargets( self, attacker ); } G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); VectorCopy( self->currentOrigin, org ); org[2] += 20; G_PlayEffect( "emplaced/explode", org ); // Turn the top of the eweb off. #define TURN_OFF 0x00000100//G2SURFACEFLAG_NODESCENDANTS gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "eweb_damage", TURN_OFF ); // create some persistent smoke by using a dynamically created fx runner gentity_t *ent = G_Spawn(); if ( ent ) { ent->delay = 200; ent->random = 100; ent->fxID = G_EffectIndex( "emplaced/dead_smoke" ); ent->e_ThinkFunc = thinkF_fx_runner_think; ent->nextthink = level.time + 50; // move up above the gun origin VectorCopy( self->currentOrigin, org ); org[2] += 35; G_SetOrigin( ent, org ); VectorCopy( org, ent->s.origin ); VectorSet( ent->s.angles, -90, 0, 0 ); // up G_SetAngles( ent, ent->s.angles ); gi.linkentity( ent ); } G_ActivateBehavior( self, BSET_DEATH ); }
void G_FindCraneParts() { vec3_t dist; edict_t *cable; edict_t *control; edict_t *beam; edict_t *hoist; edict_t *hook; edict_t *light; edict_t *p1, *p2; edict_t *e; int direction; int i; for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) { if (!e->inuse) continue; if (!e->classname) continue; if (Q_strcasecmp(e->classname,"crane_control")) continue; control = e; beam = G_Find(NULL,FOFS(targetname),control->target); if(!beam) { gi.dprintf("Crane_control with no target\n"); G_FreeEdict(control); return; } // get path_corner locations to determine movement direction p1 = G_Find(NULL,FOFS(targetname),beam->pathtarget); if(!p1->target) { gi.dprintf("Only 1 path_corner in pathtarget sequence for crane_beam\n" "(2 are required)\n"); G_FreeEdict(control); G_FreeEdict(beam); G_FreeEdict(p1); return; } p2 = G_Find(NULL,FOFS(targetname),p1->target); VectorSubtract(p1->s.origin,p2->s.origin,dist); if(fabs(dist[0]) > fabs(dist[1])) { VectorSet(beam->movedir, 1.0,0.0,0.0); direction = 0; if(p1->s.origin[0] < p2->s.origin[0]) { VectorCopy(p1->s.origin,beam->pos1); VectorCopy(p2->s.origin,beam->pos2); } else { VectorCopy(p2->s.origin,beam->pos1); VectorCopy(p1->s.origin,beam->pos2); } } else { VectorSet(beam->movedir, 0.0,1.0,0.0); direction = 1; if(p1->s.origin[1] < p2->s.origin[1]) { VectorCopy(p1->s.origin,beam->pos1); VectorCopy(p2->s.origin,beam->pos2); } else { VectorCopy(p2->s.origin,beam->pos1); VectorCopy(p1->s.origin,beam->pos2); } } hoist = G_Find(NULL,FOFS(targetname),beam->target); if(!hoist) { gi.dprintf("Crane_beam with no target\n"); G_FreeEdict(control); G_FreeEdict(beam); return; } // get path_corner locations to determine movement direction p1 = G_Find(NULL,FOFS(targetname),hoist->pathtarget); if(!p1->target) { gi.dprintf("Only 1 path_corner in pathtarget sequence for crane_hoist\n" "(2 are required)\n"); G_FreeEdict(control); G_FreeEdict(beam); G_FreeEdict(hoist); G_FreeEdict(p1); return; } p2 = G_Find(NULL,FOFS(targetname),p1->target); VectorSubtract(p1->s.origin,p2->s.origin,dist); if(fabs(dist[0]) > fabs(dist[1])) { VectorSet(hoist->movedir, 1.0,0.0,0.0); if(p1->s.origin[0] < p2->s.origin[0]) { VectorCopy(p1->s.origin,hoist->pos1); VectorCopy(p2->s.origin,hoist->pos2); } else { VectorCopy(p2->s.origin,hoist->pos1); VectorCopy(p1->s.origin,hoist->pos2); } } else { VectorSet(hoist->movedir, 0.0,1.0,0.0); if(p1->s.origin[1] < p2->s.origin[1]) { VectorCopy(p1->s.origin,hoist->pos1); VectorCopy(p2->s.origin,hoist->pos2); } else { VectorCopy(p2->s.origin,hoist->pos1); VectorCopy(p1->s.origin,hoist->pos2); } } // correct spawnflags for beam and hoist speakers if(beam->speaker) { if(direction) beam->speaker->spawnflags = 2; else beam->speaker->spawnflags = 1; } if(hoist->speaker) { if(direction) hoist->speaker->spawnflags = 1; else hoist->speaker->spawnflags = 2; } hook = G_Find(NULL,FOFS(targetname),hoist->target); if(!hook) { gi.dprintf("Crane hoist with no target\n"); G_FreeEdict(control); G_FreeEdict(beam); G_FreeEdict(hoist); return; } // Turn on hook ambient sound if control is on // We use a trick here... since hook by definition cannot // be moving unless control is on, then with control off // we set hook speaker spawnflag to require hook to be // moving to play if(hook->speaker) hook->speaker->spawnflags = 1 - (control->spawnflags & 1); // Get offset from hook origin to hoist origin, so we can // correct timing problems VectorSubtract(hook->s.origin,hoist->s.origin,hook->offset); // get path_corner locations to determine movement direction p1 = G_Find(NULL,FOFS(targetname),hook->pathtarget); if(!p1->target) { gi.dprintf("Only 1 path_corner in pathtarget sequence for crane_hook\n" "(2 are required)\n"); G_FreeEdict(control); G_FreeEdict(beam); G_FreeEdict(hoist); G_FreeEdict(hook); G_FreeEdict(p1); return; } p2 = G_Find(NULL,FOFS(targetname),p1->target); VectorSet(hook->movedir,0.0,0.0,1.0); if(p1->s.origin[2] < p2->s.origin[2]) { VectorCopy(p1->s.origin,hook->pos1); VectorCopy(p2->s.origin,hook->pos2); } else { VectorCopy(p2->s.origin,hook->pos1); VectorCopy(p1->s.origin,hook->pos2); } control->crane_control = control; control->crane_beam = beam; control->crane_hoist = hoist; control->crane_hook = hook; if(!beam->crane_control) beam->crane_control = control; if(control->team) { beam->crane_control = control; if(beam->flags & FL_TEAMSLAVE) beam->crane_onboard_control = beam->teammaster; else beam->crane_onboard_control = beam->teamchain; } else beam->crane_onboard_control = NULL; beam->crane_hoist = hoist; beam->crane_hook = hook; hoist->crane_control = beam->crane_control; hoist->crane_beam = beam; hoist->crane_hook = hook; hook->crane_control = beam->crane_control; hook->crane_beam = beam; hook->crane_hoist = hoist; if(control->spawnflags & 4) { beam->dmg = 0; hoist->dmg = 0; hook->dmg = 0; } if(hook->crane_cable == NULL) { int frame; float length; cable = G_Spawn(); cable->classname = "crane_cable"; VectorAdd(hook->absmin,hook->absmax,cable->s.origin); VectorScale(cable->s.origin,0.5,cable->s.origin); VectorAdd(cable->s.origin,hook->move_origin,cable->s.origin); VectorSubtract(cable->s.origin,hook->s.origin,cable->offset); cable->s.origin[2] = hoist->absmax[2] - 2; cable->model = "models/cable/tris.md2"; gi.setmodel(cable,cable->model); cable->s.skinnum = 0; length = hoist->absmax[2]-1 - hook->absmax[2]; frame = (int)(length/CABLE_SEGMENT); if((frame+1)*CABLE_SEGMENT < length) frame++; frame = max(0,min(frame,19)); cable->s.frame = frame; cable->solid = SOLID_NOT; cable->movetype = MOVETYPE_STOP; VectorSet(cable->mins,-2,-2,length); VectorSet(cable->maxs, 2, 2,0); gi.linkentity(cable); beam->crane_cable = cable; hoist->crane_cable = cable; hook->crane_cable = cable; cable->crane_control = control; cable->crane_beam = beam; cable->crane_hoist = hoist; cable->crane_hook = hook; } control->crane_cable = hook->crane_cable; if((hook->spawnflags & 1) && (hook->crane_light == NULL)) { light = G_Spawn(); light->s.origin[0] = (hook->absmin[0] + hook->absmax[0])/2; light->s.origin[1] = (hook->absmin[1] + hook->absmax[1])/2; light->s.origin[2] = hook->absmin[2] + 8; VectorSet(light->mins,-32,-32,-512); VectorSet(light->maxs, 32, 32, 0); light->solid = SOLID_NOT; light->movetype = MOVETYPE_NOCLIP; light->s.effects = EF_SPHERETRANS; light->s.modelindex = gi.modelindex("sprites/point.sp2"); light->s.effects = EF_HYPERBLASTER; light->svflags = SVF_NOCLIENT; VectorSubtract(light->s.origin,hook->s.origin,light->offset); gi.linkentity(light); beam->crane_light = light; hook->crane_light = light; cable->crane_light = light; } control->crane_light = hook->crane_light; // If control is NOT onboard, move beam speaker (if any) to end of // beam closest to control if(!beam->crane_onboard_control && beam->speaker) { if(beam->movedir[0] > 0) { if(control->absmin[1]+control->absmax[1] < beam->absmin[1]+beam->absmax[1]) beam->speaker->s.origin[1] = beam->absmin[1]; else beam->speaker->s.origin[1] = beam->absmax[1]; } else { if(control->absmin[0]+control->absmax[0] < beam->absmin[0]+beam->absmax[0]) beam->speaker->s.origin[0] = beam->absmin[0]; else beam->speaker->s.origin[0] = beam->absmax[0]; } beam->speaker->s.origin[2] = control->s.origin[2] + 32; VectorSubtract(beam->speaker->s.origin,beam->s.origin,beam->speaker->offset); } } }
/** * @brief Creates a server's entity / program execution context * by parsing textual entity definitions out of an ent file. * @sa CM_EntityString * @sa SV_SpawnServer */ void G_SpawnEntities (const char *mapname, qboolean day, const char *entities) { int entnum; G_FreeTags(TAG_LEVEL); OBJZERO(level); level.pathingMap = (pathing_t *)G_TagMalloc(sizeof(*level.pathingMap), TAG_LEVEL); G_EdictsReset(); Q_strncpyz(level.mapname, mapname, sizeof(level.mapname)); level.day = day; G_ResetClientData(); level.activeTeam = TEAM_NO_ACTIVE; level.actualRound = 1; level.hurtAliens = sv_hurtaliens->integer; ai_waypointList = NULL; /* parse ents */ entnum = 0; while (1) { edict_t *ent; /* parse the opening brace */ const char *token = Com_Parse(&entities); if (!entities) break; if (token[0] != '{') gi.Error("ED_LoadFromFile: found %s when expecting {", token); ent = G_Spawn(); entities = ED_ParseEdict(entities, ent); ent->mapNum = entnum++; /* Set the position of the entity */ VecToPos(ent->origin, ent->pos); /* Call this entity's specific initializer (sets ent->type) */ ED_CallSpawn(ent); /* if this entity is an bbox (e.g. actor), then center its origin based on its position */ if (ent->solid == SOLID_BBOX) G_EdictCalcOrigin(ent); } /* spawn ai players, if needed */ if (level.num_spawnpoints[TEAM_CIVILIAN]) { if (AI_CreatePlayer(TEAM_CIVILIAN) == NULL) gi.DPrintf("Could not create civilian\n"); } if ((sv_maxclients->integer == 1 || ai_numactors->integer) && level.num_spawnpoints[TEAM_ALIEN]) { if (AI_CreatePlayer(TEAM_ALIEN) == NULL) gi.DPrintf("Could not create alien\n"); } Com_Printf("Used inventory slots after ai spawn: %i\n", game.i.GetUsedSlots(&game.i)); G_FindEdictGroups(); }
/* ============== SpawnEntities Creates a server's entity / program execution context by parsing textual entity definitions out of an ent file. ============== */ void SpawnEntities (char *mapname, char *entities, char *spawnpoint) { edict_t *ent; int inhibit; char *com_token; int i; float skill_level; skill_level = floor (skill->value); if (skill_level < 0) skill_level = 0; if (skill_level > 3) skill_level = 3; if (skill->value != skill_level) gi.cvar_forceset("skill", va("%f", skill_level)); SaveClientData (); gi.FreeTags (TAG_LEVEL); memset (&level, 0, sizeof(level)); memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); strncpy (level.mapname, mapname, sizeof(level.mapname)-1); strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); // set client fields on player ents for (i=0 ; i<game.maxclients ; i++) g_edicts[i+1].client = game.clients + i; ent = NULL; inhibit = 0; // parse ents while (1) { // parse the opening brace com_token = COM_Parse (&entities); if (!entities) break; if (com_token[0] != '{') gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); if (!ent) ent = g_edicts; else ent = G_Spawn (); entities = ED_ParseEdict (entities, ent); // yet another map hack if (!Q_strcasecmp(level.mapname, "command") && !Q_strcasecmp(ent->classname, "trigger_once") && !Q_strcasecmp(ent->model, "*27")) ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; // remove things (except the world) from different skill levels or deathmatch if (ent != g_edicts) { if (deathmatch->value) { if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) { G_FreeEdict (ent); inhibit++; continue; } } else { if ( /* ((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */ ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent); inhibit++; continue; } } ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); } ED_CallSpawn (ent); } gi.dprintf ("%i entities inhibited\n", inhibit); #ifdef DEBUG i = 1; ent = EDICT_NUM(i); while (i < globals.num_edicts) { if (ent->inuse != 0 || ent->inuse != 1) Com_DPrintf("Invalid entity %d\n", i); i++, ent++; } #endif G_FindTeams (); PlayerTrail_Init (); }
void SP_crane_beam (edict_t *self) { vec3_t origin; edict_t *speaker; gi.setmodel (self, self->model); VectorAdd(self->absmin,self->absmax,origin); VectorScale(origin,0.5,origin); if (!self->targetname) { gi.dprintf ("crane_beam with no targetname at %s\n", vtos(origin)); G_FreeEdict (self); return; } if (!self->target) { gi.dprintf ("crane_beam with no target at %s\n", vtos(origin)); G_FreeEdict (self); return; } if (!self->pathtarget) { gi.dprintf ("crane_beam with no pathtarget at %s\n", vtos(origin)); G_FreeEdict (self); return; } self->classname = "crane_beam"; self->solid = SOLID_BSP; self->movetype = MOVETYPE_PUSH; if(!self->speed) self->speed = 160; self->moveinfo.speed = self->speed; if(st.noise) self->noise_index = gi.soundindex(st.noise); else self->noise_index = 0; #ifdef LOOP_SOUND_ATTENUATION if (self->attenuation <= 0) self->attenuation = ATTN_IDLE; #endif gi.linkentity (self); if(self->noise_index && !VectorLength(self->s.origin) ) { speaker = G_Spawn(); speaker->classname = "moving_speaker"; speaker->s.sound = 0; speaker->volume = 1; speaker->attenuation = self->attenuation; // was 1 speaker->think = Moving_Speaker_Think; speaker->nextthink = level.time + 2*FRAMETIME; speaker->spawnflags = 7; // owner must be moving to play speaker->owner = self; // this will be changed later when we know // controls are spawned self->speaker = speaker; VectorAdd(self->absmin,self->absmax,speaker->s.origin); VectorScale(speaker->s.origin,0.5,speaker->s.origin); VectorSubtract(speaker->s.origin,self->s.origin,speaker->offset); } }
// Add a player entity to another player's multiview list void G_smvAddView(gentity_t *ent, int pID) { int i; mview_t *mv = NULL; gentity_t *v; if (pID >= MAX_MVCLIENTS || G_smvLocateEntityInMVList(ent, pID, qfalse)) { return; } for (i = 0; i < MULTIVIEW_MAXVIEWS; i++) { if (!ent->client->pers.mv[i].fActive) { mv = &ent->client->pers.mv[i]; break; } } if (mv == NULL) { CP(va("print \"[lof]** [lon]Sorry, no more MV slots available (all[lof] %d [lon]in use)[lof]\n\"", MULTIVIEW_MAXVIEWS)); return; } mv->camera = G_Spawn(); if (mv->camera == NULL) { return; } if (ent->client->sess.sessionTeam == TEAM_SPECTATOR && /*ent->client->sess.sessionTeam != TEAM_SPECTATOR ||*/ ent->client->sess.spectatorState == SPECTATOR_FOLLOW) { SetTeam(ent, "s", qtrue, -1, -1, qfalse); } else if (ent->client->sess.sessionTeam != TEAM_SPECTATOR && !(ent->client->ps.pm_flags & PMF_LIMBO)) { limbo(ent, qtrue); } ent->client->ps.clientNum = ent - g_entities; ent->client->sess.spectatorState = SPECTATOR_FREE; ent->client->pers.mvCount++; mv->fActive = qtrue; mv->entID = pID; v = mv->camera; v->classname = "misc_portal_surface"; v->r.svFlags = SVF_PORTAL | SVF_SINGLECLIENT; // Only merge snapshots for the target client v->r.singleClient = ent->s.number; v->s.eType = ET_PORTAL; VectorClear(v->r.mins); VectorClear(v->r.maxs); trap_LinkEntity(v); v->target_ent = &g_entities[pID]; v->TargetFlag = pID; v->tagParent = ent; G_smvUpdateClientCSList(ent); }
/* ============================== G_UseTargets the global "activator" should be set to the entity that initiated the firing. If self.delay is set, a DelayedUse entity will be created that will actually do the SUB_UseTargets after that many seconds have passed. Centerprints any self.message to the activator. Search for (string)targetname in all entities that match (string)self.target and call their .use function ============================== */ void G_UseTargets (edict_t *ent, edict_t *activator) { edict_t *t; edict_t *master; qboolean done = false; // // check for a delay // if (ent->delay) { // create a temp object to fire at a later time t = G_Spawn(); t->classname = "DelayedUse"; t->nextthink = level.time + ent->delay; t->think = Think_Delay; t->activator = activator; if (!activator) gi.dprintf ("Think_Delay with no activator\n"); t->message = ent->message; t->target = ent->target; t->killtarget = ent->killtarget; return; } // // print the message // if ((ent->message) && !(activator->svflags & SVF_MONSTER)) { gi.centerprintf (activator, "%s", ent->message); if (ent->noise_index) gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0); else gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0); } // // kill killtargets // if (ent->killtarget) { t = NULL; while ((t = G_Find (t, FOFS(targetname), ent->killtarget))) { // PMM - if this entity is part of a train, cleanly remove it if (t->flags & FL_TEAMSLAVE) { // if ((g_showlogic) && (g_showlogic->value)) // gi.dprintf ("Removing %s from train!\n", t->classname); if (t->teammaster) { master = t->teammaster; while (!done) { if (master->teamchain == t) { master->teamchain = t->teamchain; done = true; } master = master->teamchain; if (!master) { // if ((g_showlogic) && (g_showlogic->value)) // gi.dprintf ("Couldn't find myself in master's chain, ignoring!\n"); } } } else { // if ((g_showlogic) && (g_showlogic->value)) // gi.dprintf ("No master to free myself from, ignoring!\n"); } } // PMM G_FreeEdict (t); if (!ent->inuse) { gi.dprintf("entity was removed while using killtargets\n"); return; } } } // // fire targets // if (ent->target) { t = NULL; while ((t = G_Find (t, FOFS(targetname), ent->target))) { // doors fire area portals in a specific way if (!Q_stricmp(t->classname, "func_areaportal") && (!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating"))) continue; if (t == ent) { gi.dprintf ("WARNING: Entity used itself.\n"); } else { if (t->use) t->use (t, ent, activator); } if (!ent->inuse) { gi.dprintf("entity was removed while using targets\n"); return; } } } }
/* * W_Fire_Instagun_Strong */ void W_Fire_Instagun( edict_t *self, vec3_t start, vec3_t dir, float damage, int knockback, int stun, int radius, int range, int mod, int timeDelta ) { vec3_t from, end; trace_t tr; edict_t *ignore, *event, *hit; int hit_movetype; int mask; bool missed = true; int dmgflags = 0; if( GS_Instagib() ) { damage = 9999; } VectorMA( start, range, dir, end ); VectorCopy( start, from ); ignore = self; mask = MASK_SHOT; if( GS_RaceGametype() ) { mask = MASK_SOLID; } tr.ent = -1; while( ignore ) { G_Trace4D( &tr, from, NULL, NULL, end, ignore, mask, timeDelta ); VectorCopy( tr.endpos, from ); ignore = NULL; if( tr.ent == -1 ) { break; } // allow trail to go through SOLID_BBOX entities (players, gibs, etc) hit = &game.edicts[tr.ent]; hit_movetype = hit->movetype; // backup the original movetype as the entity may "die" if( !ISBRUSHMODEL( hit->s.modelindex ) ) { ignore = hit; } if( ( hit != self ) && ( hit->takedamage ) ) { G_Damage( hit, self, self, dir, dir, tr.endpos, damage, knockback, stun, dmgflags, mod ); // spawn a impact event on each damaged ent event = G_SpawnEvent( EV_INSTA_EXPLOSION, DirToByte( tr.plane.normal ), tr.endpos ); event->s.ownerNum = ENTNUM( self ); event->s.firemode = FIRE_MODE_STRONG; if( hit->r.client ) { missed = false; } } // some entity was touched if( hit == world || hit_movetype == MOVETYPE_NONE || hit_movetype == MOVETYPE_PUSH ) { if( g_instajump->integer && self && self->r.client ) { // create a temporary inflictor entity edict_t *inflictor; inflictor = G_Spawn(); inflictor->s.solid = SOLID_NOT; inflictor->timeDelta = 0; VectorCopy( tr.endpos, inflictor->s.origin ); inflictor->s.ownerNum = ENTNUM( self ); inflictor->projectileInfo.maxDamage = 0; inflictor->projectileInfo.minDamage = 0; inflictor->projectileInfo.maxKnockback = knockback; inflictor->projectileInfo.minKnockback = 1; inflictor->projectileInfo.stun = 0; inflictor->projectileInfo.radius = radius; G_RadiusDamage( inflictor, self, &tr.plane, NULL, mod ); G_FreeEdict( inflictor ); } break; } } if( missed && self->r.client ) { G_AwardPlayerMissedElectrobolt( self, mod ); } // send the weapon fire effect event = G_SpawnEvent( EV_INSTATRAIL, ENTNUM( self ), start ); VectorScale( dir, 1024, event->s.origin2 ); }
/* ================== AICast_CheckLoadGame at the start of a level, the game is either saved, or loaded we must wait for all AI to spawn themselves, and a real client to connect ================== */ void AICast_CheckLoadGame( void ) { char loading[4]; gentity_t *ent = NULL; // TTimo: VC6 'may be used without having been init' qboolean ready; cast_state_t *pcs; // have we already done the save or load? if ( !saveGamePending ) { return; } // tell the cgame NOT to render the scene while we are waiting for things to settle trap_Cvar_Set( "cg_norender", "1" ); trap_Cvar_VariableStringBuffer( "savegame_loading", loading, sizeof( loading ) ); // reloading = qtrue; trap_Cvar_Set( "g_reloading", "1" ); if ( strlen( loading ) > 0 && atoi( loading ) != 0 ) { // screen should be black if we are at this stage trap_SetConfigstring( CS_SCREENFADE, va( "1 %i 1", level.time - 10 ) ); // if (!reloading && atoi(loading) == 2) { if ( !( g_reloading.integer ) && atoi( loading ) == 2 ) { // (SA) hmm, this seems redundant when it sets it above... // reloading = qtrue; // this gets reset at the Map_Restart() since the server unloads the game dll trap_Cvar_Set( "g_reloading", "1" ); } ready = qtrue; if ( numSpawningCast != numcast ) { ready = qfalse; } else if ( !( ent = AICast_FindEntityForName( "player" ) ) ) { ready = qfalse; } else if ( !ent->client || ent->client->pers.connected != CON_CONNECTED ) { ready = qfalse; } if ( ready ) { trap_Cvar_Set( "savegame_loading", "0" ); // in-case it aborts saveGamePending = qfalse; G_LoadGame( NULL ); // always load the "current" savegame // RF, spawn a thinker that will enable rendering after the client has had time to process the entities and setup the display //trap_Cvar_Set( "cg_norender", "0" ); ent = G_Spawn(); ent->nextthink = level.time + 200; ent->think = AICast_EnableRenderingThink; // wait for the clients to return from faded screen //trap_SetConfigstring( CS_SCREENFADE, va("0 %i 1500", level.time + 500) ); trap_SetConfigstring( CS_SCREENFADE, va( "0 %i 750", level.time + 500 ) ); level.reloadPauseTime = level.time + 1100; // make sure sound fades up trap_SendServerCommand( -1, va( "snd_fade 1 %d", 2000 ) ); //----(SA) added AICast_CastScriptThink(); } } else { ready = qtrue; if ( numSpawningCast != numcast ) { ready = qfalse; } else if ( !( ent = AICast_FindEntityForName( "player" ) ) ) { ready = qfalse; } else if ( !ent->client || ent->client->pers.connected != CON_CONNECTED ) { ready = qfalse; } // not loading a game, we must be in a new level, so look for some persistant data to read in, then save the game if ( ready ) { G_LoadPersistant(); // make sure we save the game after we have brought across the items trap_Cvar_Set( "g_totalPlayTime", "0" ); // reset play time trap_Cvar_Set( "g_attempts", "0" ); pcs = AICast_GetCastState( ent->s.number ); pcs->totalPlayTime = 0; pcs->lastLoadTime = 0; pcs->attempts = 0; // RF, disabled, since the pregame menu turns this off after the button is pressed, this isn't // required here // RF, spawn a thinker that will enable rendering after the client has had time to process the entities and setup the display //trap_Cvar_Set( "cg_norender", "0" ); //ent = G_Spawn(); //ent->nextthink = level.time + 200; //ent->think = AICast_EnableRenderingThink; saveGamePending = qfalse; // wait for the clients to return from faded screen // trap_SetConfigstring( CS_SCREENFADE, va("0 %i 1500", level.time + 500) ); // trap_SetConfigstring( CS_SCREENFADE, va("0 %i 750", level.time + 500) ); // (SA) send a command that will be interpreted for both the screenfade and any other effects (music cues, pregame menu, etc) // briefing menu will handle transition, just set a cvar for it to check for drawing the 'continue' button trap_SendServerCommand( -1, "rockandroll\n" ); level.reloadPauseTime = level.time + 1100; AICast_CastScriptThink(); } } }
gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) { trace_t tr; vec3_t dest; gentity_t *ent; ent = G_Spawn(); VectorCopy( origin, ent->s.origin ); VectorCopy( origin, ent->s.pos.trBase ); VectorCopy( origin, ent->r.currentOrigin ); VectorSet( ent->r.mins, -15, -15, 0 ); VectorSet( ent->r.maxs, 15, 15, 87 ); ent->s.eType = ET_GENERAL; ent->flags = FL_NO_KNOCKBACK; if( g_gametype.integer == GT_OBELISK ) { ent->r.contents = CONTENTS_SOLID; ent->takedamage = qtrue; ent->health = g_obeliskHealth.integer; ent->die = ObeliskDie; ent->pain = ObeliskPain; ent->think = ObeliskRegen; ent->nextthink = level.time + g_obeliskRegenPeriod.integer * 1000; } if( g_gametype.integer == GT_HARVESTER ) { ent->r.contents = CONTENTS_TRIGGER; ent->touch = ObeliskTouch; } if ( spawnflags & 1 ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { // mappers like to put them exactly on the floor, but being coplanar // will sometimes show up as starting in solid, so lif it up one pixel ent->s.origin[2] += 1; // drop to floor 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 ) { ent->s.origin[2] -= 1; G_Printf( "SpawnObelisk: %s startsolid at %s\n", ent->classname, vtos(ent->s.origin) ); ent->s.groundEntityNum = ENTITYNUM_NONE; G_SetOrigin( ent, ent->s.origin ); } else { // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } } ent->spawnflags = team; trap_LinkEntity( ent ); return ent; }
/* * ClientUserinfoChanged * called whenever the player updates a userinfo variable. * * The game can override any of the settings in place * (forcing skins or names, etc) before copying it off. */ void ClientUserinfoChanged( edict_t *ent, char *userinfo ) { char *s; char oldname[MAX_INFO_VALUE]; gclient_t *cl; int rgbcolor, i; assert( ent && ent->r.client ); assert( userinfo && Info_Validate( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate( userinfo ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Invalid userinfo" ); return; } cl = ent->r.client; // ip s = Info_ValueForKey( userinfo, "ip" ); if( !s ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client IP" ); return; } Q_strncpyz( cl->ip, s, sizeof( cl->ip ) ); // socket s = Info_ValueForKey( userinfo, "socket" ); if( !s ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Server didn't provide client socket" ); return; } Q_strncpyz( cl->socket, s, sizeof( cl->socket ) ); // color s = Info_ValueForKey( userinfo, "color" ); if( s ) rgbcolor = COM_ReadColorRGBString( s ); else rgbcolor = -1; if( rgbcolor != -1 ) { rgbcolor = COM_ValidatePlayerColor( rgbcolor ); Vector4Set( cl->color, COLOR_R( rgbcolor ), COLOR_G( rgbcolor ), COLOR_B( rgbcolor ), 255 ); } else { Vector4Set( cl->color, 255, 255, 255, 255 ); } // set name, it's validated and possibly changed first Q_strncpyz( oldname, cl->netname, sizeof( oldname ) ); G_SetName( ent, Info_ValueForKey( userinfo, "name" ) ); if( oldname[0] && Q_stricmp( oldname, cl->netname ) && !cl->isTV && !CheckFlood( ent, false ) ) G_PrintMsg( NULL, "%s%s is now known as %s%s\n", oldname, S_COLOR_WHITE, cl->netname, S_COLOR_WHITE ); if( !Info_SetValueForKey( userinfo, "name", cl->netname ) ) { trap_DropClient( ent, DROP_TYPE_GENERAL, "Error: Couldn't set userinfo (name)" ); return; } // clan tag G_SetClan( ent, Info_ValueForKey( userinfo, "clan" ) ); // handedness s = Info_ValueForKey( userinfo, "hand" ); if( !s ) cl->hand = 2; else cl->hand = bound( atoi( s ), 0, 2 ); // handicap s = Info_ValueForKey( userinfo, "handicap" ); if( s ) { i = atoi( s ); if( i > 90 || i < 0 ) { G_PrintMsg( ent, "Handicap must be defined in the [0-90] range.\n" ); cl->handicap = 0; } else { cl->handicap = i; } } s = Info_ValueForKey( userinfo, "cg_movementStyle" ); if( s ) { i = bound( atoi( s ), 0, GS_MAXBUNNIES - 1 ); if( trap_GetClientState( PLAYERNUM(ent) ) < CS_SPAWNED ) { if( i != cl->movestyle ) cl->movestyle = cl->movestyle_latched = i; } else if( cl->movestyle_latched != cl->movestyle ) { G_PrintMsg( ent, "A movement style change is already in progress. Please wait.\n" ); } else if( i != cl->movestyle_latched ) { cl->movestyle_latched = i; if( cl->movestyle_latched != cl->movestyle ) { edict_t *switcher; switcher = G_Spawn(); switcher->think = think_MoveTypeSwitcher; switcher->nextThink = level.time + 10000; switcher->s.ownerNum = ENTNUM( ent ); G_PrintMsg( ent, "Movement style will change in 10 seconds.\n" ); } } } // update the movement features depending on the movestyle if( !G_ISGHOSTING( ent ) && g_allow_bunny->integer ) { if( cl->movestyle == GS_CLASSICBUNNY ) cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_FWDBUNNY; else cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_FWDBUNNY; } s = Info_ValueForKey( userinfo, "cg_noAutohop" ); if( s && s[0] ) { if( atoi( s ) != 0 ) cl->ps.pmove.stats[PM_STAT_FEATURES] &= ~PMFEAT_CONTINOUSJUMP; else cl->ps.pmove.stats[PM_STAT_FEATURES] |= PMFEAT_CONTINOUSJUMP; } #ifdef UCMDTIMENUDGE s = Info_ValueForKey( userinfo, "cl_ucmdTimeNudge" ); if( !s ) { cl->ucmdTimeNudge = 0; } else { cl->ucmdTimeNudge = atoi( s ); clamp( cl->ucmdTimeNudge, -MAX_UCMD_TIMENUDGE, MAX_UCMD_TIMENUDGE ); } #endif // mm session // TODO: remove the key after storing it to gclient_t ! s = Info_ValueForKey( userinfo, "cl_mm_session" ); cl->mm_session = ( s == NULL ) ? 0 : atoi( s ); s = Info_ValueForKey( userinfo, "mmflags" ); cl->mmflags = ( s == NULL ) ? 0 : strtoul( s, NULL, 10 ); // tv if( cl->isTV ) { s = Info_ValueForKey( userinfo, "tv_port" ); cl->tv.port = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "tv_port6" ); cl->tv.port6 = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "max_cl" ); cl->tv.maxclients = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "num_cl" ); cl->tv.numclients = s ? atoi( s ) : 0; s = Info_ValueForKey( userinfo, "chan" ); cl->tv.channel = s ? atoi( s ) : 0; } if( !G_ISGHOSTING( ent ) && trap_GetClientState( PLAYERNUM( ent ) ) >= CS_SPAWNED ) G_Client_AssignTeamSkin( ent, userinfo ); // save off the userinfo in case we want to check something later Q_strncpyz( cl->userinfo, userinfo, sizeof( cl->userinfo ) ); G_UpdatePlayerInfoString( PLAYERNUM( ent ) ); G_UpdateMMPlayerInfoString( PLAYERNUM( ent ) ); G_Gametype_ScoreEvent( cl, "userinfochanged", oldname ); }
//---------------------------------------------------------------- static void turretG2_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org, ang; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... if ( ent->random ) { vectoangles( dir, ang ); ang[PITCH] += flrand( -ent->random, ent->random ); ang[YAW] += flrand( -ent->random, ent->random ); AngleVectors( ang, dir, NULL, NULL ); } vectoangles(dir, ang); if ( (ent->spawnflags&SPF_TURRETG2_TURBO) ) { //muzzle flash G_PlayEffectID( ent->genericValue13, org, ang ); WP_FireTurboLaserMissile( ent, start, dir ); if ( ent->alt_fire ) { TurboLaser_SetBoneAnim( ent, 2, 3 ); } else { TurboLaser_SetBoneAnim( ent, 0, 1 ); } } else { G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), org, ang ); bolt = G_Spawn(); bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_BLASTER; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; bolt->dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS); // Don't push them around, or else we are constantly re-aiming bolt->splashDamage = ent->splashDamage; bolt->splashRadius = ent->splashDamage; bolt->methodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->splashMethodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //bolt->trigger_formation = qfalse; // don't draw tail on first frame VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); } }
void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fright, float fup ) { vec3_t fwd, right; gentity_t *it_ent = G_Spawn(); qboolean rotate = qtrue; AngleVectors( angs, fwd, right, NULL ); if ( it_ent && gun ) { // FIXME: scaling the ammo will probably need to be tweaked to a reasonable amount...adjust as needed // Set base ammo per type if ( gun->giType == IT_WEAPON ) { it_ent->spawnflags |= 16;// VERTICAL switch( gun->giTag ) { case WP_BLASTER: it_ent->count = 15; break; case WP_REPEATER: it_ent->count = 100; break; case WP_ROCKET_LAUNCHER: it_ent->count = 4; break; } } else { rotate = qfalse; // must deliberately make it small, or else the objects will spawn inside of each other. VectorSet( it_ent->maxs, 6.75f, 6.75f, 6.75f ); VectorScale( it_ent->maxs, -1, it_ent->mins ); } it_ent->spawnflags |= 1;// ITMSF_SUSPEND it_ent->classname = gun->classname; G_SpawnItem( it_ent, gun ); // FinishSpawningItem handles everything, so clear the thinkFunc that was set in G_SpawnItem FinishSpawningItem( it_ent ); if ( gun->giType == IT_AMMO ) { if ( gun->giTag == AMMO_BLASTER ) // I guess this just has to use different logic?? { if ( g_spskill->integer >= 2 ) { it_ent->count += 10; // give more on higher difficulty because there will be more/harder enemies? } } else { // scale ammo based on skill switch ( g_spskill->integer ) { case 0: // do default break; case 1: it_ent->count *= 0.75f; break; case 2: it_ent->count *= 0.5f; break; } } } it_ent->nextthink = 0; VectorCopy( org, it_ent->s.origin ); VectorMA( it_ent->s.origin, fright, right, it_ent->s.origin ); VectorMA( it_ent->s.origin, ffwd, fwd, it_ent->s.origin ); it_ent->s.origin[2] += fup; VectorCopy( angs, it_ent->s.angles ); // by doing this, we can force the amount of ammo we desire onto the weapon for when it gets picked-up it_ent->flags |= ( FL_DROPPED_ITEM | FL_FORCE_PULLABLE_ONLY ); it_ent->physicsBounce = 0.1f; for ( int t = 0; t < 3; t++ ) { if ( rotate ) { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + crandom() * 14 ); } else { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + crandom() * 4 ); } } else { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + crandom() * 4 ); } } } G_SetAngles( it_ent, it_ent->s.angles ); G_SetOrigin( it_ent, it_ent->s.origin ); gi.linkentity( it_ent ); } }
void SP_model_train (edict_t *self) { SP_model_spawn (self); self->class_id = ENTITY_MODEL_TRAIN; // Reset s.sound, which SP_model_spawn may have turned on self->moveinfo.sound_middle = self->s.sound; self->s.sound = 0; if(!self->inuse) return; // Reset some things from SP_model_spawn self->delay = 0; self->think = NULL; self->nextthink = 0; if (self->health) { self->die = train_die; self->takedamage = DAMAGE_YES; } if(self->framenumbers > self->startframe+1) { edict_t *animator; animator = G_Spawn(); animator->owner = self; animator->think = model_train_animator; animator->nextthink = level.time + FRAMETIME; } self->s.frame = self->startframe; self->movetype = MOVETYPE_PUSH; // Really gross stuff here... translate model_spawn spawnflags // to func_train spawnflags. PLAYER_MODEL and NO_MODEL have // already been checked in SP_model_spawn and are never re-used, // so it's OK to overwrite those. if(self->spawnflags & MTRAIN_ROTATE) { self->spawnflags &= ~MTRAIN_ROTATE; self->spawnflags |= TRAIN_ROTATE; } if(self->spawnflags & MTRAIN_ROTATE_CONSTANT) { self->spawnflags &= ~MTRAIN_ROTATE_CONSTANT; self->spawnflags |= TRAIN_ROTATE_CONSTANT; } if( (self->spawnflags & (TRAIN_ROTATE | TRAIN_ROTATE_CONSTANT)) == (TRAIN_ROTATE | TRAIN_ROTATE_CONSTANT)) { self->spawnflags &= ~(TRAIN_ROTATE | TRAIN_ROTATE_CONSTANT); self->spawnflags |= TRAIN_SPLINE; } if(self->style == 3) self->spawnflags |= TRAIN_ANIMATE; // 32 if(self->style == 4) self->spawnflags |= TRAIN_ANIMATE_FAST; // 64 // TRAIN_SMOOTH forces trains to go directly to Move_Done from // Move_Final rather than slowing down (if necessary) for one // frame. if (self->spawnflags & TRAIN_SMOOTH) self->smooth_movement = true; else self->smooth_movement = false; self->blocked = train_blocked; if (self->spawnflags & TRAIN_BLOCK_STOPS) self->dmg = 0; else { if (!self->dmg) self->dmg = 100; } if (!self->speed) self->speed = 100; self->moveinfo.speed = self->speed; self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed; self->use = train_use; gi.linkentity (self); if (self->target) { // start trains on the second frame, to make sure their targets have had // a chance to spawn self->nextthink = level.time + FRAMETIME; self->think = func_train_find; } else { gi.dprintf ("model_train without a target at %s\n", vtos(self->absmin)); } }
void fire_blaster(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, qboolean hyper) { edict_t *bolt; trace_t tr; if (!self) { return; } VectorNormalize(dir); bolt = G_Spawn(); bolt->svflags = SVF_DEADMONSTER; /* yes, I know it looks weird that projectiles are deadmonsters what this means is that when prediction is used against the object (blaster/hyperblaster shots), the player won't be solid clipped against the object. Right now trying to run into a firing hyperblaster is very jerky since you are predicted 'against' the shots. */ VectorCopy(start, bolt->s.origin); VectorCopy(start, bolt->s.old_origin); vectoangles(dir, bolt->s.angles); VectorScale(dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->clipmask = MASK_SHOT; bolt->solid = SOLID_BBOX; bolt->s.effects |= effect; bolt->s.renderfx |= RF_NOSHADOW; VectorClear(bolt->mins); VectorClear(bolt->maxs); bolt->s.modelindex = gi.modelindex("models/objects/laser/tris.md2"); bolt->s.sound = gi.soundindex("misc/lasfly.wav"); bolt->owner = self; bolt->touch = blaster_touch; bolt->nextthink = level.time + 2; bolt->think = G_FreeEdict; bolt->dmg = damage; bolt->classname = "bolt"; if (hyper) { bolt->spawnflags = 1; } gi.linkentity(bolt); if (self->client) { check_dodge(self, bolt->s.origin, dir, speed); } tr = gi.trace(self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA(bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch(bolt, tr.ent, NULL, NULL); } }
/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; #ifdef MISSIONPACK vec3_t forward, impactpoint, bouncedir; int eFlags; #endif other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } #ifdef MISSIONPACK if ( other->takedamage ) { if ( ent->s.weapon != WP_PROX_LAUNCHER ) { if ( other->client && other->client->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } #endif // impact damage if (other->takedamage) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } } #ifdef MISSIONPACK if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #endif if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->client ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); return; } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } trap_LinkEntity( ent ); }
/* ================ LaunchItem Spawns an item and tosses it forward ================ */ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity, int ownerNum ) { gentity_t *dropped; trace_t tr; vec3_t vec, temp; int i; dropped = G_Spawn(); dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex dropped->s.otherEntityNum2 = 1; // DHM - Nerve :: this is taking modelindex2's place for a dropped item dropped->classname = item->classname; dropped->item = item; VectorSet( dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, 0 ); //----(SA) so items sit on the ground VectorSet( dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, 2*ITEM_RADIUS ); //----(SA) so items sit on the ground dropped->r.contents = CONTENTS_TRIGGER|CONTENTS_ITEM; dropped->clipmask = CONTENTS_SOLID | CONTENTS_MISSILECLIP; // NERVE - SMF - fix for items falling through grates dropped->touch = Touch_Item_Auto; trap_Trace( &tr, origin, dropped->r.mins, dropped->r.maxs, origin, ownerNum, MASK_SOLID ); if ( tr.startsolid ) { VectorSubtract( g_entities[ownerNum].s.origin, origin, temp ); VectorNormalize( temp ); for ( i=16; i<=48; i+=16 ) { VectorScale( temp, i, vec ); VectorAdd( origin, vec, origin ); trap_Trace( &tr, origin, dropped->r.mins, dropped->r.maxs, origin, ownerNum, MASK_SOLID ); if ( !tr.startsolid ) break; } } G_SetOrigin( dropped, origin ); dropped->s.pos.trType = TR_GRAVITY; dropped->s.pos.trTime = level.time; VectorCopy( velocity, dropped->s.pos.trDelta ); // ydnar: set yaw to parent angles temp[ PITCH ] = 0; temp[ YAW ] = g_entities[ ownerNum ].s.apos.trBase[ YAW ]; temp[ ROLL ] = 0; G_SetAngle( dropped, temp ); dropped->s.eFlags |= EF_BOUNCE_HALF; if (item->giType == IT_TEAM) { // Special case for CTF flags gentity_t* flag = &g_entities[ g_entities[ownerNum].client->flagParent ]; dropped->s.otherEntityNum = g_entities[ownerNum].client->flagParent; // store the entitynum of our original flag spawner dropped->s.density = 1; dropped->think = Team_DroppedFlagThink; dropped->nextthink = level.time + 30000; if( level.gameManager ) { G_Script_ScriptEvent( level.gameManager, "trigger", flag->item->giTag == PW_REDFLAG ? "allied_object_dropped" : "axis_object_dropped" ); } G_Script_ScriptEvent( flag, "trigger", "dropped" ); } else { // auto-remove after 30 seconds dropped->think = G_FreeEntity; dropped->nextthink = level.time + 30000; } dropped->flags = FL_DROPPED_ITEM; trap_LinkEntity (dropped); return dropped; }
void ThrowGib(edict_t *self, char *gibname, int damage, int type) { edict_t *gib; vec3_t vd; vec3_t origin; vec3_t size; float vscale; if (level.framenum > lastgibframe) { gibsthisframe = 0; lastgibframe = level.framenum; } gibsthisframe++; if (gibsthisframe > 20) { return; } gib = G_Spawn(); VectorScale(self->size, 0.5, size); VectorAdd(self->absmin, size, origin); gib->s.origin[0] = origin[0] + crandom() * size[0]; gib->s.origin[1] = origin[1] + crandom() * size[1]; gib->s.origin[2] = origin[2] + crandom() * size[2]; gi.setmodel(gib, gibname); gib->solid = SOLID_BBOX; gib->s.effects |= EF_GIB; gib->flags |= FL_NO_KNOCKBACK; gib->takedamage = DAMAGE_YES; gib->die = gib_die; if (type == GIB_ORGANIC) { gib->movetype = MOVETYPE_TOSS; gib->touch = gib_touch; vscale = 0.5; } else { gib->movetype = MOVETYPE_BOUNCE; vscale = 1.0; } VelocityForDamage(damage, vd); VectorMA(self->velocity, vscale, vd, gib->velocity); ClipGibVelocity(gib); gib->avelocity[0] = random() * 600; gib->avelocity[1] = random() * 600; gib->avelocity[2] = random() * 600; gib->think = G_FreeEdict; gib->nextthink = level.time + 10 + random() * 10; gi.linkentity(gib); }
void crane_reset_use (edict_t *self, edict_t *other, edict_t *activator) { float d1, d2; int dir; edict_t *delay; edict_t *crane; edict_t *control, *beam, *cable, *cargo, *hoist, *hook; vec3_t bonk, v1, v2; crane = G_Find (NULL, FOFS(targetname), self->target); if(!crane) { gi.dprintf("Cannot find target of crane_reset at %s\n",vtos(self->s.origin)); return; } control = crane->crane_control; control->activator = activator; if(!(control->spawnflags & 1)) { if(control->message) safe_centerprintf(activator,"%s\n",control->message); else safe_centerprintf(activator,"No power\n"); return; } beam = control->crane_beam; hoist = control->crane_hoist; hook = control->crane_hook; cable = control->crane_cable; cargo = hook->crane_cargo; VectorSubtract(beam->pos1,self->s.origin,v1); VectorSubtract(beam->pos2,self->s.origin,v2); d1 = VectorLength(v1); d2 = VectorLength(v2); if(d2 < d1) control->crane_increment = 1; else control->crane_increment = -1; if(beam->movedir[0] > 0) { // travels in X dir = 0; if(control->crane_increment > 0) { if(Crane_Hook_Bonk(hook,0,1,bonk)) { bonk[0] += beam->absmax[0] - hook->absmax[0]; beam->crane_bonk = min(bonk[0],beam->pos2[0]); } else beam->crane_bonk = beam->pos2[0]; beam->crane_bonk += beam->absmin[0] - beam->absmax[0]; } else { if(Crane_Hook_Bonk(hook,0,-1,bonk)) { bonk[0] += beam->absmin[0] - hook->absmin[0]; beam->crane_bonk = max(bonk[0],beam->pos1[0]); } else beam->crane_bonk = beam->pos1[0]; } } else { // travels in Y dir = 1; if(control->crane_increment > 0) { if(Crane_Hook_Bonk(hook,1,1,bonk)) { bonk[1] += beam->absmax[1] - hook->absmax[1]; beam->crane_bonk = min(bonk[1],beam->pos2[1]); } else beam->crane_bonk = beam->pos2[1]; beam->crane_bonk += beam->absmin[1] - beam->absmax[1]; } else { if(Crane_Hook_Bonk(hook,1,-1,bonk)) { bonk[1] += beam->absmin[1] - hook->absmin[1]; beam->crane_bonk = max(bonk[1],beam->pos1[1]); } else beam->crane_bonk = beam->pos1[1]; } } if(beam->speaker && beam->crane_onboard_control) { beam->speaker->owner = beam->crane_onboard_control; VectorAdd(beam->crane_onboard_control->absmin, beam->crane_onboard_control->absmax, beam->speaker->s.origin); VectorScale(beam->speaker->s.origin,0.5,beam->speaker->s.origin); VectorSubtract(beam->speaker->s.origin, beam->crane_onboard_control->s.origin,beam->speaker->offset); beam->speaker->owner->noise_index = beam->noise_index; } beam->crane_dir = dir; beam->moveinfo.remaining_distance = control->crane_increment * (beam->crane_bonk - beam->absmin[dir]); if(beam->moveinfo.remaining_distance <= 0) return; Crane_AdjustSpeed(beam); VectorSet(beam->moveinfo.dir, beam->movedir[0]*control->crane_increment, beam->movedir[1]*control->crane_increment, 0); beam->crane_control = control; hoist->crane_dir = dir; hoist->crane_bonk = beam->crane_bonk + hoist->absmin[dir] - beam->absmin[dir]; hoist->crane_control = control; memcpy(&hoist->moveinfo,&beam->moveinfo,sizeof(moveinfo_t)); hook->crane_dir = dir; hook->crane_bonk = beam->crane_bonk + hook->absmin[dir] - beam->absmin[dir]; hook->crane_control = control; memcpy(&hook->moveinfo,&beam->moveinfo,sizeof(moveinfo_t)); cable->crane_dir = dir; cable->crane_bonk = beam->crane_bonk + cable->absmin[dir] - beam->absmin[dir]; cable->crane_control = control; memcpy(&cable->moveinfo,&beam->moveinfo,sizeof(moveinfo_t)); if(beam->crane_onboard_control) { beam->crane_onboard_control->crane_dir = dir; beam->crane_onboard_control->crane_bonk = beam->crane_bonk + beam->crane_onboard_control->absmin[dir] - beam->absmin[dir]; beam->crane_onboard_control->crane_control = control; memcpy(&beam->crane_onboard_control->moveinfo,&beam->moveinfo,sizeof(moveinfo_t)); } if(cargo) { cargo->crane_dir = dir; cargo->crane_bonk = beam->crane_bonk + cargo->absmin[dir] - beam->absmin[dir]; cargo->crane_control = control; memcpy(&cargo->moveinfo,&beam->moveinfo,sizeof(moveinfo_t)); } delay = G_Spawn(); delay->owner = control; delay->think = crane_reset_go; delay->nextthink = level.time + FRAMETIME; gi.linkentity(delay); self->count--; if(!self->count) { self->think = G_FreeEdict; self->nextthink = level.time + 1; } }
/* ================ LaunchItem Spawns an item and tosses it forward ================ */ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity ) { gentity_t *dropped; dropped = G_Spawn(); dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item dropped->classname = item->classname; dropped->item = item; VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS); VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); dropped->r.contents = CONTENTS_TRIGGER; dropped->touch = Touch_Item; G_SetOrigin( dropped, origin ); dropped->s.pos.trType = TR_GRAVITY; dropped->s.pos.trTime = level.time; VectorCopy( velocity, dropped->s.pos.trDelta ); dropped->s.eFlags |= EF_BOUNCE_HALF; #ifdef MISSIONPACK if ((g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF) && item->giType == IT_TEAM) { // Special case for CTF flags #else if (g_gametype.integer == GT_CTF && item->giType == IT_TEAM) { // Special case for CTF flags #endif dropped->think = Team_DroppedFlagThink; dropped->nextthink = level.time + 30000; Team_CheckDroppedItem( dropped ); } else { // auto-remove after 30 seconds dropped->think = G_FreeEntity; dropped->nextthink = level.time + 30000; } dropped->flags = FL_DROPPED_ITEM; trap_LinkEntity (dropped); return dropped; } /* ================ Drop_Item Spawns an item and tosses it forward ================ */ gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle ) { vec3_t velocity; vec3_t angles; VectorCopy( ent->s.apos.trBase, angles ); angles[YAW] += angle; angles[PITCH] = 0; // always forward AngleVectors( angles, velocity, NULL, NULL ); VectorScale( velocity, 150, velocity ); velocity[2] += 200 + crandom() * 50; return LaunchItem( item, ent->s.pos.trBase, velocity ); }
void SP_trigger_objective_info( gentity_t *ent ) { char *scorestring; if ( !ent->track ) { G_Error( "'trigger_objective_info' does not have a 'track' \n" ); } if ( level.numOidTriggers >= MAX_OID_TRIGGERS ) { G_Error( "Exceeded maximum number of 'trigger_objective_info' entities\n" ); } // JPW NERVE -- if this trigger has a "score" field set, then blowing up an objective // inside of this field will add "score" to the right player team. storing this // in ent->accuracy since that's unused. G_SpawnString( "score", "0", &scorestring ); ent->accuracy = atof( scorestring ); // jpw // Arnout: HACK HACK - someone at nerve forgot to add the score field to sub - have to // hardcode it cause we don't want people to download the map again { char mapName[MAX_QPATH]; trap_Cvar_VariableStringBuffer( "mapname", mapName, sizeof( mapName ) ); if ( !Q_stricmp( mapName, "mp_sub" ) && !Q_stricmp( ent->track, "the Axis Submarine" ) ) { ent->accuracy = 15; } } trap_SetConfigstring( CS_OID_TRIGGERS + level.numOidTriggers, ent->track ); ent->s.teamNum = level.numOidTriggers; level.numOidTriggers++; InitTrigger( ent ); // unlike other triggers, we need to send this one to the client ent->r.svFlags &= ~SVF_NOCLIENT; ent->s.eType = ET_OID_TRIGGER; trap_LinkEntity( ent ); // NERVE - SMF - spawn an explosive indicator if ( ( ent->spawnflags & AXIS_OBJECTIVE ) || ( ent->spawnflags & ALLIED_OBJECTIVE ) ) { gentity_t *e; e = G_Spawn(); e->r.svFlags = SVF_BROADCAST; e->classname = "explosive_indicator"; e->s.eType = ET_EXPLOSIVE_INDICATOR; e->s.pos.trType = TR_STATIONARY; if ( ent->spawnflags & AXIS_OBJECTIVE ) { e->s.teamNum = 1; } else if ( ent->spawnflags & ALLIED_OBJECTIVE ) { e->s.teamNum = 2; } e->r.ownerNum = ent->s.number; e->think = explosive_indicator_think; e->nextthink = level.time + FRAMETIME; VectorCopy( ent->r.mins, e->s.pos.trBase ); VectorAdd( ent->r.maxs, e->s.pos.trBase, e->s.pos.trBase ); VectorScale( e->s.pos.trBase, 0.5, e->s.pos.trBase ); SnapVector( e->s.pos.trBase ); trap_LinkEntity( e ); } // -NERVE - SMF }
/* =================== G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from level.spawnVars[], then call the class specfic spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { int i; gentity_t *ent; char *s, *value, *gametypeName; static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"}; // get the next free entity ent = G_Spawn(); for ( i = 0 ; i < level.numSpawnVars ; i++ ) { G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent ); } // check for "notsingle" flag if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_SpawnInt( "notsingle", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } // check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER) if ( g_gametype.integer >= GT_TEAM ) { G_SpawnInt( "notteam", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } else { G_SpawnInt( "notfree", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } #ifdef MISSIONPACK G_SpawnInt( "notta", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } #else G_SpawnInt( "notq3a", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } #endif if( G_SpawnString( "gametype", NULL, &value ) ) { if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[g_gametype.integer]; s = strstr( value, gametypeName ); if( !s ) { G_FreeEntity( ent ); return; } } } // move editor origin to pos VectorCopy( ent->s.origin, ent->r.currentOrigin ); // if we didn't get a classname, don't bother spawning anything if ( !G_CallSpawn( ent ) ) { G_FreeEntity( ent ); } }
/* ================ LaunchItem Spawns an item and tosses it forward ================ */ gentity_t *LaunchItem( gitem_t *item, const vec3_t origin, const vec3_t velocity, char *target ) { gentity_t *dropped; dropped = G_Spawn(); dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item dropped->classname = G_NewString(item->classname); //copy it so it can be freed safely dropped->item = item; // try using the "correct" mins/maxs first VectorSet( dropped->mins, item->mins[0], item->mins[1], item->mins[2] ); VectorSet( dropped->maxs, item->maxs[0], item->maxs[1], item->maxs[2] ); if ((!dropped->mins[0] && !dropped->mins[1] && !dropped->mins[2]) && (!dropped->maxs[0] && !dropped->maxs[1] && !dropped->maxs[2])) { VectorSet( dropped->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); VectorScale( dropped->maxs, -1, dropped->mins ); } dropped->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_TRIGGER;//not CONTENTS_BODY for dropped items, don't need to ID them if ( target && target[0] ) { dropped->target = G_NewString( target ); } else { // if not targeting something, auto-remove after 30 seconds // only if it's NOT a security or goodie key if (dropped->item->giTag != INV_SECURITY_KEY ) { dropped->e_ThinkFunc = thinkF_G_FreeEntity; dropped->nextthink = level.time + 30000; } if ( dropped->item->giType == IT_AMMO && dropped->item->giTag == AMMO_FORCE ) { dropped->nextthink = -1; dropped->e_ThinkFunc = thinkF_NULL; } } dropped->e_TouchFunc = touchF_Touch_Item; if ( item->giType == IT_WEAPON ) { // give weapon items zero pitch, a random yaw, and rolled onto their sides...but would be bad to do this for a bowcaster if ( item->giTag != WP_BOWCASTER && item->giTag != WP_THERMAL && item->giTag != WP_TRIP_MINE && item->giTag != WP_DET_PACK ) { VectorSet( dropped->s.angles, 0, crandom() * 180, 90.0f ); G_SetAngles( dropped, dropped->s.angles ); } } G_SetOrigin( dropped, origin ); dropped->s.pos.trType = TR_GRAVITY; dropped->s.pos.trTime = level.time; VectorCopy( velocity, dropped->s.pos.trDelta ); dropped->s.eFlags |= EF_BOUNCE_HALF; dropped->flags = FL_DROPPED_ITEM; gi.linkentity (dropped); return dropped; }
// links the trigger to it's objective, determining if it's a func_explosive // of func_constructible and spawning the right indicator void Think_SetupObjectiveInfo(gentity_t *ent) { ent->target_ent = G_FindByTargetname(NULL, ent->target); if (!ent->target_ent) { G_Error("'trigger_objective_info' has a missing target '%s'\n", ent->target); } if (ent->target_ent->s.eType == ET_EXPLOSIVE) { // this is for compass usage if ((ent->spawnflags & AXIS_OBJECTIVE) || (ent->spawnflags & ALLIED_OBJECTIVE)) { gentity_t *e = G_Spawn(); e->r.svFlags = SVF_BROADCAST; e->classname = "explosive_indicator"; if (ent->spawnflags & 8) { e->s.eType = ET_TANK_INDICATOR; } else { e->s.eType = ET_EXPLOSIVE_INDICATOR; } e->parent = ent; e->s.pos.trType = TR_STATIONARY; if (ent->spawnflags & AXIS_OBJECTIVE) { e->s.teamNum = 1; } else if (ent->spawnflags & ALLIED_OBJECTIVE) { e->s.teamNum = 2; } G_SetOrigin(e, ent->r.currentOrigin); e->s.modelindex2 = ent->s.teamNum; e->r.ownerNum = ent->s.number; e->think = explosive_indicator_think; e->nextthink = level.time + FRAMETIME; e->s.effect1Time = ent->target_ent->constructibleStats.weaponclass; if (ent->tagParent) { e->tagParent = ent->tagParent; Q_strncpyz(e->tagName, ent->tagName, MAX_QPATH); } else { VectorCopy(ent->r.absmin, e->s.pos.trBase); VectorAdd(ent->r.absmax, e->s.pos.trBase, e->s.pos.trBase); VectorScale(e->s.pos.trBase, 0.5, e->s.pos.trBase); } SnapVector(e->s.pos.trBase); trap_LinkEntity(e); ent->target_ent->parent = ent; } } else if (ent->target_ent->s.eType == ET_CONSTRUCTIBLE) { gentity_t *constructibles[2]; int team[2] = { 0 }; ent->target_ent->parent = ent; constructibles[0] = ent->target_ent; constructibles[1] = G_FindByTargetname(constructibles[0], ent->target); // see if we are targetting a 2nd one for two team constructibles team[0] = constructibles[0]->spawnflags & AXIS_CONSTRUCTIBLE ? TEAM_AXIS : TEAM_ALLIES; constructibles[0]->s.otherEntityNum2 = ent->s.teamNum; if (constructibles[1]) { team[1] = constructibles[1]->spawnflags & AXIS_CONSTRUCTIBLE ? TEAM_AXIS : TEAM_ALLIES; if (constructibles[1]->s.eType != ET_CONSTRUCTIBLE) { G_Error("'trigger_objective_info' targets multiple entities with targetname '%s', the second one isn't a 'func_constructible' [%d]\n", ent->target, constructibles[1]->s.eType); } if (team[0] == team[1]) { G_Error("'trigger_objective_info' targets two 'func_constructible' entities with targetname '%s' that are constructible by the same team\n", ent->target); } constructibles[1]->s.otherEntityNum2 = ent->s.teamNum; ent->chain = constructibles[1]; ent->chain->parent = ent; constructibles[0]->chain = constructibles[1]; constructibles[1]->chain = constructibles[0]; } else { constructibles[0]->chain = NULL; } // if already constructed (in case of START_BUILT) if (constructibles[0]->s.angles2[1] == 0) { // spawn a constructible icon - this is for compass usage gentity_t *e = G_Spawn(); e->r.svFlags = SVF_BROADCAST; e->classname = "constructible_indicator"; if (ent->spawnflags & 8) { e->s.eType = ET_TANK_INDICATOR_DEAD; } else { e->s.eType = ET_CONSTRUCTIBLE_INDICATOR; } e->s.pos.trType = TR_STATIONARY; if (constructibles[1]) { // see if one of the two is still partially built (happens when a multistage destructible construction blows up for the first time) if (constructibles[0]->count2 && constructibles[0]->grenadeFired > 1) { e->s.teamNum = team[0]; } else if (constructibles[1]->count2 && constructibles[1]->grenadeFired > 1) { e->s.teamNum = team[1]; } else { e->s.teamNum = 3; // both teams } } else { e->s.teamNum = team[0]; } e->s.modelindex2 = ent->s.teamNum; e->r.ownerNum = ent->s.number; ent->count2 = (e - g_entities); e->think = constructible_indicator_think; e->nextthink = level.time + FRAMETIME; e->parent = ent; if (ent->tagParent) { e->tagParent = ent->tagParent; Q_strncpyz(e->tagName, ent->tagName, MAX_QPATH); } else { VectorCopy(ent->r.absmin, e->s.pos.trBase); VectorAdd(ent->r.absmax, e->s.pos.trBase, e->s.pos.trBase); VectorScale(e->s.pos.trBase, 0.5, e->s.pos.trBase); } SnapVector(e->s.pos.trBase); trap_LinkEntity(e); // moved down } ent->touch = Touch_ObjectiveInfo; } else if (ent->target_ent->s.eType == ET_COMMANDMAP_MARKER) { ent->target_ent->parent = ent; } trap_LinkEntity(ent); }
void G_SpawnGEntityFromSpawnVars( qboolean inSubBSP ) { int i; gentity_t *ent; char *s, *value, *gametypeName; static char *gametypeNames[] = {"ffa", "holocron", "jedimaster", "duel", "powerduel", "single", "team", "siege", "ctf", "cty"}; // get the next free entity ent = G_Spawn(); for ( i = 0 ; i < level.numSpawnVars ; i++ ) { BG_ParseField( fields, level.spawnVars[i][0], level.spawnVars[i][1], (byte *)ent ); } // check for "notsingle" flag if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_SpawnInt( "notsingle", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } // check for "notteam" flag (GT_FFA, GT_DUEL, GT_SINGLE_PLAYER) if ( g_gametype.integer >= GT_TEAM ) { G_SpawnInt( "notteam", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } else { G_SpawnInt( "notfree", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } } G_SpawnInt( "notta", "0", &i ); if ( i ) { G_FreeEntity( ent ); return; } if( G_SpawnString( "gametype", NULL, &value ) ) { if( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { gametypeName = gametypeNames[g_gametype.integer]; s = strstr( value, gametypeName ); if( !s ) { G_FreeEntity( ent ); return; } } } // move editor origin to pos VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); // if we didn't get a classname, don't bother spawning anything if ( !G_CallSpawn( ent ) ) { G_FreeEntity( ent ); } //Tag on the ICARUS scripting information only to valid recipients if ( trap_ICARUS_ValidEnt( ent ) ) { trap_ICARUS_InitEnt( ent ); if ( ent->classname && ent->classname[0] ) { if ( Q_strncmp( "NPC_", ent->classname, 4 ) != 0 ) {//Not an NPC_spawner (rww - probably don't even care for MP, but whatever) G_ActivateBehavior( ent, BSET_SPAWN ); } } } }
/* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f (edict_t *ent) { char *name; gitem_t *it; int index; int i; qboolean give_all; edict_t *it_ent; if (deathmatch->value && !sv_cheats->value) { gi.cprintf (ent, PRINT_HIGH, "You must run the server with '+set cheats 1' to enable this command.\n"); return; } name = gi.args(); if (Q_stricmp(name, "all") == 0) give_all = true; else give_all = false; if (give_all || Q_stricmp(gi.argv(1), "health") == 0) { if (gi.argc() == 3) ent->health = atoi(gi.argv(2)); else ent->health = ent->max_health; if (!give_all) return; } if (give_all || Q_stricmp(name, "weapons") == 0) { for (i=0 ; i<game.num_items ; i++) { it = itemlist + i; if (!it->pickup) continue; if (!(it->flags & IT_WEAPON)) continue; ent->client->pers.inventory[i] += 1; } if (!give_all) return; } if (give_all || Q_stricmp(name, "ammo") == 0) { for (i=0 ; i<game.num_items ; i++) { it = itemlist + i; if (!it->pickup) continue; if (!(it->flags & IT_AMMO)) continue; Add_Ammo (ent, it, 1000); } if (!give_all) return; } if (give_all || Q_stricmp(name, "armor") == 0) { gitem_armor_t *info; it = FindItem("Jacket Armor"); ent->client->pers.inventory[ITEM_INDEX(it)] = 0; it = FindItem("Combat Armor"); ent->client->pers.inventory[ITEM_INDEX(it)] = 0; it = FindItem("Body Armor"); info = (gitem_armor_t *)it->info; ent->client->pers.inventory[ITEM_INDEX(it)] = info->max_count; if (!give_all) return; } if (give_all || Q_stricmp(name, "Power Shield") == 0) { it = FindItem("Power Shield"); it_ent = G_Spawn(); it_ent->classname = it->classname; SpawnItem (it_ent, it); Touch_Item (it_ent, ent, NULL, NULL); if (it_ent->inuse) G_FreeEdict(it_ent); if (!give_all) return; } if (give_all) { for (i=0 ; i<game.num_items ; i++) { it = itemlist + i; if (!it->pickup) continue; if (it->flags & (IT_ARMOR|IT_WEAPON|IT_AMMO)) continue; ent->client->pers.inventory[i] = 1; } return; } it = FindItem (name); if (!it) { name = gi.argv(1); it = FindItem (name); if (!it) { gi.cprintf (ent, PRINT_HIGH, "unknown item\n"); return; } } if (!it->pickup) { gi.cprintf (ent, PRINT_HIGH, "non-pickup item\n"); return; } index = ITEM_INDEX(it); if (it->flags & IT_AMMO) { if (gi.argc() == 3) ent->client->pers.inventory[index] = atoi(gi.argv(2)); else ent->client->pers.inventory[index] += it->quantity; } else { it_ent = G_Spawn(); it_ent->classname = it->classname; SpawnItem (it_ent, it); Touch_Item (it_ent, ent, NULL, NULL); if (it_ent->inuse) G_FreeEdict(it_ent); } }
//---------------------------------------------------------- void emplaced_gun_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { vec3_t fwd1, fwd2; if ( self->health <= 0 ) { // can't use a dead gun. return; } if ( self->svFlags & SVF_INACTIVE ) { return; // can't use inactive gun } if ( !activator->client ) { return; // only a client can use it. } if ( self->activator ) { // someone is already in the gun. return; } if ( other && other->client && G_IsRidingVehicle( other ) ) {//can't use eweb when on a vehicle return; } if ( activator && activator->client && G_IsRidingVehicle( activator ) ) {//can't use eweb when on a vehicle return; } // We'll just let the designers duke this one out....I mean, as to whether they even want to limit such a thing. if ( self->spawnflags & EMPLACED_FACING ) { // Let's get some direction vectors for the users AngleVectors( activator->client->ps.viewangles, fwd1, NULL, NULL ); // Get the guns direction vector AngleVectors( self->pos1, fwd2, NULL, NULL ); float dot = DotProduct( fwd1, fwd2 ); // Must be reasonably facing the way the gun points ( 90 degrees or so ), otherwise we don't allow to use it. if ( dot < 0.0f ) { return; } } // don't allow using it again for half a second if ( self->delay + 500 < level.time ) { int oldWeapon = activator->s.weapon; if ( oldWeapon == WP_SABER ) { self->alt_fire = activator->client->ps.SaberActive(); } // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; G_RemoveWeaponModels( activator ); extern void ChangeWeapon( gentity_t *ent, int newWeapon ); if ( activator->NPC ) { ChangeWeapon( activator, WP_EMPLACED_GUN ); } else if ( activator->s.number == 0 ) { // we don't want for it to draw the weapon select stuff cg.weaponSelect = WP_EMPLACED_GUN; CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 ); } // Since we move the activator inside of the gun, we reserve a solid spot where they were standing in order to be able to get back out without being in solid if ( self->nextTrain ) {//you never know G_FreeEntity( self->nextTrain ); } self->nextTrain = G_Spawn(); //self->nextTrain->classname = "emp_placeholder"; self->nextTrain->contents = CONTENTS_MONSTERCLIP|CONTENTS_PLAYERCLIP;//hmm... playerclip too now that we're doing it for NPCs? G_SetOrigin( self->nextTrain, activator->client->ps.origin ); VectorCopy( activator->mins, self->nextTrain->mins ); VectorCopy( activator->maxs, self->nextTrain->maxs ); gi.linkentity( self->nextTrain ); //need to inflate the activator's mins/maxs since the gunsit anim puts them outside of their bbox VectorSet( activator->mins, -24, -24, -24 ); VectorSet( activator->maxs, 24, 24, 40 ); // Move the activator into the center of the gun. For NPC's the only way the can get out of the gun is to die. VectorCopy( self->s.origin, activator->client->ps.origin ); activator->client->ps.origin[2] += 30; // move them up so they aren't standing in the floor gi.linkentity( activator ); // the gun will track which weapon we used to have self->s.weapon = oldWeapon; // Lock the player activator->client->ps.eFlags |= EF_LOCKED_TO_WEAPON; activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. self->activator = activator; self->delay = level.time; // can't disconnect from the thing for half a second // Let the gun be considered an enemy //Ugh, so much AI code seems to assume enemies are clients, maybe this shouldn't be on, but it's too late in the game to change it now without knowing what side-effects this will have self->svFlags |= SVF_NONNPC_ENEMY; self->noDamageTeam = activator->client->playerTeam; // FIXME: don't do this, we'll try and actually put the player in this beast // move the player to the center of the gun // activator->contents = 0; // VectorCopy( self->currentOrigin, activator->client->ps.origin ); SetClientViewAngle( activator, self->pos1 ); //FIXME: should really wait a bit after spawn and get this just once? self->waypoint = NAV::GetNearestNode(self); #ifdef _DEBUG if ( self->waypoint == -1 ) { gi.Printf( S_COLOR_RED"ERROR: no waypoint for emplaced_gun %s at %s\n", self->targetname, vtos(self->currentOrigin) ); } #endif G_Sound( self, G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" )); #ifdef _IMMERSION G_Force( self, G_ForceIndex( "fffx/weapons/emplaced/emplaced_mount", FF_CHANNEL_TOUCH ) ); #endif // _IMMERSION if ( !(self->spawnflags&EMPLACED_PLAYERUSE) || activator->s.number == 0 ) {//player-only usescript or any usescript // Run use script G_ActivateBehavior( self, BSET_USE ); } } }
/* =================== G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from level.spawnVars[], then call the class specfic spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { int i; gentity_t *ent; char *s, *value; // get the next free entity ent = G_Spawn(); for ( i = 0 ; i < level.numSpawnVars ; i++ ) { G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent ); } // check for "notsingle" flag if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_SpawnInt( "notsingle", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } // check for "notteam" flag (GT_FFA, GT_TOURNAMENT, GT_SINGLE_PLAYER) if ( gt[g_gametype.integer].teams ) { G_SpawnInt( "notteam", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } else { G_SpawnInt( "notfree", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } #ifdef MISSIONPACK G_SpawnInt( "notta", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } #else G_SpawnInt( "notq3a", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } #endif //fnq3 G_SpawnInt( "notfnq3", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } if ( g_gametype.integer >= GT_FFA && g_gametype.integer < GT_MAX_GAME_TYPE ) { if ( G_SpawnString("gametype", NULL, &value) ) { s = strstr( value, gt[g_gametype.integer].refName ); if ( !s ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( G_SpawnString("not_gametype", NULL, &value) ) { s = strstr( value, gt[g_gametype.integer].refName ); if ( s ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } } if ( gt[g_gametype.integer].arena ) { G_SpawnInt( "not_gt_arena", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].ctf ) { G_SpawnInt( "not_gt_ctf", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].dom ) { G_SpawnInt( "not_gt_dom", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].duel ) { G_SpawnInt( "not_gt_duel", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].elimination ) { G_SpawnInt( "not_gt_elim", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].freeze ) { G_SpawnInt( "not_gt_freeze", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].single ) { G_SpawnInt( "not_gt_single", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].tdm ) { G_SpawnInt( "not_gt_tdm", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } if ( gt[g_gametype.integer].teams ) { G_SpawnInt( "not_gt_teams", "0", &i ); if ( i ) { ADJUST_AREAPORTAL(); G_FreeEntity( ent ); return; } } //-fnq3 // move editor origin to pos VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); // if we didn't get a classname, don't bother spawning anything if ( !G_CallSpawn(ent) ) { G_FreeEntity( ent ); } }