m_widow.c 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. /*
  4. ==============================================================================
  5. black widow
  6. ==============================================================================
  7. */
  8. // self->timestamp used to prevent rapid fire of railgun
  9. // self->plat2flags used for fire count (flashes)
  10. // self->monsterinfo.pausetime used for timing of blaster shots
  11. #include "g_local.h"
  12. #include "m_widow.h"
  13. #define NUM_STALKERS_SPAWNED 6 // max # of stalkers she can spawn
  14. #define RAIL_TIME 3
  15. #define BLASTER_TIME 2
  16. #define BLASTER2_DAMAGE 10
  17. #define WIDOW_RAIL_DAMAGE 50
  18. #define DRAWBBOX NULL
  19. #define SHOWME NULL // showme
  20. void BossExplode (edict_t *self);
  21. qboolean infront (edict_t *self, edict_t *other);
  22. static int sound_pain1;
  23. static int sound_pain2;
  24. static int sound_pain3;
  25. static int sound_search1;
  26. static int sound_rail;
  27. static int sound_sight;
  28. static unsigned long shotsfired;
  29. static vec3_t spawnpoints[] = {
  30. {30, 100, 16},
  31. {30, -100, 16}
  32. };
  33. static vec3_t beameffects[] = {
  34. {12.58, -43.71, 68.88},
  35. {3.43, 58.72, 68.41}
  36. };
  37. static float sweep_angles[] = {
  38. // 32.0, 26.0, 20.0, 11.5, 3.0, -8.0, -13.0, -27.0, -41.0
  39. 32.0, 26.0, 20.0, 10.0, 0.0, -6.5, -13.0, -27.0, -41.0
  40. };
  41. vec3_t stalker_mins = {-28, -28, -18};
  42. vec3_t stalker_maxs = {28, 28, 18};
  43. unsigned int widow_damage_multiplier;
  44. void widow_run (edict_t *self);
  45. void widow_stand (edict_t *self);
  46. void widow_dead (edict_t *self);
  47. void widow_attack (edict_t *self);
  48. void widow_attack_blaster (edict_t *self);
  49. void widow_reattack_blaster (edict_t *self);
  50. void widow_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
  51. void widow_start_spawn (edict_t *self);
  52. void widow_done_spawn (edict_t *self);
  53. void widow_spawn_check (edict_t *self);
  54. void widow_prep_spawn (edict_t *self);
  55. void widow_attack_rail (edict_t *self);
  56. void widow_start_run_5 (edict_t *self);
  57. void widow_start_run_10 (edict_t *self);
  58. void widow_start_run_12 (edict_t *self);
  59. void WidowCalcSlots (edict_t *self);
  60. void drawbbox (edict_t *self);
  61. void showme (edict_t *self)
  62. {
  63. gi.dprintf ("frame %d\n", self->s.frame);
  64. }
  65. void widow_search (edict_t *self)
  66. {
  67. // if (random() < 0.5)
  68. // gi.sound (self, CHAN_VOICE, sound_search1, 1, ATTN_NONE, 0);
  69. }
  70. void widow_sight (edict_t *self, edict_t *other)
  71. {
  72. self->monsterinfo.pausetime = 0;
  73. // gi.sound (self, CHAN_WEAPON, sound_sight, 1, ATTN_NORM, 0);
  74. // if ((g_showlogic) && (g_showlogic->value))
  75. // gi.dprintf ("widow: found target!\n");
  76. }
  77. mmove_t widow_move_attack_post_blaster;
  78. mmove_t widow_move_attack_post_blaster_r;
  79. mmove_t widow_move_attack_post_blaster_l;
  80. mmove_t widow_move_attack_blaster;
  81. float target_angle (edict_t *self)
  82. {
  83. vec3_t target;
  84. float enemy_yaw;
  85. VectorSubtract (self->s.origin, self->enemy->s.origin, target);
  86. enemy_yaw = self->s.angles[YAW] - vectoyaw2(target);
  87. if (enemy_yaw < 0)
  88. enemy_yaw += 360.0;
  89. // this gets me 0 degrees = forward
  90. enemy_yaw -= 180.0;
  91. // positive is to right, negative to left
  92. return enemy_yaw;
  93. }
  94. int WidowTorso (edict_t *self)
  95. {
  96. float enemy_yaw;
  97. enemy_yaw = target_angle (self);
  98. // if ((g_showlogic) && (g_showlogic->value))
  99. // gi.dprintf ("%2.2f -> ", enemy_yaw);
  100. if (enemy_yaw >= 105)
  101. {
  102. self->monsterinfo.currentmove = &widow_move_attack_post_blaster_r;
  103. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  104. return 0;
  105. }
  106. if (enemy_yaw <= -75.0)
  107. {
  108. self->monsterinfo.currentmove = &widow_move_attack_post_blaster_l;
  109. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  110. return 0;
  111. }
  112. if (enemy_yaw >= 95)
  113. return FRAME_fired03;
  114. else if (enemy_yaw >= 85)
  115. return FRAME_fired04;
  116. else if (enemy_yaw >= 75)
  117. return FRAME_fired05;
  118. else if (enemy_yaw >= 65)
  119. return FRAME_fired06;
  120. else if (enemy_yaw >= 55)
  121. return FRAME_fired07;
  122. else if (enemy_yaw >= 45)
  123. return FRAME_fired08;
  124. else if (enemy_yaw >= 35)
  125. return FRAME_fired09;
  126. else if (enemy_yaw >= 25)
  127. return FRAME_fired10;
  128. else if (enemy_yaw >= 15)
  129. return FRAME_fired11;
  130. else if (enemy_yaw >= 5)
  131. return FRAME_fired12;
  132. else if (enemy_yaw >= -5)
  133. return FRAME_fired13;
  134. else if (enemy_yaw >= -15)
  135. return FRAME_fired14;
  136. else if (enemy_yaw >= -25)
  137. return FRAME_fired15;
  138. else if (enemy_yaw >= -35)
  139. return FRAME_fired16;
  140. else if (enemy_yaw >= -45)
  141. return FRAME_fired17;
  142. else if (enemy_yaw >= -55)
  143. return FRAME_fired18;
  144. else if (enemy_yaw >= -65)
  145. return FRAME_fired19;
  146. else if (enemy_yaw >= -75)
  147. return FRAME_fired20;
  148. /*
  149. if (fabs(enemy_yaw) < 11.25)
  150. return FRAME_fired03;
  151. else if (fabs(enemy_yaw) > 56.25)
  152. {
  153. self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
  154. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  155. return;
  156. }
  157. else if ((enemy_yaw >= 11.25) && (enemy_yaw < 33.75))
  158. return FRAME_fired04;
  159. else if (enemy_yaw >= 33.75)
  160. return FRAME_fired05;
  161. else if ((enemy_yaw <= -11.25) && (enemy_yaw > -33.75))
  162. return FRAME_fired06;
  163. else if (enemy_yaw <= -33.75)
  164. return FRAME_fired07;
  165. */
  166. }
  167. #define VARIANCE 15.0
  168. void WidowBlaster (edict_t *self)
  169. {
  170. vec3_t forward, right, target, vec, targ_angles;
  171. vec3_t start;
  172. int flashnum;
  173. int effect;
  174. if (!self->enemy)
  175. return;
  176. shotsfired++;
  177. if (!(shotsfired % 4))
  178. effect = EF_BLASTER;
  179. else
  180. effect = 0;
  181. AngleVectors (self->s.angles, forward, right, NULL);
  182. if ((self->s.frame >= FRAME_spawn05) && (self->s.frame <= FRAME_spawn13))
  183. {
  184. // sweep
  185. flashnum = MZ2_WIDOW_BLASTER_SWEEP1 + self->s.frame - FRAME_spawn05;
  186. G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
  187. VectorSubtract (self->enemy->s.origin, start, target);
  188. vectoangles2 (target, targ_angles);
  189. VectorCopy (self->s.angles, vec);
  190. vec[PITCH] += targ_angles[PITCH];
  191. vec[YAW] -= sweep_angles[flashnum-MZ2_WIDOW_BLASTER_SWEEP1];
  192. AngleVectors (vec, forward, NULL, NULL);
  193. monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
  194. /* if (self->s.frame == FRAME_spawn13)
  195. {
  196. VectorMA (start, 1024, forward, debugend);
  197. gi.WriteByte (svc_temp_entity);
  198. gi.WriteByte (TE_DEBUGTRAIL);
  199. gi.WritePosition (start);
  200. gi.WritePosition (debugend);
  201. gi.multicast (start, MULTICAST_ALL);
  202. drawbbox (self);
  203. self->monsterinfo.aiflags |= AI_HOLD_FRAME|AI_MANUAL_STEERING;
  204. }
  205. */
  206. }
  207. else if ((self->s.frame >= FRAME_fired02a) && (self->s.frame <= FRAME_fired20))
  208. {
  209. vec3_t angles;
  210. float aim_angle, target_angle;
  211. float error;
  212. self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  213. self->monsterinfo.nextframe = WidowTorso (self);
  214. if (!self->monsterinfo.nextframe)
  215. self->monsterinfo.nextframe = self->s.frame;
  216. // if ((g_showlogic) && (g_showlogic->value))
  217. // gi.dprintf ("%d\n", self->monsterinfo.nextframe);
  218. if (self->s.frame == FRAME_fired02a)
  219. flashnum = MZ2_WIDOW_BLASTER_0;
  220. else
  221. flashnum = MZ2_WIDOW_BLASTER_100 + self->s.frame - FRAME_fired03;
  222. G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
  223. PredictAim (self->enemy, start, 1000, true, ((random()*0.1)-0.05), forward, NULL);
  224. // clamp it to within 10 degrees of the aiming angle (where she's facing)
  225. vectoangles2 (forward, angles);
  226. // give me 100 -> -70
  227. aim_angle = 100 - (10*(flashnum-MZ2_WIDOW_BLASTER_100));
  228. if (aim_angle <= 0)
  229. aim_angle += 360;
  230. target_angle = self->s.angles[YAW] - angles[YAW];
  231. if (target_angle <= 0)
  232. target_angle += 360;
  233. error = aim_angle - target_angle;
  234. // positive error is to entity's left, aka positive direction in engine
  235. // unfortunately, I decided that for the aim_angle, positive was right. *sigh*
  236. if (error > VARIANCE)
  237. {
  238. // if ((g_showlogic) && (g_showlogic->value))
  239. // gi.dprintf ("angle %2.2f (really %2.2f) (%2.2f off of %2.2f) corrected to", target_angle, angles[YAW], error, aim_angle);
  240. angles[YAW] = (self->s.angles[YAW] - aim_angle) + VARIANCE;
  241. // if ((g_showlogic) && (g_showlogic->value))
  242. // {
  243. // if (angles[YAW] <= 0)
  244. // angles[YAW] += 360;
  245. // gi.dprintf (" %2.2f\n", angles[YAW]);
  246. // }
  247. AngleVectors (angles, forward, NULL, NULL);
  248. }
  249. else if (error < -VARIANCE)
  250. {
  251. // if ((g_showlogic) && (g_showlogic->value))
  252. // gi.dprintf ("angle %2.2f (really %2.2f) (%2.2f off of %2.2f) corrected to", target_angle, angles[YAW], error, aim_angle);
  253. angles[YAW] = (self->s.angles[YAW] - aim_angle) - VARIANCE;
  254. // if ((g_showlogic) && (g_showlogic->value))
  255. // {
  256. // if (angles[YAW] <= 0)
  257. // angles[YAW] += 360;
  258. // gi.dprintf (" %2.2f\n", angles[YAW]);
  259. // }
  260. AngleVectors (angles, forward, NULL, NULL);
  261. }
  262. // gi.dprintf ("%2.2f - %2.2f - %2.2f - %2.2f\n", aim_angle, self->s.angles[YAW] - angles[YAW], target_angle, error);
  263. // gi.dprintf ("%2.2f - %2.2f - %2.2f\n", angles[YAW], aim_angle, self->s.angles[YAW]);
  264. /*
  265. if (self->s.frame == FRAME_fired20)
  266. {
  267. VectorMA (start, 512, forward, debugend);
  268. gi.WriteByte (svc_temp_entity);
  269. gi.WriteByte (TE_DEBUGTRAIL);
  270. gi.WritePosition (start);
  271. gi.WritePosition (forward);
  272. gi.multicast (start, MULTICAST_ALL);
  273. drawbbox (self);
  274. self->monsterinfo.aiflags |= AI_HOLD_FRAME;
  275. self->monsterinfo.nextframe = FRAME_fired20;
  276. self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  277. }
  278. */
  279. /*
  280. if (!(self->plat2flags % 3))
  281. effect = EF_HYPERBLASTER;
  282. else
  283. effect = 0;
  284. self->plat2flags ++;
  285. */
  286. monster_fire_blaster2 (self, start, forward, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
  287. }
  288. else if ((self->s.frame >= FRAME_run01) && (self->s.frame <= FRAME_run08))
  289. {
  290. flashnum = MZ2_WIDOW_RUN_1 + self->s.frame - FRAME_run01;
  291. G_ProjectSource (self->s.origin, monster_flash_offset[flashnum], forward, right, start);
  292. VectorSubtract (self->enemy->s.origin, start, target);
  293. target[2] += self->enemy->viewheight;
  294. monster_fire_blaster2 (self, start, target, BLASTER2_DAMAGE*widow_damage_multiplier, 1000, flashnum, effect);
  295. }
  296. // else
  297. // {
  298. // if ((g_showlogic) && (g_showlogic->value))
  299. // gi.dprintf ("widow: firing on non-fire frame!\n");
  300. // }
  301. }
  302. void WidowSpawn (edict_t *self)
  303. {
  304. vec3_t f, r, u, offset, startpoint, spawnpoint;
  305. edict_t *ent, *designated_enemy;
  306. int i;
  307. AngleVectors (self->s.angles, f, r, u);
  308. for (i=0; i < 2; i++)
  309. {
  310. VectorCopy (spawnpoints[i], offset);
  311. G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
  312. if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
  313. {
  314. ent = CreateGroundMonster (spawnpoint, self->s.angles, stalker_mins, stalker_maxs, "monster_stalker", 256);
  315. if (!ent)
  316. continue;
  317. self->monsterinfo.monster_used++;
  318. ent->monsterinfo.commander = self;
  319. // if ((g_showlogic) && (g_showlogic->value))
  320. // gi.dprintf ("widow: post-spawn : %d slots left out of %d\n", SELF_SLOTS_LEFT, self->monsterinfo.monster_slots);
  321. ent->nextthink = level.time;
  322. ent->think (ent);
  323. ent->monsterinfo.aiflags |= AI_SPAWNED_WIDOW|AI_DO_NOT_COUNT|AI_IGNORE_SHOTS;
  324. if (!(coop && coop->value))
  325. {
  326. designated_enemy = self->enemy;
  327. }
  328. else
  329. {
  330. designated_enemy = PickCoopTarget(ent);
  331. if (designated_enemy)
  332. {
  333. // try to avoid using my enemy
  334. if (designated_enemy == self->enemy)
  335. {
  336. designated_enemy = PickCoopTarget(ent);
  337. if (designated_enemy)
  338. {
  339. // if ((g_showlogic) && (g_showlogic->value))
  340. // {
  341. // gi.dprintf ("PickCoopTarget returned a %s - ", designated_enemy->classname);
  342. // if (designated_enemy->client)
  343. // gi.dprintf ("with name %s\n", designated_enemy->client->pers.netname);
  344. // else
  345. // gi.dprintf ("NOT A CLIENT\n");
  346. // }
  347. }
  348. else
  349. {
  350. // if ((g_showlogic) && (g_showlogic->value))
  351. // gi.dprintf ("pick coop failed, using my current enemy\n");
  352. designated_enemy = self->enemy;
  353. }
  354. }
  355. }
  356. else
  357. {
  358. // if ((g_showlogic) && (g_showlogic->value))
  359. // gi.dprintf ("pick coop failed, using my current enemy\n");
  360. designated_enemy = self->enemy;
  361. }
  362. }
  363. if ((designated_enemy->inuse) && (designated_enemy->health > 0))
  364. {
  365. ent->enemy = designated_enemy;
  366. FoundTarget (ent);
  367. ent->monsterinfo.attack(ent);
  368. }
  369. }
  370. }
  371. }
  372. void widow_spawn_check (edict_t *self)
  373. {
  374. WidowBlaster(self);
  375. WidowSpawn (self);
  376. }
  377. void widow_ready_spawn (edict_t *self)
  378. {
  379. vec3_t f, r, u, offset, startpoint, spawnpoint;
  380. int i;
  381. WidowBlaster(self);
  382. AngleVectors (self->s.angles, f, r, u);
  383. for (i=0; i < 2; i++)
  384. {
  385. VectorCopy (spawnpoints[i], offset);
  386. G_ProjectSource2 (self->s.origin, offset, f, r, u, startpoint);
  387. if (FindSpawnPoint (startpoint, stalker_mins, stalker_maxs, spawnpoint, 64))
  388. {
  389. SpawnGrow_Spawn (spawnpoint, 1);
  390. }
  391. }
  392. }
  393. void widow_step (edict_t *self)
  394. {
  395. gi.sound (self, CHAN_BODY, gi.soundindex("widow/bwstep3.wav"), 1, ATTN_NORM, 0);
  396. }
  397. mframe_t widow_frames_stand [] =
  398. {
  399. ai_stand, 0, NULL,
  400. ai_stand, 0, NULL,
  401. ai_stand, 0, NULL,
  402. ai_stand, 0, NULL,
  403. ai_stand, 0, NULL,
  404. ai_stand, 0, NULL,
  405. ai_stand, 0, NULL,
  406. ai_stand, 0, NULL,
  407. ai_stand, 0, NULL,
  408. ai_stand, 0, NULL,
  409. ai_stand, 0, NULL
  410. };
  411. mmove_t widow_move_stand = {FRAME_idle01, FRAME_idle11, widow_frames_stand, NULL};
  412. mframe_t widow_frames_walk [] =
  413. {
  414. // hand generated numbers
  415. /*
  416. ai_run, 6, NULL,
  417. ai_run, 3, NULL,
  418. ai_run, 3, NULL,
  419. ai_run, 3, NULL,
  420. ai_run, 4, NULL, //5
  421. ai_run, 4, NULL,
  422. ai_run, 4, NULL,
  423. ai_run, 4.5, NULL,
  424. ai_run, 3, NULL,
  425. ai_run, 5, NULL, //10
  426. ai_run, 8, NULL,
  427. ai_run, 8, NULL,
  428. ai_run, 6.5, NULL
  429. */
  430. // auto generated numbers
  431. ai_walk, 2.79, widow_step,
  432. ai_walk, 2.77, NULL,
  433. ai_walk, 3.53, NULL,
  434. ai_walk, 3.97, NULL,
  435. ai_walk, 4.13, NULL, //5
  436. ai_walk, 4.09, NULL,
  437. ai_walk, 3.84, NULL,
  438. ai_walk, 3.62, widow_step,
  439. ai_walk, 3.29, NULL,
  440. ai_walk, 6.08, NULL, //10
  441. ai_walk, 6.94, NULL,
  442. ai_walk, 5.73, NULL,
  443. ai_walk, 2.85, NULL
  444. };
  445. mmove_t widow_move_walk = {FRAME_walk01, FRAME_walk13, widow_frames_walk, NULL};
  446. mframe_t widow_frames_run [] =
  447. {
  448. ai_run, 2.79, widow_step,
  449. ai_run, 2.77, NULL,
  450. ai_run, 3.53, NULL,
  451. ai_run, 3.97, NULL,
  452. ai_run, 4.13, NULL, //5
  453. ai_run, 4.09, NULL,
  454. ai_run, 3.84, NULL,
  455. ai_run, 3.62, widow_step,
  456. ai_run, 3.29, NULL,
  457. ai_run, 6.08, NULL, //10
  458. ai_run, 6.94, NULL,
  459. ai_run, 5.73, NULL,
  460. ai_run, 2.85, NULL
  461. };
  462. mmove_t widow_move_run = {FRAME_walk01, FRAME_walk13, widow_frames_run, NULL};
  463. void widow_stepshoot (edict_t *self)
  464. {
  465. gi.sound (self, CHAN_BODY, gi.soundindex("widow/bwstep2.wav"), 1, ATTN_NORM,0);
  466. WidowBlaster (self);
  467. }
  468. mframe_t widow_frames_run_attack [] =
  469. {
  470. ai_charge, 13, widow_stepshoot,
  471. ai_charge, 11.72, WidowBlaster,
  472. ai_charge, 18.04, WidowBlaster,
  473. ai_charge, 14.58, WidowBlaster,
  474. ai_charge, 13, widow_stepshoot, //5
  475. ai_charge, 12.12, WidowBlaster,
  476. ai_charge, 19.63, WidowBlaster,
  477. ai_charge, 11.37, WidowBlaster
  478. };
  479. mmove_t widow_move_run_attack = {FRAME_run01, FRAME_run08, widow_frames_run_attack, widow_run};
  480. //
  481. // These three allow specific entry into the run sequence
  482. //
  483. void widow_start_run_5 (edict_t *self)
  484. {
  485. self->monsterinfo.currentmove = &widow_move_run;
  486. self->monsterinfo.nextframe = FRAME_walk05;
  487. }
  488. void widow_start_run_10 (edict_t *self)
  489. {
  490. self->monsterinfo.currentmove = &widow_move_run;
  491. self->monsterinfo.nextframe = FRAME_walk10;
  492. }
  493. void widow_start_run_12 (edict_t *self)
  494. {
  495. self->monsterinfo.currentmove = &widow_move_run;
  496. self->monsterinfo.nextframe = FRAME_walk12;
  497. }
  498. mframe_t widow_frames_attack_pre_blaster [] =
  499. {
  500. ai_charge, 0, NULL,
  501. ai_charge, 0, NULL,
  502. ai_charge, 0, widow_attack_blaster
  503. };
  504. mmove_t widow_move_attack_pre_blaster = {FRAME_fired01, FRAME_fired02a, widow_frames_attack_pre_blaster, NULL};
  505. // Loop this
  506. mframe_t widow_frames_attack_blaster [] =
  507. {
  508. ai_charge, 0, widow_reattack_blaster, // straight ahead
  509. ai_charge, 0, widow_reattack_blaster, // 100 degrees right
  510. ai_charge, 0, widow_reattack_blaster,
  511. ai_charge, 0, widow_reattack_blaster,
  512. ai_charge, 0, widow_reattack_blaster,
  513. ai_charge, 0, widow_reattack_blaster,
  514. ai_charge, 0, widow_reattack_blaster, // 50 degrees right
  515. ai_charge, 0, widow_reattack_blaster,
  516. ai_charge, 0, widow_reattack_blaster,
  517. ai_charge, 0, widow_reattack_blaster,
  518. ai_charge, 0, widow_reattack_blaster,
  519. ai_charge, 0, widow_reattack_blaster, // straight
  520. ai_charge, 0, widow_reattack_blaster,
  521. ai_charge, 0, widow_reattack_blaster,
  522. ai_charge, 0, widow_reattack_blaster,
  523. ai_charge, 0, widow_reattack_blaster,
  524. ai_charge, 0, widow_reattack_blaster, // 50 degrees left
  525. ai_charge, 0, widow_reattack_blaster,
  526. ai_charge, 0, widow_reattack_blaster // 70 degrees left
  527. };
  528. mmove_t widow_move_attack_blaster = {FRAME_fired02a, FRAME_fired20, widow_frames_attack_blaster, NULL};
  529. mframe_t widow_frames_attack_post_blaster [] =
  530. {
  531. ai_charge, 0, NULL,
  532. ai_charge, 0, NULL
  533. };
  534. mmove_t widow_move_attack_post_blaster = {FRAME_fired21, FRAME_fired22, widow_frames_attack_post_blaster, widow_run};
  535. mframe_t widow_frames_attack_post_blaster_r [] =
  536. {
  537. ai_charge, -2, NULL,
  538. ai_charge, -10, NULL,
  539. ai_charge, -2, NULL,
  540. ai_charge, 0, NULL,
  541. ai_charge, 0, widow_start_run_12
  542. };
  543. mmove_t widow_move_attack_post_blaster_r = {FRAME_transa01, FRAME_transa05, widow_frames_attack_post_blaster_r, NULL};
  544. mframe_t widow_frames_attack_post_blaster_l [] =
  545. {
  546. ai_charge, 0, NULL,
  547. ai_charge, 14, NULL,
  548. ai_charge, -2, NULL,
  549. ai_charge, 10, NULL,
  550. ai_charge, 10, widow_start_run_12
  551. };
  552. mmove_t widow_move_attack_post_blaster_l = {FRAME_transb01, FRAME_transb05, widow_frames_attack_post_blaster_l, NULL};
  553. mmove_t widow_move_attack_rail;
  554. mmove_t widow_move_attack_rail_l;
  555. mmove_t widow_move_attack_rail_r;
  556. void WidowRail (edict_t *self)
  557. {
  558. vec3_t start;
  559. vec3_t dir;
  560. vec3_t forward, right;
  561. int flash;
  562. // gi.dprintf ("railing!\n");
  563. AngleVectors (self->s.angles, forward, right, NULL);
  564. if (self->monsterinfo.currentmove == &widow_move_attack_rail)
  565. flash = MZ2_WIDOW_RAIL;
  566. else if (self->monsterinfo.currentmove == &widow_move_attack_rail_l)
  567. {
  568. flash = MZ2_WIDOW_RAIL_LEFT;
  569. }
  570. else if (self->monsterinfo.currentmove == &widow_move_attack_rail_r)
  571. {
  572. flash = MZ2_WIDOW_RAIL_RIGHT;
  573. }
  574. G_ProjectSource (self->s.origin, monster_flash_offset[flash], forward, right, start);
  575. // calc direction to where we targeted
  576. VectorSubtract (self->pos1, start, dir);
  577. VectorNormalize (dir);
  578. monster_fire_railgun (self, start, dir, WIDOW_RAIL_DAMAGE*widow_damage_multiplier, 100, flash);
  579. self->timestamp = level.time + RAIL_TIME;
  580. }
  581. void WidowSaveLoc (edict_t *self)
  582. {
  583. VectorCopy (self->enemy->s.origin, self->pos1); //save for aiming the shot
  584. self->pos1[2] += self->enemy->viewheight;
  585. };
  586. void widow_start_rail (edict_t *self)
  587. {
  588. self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  589. }
  590. void widow_rail_done (edict_t *self)
  591. {
  592. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  593. }
  594. mframe_t widow_frames_attack_pre_rail [] =
  595. {
  596. ai_charge, 0, widow_start_rail,
  597. ai_charge, 0, NULL,
  598. ai_charge, 0, NULL,
  599. ai_charge, 0, widow_attack_rail
  600. };
  601. mmove_t widow_move_attack_pre_rail = {FRAME_transc01, FRAME_transc04, widow_frames_attack_pre_rail, NULL};
  602. mframe_t widow_frames_attack_rail [] =
  603. {
  604. ai_charge, 0, NULL,
  605. ai_charge, 0, NULL,
  606. ai_charge, 0, WidowSaveLoc,
  607. ai_charge, -10, WidowRail,
  608. ai_charge, 0, NULL,
  609. ai_charge, 0, NULL,
  610. ai_charge, 0, NULL,
  611. ai_charge, 0, NULL,
  612. ai_charge, 0, widow_rail_done
  613. };
  614. mmove_t widow_move_attack_rail = {FRAME_firea01, FRAME_firea09, widow_frames_attack_rail, widow_run};
  615. mframe_t widow_frames_attack_rail_r [] =
  616. {
  617. ai_charge, 0, NULL,
  618. ai_charge, 0, NULL,
  619. ai_charge, 0, WidowSaveLoc,
  620. ai_charge, -10, WidowRail,
  621. ai_charge, 0, NULL,
  622. ai_charge, 0, NULL,
  623. ai_charge, 0, NULL,
  624. ai_charge, 0, NULL,
  625. ai_charge, 0, widow_rail_done
  626. };
  627. mmove_t widow_move_attack_rail_r = {FRAME_fireb01, FRAME_fireb09, widow_frames_attack_rail_r, widow_run};
  628. mframe_t widow_frames_attack_rail_l [] =
  629. {
  630. ai_charge, 0, NULL,
  631. ai_charge, 0, NULL,
  632. ai_charge, 0, WidowSaveLoc,
  633. ai_charge, -10, WidowRail,
  634. ai_charge, 0, NULL,
  635. ai_charge, 0, NULL,
  636. ai_charge, 0, NULL,
  637. ai_charge, 0, NULL,
  638. ai_charge, 0, widow_rail_done
  639. };
  640. mmove_t widow_move_attack_rail_l = {FRAME_firec01, FRAME_firec09, widow_frames_attack_rail_l, widow_run};
  641. void widow_attack_rail (edict_t *self)
  642. {
  643. float enemy_angle;
  644. // gi.dprintf ("going to the rail!\n");
  645. enemy_angle = target_angle (self);
  646. if (enemy_angle < -15)
  647. self->monsterinfo.currentmove = &widow_move_attack_rail_l;
  648. else if (enemy_angle > 15)
  649. self->monsterinfo.currentmove = &widow_move_attack_rail_r;
  650. else
  651. self->monsterinfo.currentmove = &widow_move_attack_rail;
  652. }
  653. void widow_start_spawn (edict_t *self)
  654. {
  655. self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  656. }
  657. void widow_done_spawn (edict_t *self)
  658. {
  659. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  660. }
  661. mframe_t widow_frames_spawn [] =
  662. {
  663. ai_charge, 0, NULL, //1
  664. ai_charge, 0, NULL,
  665. ai_charge, 0, NULL,
  666. ai_charge, 0, widow_start_spawn,
  667. ai_charge, 0, NULL, //5
  668. ai_charge, 0, WidowBlaster, //6
  669. ai_charge, 0, widow_ready_spawn, //7
  670. ai_charge, 0, WidowBlaster,
  671. ai_charge, 0, WidowBlaster, //9
  672. ai_charge, 0, widow_spawn_check,
  673. ai_charge, 0, WidowBlaster, //11
  674. ai_charge, 0, WidowBlaster,
  675. ai_charge, 0, WidowBlaster, //13
  676. ai_charge, 0, NULL,
  677. ai_charge, 0, NULL,
  678. ai_charge, 0, NULL,
  679. ai_charge, 0, NULL,
  680. ai_charge, 0, widow_done_spawn
  681. };
  682. mmove_t widow_move_spawn = {FRAME_spawn01, FRAME_spawn18, widow_frames_spawn, widow_run};
  683. mframe_t widow_frames_pain_heavy [] =
  684. {
  685. ai_move, 0, NULL,
  686. ai_move, 0, NULL,
  687. ai_move, 0, NULL,
  688. ai_move, 0, NULL,
  689. ai_move, 0, NULL,
  690. ai_move, 0, NULL,
  691. ai_move, 0, NULL,
  692. ai_move, 0, NULL,
  693. ai_move, 0, NULL,
  694. ai_move, 0, NULL,
  695. ai_move, 0, NULL,
  696. ai_move, 0, NULL,
  697. ai_move, 0, NULL
  698. };
  699. mmove_t widow_move_pain_heavy = {FRAME_pain01, FRAME_pain13, widow_frames_pain_heavy, widow_run};
  700. mframe_t widow_frames_pain_light [] =
  701. {
  702. ai_move, 0, NULL,
  703. ai_move, 0, NULL,
  704. ai_move, 0, NULL
  705. };
  706. mmove_t widow_move_pain_light = {FRAME_pain201, FRAME_pain203, widow_frames_pain_light, widow_run};
  707. void spawn_out_start (edict_t *self)
  708. {
  709. vec3_t startpoint,f,r,u;
  710. self->wait = level.time + 2.0;
  711. // gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NONE, 0);
  712. AngleVectors (self->s.angles, f, r, u);
  713. G_ProjectSource2 (self->s.origin, beameffects[0], f, r, u, startpoint);
  714. gi.WriteByte (svc_temp_entity);
  715. gi.WriteByte (TE_WIDOWBEAMOUT);
  716. gi.WriteShort (20001);
  717. gi.WritePosition (startpoint);
  718. gi.multicast (startpoint, MULTICAST_ALL);
  719. G_ProjectSource2 (self->s.origin, beameffects[1], f, r, u, startpoint);
  720. gi.WriteByte (svc_temp_entity);
  721. gi.WriteByte (TE_WIDOWBEAMOUT);
  722. gi.WriteShort (20002);
  723. gi.WritePosition (startpoint);
  724. gi.multicast (startpoint, MULTICAST_ALL);
  725. gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/bwidowbeamout.wav"), 1, ATTN_NORM, 0);
  726. }
  727. void spawn_out_do (edict_t *self)
  728. {
  729. vec3_t startpoint,f,r,u;
  730. AngleVectors (self->s.angles, f, r, u);
  731. G_ProjectSource2 (self->s.origin, beameffects[0], f, r, u, startpoint);
  732. gi.WriteByte (svc_temp_entity);
  733. gi.WriteByte (TE_WIDOWSPLASH);
  734. gi.WritePosition (startpoint);
  735. gi.multicast (startpoint, MULTICAST_ALL);
  736. G_ProjectSource2 (self->s.origin, beameffects[1], f, r, u, startpoint);
  737. gi.WriteByte (svc_temp_entity);
  738. gi.WriteByte (TE_WIDOWSPLASH);
  739. gi.WritePosition (startpoint);
  740. gi.multicast (startpoint, MULTICAST_ALL);
  741. VectorCopy (self->s.origin, startpoint);
  742. startpoint[2] += 36;
  743. gi.WriteByte (svc_temp_entity);
  744. gi.WriteByte (TE_BOSSTPORT);
  745. gi.WritePosition (startpoint);
  746. gi.multicast (startpoint, MULTICAST_PVS);
  747. Widowlegs_Spawn (self->s.origin, self->s.angles);
  748. G_FreeEdict (self);
  749. }
  750. mframe_t widow_frames_death [] =
  751. {
  752. ai_move, 0, NULL,
  753. ai_move, 0, NULL,
  754. ai_move, 0, NULL,
  755. ai_move, 0, NULL,
  756. ai_move, 0, NULL, //5
  757. ai_move, 0, NULL,
  758. ai_move, 0, NULL,
  759. ai_move, 0, NULL,
  760. ai_move, 0, NULL,
  761. ai_move, 0, spawn_out_start, //10
  762. ai_move, 0, NULL,
  763. ai_move, 0, NULL,
  764. ai_move, 0, NULL,
  765. ai_move, 0, NULL,
  766. ai_move, 0, NULL, //15
  767. ai_move, 0, NULL,
  768. ai_move, 0, NULL,
  769. ai_move, 0, NULL,
  770. ai_move, 0, NULL,
  771. ai_move, 0, NULL, //20
  772. ai_move, 0, NULL,
  773. ai_move, 0, NULL,
  774. ai_move, 0, NULL,
  775. ai_move, 0, NULL,
  776. ai_move, 0, NULL, //25
  777. ai_move, 0, NULL,
  778. ai_move, 0, NULL,
  779. ai_move, 0, NULL,
  780. ai_move, 0, NULL,
  781. ai_move, 0, NULL, //30
  782. ai_move, 0, spawn_out_do
  783. };
  784. mmove_t widow_move_death = {FRAME_death01, FRAME_death31, widow_frames_death, NULL};
  785. void widow_attack_kick (edict_t *self)
  786. {
  787. vec3_t aim;
  788. // VectorSet (aim, MELEE_DISTANCE, 0, 4);
  789. VectorSet (aim, 100, 0, 4);
  790. if (self->enemy->groundentity)
  791. fire_hit (self, aim, (50 + (rand() % 6)), 500);
  792. else // not as much kick if they're in the air .. makes it harder to land on her head
  793. fire_hit (self, aim, (50 + (rand() % 6)), 250);
  794. }
  795. mframe_t widow_frames_attack_kick [] =
  796. {
  797. ai_move, 0, NULL,
  798. ai_move, 0, NULL,
  799. ai_move, 0, NULL,
  800. ai_move, 0, widow_attack_kick,
  801. ai_move, 0, NULL, // 5
  802. ai_move, 0, NULL,
  803. ai_move, 0, NULL,
  804. ai_move, 0, NULL
  805. };
  806. mmove_t widow_move_attack_kick = {FRAME_kick01, FRAME_kick08, widow_frames_attack_kick, widow_run};
  807. void widow_stand (edict_t *self)
  808. {
  809. // gi.dprintf ("widow stand\n");
  810. gi.sound (self, CHAN_WEAPON, gi.soundindex ("widow/laugh.wav"), 1, ATTN_NORM, 0);
  811. self->monsterinfo.currentmove = &widow_move_stand;
  812. }
  813. void widow_run (edict_t *self)
  814. {
  815. self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
  816. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  817. self->monsterinfo.currentmove = &widow_move_stand;
  818. else
  819. self->monsterinfo.currentmove = &widow_move_run;
  820. }
  821. void widow_walk (edict_t *self)
  822. {
  823. self->monsterinfo.currentmove = &widow_move_walk;
  824. }
  825. void widow_attack (edict_t *self)
  826. {
  827. float luck;
  828. qboolean rail_frames = false, blaster_frames = false, blocked = false, anger = false;
  829. self->movetarget = NULL;
  830. if (self->monsterinfo.aiflags & AI_BLOCKED)
  831. {
  832. blocked = true;
  833. self->monsterinfo.aiflags &= ~AI_BLOCKED;
  834. }
  835. if (self->monsterinfo.aiflags & AI_TARGET_ANGER)
  836. {
  837. anger = true;
  838. self->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
  839. }
  840. if ((!self->enemy) || (!self->enemy->inuse))
  841. return;
  842. if (self->bad_area)
  843. {
  844. if ((random() < 0.1) || (level.time < self->timestamp))
  845. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  846. else
  847. {
  848. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  849. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  850. }
  851. return;
  852. }
  853. // frames FRAME_walk13, FRAME_walk01, FRAME_walk02, FRAME_walk03 are rail gun start frames
  854. // frames FRAME_walk09, FRAME_walk10, FRAME_walk11, FRAME_walk12 are spawn & blaster start frames
  855. if ((self->s.frame == FRAME_walk13) || ((self->s.frame >= FRAME_walk01) && (self->s.frame <= FRAME_walk03)))
  856. rail_frames = true;
  857. if ((self->s.frame >= FRAME_walk09) && (self->s.frame <= FRAME_walk12))
  858. blaster_frames = true;
  859. WidowCalcSlots(self);
  860. // if we can't see the target, spawn stuff regardless of frame
  861. if ((self->monsterinfo.attack_state == AS_BLIND) && (SELF_SLOTS_LEFT >= 2))
  862. {
  863. // if ((g_showlogic) && (g_showlogic->value))
  864. // gi.dprintf ("attacking blind!\n");
  865. self->monsterinfo.currentmove = &widow_move_spawn;
  866. return;
  867. }
  868. // accept bias towards spawning regardless of frame
  869. if (blocked && (SELF_SLOTS_LEFT >= 2))
  870. {
  871. self->monsterinfo.currentmove = &widow_move_spawn;
  872. return;
  873. }
  874. if ((realrange(self, self->enemy) > 300) && (!anger) && (random() < 0.5) && (!blocked))
  875. {
  876. self->monsterinfo.currentmove = &widow_move_run_attack;
  877. return;
  878. }
  879. if (blaster_frames)
  880. {
  881. // gi.dprintf ("blaster frame %2.2f <= %2.2f\n", self->monsterinfo.pausetime + BLASTER_TIME, level.time);
  882. if (SELF_SLOTS_LEFT >= 2)
  883. {
  884. self->monsterinfo.currentmove = &widow_move_spawn;
  885. return;
  886. }
  887. else if (self->monsterinfo.pausetime + BLASTER_TIME <= level.time)
  888. {
  889. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  890. return;
  891. }
  892. }
  893. if (rail_frames)
  894. {
  895. // gi.dprintf ("rail frame %2.2f - %2.2f\n", level.time, self->timestamp);
  896. if (!(level.time < self->timestamp))
  897. {
  898. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  899. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  900. }
  901. }
  902. if ((rail_frames) || (blaster_frames))
  903. return;
  904. // if ((g_showlogic) && (g_showlogic->value))
  905. // gi.dprintf ("widow: unknown start frame, picking randomly\n");
  906. luck = random();
  907. if (SELF_SLOTS_LEFT >= 2)
  908. {
  909. if ((luck <= 0.40) && (self->monsterinfo.pausetime + BLASTER_TIME <= level.time))
  910. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  911. else if ((luck <= 0.7) && !(level.time < self->timestamp))
  912. {
  913. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  914. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  915. }
  916. else
  917. self->monsterinfo.currentmove = &widow_move_spawn;
  918. }
  919. else
  920. {
  921. if (level.time < self->timestamp)
  922. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  923. else if ((luck <= 0.50) || (level.time + BLASTER_TIME >= self->monsterinfo.pausetime))
  924. {
  925. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  926. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  927. }
  928. else // holdout to blaster
  929. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  930. }
  931. }
  932. /*
  933. void widow_attack (edict_t *self)
  934. {
  935. float range, luck;
  936. // gi.dprintf ("widow attack\n");
  937. if ((!self->enemy) || (!self->enemy->inuse))
  938. return;
  939. if (self->bad_area)
  940. {
  941. if ((random() < 0.1) || (level.time < self->timestamp))
  942. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  943. else
  944. {
  945. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  946. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  947. }
  948. return;
  949. }
  950. // if we can't see the target, spawn stuff
  951. if ((self->monsterinfo.attack_state == AS_BLIND) && (blaster_frames))
  952. {
  953. self->monsterinfo.currentmove = &widow_move_spawn;
  954. return;
  955. }
  956. range = realrange (self, self->enemy);
  957. if (range < 600)
  958. {
  959. luck = random();
  960. if (SLOTS_LEFT >= 2)
  961. {
  962. if (luck <= 0.40)
  963. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  964. else if ((luck <= 0.7) && !(level.time < self->timestamp))
  965. {
  966. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  967. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  968. }
  969. else
  970. self->monsterinfo.currentmove = &widow_move_spawn;
  971. }
  972. else
  973. {
  974. if ((luck <= 0.50) || (level.time < self->timestamp))
  975. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  976. else
  977. {
  978. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  979. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  980. }
  981. }
  982. }
  983. else
  984. {
  985. luck = random();
  986. if (SLOTS_LEFT >= 2)
  987. {
  988. if (luck < 0.3)
  989. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  990. else if ((luck < 0.65) || (level.time < self->timestamp))
  991. self->monsterinfo.currentmove = &widow_move_spawn;
  992. else
  993. {
  994. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  995. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  996. }
  997. }
  998. else
  999. {
  1000. if ((luck < 0.45) || (level.time < self->timestamp))
  1001. self->monsterinfo.currentmove = &widow_move_attack_pre_blaster;
  1002. else
  1003. {
  1004. gi.sound (self, CHAN_WEAPON, sound_rail, 1, ATTN_NORM, 0);
  1005. self->monsterinfo.currentmove = &widow_move_attack_pre_rail;
  1006. }
  1007. }
  1008. }
  1009. }
  1010. */
  1011. void widow_attack_blaster (edict_t *self)
  1012. {
  1013. self->monsterinfo.pausetime = level.time + 1.0 + (2.0*random());
  1014. // self->monsterinfo.pausetime = level.time + 100;
  1015. // self->plat2flags = 0;
  1016. self->monsterinfo.currentmove = &widow_move_attack_blaster;
  1017. self->monsterinfo.nextframe = WidowTorso (self);
  1018. }
  1019. void widow_reattack_blaster (edict_t *self)
  1020. {
  1021. WidowBlaster(self);
  1022. // if ((g_showlogic) && (g_showlogic->value))
  1023. // {
  1024. // if (self->monsterinfo.currentmove == &widow_move_attack_post_blaster_l)
  1025. // gi.dprintf ("pulling left!\n");
  1026. // if (self->monsterinfo.currentmove == &widow_move_attack_post_blaster_r)
  1027. // gi.dprintf ("pulling right!\n");
  1028. // }
  1029. // self->monsterinfo.currentmove = &widow_move_attack_blaster;
  1030. // self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
  1031. // return;
  1032. // if WidowBlaster bailed us out of the frames, just bail
  1033. if ((self->monsterinfo.currentmove == &widow_move_attack_post_blaster_l) ||
  1034. (self->monsterinfo.currentmove == &widow_move_attack_post_blaster_r))
  1035. return;
  1036. // if we're not done with the attack, don't leave the sequence
  1037. if (self->monsterinfo.pausetime >= level.time)
  1038. return;
  1039. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  1040. self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
  1041. }
  1042. /*
  1043. if ( infront(self, self->enemy) )
  1044. if (random() <= 0.5)
  1045. if ((random() < 0.7) || (SLOTS_LEFT <= 1))
  1046. self->monsterinfo.currentmove = &widow_move_attack_blaster;
  1047. else
  1048. self->monsterinfo.currentmove = &widow_move_spawn;
  1049. else
  1050. self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
  1051. else
  1052. self->monsterinfo.currentmove = &widow_move_attack_post_blaster;
  1053. }
  1054. */
  1055. void widow_pain (edict_t *self, edict_t *other, float kick, int damage)
  1056. {
  1057. if (self->health < (self->max_health / 2))
  1058. self->s.skinnum = 1;
  1059. if (skill->value == 3)
  1060. return; // no pain anims in nightmare
  1061. if (level.time < self->pain_debounce_time)
  1062. return;
  1063. if (self->monsterinfo.pausetime == 100000000)
  1064. self->monsterinfo.pausetime = 0;
  1065. self->pain_debounce_time = level.time + 5;
  1066. if (damage < 15)
  1067. {
  1068. gi.sound (self, CHAN_VOICE, sound_pain1, 1, ATTN_NONE, 0);
  1069. }
  1070. else if (damage < 75)
  1071. {
  1072. if ((skill->value < 3) && (random() < (0.6 - (0.2*((float)skill->value)))))
  1073. {
  1074. self->monsterinfo.currentmove = &widow_move_pain_light;
  1075. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  1076. }
  1077. gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NONE, 0);
  1078. }
  1079. else
  1080. {
  1081. if ((skill->value < 3) && (random() < (0.75 - (0.1*((float)skill->value)))))
  1082. {
  1083. self->monsterinfo.currentmove = &widow_move_pain_heavy;
  1084. self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
  1085. }
  1086. gi.sound (self, CHAN_VOICE, sound_pain3, 1, ATTN_NONE, 0);
  1087. }
  1088. }
  1089. void widow_dead (edict_t *self)
  1090. {
  1091. VectorSet (self->mins, -56, -56, 0);
  1092. VectorSet (self->maxs, 56, 56, 80);
  1093. self->movetype = MOVETYPE_TOSS;
  1094. self->svflags |= SVF_DEADMONSTER;
  1095. self->nextthink = 0;
  1096. gi.linkentity (self);
  1097. }
  1098. void widow_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  1099. {
  1100. self->deadflag = DEAD_DEAD;
  1101. self->takedamage = DAMAGE_NO;
  1102. self->count = 0;
  1103. self->monsterinfo.quad_framenum = 0;
  1104. self->monsterinfo.double_framenum = 0;
  1105. self->monsterinfo.invincible_framenum = 0;
  1106. self->monsterinfo.currentmove = &widow_move_death;
  1107. }
  1108. void widow_melee (edict_t *self)
  1109. {
  1110. // monster_done_dodge (self);
  1111. self->monsterinfo.currentmove = &widow_move_attack_kick;
  1112. }
  1113. void WidowGoinQuad (edict_t *self, float framenum)
  1114. {
  1115. self->monsterinfo.quad_framenum = framenum;
  1116. widow_damage_multiplier = 4;
  1117. }
  1118. void WidowDouble (edict_t *self, float framenum)
  1119. {
  1120. self->monsterinfo.double_framenum = framenum;
  1121. widow_damage_multiplier = 2;
  1122. }
  1123. void WidowPent (edict_t *self, float framenum)
  1124. {
  1125. self->monsterinfo.invincible_framenum = framenum;
  1126. }
  1127. void WidowPowerArmor (edict_t *self)
  1128. {
  1129. self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
  1130. // I don't like this, but it works
  1131. if (self->monsterinfo.power_armor_power <= 0)
  1132. self->monsterinfo.power_armor_power += 250 * skill->value;
  1133. }
  1134. void WidowRespondPowerup (edict_t *self, edict_t *other)
  1135. {
  1136. if (other->s.effects & EF_QUAD)
  1137. {
  1138. if (skill->value == 1)
  1139. WidowDouble (self, other->client->quad_framenum);
  1140. else if (skill->value == 2)
  1141. WidowGoinQuad (self, other->client->quad_framenum);
  1142. else if (skill->value == 3)
  1143. {
  1144. WidowGoinQuad (self, other->client->quad_framenum);
  1145. WidowPowerArmor (self);
  1146. }
  1147. }
  1148. else if (other->s.effects & EF_DOUBLE)
  1149. {
  1150. if (skill->value == 2)
  1151. WidowDouble (self, other->client->double_framenum);
  1152. else if (skill->value == 3)
  1153. {
  1154. WidowDouble (self, other->client->double_framenum);
  1155. WidowPowerArmor (self);
  1156. }
  1157. }
  1158. else
  1159. widow_damage_multiplier = 1;
  1160. if (other->s.effects & EF_PENT)
  1161. {
  1162. if (skill->value == 1)
  1163. WidowPowerArmor (self);
  1164. else if (skill->value == 2)
  1165. WidowPent (self, other->client->invincible_framenum);
  1166. else if (skill->value == 3)
  1167. {
  1168. WidowPent (self, other->client->invincible_framenum);
  1169. WidowPowerArmor (self);
  1170. }
  1171. }
  1172. }
  1173. void WidowPowerups (edict_t *self)
  1174. {
  1175. int player;
  1176. edict_t *ent;
  1177. if (!(coop && coop->value))
  1178. {
  1179. WidowRespondPowerup (self, self->enemy);
  1180. }
  1181. else
  1182. {
  1183. // in coop, check for pents, then quads, then doubles
  1184. for (player = 1; player <= game.maxclients; player++)
  1185. {
  1186. ent = &g_edicts[player];
  1187. if (!ent->inuse)
  1188. continue;
  1189. if (!ent->client)
  1190. continue;
  1191. if (ent->s.effects & EF_PENT)
  1192. {
  1193. WidowRespondPowerup (self, ent);
  1194. return;
  1195. }
  1196. }
  1197. for (player = 1; player <= game.maxclients; player++)
  1198. {
  1199. ent = &g_edicts[player];
  1200. if (!ent->inuse)
  1201. continue;
  1202. if (!ent->client)
  1203. continue;
  1204. if (ent->s.effects & EF_QUAD)
  1205. {
  1206. WidowRespondPowerup (self, ent);
  1207. return;
  1208. }
  1209. }
  1210. for (player = 1; player <= game.maxclients; player++)
  1211. {
  1212. ent = &g_edicts[player];
  1213. if (!ent->inuse)
  1214. continue;
  1215. if (!ent->client)
  1216. continue;
  1217. if (ent->s.effects & EF_DOUBLE)
  1218. {
  1219. WidowRespondPowerup (self, ent);
  1220. return;
  1221. }
  1222. }
  1223. }
  1224. }
  1225. qboolean Widow_CheckAttack (edict_t *self)
  1226. {
  1227. vec3_t spot1, spot2;
  1228. vec3_t temp;
  1229. float chance;
  1230. trace_t tr;
  1231. qboolean enemy_infront;
  1232. int enemy_range;
  1233. float enemy_yaw;
  1234. float real_enemy_range;
  1235. if (!self->enemy)
  1236. return false;
  1237. WidowPowerups(self);
  1238. if (self->monsterinfo.currentmove == &widow_move_run)
  1239. {
  1240. // if we're in run, make sure we're in a good frame for attacking before doing anything else
  1241. // frames 1,2,3,9,10,11,13 good to fire
  1242. switch (self->s.frame)
  1243. {
  1244. case FRAME_walk04:
  1245. case FRAME_walk05:
  1246. case FRAME_walk06:
  1247. case FRAME_walk07:
  1248. case FRAME_walk08:
  1249. case FRAME_walk12:
  1250. {
  1251. // if ((g_showlogic) && (g_showlogic->value))
  1252. // gi.dprintf ("Not in good walk frame (%d), not attacking\n", (self->s.frame - FRAME_walk01+1));
  1253. return false;
  1254. }
  1255. default:
  1256. break;
  1257. }
  1258. }
  1259. // give a LARGE bias to spawning things when we have room
  1260. // use AI_BLOCKED as a signal to attack to spawn
  1261. if ((random() < 0.8) && (SELF_SLOTS_LEFT >= 2) && (realrange(self, self->enemy) > 150))
  1262. {
  1263. self->monsterinfo.aiflags |= AI_BLOCKED;
  1264. self->monsterinfo.attack_state = AS_MISSILE;
  1265. return true;
  1266. }
  1267. if (self->enemy->health > 0)
  1268. {
  1269. // see if any entities are in the way of the shot
  1270. VectorCopy (self->s.origin, spot1);
  1271. spot1[2] += self->viewheight;
  1272. VectorCopy (self->enemy->s.origin, spot2);
  1273. spot2[2] += self->enemy->viewheight;
  1274. tr = gi.trace (spot1, NULL, NULL, spot2, self, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_SLIME|CONTENTS_LAVA);
  1275. // do we have a clear shot?
  1276. if (tr.ent != self->enemy)
  1277. {
  1278. // go ahead and spawn stuff if we're mad a a client
  1279. if (self->enemy->client && SELF_SLOTS_LEFT >= 2)
  1280. {
  1281. self->monsterinfo.attack_state = AS_BLIND;
  1282. return true;
  1283. }
  1284. // PGM - we want them to go ahead and shoot at info_notnulls if they can.
  1285. if(self->enemy->solid != SOLID_NOT || tr.fraction < 1.0) //PGM
  1286. return false;
  1287. }
  1288. }
  1289. enemy_infront = infront(self, self->enemy);
  1290. enemy_range = range(self, self->enemy);
  1291. VectorSubtract (self->enemy->s.origin, self->s.origin, temp);
  1292. enemy_yaw = vectoyaw2(temp);
  1293. self->ideal_yaw = enemy_yaw;
  1294. real_enemy_range = realrange (self, self->enemy);
  1295. // if (g_showlogic->value)
  1296. // gi.dprintf ("range = %2.2f\n", real_enemy_range);
  1297. // melee attack
  1298. // if (enemy_range == RANGE_MELEE)
  1299. if (real_enemy_range <= (MELEE_DISTANCE+20))
  1300. {
  1301. // don't always melee in easy mode
  1302. if (skill->value == 0 && (rand()&3) )
  1303. return false;
  1304. if (self->monsterinfo.melee)
  1305. self->monsterinfo.attack_state = AS_MELEE;
  1306. else
  1307. self->monsterinfo.attack_state = AS_MISSILE;
  1308. return true;
  1309. }
  1310. if (level.time < self->monsterinfo.attack_finished)
  1311. return false;
  1312. if (self->monsterinfo.aiflags & AI_STAND_GROUND)
  1313. {
  1314. chance = 0.4;
  1315. }
  1316. else if (enemy_range == RANGE_MELEE)
  1317. {
  1318. chance = 0.8;
  1319. }
  1320. else if (enemy_range == RANGE_NEAR)
  1321. {
  1322. chance = 0.7;
  1323. }
  1324. else if (enemy_range == RANGE_MID)
  1325. {
  1326. chance = 0.6;
  1327. }
  1328. else if (enemy_range == RANGE_FAR)
  1329. {
  1330. chance = 0.5;
  1331. }
  1332. // PGM - go ahead and shoot every time if it's a info_notnull
  1333. if ((random () < chance) || (self->enemy->solid == SOLID_NOT))
  1334. {
  1335. self->monsterinfo.attack_state = AS_MISSILE;
  1336. return true;
  1337. }
  1338. return false;
  1339. }
  1340. qboolean widow_blocked (edict_t *self, float dist)
  1341. {
  1342. // if we get blocked while we're in our run/attack mode, turn on a meaningless (in this context)AI flag,
  1343. // and call attack to get a new attack sequence. make sure to turn it off when we're done.
  1344. //
  1345. // I'm using AI_TARGET_ANGER for this purpose
  1346. if (self->monsterinfo.currentmove == &widow_move_run_attack)
  1347. {
  1348. self->monsterinfo.aiflags |= AI_TARGET_ANGER;
  1349. if (self->monsterinfo.checkattack(self))
  1350. self->monsterinfo.attack(self);
  1351. else
  1352. self->monsterinfo.run(self);
  1353. return true;
  1354. }
  1355. if(blocked_checkshot (self, 0.25 + (0.05 * skill->value) ))
  1356. return true;
  1357. /*
  1358. if(blocked_checkjump (self, dist, 192, 40))
  1359. {
  1360. infantry_jump(self);
  1361. return true;
  1362. }
  1363. if(blocked_checkplat (self, dist))
  1364. return true;
  1365. */
  1366. return false;
  1367. }
  1368. void WidowCalcSlots (edict_t *self)
  1369. {
  1370. int old_slots;
  1371. old_slots = self->monsterinfo.monster_slots;
  1372. switch ((int)skill->value)
  1373. {
  1374. case 0:
  1375. case 1:
  1376. self->monsterinfo.monster_slots = 3;
  1377. break;
  1378. case 2:
  1379. self->monsterinfo.monster_slots = 4;
  1380. break;
  1381. case 3:
  1382. self->monsterinfo.monster_slots = 6;
  1383. break;
  1384. default:
  1385. self->monsterinfo.monster_slots = 3;
  1386. break;
  1387. }
  1388. if (coop->value)
  1389. {
  1390. self->monsterinfo.monster_slots = min (6, self->monsterinfo.monster_slots + ((skill->value)*(CountPlayers()-1)));
  1391. }
  1392. // if ((g_showlogic) && (g_showlogic->value) && (old_slots != self->monsterinfo.monster_slots))
  1393. // gi.dprintf ("number of slots changed from %d to %d\n", old_slots, self->monsterinfo.monster_slots);
  1394. }
  1395. void WidowPrecache ()
  1396. {
  1397. // cache in all of the stalker stuff, widow stuff, spawngro stuff, gibs
  1398. gi.soundindex ("stalker/pain.wav");
  1399. gi.soundindex ("stalker/death.wav");
  1400. gi.soundindex ("stalker/sight.wav");
  1401. gi.soundindex ("stalker/melee1.wav");
  1402. gi.soundindex ("stalker/melee2.wav");
  1403. gi.soundindex ("stalker/idle.wav");
  1404. gi.soundindex ("tank/tnkatck3.wav");
  1405. gi.modelindex ("models/proj/laser2/tris.md2");
  1406. gi.modelindex ("models/monsters/stalker/tris.md2");
  1407. gi.modelindex ("models/items/spawngro2/tris.md2");
  1408. gi.modelindex ("models/objects/gibs/sm_metal/tris.md2");
  1409. gi.modelindex ("models/objects/gibs/gear/tris.md2");
  1410. gi.modelindex ("models/monsters/blackwidow/gib1/tris.md2");
  1411. gi.modelindex ("models/monsters/blackwidow/gib2/tris.md2");
  1412. gi.modelindex ("models/monsters/blackwidow/gib3/tris.md2");
  1413. gi.modelindex ("models/monsters/blackwidow/gib4/tris.md2");
  1414. gi.modelindex ("models/monsters/blackwidow2/gib1/tris.md2");
  1415. gi.modelindex ("models/monsters/blackwidow2/gib2/tris.md2");
  1416. gi.modelindex ("models/monsters/blackwidow2/gib3/tris.md2");
  1417. gi.modelindex ("models/monsters/blackwidow2/gib4/tris.md2");
  1418. gi.modelindex ("models/monsters/legs/tris.md2");
  1419. gi.soundindex ("misc/bwidowbeamout.wav");
  1420. gi.soundindex ("misc/bigtele.wav");
  1421. gi.soundindex ("widow/bwstep3.wav");
  1422. gi.soundindex ("widow/bwstep2.wav");
  1423. }
  1424. /*QUAKED monster_widow (1 .5 0) (-40 -40 0) (40 40 144) Ambush Trigger_Spawn Sight
  1425. */
  1426. void SP_monster_widow (edict_t *self)
  1427. {
  1428. if (deathmatch->value)
  1429. {
  1430. G_FreeEdict (self);
  1431. return;
  1432. }
  1433. sound_pain1 = gi.soundindex ("widow/bw1pain1.wav");
  1434. sound_pain2 = gi.soundindex ("widow/bw1pain2.wav");
  1435. sound_pain3 = gi.soundindex ("widow/bw1pain3.wav");
  1436. sound_search1 = gi.soundindex ("bosshovr/bhvunqv1.wav");
  1437. // sound_sight = gi.soundindex ("widow/sight.wav");
  1438. sound_rail = gi.soundindex ("gladiator/railgun.wav");
  1439. // self->s.sound = gi.soundindex ("bosshovr/bhvengn1.wav");
  1440. self->movetype = MOVETYPE_STEP;
  1441. self->solid = SOLID_BBOX;
  1442. self->s.modelindex = gi.modelindex ("models/monsters/blackwidow/tris.md2");
  1443. VectorSet (self->mins, -40, -40, 0);
  1444. VectorSet (self->maxs, 40, 40, 144);
  1445. self->health = 2000 + 1000*(skill->value);
  1446. if (coop->value)
  1447. self->health += 500*(skill->value);
  1448. // self->health = 1;
  1449. self->gib_health = -5000;
  1450. self->mass = 1500;
  1451. /*
  1452. if (skill->value == 2)
  1453. {
  1454. self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
  1455. self->monsterinfo.power_armor_power = 250;
  1456. }
  1457. else */if (skill->value == 3)
  1458. {
  1459. self->monsterinfo.power_armor_type = POWER_ARMOR_SHIELD;
  1460. self->monsterinfo.power_armor_power = 500;
  1461. }
  1462. self->yaw_speed = 30;
  1463. self->flags |= FL_IMMUNE_LASER;
  1464. self->monsterinfo.aiflags |= AI_IGNORE_SHOTS;
  1465. self->pain = widow_pain;
  1466. self->die = widow_die;
  1467. self->monsterinfo.melee = widow_melee;
  1468. self->monsterinfo.stand = widow_stand;
  1469. self->monsterinfo.walk = widow_walk;
  1470. self->monsterinfo.run = widow_run;
  1471. self->monsterinfo.attack = widow_attack;
  1472. self->monsterinfo.search = widow_search;
  1473. self->monsterinfo.checkattack = Widow_CheckAttack;
  1474. self->monsterinfo.sight = widow_sight;
  1475. self->monsterinfo.blocked = widow_blocked;
  1476. gi.linkentity (self);
  1477. self->monsterinfo.currentmove = &widow_move_stand;
  1478. self->monsterinfo.scale = MODEL_SCALE;
  1479. WidowPrecache();
  1480. WidowCalcSlots(self);
  1481. widow_damage_multiplier = 1;
  1482. walkmonster_start (self);
  1483. }