1 /*
2 Copyright (C) 2009 COR Entertainment, LLC.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /*
21 ==============================================================================
22
23 Death Ray Gun
24
25 ==============================================================================
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32
33 #include "g_local.h"
34 #include "g_deathray.h"
35
36
37 static int sound_pain;
38 static int sound_die;
39 static int sound_idle;
40 static int sound_punch;
41 static int sound_sight;
42 static int sound_search;
43
44
deathray_search(edict_t * self)45 void deathray_search (edict_t *self)
46 {
47 gi.sound (self, CHAN_VOICE, sound_search, 1, ATTN_NORM, 0);
48 }
49
50 mframe_t deathray_frames_stand [] =
51 {
52 {ai_stand, 0, NULL},
53 {ai_stand, 0, NULL},
54 {ai_stand, 0, NULL},
55 {ai_stand, 0, NULL},
56 {ai_stand, 0, NULL},
57 {ai_stand, 0, NULL},
58 {ai_stand, 0, NULL}
59
60 };
61 mmove_t deathray_move_stand = {FRAME_stand01, FRAME_stand06, deathray_frames_stand, NULL};
62
deathray_stand(edict_t * self)63 void deathray_stand (edict_t *self)
64 {
65 self->monsterinfo.currentmove = &deathray_move_stand;
66 }
67
68 mframe_t deathray_frames_run1 [] =
69 {
70 {ai_run, 0, NULL},
71 {ai_run, 0, NULL},
72 {ai_run, 0, NULL},
73 {ai_run, 0, NULL},
74 {ai_run, 0, NULL},
75 {ai_run, 0, NULL}
76 };
77 mmove_t deathray_move_run1 = {FRAME_stand01, FRAME_stand06, deathray_frames_run1, NULL};
78
deathray_run(edict_t * self)79 void deathray_run (edict_t *self)
80 {
81 self->monsterinfo.currentmove = &deathray_move_run1;
82 }
83
deathrayShot(edict_t * self)84 void deathrayShot (edict_t *self)
85 {
86 vec3_t forward, right;
87 vec3_t start;
88 vec3_t end;
89 vec3_t dir;
90 vec3_t from;
91 vec3_t offset;
92 int damage = 50;
93 trace_t tr;
94
95 //tactical
96 if(g_tactical->value)
97 {
98 if(self->spawnflags & 1)
99 {
100 //if power source is down, and backup gen is on, get erratic
101 if(!tacticalScore.humanPowerSource)
102 {
103 if(!tacticalScore.humanBackupGen)
104 return;
105
106 if(random() < 0.5)
107 {
108 if(self->enemy->ctype == 1)
109 {
110 if(random() < 0.5)
111 return;
112 }
113 else
114 return;
115 }
116 }
117 else if(!tacticalScore.humanComputer) //we have power, but no computer, so behave erratically
118 {
119 if(random() < 0.5)
120 {
121 if(self->enemy->ctype == 1)
122 {
123 if(random() < 0.5)
124 return;
125 }
126 else
127 return;
128 }
129 }
130
131 //do not fire on humans
132 if(self->enemy->ctype == 1)
133 return;
134 }
135 else
136 {
137 if(!tacticalScore.alienPowerSource)
138 {
139 if(!tacticalScore.alienBackupGen)
140 return;
141
142 if(random() < 0.5)
143 {
144 if(self->enemy->ctype == 0)
145 {
146 if(random() < 0.5)
147 return;
148 }
149 else
150 return;
151 }
152 }
153 else if(!tacticalScore.alienComputer)
154 {
155 if(random() < 0.5)
156 {
157 if(self->enemy->ctype == 0)
158 {
159 if(random() < 0.5)
160 return;
161 }
162 else
163 return;
164 }
165 }
166
167 //do not fire on aliens
168 if(self->enemy->ctype == 0)
169 return;
170 }
171 }
172
173 AngleVectors (self->s.angles, forward, right, NULL);
174 VectorSet(offset, 32, 0, 48);
175 G_ProjectSource (self->s.origin, offset, forward, right, start);
176 VectorCopy (self->s.origin, start);
177 VectorCopy (self->enemy->s.origin, end);
178 end[2] += self->enemy->viewheight;
179 VectorSubtract (end, start, dir);
180 right[0] = forward[0] * 64;
181 right[1] = forward[1] * 64;
182 VectorAdd(start, right, start);
183 end[2] -= 32;
184 start[2] += 64;
185 VectorCopy (start, from);
186 tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);
187 VectorCopy (tr.endpos, from);
188
189 // send muzzle flash
190 gi.WriteByte (svc_muzzleflash);
191 gi.WriteShort (self-g_edicts);
192 gi.WriteByte (MZ_RAILGUN);
193 gi.multicast (self->s.origin, MULTICAST_PVS);
194
195 gi.WriteByte (svc_temp_entity);
196 //tactical
197 if(self->spawnflags & 1)
198 gi.WriteByte (TE_REDLASER);
199 else
200 gi.WriteByte (TE_VAPORBEAM);
201 gi.WritePosition (start);
202 gi.WritePosition (end);
203 gi.multicast (self->s.origin, MULTICAST_PHS);
204
205 gi.WriteByte (svc_temp_entity);
206 gi.WriteByte (TE_BFG_BIGEXPLOSION);
207 gi.WritePosition (end);
208 gi.multicast (end, MULTICAST_PVS);
209
210 gi.sound (self, CHAN_VOICE, sound_punch, 1, ATTN_NORM, 0);
211
212 if ((tr.ent != self) && (tr.ent->takedamage))
213 T_Damage (tr.ent, self, self, dir, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_DEATHRAY);
214 else if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
215 {
216 gi.WriteByte (svc_temp_entity);
217 gi.WriteByte (TE_SCREEN_SPARKS);
218 gi.WritePosition (tr.endpos);
219 gi.WriteDir (tr.plane.normal);
220 gi.multicast (self->s.origin, MULTICAST_PVS);
221 }
222 }
223
224 mframe_t deathray_frames_attack_shoot [] =
225 {
226 {ai_charge, 0, NULL},
227 {ai_charge, 0, deathrayShot},
228 {ai_charge, 0, NULL},
229 {ai_charge, 0, NULL},
230 {ai_charge, 0, deathrayShot},
231 {ai_charge, 0, NULL},
232 {ai_charge, 0, deathrayShot}
233 };
234 mmove_t deathray_move_attack_shoot = {FRAME_stand01, FRAME_stand06, deathray_frames_attack_shoot, deathray_run};
235
236
deathray_sight(edict_t * self,edict_t * other)237 void deathray_sight (edict_t *self, edict_t *other)
238 {
239 gi.sound (self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0);
240 self->monsterinfo.currentmove = &deathray_move_run1;
241
242 }
243
deathray_attack(edict_t * self)244 void deathray_attack (edict_t *self)
245 {
246 self->monsterinfo.currentmove = &deathray_move_attack_shoot;
247 }
248
249 mframe_t deathray_frames_pain1 [] =
250 {
251 {ai_move, 0, NULL},
252 {ai_move, 0, NULL},
253 {ai_move, 0, NULL},
254 {ai_move, 0, NULL},
255 {ai_move, 0, NULL},
256 {ai_move, 0, NULL}
257 };
258 mmove_t deathray_move_pain1 = {FRAME_stand01, FRAME_stand06, deathray_frames_pain1, deathray_run};
259
deathray_pain(edict_t * self,edict_t * other,float kick,int damage)260 void deathray_pain (edict_t *self, edict_t *other, float kick, int damage)
261 {
262 if (self->health < (self->max_health / 2))
263 self->s.skinnum = 1;
264
265 if (level.time < self->pain_debounce_time)
266 return;
267
268 self->pain_debounce_time = level.time + 3;
269 gi.sound (self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
270 self->monsterinfo.currentmove = &deathray_move_pain1;
271 }
272
273
deathray_dead(edict_t * self)274 void deathray_dead (edict_t *self)
275 {
276 VectorSet (self->mins, -16, -16, -42);
277 VectorSet (self->maxs, 16, 16, -8);
278 self->movetype = MOVETYPE_TOSS;
279 self->svflags |= SVF_DEADMONSTER;
280 self->nextthink = 0;
281 gi.linkentity (self);
282 }
283
284
285 mframe_t deathray_frames_death1 [] =
286 {
287 {ai_move, 0, NULL},
288 {ai_move, 0, NULL},
289 {ai_move, 0, NULL},
290 {ai_move, 0, NULL},
291 {ai_move, 0, NULL}
292
293
294 };
295 mmove_t deathray_move_death1 = {FRAME_stand01, FRAME_stand05, deathray_frames_death1, deathray_dead};
296
deathray_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)297 void deathray_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
298 {
299
300 gi.WriteByte (svc_temp_entity);
301 gi.WriteByte (TE_EXPLOSION1);
302 gi.WritePosition (self->s.origin);
303 gi.multicast (self->s.origin, MULTICAST_PVS);
304
305 self->deadflag = DEAD_DEAD;
306
307 gi.sound( &g_edicts[1], CHAN_AUTO, gi.soundindex( "world/explosion1.wav" ), 1, ATTN_NONE, 0 );
308
309 G_FreeEdict (self);
310 }
311
SP_npc_deathray(edict_t * self)312 void SP_npc_deathray (edict_t *self)
313 {
314
315 // pre-caches
316 sound_pain = gi.soundindex ("misc/deathray/fizz.wav");
317 sound_die = gi.soundindex ("misc/deathray/fizz.wav");
318 sound_idle = gi.soundindex ("misc/deathray/weird2.wav");
319 sound_punch = gi.soundindex ("misc/deathray/shoot.wav");
320 sound_search = gi.soundindex ("misc/deathray/weird2.wav");
321 sound_sight = gi.soundindex ("misc/deathray/weird2.wav");
322
323 self->s.modelindex = gi.modelindex("models/misc/deathray/deathray.md2");
324
325 VectorSet (self->mins, -16, -16, 0);
326 VectorSet (self->maxs, 16, 16, 48);
327 self->movetype = MOVETYPE_STEP;
328 self->solid = SOLID_BBOX;
329 self->takedamage = DAMAGE_NO; //invincible
330 self->max_health = 5000;
331 self->health = self->max_health;
332 self->gib_health = 0;
333 self->mass = 5000;
334
335 self->pain = deathray_pain;
336 self->die = deathray_die;
337
338 self->monsterinfo.stand = deathray_stand;
339 self->monsterinfo.walk = deathray_run;
340 self->monsterinfo.run = deathray_run;
341 self->monsterinfo.dodge = NULL;
342 self->monsterinfo.attack = deathray_attack;
343 self->monsterinfo.melee = deathray_attack;
344 self->monsterinfo.sight = deathray_sight;
345 self->monsterinfo.search = deathray_search;
346 self->s.renderfx |= RF_MONSTER;
347
348 self->monsterinfo.currentmove = &deathray_move_stand;
349 self->monsterinfo.scale = MODEL_SCALE;
350
351 gi.linkentity (self);
352
353 walkmonster_start (self);
354 }
355
SP_misc_deathray(edict_t * self)356 void SP_misc_deathray (edict_t *self)
357 {
358
359 // pre-caches
360 sound_pain = gi.soundindex ("misc/deathray/fizz.wav");
361 sound_die = gi.soundindex ("misc/deathray/fizz.wav");
362 sound_idle = gi.soundindex ("misc/deathray/weird2.wav");
363 sound_punch = gi.soundindex ("misc/deathray/shoot.wav");
364 sound_search = gi.soundindex ("misc/deathray/weird2.wav");
365 sound_sight = gi.soundindex ("misc/deathray/weird2.wav");
366
367 //to do - make different mesh for human weapon
368 self->s.modelindex = gi.modelindex("models/misc/deathray/deathray.md2");
369
370 VectorSet (self->mins, -16, -16, 0);
371 VectorSet (self->maxs, 16, 16, 48);
372 self->movetype = MOVETYPE_STEP;
373 self->solid = SOLID_BBOX;
374 self->takedamage = DAMAGE_YES;
375 self->max_health = 300;
376 self->health = self->max_health;
377 self->gib_health = 0;
378 self->mass = 5000;
379
380 self->pain = deathray_pain;
381 self->die = deathray_die;
382
383 self->monsterinfo.stand = deathray_stand;
384 self->monsterinfo.walk = deathray_run;
385 self->monsterinfo.run = deathray_run;
386 self->monsterinfo.dodge = NULL;
387 self->monsterinfo.attack = deathray_attack;
388 self->monsterinfo.melee = deathray_attack;
389 self->monsterinfo.sight = deathray_sight;
390 self->monsterinfo.search = deathray_search;
391 self->s.renderfx |= RF_MONSTER;
392
393 self->monsterinfo.currentmove = &deathray_move_stand;
394 self->monsterinfo.scale = MODEL_SCALE;
395
396 gi.linkentity (self);
397
398 walkmonster_start (self);
399 }
400