1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7
8 This file is part of the OpenJK source code.
9
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23
24 #include "g_local.h"
25
G_SpawnString(const char * key,const char * defaultString,char ** out)26 qboolean G_SpawnString( const char *key, const char *defaultString, char **out ) {
27 int i;
28
29 if ( !level.spawning ) {
30 *out = (char *)defaultString;
31 // trap->Error( ERR_DROP, "G_SpawnString() called while not spawning" );
32 }
33
34 for ( i = 0 ; i < level.numSpawnVars ; i++ ) {
35 if ( !Q_stricmp( key, level.spawnVars[i][0] ) ) {
36 *out = level.spawnVars[i][1];
37 return qtrue;
38 }
39 }
40
41 *out = (char *)defaultString;
42 return qfalse;
43 }
44
G_SpawnFloat(const char * key,const char * defaultString,float * out)45 qboolean G_SpawnFloat( const char *key, const char *defaultString, float *out ) {
46 char *s;
47 qboolean present;
48
49 present = G_SpawnString( key, defaultString, &s );
50 *out = atof( s );
51 return present;
52 }
53
G_SpawnInt(const char * key,const char * defaultString,int * out)54 qboolean G_SpawnInt( const char *key, const char *defaultString, int *out ) {
55 char *s;
56 qboolean present;
57
58 present = G_SpawnString( key, defaultString, &s );
59 *out = atoi( s );
60 return present;
61 }
62
G_SpawnVector(const char * key,const char * defaultString,float * out)63 qboolean G_SpawnVector( const char *key, const char *defaultString, float *out ) {
64 char *s;
65 qboolean present;
66
67 present = G_SpawnString( key, defaultString, &s );
68 if ( sscanf( s, "%f %f %f", &out[0], &out[1], &out[2] ) != 3 ) {
69 trap->Print( "G_SpawnVector: Failed sscanf on %s (default: %s)\n", key, defaultString );
70 VectorClear( out );
71 return qfalse;
72 }
73 return present;
74 }
75
G_SpawnBoolean(const char * key,const char * defaultString,qboolean * out)76 qboolean G_SpawnBoolean( const char *key, const char *defaultString, qboolean *out ) {
77 char *s;
78 qboolean present;
79
80 present = G_SpawnString( key, defaultString, &s );
81
82 if ( !Q_stricmp( s, "qtrue" ) || !Q_stricmp( s, "true" ) || !Q_stricmp( s, "yes" ) || !Q_stricmp( s, "1" ) )
83 *out = qtrue;
84 else if ( !Q_stricmp( s, "qfalse" ) || !Q_stricmp( s, "false" ) || !Q_stricmp( s, "no" ) || !Q_stricmp( s, "0" ) )
85 *out = qfalse;
86 else
87 *out = qfalse;
88
89 return present;
90 }
91
92 //
93 // fields are needed for spawning from the entity string
94 //
95 typedef enum {
96 F_INT,
97 F_FLOAT,
98 F_STRING, // string on disk, pointer in memory
99 F_VECTOR,
100 F_ANGLEHACK,
101 F_PARM1, // Special case for parms
102 F_PARM2, // Special case for parms
103 F_PARM3, // Special case for parms
104 F_PARM4, // Special case for parms
105 F_PARM5, // Special case for parms
106 F_PARM6, // Special case for parms
107 F_PARM7, // Special case for parms
108 F_PARM8, // Special case for parms
109 F_PARM9, // Special case for parms
110 F_PARM10, // Special case for parms
111 F_PARM11, // Special case for parms
112 F_PARM12, // Special case for parms
113 F_PARM13, // Special case for parms
114 F_PARM14, // Special case for parms
115 F_PARM15, // Special case for parms
116 F_PARM16 // Special case for parms
117 } fieldtype_t;
118
119 typedef struct field_s {
120 const char *name;
121 size_t ofs;
122 fieldtype_t type;
123 } field_t;
124
125 field_t fields[] = {
126 { "alliedteam", FOFS( alliedTeam ), F_INT },//for misc_turrets
127 { "angerscript", FOFS( behaviorSet[BSET_ANGER] ), F_STRING },//name of script to run
128 { "angle", FOFS( s.angles ), F_ANGLEHACK },
129 { "angles", FOFS( s.angles ), F_VECTOR },
130 { "attackscript", FOFS( behaviorSet[BSET_ATTACK] ), F_STRING },//name of script to run
131 { "awakescript", FOFS( behaviorSet[BSET_AWAKE] ), F_STRING },//name of script to run
132 { "blockedscript", FOFS( behaviorSet[BSET_BLOCKED] ), F_STRING },//name of script to run
133 { "chunksize", FOFS( mass ), F_FLOAT },//for func_breakables
134 { "classname", FOFS( classname ), F_STRING },
135 { "closetarget", FOFS( closetarget ), F_STRING },//for doors
136 { "count", FOFS( count ), F_INT },
137 { "deathscript", FOFS( behaviorSet[BSET_DEATH] ), F_STRING },//name of script to run
138 { "delay", FOFS( delay ), F_INT },
139 { "delayscript", FOFS( behaviorSet[BSET_DELAYED] ), F_STRING },//name of script to run
140 { "delayscripttime", FOFS( delayScriptTime ), F_INT },//name of script to run
141 { "dmg", FOFS( damage ), F_INT },
142 { "ffdeathscript", FOFS( behaviorSet[BSET_FFDEATH] ), F_STRING },//name of script to run
143 { "ffirescript", FOFS( behaviorSet[BSET_FFIRE] ), F_STRING },//name of script to run
144 { "fleescript", FOFS( behaviorSet[BSET_FLEE] ), F_STRING },//name of script to run
145 { "fullname", FOFS( fullName ), F_STRING },
146 { "goaltarget", FOFS( goaltarget ), F_STRING },//for siege
147 { "healingclass", FOFS( healingclass ), F_STRING },
148 { "healingrate", FOFS( healingrate ), F_INT },
149 { "healingsound", FOFS( healingsound ), F_STRING },
150 { "health", FOFS( health ), F_INT },
151 { "idealclass", FOFS( idealclass ), F_STRING },//for siege spawnpoints
152 { "linear", FOFS( alt_fire ), F_INT },//for movers to use linear movement
153 { "lostenemyscript", FOFS( behaviorSet[BSET_LOSTENEMY] ), F_STRING },//name of script to run
154 { "message", FOFS( message ), F_STRING },
155 { "mindtrickscript", FOFS( behaviorSet[BSET_MINDTRICK] ), F_STRING },//name of script to run
156 { "model", FOFS( model ), F_STRING },
157 { "model2", FOFS( model2 ), F_STRING },
158 { "npc_target", FOFS( NPC_target ), F_STRING },
159 { "npc_target2", FOFS( target2 ), F_STRING },//NPC_spawner only
160 { "npc_target4", FOFS( target4 ), F_STRING },//NPC_spawner only
161 { "npc_targetname", FOFS( NPC_targetname ), F_STRING },
162 { "npc_type", FOFS( NPC_type ), F_STRING },
163 { "numchunks", FOFS( radius ), F_FLOAT },//for func_breakables
164 { "opentarget", FOFS( opentarget ), F_STRING },//for doors
165 { "origin", FOFS( s.origin ), F_VECTOR },
166 { "ownername", FOFS( ownername ), F_STRING },
167 { "painscript", FOFS( behaviorSet[BSET_PAIN] ), F_STRING },//name of script to run
168 { "paintarget", FOFS( paintarget ), F_STRING },//for doors
169 { "parm1", 0, F_PARM1 },
170 { "parm10", 0, F_PARM10 },
171 { "parm11", 0, F_PARM11 },
172 { "parm12", 0, F_PARM12 },
173 { "parm13", 0, F_PARM13 },
174 { "parm14", 0, F_PARM14 },
175 { "parm15", 0, F_PARM15 },
176 { "parm16", 0, F_PARM16 },
177 { "parm2", 0, F_PARM2 },
178 { "parm3", 0, F_PARM3 },
179 { "parm4", 0, F_PARM4 },
180 { "parm5", 0, F_PARM5 },
181 { "parm6", 0, F_PARM6 },
182 { "parm7", 0, F_PARM7 },
183 { "parm8", 0, F_PARM8 },
184 { "parm9", 0, F_PARM9 },
185 { "radius", FOFS( radius ), F_FLOAT },
186 { "random", FOFS( random ), F_FLOAT },
187 { "roffname", FOFS( roffname ), F_STRING },
188 { "rofftarget", FOFS( rofftarget ), F_STRING },
189 { "script_targetname", FOFS( script_targetname ), F_STRING },//scripts look for this when "affecting"
190 { "soundset", FOFS( soundSet ), F_STRING },
191 { "spawnflags", FOFS( spawnflags ), F_INT },
192 { "spawnscript", FOFS( behaviorSet[BSET_SPAWN] ), F_STRING },//name of script to run
193 { "speed", FOFS( speed ), F_FLOAT },
194 { "target", FOFS( target ), F_STRING },
195 { "target2", FOFS( target2 ), F_STRING },
196 { "target3", FOFS( target3 ), F_STRING },
197 { "target4", FOFS( target4 ), F_STRING },
198 { "target5", FOFS( target5 ), F_STRING },
199 { "target6", FOFS( target6 ), F_STRING },
200 { "targetname", FOFS( targetname ), F_STRING },
201 { "targetshadername", FOFS( targetShaderName ), F_STRING },
202 { "targetshadernewname", FOFS( targetShaderNewName ), F_STRING },
203 { "team", FOFS( team ), F_STRING },
204 { "teamnodmg", FOFS( teamnodmg ), F_INT },
205 { "teamowner", FOFS( s.teamowner ), F_INT },
206 { "teamuser", FOFS( alliedTeam ), F_INT },
207 { "usescript", FOFS( behaviorSet[BSET_USE] ), F_STRING },//name of script to run
208 { "victoryscript", FOFS( behaviorSet[BSET_VICTORY] ), F_STRING },//name of script to run
209 { "wait", FOFS( wait ), F_FLOAT },
210 };
211
212 typedef struct spawn_s {
213 const char *name;
214 void (*spawn)(gentity_t *ent);
215 } spawn_t;
216
217 void SP_info_player_start (gentity_t *ent);
218 void SP_info_player_duel( gentity_t *ent );
219 void SP_info_player_duel1( gentity_t *ent );
220 void SP_info_player_duel2( gentity_t *ent );
221 void SP_info_player_deathmatch (gentity_t *ent);
222 void SP_info_player_siegeteam1 (gentity_t *ent);
223 void SP_info_player_siegeteam2 (gentity_t *ent);
224 void SP_info_player_intermission (gentity_t *ent);
225 void SP_info_player_intermission_red (gentity_t *ent);
226 void SP_info_player_intermission_blue (gentity_t *ent);
227 void SP_info_jedimaster_start (gentity_t *ent);
228 void SP_info_player_start_red (gentity_t *ent);
229 void SP_info_player_start_blue (gentity_t *ent);
230 void SP_info_firstplace(gentity_t *ent);
231 void SP_info_secondplace(gentity_t *ent);
232 void SP_info_thirdplace(gentity_t *ent);
233 void SP_info_podium(gentity_t *ent);
234
235 void SP_info_siege_objective (gentity_t *ent);
236 void SP_info_siege_radaricon (gentity_t *ent);
237 void SP_info_siege_decomplete (gentity_t *ent);
238 void SP_target_siege_end (gentity_t *ent);
239 void SP_misc_siege_item (gentity_t *ent);
240
241 void SP_func_plat (gentity_t *ent);
242 void SP_func_static (gentity_t *ent);
243 void SP_func_rotating (gentity_t *ent);
244 void SP_func_bobbing (gentity_t *ent);
245 void SP_func_pendulum( gentity_t *ent );
246 void SP_func_button (gentity_t *ent);
247 void SP_func_door (gentity_t *ent);
248 void SP_func_train (gentity_t *ent);
249 void SP_func_timer (gentity_t *self);
250 void SP_func_breakable (gentity_t *ent);
251 void SP_func_glass (gentity_t *ent);
252 void SP_func_usable( gentity_t *ent);
253 void SP_func_wall( gentity_t *ent );
254
255 void SP_trigger_lightningstrike( gentity_t *ent );
256
257 void SP_trigger_always (gentity_t *ent);
258 void SP_trigger_multiple (gentity_t *ent);
259 void SP_trigger_once( gentity_t *ent );
260 void SP_trigger_push (gentity_t *ent);
261 void SP_trigger_teleport (gentity_t *ent);
262 void SP_trigger_hurt (gentity_t *ent);
263 void SP_trigger_space(gentity_t *self);
264 void SP_trigger_shipboundary(gentity_t *self);
265 void SP_trigger_hyperspace(gentity_t *self);
266 void SP_trigger_asteroid_field(gentity_t *self);
267
268 void SP_target_remove_powerups( gentity_t *ent );
269 void SP_target_give (gentity_t *ent);
270 void SP_target_delay (gentity_t *ent);
271 void SP_target_speaker (gentity_t *ent);
272 void SP_target_print (gentity_t *ent);
273 void SP_target_laser (gentity_t *self);
274 void SP_target_character (gentity_t *ent);
275 void SP_target_score( gentity_t *ent );
276 void SP_target_teleporter( gentity_t *ent );
277 void SP_target_relay (gentity_t *ent);
278 void SP_target_kill (gentity_t *ent);
279 void SP_target_position (gentity_t *ent);
280 void SP_target_location (gentity_t *ent);
281 void SP_target_counter (gentity_t *self);
282 void SP_target_random (gentity_t *self);
283 void SP_target_scriptrunner( gentity_t *self );
284 void SP_target_interest (gentity_t *self);
285 void SP_target_activate (gentity_t *self);
286 void SP_target_deactivate (gentity_t *self);
287 void SP_target_level_change( gentity_t *self );
288 void SP_target_play_music( gentity_t *self );
289 void SP_target_push (gentity_t *ent);
290
291 void SP_light (gentity_t *self);
292 void SP_info_null (gentity_t *self);
293 void SP_info_notnull (gentity_t *self);
294 void SP_info_camp (gentity_t *self);
295 void SP_path_corner (gentity_t *self);
296
297 void SP_misc_teleporter_dest (gentity_t *self);
298 void SP_misc_model(gentity_t *ent);
299 void SP_misc_model_static(gentity_t *ent);
300 void SP_misc_model_breakable( gentity_t *ent ) ;
301 void SP_misc_G2model(gentity_t *ent);
302 void SP_misc_portal_camera(gentity_t *ent);
303 void SP_misc_portal_surface(gentity_t *ent);
304 void SP_misc_weather_zone( gentity_t *ent );
305
306 void SP_misc_bsp (gentity_t *ent);
307 void SP_terrain (gentity_t *ent);
308 void SP_misc_skyportal_orient (gentity_t *ent);
309 void SP_misc_skyportal (gentity_t *ent);
310
311 void SP_misc_ammo_floor_unit(gentity_t *ent);
312 void SP_misc_shield_floor_unit( gentity_t *ent );
313 void SP_misc_model_shield_power_converter( gentity_t *ent );
314 void SP_misc_model_ammo_power_converter( gentity_t *ent );
315 void SP_misc_model_health_power_converter( gentity_t *ent );
316
317 void SP_fx_runner( gentity_t *ent );
318
319 void SP_target_screenshake(gentity_t *ent);
320 void SP_target_escapetrig(gentity_t *ent);
321
322 void SP_misc_maglock ( gentity_t *self );
323
324 void SP_misc_faller(gentity_t *ent);
325
326 void SP_misc_holocron(gentity_t *ent);
327
328 void SP_reference_tag ( gentity_t *ent );
329
330 void SP_misc_weapon_shooter( gentity_t *self );
331
332 void SP_misc_cubemap( gentity_t *ent );
333
334 void SP_NPC_spawner( gentity_t *self );
335
336 void SP_NPC_Vehicle( gentity_t *self);
337
338 void SP_NPC_Kyle( gentity_t *self );
339 void SP_NPC_Lando( gentity_t *self );
340 void SP_NPC_Jan( gentity_t *self );
341 void SP_NPC_Luke( gentity_t *self );
342 void SP_NPC_MonMothma( gentity_t *self );
343 void SP_NPC_Tavion( gentity_t *self );
344 void SP_NPC_Tavion_New( gentity_t *self );
345 void SP_NPC_Alora( gentity_t *self );
346 void SP_NPC_Reelo( gentity_t *self );
347 void SP_NPC_Galak( gentity_t *self );
348 void SP_NPC_Desann( gentity_t *self );
349 void SP_NPC_Bartender( gentity_t *self );
350 void SP_NPC_MorganKatarn( gentity_t *self );
351 void SP_NPC_Jedi( gentity_t *self );
352 void SP_NPC_Prisoner( gentity_t *self );
353 void SP_NPC_Rebel( gentity_t *self );
354 void SP_NPC_Human_Merc( gentity_t *self );
355 void SP_NPC_Stormtrooper( gentity_t *self );
356 void SP_NPC_StormtrooperOfficer( gentity_t *self );
357 void SP_NPC_Snowtrooper( gentity_t *self);
358 void SP_NPC_Tie_Pilot( gentity_t *self );
359 void SP_NPC_Ugnaught( gentity_t *self );
360 void SP_NPC_Jawa( gentity_t *self );
361 void SP_NPC_Gran( gentity_t *self );
362 void SP_NPC_Rodian( gentity_t *self );
363 void SP_NPC_Weequay( gentity_t *self );
364 void SP_NPC_Trandoshan( gentity_t *self );
365 void SP_NPC_Tusken( gentity_t *self );
366 void SP_NPC_Noghri( gentity_t *self );
367 void SP_NPC_SwampTrooper( gentity_t *self );
368 void SP_NPC_Imperial( gentity_t *self );
369 void SP_NPC_ImpWorker( gentity_t *self );
370 void SP_NPC_BespinCop( gentity_t *self );
371 void SP_NPC_Reborn( gentity_t *self );
372 void SP_NPC_ShadowTrooper( gentity_t *self );
373 void SP_NPC_Monster_Murjj( gentity_t *self );
374 void SP_NPC_Monster_Swamp( gentity_t *self );
375 void SP_NPC_Monster_Howler( gentity_t *self );
376 void SP_NPC_Monster_Claw( gentity_t *self );
377 void SP_NPC_Monster_Glider( gentity_t *self );
378 void SP_NPC_Monster_Flier2( gentity_t *self );
379 void SP_NPC_Monster_Lizard( gentity_t *self );
380 void SP_NPC_Monster_Fish( gentity_t *self );
381 void SP_NPC_Monster_Wampa( gentity_t *self );
382 void SP_NPC_Monster_Rancor( gentity_t *self );
383 void SP_NPC_MineMonster( gentity_t *self );
384 void SP_NPC_Droid_Interrogator( gentity_t *self );
385 void SP_NPC_Droid_Probe( gentity_t *self );
386 void SP_NPC_Droid_Mark1( gentity_t *self );
387 void SP_NPC_Droid_Mark2( gentity_t *self );
388 void SP_NPC_Droid_ATST( gentity_t *self );
389 void SP_NPC_Droid_Seeker( gentity_t *self );
390 void SP_NPC_Droid_Remote( gentity_t *self );
391 void SP_NPC_Droid_Sentry( gentity_t *self );
392 void SP_NPC_Droid_Gonk( gentity_t *self );
393 void SP_NPC_Droid_Mouse( gentity_t *self );
394 void SP_NPC_Droid_R2D2( gentity_t *self );
395 void SP_NPC_Droid_R5D2( gentity_t *self );
396 void SP_NPC_Droid_Protocol( gentity_t *self );
397
398 void SP_NPC_Reborn_New( gentity_t *self);
399 void SP_NPC_Cultist( gentity_t *self );
400 void SP_NPC_Cultist_Saber( gentity_t *self );
401 void SP_NPC_Cultist_Saber_Powers( gentity_t *self );
402 void SP_NPC_Cultist_Destroyer( gentity_t *self );
403 void SP_NPC_Cultist_Commando( gentity_t *self );
404
405 void SP_waypoint (gentity_t *ent);
406 void SP_waypoint_small (gentity_t *ent);
407 void SP_waypoint_navgoal (gentity_t *ent);
408 void SP_waypoint_navgoal_8 (gentity_t *ent);
409 void SP_waypoint_navgoal_4 (gentity_t *ent);
410 void SP_waypoint_navgoal_2 (gentity_t *ent);
411 void SP_waypoint_navgoal_1 (gentity_t *ent);
412
413 void SP_CreateWind( gentity_t *ent );
414 void SP_CreateSpaceDust( gentity_t *ent );
415 void SP_CreateSnow( gentity_t *ent );
416 void SP_CreateRain( gentity_t *ent );
417
418 void SP_point_combat( gentity_t *self );
419
420 void SP_shooter_blaster( gentity_t *ent );
421
422 void SP_team_CTF_redplayer( gentity_t *ent );
423 void SP_team_CTF_blueplayer( gentity_t *ent );
424
425 void SP_team_CTF_redspawn( gentity_t *ent );
426 void SP_team_CTF_bluespawn( gentity_t *ent );
427
428 void SP_misc_turret( gentity_t *ent );
429 void SP_misc_turretG2( gentity_t *base );
430
SP_item_botroam(gentity_t * ent)431 void SP_item_botroam( gentity_t *ent ) { }
432
SP_gametype_item(gentity_t * ent)433 void SP_gametype_item ( gentity_t* ent )
434 {
435 gitem_t *item = NULL;
436 char *value;
437 int team = -1;
438
439 G_SpawnString("teamfilter", "", &value);
440
441 G_SetOrigin( ent, ent->s.origin );
442
443 // If a team filter is set then override any team settings for the spawns
444 if ( level.mTeamFilter[0] )
445 {
446 if ( Q_stricmp ( level.mTeamFilter, "red") == 0 )
447 {
448 team = TEAM_RED;
449 }
450 else if ( Q_stricmp ( level.mTeamFilter, "blue") == 0 )
451 {
452 team = TEAM_BLUE;
453 }
454 }
455
456 if (ent->targetname && ent->targetname[0])
457 {
458 if (team != -1)
459 {
460 if (strstr(ent->targetname, "flag"))
461 {
462 if (team == TEAM_RED)
463 {
464 item = BG_FindItem("team_CTF_redflag");
465 }
466 else
467 { //blue
468 item = BG_FindItem("team_CTF_blueflag");
469 }
470 }
471 }
472 else if (strstr(ent->targetname, "red_flag"))
473 {
474 item = BG_FindItem("team_CTF_redflag");
475 }
476 else if (strstr(ent->targetname, "blue_flag"))
477 {
478 item = BG_FindItem("team_CTF_blueflag");
479 }
480 else
481 {
482 item = NULL;
483 }
484
485 if (item)
486 {
487 ent->targetname = NULL;
488 ent->classname = item->classname;
489 G_SpawnItem( ent, item );
490 }
491 }
492 }
493
494 void SP_emplaced_gun( gentity_t *ent );
495
496 spawn_t spawns[] = {
497 { "emplaced_gun", SP_emplaced_gun },
498 { "func_bobbing", SP_func_bobbing },
499 { "func_breakable", SP_func_breakable },
500 { "func_button", SP_func_button },
501 { "func_door", SP_func_door },
502 { "func_glass", SP_func_glass },
503 { "func_group", SP_info_null },
504 { "func_pendulum", SP_func_pendulum },
505 { "func_plat", SP_func_plat },
506 { "func_rotating", SP_func_rotating },
507 { "func_static", SP_func_static },
508 { "func_timer", SP_func_timer }, // rename trigger_timer?
509 { "func_train", SP_func_train },
510 { "func_usable", SP_func_usable },
511 { "func_wall", SP_func_wall },
512 { "fx_rain", SP_CreateRain },
513 { "fx_runner", SP_fx_runner },
514 { "fx_snow", SP_CreateSnow },
515 { "fx_spacedust", SP_CreateSpaceDust },
516 { "fx_wind", SP_CreateWind },
517 { "gametype_item", SP_gametype_item },
518 { "info_camp", SP_info_camp },
519 { "info_jedimaster_start", SP_info_jedimaster_start },
520 { "info_notnull", SP_info_notnull }, // use target_position instead
521 { "info_null", SP_info_null },
522 { "info_player_deathmatch", SP_info_player_deathmatch },
523 { "info_player_duel", SP_info_player_duel },
524 { "info_player_duel1", SP_info_player_duel1 },
525 { "info_player_duel2", SP_info_player_duel2 },
526 { "info_player_intermission", SP_info_player_intermission },
527 { "info_player_intermission_blue", SP_info_player_intermission_blue },
528 { "info_player_intermission_red", SP_info_player_intermission_red },
529 { "info_player_siegeteam1", SP_info_player_siegeteam1 },
530 { "info_player_siegeteam2", SP_info_player_siegeteam2 },
531 { "info_player_start", SP_info_player_start },
532 { "info_player_start_blue", SP_info_player_start_blue },
533 { "info_player_start_red", SP_info_player_start_red },
534 { "info_siege_decomplete", SP_info_siege_decomplete },
535 { "info_siege_objective", SP_info_siege_objective },
536 { "info_siege_radaricon", SP_info_siege_radaricon },
537 { "item_botroam", SP_item_botroam },
538 { "light", SP_light },
539 { "misc_ammo_floor_unit", SP_misc_ammo_floor_unit },
540 { "misc_bsp", SP_misc_bsp },
541 { "misc_cubemap", SP_misc_cubemap },
542 { "misc_faller", SP_misc_faller },
543 { "misc_G2model", SP_misc_G2model },
544 { "misc_holocron", SP_misc_holocron },
545 { "misc_maglock", SP_misc_maglock },
546 { "misc_model", SP_misc_model },
547 { "misc_model_ammo_power_converter", SP_misc_model_ammo_power_converter },
548 { "misc_model_breakable", SP_misc_model_breakable },
549 { "misc_model_health_power_converter", SP_misc_model_health_power_converter },
550 { "misc_model_shield_power_converter", SP_misc_model_shield_power_converter },
551 { "misc_model_static", SP_misc_model_static },
552 { "misc_portal_camera", SP_misc_portal_camera },
553 { "misc_portal_surface", SP_misc_portal_surface },
554 { "misc_shield_floor_unit", SP_misc_shield_floor_unit },
555 { "misc_siege_item", SP_misc_siege_item },
556 { "misc_skyportal", SP_misc_skyportal },
557 { "misc_skyportal_orient", SP_misc_skyportal_orient },
558 { "misc_teleporter_dest", SP_misc_teleporter_dest },
559 { "misc_turret", SP_misc_turret },
560 { "misc_turretG2", SP_misc_turretG2 },
561 { "misc_weapon_shooter", SP_misc_weapon_shooter },
562 { "misc_weather_zone", SP_misc_weather_zone },
563 { "npc_alora", SP_NPC_Alora },
564 { "npc_bartender", SP_NPC_Bartender },
565 { "npc_bespincop", SP_NPC_BespinCop },
566 { "npc_colombian_emplacedgunner", SP_NPC_ShadowTrooper },
567 { "npc_colombian_rebel", SP_NPC_Reborn },
568 { "npc_colombian_soldier", SP_NPC_Reborn },
569 { "npc_cultist", SP_NPC_Cultist },
570 { "npc_cultist_commando", SP_NPC_Cultist_Commando },
571 { "npc_cultist_destroyer", SP_NPC_Cultist_Destroyer },
572 { "npc_cultist_saber", SP_NPC_Cultist_Saber },
573 { "npc_cultist_saber_powers", SP_NPC_Cultist_Saber_Powers },
574 { "npc_desann", SP_NPC_Desann },
575 { "npc_droid_atst", SP_NPC_Droid_ATST },
576 { "npc_droid_gonk", SP_NPC_Droid_Gonk },
577 { "npc_droid_interrogator", SP_NPC_Droid_Interrogator },
578 { "npc_droid_mark1", SP_NPC_Droid_Mark1 },
579 { "npc_droid_mark2", SP_NPC_Droid_Mark2 },
580 { "npc_droid_mouse", SP_NPC_Droid_Mouse },
581 { "npc_droid_probe", SP_NPC_Droid_Probe },
582 { "npc_droid_protocol", SP_NPC_Droid_Protocol },
583 { "npc_droid_r2d2", SP_NPC_Droid_R2D2 },
584 { "npc_droid_r5d2", SP_NPC_Droid_R5D2 },
585 { "npc_droid_remote", SP_NPC_Droid_Remote },
586 { "npc_droid_seeker", SP_NPC_Droid_Seeker },
587 { "npc_droid_sentry", SP_NPC_Droid_Sentry },
588 { "npc_galak", SP_NPC_Galak },
589 { "npc_gran", SP_NPC_Gran },
590 { "npc_human_merc", SP_NPC_Human_Merc },
591 { "npc_imperial", SP_NPC_Imperial },
592 { "npc_impworker", SP_NPC_ImpWorker },
593 { "npc_jan", SP_NPC_Jan },
594 { "npc_jawa", SP_NPC_Jawa },
595 { "npc_jedi", SP_NPC_Jedi },
596 { "npc_kyle", SP_NPC_Kyle },
597 { "npc_lando", SP_NPC_Lando },
598 { "npc_luke", SP_NPC_Luke },
599 { "npc_manuel_vergara_rmg", SP_NPC_Desann },
600 { "npc_minemonster", SP_NPC_MineMonster },
601 { "npc_monmothma", SP_NPC_MonMothma },
602 { "npc_monster_claw", SP_NPC_Monster_Claw },
603 { "npc_monster_fish", SP_NPC_Monster_Fish },
604 { "npc_monster_flier2", SP_NPC_Monster_Flier2 },
605 { "npc_monster_glider", SP_NPC_Monster_Glider },
606 { "npc_monster_howler", SP_NPC_Monster_Howler },
607 { "npc_monster_lizard", SP_NPC_Monster_Lizard },
608 { "npc_monster_murjj", SP_NPC_Monster_Murjj },
609 { "npc_monster_rancor", SP_NPC_Monster_Rancor },
610 { "npc_monster_swamp", SP_NPC_Monster_Swamp },
611 { "npc_monster_wampa", SP_NPC_Monster_Wampa },
612 { "npc_morgankatarn", SP_NPC_MorganKatarn },
613 { "npc_noghri", SP_NPC_Noghri },
614 { "npc_prisoner", SP_NPC_Prisoner },
615 { "npc_rebel", SP_NPC_Rebel },
616 { "npc_reborn", SP_NPC_Reborn },
617 { "npc_reborn_new", SP_NPC_Reborn_New },
618 { "npc_reelo", SP_NPC_Reelo },
619 { "npc_rodian", SP_NPC_Rodian },
620 { "npc_shadowtrooper", SP_NPC_ShadowTrooper },
621 { "npc_snowtrooper", SP_NPC_Snowtrooper },
622 { "npc_spawner", SP_NPC_spawner },
623 { "npc_stormtrooper", SP_NPC_Stormtrooper },
624 { "npc_stormtrooperofficer", SP_NPC_StormtrooperOfficer },
625 { "npc_swamptrooper", SP_NPC_SwampTrooper },
626 { "npc_tavion", SP_NPC_Tavion },
627 { "npc_tavion_new", SP_NPC_Tavion_New },
628 { "npc_tie_pilot", SP_NPC_Tie_Pilot },
629 { "npc_trandoshan", SP_NPC_Trandoshan },
630 { "npc_tusken", SP_NPC_Tusken },
631 { "npc_ugnaught", SP_NPC_Ugnaught },
632 { "npc_vehicle", SP_NPC_Vehicle },
633 { "npc_weequay", SP_NPC_Weequay },
634 { "path_corner", SP_path_corner },
635 { "point_combat", SP_point_combat },
636 { "ref_tag", SP_reference_tag },
637 { "ref_tag_huge", SP_reference_tag },
638 { "shooter_blaster", SP_shooter_blaster },
639 { "target_activate", SP_target_activate },
640 { "target_counter", SP_target_counter },
641 { "target_deactivate", SP_target_deactivate },
642 { "target_delay", SP_target_delay },
643 { "target_escapetrig", SP_target_escapetrig },
644 { "target_give", SP_target_give },
645 { "target_interest", SP_target_interest },
646 { "target_kill", SP_target_kill },
647 { "target_laser", SP_target_laser },
648 { "target_level_change", SP_target_level_change },
649 { "target_location", SP_target_location },
650 { "target_play_music", SP_target_play_music },
651 { "target_position", SP_target_position },
652 { "target_print", SP_target_print },
653 { "target_push", SP_target_push },
654 { "target_random", SP_target_random },
655 { "target_relay", SP_target_relay },
656 { "target_remove_powerups", SP_target_remove_powerups },
657 { "target_score", SP_target_score },
658 { "target_screenshake", SP_target_screenshake },
659 { "target_scriptrunner", SP_target_scriptrunner },
660 { "target_siege_end", SP_target_siege_end },
661 { "target_speaker", SP_target_speaker },
662 { "target_teleporter", SP_target_teleporter },
663 { "team_CTF_blueplayer", SP_team_CTF_blueplayer },
664 { "team_CTF_bluespawn", SP_team_CTF_bluespawn },
665 { "team_CTF_redplayer", SP_team_CTF_redplayer },
666 { "team_CTF_redspawn", SP_team_CTF_redspawn },
667 { "terrain", SP_terrain },
668 { "trigger_always", SP_trigger_always },
669 { "trigger_asteroid_field", SP_trigger_asteroid_field },
670 { "trigger_hurt", SP_trigger_hurt },
671 { "trigger_hyperspace", SP_trigger_hyperspace },
672 { "trigger_lightningstrike", SP_trigger_lightningstrike },
673 { "trigger_multiple", SP_trigger_multiple },
674 { "trigger_once", SP_trigger_once },
675 { "trigger_push", SP_trigger_push },
676 { "trigger_shipboundary", SP_trigger_shipboundary },
677 { "trigger_space", SP_trigger_space },
678 { "trigger_teleport", SP_trigger_teleport },
679 { "waypoint", SP_waypoint },
680 { "waypoint_navgoal", SP_waypoint_navgoal },
681 { "waypoint_navgoal_1", SP_waypoint_navgoal_1 },
682 { "waypoint_navgoal_2", SP_waypoint_navgoal_2 },
683 { "waypoint_navgoal_4", SP_waypoint_navgoal_4 },
684 { "waypoint_navgoal_8", SP_waypoint_navgoal_8 },
685 { "waypoint_small", SP_waypoint_small },
686 };
687
688 /*
689 ===============
690 G_CallSpawn
691
692 Finds the spawn function for the entity and calls it,
693 returning qfalse if not found
694 ===============
695 */
spawncmp(const void * a,const void * b)696 static int spawncmp( const void *a, const void *b ) {
697 return Q_stricmp( (const char *)a, ((spawn_t*)b)->name );
698 }
699
G_CallSpawn(gentity_t * ent)700 qboolean G_CallSpawn( gentity_t *ent ) {
701 spawn_t *s;
702 gitem_t *item;
703
704 if ( !ent->classname ) {
705 trap->Print( "G_CallSpawn: NULL classname\n" );
706 return qfalse;
707 }
708
709 // check item spawn functions
710 //TODO: cant reorder items because compat so....?
711 for ( item=bg_itemlist+1 ; item->classname ; item++ ) {
712 if ( !strcmp(item->classname, ent->classname) ) {
713 G_SpawnItem( ent, item );
714 return qtrue;
715 }
716 }
717
718 // check normal spawn functions
719 s = (spawn_t *)Q_LinearSearch( ent->classname, spawns, ARRAY_LEN( spawns ), sizeof( spawn_t ), spawncmp );
720 if ( s )
721 {// found it
722 if ( VALIDSTRING( ent->healingsound ) )
723 G_SoundIndex( ent->healingsound );
724
725 s->spawn( ent );
726 return qtrue;
727 }
728
729 trap->Print( "%s doesn't have a spawn function\n", ent->classname );
730 return qfalse;
731 }
732
733 /*
734 =============
735 G_NewString
736
737 Builds a copy of the string, translating \n to real linefeeds
738 so message texts can be multi-line
739 =============
740 */
G_NewString(const char * string)741 char *G_NewString( const char *string )
742 {
743 char *newb=NULL, *new_p=NULL;
744 int i=0, len=0;
745
746 len = strlen( string )+1;
747 new_p = newb = (char *)G_Alloc( len );
748
749 for ( i=0; i<len; i++ )
750 {// turn \n into a real linefeed
751 if ( string[i] == '\\' && i < len-1 )
752 {
753 if ( string[i+1] == 'n' )
754 {
755 *new_p++ = '\n';
756 i++;
757 }
758 else
759 *new_p++ = '\\';
760 }
761 else
762 *new_p++ = string[i];
763 }
764
765 return newb;
766 }
767
G_NewString_Safe(const char * string)768 char *G_NewString_Safe( const char *string )
769 {
770 char *newb=NULL, *new_p=NULL;
771 int i=0, len=0;
772
773 len = strlen( string )+1;
774 new_p = newb = (char *)malloc( len );
775
776 if ( !new_p )
777 return NULL;
778
779 for ( i=0; i<len; i++ )
780 {// turn \n into a real linefeed
781 if ( string[i] == '\\' && i < len-1 )
782 {
783 if ( string[i+1] == 'n' )
784 {
785 *new_p++ = '\n';
786 i++;
787 }
788 else
789 *new_p++ = '\\';
790 }
791 else
792 *new_p++ = string[i];
793 }
794
795 return newb;
796 }
797
798 /*
799 ===============
800 G_ParseField
801
802 Takes a key/value pair and sets the binary values
803 in a gentity
804 ===============
805 */
806
fieldcmp(const void * a,const void * b)807 static int fieldcmp( const void *a, const void *b ) {
808 return Q_stricmp( (const char *)a, ((field_t*)b)->name );
809 }
810
811 void Q3_SetParm ( int entID, int parmNum, const char *parmValue );
G_ParseField(const char * key,const char * value,gentity_t * ent)812 void G_ParseField( const char *key, const char *value, gentity_t *ent )
813 {
814 field_t *f;
815 byte *b;
816 float v;
817 vec3_t vec;
818
819 f = (field_t *)Q_LinearSearch( key, fields, ARRAY_LEN( fields ), sizeof( field_t ), fieldcmp );
820 if ( f )
821 {// found it
822 b = (byte *)ent;
823
824 switch( f->type ) {
825 case F_STRING:
826 *(char **)(b+f->ofs) = G_NewString (value);
827 break;
828 case F_VECTOR:
829 if ( sscanf( value, "%f %f %f", &vec[0], &vec[1], &vec[2] ) == 3 ) {
830 ((float *)(b+f->ofs))[0] = vec[0];
831 ((float *)(b+f->ofs))[1] = vec[1];
832 ((float *)(b+f->ofs))[2] = vec[2];
833 }
834 else {
835 trap->Print( "G_ParseField: Failed sscanf on F_VECTOR (key/value: %s/%s)\n", key, value );
836 ((float *)(b+f->ofs))[0] = ((float *)(b+f->ofs))[1] = ((float *)(b+f->ofs))[2] = 0.0f;
837 }
838 break;
839 case F_INT:
840 *(int *)(b+f->ofs) = atoi(value);
841 break;
842 case F_FLOAT:
843 *(float *)(b+f->ofs) = atof(value);
844 break;
845 case F_ANGLEHACK:
846 v = atof(value);
847 ((float *)(b+f->ofs))[0] = 0;
848 ((float *)(b+f->ofs))[1] = v;
849 ((float *)(b+f->ofs))[2] = 0;
850 break;
851 case F_PARM1:
852 case F_PARM2:
853 case F_PARM3:
854 case F_PARM4:
855 case F_PARM5:
856 case F_PARM6:
857 case F_PARM7:
858 case F_PARM8:
859 case F_PARM9:
860 case F_PARM10:
861 case F_PARM11:
862 case F_PARM12:
863 case F_PARM13:
864 case F_PARM14:
865 case F_PARM15:
866 case F_PARM16:
867 Q3_SetParm( ent->s.number, (f->type - F_PARM1), (char *) value );
868 break;
869 }
870 return;
871 }
872 }
873
874 #define ADJUST_AREAPORTAL() \
875 if(ent->s.eType == ET_MOVER) \
876 { \
877 trap->LinkEntity((sharedEntity_t *)ent); \
878 trap->AdjustAreaPortalState((sharedEntity_t *)ent, qtrue); \
879 }
880
881 /*
882 ===================
883 G_SpawnGEntityFromSpawnVars
884
885 Spawn an entity and fill in all of the level fields from
886 level.spawnVars[], then call the class specfic spawn function
887 ===================
888 */
G_SpawnGEntityFromSpawnVars(qboolean inSubBSP)889 void G_SpawnGEntityFromSpawnVars( qboolean inSubBSP ) {
890 int i;
891 gentity_t *ent;
892 char *s, *value, *gametypeName;
893 static char *gametypeNames[GT_MAX_GAME_TYPE] = {"ffa", "holocron", "jedimaster", "duel", "powerduel", "single", "team", "siege", "ctf", "cty"};
894
895 // get the next free entity
896 ent = G_Spawn();
897
898 for ( i = 0 ; i < level.numSpawnVars ; i++ ) {
899 G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], ent );
900 }
901
902 // check for "notsingle" flag
903 if ( level.gametype == GT_SINGLE_PLAYER ) {
904 G_SpawnInt( "notsingle", "0", &i );
905 if ( i ) {
906 ADJUST_AREAPORTAL();
907 G_FreeEntity( ent );
908 return;
909 }
910 }
911 // check for "notteam" flag (GT_FFA, GT_DUEL, GT_SINGLE_PLAYER)
912 if ( level.gametype >= GT_TEAM ) {
913 G_SpawnInt( "notteam", "0", &i );
914 if ( i ) {
915 ADJUST_AREAPORTAL();
916 G_FreeEntity( ent );
917 return;
918 }
919 } else {
920 G_SpawnInt( "notfree", "0", &i );
921 if ( i ) {
922 ADJUST_AREAPORTAL();
923 G_FreeEntity( ent );
924 return;
925 }
926 }
927
928 if( G_SpawnString( "gametype", NULL, &value ) ) {
929 if( level.gametype >= GT_FFA && level.gametype < GT_MAX_GAME_TYPE ) {
930 gametypeName = gametypeNames[level.gametype];
931
932 s = strstr( value, gametypeName );
933 if( !s ) {
934 ADJUST_AREAPORTAL();
935 G_FreeEntity( ent );
936 return;
937 }
938 }
939 }
940
941 // move editor origin to pos
942 VectorCopy( ent->s.origin, ent->s.pos.trBase );
943 VectorCopy( ent->s.origin, ent->r.currentOrigin );
944
945 // if we didn't get a classname, don't bother spawning anything
946 if ( !G_CallSpawn( ent ) ) {
947 G_FreeEntity( ent );
948 }
949
950 //Tag on the ICARUS scripting information only to valid recipients
951 if ( trap->ICARUS_ValidEnt( (sharedEntity_t *)ent ) )
952 {
953 trap->ICARUS_InitEnt( (sharedEntity_t *)ent );
954
955 if ( ent->classname && ent->classname[0] )
956 {
957 if ( Q_strncmp( "NPC_", ent->classname, 4 ) != 0 )
958 {//Not an NPC_spawner (rww - probably don't even care for MP, but whatever)
959 G_ActivateBehavior( ent, BSET_SPAWN );
960 }
961 }
962 }
963 }
964
965 /*
966 ====================
967 G_AddSpawnVarToken
968 ====================
969 */
G_AddSpawnVarToken(const char * string)970 char *G_AddSpawnVarToken( const char *string ) {
971 int l;
972 char *dest;
973
974 l = strlen( string );
975 if ( level.numSpawnVarChars + l + 1 > MAX_SPAWN_VARS_CHARS ) {
976 trap->Error( ERR_DROP, "G_AddSpawnVarToken: MAX_SPAWN_VARS_CHARS" );
977 }
978
979 dest = level.spawnVarChars + level.numSpawnVarChars;
980 memcpy( dest, string, l+1 );
981
982 level.numSpawnVarChars += l + 1;
983
984 return dest;
985 }
986
AddSpawnField(char * field,char * value)987 void AddSpawnField(char *field, char *value)
988 {
989 int i;
990
991 for(i=0;i<level.numSpawnVars;i++)
992 {
993 if (Q_stricmp(level.spawnVars[i][0], field) == 0)
994 {
995 level.spawnVars[ i ][1] = G_AddSpawnVarToken( value );
996 return;
997 }
998 }
999
1000 level.spawnVars[ level.numSpawnVars ][0] = G_AddSpawnVarToken( field );
1001 level.spawnVars[ level.numSpawnVars ][1] = G_AddSpawnVarToken( value );
1002 level.numSpawnVars++;
1003 }
1004
1005 #define NOVALUE "novalue"
1006
HandleEntityAdjustment(void)1007 static void HandleEntityAdjustment(void)
1008 {
1009 char *value;
1010 vec3_t origin, newOrigin, angles;
1011 char temp[MAX_QPATH];
1012 float rotation;
1013
1014 G_SpawnString("origin", NOVALUE, &value);
1015 if (Q_stricmp(value, NOVALUE) != 0)
1016 {
1017 if ( sscanf( value, "%f %f %f", &origin[0], &origin[1], &origin[2] ) != 3 ) {
1018 trap->Print( "HandleEntityAdjustment: failed sscanf on 'origin' (%s)\n", value );
1019 VectorClear( origin );
1020 }
1021 }
1022 else
1023 {
1024 origin[0] = origin[1] = origin[2] = 0.0;
1025 }
1026
1027 rotation = DEG2RAD(level.mRotationAdjust);
1028 newOrigin[0] = origin[0]*cos(rotation) - origin[1]*sin(rotation);
1029 newOrigin[1] = origin[0]*sin(rotation) + origin[1]*cos(rotation);
1030 newOrigin[2] = origin[2];
1031 VectorAdd(newOrigin, level.mOriginAdjust, newOrigin);
1032 // damn VMs don't handle outputing a float that is compatible with sscanf in all cases
1033 Com_sprintf(temp, sizeof( temp ), "%0.0f %0.0f %0.0f", newOrigin[0], newOrigin[1], newOrigin[2]);
1034 AddSpawnField("origin", temp);
1035
1036 G_SpawnString("angles", NOVALUE, &value);
1037 if (Q_stricmp(value, NOVALUE) != 0)
1038 {
1039 if ( sscanf( value, "%f %f %f", &angles[0], &angles[1], &angles[2] ) != 3 ) {
1040 trap->Print( "HandleEntityAdjustment: failed sscanf on 'angles' (%s)\n", value );
1041 VectorClear( angles );
1042 }
1043
1044 angles[YAW] = fmod(angles[YAW] + level.mRotationAdjust, 360.0f);
1045 // damn VMs don't handle outputing a float that is compatible with sscanf in all cases
1046 Com_sprintf(temp, sizeof( temp ), "%0.0f %0.0f %0.0f", angles[0], angles[1], angles[2]);
1047 AddSpawnField("angles", temp);
1048 }
1049 else
1050 {
1051 G_SpawnString("angle", NOVALUE, &value);
1052 if (Q_stricmp(value, NOVALUE) != 0)
1053 {
1054 angles[YAW] = atof( value );
1055 }
1056 else
1057 {
1058 angles[YAW] = 0.0;
1059 }
1060 angles[YAW] = fmod(angles[YAW] + level.mRotationAdjust, 360.0f);
1061 Com_sprintf(temp, sizeof( temp ), "%0.0f", angles[YAW]);
1062 AddSpawnField("angle", temp);
1063 }
1064
1065 // RJR experimental code for handling "direction" field of breakable brushes
1066 // though direction is rarely ever used.
1067 G_SpawnString("direction", NOVALUE, &value);
1068 if (Q_stricmp(value, NOVALUE) != 0)
1069 {
1070 if ( sscanf( value, "%f %f %f", &angles[0], &angles[1], &angles[2] ) != 3 ) {
1071 trap->Print( "HandleEntityAdjustment: failed sscanf on 'direction' (%s)\n", value );
1072 VectorClear( angles );
1073 }
1074 }
1075 else
1076 {
1077 angles[0] = angles[1] = angles[2] = 0.0;
1078 }
1079 angles[YAW] = fmod(angles[YAW] + level.mRotationAdjust, 360.0f);
1080 Com_sprintf(temp, sizeof( temp ), "%0.0f %0.0f %0.0f", angles[0], angles[1], angles[2]);
1081 AddSpawnField("direction", temp);
1082
1083
1084 AddSpawnField("BSPInstanceID", level.mTargetAdjust);
1085
1086 G_SpawnString("targetname", NOVALUE, &value);
1087 if (Q_stricmp(value, NOVALUE) != 0)
1088 {
1089 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1090 AddSpawnField("targetname", temp);
1091 }
1092
1093 G_SpawnString("target", NOVALUE, &value);
1094 if (Q_stricmp(value, NOVALUE) != 0)
1095 {
1096 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1097 AddSpawnField("target", temp);
1098 }
1099
1100 G_SpawnString("killtarget", NOVALUE, &value);
1101 if (Q_stricmp(value, NOVALUE) != 0)
1102 {
1103 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1104 AddSpawnField("killtarget", temp);
1105 }
1106
1107 G_SpawnString("brushparent", NOVALUE, &value);
1108 if (Q_stricmp(value, NOVALUE) != 0)
1109 {
1110 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1111 AddSpawnField("brushparent", temp);
1112 }
1113
1114 G_SpawnString("brushchild", NOVALUE, &value);
1115 if (Q_stricmp(value, NOVALUE) != 0)
1116 {
1117 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1118 AddSpawnField("brushchild", temp);
1119 }
1120
1121 G_SpawnString("enemy", NOVALUE, &value);
1122 if (Q_stricmp(value, NOVALUE) != 0)
1123 {
1124 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1125 AddSpawnField("enemy", temp);
1126 }
1127
1128 G_SpawnString("ICARUSname", NOVALUE, &value);
1129 if (Q_stricmp(value, NOVALUE) != 0)
1130 {
1131 Com_sprintf(temp, sizeof( temp ), "%s%s", level.mTargetAdjust, value);
1132 AddSpawnField("ICARUSname", temp);
1133 }
1134 }
1135
1136 /*
1137 ====================
1138 G_ParseSpawnVars
1139
1140 Parses a brace bounded set of key / value pairs out of the
1141 level's entity strings into level.spawnVars[]
1142
1143 This does not actually spawn an entity.
1144 ====================
1145 */
G_ParseSpawnVars(qboolean inSubBSP)1146 qboolean G_ParseSpawnVars( qboolean inSubBSP ) {
1147 char keyname[MAX_TOKEN_CHARS];
1148 char com_token[MAX_TOKEN_CHARS];
1149
1150 level.numSpawnVars = 0;
1151 level.numSpawnVarChars = 0;
1152
1153 // parse the opening brace
1154 if ( !trap->GetEntityToken( com_token, sizeof( com_token ) ) ) {
1155 // end of spawn string
1156 return qfalse;
1157 }
1158 if ( com_token[0] != '{' ) {
1159 trap->Error( ERR_DROP, "G_ParseSpawnVars: found %s when expecting {",com_token );
1160 }
1161
1162 // go through all the key / value pairs
1163 while ( 1 ) {
1164 // parse key
1165 if ( !trap->GetEntityToken( keyname, sizeof( keyname ) ) ) {
1166 trap->Error( ERR_DROP, "G_ParseSpawnVars: EOF without closing brace" );
1167 }
1168
1169 if ( keyname[0] == '}' ) {
1170 break;
1171 }
1172
1173 // parse value
1174 if ( !trap->GetEntityToken( com_token, sizeof( com_token ) ) ) {
1175 trap->Error( ERR_DROP, "G_ParseSpawnVars: EOF without closing brace" );
1176 }
1177
1178 if ( com_token[0] == '}' ) {
1179 trap->Error( ERR_DROP, "G_ParseSpawnVars: closing brace without data" );
1180 }
1181 if ( level.numSpawnVars == MAX_SPAWN_VARS ) {
1182 trap->Error( ERR_DROP, "G_ParseSpawnVars: MAX_SPAWN_VARS" );
1183 }
1184 level.spawnVars[ level.numSpawnVars ][0] = G_AddSpawnVarToken( keyname );
1185 level.spawnVars[ level.numSpawnVars ][1] = G_AddSpawnVarToken( com_token );
1186 level.numSpawnVars++;
1187 }
1188
1189 if (inSubBSP)
1190 {
1191 HandleEntityAdjustment();
1192 }
1193
1194 return qtrue;
1195 }
1196
1197
1198 static char *defaultStyles[32][3] =
1199 {
1200 { // 0 normal
1201 "z",
1202 "z",
1203 "z"
1204 },
1205 { // 1 FLICKER (first variety)
1206 "mmnmmommommnonmmonqnmmo",
1207 "mmnmmommommnonmmonqnmmo",
1208 "mmnmmommommnonmmonqnmmo"
1209 },
1210 { // 2 SLOW STRONG PULSE
1211 "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb",
1212 "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb",
1213 "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcb"
1214 },
1215 { // 3 CANDLE (first variety)
1216 "mmmmmaaaaammmmmaaaaaabcdefgabcdefg",
1217 "mmmmmaaaaammmmmaaaaaabcdefgabcdefg",
1218 "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"
1219 },
1220 { // 4 FAST STROBE
1221 "mamamamamama",
1222 "mamamamamama",
1223 "mamamamamama"
1224 },
1225 { // 5 GENTLE PULSE 1
1226 "jklmnopqrstuvwxyzyxwvutsrqponmlkj",
1227 "jklmnopqrstuvwxyzyxwvutsrqponmlkj",
1228 "jklmnopqrstuvwxyzyxwvutsrqponmlkj"
1229 },
1230 { // 6 FLICKER (second variety)
1231 "nmonqnmomnmomomno",
1232 "nmonqnmomnmomomno",
1233 "nmonqnmomnmomomno"
1234 },
1235 { // 7 CANDLE (second variety)
1236 "mmmaaaabcdefgmmmmaaaammmaamm",
1237 "mmmaaaabcdefgmmmmaaaammmaamm",
1238 "mmmaaaabcdefgmmmmaaaammmaamm"
1239 },
1240 { // 8 CANDLE (third variety)
1241 "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa",
1242 "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa",
1243 "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"
1244 },
1245 { // 9 SLOW STROBE (fourth variety)
1246 "aaaaaaaazzzzzzzz",
1247 "aaaaaaaazzzzzzzz",
1248 "aaaaaaaazzzzzzzz"
1249 },
1250 { // 10 FLUORESCENT FLICKER
1251 "mmamammmmammamamaaamammma",
1252 "mmamammmmammamamaaamammma",
1253 "mmamammmmammamamaaamammma"
1254 },
1255 { // 11 SLOW PULSE NOT FADE TO BLACK
1256 "abcdefghijklmnopqrrqponmlkjihgfedcba",
1257 "abcdefghijklmnopqrrqponmlkjihgfedcba",
1258 "abcdefghijklmnopqrrqponmlkjihgfedcba"
1259 },
1260 { // 12 FAST PULSE FOR JEREMY
1261 "mkigegik",
1262 "mkigegik",
1263 "mkigegik"
1264 },
1265 { // 13 Test Blending
1266 "abcdefghijklmqrstuvwxyz",
1267 "zyxwvutsrqmlkjihgfedcba",
1268 "aammbbzzccllcckkffyyggp"
1269 },
1270 { // 14
1271 "",
1272 "",
1273 ""
1274 },
1275 { // 15
1276 "",
1277 "",
1278 ""
1279 },
1280 { // 16
1281 "",
1282 "",
1283 ""
1284 },
1285 { // 17
1286 "",
1287 "",
1288 ""
1289 },
1290 { // 18
1291 "",
1292 "",
1293 ""
1294 },
1295 { // 19
1296 "",
1297 "",
1298 ""
1299 },
1300 { // 20
1301 "",
1302 "",
1303 ""
1304 },
1305 { // 21
1306 "",
1307 "",
1308 ""
1309 },
1310 { // 22
1311 "",
1312 "",
1313 ""
1314 },
1315 { // 23
1316 "",
1317 "",
1318 ""
1319 },
1320 { // 24
1321 "",
1322 "",
1323 ""
1324 },
1325 { // 25
1326 "",
1327 "",
1328 ""
1329 },
1330 { // 26
1331 "",
1332 "",
1333 ""
1334 },
1335 { // 27
1336 "",
1337 "",
1338 ""
1339 },
1340 { // 28
1341 "",
1342 "",
1343 ""
1344 },
1345 { // 29
1346 "",
1347 "",
1348 ""
1349 },
1350 { // 30
1351 "",
1352 "",
1353 ""
1354 },
1355 { // 31
1356 "",
1357 "",
1358 ""
1359 }
1360 };
1361
1362 void *precachedKyle = 0;
1363 void scriptrunner_run (gentity_t *self);
1364
1365 /*QUAKED worldspawn (0 0 0) ?
1366
1367 Every map should have exactly one worldspawn.
1368 "music" music wav file
1369 "gravity" 800 is default gravity
1370 "message" Text to print during connection process
1371
1372 BSP Options
1373 "gridsize" size of lighting grid to "X Y Z". default="64 64 128"
1374 "ambient" scale of global light (from _color)
1375 "fog" shader name of the global fog texture - must include the full path, such as "textures/rj/fog1"
1376 "distancecull" value for vis for the maximum viewing distance
1377 "chopsize" value for bsp on the maximum polygon / portal size
1378 "ls_Xr" override lightstyle X with this pattern for Red.
1379 "ls_Xg" green (valid patterns are "a-z")
1380 "ls_Xb" blue (a is OFF, z is ON)
1381
1382 "fogstart" override fog start distance and force linear
1383 "radarrange" for Siege/Vehicle radar - default range is 2500
1384 */
1385 extern void EWebPrecache(void); //g_items.c
1386 float g_cullDistance;
SP_worldspawn(void)1387 void SP_worldspawn( void )
1388 {
1389 char *text, temp[32];
1390 int i;
1391 int lengthRed, lengthBlue, lengthGreen;
1392
1393 //I want to "cull" entities out of net sends to clients to reduce
1394 //net traffic on our larger open maps -rww
1395 G_SpawnFloat("distanceCull", "6000.0", &g_cullDistance);
1396 trap->SetServerCull(g_cullDistance);
1397
1398 G_SpawnString( "classname", "", &text );
1399 if ( Q_stricmp( text, "worldspawn" ) ) {
1400 trap->Error( ERR_DROP, "SP_worldspawn: The first entity isn't 'worldspawn'" );
1401 }
1402
1403 for ( i = 0 ; i < level.numSpawnVars ; i++ )
1404 {
1405 if ( Q_stricmp( "spawnscript", level.spawnVars[i][0] ) == 0 )
1406 {//ONly let them set spawnscript, we don't want them setting an angle or something on the world.
1407 G_ParseField( level.spawnVars[i][0], level.spawnVars[i][1], &g_entities[ENTITYNUM_WORLD] );
1408 }
1409 }
1410 //The server will precache the standard model and animations, so that there is no hit
1411 //when the first client connnects.
1412 if (!BGPAFtextLoaded)
1413 {
1414 BG_ParseAnimationFile("models/players/_humanoid/animation.cfg", bgHumanoidAnimations, qtrue);
1415 }
1416
1417 if (!precachedKyle)
1418 {
1419 int defSkin;
1420
1421 trap->G2API_InitGhoul2Model(&precachedKyle, "models/players/" DEFAULT_MODEL "/model.glm", 0, 0, -20, 0, 0);
1422
1423 if (precachedKyle)
1424 {
1425 defSkin = trap->R_RegisterSkin("models/players/" DEFAULT_MODEL "/model_default.skin");
1426 trap->G2API_SetSkin(precachedKyle, 0, defSkin, defSkin);
1427 }
1428 }
1429
1430 if (!g2SaberInstance)
1431 {
1432 trap->G2API_InitGhoul2Model(&g2SaberInstance, DEFAULT_SABER_MODEL, 0, 0, -20, 0, 0);
1433
1434 if (g2SaberInstance)
1435 {
1436 // indicate we will be bolted to model 0 (ie the player) on bolt 0 (always the right hand) when we get copied
1437 trap->G2API_SetBoltInfo(g2SaberInstance, 0, 0);
1438 // now set up the gun bolt on it
1439 trap->G2API_AddBolt(g2SaberInstance, 0, "*blade1");
1440 }
1441 }
1442
1443 if (level.gametype == GT_SIEGE)
1444 { //a tad bit of a hack, but..
1445 EWebPrecache();
1446 }
1447
1448 // make some data visible to connecting client
1449 trap->SetConfigstring( CS_GAME_VERSION, GAME_VERSION );
1450
1451 trap->SetConfigstring( CS_LEVEL_START_TIME, va("%i", level.startTime ) );
1452
1453 G_SpawnString( "music", "", &text );
1454 trap->SetConfigstring( CS_MUSIC, text );
1455
1456 G_SpawnString( "message", "", &text );
1457 trap->SetConfigstring( CS_MESSAGE, text ); // map specific message
1458
1459 trap->SetConfigstring( CS_MOTD, g_motd.string ); // message of the day
1460
1461 G_SpawnString( "gravity", "800", &text );
1462 trap->Cvar_Set( "g_gravity", text );
1463 trap->Cvar_Update( &g_gravity );
1464
1465 G_SpawnString( "enableBreath", "0", &text );
1466
1467 G_SpawnString( "soundSet", "default", &text );
1468 trap->SetConfigstring( CS_GLOBAL_AMBIENT_SET, text );
1469
1470 g_entities[ENTITYNUM_WORLD].s.number = ENTITYNUM_WORLD;
1471 g_entities[ENTITYNUM_WORLD].r.ownerNum = ENTITYNUM_NONE;
1472 g_entities[ENTITYNUM_WORLD].classname = "worldspawn";
1473
1474 g_entities[ENTITYNUM_NONE].s.number = ENTITYNUM_NONE;
1475 g_entities[ENTITYNUM_NONE].r.ownerNum = ENTITYNUM_NONE;
1476 g_entities[ENTITYNUM_NONE].classname = "nothing";
1477
1478 // see if we want a warmup time
1479 trap->SetConfigstring( CS_WARMUP, "" );
1480 if ( g_restarted.integer ) {
1481 trap->Cvar_Set( "g_restarted", "0" );
1482 trap->Cvar_Update( &g_restarted );
1483 level.warmupTime = 0;
1484 }
1485 else if ( g_doWarmup.integer && level.gametype != GT_DUEL && level.gametype != GT_POWERDUEL && level.gametype != GT_SIEGE ) { // Turn it on
1486 level.warmupTime = -1;
1487 trap->SetConfigstring( CS_WARMUP, va("%i", level.warmupTime) );
1488 G_LogPrintf( "Warmup:\n" );
1489 }
1490
1491 trap->SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+0, defaultStyles[0][0]);
1492 trap->SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+1, defaultStyles[0][1]);
1493 trap->SetConfigstring(CS_LIGHT_STYLES+(LS_STYLES_START*3)+2, defaultStyles[0][2]);
1494
1495 for(i=1;i<LS_NUM_STYLES;i++)
1496 {
1497 Com_sprintf(temp, sizeof(temp), "ls_%dr", i);
1498 G_SpawnString(temp, defaultStyles[i][0], &text);
1499 lengthRed = strlen(text);
1500 trap->SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+0, text);
1501
1502 Com_sprintf(temp, sizeof(temp), "ls_%dg", i);
1503 G_SpawnString(temp, defaultStyles[i][1], &text);
1504 lengthGreen = strlen(text);
1505 trap->SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+1, text);
1506
1507 Com_sprintf(temp, sizeof(temp), "ls_%db", i);
1508 G_SpawnString(temp, defaultStyles[i][2], &text);
1509 lengthBlue = strlen(text);
1510 trap->SetConfigstring(CS_LIGHT_STYLES+((i+LS_STYLES_START)*3)+2, text);
1511
1512 if (lengthRed != lengthGreen || lengthGreen != lengthBlue)
1513 {
1514 Com_Error(ERR_DROP, "Style %d has inconsistent lengths: R %d, G %d, B %d",
1515 i, lengthRed, lengthGreen, lengthBlue);
1516 }
1517 }
1518 }
1519
1520 //rww - Planning on having something here?
SP_bsp_worldspawn(void)1521 qboolean SP_bsp_worldspawn ( void )
1522 {
1523 return qtrue;
1524 }
1525
G_PrecacheSoundsets(void)1526 void G_PrecacheSoundsets( void )
1527 {
1528 gentity_t *ent = NULL;
1529 int i;
1530 int countedSets = 0;
1531
1532 for ( i = 0; i < MAX_GENTITIES; i++ )
1533 {
1534 ent = &g_entities[i];
1535
1536 if (ent->inuse && ent->soundSet && ent->soundSet[0])
1537 {
1538 if (countedSets >= MAX_AMBIENT_SETS)
1539 {
1540 Com_Error(ERR_DROP, "MAX_AMBIENT_SETS was exceeded! (too many soundsets)\n");
1541 }
1542
1543 ent->s.soundSetIndex = G_SoundSetIndex(ent->soundSet);
1544 countedSets++;
1545 }
1546 }
1547 }
1548
G_LinkLocations(void)1549 void G_LinkLocations( void ) {
1550 int i, n;
1551
1552 if ( level.locations.linked )
1553 return;
1554
1555 level.locations.linked = qtrue;
1556
1557 trap->SetConfigstring( CS_LOCATIONS, "unknown" );
1558
1559 for ( i=0, n=1; i<level.locations.num; i++ ) {
1560 level.locations.data[i].cs_index = n;
1561 trap->SetConfigstring( CS_LOCATIONS + n, level.locations.data[i].message );
1562 n++;
1563 }
1564 // All linked together now
1565 }
1566
1567 /*
1568 ==============
1569 G_SpawnEntitiesFromString
1570
1571 Parses textual entity definitions out of an entstring and spawns gentities.
1572 ==============
1573 */
G_SpawnEntitiesFromString(qboolean inSubBSP)1574 void G_SpawnEntitiesFromString( qboolean inSubBSP ) {
1575 // allow calls to G_Spawn*()
1576 level.spawning = qtrue;
1577 level.numSpawnVars = 0;
1578
1579 // the worldspawn is not an actual entity, but it still
1580 // has a "spawn" function to perform any global setup
1581 // needed by a level (setting configstrings or cvars, etc)
1582 if ( !G_ParseSpawnVars(qfalse) ) {
1583 trap->Error( ERR_DROP, "SpawnEntities: no entities" );
1584 }
1585
1586 if (!inSubBSP)
1587 {
1588 SP_worldspawn();
1589 }
1590 else
1591 {
1592 // Skip this guy if its worldspawn fails
1593 if ( !SP_bsp_worldspawn() )
1594 {
1595 return;
1596 }
1597 }
1598
1599 // parse ents
1600 while( G_ParseSpawnVars(inSubBSP) ) {
1601 G_SpawnGEntityFromSpawnVars(inSubBSP);
1602 }
1603
1604 if( g_entities[ENTITYNUM_WORLD].behaviorSet[BSET_SPAWN] && g_entities[ENTITYNUM_WORLD].behaviorSet[BSET_SPAWN][0] )
1605 {//World has a spawn script, but we don't want the world in ICARUS and running scripts,
1606 //so make a scriptrunner and start it going.
1607 gentity_t *script_runner = G_Spawn();
1608 if ( script_runner )
1609 {
1610 script_runner->behaviorSet[BSET_USE] = g_entities[ENTITYNUM_WORLD].behaviorSet[BSET_SPAWN];
1611 script_runner->count = 1;
1612 script_runner->think = scriptrunner_run;
1613 script_runner->nextthink = level.time + 100;
1614
1615 if ( script_runner->inuse )
1616 {
1617 trap->ICARUS_InitEnt( (sharedEntity_t *)script_runner );
1618 }
1619 }
1620 }
1621
1622 if (!inSubBSP)
1623 {
1624 level.spawning = qfalse; // any future calls to G_Spawn*() will be errors
1625 }
1626
1627 G_LinkLocations();
1628
1629 G_PrecacheSoundsets();
1630 }
1631
1632