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