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