1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 
6 #include "xtux.h"
7 #include "server.h"
8 #include "entity.h"
9 #include "clients.h"
10 #include "sv_net.h"
11 #include "weapon.h"
12 #include "sv_map.h"
13 #include "hitscan.h"
14 #include "ai.h"
15 
16 extern map_t *map;
17 extern entity *root;
18 extern entity *tail;
19 extern server_t server;
20 extern byte num_entity_types;
21 
22 byte num_weapon_types;
23 
24 static void muzzle_flash(point P, byte dir);
25 
weapon_init(void)26 void weapon_init(void)
27 {
28     ent_type_t *et;
29     int weapon;
30     int i;
31 
32     num_weapon_types = weapon_type_init();
33 
34     /* Match weapon names to weapon types */
35     for( i=0 ; i < num_entity_types ; i++) {
36 	if( (et = entity_type(i)) == NULL || et->weapon_name[0] == '\0' )
37 	    continue;
38 
39 	if( et->class == ITEM && et->item_action == GIVEWEAPON ) {
40 	    /* The Weapon a GIVEWEAPON item gives you */
41 	    if( (weapon = match_weapon_type( et->weapon_name )) >= 0 )
42 		et->item_type = weapon;
43 	    else {
44 		printf("Couldn't match weapon type for item %s\n", et->name);
45 		et->item_type = 0;
46 	    }
47 	} else if( et->weapon_name ) {
48 	    /* Default weapons for entities */
49 	    if( (weapon = match_weapon_type( et->weapon_name )) >= 0 )
50 		et->weapon = weapon;
51 	    else {
52 		printf("Couldn't match weapon type for entity %s\n", et->name);
53 		et->weapon = 0;
54 	    }
55 	}
56 
57     }
58 
59 }
60 
61 
weapon_update(void)62 void weapon_update(void)
63 {
64     entity *ent;
65     int weap, i;
66 
67     for( ent = root ; ent != NULL ; ent = ent->next ) {
68 	if( ent->trigger )
69 	    weapon_fire(ent);
70 
71 	if( ent->weapon_switch  && !( ent->powerup & ( 1<<PU_FIXED_WEAPON ) ) ) {
72 	    if( server.now - ent->last_switch >= WEAPON_SWITCH_TIME ) {
73 
74 		weap = ent->weapon;
75 		i = 0; /* Amount of weapons we have tried */
76 		do {
77 		    if( i++ >= num_weapon_types ) {
78 			printf("entity has no weapons!\n");
79 			weap = WT_NONE;
80 			break;
81 		    }
82 
83 		    /* Increment within range */
84 		    weap += ent->weapon_switch;
85 		    if( weap >= num_weapon_types )
86 			weap = 1;
87 		    else if( weap < 1 )
88 			weap = num_weapon_types - 1;
89 
90 		} while( ent->has_weapon[ weap ] == 0 );
91 
92 		ent->weapon = weap;
93 		ent->last_switch = server.now;
94 		ent->weapon_switch = 0;
95 
96 	    }
97 	}
98 
99     }
100 
101 }
102 
103 
104 extern float sin_lookup[DEGREES];
105 extern float cos_lookup[DEGREES];
106 
weapon_fire(entity * ent)107 void weapon_fire(entity *ent)
108 {
109     weap_type_t *wt;
110     ent_type_t *et, *pt;
111     entity *proj;
112     msec_t reload_time;
113     int i;
114     ammunition_t *ammo, ammo_needed;
115     float x, y;
116     float x_v, y_v;
117     point start;
118     byte dir, d;
119 
120     if( (wt = weapon_type(ent->weapon)) == NULL ) {
121 	printf("Error firing weapon %d for entity %d (%s)\n",
122 	       ent->weapon, ent->id, ent->type_info->name);
123 	return;
124     }
125 
126     reload_time = wt->reload_time;
127     if( ent->powerup & (1<<PU_DOUBLE_FIRE_RATE) )
128 	reload_time /= 2;
129 
130     if( server.now - ent->last_fired < reload_time ) {
131 	/* Send client "click" sound? */
132 	return;
133     }
134 
135     et = ent->type_info;
136 
137     if( wt->ammo_type != AMMO_INFINITE ) {
138 	ammo = &ent->ammo[ wt->ammo_type ];
139 
140 	if( *ammo < 1 ) {
141 	    *ammo = 0;
142 	    ent->weapon = et->weapon;
143 	    ent->last_switch = server.now;
144 	    return;
145 	}
146 
147 	ammo_needed = wt->ammo_usage;
148 	if( wt->class == WT_BEAM )
149 	    ammo_needed /= server.fps + 0.5; /* Round */
150 
151 	if( ent->powerup & (1<<PU_HALF_AMMO_USAGE) ) {
152 	    if( (rand()%2) == 0 ) /* Avg = 50% usage */
153 		ammo_needed = 0;
154 	}
155 
156 	if( *ammo < ammo_needed )
157 	    return;
158 	else
159 	    *ammo -= ammo_needed;
160     }
161 
162     ent->last_fired = server.now;
163     start.x = ent->x + et->width/2;
164     start.y = ent->y + et->height - (et->height/5 * 2);
165     /* adjust y to top of ground clip box (see world.c) */
166 
167     d = ent->dir + 16;
168     d /= 32;
169     d *= 32;
170 
171     /* Get it outside shooters body */
172     start.x += sin_lookup[d] * (et->width/2);
173     start.y += -cos_lookup[d] * (et->height/2);
174 
175     if( wt->muzzle_flash )
176 	muzzle_flash(start, d);
177 
178     for( i=0 ; i < wt->number ; i++ ) {
179 	dir = ent->dir;
180 
181 	if( wt->spread )
182 	    dir += rand()%(wt->spread * 2) - wt->spread;
183 
184 	switch( wt->class ) {
185 	case WT_NONE:
186 	    break;
187 	case WT_PROJECTILE:
188 	    /* Only 1/2 relative motion, but it makes it easier to aim */
189 	    x_v = ent->x_v / 2;
190 	    y_v = ent->y_v / 2;
191 	    x_v += sin_lookup[ent->dir] * wt->speed;
192 	    y_v -= cos_lookup[ent->dir] * wt->speed;
193 
194 	    if( (pt = entity_type(wt->projectile)) == NULL ) {
195 		wt->projectile = 0;
196 		return;
197 	    }
198 	    x = start.x - pt->width/2;
199 	    y = start.y - pt->height + pt->height/5 * 2;
200 	    proj = entity_new( wt->projectile, x, y, x_v, y_v);
201 	    proj->dir = ent->dir;
202 	    proj->pid = ent->id; /* Set parent id to the projectiles shooter */
203 	    /* If the projectile hits something, the projectile's weapon value
204 	       (which is the weapon that caused the projectile to be fired)
205 	       will be the one used to call weapon_hit() */
206 	    proj->weapon = ent->weapon;
207 	    break;
208 	default:
209 	    hitscan_fire( ent, ent->weapon, start, dir );
210 	}
211     }
212 
213 }
214 
215 
216 /* Shooter hit victim at point P with weapon of type weapon */
weapon_hit(entity * shooter,entity * victim,point P,byte weaptype)217 void weapon_hit(entity *shooter, entity *victim, point P, byte weaptype)
218 {
219     ent_type_t *et;
220     weap_type_t *wt;
221     int damage;
222     netmsg msg;
223     byte dir;
224 
225     wt = weapon_type(weaptype);
226 
227     if( victim == NULL ) {
228 	printf("VICTIM = NULL!\n");
229 	return;
230     } else
231 	et = victim->type_info;
232 
233     /* Work out if we should be here first */
234     if( victim->class == ITEM || victim->health <= 0 )
235 	return;
236 
237     damage = wt->damage;
238     if( wt->class == WT_BEAM ) { /* Damage is done over a second for beams */
239 	damage /= server.fps;
240 	if( damage < 1 ) {
241 	    printf("Fuxored damage\n");
242 	    damage = 1;
243 	}
244     }
245 
246     /* Resistance reduces damage by 50% */
247     if( victim->powerup & (1<<PU_RESISTANCE) )
248 	damage /= 2;
249 
250     victim->health -= damage;
251 
252     if( wt->push_factor ) {
253 	dir = calculate_dir(shooter, victim);
254 	victim->x_v += sin_lookup[ dir ] * damage * wt->push_factor;
255 	victim->y_v -= cos_lookup[ dir ] * damage * wt->push_factor;
256     }
257 
258     if( victim->health <= 0 )
259 	entity_killed(shooter, victim, P, weaptype);
260     else if( victim->ai == AI_FIGHT ) {
261 	victim->target = shooter; /* Target the entity that just shot them */
262     }
263 
264     /* Spawn blood particles if victim bleeds */
265     if( et->bleeder ) {
266 	msg.type = NETMSG_PARTICLES;
267 	msg.particles.effect = P_BLOOD;
268 	msg.particles.dir = shooter? shooter->dir : 0;
269 	msg.particles.length = damage;
270 	msg.particles.color1 = COL_RED;
271 	msg.particles.color2 = COL_RED;
272 	msg.particles.x = P.x;
273 	msg.particles.y = P.y;
274 	sv_net_send_to_all(msg);
275     }
276 
277 }
278 
279 
weapon_explode(entity * explosion,int radius,int damage)280 void weapon_explode(entity *explosion, int radius, int damage )
281 {
282     netmsg msg;
283     entity *victim, *shooter;
284     point P;
285     int d1, dist, hurt;
286     int i;
287     byte dir;
288 
289     /* Where the explosion starts */
290     P.x = explosion->x + explosion->type_info->width/2;
291     P.y = explosion->y + explosion->type_info->height/2;
292 
293     /* Draw explosion */
294     msg.type = NETMSG_PARTICLES;
295     msg.particles.effect = P_EXPLOSION;
296     msg.particles.dir = 0;
297     msg.particles.length = (damage + radius) * 20;
298     msg.particles.color1 = COL_ORANGE;
299     msg.particles.color2 = COL_RED;
300     msg.particles.x = P.x;
301     msg.particles.y = P.y;
302     sv_net_send_to_all(msg);
303 
304     /* Make some soot at the site of explosions */
305 
306 
307     msg.particles.effect = P_SHARDS;
308     msg.particles.color1 = COL_BLACK;
309     msg.particles.color2 = COL_GRAY;
310 
311     for( i=0 ; i<10 ; i++ ) {
312 	dir = rand();
313 	msg.particles.x = P.x + sin_lookup[ dir ] * i * 5;
314 	msg.particles.y = P.y - cos_lookup[ dir ] * i * 5;
315 	sv_net_send_to_all(msg);
316     }
317 
318     /* Find the person who shot the exploding weapon */
319     if( (shooter = findent(explosion->pid)) == NULL )
320 	return; /* Shooter is no longer in the game */
321 
322     /* Do explosion damage */
323     for( victim = root ; victim != NULL ; victim = victim->next ) {
324 	if( victim->class == ITEM || victim->health <= 0 )
325 	    continue;
326 
327 	dist = entity_dist(explosion, victim);
328 
329 	/* Account for entity's width in factoring distance from explosion */
330 	d1 = MIN( victim->type_info->width, victim->type_info->height ) / 2;
331 	if( dist > d1 )
332 	    dist -= d1;
333 
334 	/* Apply damage if entity is within blast range */
335 	if( dist < radius ) {
336 	    hurt = damage * (radius - dist) / radius;
337 	    if( victim->powerup & (1<<PU_RESISTANCE) )
338 		hurt /= 2;
339 
340 	    victim->health -= hurt;
341 	    if( victim->health <= 0 )
342 		entity_killed(shooter, victim, P, explosion->weapon);
343 	}
344     }
345 
346 }
347 
348 
weapon_reset(entity * ent)349 void weapon_reset(entity *ent)
350 {
351     weap_type_t *wt;
352 
353     wt = weapon_type(ent->type_info->weapon);
354     memset(ent->has_weapon, 0, num_weapon_types);
355     ent->has_weapon[ent->type_info->weapon] = 1; /* Has default weapon */
356     ent->ammo[wt->ammo_type] = wt->initial_ammo;
357     ent->weapon = ent->type_info->weapon;
358 
359 }
360 
muzzle_flash(point P,byte dir)361 static void muzzle_flash(point P, byte dir)
362 {
363     netmsg msg;
364 
365     msg.type = NETMSG_PARTICLES;
366     msg.particles.effect = P_MFLASH;
367     msg.particles.dir = dir;
368     msg.particles.length = 50;
369     msg.particles.color1 = COL_YELLOW;
370     msg.particles.color2 = COL_RED;
371     msg.particles.x = P.x;
372     msg.particles.y = P.y;
373     sv_net_send_to_all(msg);
374 
375 }
376