m_medic.c 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. MEDIC
  6. ==============================================================================
  7. */
  8. #include "g_local.h"
  9. #include "m_medic.h"
  10. #define MEDIC_MIN_DISTANCE 32
  11. #define MEDIC_MAX_HEAL_DISTANCE 400
  12. #define MEDIC_TRY_TIME 10.0
  13. // FIXME -
  14. //
  15. // owner moved to monsterinfo.healer instead
  16. //
  17. // For some reason, the healed monsters are rarely ending up in the floor
  18. //
  19. // 5/15/1998 I think I fixed these, keep an eye on them
  20. qboolean visible (edict_t *self, edict_t *other);
  21. void M_SetEffects (edict_t *ent);
  22. qboolean FindTarget (edict_t *self);
  23. void HuntTarget (edict_t *self);
  24. void FoundTarget (edict_t *self);
  25. char *ED_NewString (char *string);
  26. void spawngrow_think (edict_t *self);
  27. void SpawnGrow_Spawn (vec3_t startpos, int size);
  28. void ED_CallSpawn (edict_t *ent);
  29. void M_FliesOff (edict_t *self);
  30. void M_FliesOn (edict_t *self);
  31. static int sound_idle1;
  32. static int sound_pain1;
  33. static int sound_pain2;
  34. static int sound_die;
  35. static int sound_sight;
  36. static int sound_search;
  37. static int sound_hook_launch;
  38. static int sound_hook_hit;
  39. static int sound_hook_heal;
  40. static int sound_hook_retract;
  41. // PMM - commander sounds
  42. static int commander_sound_idle1;
  43. static int commander_sound_pain1;
  44. static int commander_sound_pain2;
  45. static int commander_sound_die;
  46. static int commander_sound_sight;
  47. static int commander_sound_search;
  48. static int commander_sound_hook_launch;
  49. static int commander_sound_hook_hit;
  50. static int commander_sound_hook_heal;
  51. static int commander_sound_hook_retract;
  52. static int commander_sound_spawn;
  53. char * reinforcements[] = {
  54. {"monster_soldier_light"}, // 0
  55. {"monster_soldier"}, // 1
  56. {"monster_soldier_ss"}, // 2
  57. {"monster_infantry"}, // 3
  58. {"monster_gunner"}, // 4
  59. // {"monster_chick"}, // 4
  60. {"monster_medic"}, // 5
  61. {"monster_gladiator"} // 6
  62. };
  63. vec3_t reinforcement_mins[] = {
  64. {-16, -16, -24},
  65. {-16, -16, -24},
  66. {-16, -16, -24},
  67. {-16, -16, -24},
  68. {-16, -16, -24},
  69. {-16, -16, -24},
  70. {-32, -32, -24}
  71. };
  72. vec3_t reinforcement_maxs[] = {
  73. {16, 16, 32},
  74. {16, 16, 32},
  75. {16, 16, 32},
  76. {16, 16, 32},
  77. {16, 16, 32},
  78. {16, 16, 32},
  79. {32, 32, 64}
  80. };
  81. vec3_t reinforcement_position[] = {
  82. {80, 0, 0},
  83. {40, 60, 0},
  84. {40, -60, 0},
  85. {0, 80, 0},
  86. {0, -80, 0}
  87. };
  88. void cleanupHeal (edict_t *self, qboolean change_frame)
  89. {
  90. // clean up target, if we have one and it's legit
  91. if (self->enemy && self->enemy->inuse)
  92. {
  93. self->enemy->monsterinfo.healer = NULL;
  94. self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING;
  95. self->enemy->takedamage = DAMAGE_YES;
  96. M_SetEffects (self->enemy);
  97. }
  98. if (change_frame)
  99. self->monsterinfo.nextframe = FRAME_attack52;
  100. }
  101. void abortHeal (edict_t *self, qboolean change_frame, qboolean gib, qboolean mark)
  102. {
  103. int hurt;
  104. static vec3_t pain_normal = { 0, 0, 1 };
  105. // clean up target
  106. cleanupHeal (self, change_frame);
  107. // gib em!
  108. if ((mark) && (self->enemy) && (self->enemy->inuse))
  109. {
  110. // if ((g_showlogic) && (g_showlogic->value))
  111. // gi.dprintf ("%s - marking target as bad\n", self->classname);
  112. // if the first badMedic slot is filled by a medic, skip it and use the second one
  113. if ((self->enemy->monsterinfo.badMedic1) && (self->enemy->monsterinfo.badMedic1->inuse)
  114. && (!strncmp(self->enemy->monsterinfo.badMedic1->classname, "monster_medic", 13)) )
  115. {
  116. self->enemy->monsterinfo.badMedic2 = self;
  117. }
  118. else
  119. {
  120. self->enemy->monsterinfo.badMedic1 = self;
  121. }
  122. }
  123. if ((gib) && (self->enemy) && (self->enemy->inuse))
  124. {
  125. // if ((g_showlogic) && (g_showlogic->value))
  126. // gi.dprintf ("%s - gibbing bad heal target", self->classname);
  127. if(self->enemy->gib_health)
  128. hurt = - self->enemy->gib_health;
  129. else
  130. hurt = 500;
  131. T_Damage (self->enemy, self, self, vec3_origin, self->enemy->s.origin,
  132. pain_normal, hurt, 0, 0, MOD_UNKNOWN);
  133. }
  134. // clean up self
  135. self->monsterinfo.aiflags &= ~AI_MEDIC;
  136. if ((self->oldenemy) && (self->oldenemy->inuse))
  137. self->enemy = self->oldenemy;
  138. else
  139. self->enemy = NULL;
  140. self->monsterinfo.medicTries = 0;
  141. }
  142. qboolean canReach (edict_t *self, edict_t *other)
  143. {
  144. vec3_t spot1;
  145. vec3_t spot2;
  146. trace_t trace;
  147. VectorCopy (self->s.origin, spot1);
  148. spot1[2] += self->viewheight;
  149. VectorCopy (other->s.origin, spot2);
  150. spot2[2] += other->viewheight;
  151. trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_SHOT|MASK_WATER);
  152. if (trace.fraction == 1.0 || trace.ent == other) // PGM
  153. return true;
  154. return false;
  155. }
  156. edict_t *medic_FindDeadMonster (edict_t *self)
  157. {
  158. float radius;
  159. edict_t *ent = NULL;
  160. edict_t *best = NULL;
  161. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  162. radius = MEDIC_MAX_HEAL_DISTANCE;
  163. else
  164. radius = 1024;
  165. while ((ent = findradius(ent, self->s.origin, radius)) != NULL)
  166. {
  167. if (ent == self)
  168. continue;
  169. if (!(ent->svflags & SVF_MONSTER))
  170. continue;
  171. if (ent->monsterinfo.aiflags & AI_GOOD_GUY)
  172. continue;
  173. // check to make sure we haven't bailed on this guy already
  174. if ((ent->monsterinfo.badMedic1 == self) || (ent->monsterinfo.badMedic2 == self))
  175. continue;
  176. if (ent->monsterinfo.healer)
  177. // FIXME - this is correcting a bug that is somewhere else
  178. // if the healer is a monster, and it's in medic mode .. continue .. otherwise
  179. // we will override the healer, if it passes all the other tests
  180. if ((ent->monsterinfo.healer->inuse) && (ent->monsterinfo.healer->health > 0) &&
  181. (ent->monsterinfo.healer->svflags & SVF_MONSTER) && (ent->monsterinfo.healer->monsterinfo.aiflags & AI_MEDIC))
  182. continue;
  183. if (ent->health > 0)
  184. continue;
  185. if ((ent->nextthink) && !((ent->think == M_FliesOn) || (ent->think == M_FliesOff)))
  186. continue;
  187. if (!visible(self, ent))
  188. // if (!canReach(self, ent))
  189. continue;
  190. if (!strncmp(ent->classname, "player", 6)) // stop it from trying to heal player_noise entities
  191. continue;
  192. // FIXME - there's got to be a better way ..
  193. // make sure we don't spawn people right on top of us
  194. if (realrange(self, ent) <= MEDIC_MIN_DISTANCE)
  195. continue;
  196. if (!best)
  197. {
  198. best = ent;
  199. continue;
  200. }
  201. if (ent->max_health <= best->max_health)
  202. continue;
  203. best = ent;
  204. }
  205. if (best)
  206. self->timestamp = level.time + MEDIC_TRY_TIME;
  207. return best;
  208. }
  209. void medic_idle (edict_t *self)
  210. {
  211. edict_t *ent;
  212. // PMM - commander sounds
  213. if (self->mass == 400)
  214. gi.sound (self, CHAN_VOICE, sound_idle1, 1, ATTN_IDLE, 0);
  215. else
  216. gi.sound (self, CHAN_VOICE, commander_sound_idle1, 1, ATTN_IDLE, 0);
  217. if (!self->oldenemy)
  218. {
  219. ent = medic_FindDeadMonster(self);
  220. if (ent)
  221. {
  222. self->oldenemy = self->enemy;
  223. self->enemy = ent;
  224. self->enemy->monsterinfo.healer = self;
  225. self->monsterinfo.aiflags |= AI_MEDIC;
  226. FoundTarget (self);
  227. }
  228. }
  229. }
  230. void medic_search (edict_t *self)
  231. {
  232. edict_t *ent;
  233. // PMM - commander sounds
  234. if (self->mass == 400)
  235. gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_IDLE, 0);
  236. else
  237. gi.sound (self, CHAN_VOICE, commander_sound_search, 1, ATTN_IDLE, 0);
  238. if (!self->oldenemy)
  239. {
  240. ent = medic_FindDeadMonster(self);
  241. if (ent)
  242. {
  243. self->oldenemy = self->enemy;
  244. self->enemy = ent;
  245. self->enemy->monsterinfo.healer = self;
  246. self->monsterinfo.aiflags |= AI_MEDIC;
  247. FoundTarget (self);
  248. }
  249. }
  250. }
  251. void medic_sight (edict_t *self, edict_t *other)
  252. {
  253. // PMM - commander sounds
  254. if (self->mass == 400)
  255. gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
  256. else
  257. gi.sound (self, CHAN_VOICE, commander_sound_sight, 1, ATTN_NORM, 0);
  258. }
  259. mframe_t medic_frames_stand [] =
  260. {
  261. ai_stand, 0, medic_idle,
  262. ai_stand, 0, NULL,
  263. ai_stand, 0, NULL,
  264. ai_stand, 0, NULL,
  265. ai_stand, 0, NULL,
  266. ai_stand, 0, NULL,
  267. ai_stand, 0, NULL,
  268. ai_stand, 0, NULL,
  269. ai_stand, 0, NULL,
  270. ai_stand, 0, NULL,
  271. ai_stand, 0, NULL,
  272. ai_stand, 0, NULL,
  273. ai_stand, 0, NULL,
  274. ai_stand, 0, NULL,
  275. ai_stand, 0, NULL,
  276. ai_stand, 0, NULL,
  277. ai_stand, 0, NULL,
  278. ai_stand, 0, NULL,
  279. ai_stand, 0, NULL,
  280. ai_stand, 0, NULL,
  281. ai_stand, 0, NULL,
  282. ai_stand, 0, NULL,
  283. ai_stand, 0, NULL,
  284. ai_stand, 0, NULL,
  285. ai_stand, 0, NULL,
  286. ai_stand, 0, NULL,
  287. ai_stand, 0, NULL,
  288. ai_stand, 0, NULL,
  289. ai_stand, 0, NULL,
  290. ai_stand, 0, NULL,
  291. ai_stand, 0, NULL,
  292. ai_stand, 0, NULL,
  293. ai_stand, 0, NULL,
  294. ai_stand, 0, NULL,
  295. ai_stand, 0, NULL,
  296. ai_stand, 0, NULL,
  297. ai_stand, 0, NULL,
  298. ai_stand, 0, NULL,
  299. ai_stand, 0, NULL,
  300. ai_stand, 0, NULL,
  301. ai_stand, 0, NULL,
  302. ai_stand, 0, NULL,
  303. ai_stand, 0, NULL,
  304. ai_stand, 0, NULL,
  305. ai_stand, 0, NULL,
  306. ai_stand, 0, NULL,
  307. ai_stand, 0, NULL,
  308. ai_stand, 0, NULL,
  309. ai_stand, 0, NULL,
  310. ai_stand, 0, NULL,
  311. ai_stand, 0, NULL,
  312. ai_stand, 0, NULL,
  313. ai_stand, 0, NULL,
  314. ai_stand, 0, NULL,
  315. ai_stand, 0, NULL,
  316. ai_stand, 0, NULL,
  317. ai_stand, 0, NULL,
  318. ai_stand, 0, NULL,
  319. ai_stand, 0, NULL,
  320. ai_stand, 0, NULL,
  321. ai_stand, 0, NULL,
  322. ai_stand, 0, NULL,
  323. ai_stand, 0, NULL,
  324. ai_stand, 0, NULL,
  325. ai_stand, 0, NULL,
  326. ai_stand, 0, NULL,
  327. ai_stand, 0, NULL,
  328. ai_stand, 0, NULL,
  329. ai_stand, 0, NULL,
  330. ai_stand, 0, NULL,
  331. ai_stand, 0, NULL,
  332. ai_stand, 0, NULL,
  333. ai_stand, 0, NULL,
  334. ai_stand, 0, NULL,
  335. ai_stand, 0, NULL,
  336. ai_stand, 0, NULL,
  337. ai_stand, 0, NULL,
  338. ai_stand, 0, NULL,
  339. ai_stand, 0, NULL,
  340. ai_stand, 0, NULL,
  341. ai_stand, 0, NULL,
  342. ai_stand, 0, NULL,
  343. ai_stand, 0, NULL,
  344. ai_stand, 0, NULL,
  345. ai_stand, 0, NULL,
  346. ai_stand, 0, NULL,
  347. ai_stand, 0, NULL,
  348. ai_stand, 0, NULL,
  349. ai_stand, 0, NULL,
  350. ai_stand, 0, NULL,
  351. };
  352. mmove_t medic_move_stand = {FRAME_wait1, FRAME_wait90, medic_frames_stand, NULL};
  353. void medic_stand (edict_t *self)
  354. {
  355. self->monsterinfo.currentmove = &medic_move_stand;
  356. }
  357. mframe_t medic_frames_walk [] =
  358. {
  359. ai_walk, 6.2, NULL,
  360. ai_walk, 18.1, NULL,
  361. ai_walk, 1, NULL,
  362. ai_walk, 9, NULL,
  363. ai_walk, 10, NULL,
  364. ai_walk, 9, NULL,
  365. ai_walk, 11, NULL,
  366. ai_walk, 11.6, NULL,
  367. ai_walk, 2, NULL,
  368. ai_walk, 9.9, NULL,
  369. ai_walk, 14, NULL,
  370. ai_walk, 9.3, NULL
  371. };
  372. mmove_t medic_move_walk = {FRAME_walk1, FRAME_walk12, medic_frames_walk, NULL};
  373. void medic_walk (edict_t *self)
  374. {
  375. self->monsterinfo.currentmove = &medic_move_walk;
  376. }
  377. mframe_t medic_frames_run [] =
  378. {
  379. ai_run, 18, NULL,
  380. ai_run, 22.5, NULL,
  381. ai_run, 25.4, monster_done_dodge,
  382. ai_run, 23.4, NULL,
  383. ai_run, 24, NULL,
  384. ai_run, 35.6, NULL //pmm
  385. };
  386. mmove_t medic_move_run = {FRAME_run1, FRAME_run6, medic_frames_run, NULL};
  387. void medic_run (edict_t *self)
  388. {
  389. monster_done_dodge (self);
  390. if (!(self->monsterinfo.aiflags & AI_MEDIC))
  391. {
  392. edict_t *ent;
  393. ent = medic_FindDeadMonster(self);
  394. if (ent)
  395. {
  396. self->oldenemy = self->enemy;
  397. self->enemy = ent;
  398. self->enemy->monsterinfo.healer = self;
  399. self->monsterinfo.aiflags |= AI_MEDIC;
  400. FoundTarget (self);
  401. return;
  402. }
  403. }
  404. // else if (!canReach(self, self->enemy))
  405. // {
  406. // abortHeal (self, 0);
  407. // }
  408. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  409. self->monsterinfo.currentmove = &medic_move_stand;
  410. else
  411. self->monsterinfo.currentmove = &medic_move_run;
  412. }
  413. mframe_t medic_frames_pain1 [] =
  414. {
  415. ai_move, 0, NULL,
  416. ai_move, 0, NULL,
  417. ai_move, 0, NULL,
  418. ai_move, 0, NULL,
  419. ai_move, 0, NULL,
  420. ai_move, 0, NULL,
  421. ai_move, 0, NULL,
  422. ai_move, 0, NULL
  423. };
  424. mmove_t medic_move_pain1 = {FRAME_paina1, FRAME_paina8, medic_frames_pain1, medic_run};
  425. mframe_t medic_frames_pain2 [] =
  426. {
  427. ai_move, 0, NULL,
  428. ai_move, 0, NULL,
  429. ai_move, 0, NULL,
  430. ai_move, 0, NULL,
  431. ai_move, 0, NULL,
  432. ai_move, 0, NULL,
  433. ai_move, 0, NULL,
  434. ai_move, 0, NULL,
  435. ai_move, 0, NULL,
  436. ai_move, 0, NULL,
  437. ai_move, 0, NULL,
  438. ai_move, 0, NULL,
  439. ai_move, 0, NULL,
  440. ai_move, 0, NULL,
  441. ai_move, 0, NULL
  442. };
  443. mmove_t medic_move_pain2 = {FRAME_painb1, FRAME_painb15, medic_frames_pain2, medic_run};
  444. void medic_pain (edict_t *self, edict_t *other, float kick, int damage)
  445. {
  446. monster_done_dodge (self);
  447. if ((self->health < (self->max_health / 2)))
  448. if (self->mass > 400)
  449. self->s.skinnum = 3;
  450. else
  451. self->s.skinnum = 1;
  452. if (level.time < self->pain_debounce_time)
  453. return;
  454. self->pain_debounce_time = level.time + 3;
  455. if (skill->value == 3)
  456. return; // no pain anims in nightmare
  457. // if we're healing someone, we ignore pain
  458. if (self->monsterinfo.aiflags & AI_MEDIC)
  459. return;
  460. if (self->mass > 400)
  461. {
  462. if (damage < 35)
  463. {
  464. gi.sound (self, CHAN_VOICE, commander_sound_pain1, 1, ATTN_NORM, 0);
  465. return;
  466. }
  467. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  468. self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
  469. gi.sound (self, CHAN_VOICE, commander_sound_pain2, 1, ATTN_NORM, 0);
  470. if (random() < (min(((float)damage * 0.005), 0.5))) // no more than 50% chance of big pain
  471. self->monsterinfo.currentmove = &medic_move_pain2;
  472. else
  473. self->monsterinfo.currentmove = &medic_move_pain1;
  474. }
  475. else if (random() < 0.5)
  476. {
  477. self->monsterinfo.currentmove = &medic_move_pain1;
  478. gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NORM, 0);
  479. }
  480. else
  481. {
  482. self->monsterinfo.currentmove = &medic_move_pain2;
  483. gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
  484. }
  485. // PMM - clear duck flag
  486. if (self->monsterinfo.aiflags & AI_DUCKED)
  487. monster_duck_up(self);
  488. }
  489. void medic_fire_blaster (edict_t *self)
  490. {
  491. vec3_t start;
  492. vec3_t forward, right;
  493. vec3_t end;
  494. vec3_t dir;
  495. int effect;
  496. int damage = 2;
  497. // paranoia checking
  498. if (!(self->enemy && self->enemy->inuse))
  499. return;
  500. if ((self->s.frame == FRAME_attack9) || (self->s.frame == FRAME_attack12))
  501. effect = EF_BLASTER;
  502. else if ((self->s.frame == FRAME_attack19) || (self->s.frame == FRAME_attack22) || (self->s.frame == FRAME_attack25) || (self->s.frame == FRAME_attack28))
  503. effect = EF_HYPERBLASTER;
  504. else
  505. effect = 0;
  506. AngleVectors (self->s.angles, forward, right, NULL);
  507. G_ProjectSource (self->s.origin, monster_flash_offset[MZ2_MEDIC_BLASTER_1], forward, right, start);
  508. VectorCopy (self->enemy->s.origin, end);
  509. end[2] += self->enemy->viewheight;
  510. VectorSubtract (end, start, dir);
  511. if (!strcmp(self->enemy->classname, "tesla"))
  512. damage = 3;
  513. // medic commander shoots blaster2
  514. if (self->mass > 400)
  515. monster_fire_blaster2 (self, start, dir, damage, 1000, MZ2_MEDIC_BLASTER_2, effect);
  516. else
  517. monster_fire_blaster (self, start, dir, damage, 1000, MZ2_MEDIC_BLASTER_1, effect);
  518. }
  519. void medic_dead (edict_t *self)
  520. {
  521. VectorSet (self->mins, -16, -16, -24);
  522. VectorSet (self->maxs, 16, 16, -8);
  523. self->movetype = MOVETYPE_TOSS;
  524. self->svflags |= SVF_DEADMONSTER;
  525. self->nextthink = 0;
  526. gi.linkentity (self);
  527. }
  528. mframe_t medic_frames_death [] =
  529. {
  530. ai_move, 0, NULL,
  531. ai_move, 0, NULL,
  532. ai_move, 0, NULL,
  533. ai_move, 0, NULL,
  534. ai_move, 0, NULL,
  535. ai_move, 0, NULL,
  536. ai_move, 0, NULL,
  537. ai_move, 0, NULL,
  538. ai_move, 0, NULL,
  539. ai_move, 0, NULL,
  540. ai_move, 0, NULL,
  541. ai_move, 0, NULL,
  542. ai_move, 0, NULL,
  543. ai_move, 0, NULL,
  544. ai_move, 0, NULL,
  545. ai_move, 0, NULL,
  546. ai_move, 0, NULL,
  547. ai_move, 0, NULL,
  548. ai_move, 0, NULL,
  549. ai_move, 0, NULL,
  550. ai_move, 0, NULL,
  551. ai_move, 0, NULL,
  552. ai_move, 0, NULL,
  553. ai_move, 0, NULL,
  554. ai_move, 0, NULL,
  555. ai_move, 0, NULL,
  556. ai_move, 0, NULL,
  557. ai_move, 0, NULL,
  558. ai_move, 0, NULL,
  559. ai_move, 0, NULL
  560. };
  561. mmove_t medic_move_death = {FRAME_death1, FRAME_death30, medic_frames_death, medic_dead};
  562. void medic_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  563. {
  564. int n;
  565. // if we had a pending patient, he was already freed up in Killed
  566. // check for gib
  567. if (self->health <= self->gib_health)
  568. {
  569. gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
  570. for (n= 0; n < 2; n++)
  571. ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
  572. for (n= 0; n < 4; n++)
  573. ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
  574. ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
  575. self->deadflag = DEAD_DEAD;
  576. return;
  577. }
  578. if (self->deadflag == DEAD_DEAD)
  579. return;
  580. // regular death
  581. // PMM
  582. if (self->mass == 400)
  583. gi.sound (self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
  584. else
  585. gi.sound (self, CHAN_VOICE, commander_sound_die, 1, ATTN_NORM, 0);
  586. //
  587. self->deadflag = DEAD_DEAD;
  588. self->takedamage = DAMAGE_YES;
  589. self->monsterinfo.currentmove = &medic_move_death;
  590. }
  591. mframe_t medic_frames_duck [] =
  592. {
  593. ai_move, -1, NULL,
  594. ai_move, -1, NULL,
  595. ai_move, -1, monster_duck_down,
  596. ai_move, -1, monster_duck_hold,
  597. ai_move, -1, NULL,
  598. ai_move, -1, NULL,
  599. ai_move, -1, NULL, // PMM - duck up used to be here
  600. ai_move, -1, NULL,
  601. ai_move, -1, NULL,
  602. ai_move, -1, NULL,
  603. ai_move, -1, NULL,
  604. ai_move, -1, NULL,
  605. ai_move, -1, NULL,
  606. ai_move, -1, monster_duck_up,
  607. ai_move, -1, NULL,
  608. ai_move, -1, NULL
  609. };
  610. mmove_t medic_move_duck = {FRAME_duck1, FRAME_duck16, medic_frames_duck, medic_run};
  611. // PMM -- moved dodge code to after attack code so I can reference attack frames
  612. mframe_t medic_frames_attackHyperBlaster [] =
  613. {
  614. ai_charge, 0, NULL,
  615. ai_charge, 0, NULL,
  616. ai_charge, 0, NULL,
  617. ai_charge, 0, NULL,
  618. ai_charge, 0, medic_fire_blaster,
  619. ai_charge, 0, medic_fire_blaster,
  620. ai_charge, 0, medic_fire_blaster,
  621. ai_charge, 0, medic_fire_blaster,
  622. ai_charge, 0, medic_fire_blaster,
  623. ai_charge, 0, medic_fire_blaster,
  624. ai_charge, 0, medic_fire_blaster,
  625. ai_charge, 0, medic_fire_blaster,
  626. ai_charge, 0, medic_fire_blaster,
  627. ai_charge, 0, medic_fire_blaster,
  628. ai_charge, 0, medic_fire_blaster,
  629. ai_charge, 0, medic_fire_blaster
  630. };
  631. mmove_t medic_move_attackHyperBlaster = {FRAME_attack15, FRAME_attack30, medic_frames_attackHyperBlaster, medic_run};
  632. void medic_continue (edict_t *self)
  633. {
  634. if (visible (self, self->enemy) )
  635. if (random() <= 0.95)
  636. self->monsterinfo.currentmove = &medic_move_attackHyperBlaster;
  637. }
  638. mframe_t medic_frames_attackBlaster [] =
  639. {
  640. ai_charge, 0, NULL,
  641. ai_charge, 5, NULL,
  642. ai_charge, 5, NULL,
  643. ai_charge, 3, NULL,
  644. ai_charge, 2, NULL,
  645. ai_charge, 0, NULL,
  646. ai_charge, 0, NULL,
  647. ai_charge, 0, NULL,
  648. ai_charge, 0, medic_fire_blaster,
  649. ai_charge, 0, NULL,
  650. ai_charge, 0, NULL,
  651. ai_charge, 0, medic_fire_blaster,
  652. ai_charge, 0, NULL,
  653. ai_charge, 0, medic_continue // Change to medic_continue... Else, go to frame 32
  654. };
  655. mmove_t medic_move_attackBlaster = {FRAME_attack1, FRAME_attack14, medic_frames_attackBlaster, medic_run};
  656. void medic_hook_launch (edict_t *self)
  657. {
  658. // PMM - commander sounds
  659. if (self->mass == 400)
  660. gi.sound (self, CHAN_WEAPON, sound_hook_launch, 1, ATTN_NORM, 0);
  661. else
  662. gi.sound (self, CHAN_WEAPON, commander_sound_hook_launch, 1, ATTN_NORM, 0);
  663. }
  664. static vec3_t medic_cable_offsets[] =
  665. {
  666. 45.0, -9.2, 15.5,
  667. 48.4, -9.7, 15.2,
  668. 47.8, -9.8, 15.8,
  669. 47.3, -9.3, 14.3,
  670. 45.4, -10.1, 13.1,
  671. 41.9, -12.7, 12.0,
  672. 37.8, -15.8, 11.2,
  673. 34.3, -18.4, 10.7,
  674. 32.7, -19.7, 10.4,
  675. 32.7, -19.7, 10.4
  676. };
  677. void medic_cable_attack (edict_t *self)
  678. {
  679. vec3_t offset, start, end, f, r;
  680. trace_t tr;
  681. vec3_t dir;
  682. // vec3_t angles;
  683. float distance;
  684. if ((!self->enemy) || (!self->enemy->inuse) || (self->enemy->s.effects & EF_GIB))
  685. {
  686. // if ((g_showlogic) && (g_showlogic->value))
  687. // gi.dprintf ("medic_commander - aborting heal due to target's disappearance\n");
  688. abortHeal (self, true, false, false);
  689. return;
  690. }
  691. // see if our enemy has changed to a client, or our target has more than 0 health,
  692. // abort it .. we got switched to someone else due to damage
  693. if ((self->enemy->client) || (self->enemy->health > 0))
  694. {
  695. abortHeal (self, true, false, false);
  696. return;
  697. }
  698. AngleVectors (self->s.angles, f, r, NULL);
  699. VectorCopy (medic_cable_offsets[self->s.frame - FRAME_attack42], offset);
  700. G_ProjectSource (self->s.origin, offset, f, r, start);
  701. // check for max distance
  702. // not needed, done in checkattack
  703. // check for min distance
  704. VectorSubtract (start, self->enemy->s.origin, dir);
  705. distance = VectorLength(dir);
  706. if (distance < MEDIC_MIN_DISTANCE)
  707. {
  708. // if ((g_showlogic) && (g_showlogic->value))
  709. // gi.dprintf ("medic_commander - aborting heal due to proximity to target ");
  710. abortHeal (self, true, true, false );
  711. return;
  712. }
  713. // if ((g_showlogic)&&(g_showlogic->value))
  714. // gi.dprintf ("distance to target is %f\n", distance);
  715. // if (distance > 300)
  716. // return;
  717. // check for min/max pitch
  718. // PMM -- took out since it doesn't look bad when it fails
  719. // vectoangles (dir, angles);
  720. // if (angles[0] < -180)
  721. // angles[0] += 360;
  722. // if (fabs(angles[0]) > 45)
  723. // {
  724. // if ((g_showlogic) && (g_showlogic->value))
  725. // gi.dprintf ("medic_commander - aborting heal due to bad angle\n");
  726. // abortHeal(self);
  727. // return;
  728. // }
  729. tr = gi.trace (start, NULL, NULL, self->enemy->s.origin, self, MASK_SOLID);
  730. if (tr.fraction != 1.0 && tr.ent != self->enemy)
  731. {
  732. if (tr.ent == world)
  733. {
  734. // give up on second try
  735. if (self->monsterinfo.medicTries > 1)
  736. {
  737. abortHeal (self, true, false, true);
  738. return;
  739. }
  740. self->monsterinfo.medicTries++;
  741. cleanupHeal (self, 1);
  742. return;
  743. }
  744. // if ((g_showlogic) && (g_showlogic->value))
  745. // gi.dprintf ("medic_commander - aborting heal due to beamus interruptus\n");
  746. abortHeal (self, true, false, false);
  747. return;
  748. }
  749. if (self->s.frame == FRAME_attack43)
  750. {
  751. // PMM - commander sounds
  752. if (self->mass == 400)
  753. gi.sound (self->enemy, CHAN_AUTO, sound_hook_hit, 1, ATTN_NORM, 0);
  754. else
  755. gi.sound (self->enemy, CHAN_AUTO, commander_sound_hook_hit, 1, ATTN_NORM, 0);
  756. self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
  757. self->enemy->takedamage = DAMAGE_NO;
  758. M_SetEffects (self->enemy);
  759. }
  760. else if (self->s.frame == FRAME_attack50)
  761. {
  762. vec3_t maxs;
  763. self->enemy->spawnflags = 0;
  764. self->enemy->monsterinfo.aiflags = 0;
  765. self->enemy->target = NULL;
  766. self->enemy->targetname = NULL;
  767. self->enemy->combattarget = NULL;
  768. self->enemy->deathtarget = NULL;
  769. self->enemy->monsterinfo.healer = self;
  770. VectorCopy (self->enemy->maxs, maxs);
  771. maxs[2] += 48; // compensate for change when they die
  772. tr = gi.trace (self->enemy->s.origin, self->enemy->mins, maxs, self->enemy->s.origin, self->enemy, MASK_MONSTERSOLID);
  773. if (tr.startsolid || tr.allsolid)
  774. {
  775. // if ((g_showlogic) && (g_showlogic->value))
  776. // gi.dprintf ("Spawn point obstructed, aborting heal!\n");
  777. abortHeal (self, true, true, false);
  778. return;
  779. }
  780. else if (tr.ent != world)
  781. {
  782. // if ((g_showlogic) && (g_showlogic->value))
  783. // gi.dprintf("heal in entity %s\n", tr.ent->classname);
  784. abortHeal (self, true, true, false);
  785. return;
  786. }
  787. /* else if (tr.ent == world)
  788. {
  789. if ((g_showlogic) && (g_showlogic->value))
  790. gi.dprintf ("heal in world, aborting!\n");
  791. abortHeal (self, 1);
  792. return;
  793. }
  794. */ else
  795. {
  796. self->enemy->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
  797. ED_CallSpawn (self->enemy);
  798. if (self->enemy->think)
  799. {
  800. self->enemy->nextthink = level.time;
  801. self->enemy->think (self->enemy);
  802. }
  803. // self->enemy->monsterinfo.aiflags |= AI_RESURRECTING;
  804. self->enemy->monsterinfo.aiflags &= ~AI_RESURRECTING;
  805. self->enemy->monsterinfo.aiflags |= AI_IGNORE_SHOTS|AI_DO_NOT_COUNT;
  806. // turn off flies
  807. self->enemy->s.effects &= ~EF_FLIES;
  808. self->enemy->monsterinfo.healer = NULL;
  809. if ((self->oldenemy) && (self->oldenemy->inuse) && (self->oldenemy->health > 0))
  810. {
  811. // if ((g_showlogic) && (g_showlogic->value))
  812. // gi.dprintf ("setting heal target's enemy to %s\n", self->oldenemy->classname);
  813. self->enemy->enemy = self->oldenemy;
  814. // HuntTarget (self->enemy);
  815. FoundTarget (self->enemy);
  816. }
  817. else
  818. {
  819. // if (g_showlogic && g_showlogic->value)
  820. // gi.dprintf ("no valid enemy to set!\n");
  821. self->enemy->enemy = NULL;
  822. if (!FindTarget (self->enemy))
  823. {
  824. // no valid enemy, so stop acting
  825. self->enemy->monsterinfo.pausetime = level.time + 100000000;
  826. self->enemy->monsterinfo.stand (self->enemy);
  827. }
  828. self->enemy = NULL;
  829. self->oldenemy = NULL;
  830. if (!FindTarget (self))
  831. {
  832. // no valid enemy, so stop acting
  833. self->monsterinfo.pausetime = level.time + 100000000;
  834. self->monsterinfo.stand (self);
  835. return;
  836. }
  837. }
  838. }
  839. }
  840. else
  841. {
  842. if (self->s.frame == FRAME_attack44)
  843. // PMM - medic commander sounds
  844. if (self->mass == 400)
  845. gi.sound (self, CHAN_WEAPON, sound_hook_heal, 1, ATTN_NORM, 0);
  846. else
  847. gi.sound (self, CHAN_WEAPON, commander_sound_hook_heal, 1, ATTN_NORM, 0);
  848. }
  849. // adjust start for beam origin being in middle of a segment
  850. VectorMA (start, 8, f, start);
  851. // adjust end z for end spot since the monster is currently dead
  852. VectorCopy (self->enemy->s.origin, end);
  853. end[2] = self->enemy->absmin[2] + self->enemy->size[2] / 2;
  854. gi.WriteByte (svc_temp_entity);
  855. gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
  856. gi.WriteShort (self - g_edicts);
  857. gi.WritePosition (start);
  858. gi.WritePosition (end);
  859. gi.multicast (self->s.origin, MULTICAST_PVS);
  860. }
  861. void medic_hook_retract (edict_t *self)
  862. {
  863. if (self->mass == 400)
  864. gi.sound (self, CHAN_WEAPON, sound_hook_retract, 1, ATTN_NORM, 0);
  865. else
  866. gi.sound (self, CHAN_WEAPON, sound_hook_retract, 1, ATTN_NORM, 0);
  867. self->monsterinfo.aiflags &= ~AI_MEDIC;
  868. if ((self->oldenemy) && (self->oldenemy->inuse))
  869. self->enemy = self->oldenemy;
  870. else
  871. {
  872. self->enemy = NULL;
  873. self->oldenemy = NULL;
  874. if (!FindTarget (self))
  875. {
  876. // no valid enemy, so stop acting
  877. self->monsterinfo.pausetime = level.time + 100000000;
  878. self->monsterinfo.stand (self);
  879. return;
  880. }
  881. }
  882. }
  883. mframe_t medic_frames_attackCable [] =
  884. {
  885. // ROGUE - negated 36-40 so he scoots back from his target a little
  886. // ROGUE - switched 33-36 to ai_charge
  887. // ROGUE - changed frame 52 to 0 to compensate for changes in 36-40
  888. ai_charge, 2, NULL, //33
  889. ai_charge, 3, NULL,
  890. ai_charge, 5, NULL,
  891. ai_charge, -4.4, NULL, //36
  892. ai_charge, -4.7, NULL, //37
  893. ai_charge, -5, NULL,
  894. ai_charge, -6, NULL,
  895. ai_charge, -4, NULL, //40
  896. ai_charge, 0, NULL,
  897. ai_move, 0, medic_hook_launch, //42
  898. ai_move, 0, medic_cable_attack, //43
  899. ai_move, 0, medic_cable_attack,
  900. ai_move, 0, medic_cable_attack,
  901. ai_move, 0, medic_cable_attack,
  902. ai_move, 0, medic_cable_attack,
  903. ai_move, 0, medic_cable_attack,
  904. ai_move, 0, medic_cable_attack,
  905. ai_move, 0, medic_cable_attack,
  906. ai_move, 0, medic_cable_attack, //51
  907. ai_move, 0, medic_hook_retract, //52
  908. ai_move, -1.5, NULL,
  909. ai_move, -1.2, NULL,
  910. ai_move, -3, NULL,
  911. ai_move, -2, NULL,
  912. ai_move, 0.3, NULL,
  913. ai_move, 0.7, NULL,
  914. ai_move, 1.2, NULL,
  915. ai_move, 1.3, NULL //60
  916. };
  917. mmove_t medic_move_attackCable = {FRAME_attack33, FRAME_attack60, medic_frames_attackCable, medic_run};
  918. void medic_start_spawn (edict_t *self)
  919. {
  920. gi.sound (self, CHAN_WEAPON, commander_sound_spawn, 1, ATTN_NORM, 0);
  921. self->monsterinfo.nextframe = FRAME_attack48;
  922. }
  923. void medic_determine_spawn (edict_t *self)
  924. {
  925. vec3_t f, r, offset, startpoint, spawnpoint;
  926. float lucky;
  927. int summonStr;
  928. int count;
  929. int inc;
  930. int num_summoned; // should be 1, 3, or 5
  931. int num_success = 0;
  932. lucky = random();
  933. summonStr = skill->value;
  934. // bell curve - 0 = 67%, 1 = 93%, 2 = 99% -- too steep
  935. // this ends up with
  936. // -3 = 5%
  937. // -2 = 10%
  938. // -1 = 15%
  939. // 0 = 40%
  940. // +1 = 15%
  941. // +2 = 10%
  942. // +3 = 5%
  943. if (lucky < 0.05)
  944. summonStr -= 3;
  945. else if (lucky < 0.15)
  946. summonStr -= 2;
  947. else if (lucky < 0.3)
  948. summonStr -= 1;
  949. else if (lucky > 0.95)
  950. summonStr += 3;
  951. else if (lucky > 0.85)
  952. summonStr += 2;
  953. else if (lucky > 0.7)
  954. summonStr += 1;
  955. if (summonStr < 0)
  956. summonStr = 0;
  957. //FIXME - need to remember this, might as well use this int that isn't used for monsters
  958. self->plat2flags = summonStr;
  959. AngleVectors (self->s.angles, f, r, NULL);
  960. // this yields either 1, 3, or 5
  961. if (summonStr)
  962. num_summoned = (summonStr - 1) + (summonStr % 2);
  963. else
  964. num_summoned = 1;
  965. // if ((g_showlogic) && (g_showlogic->value))
  966. // gi.dprintf ("medic_commander: summonStr = %d num = %d\n", summonStr, num_summoned);
  967. for (count = 0; count < num_summoned; count++)
  968. {
  969. inc = count + (count%2); // 0, 2, 2, 4, 4
  970. VectorCopy (reinforcement_position[count], offset);
  971. G_ProjectSource (self->s.origin, offset, f, r, startpoint);
  972. // a little off the ground
  973. startpoint[2] += 10;
  974. if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
  975. {
  976. if (CheckGroundSpawnPoint(spawnpoint,
  977. reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc],
  978. 256, -1))
  979. {
  980. num_success++;
  981. // we found a spot, we're done here
  982. count = num_summoned;
  983. }
  984. // else if ((g_showlogic) && (g_showlogic->value))
  985. // gi.dprintf ("medic_commander: CheckGroundSpawnPoint says bad stuff down there!\n");
  986. }
  987. }
  988. if (num_success == 0)
  989. {
  990. for (count = 0; count < num_summoned; count++)
  991. {
  992. inc = count + (count%2); // 0, 2, 2, 4, 4
  993. VectorCopy (reinforcement_position[count], offset);
  994. // check behind
  995. offset[0] *= -1.0;
  996. offset[1] *= -1.0;
  997. G_ProjectSource (self->s.origin, offset, f, r, startpoint);
  998. // a little off the ground
  999. startpoint[2] += 10;
  1000. if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
  1001. {
  1002. if (CheckGroundSpawnPoint(spawnpoint,
  1003. reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc],
  1004. 256, -1))
  1005. {
  1006. num_success++;
  1007. // we found a spot, we're done here
  1008. count = num_summoned;
  1009. }
  1010. }
  1011. }
  1012. if (num_success)
  1013. {
  1014. self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  1015. self->ideal_yaw = anglemod(self->s.angles[YAW]) + 180;
  1016. if (self->ideal_yaw > 360.0)
  1017. self->ideal_yaw -= 360.0;
  1018. // self->plat2flags *= -1;
  1019. }
  1020. }
  1021. if (num_success == 0)
  1022. {
  1023. // if ((g_showlogic) && (g_showlogic->value))
  1024. // gi.dprintf ("medic_commander: failed to find any spawn points, aborting!\n");
  1025. self->monsterinfo.nextframe = FRAME_attack53;
  1026. }
  1027. }
  1028. void medic_spawngrows (edict_t *self)
  1029. {
  1030. vec3_t f, r, offset, startpoint, spawnpoint;
  1031. int summonStr;
  1032. int count;
  1033. int inc;
  1034. int num_summoned; // should be 1, 3, or 5
  1035. int num_success = 0;
  1036. float current_yaw;
  1037. qboolean behind = false;
  1038. // if we've been directed to turn around
  1039. if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
  1040. {
  1041. current_yaw = anglemod(self->s.angles[YAW]);
  1042. if (fabs(current_yaw - self->ideal_yaw) > 0.1)
  1043. {
  1044. self->monsterinfo.aiflags |= AI_HOLD_FRAME;
  1045. return;
  1046. }
  1047. // done turning around
  1048. self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
  1049. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  1050. }
  1051. // if (self->plat2flags < 0)
  1052. // {
  1053. // summonStr = -1.0 * self->plat2flags;
  1054. // behind = true;
  1055. // }
  1056. // else
  1057. summonStr = self->plat2flags;
  1058. AngleVectors (self->s.angles, f, r, NULL);
  1059. // num_summoned = ((((summonStr-1)/2)+1)*2)-1; // this yields either 1, 3, or 5
  1060. if (summonStr)
  1061. num_summoned = (summonStr - 1) + (summonStr % 2);
  1062. else
  1063. num_summoned = 1;
  1064. for (count = 0; count < num_summoned; count++)
  1065. {
  1066. inc = count + (count%2); // 0, 2, 2, 4, 4
  1067. VectorCopy (reinforcement_position[count], offset);
  1068. G_ProjectSource (self->s.origin, offset, f, r, startpoint);
  1069. // a little off the ground
  1070. startpoint[2] += 10;
  1071. if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
  1072. {
  1073. if (CheckGroundSpawnPoint(spawnpoint,
  1074. reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc],
  1075. 256, -1))
  1076. {
  1077. num_success++;
  1078. if ((summonStr-inc) > 3)
  1079. SpawnGrow_Spawn (spawnpoint, 1); // big monster
  1080. else
  1081. SpawnGrow_Spawn (spawnpoint, 0); // normal size
  1082. }
  1083. }
  1084. }
  1085. if (num_success == 0)
  1086. {
  1087. // if ((g_showlogic) && (g_showlogic->value))
  1088. // gi.dprintf ("medic_commander: spawngrows bad, aborting!\n");
  1089. self->monsterinfo.nextframe = FRAME_attack53;
  1090. }
  1091. }
  1092. void medic_finish_spawn (edict_t *self)
  1093. {
  1094. edict_t *ent;
  1095. vec3_t f, r, offset, startpoint, spawnpoint;
  1096. int summonStr;
  1097. int count;
  1098. int inc;
  1099. int num_summoned; // should be 1, 3, or 5
  1100. qboolean behind = false;
  1101. edict_t *designated_enemy;
  1102. // trace_t tr;
  1103. // vec3_t mins, maxs;
  1104. // this is one bigger than the soldier's real mins .. just for paranoia's sake
  1105. // VectorSet (mins, -17, -17, -25);
  1106. // VectorSet (maxs, 17, 17, 33);
  1107. //FIXME - better place to store this info?
  1108. if (self->plat2flags < 0)
  1109. {
  1110. behind = true;
  1111. self->plat2flags *= -1;
  1112. }
  1113. summonStr = self->plat2flags;
  1114. AngleVectors (self->s.angles, f, r, NULL);
  1115. // num_summoned = ((((summonStr-1)/2)+1)*2)-1; // this yields either 1, 3, or 5
  1116. if (summonStr)
  1117. num_summoned = (summonStr - 1) + (summonStr % 2);
  1118. else
  1119. num_summoned = 1;
  1120. // if ((g_showlogic) && (g_showlogic->value))
  1121. // gi.dprintf ("medic_commander: summonStr = %d num = %d\n", summonStr, num_summoned);
  1122. for (count = 0; count < num_summoned; count++)
  1123. {
  1124. inc = count + (count%2); // 0, 2, 2, 4, 4
  1125. VectorCopy (reinforcement_position[count], offset);
  1126. G_ProjectSource (self->s.origin, offset, f, r, startpoint);
  1127. // a little off the ground
  1128. startpoint[2] += 10;
  1129. ent = NULL;
  1130. if (FindSpawnPoint (startpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc], spawnpoint, 32))
  1131. {
  1132. if (CheckSpawnPoint (spawnpoint, reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc]))
  1133. ent = CreateGroundMonster (spawnpoint, self->s.angles,
  1134. reinforcement_mins[summonStr-inc], reinforcement_maxs[summonStr-inc],
  1135. reinforcements[summonStr-inc], 256);
  1136. // else if ((g_showlogic) && (g_showlogic->value))
  1137. // gi.dprintf ("CheckSpawnPoint failed volume check!\n");
  1138. }
  1139. else
  1140. {
  1141. // if ((g_showlogic) && (g_showlogic->value))
  1142. // gi.dprintf ("FindSpawnPoint failed to find a point!\n");
  1143. }
  1144. if (!ent)
  1145. {
  1146. // if ((g_showlogic) && (g_showlogic->value))
  1147. // gi.dprintf ("Spawn point obstructed for %s, aborting!\n", reinforcements[summonStr-inc]);
  1148. continue;
  1149. }
  1150. // gi.sound (self, CHAN_WEAPON, commander_sound_spawn, 1, ATTN_NORM, 0);
  1151. if (ent->think)
  1152. {
  1153. ent->nextthink = level.time;
  1154. ent->think (ent);
  1155. }
  1156. ent->monsterinfo.aiflags |= AI_IGNORE_SHOTS|AI_DO_NOT_COUNT|AI_SPAWNED_MEDIC_C;
  1157. ent->monsterinfo.commander = self;
  1158. self->monsterinfo.monster_slots--;
  1159. // if ((g_showlogic) && (g_showlogic->value))
  1160. // gi.dprintf ("medic_commander: %d slots remaining\n", self->monsterinfo.monster_slots);
  1161. if (self->monsterinfo.aiflags & AI_MEDIC)
  1162. designated_enemy = self->oldenemy;
  1163. else
  1164. designated_enemy = self->enemy;
  1165. if (coop && coop->value)
  1166. {
  1167. designated_enemy = PickCoopTarget(ent);
  1168. if (designated_enemy)
  1169. {
  1170. // try to avoid using my enemy
  1171. if (designated_enemy == self->enemy)
  1172. {
  1173. designated_enemy = PickCoopTarget(ent);
  1174. if (designated_enemy)
  1175. {
  1176. // if ((g_showlogic) && (g_showlogic->value))
  1177. // {
  1178. // gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
  1179. // if (designated_enemy->client)
  1180. // gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
  1181. // else
  1182. // gi.dprintf ("NOT A CLIENT\n");
  1183. // }
  1184. }
  1185. else
  1186. {
  1187. // if ((g_showlogic) && (g_showlogic->value))
  1188. // gi.dprintf ("pick coop failed, using my current enemy\n");
  1189. designated_enemy = self->enemy;
  1190. }
  1191. }
  1192. }
  1193. else
  1194. {
  1195. // if ((g_showlogic) && (g_showlogic->value))
  1196. // gi.dprintf ("pick coop failed, using my current enemy\n");
  1197. designated_enemy = self->enemy;
  1198. }
  1199. }
  1200. if ((designated_enemy) && (designated_enemy->inuse) && (designated_enemy->health > 0))
  1201. {
  1202. // fixme
  1203. // if ((g_showlogic) && (g_showlogic -> value))
  1204. // gi.dprintf ("setting enemy to %s\n", designated_enemy->classname);
  1205. ent->enemy = designated_enemy;
  1206. FoundTarget (ent);
  1207. }
  1208. else
  1209. {
  1210. ent->enemy = NULL;
  1211. ent->monsterinfo.stand (ent);
  1212. }
  1213. // ent->s.event = EV_PLAYER_TELEPORT;
  1214. }
  1215. }
  1216. mframe_t medic_frames_callReinforcements [] =
  1217. {
  1218. // ROGUE - 33-36 now ai_charge
  1219. ai_charge, 2, NULL, //33
  1220. ai_charge, 3, NULL,
  1221. ai_charge, 5, NULL,
  1222. ai_charge, 4.4, NULL, //36
  1223. ai_charge, 4.7, NULL,
  1224. ai_charge, 5, NULL,
  1225. ai_charge, 6, NULL,
  1226. ai_charge, 4, NULL, //40
  1227. ai_charge, 0, NULL,
  1228. ai_move, 0, medic_start_spawn, //42
  1229. ai_move, 0, NULL, //43 -- 43 through 47 are skipped
  1230. ai_move, 0, NULL,
  1231. ai_move, 0, NULL,
  1232. ai_move, 0, NULL,
  1233. ai_move, 0, NULL,
  1234. ai_move, 0, medic_determine_spawn, //48
  1235. ai_charge, 0, medic_spawngrows, //49
  1236. ai_move, 0, NULL, //50
  1237. ai_move, 0, NULL, //51
  1238. ai_move, -15, medic_finish_spawn, //52
  1239. ai_move, -1.5, NULL,
  1240. ai_move, -1.2, NULL,
  1241. ai_move, -3, NULL,
  1242. ai_move, -2, NULL,
  1243. ai_move, 0.3, NULL,
  1244. ai_move, 0.7, NULL,
  1245. ai_move, 1.2, NULL,
  1246. ai_move, 1.3, NULL //60
  1247. };
  1248. mmove_t medic_move_callReinforcements = {FRAME_attack33, FRAME_attack60, medic_frames_callReinforcements, medic_run};
  1249. void medic_attack(edict_t *self)
  1250. {
  1251. int enemy_range;
  1252. float r;
  1253. monster_done_dodge (self);
  1254. enemy_range = range(self, self->enemy);
  1255. // signal from checkattack to spawn
  1256. if (self->monsterinfo.aiflags & AI_BLOCKED)
  1257. {
  1258. self->monsterinfo.currentmove = &medic_move_callReinforcements;
  1259. self->monsterinfo.aiflags &= ~AI_BLOCKED;
  1260. }
  1261. r = random();
  1262. if (self->monsterinfo.aiflags & AI_MEDIC)
  1263. {
  1264. if ((self->mass > 400) && (r > 0.8) && (self->monsterinfo.monster_slots > 2))
  1265. self->monsterinfo.currentmove = &medic_move_callReinforcements;
  1266. else
  1267. self->monsterinfo.currentmove = &medic_move_attackCable;
  1268. }
  1269. else
  1270. {
  1271. if (self->monsterinfo.attack_state == AS_BLIND)
  1272. {
  1273. self->monsterinfo.currentmove = &medic_move_callReinforcements;
  1274. return;
  1275. }
  1276. if ((self->mass > 400) && (r > 0.2) && (enemy_range != RANGE_MELEE) && (self->monsterinfo.monster_slots > 2))
  1277. self->monsterinfo.currentmove = &medic_move_callReinforcements;
  1278. else
  1279. self->monsterinfo.currentmove = &medic_move_attackBlaster;
  1280. }
  1281. }
  1282. qboolean medic_checkattack (edict_t *self)
  1283. {
  1284. if (self->monsterinfo.aiflags & AI_MEDIC)
  1285. {
  1286. // if our target went away
  1287. if ((!self->enemy) || (!self->enemy->inuse))
  1288. {
  1289. // if (g_showlogic && g_showlogic->value)
  1290. // gi.dprintf ("aborting heal target due to gib\n");
  1291. abortHeal (self, true, false, false);
  1292. return false;
  1293. }
  1294. // if we ran out of time, give up
  1295. if (self->timestamp < level.time)
  1296. {
  1297. // if (g_showlogic && g_showlogic->value)
  1298. // gi.dprintf ("aborting heal target (%s) due to time\n", self->enemy->classname);
  1299. abortHeal (self, true, false, true);
  1300. self->timestamp = 0;
  1301. return false;
  1302. }
  1303. if (realrange(self, self->enemy) < MEDIC_MAX_HEAL_DISTANCE+10)
  1304. {
  1305. medic_attack(self);
  1306. return true;
  1307. }
  1308. else
  1309. {
  1310. self->monsterinfo.attack_state = AS_STRAIGHT;
  1311. return false;
  1312. }
  1313. }
  1314. if (self->enemy->client && !visible (self, self->enemy) && (self->monsterinfo.monster_slots > 2))
  1315. {
  1316. self->monsterinfo.attack_state = AS_BLIND;
  1317. return true;
  1318. }
  1319. // give a LARGE bias to spawning things when we have room
  1320. // use AI_BLOCKED as a signal to attack to spawn
  1321. if ((random() < 0.8) && (self->monsterinfo.monster_slots > 5) && (realrange(self, self->enemy) > 150))
  1322. {
  1323. self->monsterinfo.aiflags |= AI_BLOCKED;
  1324. self->monsterinfo.attack_state = AS_MISSILE;
  1325. return true;
  1326. }
  1327. // ROGUE
  1328. // since his idle animation looks kinda bad in combat, if we're not in easy mode, always attack
  1329. // when he's on a combat point
  1330. if (skill->value > 0)
  1331. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  1332. {
  1333. self->monsterinfo.attack_state = AS_MISSILE;
  1334. return true;
  1335. }
  1336. return M_CheckAttack (self);
  1337. }
  1338. /*
  1339. void medic_dodge (edict_t *self, edict_t *attacker, float eta, trace_t *tr)
  1340. {
  1341. if (random() > 0.25)
  1342. return;
  1343. if (!self->enemy)
  1344. self->enemy = attacker;
  1345. self->monsterinfo.currentmove = &medic_move_duck;
  1346. //===========
  1347. //PMM - rogue rewrite of dodge code.
  1348. float r;
  1349. float height;
  1350. int shooting = 0;
  1351. // this needs to be before the AI_MEDIC check, because
  1352. // a) if AI_MEDIC is set, we should have an enemy anyway and
  1353. // b) FoundTarget calls medic_run, which can set AI_MEDIC
  1354. if (!self->enemy)
  1355. {
  1356. self->enemy = attacker;
  1357. FoundTarget (self);
  1358. }
  1359. // don't dodge if you're healing
  1360. if (self->monsterinfo.aiflags & AI_MEDIC)
  1361. return;
  1362. // PMM - don't bother if it's going to hit anyway; fix for weird in-your-face etas (I was
  1363. // seeing numbers like 13 and 14)
  1364. if ((eta < 0.1) || (eta > 5))
  1365. return;
  1366. r = random();
  1367. if (r > (0.25*((skill->value)+1)))
  1368. return;
  1369. if ((self->monsterinfo.currentmove == &medic_move_attackHyperBlaster) ||
  1370. (self->monsterinfo.currentmove == &medic_move_attackCable) ||
  1371. (self->monsterinfo.currentmove == &medic_move_attackBlaster))
  1372. {
  1373. shooting = 1;
  1374. }
  1375. if (self->monsterinfo.aiflags & AI_DODGING)
  1376. {
  1377. height = self->absmax[2];
  1378. }
  1379. else
  1380. {
  1381. height = self->absmax[2]-32-1; // the -1 is because the absmax is s.origin + maxs + 1
  1382. }
  1383. // check to see if it makes sense to duck
  1384. if (tr->endpos[2] <= height)
  1385. {
  1386. vec3_t right, diff;
  1387. if (shooting)
  1388. {
  1389. self->monsterinfo.attack_state = AS_SLIDING;
  1390. return;
  1391. }
  1392. AngleVectors (self->s.angles, NULL, right, NULL);
  1393. VectorSubtract (tr->endpos, self->s.origin, diff);
  1394. if (DotProduct (right, diff) < 0)
  1395. {
  1396. self->monsterinfo.lefty = 1;
  1397. }
  1398. // if it doesn't sense to duck, try to strafe away
  1399. monster_done_dodge (self);
  1400. self->monsterinfo.currentmove = &medic_move_run;
  1401. self->monsterinfo.attack_state = AS_SLIDING;
  1402. return;
  1403. }
  1404. if (skill->value == 0)
  1405. {
  1406. self->monsterinfo.currentmove = &medic_move_duck;
  1407. // PMM - stupid dodge
  1408. self->monsterinfo.duck_wait_time = level.time + eta + 1;
  1409. self->monsterinfo.aiflags |= AI_DODGING;
  1410. return;
  1411. }
  1412. if (!shooting)
  1413. {
  1414. self->monsterinfo.currentmove = &medic_move_duck;
  1415. self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
  1416. self->monsterinfo.aiflags |= AI_DODGING;
  1417. }
  1418. return;
  1419. //PMM
  1420. //===========
  1421. }
  1422. */
  1423. void MedicCommanderCache (void)
  1424. {
  1425. edict_t *newEnt;
  1426. int modelidx;
  1427. int i;
  1428. //FIXME - better way to do this? this is quick and dirty
  1429. for (i=0; i < 7; i++)
  1430. {
  1431. newEnt = G_Spawn();
  1432. VectorCopy(vec3_origin, newEnt->s.origin);
  1433. VectorCopy(vec3_origin, newEnt->s.angles);
  1434. newEnt->classname = ED_NewString (reinforcements[i]);
  1435. newEnt->monsterinfo.aiflags |= AI_DO_NOT_COUNT;
  1436. ED_CallSpawn(newEnt);
  1437. // FIXME - could copy mins/maxs into reinforcements from here
  1438. G_FreeEdict (newEnt);
  1439. }
  1440. modelidx = gi.modelindex("models/items/spawngro/tris.md2");
  1441. modelidx = gi.modelindex("models/items/spawngro2/tris.md2");
  1442. }
  1443. void medic_duck (edict_t *self, float eta)
  1444. {
  1445. // don't dodge if you're healing
  1446. if (self->monsterinfo.aiflags & AI_MEDIC)
  1447. return;
  1448. if ((self->monsterinfo.currentmove == &medic_move_attackHyperBlaster) ||
  1449. (self->monsterinfo.currentmove == &medic_move_attackCable) ||
  1450. (self->monsterinfo.currentmove == &medic_move_attackBlaster) ||
  1451. (self->monsterinfo.currentmove == &medic_move_callReinforcements))
  1452. {
  1453. // he ignores skill
  1454. self->monsterinfo.aiflags &= ~AI_DUCKED;
  1455. return;
  1456. }
  1457. if (skill->value == 0)
  1458. // PMM - stupid dodge
  1459. self->monsterinfo.duck_wait_time = level.time + eta + 1;
  1460. else
  1461. self->monsterinfo.duck_wait_time = level.time + eta + (0.1 * (3 - skill->value));
  1462. // has to be done immediately otherwise he can get stuck
  1463. monster_duck_down(self);
  1464. self->monsterinfo.nextframe = FRAME_duck1;
  1465. self->monsterinfo.currentmove = &medic_move_duck;
  1466. return;
  1467. }
  1468. void medic_sidestep (edict_t *self)
  1469. {
  1470. if ((self->monsterinfo.currentmove == &medic_move_attackHyperBlaster) ||
  1471. (self->monsterinfo.currentmove == &medic_move_attackCable) ||
  1472. (self->monsterinfo.currentmove == &medic_move_attackBlaster) ||
  1473. (self->monsterinfo.currentmove == &medic_move_callReinforcements))
  1474. {
  1475. // if we're shooting, and not on easy, don't dodge
  1476. if (skill->value)
  1477. {
  1478. self->monsterinfo.aiflags &= ~AI_DODGING;
  1479. return;
  1480. }
  1481. }
  1482. if (self->monsterinfo.currentmove != &medic_move_run)
  1483. self->monsterinfo.currentmove = &medic_move_run;
  1484. }
  1485. //===========
  1486. //PGM
  1487. qboolean medic_blocked (edict_t *self, float dist)
  1488. {
  1489. if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
  1490. return true;
  1491. if(blocked_checkplat (self, dist))
  1492. return true;
  1493. return false;
  1494. }
  1495. //PGM
  1496. //===========
  1497. /*QUAKED monster_medic_commander (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
  1498. */
  1499. /*QUAKED monster_medic (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
  1500. */
  1501. void SP_monster_medic (edict_t *self)
  1502. {
  1503. if (deathmatch->value)
  1504. {
  1505. G_FreeEdict (self);
  1506. return;
  1507. }
  1508. self->movetype = MOVETYPE_STEP;
  1509. self->solid = SOLID_BBOX;
  1510. self->s.modelindex = gi.modelindex ("models/monsters/medic/tris.md2");
  1511. VectorSet (self->mins, -24, -24, -24);
  1512. VectorSet (self->maxs, 24, 24, 32);
  1513. //PMM
  1514. if (strcmp(self->classname, "monster_medic_commander") == 0)
  1515. {
  1516. self->health = 600; // fixme
  1517. self->gib_health = -130;
  1518. self->mass = 600;
  1519. self->yaw_speed = 40; // default is 20
  1520. MedicCommanderCache();
  1521. // self->s.skinnum = 2;
  1522. }
  1523. else
  1524. {
  1525. //PMM
  1526. self->health = 300;
  1527. self->gib_health = -130;
  1528. self->mass = 400;
  1529. // self->s.skinnum = 0;
  1530. }
  1531. self->pain = medic_pain;
  1532. self->die = medic_die;
  1533. self->monsterinfo.stand = medic_stand;
  1534. self->monsterinfo.walk = medic_walk;
  1535. self->monsterinfo.run = medic_run;
  1536. // pmm
  1537. self->monsterinfo.dodge = M_MonsterDodge;
  1538. self->monsterinfo.duck = medic_duck;
  1539. self->monsterinfo.unduck = monster_duck_up;
  1540. self->monsterinfo.sidestep = medic_sidestep;
  1541. // self->monsterinfo.dodge = medic_dodge;
  1542. // pmm
  1543. self->monsterinfo.attack = medic_attack;
  1544. self->monsterinfo.melee = NULL;
  1545. self->monsterinfo.sight = medic_sight;
  1546. self->monsterinfo.idle = medic_idle;
  1547. self->monsterinfo.search = medic_search;
  1548. self->monsterinfo.checkattack = medic_checkattack;
  1549. self->monsterinfo.blocked = medic_blocked;
  1550. gi.linkentity (self);
  1551. self->monsterinfo.currentmove = &medic_move_stand;
  1552. self->monsterinfo.scale = MODEL_SCALE;
  1553. walkmonster_start (self);
  1554. //PMM
  1555. self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
  1556. if (self->mass > 400)
  1557. {
  1558. self->s.skinnum = 2;
  1559. if (skill->value == 0)
  1560. self->monsterinfo.monster_slots = 3;
  1561. else if (skill->value == 1)
  1562. self->monsterinfo.monster_slots = 4;
  1563. else if (skill->value == 2)
  1564. self->monsterinfo.monster_slots = 6;
  1565. else if (skill->value == 3)
  1566. self->monsterinfo.monster_slots = 6;
  1567. // commander sounds
  1568. commander_sound_idle1 = gi.soundindex ("medic_commander/medidle.wav");
  1569. commander_sound_pain1 = gi.soundindex ("medic_commander/medpain1.wav");
  1570. commander_sound_pain2 = gi.soundindex ("medic_commander/medpain2.wav");
  1571. commander_sound_die = gi.soundindex ("medic_commander/meddeth.wav");
  1572. commander_sound_sight = gi.soundindex ("medic_commander/medsght.wav");
  1573. commander_sound_search = gi.soundindex ("medic_commander/medsrch.wav");
  1574. commander_sound_hook_launch = gi.soundindex ("medic_commander/medatck2c.wav");
  1575. commander_sound_hook_hit = gi.soundindex ("medic_commander/medatck3a.wav");
  1576. commander_sound_hook_heal = gi.soundindex ("medic_commander/medatck4a.wav");
  1577. commander_sound_hook_retract = gi.soundindex ("medic_commander/medatck5a.wav");
  1578. commander_sound_spawn = gi.soundindex ("medic_commander/monsterspawn1.wav");
  1579. gi.soundindex ("tank/tnkatck3.wav");
  1580. }
  1581. else
  1582. {
  1583. sound_idle1 = gi.soundindex ("medic/idle.wav");
  1584. sound_pain1 = gi.soundindex ("medic/medpain1.wav");
  1585. sound_pain2 = gi.soundindex ("medic/medpain2.wav");
  1586. sound_die = gi.soundindex ("medic/meddeth1.wav");
  1587. sound_sight = gi.soundindex ("medic/medsght1.wav");
  1588. sound_search = gi.soundindex ("medic/medsrch1.wav");
  1589. sound_hook_launch = gi.soundindex ("medic/medatck2.wav");
  1590. sound_hook_hit = gi.soundindex ("medic/medatck3.wav");
  1591. sound_hook_heal = gi.soundindex ("medic/medatck4.wav");
  1592. sound_hook_retract = gi.soundindex ("medic/medatck5.wav");
  1593. gi.soundindex ("medic/medatck1.wav");
  1594. self->s.skinnum = 0;
  1595. }
  1596. //pmm
  1597. }