g_monster.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. #include "g_local.h"
  4. //
  5. // monster weapons
  6. //
  7. //FIXME mosnters should call these with a totally accurate direction
  8. // and we can mess it up based on skill. Spread should be for normal
  9. // and we can tighten or loosen based on skill. We could muck with
  10. // the damages too, but I'm not sure that's such a good idea.
  11. void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype)
  12. {
  13. fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
  14. gi.WriteByte (svc_muzzleflash2);
  15. gi.WriteShort (self - g_edicts);
  16. gi.WriteByte (flashtype);
  17. gi.multicast (start, MULTICAST_PVS);
  18. }
  19. void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype)
  20. {
  21. fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN);
  22. gi.WriteByte (svc_muzzleflash2);
  23. gi.WriteShort (self - g_edicts);
  24. gi.WriteByte (flashtype);
  25. gi.multicast (start, MULTICAST_PVS);
  26. }
  27. void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
  28. {
  29. fire_blaster (self, start, dir, damage, speed, effect, false);
  30. gi.WriteByte (svc_muzzleflash2);
  31. gi.WriteShort (self - g_edicts);
  32. gi.WriteByte (flashtype);
  33. gi.multicast (start, MULTICAST_PVS);
  34. }
  35. //ROGUE
  36. void monster_fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
  37. {
  38. fire_blaster2 (self, start, dir, damage, speed, effect, false);
  39. gi.WriteByte (svc_muzzleflash2);
  40. gi.WriteShort (self - g_edicts);
  41. gi.WriteByte (flashtype);
  42. gi.multicast (start, MULTICAST_PVS);
  43. }
  44. // FIXME -- add muzzle flash
  45. void monster_fire_tracker (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, edict_t *enemy, int flashtype)
  46. {
  47. fire_tracker (self, start, dir, damage, speed, enemy);
  48. gi.WriteByte (svc_muzzleflash2);
  49. gi.WriteShort (self - g_edicts);
  50. gi.WriteByte (flashtype);
  51. gi.multicast (start, MULTICAST_PVS);
  52. }
  53. void monster_fire_heat (edict_t *self, vec3_t start, vec3_t dir, vec3_t offset, int damage, int kick, int flashtype)
  54. {
  55. fire_heat (self, start, dir, offset, damage, kick, true);
  56. gi.WriteByte (svc_muzzleflash2);
  57. gi.WriteShort (self - g_edicts);
  58. gi.WriteByte (flashtype);
  59. gi.multicast (start, MULTICAST_PVS);
  60. }
  61. //ROGUE
  62. void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
  63. {
  64. fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
  65. gi.WriteByte (svc_muzzleflash2);
  66. gi.WriteShort (self - g_edicts);
  67. gi.WriteByte (flashtype);
  68. gi.multicast (start, MULTICAST_PVS);
  69. }
  70. void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
  71. {
  72. fire_rocket (self, start, dir, damage, speed, damage+20, damage);
  73. gi.WriteByte (svc_muzzleflash2);
  74. gi.WriteShort (self - g_edicts);
  75. gi.WriteByte (flashtype);
  76. gi.multicast (start, MULTICAST_PVS);
  77. }
  78. void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
  79. {
  80. // PMM
  81. if (!(gi.pointcontents (start) & MASK_SOLID))
  82. fire_rail (self, start, aimdir, damage, kick);
  83. gi.WriteByte (svc_muzzleflash2);
  84. gi.WriteShort (self - g_edicts);
  85. gi.WriteByte (flashtype);
  86. gi.multicast (start, MULTICAST_PVS);
  87. }
  88. void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
  89. {
  90. fire_bfg (self, start, aimdir, damage, speed, damage_radius);
  91. gi.WriteByte (svc_muzzleflash2);
  92. gi.WriteShort (self - g_edicts);
  93. gi.WriteByte (flashtype);
  94. gi.multicast (start, MULTICAST_PVS);
  95. }
  96. //
  97. // Monster utility functions
  98. //
  99. void M_FliesOff (edict_t *self)
  100. {
  101. self->s.effects &= ~EF_FLIES;
  102. self->s.sound = 0;
  103. }
  104. void M_FliesOn (edict_t *self)
  105. {
  106. if (self->waterlevel)
  107. return;
  108. self->s.effects |= EF_FLIES;
  109. self->s.sound = gi.soundindex ("infantry/inflies1.wav");
  110. self->think = M_FliesOff;
  111. self->nextthink = level.time + 60;
  112. }
  113. void M_FlyCheck (edict_t *self)
  114. {
  115. if (self->waterlevel)
  116. return;
  117. if (random() > 0.5)
  118. return;
  119. self->think = M_FliesOn;
  120. self->nextthink = level.time + 5 + 10 * random();
  121. }
  122. void AttackFinished (edict_t *self, float time)
  123. {
  124. self->monsterinfo.attack_finished = level.time + time;
  125. }
  126. void M_CheckGround (edict_t *ent)
  127. {
  128. vec3_t point;
  129. trace_t trace;
  130. if (ent->flags & (FL_SWIM|FL_FLY))
  131. return;
  132. #ifdef ROGUE_GRAVITY
  133. if ((ent->velocity[2] * ent->gravityVector[2]) < -100) // PGM
  134. #else
  135. if (ent->velocity[2] > 100)
  136. #endif
  137. {
  138. ent->groundentity = NULL;
  139. return;
  140. }
  141. // if the hull point one-quarter unit down is solid the entity is on ground
  142. point[0] = ent->s.origin[0];
  143. point[1] = ent->s.origin[1];
  144. #ifdef ROGUE_GRAVITY
  145. point[2] = ent->s.origin[2] + (0.25 * ent->gravityVector[2]); //PGM
  146. #else
  147. point[2] = ent->s.origin[2] - 0.25;
  148. #endif
  149. trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
  150. // check steepness
  151. #ifdef ROGUE_GRAVITY
  152. //PGM
  153. if ( ent->gravityVector[2] < 0) // normal gravity
  154. {
  155. if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
  156. {
  157. ent->groundentity = NULL;
  158. return;
  159. }
  160. }
  161. else // inverted gravity
  162. {
  163. if ( trace.plane.normal[2] > -0.7 && !trace.startsolid)
  164. {
  165. ent->groundentity = NULL;
  166. return;
  167. }
  168. }
  169. //PGM
  170. #else
  171. if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
  172. {
  173. ent->groundentity = NULL;
  174. return;
  175. }
  176. #endif
  177. // ent->groundentity = trace.ent;
  178. // ent->groundentity_linkcount = trace.ent->linkcount;
  179. // if (!trace.startsolid && !trace.allsolid)
  180. // VectorCopy (trace.endpos, ent->s.origin);
  181. if (!trace.startsolid && !trace.allsolid)
  182. {
  183. VectorCopy (trace.endpos, ent->s.origin);
  184. ent->groundentity = trace.ent;
  185. ent->groundentity_linkcount = trace.ent->linkcount;
  186. ent->velocity[2] = 0;
  187. }
  188. }
  189. void M_CatagorizePosition (edict_t *ent)
  190. {
  191. vec3_t point;
  192. int cont;
  193. //
  194. // get waterlevel
  195. //
  196. point[0] = ent->s.origin[0];
  197. point[1] = ent->s.origin[1];
  198. point[2] = ent->s.origin[2] + ent->mins[2] + 1;
  199. cont = gi.pointcontents (point);
  200. if (!(cont & MASK_WATER))
  201. {
  202. ent->waterlevel = 0;
  203. ent->watertype = 0;
  204. return;
  205. }
  206. ent->watertype = cont;
  207. ent->waterlevel = 1;
  208. point[2] += 26;
  209. cont = gi.pointcontents (point);
  210. if (!(cont & MASK_WATER))
  211. return;
  212. ent->waterlevel = 2;
  213. point[2] += 22;
  214. cont = gi.pointcontents (point);
  215. if (cont & MASK_WATER)
  216. ent->waterlevel = 3;
  217. }
  218. void M_WorldEffects (edict_t *ent)
  219. {
  220. int dmg;
  221. if (ent->health > 0)
  222. {
  223. if (!(ent->flags & FL_SWIM))
  224. {
  225. if (ent->waterlevel < 3)
  226. {
  227. ent->air_finished = level.time + 12;
  228. }
  229. else if (ent->air_finished < level.time)
  230. { // drown!
  231. if (ent->pain_debounce_time < level.time)
  232. {
  233. dmg = 2 + 2 * floor(level.time - ent->air_finished);
  234. if (dmg > 15)
  235. dmg = 15;
  236. T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
  237. ent->pain_debounce_time = level.time + 1;
  238. }
  239. }
  240. }
  241. else
  242. {
  243. if (ent->waterlevel > 0)
  244. {
  245. ent->air_finished = level.time + 9;
  246. }
  247. else if (ent->air_finished < level.time)
  248. { // suffocate!
  249. if (ent->pain_debounce_time < level.time)
  250. {
  251. dmg = 2 + 2 * floor(level.time - ent->air_finished);
  252. if (dmg > 15)
  253. dmg = 15;
  254. T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
  255. ent->pain_debounce_time = level.time + 1;
  256. }
  257. }
  258. }
  259. }
  260. if (ent->waterlevel == 0)
  261. {
  262. if (ent->flags & FL_INWATER)
  263. {
  264. gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
  265. ent->flags &= ~FL_INWATER;
  266. }
  267. return;
  268. }
  269. if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
  270. {
  271. if (ent->damage_debounce_time < level.time)
  272. {
  273. ent->damage_debounce_time = level.time + 0.2;
  274. T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
  275. }
  276. }
  277. if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
  278. {
  279. if (ent->damage_debounce_time < level.time)
  280. {
  281. ent->damage_debounce_time = level.time + 1;
  282. T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
  283. }
  284. }
  285. if ( !(ent->flags & FL_INWATER) )
  286. {
  287. if (!(ent->svflags & SVF_DEADMONSTER))
  288. {
  289. if (ent->watertype & CONTENTS_LAVA)
  290. if (random() <= 0.5)
  291. gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
  292. else
  293. gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
  294. else if (ent->watertype & CONTENTS_SLIME)
  295. gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
  296. else if (ent->watertype & CONTENTS_WATER)
  297. gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
  298. }
  299. ent->flags |= FL_INWATER;
  300. ent->damage_debounce_time = 0;
  301. }
  302. }
  303. void M_droptofloor (edict_t *ent)
  304. {
  305. vec3_t end;
  306. trace_t trace;
  307. #ifdef ROGUE_GRAVITY
  308. //PGM
  309. if(ent->gravityVector[2] < 0)
  310. {
  311. ent->s.origin[2] += 1;
  312. VectorCopy (ent->s.origin, end);
  313. end[2] -= 256;
  314. }
  315. else
  316. {
  317. ent->s.origin[2] -= 1;
  318. VectorCopy (ent->s.origin, end);
  319. end[2] += 256;
  320. }
  321. //PGM
  322. #else
  323. ent->s.origin[2] += 1;
  324. VectorCopy (ent->s.origin, end);
  325. end[2] -= 256;
  326. #endif
  327. trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
  328. if (trace.fraction == 1 || trace.allsolid)
  329. return;
  330. VectorCopy (trace.endpos, ent->s.origin);
  331. gi.linkentity (ent);
  332. M_CheckGround (ent);
  333. M_CatagorizePosition (ent);
  334. }
  335. void M_SetEffects (edict_t *ent)
  336. {
  337. int remaining;
  338. ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN|EF_DOUBLE|EF_QUAD|EF_PENT);
  339. ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE|RF_SHELL_DOUBLE);
  340. if (ent->monsterinfo.aiflags & AI_RESURRECTING)
  341. {
  342. ent->s.effects |= EF_COLOR_SHELL;
  343. ent->s.renderfx |= RF_SHELL_RED;
  344. }
  345. if (ent->health <= 0)
  346. return;
  347. if (ent->powerarmor_time > level.time)
  348. {
  349. if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
  350. {
  351. ent->s.effects |= EF_POWERSCREEN;
  352. }
  353. else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
  354. {
  355. ent->s.effects |= EF_COLOR_SHELL;
  356. ent->s.renderfx |= RF_SHELL_GREEN;
  357. }
  358. }
  359. // PMM - new monster powerups
  360. if (ent->monsterinfo.quad_framenum > level.framenum)
  361. {
  362. remaining = ent->monsterinfo.quad_framenum - level.framenum;
  363. if (remaining > 30 || (remaining & 4) )
  364. ent->s.effects |= EF_QUAD;
  365. }
  366. else
  367. ent->s.effects &= ~EF_QUAD;
  368. if (ent->monsterinfo.double_framenum > level.framenum)
  369. {
  370. remaining = ent->monsterinfo.double_framenum - level.framenum;
  371. if (remaining > 30 || (remaining & 4) )
  372. ent->s.effects |= EF_DOUBLE;
  373. }
  374. else
  375. ent->s.effects &= ~EF_DOUBLE;
  376. if (ent->monsterinfo.invincible_framenum > level.framenum)
  377. {
  378. remaining = ent->monsterinfo.invincible_framenum - level.framenum;
  379. if (remaining > 30 || (remaining & 4) )
  380. ent->s.effects |= EF_PENT;
  381. }
  382. else
  383. ent->s.effects &= ~EF_PENT;
  384. // PMM
  385. // PMM - testing
  386. // ent->s.effects |= EF_COLOR_SHELL;
  387. // ent->s.renderfx |= RF_SHELL_HALF_DAM;
  388. /*
  389. if (fmod (level.time, 4.0) > 2.0)
  390. {
  391. gi.dprintf ("invulnerable ");
  392. ent->s.renderfx |= RF_SHELL_RED;
  393. }
  394. else
  395. ent->s.renderfx &= ~RF_SHELL_RED;
  396. if (fmod (level.time, 8.0) > 4.0)
  397. {
  398. gi.dprintf ("shield ");
  399. ent->s.renderfx |= RF_SHELL_GREEN;
  400. }
  401. else
  402. ent->s.renderfx &= ~RF_SHELL_GREEN;
  403. if (fmod (level.time, 16.0) > 8.0)
  404. {
  405. gi.dprintf ("quad ");
  406. ent->s.renderfx |= RF_SHELL_BLUE;\
  407. }
  408. else
  409. ent->s.renderfx &= ~RF_SHELL_BLUE;
  410. if (fmod (level.time, 32.0) > 16.0)
  411. {
  412. gi.dprintf ("double ");
  413. ent->s.renderfx |= RF_SHELL_DOUBLE;
  414. }
  415. else
  416. ent->s.renderfx &= ~RF_SHELL_DOUBLE;
  417. if (fmod (level.time, 64.0) > 32.0)
  418. {
  419. gi.dprintf ("half ");
  420. ent->s.renderfx |= RF_SHELL_HALF_DAM;
  421. }
  422. else
  423. ent->s.renderfx &= ~RF_SHELL_HALF_DAM;
  424. gi.dprintf ("\n");
  425. */
  426. }
  427. void M_MoveFrame (edict_t *self)
  428. {
  429. mmove_t *move;
  430. int index;
  431. move = self->monsterinfo.currentmove;
  432. self->nextthink = level.time + FRAMETIME;
  433. if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
  434. {
  435. self->s.frame = self->monsterinfo.nextframe;
  436. self->monsterinfo.nextframe = 0;
  437. }
  438. else
  439. {
  440. if (self->s.frame == move->lastframe)
  441. {
  442. if (move->endfunc)
  443. {
  444. move->endfunc (self);
  445. // regrab move, endfunc is very likely to change it
  446. move = self->monsterinfo.currentmove;
  447. // check for death
  448. if (self->svflags & SVF_DEADMONSTER)
  449. return;
  450. }
  451. }
  452. if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
  453. {
  454. self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
  455. self->s.frame = move->firstframe;
  456. }
  457. else
  458. {
  459. if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
  460. {
  461. self->s.frame++;
  462. if (self->s.frame > move->lastframe)
  463. self->s.frame = move->firstframe;
  464. }
  465. }
  466. }
  467. index = self->s.frame - move->firstframe;
  468. if (move->frame[index].aifunc)
  469. if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
  470. move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
  471. else
  472. move->frame[index].aifunc (self, 0);
  473. if (move->frame[index].thinkfunc)
  474. move->frame[index].thinkfunc (self);
  475. }
  476. void monster_think (edict_t *self)
  477. {
  478. M_MoveFrame (self);
  479. if (self->linkcount != self->monsterinfo.linkcount)
  480. {
  481. self->monsterinfo.linkcount = self->linkcount;
  482. M_CheckGround (self);
  483. }
  484. M_CatagorizePosition (self);
  485. M_WorldEffects (self);
  486. M_SetEffects (self);
  487. }
  488. /*
  489. ================
  490. monster_use
  491. Using a monster makes it angry at the current activator
  492. ================
  493. */
  494. void monster_use (edict_t *self, edict_t *other, edict_t *activator)
  495. {
  496. if (self->enemy)
  497. return;
  498. if (self->health <= 0)
  499. return;
  500. if (activator->flags & FL_NOTARGET)
  501. return;
  502. if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
  503. return;
  504. if (activator->flags & FL_DISGUISED) // PGM
  505. return; // PGM
  506. // delay reaction so if the monster is teleported, its sound is still heard
  507. self->enemy = activator;
  508. FoundTarget (self);
  509. }
  510. void monster_start_go (edict_t *self);
  511. void monster_triggered_spawn (edict_t *self)
  512. {
  513. self->s.origin[2] += 1;
  514. KillBox (self);
  515. self->solid = SOLID_BBOX;
  516. self->movetype = MOVETYPE_STEP;
  517. self->svflags &= ~SVF_NOCLIENT;
  518. self->air_finished = level.time + 12;
  519. gi.linkentity (self);
  520. monster_start_go (self);
  521. if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
  522. {
  523. if(!(self->enemy->flags & FL_DISGUISED)) // PGM
  524. FoundTarget (self);
  525. else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
  526. self->enemy = NULL;
  527. }
  528. else
  529. {
  530. self->enemy = NULL;
  531. }
  532. }
  533. void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
  534. {
  535. // we have a one frame delay here so we don't telefrag the guy who activated us
  536. self->think = monster_triggered_spawn;
  537. self->nextthink = level.time + FRAMETIME;
  538. if (activator->client)
  539. self->enemy = activator;
  540. self->use = monster_use;
  541. }
  542. void monster_triggered_start (edict_t *self)
  543. {
  544. self->solid = SOLID_NOT;
  545. self->movetype = MOVETYPE_NONE;
  546. self->svflags |= SVF_NOCLIENT;
  547. self->nextthink = 0;
  548. self->use = monster_triggered_spawn_use;
  549. }
  550. /*
  551. ================
  552. monster_death_use
  553. When a monster dies, it fires all of its targets with the current
  554. enemy as activator.
  555. ================
  556. */
  557. void monster_death_use (edict_t *self)
  558. {
  559. self->flags &= ~(FL_FLY|FL_SWIM);
  560. self->monsterinfo.aiflags &= AI_GOOD_GUY;
  561. if (self->item)
  562. {
  563. Drop_Item (self, self->item);
  564. self->item = NULL;
  565. }
  566. if (self->deathtarget)
  567. self->target = self->deathtarget;
  568. if (!self->target)
  569. return;
  570. G_UseTargets (self, self->enemy);
  571. }
  572. //============================================================================
  573. qboolean monster_start (edict_t *self)
  574. {
  575. if (deathmatch->value)
  576. {
  577. G_FreeEdict (self);
  578. return false;
  579. }
  580. if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & AI_GOOD_GUY))
  581. {
  582. self->spawnflags &= ~4;
  583. self->spawnflags |= 1;
  584. // gi.dprintf("fixed spawnflags on %s at %s\n", self->classname, vtos(self->s.origin));
  585. }
  586. if ((!(self->monsterinfo.aiflags & AI_GOOD_GUY)) && (!(self->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
  587. level.total_monsters++;
  588. self->nextthink = level.time + FRAMETIME;
  589. self->svflags |= SVF_MONSTER;
  590. self->s.renderfx |= RF_FRAMELERP;
  591. self->takedamage = DAMAGE_AIM;
  592. self->air_finished = level.time + 12;
  593. self->use = monster_use;
  594. self->max_health = self->health;
  595. self->clipmask = MASK_MONSTERSOLID;
  596. self->s.skinnum = 0;
  597. self->deadflag = DEAD_NO;
  598. self->svflags &= ~SVF_DEADMONSTER;
  599. if (!self->monsterinfo.checkattack)
  600. self->monsterinfo.checkattack = M_CheckAttack;
  601. VectorCopy (self->s.origin, self->s.old_origin);
  602. if (st.item)
  603. {
  604. self->item = FindItemByClassname (st.item);
  605. if (!self->item)
  606. gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
  607. }
  608. // randomize what frame they start on
  609. if (self->monsterinfo.currentmove)
  610. self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
  611. // PMM - get this so I don't have to do it in all of the monsters
  612. self->monsterinfo.base_height = self->maxs[2];
  613. // PMM - clear these
  614. self->monsterinfo.quad_framenum = 0;
  615. self->monsterinfo.double_framenum = 0;
  616. self->monsterinfo.invincible_framenum = 0;
  617. return true;
  618. }
  619. void monster_start_go (edict_t *self)
  620. {
  621. vec3_t v;
  622. if (self->health <= 0)
  623. return;
  624. // check for target to combat_point and change to combattarget
  625. if (self->target)
  626. {
  627. qboolean notcombat;
  628. qboolean fixup;
  629. edict_t *target;
  630. target = NULL;
  631. notcombat = false;
  632. fixup = false;
  633. while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
  634. {
  635. if (strcmp(target->classname, "point_combat") == 0)
  636. {
  637. self->combattarget = self->target;
  638. fixup = true;
  639. }
  640. else
  641. {
  642. notcombat = true;
  643. }
  644. }
  645. if (notcombat && self->combattarget)
  646. gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
  647. if (fixup)
  648. self->target = NULL;
  649. }
  650. // validate combattarget
  651. if (self->combattarget)
  652. {
  653. edict_t *target;
  654. target = NULL;
  655. while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
  656. {
  657. if (strcmp(target->classname, "point_combat") != 0)
  658. {
  659. gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
  660. self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
  661. self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
  662. (int)target->s.origin[2]);
  663. }
  664. }
  665. }
  666. if (self->target)
  667. {
  668. self->goalentity = self->movetarget = G_PickTarget(self->target);
  669. if (!self->movetarget)
  670. {
  671. gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
  672. self->target = NULL;
  673. self->monsterinfo.pausetime = 100000000;
  674. self->monsterinfo.stand (self);
  675. }
  676. else if (strcmp (self->movetarget->classname, "path_corner") == 0)
  677. {
  678. VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
  679. self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
  680. self->monsterinfo.walk (self);
  681. self->target = NULL;
  682. }
  683. else
  684. {
  685. self->goalentity = self->movetarget = NULL;
  686. self->monsterinfo.pausetime = 100000000;
  687. self->monsterinfo.stand (self);
  688. }
  689. }
  690. else
  691. {
  692. self->monsterinfo.pausetime = 100000000;
  693. self->monsterinfo.stand (self);
  694. }
  695. self->think = monster_think;
  696. self->nextthink = level.time + FRAMETIME;
  697. }
  698. void walkmonster_start_go (edict_t *self)
  699. {
  700. if (!(self->spawnflags & 2) && level.time < 1)
  701. {
  702. M_droptofloor (self);
  703. if (self->groundentity)
  704. if (!M_walkmove (self, 0, 0))
  705. gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
  706. }
  707. if (!self->yaw_speed)
  708. self->yaw_speed = 20;
  709. // PMM - stalkers are too short for this
  710. if (!(strcmp(self->classname, "monster_stalker")))
  711. self->viewheight = 15;
  712. else
  713. self->viewheight = 25;
  714. monster_start_go (self);
  715. if (self->spawnflags & 2)
  716. monster_triggered_start (self);
  717. }
  718. void walkmonster_start (edict_t *self)
  719. {
  720. self->think = walkmonster_start_go;
  721. monster_start (self);
  722. }
  723. void flymonster_start_go (edict_t *self)
  724. {
  725. if (!M_walkmove (self, 0, 0))
  726. gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
  727. if (!self->yaw_speed)
  728. self->yaw_speed = 10;
  729. self->viewheight = 25;
  730. monster_start_go (self);
  731. if (self->spawnflags & 2)
  732. monster_triggered_start (self);
  733. }
  734. void flymonster_start (edict_t *self)
  735. {
  736. self->flags |= FL_FLY;
  737. self->think = flymonster_start_go;
  738. monster_start (self);
  739. }
  740. void swimmonster_start_go (edict_t *self)
  741. {
  742. if (!self->yaw_speed)
  743. self->yaw_speed = 10;
  744. self->viewheight = 10;
  745. monster_start_go (self);
  746. if (self->spawnflags & 2)
  747. monster_triggered_start (self);
  748. }
  749. void swimmonster_start (edict_t *self)
  750. {
  751. self->flags |= FL_SWIM;
  752. self->think = swimmonster_start_go;
  753. monster_start (self);
  754. }
  755. //ROGUE
  756. void stationarymonster_start_go (edict_t *self);
  757. void stationarymonster_triggered_spawn (edict_t *self)
  758. {
  759. KillBox (self);
  760. self->solid = SOLID_BBOX;
  761. self->movetype = MOVETYPE_NONE;
  762. self->svflags &= ~SVF_NOCLIENT;
  763. self->air_finished = level.time + 12;
  764. gi.linkentity (self);
  765. // FIXME - why doesn't this happen with real monsters?
  766. self->spawnflags &= ~2;
  767. stationarymonster_start_go (self);
  768. if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
  769. {
  770. if(!(self->enemy->flags & FL_DISGUISED)) // PGM
  771. FoundTarget (self);
  772. else // PMM - just in case, make sure to clear the enemy so FindTarget doesn't get confused
  773. self->enemy = NULL;
  774. }
  775. else
  776. {
  777. self->enemy = NULL;
  778. }
  779. }
  780. void stationarymonster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
  781. {
  782. // we have a one frame delay here so we don't telefrag the guy who activated us
  783. self->think = stationarymonster_triggered_spawn;
  784. self->nextthink = level.time + FRAMETIME;
  785. if (activator->client)
  786. self->enemy = activator;
  787. self->use = monster_use;
  788. }
  789. void stationarymonster_triggered_start (edict_t *self)
  790. {
  791. self->solid = SOLID_NOT;
  792. self->movetype = MOVETYPE_NONE;
  793. self->svflags |= SVF_NOCLIENT;
  794. self->nextthink = 0;
  795. self->use = stationarymonster_triggered_spawn_use;
  796. }
  797. void stationarymonster_start_go (edict_t *self)
  798. {
  799. // PGM - only turrets use this, so remove the error message. They're supposed to be in solid.
  800. // if (!M_walkmove (self, 0, 0))
  801. // gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
  802. if (!self->yaw_speed)
  803. self->yaw_speed = 20;
  804. // self->viewheight = 25;
  805. monster_start_go (self);
  806. if (self->spawnflags & 2)
  807. stationarymonster_triggered_start (self);
  808. }
  809. void stationarymonster_start (edict_t *self)
  810. {
  811. self->think = stationarymonster_start_go;
  812. monster_start (self);
  813. }
  814. void monster_done_dodge (edict_t *self)
  815. {
  816. // if ((g_showlogic) && (g_showlogic->value))
  817. // gi.dprintf ("%s done dodging\n", self->classname);
  818. self->monsterinfo.aiflags &= ~AI_DODGING;
  819. }
  820. //ROGUE