PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/codemp/game/g_misc.c

https://github.com/Stoiss/jaMME
C | 3661 lines | 2211 code | 579 blank | 871 comment | 403 complexity | 026239744d1ac742a3b4818d6d229b2e MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // g_misc.c
  4. #include "g_local.h"
  5. #include "ghoul2/G2.h"
  6. #include "ai_main.h" //for the g2animents
  7. #define HOLOCRON_RESPAWN_TIME 30000
  8. #define MAX_AMMO_GIVE 2
  9. #define STATION_RECHARGE_TIME 100
  10. void HolocronThink(gentity_t *ent);
  11. /*QUAKED func_group (0 0 0) ?
  12. Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
  13. */
  14. /*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
  15. Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
  16. */
  17. void SP_info_camp( gentity_t *self ) {
  18. G_SetOrigin( self, self->s.origin );
  19. }
  20. /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
  21. Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
  22. */
  23. void SP_info_null( gentity_t *self ) {
  24. G_FreeEntity( self );
  25. }
  26. /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
  27. Used as a positional target for in-game calculation, like jumppad targets.
  28. target_position does the same thing
  29. */
  30. void SP_info_notnull( gentity_t *self ){
  31. G_SetOrigin( self, self->s.origin );
  32. }
  33. /*QUAKED lightJunior (0 0.7 0.3) (-8 -8 -8) (8 8 8) nonlinear angle negative_spot negative_point
  34. Non-displayed light that only affects dynamic game models, but does not contribute to lightmaps
  35. "light" overrides the default 300 intensity.
  36. Nonlinear checkbox gives inverse square falloff instead of linear
  37. Angle adds light:surface angle calculations (only valid for "Linear" lights) (wolf)
  38. Lights pointed at a target will be spotlights.
  39. "radius" overrides the default 64 unit radius of a spotlight at the target point.
  40. "fade" falloff/radius adjustment value. multiply the run of the slope by "fade" (1.0f default) (only valid for "Linear" lights) (wolf)
  41. */
  42. /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear noIncidence START_OFF
  43. Non-displayed light.
  44. "light" overrides the default 300 intensity. - affects size
  45. a negative "light" will subtract the light's color
  46. 'Linear' checkbox gives linear falloff instead of inverse square
  47. 'noIncidence' checkbox makes lighting smoother
  48. Lights pointed at a target will be spotlights.
  49. "radius" overrides the default 64 unit radius of a spotlight at the target point.
  50. "scale" multiplier for the light intensity - does not affect size (default 1)
  51. greater than 1 is brighter, between 0 and 1 is dimmer.
  52. "color" sets the light's color
  53. "targetname" to indicate a switchable light - NOTE that all lights with the same targetname will be grouped together and act as one light (ie: don't mix colors, styles or start_off flag)
  54. "style" to specify a specify light style, even for switchable lights!
  55. "style_off" light style to use when switched off (Only for switchable lights)
  56. 1 FLICKER (first variety)
  57. 2 SLOW STRONG PULSE
  58. 3 CANDLE (first variety)
  59. 4 FAST STROBE
  60. 5 GENTLE PULSE 1
  61. 6 FLICKER (second variety)
  62. 7 CANDLE (second variety)
  63. 8 CANDLE (third variety)
  64. 9 SLOW STROBE (fourth variety)
  65. 10 FLUORESCENT FLICKER
  66. 11 SLOW PULSE NOT FADE TO BLACK
  67. 12 FAST PULSE FOR JEREMY
  68. 13 Test Blending
  69. */
  70. static void misc_lightstyle_set ( gentity_t *ent)
  71. {
  72. const int mLightStyle = ent->count;
  73. const int mLightSwitchStyle = ent->bounceCount;
  74. const int mLightOffStyle = ent->fly_sound_debounce_time;
  75. if (!ent->alt_fire)
  76. { //turn off
  77. if (mLightOffStyle) //i have a light style i'd like to use when off
  78. {
  79. char lightstyle[32];
  80. trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+0, lightstyle, 32);
  81. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
  82. trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+1, lightstyle, 32);
  83. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
  84. trap_GetConfigstring(CS_LIGHT_STYLES + (mLightOffStyle*3)+2, lightstyle, 32);
  85. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
  86. }else
  87. {
  88. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "a");
  89. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "a");
  90. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "a");
  91. }
  92. }
  93. else
  94. { //Turn myself on now
  95. if (mLightSwitchStyle) //i have a light style i'd like to use when on
  96. {
  97. char lightstyle[32];
  98. trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+0, lightstyle, 32);
  99. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, lightstyle);
  100. trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+1, lightstyle, 32);
  101. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, lightstyle);
  102. trap_GetConfigstring(CS_LIGHT_STYLES + (mLightSwitchStyle*3)+2, lightstyle, 32);
  103. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, lightstyle);
  104. }
  105. else
  106. {
  107. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+0, "z");
  108. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+1, "z");
  109. trap_SetConfigstring(CS_LIGHT_STYLES + (mLightStyle*3)+2, "z");
  110. }
  111. }
  112. }
  113. void misc_dlight_use ( gentity_t *ent, gentity_t *other, gentity_t *activator )
  114. {
  115. G_ActivateBehavior(ent,BSET_USE);
  116. ent->alt_fire = !ent->alt_fire; //toggle
  117. misc_lightstyle_set (ent);
  118. }
  119. void SP_light( gentity_t *self ) {
  120. if (!self->targetname )
  121. {//if i don't have a light style switch, the i go away
  122. G_FreeEntity( self );
  123. return;
  124. }
  125. G_SpawnInt( "style", "0", &self->count );
  126. G_SpawnInt( "switch_style", "0", &self->bounceCount );
  127. G_SpawnInt( "style_off", "0", &self->fly_sound_debounce_time );
  128. G_SetOrigin( self, self->s.origin );
  129. trap_LinkEntity( self );
  130. self->use = misc_dlight_use;
  131. self->s.eType = ET_GENERAL;
  132. self->alt_fire = qfalse;
  133. self->r.svFlags |= SVF_NOCLIENT;
  134. if ( !(self->spawnflags & 4) )
  135. { //turn myself on now
  136. self->alt_fire = qtrue;
  137. }
  138. misc_lightstyle_set (self);
  139. }
  140. /*
  141. =================================================================================
  142. TELEPORTERS
  143. =================================================================================
  144. */
  145. void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
  146. gentity_t *tent;
  147. qboolean isNPC = qfalse;
  148. if (player->s.eType == ET_NPC)
  149. {
  150. isNPC = qtrue;
  151. }
  152. // use temp events at source and destination to prevent the effect
  153. // from getting dropped by a second player event
  154. if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  155. tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
  156. tent->s.clientNum = player->s.clientNum;
  157. tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
  158. tent->s.clientNum = player->s.clientNum;
  159. }
  160. // unlink to make sure it can't possibly interfere with G_KillBox
  161. trap_UnlinkEntity (player);
  162. VectorCopy ( origin, player->client->ps.origin );
  163. player->client->ps.origin[2] += 1;
  164. // spit the player out
  165. AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
  166. VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
  167. player->client->ps.pm_time = 160; // hold time
  168. player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  169. // toggle the teleport bit so the client knows to not lerp
  170. player->client->ps.eFlags ^= EF_TELEPORT_BIT;
  171. // set angles
  172. SetClientViewAngle( player, angles );
  173. // kill anything at the destination
  174. if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  175. G_KillBox (player);
  176. }
  177. // save results of pmove
  178. BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
  179. if (isNPC)
  180. {
  181. player->s.eType = ET_NPC;
  182. }
  183. // use the precise origin for linking
  184. VectorCopy( player->client->ps.origin, player->r.currentOrigin );
  185. if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  186. trap_LinkEntity (player);
  187. }
  188. }
  189. /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
  190. Point teleporters at these.
  191. Now that we don't have teleport destination pads, this is just
  192. an info_notnull
  193. */
  194. void SP_misc_teleporter_dest( gentity_t *ent ) {
  195. }
  196. //===========================================================
  197. /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
  198. "model" arbitrary .md3 or .ase file to display
  199. turns into map triangles - not solid
  200. */
  201. void SP_misc_model( gentity_t *ent ) {
  202. #if 0
  203. ent->s.modelindex = G_ModelIndex( ent->model );
  204. VectorSet (ent->r.mins, -16, -16, -16);
  205. VectorSet (ent->r.maxs, 16, 16, 16);
  206. trap_LinkEntity (ent);
  207. G_SetOrigin( ent, ent->s.origin );
  208. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  209. #else
  210. G_FreeEntity( ent );
  211. #endif
  212. }
  213. /*QUAKED misc_model_static (1 0 0) (-16 -16 0) (16 16 16)
  214. "model" arbitrary .md3 file to display
  215. "zoffset" units to offset vertical culling position by, can be
  216. negative or positive. This does not affect the actual
  217. position of the model, only the culling position. Use
  218. it for models with stupid origins that go below the
  219. ground and whatnot.
  220. "modelscale" scale on all axis
  221. "modelscale_vec" scale difference axis
  222. loaded as a model in the renderer - does not take up precious
  223. bsp space!
  224. */
  225. void SP_misc_model_static(gentity_t *ent)
  226. {
  227. G_FreeEntity( ent );
  228. }
  229. /*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION
  230. SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health
  231. AUTOANIMATE - Will cycle it's anim
  232. DEADSOLID - Stay solid even when destroyed (in case damage model is rather large).
  233. NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists
  234. USE_MODEL - When used, will toggle to it's usemodel (model name + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked
  235. USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage
  236. PLAYER_USE - Player can use it with the use button
  237. NO_EXPLOSION - By default, will explode when it dies...this is your override.
  238. "model" arbitrary .md3 file to display
  239. "health" how much health to have - default is zero (not breakable) If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving
  240. "targetname" when used, dies and displays damagemodel, if any (if not, removes itself)
  241. "target" What to use when it dies
  242. "target2" What to use when it's repaired
  243. "target3" What to use when it's used while it's broken
  244. "paintarget" target to fire when hit (but not destroyed)
  245. "count" the amount of armor/health/ammo given (default 50)
  246. "gravity" if set to 1, this will be affected by gravity
  247. "radius" Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1) (.5) is half as many chunks, (2) is twice as many chunks
  248. Damage: default is none
  249. "splashDamage" - damage to do (will make it explode on death)
  250. "splashRadius" - radius for above damage
  251. "team" - This cannot take damage from members of this team:
  252. "player"
  253. "neutral"
  254. "enemy"
  255. "material" - default is "8 - MAT_NONE" - choose from this list:
  256. 0 = MAT_METAL (grey metal)
  257. 1 = MAT_GLASS
  258. 2 = MAT_ELECTRICAL (sparks only)
  259. 3 = MAT_ELEC_METAL (METAL chunks and sparks)
  260. 4 = MAT_DRK_STONE (brown stone chunks)
  261. 5 = MAT_LT_STONE (tan stone chunks)
  262. 6 = MAT_GLASS_METAL (glass and METAL chunks)
  263. 7 = MAT_METAL2 (blue/grey metal)
  264. 8 = MAT_NONE (no chunks-DEFAULT)
  265. 9 = MAT_GREY_STONE (grey colored stone)
  266. 10 = MAT_METAL3 (METAL and METAL2 chunk combo)
  267. 11 = MAT_CRATE1 (yellow multi-colored crate chunks)
  268. 12 = MAT_GRATE1 (grate chunks--looks horrible right now)
  269. 13 = MAT_ROPE (for yavin_trial, no chunks, just wispy bits )
  270. 14 = MAT_CRATE2 (red multi-colored crate chunks)
  271. 15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout )
  272. FIXME/TODO:
  273. set size better?
  274. multiple damage models?
  275. custom explosion effect/sound?
  276. */
  277. void misc_model_breakable_gravity_init( gentity_t *ent, qboolean dropToFloor );
  278. void misc_model_breakable_init( gentity_t *ent );
  279. void SP_misc_model_breakable( gentity_t *ent )
  280. {
  281. float grav;
  282. G_SpawnInt( "material", "8", (int*)&ent->material );
  283. G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer
  284. misc_model_breakable_init( ent );
  285. if ( !ent->r.mins[0] && !ent->r.mins[1] && !ent->r.mins[2] )
  286. {
  287. VectorSet (ent->r.mins, -16, -16, -16);
  288. }
  289. if ( !ent->r.maxs[0] && !ent->r.maxs[1] && !ent->r.maxs[2] )
  290. {
  291. VectorSet (ent->r.maxs, 16, 16, 16);
  292. }
  293. G_SetOrigin( ent, ent->s.origin );
  294. G_SetAngles( ent, ent->s.angles );
  295. trap_LinkEntity (ent);
  296. if ( ent->spawnflags & 128 )
  297. {//Can be used by the player's BUTTON_USE
  298. ent->r.svFlags |= SVF_PLAYER_USABLE;
  299. }
  300. ent->s.teamowner = 0;
  301. G_SpawnFloat( "gravity", "0", &grav );
  302. if ( grav )
  303. {//affected by gravity
  304. G_SetAngles( ent, ent->s.angles );
  305. G_SetOrigin( ent, ent->r.currentOrigin );
  306. misc_model_breakable_gravity_init( ent, qtrue );
  307. }
  308. }
  309. void misc_model_breakable_gravity_init( gentity_t *ent, qboolean dropToFloor )
  310. {
  311. trace_t tr;
  312. vec3_t top, bottom;
  313. ent->s.eType = ET_GENERAL;
  314. //ent->s.eFlags |= EF_BOUNCE_HALF; // FIXME
  315. ent->clipmask = MASK_SOLID|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//?
  316. ent->physicsBounce = ent->mass = VectorLength( ent->r.maxs ) + VectorLength( ent->r.mins );
  317. //drop to floor
  318. if ( dropToFloor )
  319. {
  320. VectorCopy( ent->r.currentOrigin, top );
  321. top[2] += 1;
  322. VectorCopy( ent->r.currentOrigin, bottom );
  323. bottom[2] = MIN_WORLD_COORD;
  324. trap_Trace( &tr, top, ent->r.mins, ent->r.maxs, bottom, ent->s.number, MASK_NPCSOLID );
  325. if ( !tr.allsolid && !tr.startsolid && tr.fraction < 1.0 )
  326. {
  327. G_SetOrigin( ent, tr.endpos );
  328. trap_LinkEntity( ent );
  329. }
  330. }
  331. else
  332. {
  333. G_SetOrigin( ent, ent->r.currentOrigin );
  334. trap_LinkEntity( ent );
  335. }
  336. //set up for object thinking
  337. if ( VectorCompare( ent->s.pos.trDelta, vec3_origin ) )
  338. {//not moving
  339. ent->s.pos.trType = TR_STATIONARY;
  340. }
  341. else
  342. {
  343. ent->s.pos.trType = TR_GRAVITY;
  344. }
  345. VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
  346. VectorClear( ent->s.pos.trDelta );
  347. ent->s.pos.trTime = level.time;
  348. if ( VectorCompare( ent->s.apos.trDelta, vec3_origin ) )
  349. {//not moving
  350. ent->s.apos.trType = TR_STATIONARY;
  351. }
  352. else
  353. {
  354. ent->s.apos.trType = TR_LINEAR;
  355. }
  356. VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
  357. VectorClear( ent->s.apos.trDelta );
  358. ent->s.apos.trTime = level.time;
  359. }
  360. void misc_model_breakable_init( gentity_t *ent )
  361. {
  362. if (!ent->model) {
  363. G_Error("no model set on %s at (%.1f %.1f %.1f)\n", ent->classname, ent->s.origin[0],ent->s.origin[1],ent->s.origin[2]);
  364. }
  365. //Main model
  366. ent->s.modelindex = ent->sound2to1 = G_ModelIndex( ent->model );
  367. if ( ent->spawnflags & 1 )
  368. {//Blocks movement
  369. ent->r.contents = CONTENTS_SOLID|CONTENTS_OPAQUE|CONTENTS_BODY|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//Was CONTENTS_SOLID, but only architecture should be this
  370. }
  371. else if ( ent->health )
  372. {//Can only be shot
  373. ent->r.contents = CONTENTS_SHOTCLIP;
  374. }
  375. // TODO: fix using
  376. // TODO: fix health/dying funcs
  377. }
  378. /*QUAKED misc_G2model (1 0 0) (-16 -16 -16) (16 16 16)
  379. "model" arbitrary .glm file to display
  380. */
  381. void SP_misc_G2model( gentity_t *ent ) {
  382. #if 0
  383. char name1[200] = "models/players/kyle/modelmp.glm";
  384. trap_G2API_InitGhoul2Model(&ent->s, name1, G_ModelIndex( name1 ), 0, 0, 0, 0);
  385. trap_G2API_SetBoneAnim(ent->s.ghoul2, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, level.time, -1, -1);
  386. ent->s.radius = 150;
  387. // VectorSet (ent->r.mins, -16, -16, -16);
  388. // VectorSet (ent->r.maxs, 16, 16, 16);
  389. trap_LinkEntity (ent);
  390. G_SetOrigin( ent, ent->s.origin );
  391. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  392. #else
  393. G_FreeEntity( ent );
  394. #endif
  395. }
  396. //===========================================================
  397. void locateCamera( gentity_t *ent ) {
  398. vec3_t dir;
  399. gentity_t *target;
  400. gentity_t *owner;
  401. owner = G_PickTarget( ent->target );
  402. if ( !owner ) {
  403. G_Printf( "Couldn't find target for misc_partal_surface\n" );
  404. G_FreeEntity( ent );
  405. return;
  406. }
  407. ent->r.ownerNum = owner->s.number;
  408. // frame holds the rotate speed
  409. if ( owner->spawnflags & 1 ) {
  410. ent->s.frame = 25;
  411. } else if ( owner->spawnflags & 2 ) {
  412. ent->s.frame = 75;
  413. }
  414. // swing camera ?
  415. if ( owner->spawnflags & 4 ) {
  416. // set to 0 for no rotation at all
  417. ent->s.powerups = 0;
  418. }
  419. else {
  420. ent->s.powerups = 1;
  421. }
  422. // clientNum holds the rotate offset
  423. ent->s.clientNum = owner->s.clientNum;
  424. VectorCopy( owner->s.origin, ent->s.origin2 );
  425. // see if the portal_camera has a target
  426. target = G_PickTarget( owner->target );
  427. if ( target ) {
  428. VectorSubtract( target->s.origin, owner->s.origin, dir );
  429. VectorNormalize( dir );
  430. } else {
  431. G_SetMovedir( owner->s.angles, dir );
  432. }
  433. ent->s.eventParm = DirToByte( dir );
  434. }
  435. /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
  436. The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
  437. This must be within 64 world units of the surface!
  438. */
  439. void SP_misc_portal_surface(gentity_t *ent) {
  440. VectorClear( ent->r.mins );
  441. VectorClear( ent->r.maxs );
  442. trap_LinkEntity (ent);
  443. ent->r.svFlags = SVF_PORTAL;
  444. ent->s.eType = ET_PORTAL;
  445. if ( !ent->target ) {
  446. VectorCopy( ent->s.origin, ent->s.origin2 );
  447. } else {
  448. ent->think = locateCamera;
  449. ent->nextthink = level.time + 100;
  450. }
  451. }
  452. /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
  453. The target for a misc_portal_director. You can set either angles or target another entity to determine the direction of view.
  454. "roll" an angle modifier to orient the camera around the target vector;
  455. */
  456. void SP_misc_portal_camera(gentity_t *ent) {
  457. float roll;
  458. VectorClear( ent->r.mins );
  459. VectorClear( ent->r.maxs );
  460. trap_LinkEntity (ent);
  461. G_SpawnFloat( "roll", "0", &roll );
  462. ent->s.clientNum = roll/360.0 * 256;
  463. }
  464. /*QUAKED misc_bsp (1 0 0) (-16 -16 -16) (16 16 16)
  465. "bspmodel" arbitrary .bsp file to display
  466. */
  467. void SP_misc_bsp(gentity_t *ent)
  468. {
  469. char temp[MAX_QPATH];
  470. char *out;
  471. float newAngle;
  472. int tempint;
  473. G_SpawnFloat( "angle", "0", &newAngle );
  474. if (newAngle != 0.0)
  475. {
  476. ent->s.angles[1] = newAngle;
  477. }
  478. // don't support rotation any other way
  479. ent->s.angles[0] = 0.0;
  480. ent->s.angles[2] = 0.0;
  481. G_SpawnString("bspmodel", "", &out);
  482. ent->s.eFlags = EF_PERMANENT;
  483. // Mainly for debugging
  484. G_SpawnInt( "spacing", "0", &tempint);
  485. ent->s.time2 = tempint;
  486. G_SpawnInt( "flatten", "0", &tempint);
  487. ent->s.time = tempint;
  488. Com_sprintf(temp, MAX_QPATH, "#%s", out);
  489. trap_SetBrushModel( ent, temp ); // SV_SetBrushModel -- sets mins and maxs
  490. G_BSPIndex(temp);
  491. level.mNumBSPInstances++;
  492. Com_sprintf(temp, MAX_QPATH, "%d-", level.mNumBSPInstances);
  493. VectorCopy(ent->s.origin, level.mOriginAdjust);
  494. level.mRotationAdjust = ent->s.angles[1];
  495. level.mTargetAdjust = temp;
  496. //level.hasBspInstances = qtrue; //rww - also not referenced anywhere.
  497. level.mBSPInstanceDepth++;
  498. /*
  499. G_SpawnString("filter", "", &out);
  500. strcpy(level.mFilter, out);
  501. */
  502. G_SpawnString("teamfilter", "", &out);
  503. strcpy(level.mTeamFilter, out);
  504. VectorCopy( ent->s.origin, ent->s.pos.trBase );
  505. VectorCopy( ent->s.origin, ent->r.currentOrigin );
  506. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  507. VectorCopy( ent->s.angles, ent->r.currentAngles );
  508. ent->s.eType = ET_MOVER;
  509. trap_LinkEntity (ent);
  510. trap_SetActiveSubBSP(ent->s.modelindex);
  511. G_SpawnEntitiesFromString(qtrue);
  512. trap_SetActiveSubBSP(-1);
  513. level.mBSPInstanceDepth--;
  514. //level.mFilter[0] = level.mTeamFilter[0] = 0;
  515. level.mTeamFilter[0] = 0;
  516. /*
  517. if ( g_debugRMG.integer )
  518. {
  519. G_SpawnDebugCylinder ( ent->s.origin, ent->s.time2, &g_entities[0], 2000, COLOR_WHITE );
  520. if ( ent->s.time )
  521. {
  522. G_SpawnDebugCylinder ( ent->s.origin, ent->s.time, &g_entities[0], 2000, COLOR_RED );
  523. }
  524. }
  525. */
  526. }
  527. /*QUAKED terrain (1.0 1.0 1.0) ? NOVEHDMG
  528. NOVEHDMG - don't damage vehicles upon impact with this terrain
  529. Terrain entity
  530. It will stretch to the full height of the brush
  531. numPatches - integer number of patches to split the terrain brush into (default 200)
  532. terxels - integer number of terxels on a patch side (default 4) (2 <= count <= 8)
  533. seed - integer seed for random terrain generation (default 0)
  534. textureScale - float scale of texture (default 0.005)
  535. heightmap - name of heightmap data image to use, located in heightmaps/*.png. (must be PNG format)
  536. terrainDef - defines how the game textures the terrain (file is base/ext_data/rmg/*.terrain - default is grassyhills)
  537. instanceDef - defines which bsp instances appear
  538. miscentDef - defines which client models spawn on the terrain (file is base/ext_data/rmg/*.miscents)
  539. densityMap - how dense the client models are packed
  540. */
  541. void AddSpawnField(char *field, char *value);
  542. #define MAX_INSTANCE_TYPES 16
  543. void SP_terrain(gentity_t *ent)
  544. {
  545. char temp[MAX_INFO_STRING];
  546. char final[MAX_QPATH];
  547. char seed[MAX_QPATH];
  548. char missionType[MAX_QPATH];
  549. //char soundSet[MAX_QPATH];
  550. int shaderNum, i;
  551. char *value;
  552. int terrainID;
  553. //Force it to 1 when there is terrain on the level.
  554. trap_Cvar_Set("RMG", "1");
  555. RMG.integer = 1;
  556. VectorClear (ent->s.angles);
  557. trap_SetBrushModel( ent, ent->model );
  558. // Get the shader from the top of the brush
  559. // shaderNum = gi.CM_GetShaderNum(s.modelindex);
  560. shaderNum = 0;
  561. if (RMG.integer)
  562. {
  563. /*
  564. // Grab the default terrain file from the RMG cvar
  565. trap_Cvar_VariableStringBuffer("RMG_terrain", temp, MAX_QPATH);
  566. Com_sprintf(final, MAX_QPATH, "%s", temp);
  567. AddSpawnField("terrainDef", temp);
  568. trap_Cvar_VariableStringBuffer("RMG_instances", temp, MAX_QPATH);
  569. Com_sprintf(final, MAX_QPATH, "%s", temp);
  570. AddSpawnField("instanceDef", temp);
  571. trap_Cvar_VariableStringBuffer("RMG_miscents", temp, MAX_QPATH);
  572. Com_sprintf(final, MAX_QPATH, "%s", temp);
  573. AddSpawnField("miscentDef", temp);
  574. */
  575. //rww - disabled for now, don't want cvar overrides.
  576. trap_Cvar_VariableStringBuffer("RMG_seed", seed, MAX_QPATH);
  577. trap_Cvar_VariableStringBuffer("RMG_mission", missionType, MAX_QPATH);
  578. //rww - May want to implement these at some point.
  579. //trap_Cvar_VariableStringBuffer("RMG_soundset", soundSet, MAX_QPATH);
  580. //trap_SetConfigstring(CS_AMBIENT_SOUNDSETS, soundSet );
  581. }
  582. // Get info required for the common init
  583. temp[0] = 0;
  584. G_SpawnString("heightmap", "", &value);
  585. Info_SetValueForKey(temp, "heightMap", value);
  586. G_SpawnString("numpatches", "400", &value);
  587. Info_SetValueForKey(temp, "numPatches", va("%d", atoi(value)));
  588. G_SpawnString("terxels", "4", &value);
  589. Info_SetValueForKey(temp, "terxels", va("%d", atoi(value)));
  590. Info_SetValueForKey(temp, "seed", seed);
  591. Info_SetValueForKey(temp, "minx", va("%f", ent->r.mins[0]));
  592. Info_SetValueForKey(temp, "miny", va("%f", ent->r.mins[1]));
  593. Info_SetValueForKey(temp, "minz", va("%f", ent->r.mins[2]));
  594. Info_SetValueForKey(temp, "maxx", va("%f", ent->r.maxs[0]));
  595. Info_SetValueForKey(temp, "maxy", va("%f", ent->r.maxs[1]));
  596. Info_SetValueForKey(temp, "maxz", va("%f", ent->r.maxs[2]));
  597. Info_SetValueForKey(temp, "modelIndex", va("%d", ent->s.modelindex));
  598. G_SpawnString("terraindef", "grassyhills", &value);
  599. Info_SetValueForKey(temp, "terrainDef", value);
  600. G_SpawnString("instancedef", "", &value);
  601. Info_SetValueForKey(temp, "instanceDef", value);
  602. G_SpawnString("miscentdef", "", &value);
  603. Info_SetValueForKey(temp, "miscentDef", value);
  604. Info_SetValueForKey(temp, "missionType", missionType);
  605. for(i = 0; i < MAX_INSTANCE_TYPES; i++)
  606. {
  607. trap_Cvar_VariableStringBuffer(va("RMG_instance%d", i), final, MAX_QPATH);
  608. if(strlen(final))
  609. {
  610. Info_SetValueForKey(temp, va("inst%d", i), final);
  611. }
  612. }
  613. // Set additional data required on the client only
  614. G_SpawnString("densitymap", "", &value);
  615. Info_SetValueForKey(temp, "densityMap", value);
  616. Info_SetValueForKey(temp, "shader", va("%d", shaderNum));
  617. G_SpawnString("texturescale", "0.005", &value);
  618. Info_SetValueForKey(temp, "texturescale", va("%f", atof(value)));
  619. // Initialise the common aspects of the terrain
  620. terrainID = trap_CM_RegisterTerrain(temp);
  621. // SetCommon(common);
  622. Info_SetValueForKey(temp, "terrainId", va("%d", terrainID));
  623. // Let the entity know if it is random generated or not
  624. // SetIsRandom(common->GetIsRandom());
  625. // Let the game remember everything
  626. //level.landScapes[terrainID] = ent; //rww - also not referenced
  627. // Send all the data down to the client
  628. trap_SetConfigstring(CS_TERRAINS + terrainID, temp);
  629. // Make sure the contents are properly set
  630. ent->r.contents = CONTENTS_TERRAIN;
  631. ent->r.svFlags = SVF_NOCLIENT;
  632. ent->s.eFlags = EF_PERMANENT;
  633. ent->s.eType = ET_TERRAIN;
  634. // Hook into the world so physics will work
  635. trap_LinkEntity(ent);
  636. // If running RMG then initialize the terrain and handle team skins
  637. if ( RMG.integer )
  638. {
  639. trap_RMG_Init(terrainID);
  640. /*
  641. if ( level.gametypeData->teams )
  642. {
  643. char temp[MAX_QPATH];
  644. // Red team change from RMG ?
  645. trap_GetConfigstring ( CS_GAMETYPE_REDTEAM, temp, MAX_QPATH );
  646. if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_RED] ) )
  647. {
  648. level.gametypeTeam[TEAM_RED] = trap_VM_LocalStringAlloc ( temp );
  649. }
  650. // Blue team change from RMG ?
  651. trap_GetConfigstring ( CS_GAMETYPE_BLUETEAM, temp, MAX_QPATH );
  652. if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_BLUE] ) )
  653. {
  654. level.gametypeTeam[TEAM_BLUE] = trap_VM_LocalStringAlloc ( temp );
  655. }
  656. }
  657. */
  658. }
  659. }
  660. //rww - Called by skyportal entities. This will check through entities and flag them
  661. //as portal ents if they are in the same pvs as a skyportal entity and pass
  662. //a direct point trace check between origins. I really wanted to use an eFlag for
  663. //flagging portal entities, but too many entities like to reset their eFlags.
  664. //Note that this was not part of the original wolf sky portal stuff.
  665. void G_PortalifyEntities(gentity_t *ent)
  666. {
  667. int i = 0;
  668. gentity_t *scan = NULL;
  669. while (i < MAX_GENTITIES)
  670. {
  671. scan = &g_entities[i];
  672. if (scan && scan->inuse && scan->s.number != ent->s.number && trap_InPVS(ent->s.origin, scan->r.currentOrigin))
  673. {
  674. trace_t tr;
  675. trap_Trace(&tr, ent->s.origin, vec3_origin, vec3_origin, scan->r.currentOrigin, ent->s.number, CONTENTS_SOLID);
  676. if (tr.fraction == 1.0 || (tr.entityNum == scan->s.number && tr.entityNum != ENTITYNUM_NONE && tr.entityNum != ENTITYNUM_WORLD))
  677. {
  678. if (!scan->client || scan->s.eType == ET_NPC)
  679. { //making a client a portal entity would be bad.
  680. scan->s.isPortalEnt = qtrue; //he's flagged now
  681. }
  682. }
  683. }
  684. i++;
  685. }
  686. ent->think = G_FreeEntity; //the portal entity is no longer needed because its information is stored in a config string.
  687. ent->nextthink = level.time;
  688. }
  689. /*QUAKED misc_skyportal_orient (.6 .7 .7) (-8 -8 0) (8 8 16)
  690. point from which to orient the sky portal cam in relation
  691. to the regular view position.
  692. "modelscale" the scale at which to scale positions
  693. */
  694. void SP_misc_skyportal_orient (gentity_t *ent)
  695. {
  696. G_FreeEntity(ent);
  697. }
  698. /*QUAKED misc_skyportal (.6 .7 .7) (-8 -8 0) (8 8 16)
  699. "fov" for the skybox default is 80
  700. To have the portal sky fogged, enter any of the following values:
  701. "onlyfoghere" if non-0 allows you to set a global fog, but will only use that fog within this sky portal.
  702. Also note that entities in the same PVS and visible (via point trace) from this
  703. object will be flagged as portal entities. This means they will be sent and
  704. updated from the server for every client every update regardless of where
  705. they are, and they will essentially be added to the scene twice if the client
  706. is in the same PVS as them (only once otherwise, but still once no matter
  707. where the client is). In other words, don't go overboard with it or everything
  708. will explode.
  709. */
  710. void SP_misc_skyportal (gentity_t *ent)
  711. {
  712. char *fov;
  713. vec3_t fogv; //----(SA)
  714. int fogn; //----(SA)
  715. int fogf; //----(SA)
  716. int isfog = 0; // (SA)
  717. float fov_x;
  718. G_SpawnString ("fov", "80", &fov);
  719. fov_x = atof (fov);
  720. isfog += G_SpawnVector ("fogcolor", "0 0 0", fogv);
  721. isfog += G_SpawnInt ("fognear", "0", &fogn);
  722. isfog += G_SpawnInt ("fogfar", "300", &fogf);
  723. trap_SetConfigstring( CS_SKYBOXORG, va("%.2f %.2f %.2f %.1f %i %.2f %.2f %.2f %i %i", ent->s.origin[0], ent->s.origin[1], ent->s.origin[2], fov_x, (int)isfog, fogv[0], fogv[1], fogv[2], fogn, fogf ) );
  724. ent->think = G_PortalifyEntities;
  725. ent->nextthink = level.time + 1050; //give it some time first so that all other entities are spawned.
  726. }
  727. /*QUAKED misc_holocron (0 0 1) (-8 -8 -8) (8 8 8)
  728. count Set to type of holocron (based on force power value)
  729. HEAL = 0
  730. JUMP = 1
  731. SPEED = 2
  732. PUSH = 3
  733. PULL = 4
  734. TELEPATHY = 5
  735. GRIP = 6
  736. LIGHTNING = 7
  737. RAGE = 8
  738. PROTECT = 9
  739. ABSORB = 10
  740. TEAM HEAL = 11
  741. TEAM FORCE = 12
  742. DRAIN = 13
  743. SEE = 14
  744. SABERATTACK = 15
  745. SABERDEFEND = 16
  746. SABERTHROW = 17
  747. */
  748. /*char *holocronTypeModels[] = {
  749. "models/chunks/rock/rock_big.md3",//FP_HEAL,
  750. "models/chunks/rock/rock_big.md3",//FP_LEVITATION,
  751. "models/chunks/rock/rock_big.md3",//FP_SPEED,
  752. "models/chunks/rock/rock_big.md3",//FP_PUSH,
  753. "models/chunks/rock/rock_big.md3",//FP_PULL,
  754. "models/chunks/rock/rock_big.md3",//FP_TELEPATHY,
  755. "models/chunks/rock/rock_big.md3",//FP_GRIP,
  756. "models/chunks/rock/rock_big.md3",//FP_LIGHTNING,
  757. "models/chunks/rock/rock_big.md3",//FP_RAGE,
  758. "models/chunks/rock/rock_big.md3",//FP_PROTECT,
  759. "models/chunks/rock/rock_big.md3",//FP_ABSORB,
  760. "models/chunks/rock/rock_big.md3",//FP_TEAM_HEAL,
  761. "models/chunks/rock/rock_big.md3",//FP_TEAM_FORCE,
  762. "models/chunks/rock/rock_big.md3",//FP_DRAIN,
  763. "models/chunks/rock/rock_big.md3",//FP_SEE
  764. "models/chunks/rock/rock_big.md3",//FP_SABER_OFFENSE
  765. "models/chunks/rock/rock_big.md3",//FP_SABER_DEFENSE
  766. "models/chunks/rock/rock_big.md3"//FP_SABERTHROW
  767. };*/
  768. void HolocronRespawn(gentity_t *self)
  769. {
  770. self->s.modelindex = (self->count - 128);
  771. }
  772. void HolocronPopOut(gentity_t *self)
  773. {
  774. if (Q_irand(1, 10) < 5)
  775. {
  776. self->s.pos.trDelta[0] = 150 + Q_irand(1, 100);
  777. }
  778. else
  779. {
  780. self->s.pos.trDelta[0] = -150 - Q_irand(1, 100);
  781. }
  782. if (Q_irand(1, 10) < 5)
  783. {
  784. self->s.pos.trDelta[1] = 150 + Q_irand(1, 100);
  785. }
  786. else
  787. {
  788. self->s.pos.trDelta[1] = -150 - Q_irand(1, 100);
  789. }
  790. self->s.pos.trDelta[2] = 150 + Q_irand(1, 100);
  791. }
  792. void HolocronTouch(gentity_t *self, gentity_t *other, trace_t *trace)
  793. {
  794. int i = 0;
  795. int othercarrying = 0;
  796. float time_lowest = 0;
  797. int index_lowest = -1;
  798. int hasall = 1;
  799. int forceReselect = WP_NONE;
  800. if (trace)
  801. {
  802. self->s.groundEntityNum = trace->entityNum;
  803. }
  804. if (!other || !other->client || other->health < 1)
  805. {
  806. return;
  807. }
  808. if (!self->s.modelindex)
  809. {
  810. return;
  811. }
  812. if (self->enemy)
  813. {
  814. return;
  815. }
  816. if (other->client->ps.holocronsCarried[self->count])
  817. {
  818. return;
  819. }
  820. if (other->client->ps.holocronCantTouch == self->s.number && other->client->ps.holocronCantTouchTime > level.time)
  821. {
  822. return;
  823. }
  824. while (i < NUM_FORCE_POWERS)
  825. {
  826. if (other->client->ps.holocronsCarried[i])
  827. {
  828. othercarrying++;
  829. if (index_lowest == -1 || other->client->ps.holocronsCarried[i] < time_lowest)
  830. {
  831. index_lowest = i;
  832. time_lowest = other->client->ps.holocronsCarried[i];
  833. }
  834. }
  835. else if (i != self->count)
  836. {
  837. hasall = 0;
  838. }
  839. i++;
  840. }
  841. if (hasall)
  842. { //once we pick up this holocron we'll have all of them, so give us super special best prize!
  843. //G_Printf("You deserve a pat on the back.\n");
  844. }
  845. if (!(other->client->ps.fd.forcePowersActive & (1 << other->client->ps.fd.forcePowerSelected)))
  846. { //If the player isn't using his currently selected force power, select this one
  847. if (self->count != FP_SABER_OFFENSE && self->count != FP_SABER_DEFENSE && self->count != FP_SABERTHROW && self->count != FP_LEVITATION)
  848. {
  849. other->client->ps.fd.forcePowerSelected = self->count;
  850. }
  851. }
  852. if (g_maxHolocronCarry.integer && othercarrying >= g_maxHolocronCarry.integer)
  853. { //make the oldest holocron carried by the player pop out to make room for this one
  854. other->client->ps.holocronsCarried[index_lowest] = 0;
  855. /*
  856. if (index_lowest == FP_SABER_OFFENSE && !HasSetSaberOnly())
  857. { //you lost your saberattack holocron, so no more saber for you
  858. other->client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
  859. other->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
  860. if (other->client->ps.weapon == WP_SABER)
  861. {
  862. forceReselect = WP_SABER;
  863. }
  864. }
  865. */
  866. //NOTE: No longer valid as we are now always giving a force level 1 saber attack level in holocron
  867. }
  868. //G_Sound(other, CHAN_AUTO, G_SoundIndex("sound/weapons/w_pkup.wav"));
  869. G_AddEvent( other, EV_ITEM_PICKUP, self->s.number );
  870. other->client->ps.holocronsCarried[self->count] = level.time;
  871. self->s.modelindex = 0;
  872. self->enemy = other;
  873. self->pos2[0] = 1;
  874. self->pos2[1] = level.time + HOLOCRON_RESPAWN_TIME;
  875. /*
  876. if (self->count == FP_SABER_OFFENSE && !HasSetSaberOnly())
  877. { //player gets a saber
  878. other->client->ps.stats[STAT_WEAPONS] |= (1 << WP_SABER);
  879. other->client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_STUN_BATON);
  880. if (other->client->ps.weapon == WP_STUN_BATON)
  881. {
  882. forceReselect = WP_STUN_BATON;
  883. }
  884. }
  885. */
  886. if (forceReselect != WP_NONE)
  887. {
  888. G_AddEvent(other, EV_NOAMMO, forceReselect);
  889. }
  890. //G_Printf("DON'T TOUCH ME\n");
  891. }
  892. void HolocronThink(gentity_t *ent)
  893. {
  894. if (ent->pos2[0] && (!ent->enemy || !ent->enemy->client || ent->enemy->health < 1))
  895. {
  896. if (ent->enemy && ent->enemy->client)
  897. {
  898. HolocronRespawn(ent);
  899. VectorCopy(ent->enemy->client->ps.origin, ent->s.pos.trBase);
  900. VectorCopy(ent->enemy->client->ps.origin, ent->s.origin);
  901. VectorCopy(ent->enemy->client->ps.origin, ent->r.currentOrigin);
  902. //copy to person carrying's origin before popping out of them
  903. HolocronPopOut(ent);
  904. ent->enemy->client->ps.holocronsCarried[ent->count] = 0;
  905. ent->enemy = NULL;
  906. goto justthink;
  907. }
  908. }
  909. else if (ent->pos2[0] && ent->enemy && ent->enemy->client)
  910. {
  911. ent->pos2[1] = level.time + HOLOCRON_RESPAWN_TIME;
  912. }
  913. if (ent->enemy && ent->enemy->client)
  914. {
  915. if (!ent->enemy->client->ps.holocronsCarried[ent->count])
  916. {
  917. ent->enemy->client->ps.holocronCantTouch = ent->s.number;
  918. ent->enemy->client->ps.holocronCantTouchTime = level.time + 5000;
  919. HolocronRespawn(ent);
  920. VectorCopy(ent->enemy->client->ps.origin, ent->s.pos.trBase);
  921. VectorCopy(ent->enemy->client->ps.origin, ent->s.origin);
  922. VectorCopy(ent->enemy->client->ps.origin, ent->r.currentOrigin);
  923. //copy to person carrying's origin before popping out of them
  924. HolocronPopOut(ent);
  925. ent->enemy = NULL;
  926. goto justthink;
  927. }
  928. if (!ent->enemy->inuse || (ent->enemy->client && ent->enemy->client->ps.fallingToDeath))
  929. {
  930. if (ent->enemy->inuse && ent->enemy->client)
  931. {
  932. ent->enemy->client->ps.holocronBits &= ~(1 << ent->count);
  933. ent->enemy->client->ps.holocronsCarried[ent->count] = 0;
  934. }
  935. ent->enemy = NULL;
  936. HolocronRespawn(ent);
  937. VectorCopy(ent->s.origin2, ent->s.pos.trBase);
  938. VectorCopy(ent->s.origin2, ent->s.origin);
  939. VectorCopy(ent->s.origin2, ent->r.currentOrigin);
  940. ent->s.pos.trTime = level.time;
  941. ent->pos2[0] = 0;
  942. trap_LinkEntity(ent);
  943. goto justthink;
  944. }
  945. }
  946. if (ent->pos2[0] && ent->pos2[1] < level.time)
  947. { //isn't in original place and has been there for (HOLOCRON_RESPAWN_TIME) seconds without being picked up, so respawn
  948. VectorCopy(ent->s.origin2, ent->s.pos.trBase);
  949. VectorCopy(ent->s.origin2, ent->s.origin);
  950. VectorCopy(ent->s.origin2, ent->r.currentOrigin);
  951. ent->s.pos.trTime = level.time;
  952. ent->pos2[0] = 0;
  953. trap_LinkEntity(ent);
  954. }
  955. justthink:
  956. ent->nextthink = level.time + 50;
  957. if (ent->s.pos.trDelta[0] || ent->s.pos.trDelta[1] || ent->s.pos.trDelta[2])
  958. {
  959. G_RunObject(ent);
  960. }
  961. }
  962. void SP_misc_holocron(gentity_t *ent)
  963. {
  964. vec3_t dest;
  965. trace_t tr;
  966. if (level.gametype != GT_HOLOCRON)
  967. {
  968. G_FreeEntity(ent);
  969. return;
  970. }
  971. if (HasSetSaberOnly())
  972. {
  973. if (ent->count == FP_SABER_OFFENSE ||
  974. ent->count == FP_SABER_DEFENSE ||
  975. ent->count == FP_SABERTHROW)
  976. { //having saber holocrons in saber only mode is pointless
  977. G_FreeEntity(ent);
  978. return;
  979. }
  980. }
  981. ent->s.isJediMaster = qtrue;
  982. VectorSet( ent->r.maxs, 8, 8, 8 );
  983. VectorSet( ent->r.mins, -8, -8, -8 );
  984. ent->s.origin[2] += 0.1f;
  985. ent->r.maxs[2] -= 0.1f;
  986. VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
  987. trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
  988. if ( tr.startsolid )
  989. {
  990. G_Printf ("SP_misc_holocron: misc_holocron startsolid at %s\n", vtos(ent->s.origin));
  991. G_FreeEntity( ent );
  992. return;
  993. }
  994. //add the 0.1 back after the trace
  995. ent->r.maxs[2] += 0.1f;
  996. // allow to ride movers
  997. // ent->s.groundEntityNum = tr.entityNum;
  998. G_SetOrigin( ent, tr.endpos );
  999. if (ent->count < 0)
  1000. {
  1001. ent->count = 0;
  1002. }
  1003. if (ent->count >= NUM_FORCE_POWERS)
  1004. {
  1005. ent->count = NUM_FORCE_POWERS-1;
  1006. }
  1007. /*
  1008. if (g_forcePowerDisable.integer &&
  1009. (g_forcePowerDisable.integer & (1 << ent->count)))
  1010. {
  1011. G_FreeEntity(ent);
  1012. return;
  1013. }
  1014. */
  1015. //No longer doing this, causing too many complaints about accidentally setting no force powers at all
  1016. //and starting a holocron game (making it basically just FFA)
  1017. ent->enemy = NULL;
  1018. ent->flags = FL_BOUNCE_HALF;
  1019. ent->s.modelindex = (ent->count - 128);//G_ModelIndex(holocronTypeModels[ent->count]);
  1020. ent->s.eType = ET_HOLOCRON;
  1021. ent->s.pos.trType = TR_GRAVITY;
  1022. ent->s.pos.trTime = level.time;
  1023. ent->r.contents = CONTENTS_TRIGGER;
  1024. ent->clipmask = MASK_SOLID;
  1025. ent->s.trickedentindex4 = ent->count;
  1026. if (forcePowerDarkLight[ent->count] == FORCE_DARKSIDE)
  1027. {
  1028. ent->s.trickedentindex3 = 1;
  1029. }
  1030. else if (forcePowerDarkLight[ent->count] == FORCE_LIGHTSIDE)
  1031. {
  1032. ent->s.trickedentindex3 = 2;
  1033. }
  1034. else
  1035. {
  1036. ent->s.trickedentindex3 = 3;
  1037. }
  1038. ent->physicsObject = qtrue;
  1039. VectorCopy(ent->s.pos.trBase, ent->s.origin2); //remember the spawn spot
  1040. ent->touch = HolocronTouch;
  1041. trap_LinkEntity(ent);
  1042. ent->think = HolocronThink;
  1043. ent->nextthink = level.time + 50;
  1044. }
  1045. /*
  1046. ======================================================================
  1047. SHOOTERS
  1048. ======================================================================
  1049. */
  1050. void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
  1051. vec3_t dir;
  1052. float deg;
  1053. vec3_t up, right;
  1054. // see if we have a target
  1055. if ( ent->enemy ) {
  1056. VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir );
  1057. VectorNormalize( dir );
  1058. } else {
  1059. VectorCopy( ent->movedir, dir );
  1060. }
  1061. // randomize a bit
  1062. PerpendicularVector( up, dir );
  1063. CrossProduct( up, dir, right );
  1064. deg = crandom() * ent->random;
  1065. VectorMA( dir, deg, up, dir );
  1066. deg = crandom() * ent->random;
  1067. VectorMA( dir, deg, right, dir );
  1068. VectorNormalize( dir );
  1069. switch ( ent->s.weapon ) {
  1070. case WP_BLASTER:
  1071. WP_FireBlasterMissile( ent, ent->s.origin, dir, qfalse );
  1072. break;
  1073. }
  1074. G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
  1075. }
  1076. static void InitShooter_Finish( gentity_t *ent ) {
  1077. ent->enemy = G_PickTarget( ent->target );
  1078. ent->think = 0;
  1079. ent->nextthink = 0;
  1080. }
  1081. void InitShooter( gentity_t *ent, int weapon ) {
  1082. ent->use = Use_Shooter;
  1083. ent->s.weapon = weapon;
  1084. RegisterItem( BG_FindItemForWeapon( weapon ) );
  1085. G_SetMovedir( ent->s.angles, ent->movedir );
  1086. if ( !ent->random ) {
  1087. ent->random = 1.0;
  1088. }
  1089. ent->random = sin( M_PI * ent->random / 180 );
  1090. // target might be a moving object, so we can't set movedir for it
  1091. if ( ent->target ) {
  1092. ent->think = InitShooter_Finish;
  1093. ent->nextthink = level.time + 500;
  1094. }
  1095. trap_LinkEntity( ent );
  1096. }
  1097. /*QUAKED shooter_blaster (1 0 0) (-16 -16 -16) (16 16 16)
  1098. Fires at either the target or the current direction.
  1099. "random" is the number of degrees of deviance from the taget. (1.0 default)
  1100. */
  1101. void SP_shooter_blaster( gentity_t *ent ) {
  1102. InitShooter( ent, WP_BLASTER);
  1103. }
  1104. void check_recharge(gentity_t *ent)
  1105. {
  1106. if (ent->fly_sound_debounce_time < level.time ||
  1107. !ent->activator ||
  1108. !ent->activator->client ||
  1109. !(ent->activator->client->pers.cmd.buttons & BUTTON_USE))
  1110. {
  1111. if (ent->activator)
  1112. {
  1113. G_Sound(ent, CHAN_AUTO, ent->genericValue7);
  1114. }
  1115. ent->s.loopSound = 0;
  1116. ent->s.loopIsSoundset = qfalse;
  1117. ent->activator = NULL;
  1118. ent->fly_sound_debounce_time = 0;
  1119. }
  1120. if (!ent->activator)
  1121. { //don't recharge during use
  1122. if (ent->genericValue8 < level.time)
  1123. {
  1124. if (ent->count < ent->genericValue4)
  1125. {
  1126. ent->count++;
  1127. }
  1128. ent->genericValue8 = level.time + ent->genericValue5;
  1129. }
  1130. }
  1131. ent->s.health = ent->count; //the "health bar" is gonna be how full we are
  1132. ent->nextthink = level.time;
  1133. }
  1134. /*
  1135. ================
  1136. EnergyShieldStationSettings
  1137. ================
  1138. */
  1139. void EnergyShieldStationSettings(gentity_t *ent)
  1140. {
  1141. G_SpawnInt( "count", "200", &ent->count );
  1142. G_SpawnInt("chargerate", "0", &ent->genericValue5);
  1143. if (!ent->genericValue5)
  1144. {
  1145. ent->genericValue5 = STATION_RECHARGE_TIME;
  1146. }
  1147. }
  1148. /*
  1149. ================
  1150. shield_power_converter_use
  1151. ================
  1152. */
  1153. void shield_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
  1154. {
  1155. int dif,add;
  1156. int stop = 1;
  1157. if (!activator || !activator->client)
  1158. {
  1159. return;
  1160. }
  1161. if ( level.gametype == GT_SIEGE
  1162. && other
  1163. && other->client
  1164. && other->client->siegeClass )
  1165. {
  1166. if ( !bgSiegeClasses[other->client->siegeClass].maxarmor )
  1167. {//can't use it!
  1168. G_Sound(self, CHAN_AUTO, G_SoundIndex("sound/interface/shieldcon_empty"));
  1169. return;
  1170. }
  1171. }
  1172. if (self->setTime < level.time)
  1173. {
  1174. int maxArmor;
  1175. if (!self->s.loopSound)
  1176. {
  1177. self->s.loopSound = G_SoundIndex("sound/interface/shieldcon_run");
  1178. self->s.loopIsSoundset = qfalse;
  1179. }
  1180. self->setTime = level.time + 100;
  1181. if ( level.gametype == GT_SIEGE
  1182. && other
  1183. && other->client
  1184. && other->client->siegeClass != -1 )
  1185. {
  1186. maxArmor = bgSiegeClasses[other->client->siegeClass].maxarmor;
  1187. }
  1188. else
  1189. {
  1190. maxArmor = activator->client->ps.stats[STAT_MAX_HEALTH];
  1191. }
  1192. dif = maxArmor - activator->client->ps.stats[STAT_ARMOR];
  1193. if (dif > 0) // Already at full armor?
  1194. {
  1195. if (dif >MAX_AMMO_GIVE)
  1196. {
  1197. add = MAX_AMMO_GIVE;
  1198. }
  1199. else
  1200. {
  1201. add = dif;
  1202. }
  1203. if (self->count<add)
  1204. {
  1205. add = self->count;
  1206. }
  1207. if (!self->genericValue12)
  1208. {
  1209. self->count -= add;
  1210. }
  1211. if (self->count <= 0)
  1212. {
  1213. self->setTime = 0;
  1214. }
  1215. stop = 0;
  1216. self->fly_sound_debounce_time = level.time + 500;
  1217. self->activator = activator;
  1218. activator->client->ps.stats[STAT_ARMOR] += add;
  1219. }
  1220. }
  1221. if (stop || self->count <= 0)
  1222. {
  1223. if (self->s.loopSound && self->setTime < level.time)
  1224. {
  1225. if (self->count <= 0)
  1226. {
  1227. G_Sound(self, CHAN_AUTO, G_SoundIndex("sound/interface/shieldcon_empty"));
  1228. }
  1229. else
  1230. {
  1231. G_Sound(self, CHAN_AUTO, self->genericValue7);
  1232. }
  1233. }
  1234. self->s.loopSound = 0;
  1235. self->s.loopIsSoundset = qfalse;
  1236. if (self->setTime < level.time)
  1237. {
  1238. self->setTime = level.time + self->genericValue5+100;
  1239. }
  1240. }
  1241. }
  1242. //dispense generic ammo
  1243. void ammo_generic_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *activator)
  1244. {
  1245. int /*dif,*/ add;
  1246. //int ammoType;
  1247. int stop = 1;
  1248. if (!activator || !activator->client)
  1249. {
  1250. return;
  1251. }
  1252. if (self->setTime < level.time)
  1253. {
  1254. qboolean gaveSome = qfalse;
  1255. /*
  1256. while (i < 3)
  1257. {
  1258. if (!self->s.loopSound)
  1259. {
  1260. self->s.loopSound = G_SoundIndex("sound/interface/ammocon_run");
  1261. self->s.loopIsSoundset = qfalse;
  1262. }
  1263. self->setTime = level.time + 100;
  1264. //dif = activator->client->ps.stats[STAT_MAX_HEALTH] - activator->client->ps.stats[STAT_ARMOR];
  1265. switch (i)
  1266. { //don't give rockets I guess
  1267. case 0:
  1268. ammoType = AMMO_BLASTER;
  1269. break;
  1270. case 1:
  1271. ammoType = AMMO_POWERCELL;
  1272. break;
  1273. case 2:
  1274. ammoType = AMMO_METAL_BOLTS;
  1275. break;
  1276. default:
  1277. ammoType = -1;
  1278. break;
  1279. }
  1280. if (ammoType != -1)
  1281. {
  1282. dif = ammoData[ammoType].max - activator->client->ps.ammo[ammoType];
  1283. }
  1284. else
  1285. {
  1286. dif = 0;
  1287. }
  1288. if (dif > 0)
  1289. { //only give if not full
  1290. if (dif > MAX_AMMO_GIVE)
  1291. {
  1292. add = MAX_AMMO_GIVE;
  1293. }
  1294. else
  1295. {
  1296. add = dif;
  1297. }
  1298. if (self->count<add)
  1299. {
  1300. add = self->count;
  1301. }
  1302. self->count -= add;
  1303. if (self->count <= 0)
  1304. {
  1305. self->setTime = 0;
  1306. break;
  1307. }
  1308. stop = 0;
  1309. self->fly_sound_debounce_time = level.time + 500;
  1310. self->activator = activator;
  1311. activator->client->ps.ammo[ammoType] += add;
  1312. }
  1313. i++;
  1314. }
  1315. */
  1316. int i = AMMO_BLASTER;
  1317. if (!self->s.loopSound)
  1318. {
  1319. self->s.loopSound = G_SoundIndex("sound/interface/ammocon_run");
  1320. self->s.loopIsSoundset = qfalse;
  1321. }
  1322. //self->setTime = level.time + 100;
  1323. self->fly_sound_debounce_time = level.time + 500;
  1324. self->activator = activator;
  1325. while (i < AMMO_MAX)
  1326. {
  1327. add = ammoData[i].max*0.05;
  1328. if (add < 1)
  1329. {
  1330. add = 1;
  1331. }
  1332. if ( ( (activator->client->ps.eFlags & EF_DOUBLE_AMMO) && (activator->client->ps.ammo[i] < ammoData[i].max*2)) ||
  1333. ( activator->client->ps.ammo[i] < ammoData[i].max ) )
  1334. {
  1335. gaveSome = qtrue;
  1336. if ( level.gametype == GT_SIEGE && i == AMMO_ROCKETS && activator->client->ps.ammo[i] >= 10 )
  1337. { //this stuff is already a freaking mess, so..
  1338. gaveSome = qfalse;
  1339. }
  1340. activator->client->ps.ammo[i] += add;
  1341. if ( level.gametype == GT_SIEGE && i == AMMO_ROCKETS && activator->client->ps.ammo[i] >= 10 )
  1342. { // fixme - this should SERIOUSLY be externed.
  1343. activator->client->ps.ammo[i] = 10;
  1344. }
  1345. else if ( activator->client->ps.eFlags & EF_DOUBLE_AMMO )
  1346. {
  1347. if (activator->client->ps.ammo[i] >= ammoData[i].max * 2)
  1348. { // yuck.
  1349. activator->client->ps.ammo[i] = ammoData[i].max * 2;
  1350. }
  1351. else
  1352. {
  1353. stop = 0;
  1354. }
  1355. }
  1356. else
  1357. {
  1358. if (activator->client->ps.ammo[i] >= ammoData[i].max)
  1359. {
  1360. activator->client->ps.ammo[i] = ammoData[i].max;
  1361. }
  1362. else
  1363. {
  1364. stop = 0;
  1365. }
  1366. }
  1367. }
  1368. i++;
  1369. if (!self->genericValue12 && gaveSome)
  1370. {
  1371. int sub = (add*0.2);
  1372. if (sub < 1)
  1373. {
  1374. sub = 1;
  1375. }
  1376. self->count -= sub;
  1377. if (self->count <= 0)
  1378. {
  1379. self->count = 0;
  1380. stop = 1;
  1381. break;
  1382. }
  1383. }
  1384. }
  1385. }
  1386. if (stop || self->count <= 0)
  1387. {
  1388. if (self->s.loopSound && self->setTime < level.time)
  1389. {
  1390. if (self->count <= 0)
  1391. {
  1392. G_Sound(self, CHAN_AUTO, G_SoundIndex("sound/interface/ammocon_empty"));
  1393. }
  1394. else
  1395. {
  1396. G_Sound(self, CHAN_AUTO, self->genericValue7);
  1397. }
  1398. }
  1399. self->s.loopSound = 0;
  1400. self->s.loopIsSoundset = qfalse;
  1401. if (self->setTime < level.time)
  1402. {
  1403. self->setTime = level.time + self->genericValue5+100;
  1404. }
  1405. }
  1406. }
  1407. /*QUAKED misc_ammo_floor_unit (1 0 0) (-16 -16 0) (16 16 40)
  1408. model="/models/items/a_pwr_converter.md3"
  1409. Gives generic ammo when used
  1410. "count" - max charge value (default 200)
  1411. "chargerate" - rechage 1 point every this many milliseconds (default 2000)
  1412. "nodrain" - don't drain power from station if 1
  1413. */
  1414. void SP_misc_ammo_floor_unit(gentity_t *ent)
  1415. {
  1416. vec3_t dest;
  1417. trace_t tr;
  1418. VectorSet( ent->r.mins, -16, -16, 0 );
  1419. VectorSet( ent->r.maxs, 16, 16, 40 );
  1420. ent->s.origin[2] += 0.1f;
  1421. ent->r.maxs[2] -= 0.1f;
  1422. VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
  1423. trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
  1424. if ( tr.startsolid )
  1425. {
  1426. G_Printf ("SP_misc_ammo_floor_unit: misc_ammo_floor_unit startsolid at %s\n", vtos(ent->s.origin));
  1427. G_FreeEntity( ent );
  1428. return;
  1429. }
  1430. //add the 0.1 back after the trace
  1431. ent->r.maxs[2] += 0.1f;
  1432. // allow to ride movers
  1433. ent->s.groundEntityNum = tr.entityNum;
  1434. G_SetOrigin( ent, tr.endpos );
  1435. if (!ent->health)
  1436. {
  1437. ent->health = 60;
  1438. }
  1439. if (!ent->model || !ent->model[0])
  1440. {
  1441. ent->model = "/models/items/a_pwr_converter.md3";
  1442. }
  1443. ent->s.modelindex = G_ModelIndex( ent->model );
  1444. ent->s.eFlags = 0;
  1445. ent->r.svFlags |= SVF_PLAYER_USABLE;
  1446. ent->r.contents = CONTENTS_SOLID;
  1447. ent->clipmask = MASK_SOLID;
  1448. EnergyShieldStationSettings(ent);
  1449. ent->genericValue4 = ent->count; //initial value
  1450. ent->think = check_recharge;
  1451. G_SpawnInt("nodrain", "0", &ent->genericValue12);
  1452. if (!ent->genericValue12)
  1453. {
  1454. ent->s.maxhealth = ent->s.health = ent->count;
  1455. }
  1456. ent->s.shouldtarget = qtrue;
  1457. ent->s.teamowner = 0;
  1458. ent->s.owner = ENTITYNUM_NONE;
  1459. ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
  1460. ent->use = ammo_generic_power_converter_use;
  1461. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  1462. trap_LinkEntity (ent);
  1463. G_SoundIndex("sound/interface/ammocon_run");
  1464. ent->genericValue7 = G_SoundIndex("sound/interface/ammocon_done");
  1465. G_SoundIndex("sound/interface/ammocon_empty");
  1466. if (level.gametype == GT_SIEGE)
  1467. { //show on radar from everywhere
  1468. ent->r.svFlags |= SVF_BROADCAST;
  1469. ent->s.eFlags |= EF_RADAROBJECT;
  1470. ent->s.genericenemyindex = G_IconIndex("gfx/mp/siegeicons/desert/weapon_recharge");
  1471. }
  1472. }
  1473. /*QUAKED misc_shield_floor_unit (1 0 0) (-16 -16 0) (16 16 40)
  1474. model="/models/items/a_shield_converter.md3"
  1475. Gives shield energy when used.
  1476. "count" - max charge value (default 50)
  1477. "chargerate" - rechage 1 point every this many milliseconds (default 3000)
  1478. "nodrain" - don't drain power from me
  1479. */
  1480. void SP_misc_shield_floor_unit( gentity_t *ent )
  1481. {
  1482. vec3_t dest;
  1483. trace_t tr;
  1484. if (level.gametype != GT_CTF &&
  1485. level.gametype != GT_CTY &&
  1486. level.gametype != GT_SIEGE)
  1487. {
  1488. G_FreeEntity( ent );
  1489. return;
  1490. }
  1491. VectorSet( ent->r.mins, -16, -16, 0 );
  1492. VectorSet( ent->r.maxs, 16, 16, 40 );
  1493. ent->s.origin[2] += 0.1f;
  1494. ent->r.maxs[2] -= 0.1f;
  1495. VectorSet( dest, ent->s.origin[0], ent->s.origin[1], ent->s.origin[2] - 4096 );
  1496. trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ent->s.number, MASK_SOLID );
  1497. if ( tr.startsolid )
  1498. {
  1499. G_Printf ("SP_misc_shield_floor_unit: misc_shield_floor_unit startsolid at %s\n", vtos(ent->s.origin));
  1500. G_FreeEntity( ent );
  1501. return;
  1502. }
  1503. //add the 0.1 back after the trace
  1504. ent->r.maxs[2] += 0.1f;
  1505. // allow to ride movers
  1506. ent->s.groundEntityNum = tr.entityNum;
  1507. G_SetOrigin( ent, tr.endpos );
  1508. if (!ent->health)
  1509. {
  1510. ent->health = 60;
  1511. }
  1512. if (!ent->model || !ent->model[0])
  1513. {
  1514. ent->model = "/models/items/a_shield_converter.md3";
  1515. }
  1516. ent->s.modelindex = G_ModelIndex( ent->model );
  1517. ent->s.eFlags = 0;
  1518. ent->r.svFlags |= SVF_PLAYER_USABLE;
  1519. ent->r.contents = CONTENTS_SOLID;
  1520. ent->clipmask = MASK_SOLID;
  1521. EnergyShieldStationSettings(ent);
  1522. ent->genericValue4 = ent->count; //initial value
  1523. ent->think = check_recharge;
  1524. G_SpawnInt("nodrain", "0", &ent->genericValue12);
  1525. if (!ent->genericValue12)
  1526. {
  1527. ent->s.maxhealth = ent->s.health = ent->count;
  1528. }
  1529. ent->s.shouldtarget = qtrue;
  1530. ent->s.teamowner = 0;
  1531. ent->s.owner = ENTITYNUM_NONE;
  1532. ent->nextthink = level.time + 200;// + STATION_RECHARGE_TIME;
  1533. ent->use = shield_power_converter_use;
  1534. VectorCopy( ent->s.angles, ent-

Large files files are truncated, but you can click here to view the full file