g_misc.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. //
  19. // g_misc.c
  20. #include "g_local.h"
  21. /*QUAKED func_group (0 0 0) ?
  22. Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
  23. */
  24. /*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
  25. Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
  26. */
  27. void SP_info_camp( gentity_t *self ) {
  28. G_SetOrigin( self, self->s.origin );
  29. }
  30. /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
  31. Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
  32. */
  33. void SP_info_null( gentity_t *self ) {
  34. G_FreeEntity( self );
  35. }
  36. /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
  37. Used as a positional target for in-game calculation, like jumppad targets.
  38. target_position does the same thing
  39. */
  40. void SP_info_notnull( gentity_t *self ){
  41. G_SetOrigin( self, self->s.origin );
  42. }
  43. /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear
  44. Non-displayed light.
  45. "light" overrides the default 300 intensity.
  46. Linear checbox gives linear falloff instead of inverse square
  47. Lights pointed at a target will be spotlights.
  48. "radius" overrides the default 64 unit radius of a spotlight at the target point.
  49. */
  50. void SP_light( gentity_t *self ) {
  51. G_FreeEntity( self );
  52. }
  53. /*
  54. =================================================================================
  55. TELEPORTERS
  56. =================================================================================
  57. */
  58. void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
  59. gentity_t *tent;
  60. // use temp events at source and destination to prevent the effect
  61. // from getting dropped by a second player event
  62. if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  63. tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
  64. tent->s.clientNum = player->s.clientNum;
  65. tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
  66. tent->s.clientNum = player->s.clientNum;
  67. }
  68. // unlink to make sure it can't possibly interfere with G_KillBox
  69. trap_UnlinkEntity (player);
  70. VectorCopy ( origin, player->client->ps.origin );
  71. player->client->ps.origin[2] += 1;
  72. // spit the player out
  73. AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
  74. VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
  75. player->client->ps.pm_time = 160; // hold time
  76. player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  77. // toggle the teleport bit so the client knows to not lerp
  78. player->client->ps.eFlags ^= EF_TELEPORT_BIT;
  79. // set angles
  80. SetClientViewAngle( player, angles );
  81. // kill anything at the destination
  82. if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  83. G_KillBox (player);
  84. }
  85. // save results of pmove
  86. BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
  87. // use the precise origin for linking
  88. VectorCopy( player->client->ps.origin, player->r.currentOrigin );
  89. if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  90. trap_LinkEntity (player);
  91. }
  92. }
  93. /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
  94. Point teleporters at these.
  95. Now that we don't have teleport destination pads, this is just
  96. an info_notnull
  97. */
  98. void SP_misc_teleporter_dest( gentity_t *ent ) {
  99. }
  100. //===========================================================
  101. /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
  102. "model" arbitrary .md3 file to display
  103. */
  104. void SP_misc_model( gentity_t *ent ) {
  105. #if 0
  106. ent->s.modelindex = G_ModelIndex( ent->model );
  107. VectorSet (ent->mins, -16, -16, -16);
  108. VectorSet (ent->maxs, 16, 16, 16);
  109. trap_LinkEntity (ent);
  110. G_SetOrigin( ent, ent->s.origin );
  111. VectorCopy( ent->s.angles, ent->s.apos.trBase );
  112. #else
  113. G_FreeEntity( ent );
  114. #endif
  115. }
  116. //===========================================================
  117. void locateCamera( gentity_t *ent ) {
  118. vec3_t dir;
  119. gentity_t *target;
  120. gentity_t *owner;
  121. owner = G_PickTarget( ent->target );
  122. if ( !owner ) {
  123. G_Printf( "Couldn't find target for misc_partal_surface\n" );
  124. G_FreeEntity( ent );
  125. return;
  126. }
  127. ent->r.ownerNum = owner->s.number;
  128. // frame holds the rotate speed
  129. if ( owner->spawnflags & 1 ) {
  130. ent->s.frame = 25;
  131. } else if ( owner->spawnflags & 2 ) {
  132. ent->s.frame = 75;
  133. }
  134. // swing camera ?
  135. if ( owner->spawnflags & 4 ) {
  136. // set to 0 for no rotation at all
  137. ent->s.powerups = 0;
  138. }
  139. else {
  140. ent->s.powerups = 1;
  141. }
  142. // clientNum holds the rotate offset
  143. ent->s.clientNum = owner->s.clientNum;
  144. VectorCopy( owner->s.origin, ent->s.origin2 );
  145. // see if the portal_camera has a target
  146. target = G_PickTarget( owner->target );
  147. if ( target ) {
  148. VectorSubtract( target->s.origin, owner->s.origin, dir );
  149. VectorNormalize( dir );
  150. } else {
  151. G_SetMovedir( owner->s.angles, dir );
  152. }
  153. ent->s.eventParm = DirToByte( dir );
  154. }
  155. /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
  156. The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
  157. This must be within 64 world units of the surface!
  158. */
  159. void SP_misc_portal_surface(gentity_t *ent) {
  160. VectorClear( ent->r.mins );
  161. VectorClear( ent->r.maxs );
  162. trap_LinkEntity (ent);
  163. ent->r.svFlags = SVF_PORTAL;
  164. ent->s.eType = ET_PORTAL;
  165. if ( !ent->target ) {
  166. VectorCopy( ent->s.origin, ent->s.origin2 );
  167. } else {
  168. ent->think = locateCamera;
  169. ent->nextthink = level.time + 100;
  170. }
  171. }
  172. /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
  173. The target for a misc_portal_director. You can set either angles or target another entity to determine the direction of view.
  174. "roll" an angle modifier to orient the camera around the target vector;
  175. */
  176. void SP_misc_portal_camera(gentity_t *ent) {
  177. float roll;
  178. VectorClear( ent->r.mins );
  179. VectorClear( ent->r.maxs );
  180. trap_LinkEntity (ent);
  181. G_SpawnFloat( "roll", "0", &roll );
  182. ent->s.clientNum = roll/360.0 * 256;
  183. }
  184. /*
  185. ======================================================================
  186. SHOOTERS
  187. ======================================================================
  188. */
  189. void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
  190. vec3_t dir;
  191. float deg;
  192. vec3_t up, right;
  193. // see if we have a target
  194. if ( ent->enemy ) {
  195. VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir );
  196. VectorNormalize( dir );
  197. } else {
  198. VectorCopy( ent->movedir, dir );
  199. }
  200. // randomize a bit
  201. PerpendicularVector( up, dir );
  202. CrossProduct( up, dir, right );
  203. deg = crandom() * ent->random;
  204. VectorMA( dir, deg, up, dir );
  205. deg = crandom() * ent->random;
  206. VectorMA( dir, deg, right, dir );
  207. VectorNormalize( dir );
  208. switch ( ent->s.weapon ) {
  209. case WP_GRENADE_LAUNCHER:
  210. fire_grenade( ent, ent->s.origin, dir );
  211. break;
  212. case WP_ROCKET_LAUNCHER:
  213. fire_rocket( ent, ent->s.origin, dir );
  214. break;
  215. case WP_PLASMAGUN:
  216. fire_plasma( ent, ent->s.origin, dir );
  217. break;
  218. }
  219. G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
  220. }
  221. static void InitShooter_Finish( gentity_t *ent ) {
  222. ent->enemy = G_PickTarget( ent->target );
  223. ent->think = 0;
  224. ent->nextthink = 0;
  225. }
  226. void InitShooter( gentity_t *ent, int weapon ) {
  227. ent->use = Use_Shooter;
  228. ent->s.weapon = weapon;
  229. RegisterItem( BG_FindItemForWeapon( weapon ) );
  230. G_SetMovedir( ent->s.angles, ent->movedir );
  231. if ( !ent->random ) {
  232. ent->random = 1.0;
  233. }
  234. ent->random = sin( M_PI * ent->random / 180 );
  235. // target might be a moving object, so we can't set movedir for it
  236. if ( ent->target ) {
  237. ent->think = InitShooter_Finish;
  238. ent->nextthink = level.time + 500;
  239. }
  240. trap_LinkEntity( ent );
  241. }
  242. /*QUAKED shooter_rocket (1 0 0) (-16 -16 -16) (16 16 16)
  243. Fires at either the target or the current direction.
  244. "random" the number of degrees of deviance from the taget. (1.0 default)
  245. */
  246. void SP_shooter_rocket( gentity_t *ent ) {
  247. InitShooter( ent, WP_ROCKET_LAUNCHER );
  248. }
  249. /*QUAKED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16)
  250. Fires at either the target or the current direction.
  251. "random" is the number of degrees of deviance from the taget. (1.0 default)
  252. */
  253. void SP_shooter_plasma( gentity_t *ent ) {
  254. InitShooter( ent, WP_PLASMAGUN);
  255. }
  256. /*QUAKED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16)
  257. Fires at either the target or the current direction.
  258. "random" is the number of degrees of deviance from the taget. (1.0 default)
  259. */
  260. void SP_shooter_grenade( gentity_t *ent ) {
  261. InitShooter( ent, WP_GRENADE_LAUNCHER);
  262. }
  263. #ifdef MISSIONPACK
  264. static void PortalDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod) {
  265. G_FreeEntity( self );
  266. //FIXME do something more interesting
  267. }
  268. void DropPortalDestination( gentity_t *player ) {
  269. gentity_t *ent;
  270. vec3_t snapped;
  271. // create the portal destination
  272. ent = G_Spawn();
  273. ent->s.modelindex = G_ModelIndex( "models/powerups/teleporter/tele_exit.md3" );
  274. VectorCopy( player->s.pos.trBase, snapped );
  275. SnapVector( snapped );
  276. G_SetOrigin( ent, snapped );
  277. VectorCopy( player->r.mins, ent->r.mins );
  278. VectorCopy( player->r.maxs, ent->r.maxs );
  279. ent->classname = "hi_portal destination";
  280. ent->s.pos.trType = TR_STATIONARY;
  281. ent->r.contents = CONTENTS_CORPSE;
  282. ent->takedamage = qtrue;
  283. ent->health = 200;
  284. ent->die = PortalDie;
  285. VectorCopy( player->s.apos.trBase, ent->s.angles );
  286. ent->think = G_FreeEntity;
  287. ent->nextthink = level.time + 2 * 60 * 1000;
  288. trap_LinkEntity( ent );
  289. player->client->portalID = ++level.portalSequence;
  290. ent->count = player->client->portalID;
  291. // give the item back so they can drop the source now
  292. player->client->ps.stats[STAT_HOLDABLE_ITEM] = BG_FindItem( "Portal" ) - bg_itemlist;
  293. }
  294. static void PortalTouch( gentity_t *self, gentity_t *other, trace_t *trace) {
  295. gentity_t *destination;
  296. // see if we will even let other try to use it
  297. if( other->health <= 0 ) {
  298. return;
  299. }
  300. if( !other->client ) {
  301. return;
  302. }
  303. // if( other->client->ps.persistant[PERS_TEAM] != self->spawnflags ) {
  304. // return;
  305. // }
  306. if ( other->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF
  307. Drop_Item( other, BG_FindItemForPowerup( PW_NEUTRALFLAG ), 0 );
  308. other->client->ps.powerups[PW_NEUTRALFLAG] = 0;
  309. }
  310. else if ( other->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF
  311. Drop_Item( other, BG_FindItemForPowerup( PW_REDFLAG ), 0 );
  312. other->client->ps.powerups[PW_REDFLAG] = 0;
  313. }
  314. else if ( other->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF
  315. Drop_Item( other, BG_FindItemForPowerup( PW_BLUEFLAG ), 0 );
  316. other->client->ps.powerups[PW_BLUEFLAG] = 0;
  317. }
  318. // find the destination
  319. destination = NULL;
  320. while( (destination = G_Find(destination, FOFS(classname), "hi_portal destination")) != NULL ) {
  321. if( destination->count == self->count ) {
  322. break;
  323. }
  324. }
  325. // if there is not one, die!
  326. if( !destination ) {
  327. if( self->pos1[0] || self->pos1[1] || self->pos1[2] ) {
  328. TeleportPlayer( other, self->pos1, self->s.angles );
  329. }
  330. G_Damage( other, other, other, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
  331. return;
  332. }
  333. TeleportPlayer( other, destination->s.pos.trBase, destination->s.angles );
  334. }
  335. static void PortalEnable( gentity_t *self ) {
  336. self->touch = PortalTouch;
  337. self->think = G_FreeEntity;
  338. self->nextthink = level.time + 2 * 60 * 1000;
  339. }
  340. void DropPortalSource( gentity_t *player ) {
  341. gentity_t *ent;
  342. gentity_t *destination;
  343. vec3_t snapped;
  344. // create the portal source
  345. ent = G_Spawn();
  346. ent->s.modelindex = G_ModelIndex( "models/powerups/teleporter/tele_enter.md3" );
  347. VectorCopy( player->s.pos.trBase, snapped );
  348. SnapVector( snapped );
  349. G_SetOrigin( ent, snapped );
  350. VectorCopy( player->r.mins, ent->r.mins );
  351. VectorCopy( player->r.maxs, ent->r.maxs );
  352. ent->classname = "hi_portal source";
  353. ent->s.pos.trType = TR_STATIONARY;
  354. ent->r.contents = CONTENTS_CORPSE | CONTENTS_TRIGGER;
  355. ent->takedamage = qtrue;
  356. ent->health = 200;
  357. ent->die = PortalDie;
  358. trap_LinkEntity( ent );
  359. ent->count = player->client->portalID;
  360. player->client->portalID = 0;
  361. // ent->spawnflags = player->client->ps.persistant[PERS_TEAM];
  362. ent->nextthink = level.time + 1000;
  363. ent->think = PortalEnable;
  364. // find the destination
  365. destination = NULL;
  366. while( (destination = G_Find(destination, FOFS(classname), "hi_portal destination")) != NULL ) {
  367. if( destination->count == ent->count ) {
  368. VectorCopy( destination->s.pos.trBase, ent->pos1 );
  369. break;
  370. }
  371. }
  372. }
  373. #endif