|
|
1.1 root 1: /*
2: ==============================================================================
3:
4: GUNNER
5:
6: ==============================================================================
7: */
8:
9: #include "g_local.h"
10: #include "m_gunner.h"
11:
12:
13: static int sound_pain;
14: static int sound_pain2;
15: static int sound_death;
16: static int sound_idle;
17: static int sound_open;
18: static int sound_search;
19: static int sound_sight;
20:
21:
22: void gunner_idlesound (edict_t *self)
23: {
24: gi.sound (self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
25: }
26:
27: void gunner_sight (edict_t *self, edict_t *other)
28: {
29: gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
30: }
31:
32: void gunner_search (edict_t *self)
33: {
34: gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
35: }
36:
37:
38: qboolean visible (edict_t *self, edict_t *other);
39: void GunnerGrenade (edict_t *self);
40: void GunnerFire (edict_t *self);
41: void gunner_fire_chain(edict_t *self);
42: void gunner_refire_chain(edict_t *self);
43:
44:
45: void gunner_stand (edict_t *self);
46:
47: mframe_t gunner_frames_fidget [] =
48: {
49: ai_stand, 0, NULL,
50: ai_stand, 0, NULL,
51: ai_stand, 0, NULL,
52: ai_stand, 0, NULL,
53: ai_stand, 0, NULL,
54: ai_stand, 0, NULL,
55: ai_stand, 0, NULL,
56: ai_stand, 0, gunner_idlesound,
57: ai_stand, 0, NULL,
58:
59: ai_stand, 0, NULL,
60: ai_stand, 0, NULL,
61: ai_stand, 0, NULL,
62: ai_stand, 0, NULL,
63: ai_stand, 0, NULL,
64: ai_stand, 0, NULL,
65: ai_stand, 0, NULL,
66: ai_stand, 0, NULL,
67: ai_stand, 0, NULL,
68: ai_stand, 0, NULL,
69:
70: ai_stand, 0, NULL,
71: ai_stand, 0, NULL,
72: ai_stand, 0, NULL,
73: ai_stand, 0, NULL,
74: ai_stand, 0, NULL,
75: ai_stand, 0, NULL,
76: ai_stand, 0, NULL,
77: ai_stand, 0, NULL,
78: ai_stand, 0, NULL,
79: ai_stand, 0, NULL,
80:
81: ai_stand, 0, NULL,
82: ai_stand, 0, NULL,
83: ai_stand, 0, NULL,
84: ai_stand, 0, NULL,
85: ai_stand, 0, NULL,
86: ai_stand, 0, NULL,
87: ai_stand, 0, NULL,
88: ai_stand, 0, NULL,
89: ai_stand, 0, NULL,
90: ai_stand, 0, NULL,
91:
92: ai_stand, 0, NULL,
93: ai_stand, 0, NULL,
94: ai_stand, 0, NULL,
95: ai_stand, 0, NULL,
96: ai_stand, 0, NULL,
97: ai_stand, 0, NULL,
98: ai_stand, 0, NULL,
99: ai_stand, 0, NULL,
100: ai_stand, 0, NULL,
101: ai_stand, 0, NULL
102: };
103: mmove_t gunner_move_fidget = {FRAME_stand31, FRAME_stand70, gunner_frames_fidget, gunner_stand};
104:
105: void gunner_fidget (edict_t *self)
106: {
107: if (self->monsterinfo.aiflags & AI_STAND_GROUND)
108: return;
109: if (random() <= 0.05)
110: self->monsterinfo.currentmove = &gunner_move_fidget;
111: }
112:
113: mframe_t gunner_frames_stand [] =
114: {
115: ai_stand, 0, NULL,
116: ai_stand, 0, NULL,
117: ai_stand, 0, NULL,
118: ai_stand, 0, NULL,
119: ai_stand, 0, NULL,
120: ai_stand, 0, NULL,
121: ai_stand, 0, NULL,
122: ai_stand, 0, NULL,
123: ai_stand, 0, NULL,
124: ai_stand, 0, gunner_fidget,
125:
126: ai_stand, 0, NULL,
127: ai_stand, 0, NULL,
128: ai_stand, 0, NULL,
129: ai_stand, 0, NULL,
130: ai_stand, 0, NULL,
131: ai_stand, 0, NULL,
132: ai_stand, 0, NULL,
133: ai_stand, 0, NULL,
134: ai_stand, 0, NULL,
135: ai_stand, 0, gunner_fidget,
136:
137: ai_stand, 0, NULL,
138: ai_stand, 0, NULL,
139: ai_stand, 0, NULL,
140: ai_stand, 0, NULL,
141: ai_stand, 0, NULL,
142: ai_stand, 0, NULL,
143: ai_stand, 0, NULL,
144: ai_stand, 0, NULL,
145: ai_stand, 0, NULL,
146: ai_stand, 0, gunner_fidget
147: };
148: mmove_t gunner_move_stand = {FRAME_stand01, FRAME_stand30, gunner_frames_stand, NULL};
149:
150: void gunner_stand (edict_t *self)
151: {
152: self->monsterinfo.currentmove = &gunner_move_stand;
153: }
154:
155:
156: mframe_t gunner_frames_walk [] =
157: {
158: ai_walk, 0, NULL,
159: ai_walk, 3, NULL,
160: ai_walk, 4, NULL,
161: ai_walk, 5, NULL,
162: ai_walk, 7, NULL,
163: ai_walk, 2, NULL,
164: ai_walk, 6, NULL,
165: ai_walk, 4, NULL,
166: ai_walk, 2, NULL,
167: ai_walk, 7, NULL,
168: ai_walk, 5, NULL,
169: ai_walk, 7, NULL,
170: ai_walk, 4, NULL
171: };
172: mmove_t gunner_move_walk = {FRAME_walk07, FRAME_walk19, gunner_frames_walk, NULL};
173:
174: void gunner_walk (edict_t *self)
175: {
176: self->monsterinfo.currentmove = &gunner_move_walk;
177: }
178:
179: mframe_t gunner_frames_run [] =
180: {
181: ai_run, 26, NULL,
182: ai_run, 9, NULL,
183: ai_run, 9, NULL,
184: ai_run, 9, NULL,
185: ai_run, 15, NULL,
186: ai_run, 10, NULL,
187: ai_run, 13, NULL,
188: ai_run, 6, NULL
189: };
190:
191: mmove_t gunner_move_run = {FRAME_run01, FRAME_run08, gunner_frames_run, NULL};
192:
193: void gunner_run (edict_t *self)
194: {
195: if (self->monsterinfo.aiflags & AI_STAND_GROUND)
196: self->monsterinfo.currentmove = &gunner_move_stand;
197: else
198: self->monsterinfo.currentmove = &gunner_move_run;
199: }
200:
201: mframe_t gunner_frames_runandshoot [] =
202: {
203: ai_run, 32, NULL,
204: ai_run, 15, NULL,
205: ai_run, 10, NULL,
206: ai_run, 18, NULL,
207: ai_run, 8, NULL,
208: ai_run, 20, NULL
209: };
210:
211: mmove_t gunner_move_runandshoot = {FRAME_runs01, FRAME_runs06, gunner_frames_runandshoot, NULL};
212:
213: void gunner_runandshoot (edict_t *self)
214: {
215: self->monsterinfo.currentmove = &gunner_move_runandshoot;
216: }
217:
218: mframe_t gunner_frames_pain3 [] =
219: {
220: ai_move, -3, NULL,
221: ai_move, 1, NULL,
222: ai_move, 1, NULL,
223: ai_move, 0, NULL,
224: ai_move, 1, NULL
225: };
226: mmove_t gunner_move_pain3 = {FRAME_pain301, FRAME_pain305, gunner_frames_pain3, gunner_run};
227:
228: mframe_t gunner_frames_pain2 [] =
229: {
230: ai_move, -2, NULL,
231: ai_move, 11, NULL,
232: ai_move, 6, NULL,
233: ai_move, 2, NULL,
234: ai_move, -1, NULL,
235: ai_move, -7, NULL,
236: ai_move, -2, NULL,
237: ai_move, -7, NULL
238: };
239: mmove_t gunner_move_pain2 = {FRAME_pain201, FRAME_pain208, gunner_frames_pain2, gunner_run};
240:
241: mframe_t gunner_frames_pain1 [] =
242: {
243: ai_move, 2, NULL,
244: ai_move, 0, NULL,
245: ai_move, -5, NULL,
246: ai_move, 3, NULL,
247: ai_move, -1, NULL,
248: ai_move, 0, NULL,
249: ai_move, 0, NULL,
250: ai_move, 0, NULL,
251: ai_move, 0, NULL,
252: ai_move, 1, NULL,
253: ai_move, 1, NULL,
254: ai_move, 2, NULL,
255: ai_move, 1, NULL,
256: ai_move, 0, NULL,
257: ai_move, -2, NULL,
258: ai_move, -2, NULL,
259: ai_move, 0, NULL,
260: ai_move, 0, NULL
261: };
262: mmove_t gunner_move_pain1 = {FRAME_pain101, FRAME_pain118, gunner_frames_pain1, gunner_run};
263:
264: void gunner_pain (edict_t *self, edict_t *other, float kick, int damage)
265: {
266: if (self->health < (self->max_health / 2))
267: self->s.skinnum = 1;
268:
269: if (level.time < self->pain_debounce_time)
270: return;
271:
272: self->pain_debounce_time = level.time + 3;
273:
274: if (rand()&1)
275: gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
276: else
277: gi.sound (self, CHAN_VOICE, sound_pain2, 1, ATTN_NORM, 0);
278:
279: if (skill->value == 3)
280: return; // no pain anims in nightmare
281:
282: if (damage <= 10)
283: self->monsterinfo.currentmove = &gunner_move_pain3;
284: else if (damage <= 25)
285: self->monsterinfo.currentmove = &gunner_move_pain2;
286: else
287: self->monsterinfo.currentmove = &gunner_move_pain1;
288: }
289:
290: void gunner_dead (edict_t *self)
291: {
292: VectorSet (self->mins, -16, -16, -24);
293: VectorSet (self->maxs, 16, 16, -8);
294: self->movetype = MOVETYPE_TOSS;
295: self->svflags |= SVF_DEADMONSTER;
296: self->nextthink = 0;
297: gi.linkentity (self);
298: }
299:
300: mframe_t gunner_frames_death [] =
301: {
302: ai_move, 0, NULL,
303: ai_move, 0, NULL,
304: ai_move, 0, NULL,
305: ai_move, -7, NULL,
306: ai_move, -3, NULL,
307: ai_move, -5, NULL,
308: ai_move, 8, NULL,
309: ai_move, 6, NULL,
310: ai_move, 0, NULL,
311: ai_move, 0, NULL,
312: ai_move, 0, NULL
313: };
314: mmove_t gunner_move_death = {FRAME_death01, FRAME_death11, gunner_frames_death, gunner_dead};
315:
316: void gunner_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
317: {
318: int n;
319:
320: // check for gib
321: if (self->health <= self->gib_health)
322: {
323: gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
324: for (n= 0; n < 2; n++)
325: ThrowGib (self, "models/objects/gibs/bone/tris.md2", damage, GIB_ORGANIC);
326: for (n= 0; n < 4; n++)
327: ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
328: ThrowHead (self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
329: self->deadflag = DEAD_DEAD;
330: return;
331: }
332:
333: if (self->deadflag == DEAD_DEAD)
334: return;
335:
336: // regular death
337: gi.sound (self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
338: self->deadflag = DEAD_DEAD;
339: self->takedamage = DAMAGE_YES;
340: self->monsterinfo.currentmove = &gunner_move_death;
341: }
342:
343:
344: void gunner_duck_down (edict_t *self)
345: {
346: if (self->monsterinfo.aiflags & AI_DUCKED)
347: return;
348: self->monsterinfo.aiflags |= AI_DUCKED;
349: if (skill->value >= 2)
350: {
351: if (random() > 0.5)
352: GunnerGrenade (self);
353: }
354:
355: self->maxs[2] -= 32;
356: self->takedamage = DAMAGE_YES;
357: self->monsterinfo.pausetime = level.time + 1;
358: gi.linkentity (self);
359: }
360:
361: void gunner_duck_hold (edict_t *self)
362: {
363: if (level.time >= self->monsterinfo.pausetime)
364: self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
365: else
366: self->monsterinfo.aiflags |= AI_HOLD_FRAME;
367: }
368:
369: void gunner_duck_up (edict_t *self)
370: {
371: self->monsterinfo.aiflags &= ~AI_DUCKED;
372: self->maxs[2] += 32;
373: self->takedamage = DAMAGE_AIM;
374: gi.linkentity (self);
375: }
376:
377: mframe_t gunner_frames_duck [] =
378: {
379: ai_move, 1, gunner_duck_down,
380: ai_move, 1, NULL,
381: ai_move, 1, gunner_duck_hold,
382: ai_move, 0, NULL,
383: ai_move, -1, NULL,
384: ai_move, -1, NULL,
385: ai_move, 0, gunner_duck_up,
386: ai_move, -1, NULL
387: };
388: mmove_t gunner_move_duck = {FRAME_duck01, FRAME_duck08, gunner_frames_duck, gunner_run};
389:
390: void gunner_dodge (edict_t *self, edict_t *attacker, float eta)
391: {
392: if (random() > 0.25)
393: return;
394:
395: if (!self->enemy)
396: self->enemy = attacker;
397:
398: self->monsterinfo.currentmove = &gunner_move_duck;
399: }
400:
401:
402: void gunner_opengun (edict_t *self)
403: {
404: gi.sound (self, CHAN_VOICE, sound_open, 1, ATTN_IDLE, 0);
405: }
406:
407: void GunnerFire (edict_t *self)
408: {
409: vec3_t start;
410: vec3_t forward, right;
411: vec3_t target;
412: vec3_t aim;
413: int flash_number;
414:
415: flash_number = MZ2_GUNNER_MACHINEGUN_1 + (self->s.frame - FRAME_attak216);
416:
417: AngleVectors (self->s.angles, forward, right, NULL);
418: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
419:
420: // project enemy back a bit and target there
421: VectorCopy (self->enemy->s.origin, target);
422: VectorMA (target, -0.2, self->enemy->velocity, target);
423: target[2] += self->enemy->viewheight;
424:
425: VectorSubtract (target, start, aim);
426: VectorNormalize (aim);
427: monster_fire_bullet (self, start, aim, 3, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_number);
428: }
429:
430: void GunnerGrenade (edict_t *self)
431: {
432: vec3_t start;
433: vec3_t forward, right;
434: vec3_t aim;
435: int flash_number;
436:
437: if (self->s.frame == FRAME_attak105)
438: flash_number = MZ2_GUNNER_GRENADE_1;
439: else if (self->s.frame == FRAME_attak108)
440: flash_number = MZ2_GUNNER_GRENADE_2;
441: else if (self->s.frame == FRAME_attak111)
442: flash_number = MZ2_GUNNER_GRENADE_3;
443: else // (self->s.frame == FRAME_attak114)
444: flash_number = MZ2_GUNNER_GRENADE_4;
445:
446: AngleVectors (self->s.angles, forward, right, NULL);
447: G_ProjectSource (self->s.origin, monster_flash_offset[flash_number], forward, right, start);
448:
449: //FIXME : do a spread -225 -75 75 225 degrees around forward
450: VectorCopy (forward, aim);
451:
452: monster_fire_grenade (self, start, aim, 50, 600, flash_number);
453: }
454:
455: mframe_t gunner_frames_attack_chain [] =
456: {
457: /*
458: ai_charge, 0, NULL,
459: ai_charge, 0, NULL,
460: ai_charge, 0, NULL,
461: ai_charge, 0, NULL,
462: ai_charge, 0, NULL,
463: ai_charge, 0, NULL,
464: ai_charge, 0, NULL,
465: ai_charge, 0, NULL,
466: */
467: ai_charge, 0, gunner_opengun,
468: ai_charge, 0, NULL,
469: ai_charge, 0, NULL,
470: ai_charge, 0, NULL,
471: ai_charge, 0, NULL,
472: ai_charge, 0, NULL,
473: ai_charge, 0, NULL
474: };
475: mmove_t gunner_move_attack_chain = {FRAME_attak209, FRAME_attak215, gunner_frames_attack_chain, gunner_fire_chain};
476:
477: mframe_t gunner_frames_fire_chain [] =
478: {
479: ai_charge, 0, GunnerFire,
480: ai_charge, 0, GunnerFire,
481: ai_charge, 0, GunnerFire,
482: ai_charge, 0, GunnerFire,
483: ai_charge, 0, GunnerFire,
484: ai_charge, 0, GunnerFire,
485: ai_charge, 0, GunnerFire,
486: ai_charge, 0, GunnerFire
487: };
488: mmove_t gunner_move_fire_chain = {FRAME_attak216, FRAME_attak223, gunner_frames_fire_chain, gunner_refire_chain};
489:
490: mframe_t gunner_frames_endfire_chain [] =
491: {
492: ai_charge, 0, NULL,
493: ai_charge, 0, NULL,
494: ai_charge, 0, NULL,
495: ai_charge, 0, NULL,
496: ai_charge, 0, NULL,
497: ai_charge, 0, NULL,
498: ai_charge, 0, NULL
499: };
500: mmove_t gunner_move_endfire_chain = {FRAME_attak224, FRAME_attak230, gunner_frames_endfire_chain, gunner_run};
501:
502: mframe_t gunner_frames_attack_grenade [] =
503: {
504: ai_charge, 0, NULL,
505: ai_charge, 0, NULL,
506: ai_charge, 0, NULL,
507: ai_charge, 0, NULL,
508: ai_charge, 0, GunnerGrenade,
509: ai_charge, 0, NULL,
510: ai_charge, 0, NULL,
511: ai_charge, 0, GunnerGrenade,
512: ai_charge, 0, NULL,
513: ai_charge, 0, NULL,
514: ai_charge, 0, GunnerGrenade,
515: ai_charge, 0, NULL,
516: ai_charge, 0, NULL,
517: ai_charge, 0, GunnerGrenade,
518: ai_charge, 0, NULL,
519: ai_charge, 0, NULL,
520: ai_charge, 0, NULL,
521: ai_charge, 0, NULL,
522: ai_charge, 0, NULL,
523: ai_charge, 0, NULL,
524: ai_charge, 0, NULL
525: };
526: mmove_t gunner_move_attack_grenade = {FRAME_attak101, FRAME_attak121, gunner_frames_attack_grenade, gunner_run};
527:
528: void gunner_attack(edict_t *self)
529: {
530: if (range (self, self->enemy) == RANGE_MELEE)
531: {
532: self->monsterinfo.currentmove = &gunner_move_attack_chain;
533: }
534: else
535: {
536: if (random() <= 0.5)
537: self->monsterinfo.currentmove = &gunner_move_attack_grenade;
538: else
539: self->monsterinfo.currentmove = &gunner_move_attack_chain;
540: }
541: }
542:
543: void gunner_fire_chain(edict_t *self)
544: {
545: self->monsterinfo.currentmove = &gunner_move_fire_chain;
546: }
547:
548: void gunner_refire_chain(edict_t *self)
549: {
550: if (self->enemy->health > 0)
551: if ( visible (self, self->enemy) )
552: if (random() <= 0.5)
553: {
554: self->monsterinfo.currentmove = &gunner_move_fire_chain;
555: return;
556: }
557: self->monsterinfo.currentmove = &gunner_move_endfire_chain;
558: }
559:
560: /*QUAKED monster_gunner (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
561: */
562: void SP_monster_gunner (edict_t *self)
563: {
564: if (deathmatch->value)
565: {
566: G_FreeEdict (self);
567: return;
568: }
569:
570: sound_death = gi.soundindex ("gunner/death1.wav");
571: sound_pain = gi.soundindex ("gunner/gunpain2.wav");
572: sound_pain2 = gi.soundindex ("gunner/gunpain1.wav");
573: sound_idle = gi.soundindex ("gunner/gunidle1.wav");
574: sound_open = gi.soundindex ("gunner/gunatck1.wav");
575: sound_search = gi.soundindex ("gunner/gunsrch1.wav");
576: sound_sight = gi.soundindex ("gunner/sight1.wav");
577:
578: gi.soundindex ("gunner/gunatck2.wav");
579: gi.soundindex ("gunner/gunatck3.wav");
580:
581: self->movetype = MOVETYPE_STEP;
582: self->solid = SOLID_BBOX;
583: self->s.modelindex = gi.modelindex ("models/monsters/gunner/tris.md2");
584: VectorSet (self->mins, -16, -16, -24);
585: VectorSet (self->maxs, 16, 16, 32);
586:
587: self->health = 175;
588: self->gib_health = -70;
589: self->mass = 200;
590:
591: self->pain = gunner_pain;
592: self->die = gunner_die;
593:
594: self->monsterinfo.stand = gunner_stand;
595: self->monsterinfo.walk = gunner_walk;
596: self->monsterinfo.run = gunner_run;
597: self->monsterinfo.dodge = gunner_dodge;
598: self->monsterinfo.attack = gunner_attack;
599: self->monsterinfo.melee = NULL;
600: self->monsterinfo.sight = gunner_sight;
601: self->monsterinfo.search = gunner_search;
602:
603: gi.linkentity (self);
604:
605: self->monsterinfo.currentmove = &gunner_move_stand;
606: self->monsterinfo.scale = MODEL_SCALE;
607:
608: walkmonster_start (self);
609: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.