1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 1991-2001 by
5  *
6  *      Bj�rn Stabell        <bjoern@xpilot.org>
7  *      Ken Ronny Schouten   <ken@xpilot.org>
8  *      Bert Gijsbers        <bert@xpilot.org>
9  *      Dick Balaska         <dick@xpilot.org>
10  *
11  * Copyright (C) 2003-2004 Kristian S�derblom <kps@users.sourceforge.net>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 
28 #include "xpserver.h"
29 
30 #define MISSILE_POWER_SPEED_FACT	0.25
31 #define MISSILE_POWER_TURNSPEED_FACT	0.75
32 #define MINI_TORPEDO_SPREAD_TIME	6
33 #define MINI_TORPEDO_SPREAD_SPEED	20
34 #define MINI_TORPEDO_SPREAD_ANGLE	90
35 #define MINI_MINE_SPREAD_TIME		18
36 #define MINI_MINE_SPREAD_SPEED		8
37 #define MINI_MISSILE_SPREAD_ANGLE	45
38 
39 #define CONFUSED_UPDATE_GRANULARITY	10
40 
41 
42 /***********************
43  * Functions for shots.
44  */
45 
Player_can_place_mine(player_t * pl)46 static inline bool Player_can_place_mine(player_t *pl)
47 {
48     if (pl->item[ITEM_MINE] <= 0)
49 	return false;
50     if (Player_is_phasing(pl))
51 	return false;
52     if (BIT(pl->used, HAS_SHIELD)
53 	&& !options.shieldedMining)
54 	return false;
55     return true;
56 }
57 
Place_mine(player_t * pl)58 void Place_mine(player_t *pl)
59 {
60     vector_t zero_vel = { 0.0, 0.0 };
61 
62     if (!Player_can_place_mine(pl))
63 	return;
64 
65     if (options.minMineSpeed > 0) {
66 	Place_moving_mine(pl);
67 	return;
68     }
69 
70     /*
71      * Dropped mines are immune to gravity. The latest theory is that mines
72      * contain some sort of anti-gravity device. An even later theory
73      * claims that they are anchored to space the same way as the walls are.
74      */
75     Place_general_mine(pl->id, pl->team, 0, pl->pos, zero_vel, pl->mods);
76 }
77 
78 
Place_moving_mine(player_t * pl)79 void Place_moving_mine(player_t *pl)
80 {
81     vector_t vel = pl->vel;
82 
83     if (!Player_can_place_mine(pl))
84 	return;
85 
86     if (options.minMineSpeed > 0) {
87 	if (pl->velocity < options.minMineSpeed) {
88 	    if (pl->velocity >= 1) {
89 		vel.x *= (options.minMineSpeed / pl->velocity);
90 		vel.y *= (options.minMineSpeed / pl->velocity);
91 	    } else {
92 		vel.x = options.minMineSpeed * tcos(pl->dir);
93 		vel.y = options.minMineSpeed * tsin(pl->dir);
94 	    }
95 	}
96     }
97 
98     Place_general_mine(pl->id, pl->team, GRAVITY, pl->pos, vel, pl->mods);
99 }
100 
Place_general_mine(int id,int team,int status,clpos_t pos,vector_t vel,modifiers_t mods)101 void Place_general_mine(int id, int team, int status,
102 			clpos_t pos, vector_t vel, modifiers_t mods)
103 {
104     int used, i, minis;
105     double life, drain, mass;
106     vector_t mv;
107     player_t *pl = Player_by_id(id);
108     cannon_t *cannon = Cannon_by_id(id);
109 
110     if (NumObjs + Mods_get(mods, ModsMini) >= MAX_TOTAL_SHOTS)
111 	return;
112 
113     pos = World_wrap_clpos(pos);
114 
115     if (pl && Player_is_killed(pl))
116 	life = rfrac() * 12;
117     else if (BIT(status, FROMCANNON))
118 	life = Cannon_get_shot_life(cannon);
119     else
120 	life = options.mineLife;
121 
122     if (!Mods_get(mods, ModsCluster))
123 	Mods_set(&mods, ModsVelocity, 0);
124     if (!Mods_get(mods, ModsMini))
125 	Mods_set(&mods, ModsSpread, 0);
126 
127     if (options.nukeMinSmarts <= 0)
128 	Mods_set(&mods, ModsNuclear, 0);
129     if (Mods_get(mods, ModsNuclear)) {
130 	if (pl) {
131 	    used = ((Mods_get(mods, ModsNuclear) & MODS_FULLNUCLEAR)
132 		    ? pl->item[ITEM_MINE]
133 		    : options.nukeMinMines);
134 	    if (pl->item[ITEM_MINE] < options.nukeMinMines) {
135 		Set_player_message_f(pl,
136 			"You need at least %d mines to %s %s!",
137 			options.nukeMinMines,
138 			(BIT(status, GRAVITY) ? "throw" : "drop"),
139 			Describe_shot (OBJ_MINE, status, mods, 0));
140 		return;
141 	    }
142 	} else
143 	    used = options.nukeMinMines;
144 	mass = MINE_MASS * used * NUKE_MASS_MULT;
145     } else {
146 	mass = (BIT(status, FROMCANNON) ? MINE_MASS * 0.6 : MINE_MASS);
147 	used = 1;
148     }
149 
150     if (pl) {
151 	drain = ED_MINE;
152 	if (Mods_get(mods, ModsCluster))
153 	    drain += CLUSTER_MASS_DRAIN(mass);
154 	if (pl->fuel.sum < -drain) {
155 	    Set_player_message_f(pl,
156 		    "You need at least %.1f fuel units to %s %s!",
157 		    -drain, (BIT(status, GRAVITY) ? "throw" : "drop"),
158 		    Describe_shot(OBJ_MINE, status, mods, 0));
159 	    return;
160 	}
161 	if (options.baseMineRange) {
162 	    for (i = 0; i < NumPlayers; i++) {
163 		player_t *pl_i = Player_by_index(i);
164 
165 		if (pl_i->home_base == NULL)
166 		    continue;
167 		if (pl_i->id != pl->id
168 		    && !Team_immune(pl_i->id, pl->id)
169 		    && !Player_is_tank(pl_i)) {
170 		    if (Wrap_length(pos.cx - pl_i->home_base->pos.cx,
171 				    pos.cy - pl_i->home_base->pos.cy)
172 			<= options.baseMineRange * BLOCK_CLICKS) {
173 			Set_player_message(pl, "No base mining!");
174 			return;
175 		    }
176 		}
177 	    }
178 	}
179 	Player_add_fuel(pl, drain);
180 	pl->item[ITEM_MINE] -= used;
181 
182 	if (used > 1) {
183 	    Set_message_f("%s has %s %s!", pl->name,
184 			  (BIT(status, GRAVITY) ? "thrown" : "dropped"),
185 			  Describe_shot(OBJ_MINE, status, mods, 0));
186 	    sound_play_all(NUKE_LAUNCH_SOUND);
187 	} else
188 	    sound_play_sensors(pl->pos,
189 			       BIT(status, GRAVITY)
190 			       ? DROP_MOVING_MINE_SOUND : DROP_MINE_SOUND);
191     }
192 
193     minis = Mods_get(mods, ModsMini) + 1;
194     SET_BIT(status, OWNERIMMUNE);
195 
196     for (i = 0; i < minis; i++) {
197 	mineobject_t *mine;
198 
199 	if ((mine = MINE_PTR(Object_allocate())) == NULL)
200 	    break;
201 
202 	mine->type = OBJ_MINE;
203 	mine->color = BLUE;
204 	mine->fuse = options.mineFuseTicks > 0 ? options.mineFuseTicks : -1;
205 	mine->obj_status = status;
206 	mine->id = (pl ? pl->id : NO_ID);
207 	mine->team = team;
208 	mine->mine_owner = mine->id;
209 	mine->mine_count = 0.0;
210 	Object_position_init_clpos(OBJ_PTR(mine), pos);
211 	if (minis > 1) {
212 	    int space = RES/minis, dir;
213 	    double spread = (double)(Mods_get(mods, ModsSpread) + 1);
214 
215 	    /*
216 	     * Dir gives (S is ship upwards);
217 	     *
218 	     *			      o		    o   o
219 	     *	X2: o S	o	X3:   S		X4:   S
220 	     *			    o   o	    o   o
221 	     */
222 	    dir = (i * space) + space/2 + (minis-2)*(RES/2) + (pl?pl->dir:0);
223 	    dir += (int)((rfrac() - 0.5) * space * 0.5);
224 	    dir = MOD2(dir, RES);
225 	    mv.x = MINI_MINE_SPREAD_SPEED * tcos(dir) / spread;
226 	    mv.y = MINI_MINE_SPREAD_SPEED * tsin(dir) / spread;
227 	    /*
228 	     * This causes the added initial velocity to reduce to
229 	     * zero over the MINI_MINE_SPREAD_TIME.
230 	     */
231 	    mine->mine_spread_left = MINI_MINE_SPREAD_TIME;
232 	    mine->acc.x = -mv.x / MINI_MINE_SPREAD_TIME;
233 	    mine->acc.y = -mv.y / MINI_MINE_SPREAD_TIME;
234 	} else {
235 	    mv.x = mv.y = mine->acc.x = mine->acc.y = 0.0;
236 	    mine->mine_spread_left = 0;
237 	}
238 	mine->vel = mv;
239 	mine->vel.x += vel.x * MINE_SPEED_FACT;
240 	mine->vel.y += vel.y * MINE_SPEED_FACT;
241 	mine->mass = mass / minis;
242 	mine->life = life / minis;
243 	mine->mods = mods;
244 	mine->pl_range = (int)(MINE_RANGE / minis);
245 	mine->pl_radius = MINE_RADIUS;
246 	Cell_add_object(OBJ_PTR(mine));
247     }
248 }
249 
250 /*
251  * Up to and including 3.2.6 it was:
252  *     Cause all of the given player's dropped/thrown mines to explode.
253  * Since this caused a slowdown when many mines detonated it
254  * is changed into:
255  *     Cause the mine which is closest to a player and owned
256  *     by that player to detonate.
257  */
Detonate_mines(player_t * pl)258 void Detonate_mines(player_t *pl)
259 {
260     int i, closest = -1;
261     double dist, min_dist = world->hypotenuse * CLICK + 1;
262 
263     if (Player_is_phasing(pl))
264 	return;
265 
266     for (i = 0; i < NumObjs; i++) {
267 	object_t *mine = Obj[i];
268 
269 	if (! (mine->type == OBJ_MINE))
270 	    continue;
271 	/*
272 	 * Mines which have been ECM reprogrammed should only be detonatable
273 	 * by the reprogrammer, not by the original mine placer:
274 	 */
275 	if (mine->id == pl->id) {
276 	    dist = Wrap_length(pl->pos.cx - mine->pos.cx,
277 			       pl->pos.cy - mine->pos.cy);
278 	    if (dist < min_dist) {
279 		min_dist = dist;
280 		closest = i;
281 	    }
282 	}
283     }
284     if (closest != -1)
285 	Obj[closest]->life = 0;
286 
287     return;
288 }
289 
290 /*
291  * Describes shot of 'type' which has 'status' and 'mods'.  If 'hit' is
292  * non-zero this description is part of a collision, otherwise its part
293  * of a launch message.
294  */
Describe_shot(int type,int status,modifiers_t mods,int hit)295 char *Describe_shot(int type, int status, modifiers_t mods, int hit)
296 {
297     const char		*name, *howmany = "a ", *plural = "";
298     static char		msg[MSG_LEN];
299 
300     switch (type) {
301     case OBJ_MINE:
302 	if (BIT(status, GRAVITY))
303 	    name = "bomb";
304 	else
305 	    name = "mine";
306 	break;
307     case OBJ_SMART_SHOT:
308 	name = "smart missile";
309 	break;
310     case OBJ_TORPEDO:
311 	name = "torpedo";
312 	break;
313     case OBJ_HEAT_SHOT:
314 	name = "heatseeker";
315 	break;
316     case OBJ_CANNON_SHOT:
317 	if (Mods_get(mods, ModsCluster)) {
318 	    howmany = "";
319 	    name = "flak";
320 	} else
321 	    name = "shot";
322 	break;
323     default:
324 	/*
325 	 * Cluster shots are actual debris from a cluster explosion
326 	 * so we describe it as "cluster debris".
327 	 */
328 	if (Mods_get(mods, ModsCluster)) {
329 	    howmany = "";
330 	    name = "debris";
331 	} else
332 	    name = "shot";
333 	break;
334     }
335 
336     if (Mods_get(mods, ModsMini) && !hit) {
337 	howmany = "some ";
338 	plural = (type == OBJ_TORPEDO) ? "es" : "s";
339     }
340 
341     sprintf (msg, "%s%s%s%s%s%s%s%s%s",
342 	     howmany,
343 	     ((Mods_get(mods, ModsVelocity)
344 	       || Mods_get(mods, ModsSpread)
345 	       || Mods_get(mods, ModsPower)) ? "modified " : ""),
346 	     (Mods_get(mods, ModsMini) ? "mini " : ""),
347 	     ((Mods_get(mods, ModsNuclear) & MODS_FULLNUCLEAR) ? "full " : ""),
348 	     ((Mods_get(mods, ModsNuclear) & MODS_NUCLEAR) ? "nuclear " : ""),
349 	     (Mods_get(mods, ModsImplosion) ? "imploding " : ""),
350 	     (Mods_get(mods, ModsCluster) ? "cluster " : ""),
351 	     name,
352 	     plural);
353 
354     return msg;
355 }
356 
Player_can_fire_shot(player_t * pl)357 static inline bool Player_can_fire_shot(player_t *pl)
358 {
359     if (pl->shots >= options.maxPlayerShots
360 	|| BIT(pl->used, HAS_SHIELD)
361 	|| Player_is_phasing(pl))
362 	return false;
363     return true;
364 }
365 
Fire_main_shot(player_t * pl,int type,int dir)366 void Fire_main_shot(player_t *pl, int type, int dir)
367 {
368     clpos_t m_gun, pos;
369 
370     if (!Player_can_fire_shot(pl))
371 	return;
372 
373     m_gun = Ship_get_m_gun_clpos(pl->ship, pl->dir);
374     pos.cx = pl->pos.cx + m_gun.cx;
375     pos.cy = pl->pos.cy + m_gun.cy;
376 
377     Fire_general_shot(pl->id, pl->team, pos, type,
378 		      dir, pl->mods, NO_ID);
379 }
380 
Fire_shot(player_t * pl,int type,int dir)381 void Fire_shot(player_t *pl, int type, int dir)
382 {
383     if (!Player_can_fire_shot(pl))
384 	return;
385 
386     Fire_general_shot(pl->id, pl->team, pl->pos, type,
387 		      dir, pl->mods, NO_ID);
388 }
389 
Fire_left_shot(player_t * pl,int type,int dir,int gun)390 void Fire_left_shot(player_t *pl, int type, int dir, int gun)
391 {
392     clpos_t l_gun, pos;
393 
394     if (!Player_can_fire_shot(pl))
395 	return;
396 
397     l_gun = Ship_get_l_gun_clpos(pl->ship, gun, pl->dir);
398     pos.cx = pl->pos.cx + l_gun.cx;
399     pos.cy = pl->pos.cy + l_gun.cy;
400 
401     Fire_general_shot(pl->id, pl->team, pos, type,
402 		      dir, pl->mods, NO_ID);
403 }
404 
Fire_right_shot(player_t * pl,int type,int dir,int gun)405 void Fire_right_shot(player_t *pl, int type, int dir, int gun)
406 {
407     clpos_t r_gun, pos;
408 
409     if (!Player_can_fire_shot(pl))
410 	return;
411 
412     r_gun = Ship_get_r_gun_clpos(pl->ship, gun, pl->dir);
413     pos.cx = pl->pos.cx + r_gun.cx;
414     pos.cy = pl->pos.cy + r_gun.cy;
415 
416     Fire_general_shot(pl->id, pl->team, pos, type,
417 		      dir, pl->mods, NO_ID);
418 }
419 
Fire_left_rshot(player_t * pl,int type,int dir,int gun)420 void Fire_left_rshot(player_t *pl, int type, int dir, int gun)
421 {
422     clpos_t l_rgun, pos;
423 
424     if (!Player_can_fire_shot(pl))
425 	return;
426 
427     l_rgun = Ship_get_l_rgun_clpos(pl->ship, gun, pl->dir);
428     pos.cx = pl->pos.cx + l_rgun.cx;
429     pos.cy = pl->pos.cy + l_rgun.cy;
430 
431     Fire_general_shot(pl->id, pl->team, pos, type,
432 		      dir, pl->mods, NO_ID);
433 }
434 
Fire_right_rshot(player_t * pl,int type,int dir,int gun)435 void Fire_right_rshot(player_t *pl, int type, int dir, int gun)
436 {
437     clpos_t r_rgun, pos;
438 
439     if (!Player_can_fire_shot(pl))
440 	return;
441 
442     r_rgun = Ship_get_r_rgun_clpos(pl->ship, gun, pl->dir);
443     pos.cx = pl->pos.cx + r_rgun.cx;
444     pos.cy = pl->pos.cy + r_rgun.cy;
445 
446     Fire_general_shot(pl->id, pl->team, pos, type,
447 		      dir, pl->mods, NO_ID);
448 }
449 
Fire_general_shot(int id,int team,clpos_t pos,int type,int dir,modifiers_t mods,int target_id)450 void Fire_general_shot(int id, int team,
451 		       clpos_t pos, int type, int dir,
452 		       modifiers_t mods, int target_id)
453 {
454     int used, fuse = 0, lock = 0, status = GRAVITY, i, ldir, minis;
455     int pl_range, pl_radius, rack_no = 0, racks_left = 0, r, on_this_rack = 0;
456     int side = 0, fired = 0;
457     double drain, mass = options.shotMass, life = options.shotLife;
458     double speed = options.shotSpeed, turnspeed = 0, max_speed = SPEED_LIMIT;
459     double angle, spread;
460     vector_t mv;
461     clpos_t shotpos;
462     object_t *mini_objs[MODS_MINI_MAX + 1];
463     torpobject_t *torp;
464     player_t *pl = Player_by_id(id);
465     cannon_t *cannon = Cannon_by_id(id);
466 
467     if (NumObjs >= MAX_TOTAL_SHOTS)
468 	return;
469 
470     if (!Mods_get(mods, ModsCluster))
471 	Mods_set(&mods, ModsVelocity, 0);
472     if (!Mods_get(mods, ModsMini))
473 	Mods_set(&mods, ModsSpread, 0);
474 
475     if (cannon) {
476 	mass = CANNON_SHOT_MASS;
477 	speed = Cannon_get_shot_speed(cannon);
478 	life = Cannon_get_shot_life(cannon);
479 	SET_BIT(status, FROMCANNON);
480     }
481 
482     switch (type) {
483     default:
484 	return;
485 
486     case OBJ_SHOT:
487 	Mods_clear(&mods);	/* Shots can't be modified! */
488 	/* FALLTHROUGH */
489     case OBJ_CANNON_SHOT:
490 	pl_range = pl_radius = 0;
491 	if (pl) {
492 	    if (pl->fuel.sum < -ED_SHOT)
493 		return;
494 	    Player_add_fuel(pl, ED_SHOT);
495 	    sound_play_sensors(pl->pos, FIRE_SHOT_SOUND);
496 	    Rank_fire_shot(pl);
497 	}
498 	if (!options.shotsGravity)
499 	    CLR_BIT(status, GRAVITY);
500 	break;
501 
502     case OBJ_SMART_SHOT:
503     case OBJ_HEAT_SHOT:
504 	if (type == OBJ_HEAT_SHOT
505 	    ? !options.allowHeatSeekers : !options.allowSmartMissiles) {
506 	    if (options.allowTorpedoes)
507 		type = OBJ_TORPEDO;
508 	    else
509 		return;
510 	}
511 	/* FALLTHROUGH */
512     case OBJ_TORPEDO:
513 	/*
514 	 * Make sure there are enough object entries for the mini shots.
515 	 */
516 	if (NumObjs + Mods_get(mods, ModsMini) >= MAX_TOTAL_SHOTS)
517 	    return;
518 
519 	if (pl && pl->item[ITEM_MISSILE] <= 0)
520 	    return;
521 
522 	if (options.nukeMinSmarts <= 0)
523 	    Mods_set(&mods, ModsNuclear, 0);
524 	if (Mods_get(mods, ModsNuclear)) {
525 	    if (pl) {
526 		used = ((Mods_get(mods, ModsNuclear) & MODS_FULLNUCLEAR)
527 			? pl->item[ITEM_MISSILE]
528 			: options.nukeMinSmarts);
529 		if (pl->item[ITEM_MISSILE] < options.nukeMinSmarts) {
530 		    Set_player_message_f(pl,
531 			    "You need at least %d missiles to fire %s!",
532 			    options.nukeMinSmarts,
533 			    Describe_shot (type, status, mods, 0));
534 		    return;
535 		}
536 	    } else
537 		used = options.nukeMinSmarts;
538 	    mass = MISSILE_MASS * used * NUKE_MASS_MULT;
539 	    pl_range = (type == OBJ_TORPEDO)
540 	      ? (int)NUKE_RANGE : MISSILE_RANGE;
541 	} else {
542 	    mass = MISSILE_MASS;
543 	    used = 1;
544 	    pl_range = (type == OBJ_TORPEDO)
545 	      ? (int)TORPEDO_RANGE : MISSILE_RANGE;
546 	}
547 	pl_range /= Mods_get(mods, ModsMini) + 1;
548 	pl_radius = MISSILE_LEN;
549 
550 	drain = used * ED_SMART_SHOT;
551 	if (Mods_get(mods, ModsCluster)) {
552 	    if (pl)
553 		drain += CLUSTER_MASS_DRAIN(mass);
554 	}
555 
556 	if (pl && Player_is_killed(pl))
557 	    life = rfrac() * 12;
558 	else if (!cannon)
559 	    life = options.missileLife;
560 
561 	switch (type) {
562 	case OBJ_HEAT_SHOT:
563 #ifndef HEAT_LOCK
564 	    lock = NO_ID;
565 #else  /* HEAT_LOCK */
566 	    if (pl == NULL)
567 		lock = target_id;
568 	    else {
569 		if (!BIT(pl->lock.tagged, LOCK_PLAYER)
570 		|| ((pl->lock.distance > pl->sensor_range)
571 		    && BIT(world->rules->mode, LIMITED_VISIBILITY))) {
572 		    lock = NO_ID;
573 		} else
574 		    lock = pl->lock.pl_id;
575 	    }
576 #endif /* HEAT_LOCK */
577 	    if (pl)
578 		sound_play_sensors(pl->pos, FIRE_HEAT_SHOT_SOUND);
579 	    max_speed = SMART_SHOT_MAX_SPEED * HEAT_SPEED_FACT;
580 	    turnspeed = SMART_TURNSPEED * HEAT_SPEED_FACT;
581 	    speed *= HEAT_SPEED_FACT;
582 	    break;
583 
584 	case OBJ_SMART_SHOT:
585 	    if (pl == NULL)
586 		lock = target_id;
587 	    else {
588 		if (!BIT(pl->lock.tagged, LOCK_PLAYER)
589 		|| ((pl->lock.distance > pl->sensor_range)
590 		    && BIT(world->rules->mode, LIMITED_VISIBILITY))
591 		|| !pl->visibility[GetInd(pl->lock.pl_id)].canSee)
592 		    return;
593 		lock = pl->lock.pl_id;
594 	    }
595 	    max_speed = SMART_SHOT_MAX_SPEED;
596 	    turnspeed = SMART_TURNSPEED;
597 	    break;
598 
599 	case OBJ_TORPEDO:
600 	    lock = NO_ID;
601 	    fuse = 8;
602 	    break;
603 
604 	default:
605 	    break;
606 	}
607 
608 	if (pl) {
609 	    if (pl->fuel.sum < -drain) {
610 		Set_player_message_f(pl,
611 			"You need at least %.1f fuel units to fire %s!",
612 			-drain, Describe_shot(type, status, mods, 0));
613 		return;
614 	    }
615 	    Player_add_fuel(pl, drain);
616 	    pl->item[ITEM_MISSILE] -= used;
617 
618 	    if (used > 1) {
619 		Set_message_f("%s has launched %s!", pl->name,
620 			      Describe_shot(type, status, mods, 0));
621 		sound_play_all(NUKE_LAUNCH_SOUND);
622 	    } else if (type == OBJ_SMART_SHOT)
623 		sound_play_sensors(pl->pos, FIRE_SMART_SHOT_SOUND);
624 	    else if (type == OBJ_TORPEDO)
625 		sound_play_sensors(pl->pos, FIRE_TORPEDO_SOUND);
626 	}
627 	break;
628     }
629 
630     minis = (Mods_get(mods, ModsMini) + 1);
631     speed *= (1 + (Mods_get(mods, ModsPower)
632 		   * MISSILE_POWER_SPEED_FACT));
633     max_speed *= (1 + (Mods_get(mods, ModsPower)
634 		       * MISSILE_POWER_SPEED_FACT));
635     turnspeed *= (1 + (Mods_get(mods, ModsPower)
636 		       * MISSILE_POWER_TURNSPEED_FACT));
637     spread = (double)(Mods_get(mods, ModsSpread) + 1);
638     /*
639      * Calculate the maximum time it would take to cross one ships width,
640      * don't fuse the shot/missile/torpedo for the owner only until that
641      * time passes.  This is a hack to stop various odd missile and shot
642      * mounting points killing the player when they're firing.
643      */
644     fuse += (int)((2.0 * (double)SHIP_SZ) / speed + 1.0);
645 
646     /*
647      * 			Missile Racks and Spread
648      * 			------------------------
649      *
650      * 		    A short story by H. J. Thompson
651      *
652      * Once upon a time, back in the "good old days" of XPilot, it was
653      * relatively easy thing to remember the few keys needed to fly and shoot.
654      * It was the day of Sopwith Camels biplanes, albeit triangular ones,
655      * doing close to-the-death machine gun combat with other triangular
656      * Red Barons, the hard vacuum of space whistling silently by as only
657      * something that doesn't exist could do (this was later augmented by
658      * artificial aural feedback devices on certain advanced hardware).
659      *
660      * Eventually the weapon designers came up with "smart" missiles, and
661      * another key was added to the control board, causing one missile to
662      * launch straight forwards from the front of the triangular ship.
663      * Soon other types of missiles were added, including "heat" seekers,
664      * and fast straight travelling "torpedoes" (hark, is that the sonorous
665      * ping-ping-ping of sonar equipment I hear?).
666      *
667      * Then one day along came a certain fellow who thought, among other
668      * things, that it would be neat to fire up to four missiles with one
669      * key press, just so the enemy pilot would be scared witless by the
670      * sudden appearance of four missiles hot on their tail.  To make things
671      * fair these "mini" missiles would have the same total damage of a
672      * normal missile, but would travel at the speed of a normal missile.
673      *
674      * However this fellow mused that simply launching all the missiles in
675      * the same direction and from the same point would cause the missiles
676      * to appear on top of each other.  Thus he added code to "spread" the
677      * missiles out at various angular offsets from the ship.  Indeed the
678      * angular offsets could be controlled using a spread modifier, and yet
679      * more keys appeared on a now crowded control desk.
680      *
681      * Interestingly the future would see the same fellow adding a two seater
682      * variant of the standard single seater ship, allowing one person
683      * to concentrate on flying the ship, while another could flick through
684      * out-of-date manuals searching for the right key combinations on
685      * the now huge console which would launch four full nuclear slow-cluster
686      * imploding mini super speed close spread torpedoes at the currently
687      * targetted enemy, and then engage emergency thrust and shields before
688      * the ominous looking tri-winged dagger ship recoiled at high velocity
689      * into a rocky wall half way across the other side of the universe.
690      *
691      * Back to our story, and this same fellow was musing at the design of
692      * multiple "mini" missiles, and noted that the angle of launch would
693      * also require a different launch point on the ship (actually it was
694      * the same position as if the front of the ship was rotated to point in
695      * the direction of missile launch, mainly because it was easier to
696      * write the launch/guidance computer software that way).
697      *
698      * Later, some artistically (or sadistically) minded person decided that
699      * triangular ships just didn't look good (even though they were very
700      * spatially dynamic, cheap and easy to build), and wouldn't it be just
701      * fantastic if one could have a ship shaped like a banana!  Sensibly,
702      * however, he restricted missiles and guns to the normal single frontal
703      * launching point.
704      *
705      * A few weeks later, somebody else decided how visually pleasing it
706      * would be if one could design where missiles could be fired from by
707      * adding "missile rack" points on the ship.  Up to four racks were
708      * available, and missiles would fire from exactly these points on the
709      * ship.  Since one to four missiles could be fired in one go, the
710      * combinations with various ship designs were numerous (16).
711      *
712      * What would happen if somebody fired four missiles in one go, from a
713      * ship that only had three missile racks?  How about two missiles from
714      * one with four racks?  Sadly the missile launch software hadn't been
715      * designed to take this sort of thing into account, and incredibly the
716      * original programmer wasn't notified until after First Customer Ship
717      * [sic], the launch software only slightly modified by the ship
718      * designer, who didn't know the first thing about launch acceleration
719      * curves or electronic owner immunity fuse timers.
720      *
721      * Pilots found their missiles were being fired from random points and
722      * in sometimes very odd directions, occasionally even destroying the
723      * ship without trace, severely annoying the ship's owners and several
724      * insurance underwriters.  Not soon after several ship designers were
725      * mysteriously killed in a freak "accident" involving a stray nuclear
726      * cluster bomb, and the remaining ship designers became very careful
727      * to place missile racks and extra gun turrets well away from the
728      * ship's superstructure.
729      *
730      * The original programmer who invented multiple "mini" spreading
731      * missiles quickly decided to revisit his code before any "accidents"
732      * came his way, and spent a good few hours making sure one couldn't
733      * shoot oneself in the "foot", and that missiles where launched in some
734      * reasonable and sensible directions based on the position of the
735      * missile racks.
736      *
737      * 			How It Actually Works
738      *			---------------------
739      *
740      * The first obstacle is getting the right number of missiles fired
741      * from each combination of missile rack configurations;
742      *
743      *
744      *		Minis	1	2	3	4
745      * Racks
746      *	1		1	2	3	4
747      *
748      *	2		1/-	1/1	2/1	2/2
749      *			-/1		1/2
750      *
751      *	3		1/-/-	1/1/-	1/1/1	2/1/1
752      *			-/1/-	-/1/1		1/2/1
753      *			-/-/1	1/-/1		1/1/2
754      *
755      *	4		1/-/-/-	1/1/-/-	1/1/1/-	1/1/1/1
756      *			-/1/-/-	-/1/1/-	-/1/1/1
757      *			-/-/1/-	-/-/1/1	1/-/1/1
758      *			-/-/-/1 1/-/-/1	1/1/-/1
759      *
760      * To read; For example with 2 Minis and 3 Racks, the first round will
761      * fire 1/1/-, which is one missile from left and middle racks.  The
762      * next time fired will be -/1/1; middle and right, next fire is
763      * 1/-/1; left and right.  Next time goes to the beggining state.
764      *
765      * 			Comment Point 1
766      *			---------------
767      *
768      * The *starting* rack number for each salvo cycles through the number
769      * of missiles racks.  This is stored in the player variable
770      * 'pl->missile_rack', and is only incremented after each salvo (not
771      * after each mini missile is fired).  This value is used to initialise
772      * 'rack_no', which stores the current rack that missiles are fired from.
773      *
774      * 'on_this_rack' is computed to be the number of missiles that will be
775      * fired from 'rack_no', and 'r' is used as a counter to this value.
776      *
777      * 'racks_left' count how many unused missiles racks are left on the ship
778      * in this mini missile salvo.
779      *
780      * 			Comment Point 2
781      *			---------------
782      *
783      * When 'r' reaches 'on_this_rack' all the missiles have been fired for
784      * this rack, and the next rack should be used.  'rack_no' is incremented
785      * modulo the number of available racks, and 'racks_left' is decremented.
786      * At this point 'on_this_rack' is recomputed for the next rack, and 'r'
787      * reset to zero.  Thus initially these two variables are both zero, and
788      * 'rack_no' is one less, such that these variables can be computed inside
789      * the loop to make the code simpler.
790      *
791      * The computation of 'on_this_rack' is as follows;  Given that there
792      * are M missiles and R racks remaining;
793      *
794      *	on_this_rack = int(M / R);	(ie. round down to lowest int)
795      *
796      * Then;
797      *
798      *	(M - on_this_rack) / (R - 1) < (M / R).
799      *
800      * That is, the number of missiles fired on the next rack will be
801      * more precise, and trivially can be seen that when R is 1, will
802      * give an exact number of missiles to fire on the last rack.
803      *
804      * In the code 'M' is (minis - i), and 'R' is racks_left.
805      *
806      *			Comment Point 3
807      *			---------------
808      *
809      * In order that multiple missiles fired from one rack do not conincide,
810      * each missile has to be "spread" based on the number of missiles
811      * fired from this rack point.
812      *
813      * This is computed similar to the wide shot code;
814      *
815      *	angle = (N - 1 - 2 * i) / (N - 1)
816      *
817      * Where N is the number of shots/missiles to be fired, and i is a counter
818      * from 0 .. N-1.
819      *
820      * 		i	0	1	2	3
821      * N
822      * 1		0
823      * 2		1	-1
824      * 3		1	0	-1
825      * 4		1	0.333	-0.333	-1
826      *
827      * In this code 'N' is 'on_this_rack'.
828      *
829      * Also the position of the missile rack from the center line of the
830      * ship (stored in 'side') has a linear effect on the angle, such that
831      * a point farthest from the center line contributes the largest angle;
832      *
833      * angle += (side / SHIP_SZ)
834      *
835      * Since the eventual 'angle' value used in the code should be a
836      * percentage of the unmodified launch angle, it should be ranged between
837      * -1.00 and +1.00, and thus the first angle is reduced by 33% and the
838      * second by 66%.
839      *
840      * Contact: harveyt@sco.com
841      */
842 
843     if (pl && type != OBJ_SHOT) {
844 	/*
845 	 * Initialise missile rack spread variables. (See Comment Point 1)
846 	 */
847 	on_this_rack = 0;
848 	racks_left = pl->ship->num_m_rack;
849 	rack_no = pl->missile_rack - 1;
850 	if (++pl->missile_rack >= pl->ship->num_m_rack)
851 	    pl->missile_rack = 0;
852     }
853 
854     for (r = 0, i = 0; i < minis; i++, r++) {
855 	object_t *shot;
856 
857 	if ((shot = Object_allocate()) == NULL)
858 	    break;
859 
860 	shot->life 	= life / minis;
861 	shot->fuse 	= fuse;
862 	shot->mass	= mass / minis;
863 	shot->type	= type;
864 	shot->id	= (pl ? pl->id : NO_ID);
865 	shot->team	= team;
866 	shot->color	= WHITE;
867 
868 	/* shot->count = 0;
869 	   shot->info 	= lock; */
870 	switch (shot->type) {
871 	case OBJ_TORPEDO:
872 	    TORP_PTR(shot)->torp_count = 0;
873 	    break;
874 	case OBJ_HEAT_SHOT:
875 	    HEAT_PTR(shot)->heat_count = 0;
876 	    HEAT_PTR(shot)->heat_lock_id = lock;
877 	    break;
878 	case OBJ_SMART_SHOT:
879 	    SMART_PTR(shot)->smart_count = 0;
880 	    SMART_PTR(shot)->smart_lock_id = lock;
881 	    break;
882 	default:
883 	    break;
884 	}
885 
886 	shotpos = pos;
887 	if (pl && type != OBJ_SHOT) {
888 	    clpos_t m_rack;
889 	    if (r == on_this_rack) {
890 		/*
891 		 * We've fired all the mini missiles for the current rack,
892 		 * we now move onto the next one. (See Comment Point 2)
893 		 */
894 		on_this_rack = (minis - i) / racks_left--;
895 		if (on_this_rack < 1) on_this_rack = 1;
896 		if (++rack_no >= pl->ship->num_m_rack)
897 		    rack_no = 0;
898 		r = 0;
899 	    }
900 	    m_rack = Ship_get_m_rack_clpos(pl->ship, rack_no, pl->dir);
901 	    shotpos.cx += m_rack.cx;
902 	    shotpos.cy += m_rack.cy;
903 	    /*side = CLICK_TO_PIXEL(pl->ship->m_rack[rack_no][0].cy);*/
904 	    side = CLICK_TO_PIXEL(
905 		Ship_get_m_rack_clpos(pl->ship, rack_no, 0).cy);
906 	}
907 	shotpos = World_wrap_clpos(shotpos);
908 	Object_position_init_clpos(shot, shotpos);
909 
910 	if (type == OBJ_SHOT || !pl)
911 	    angle = 0.0;
912 	else {
913 	    /*
914 	     * Calculate the percentage unmodified launch angle for missiles.
915 	     * (See Comment Point 3).
916 	     */
917 	    if (on_this_rack <= 1)
918 		angle = 0.0;
919 	    else {
920 		angle = (double)(on_this_rack - 1 - 2 * r);
921 		angle /= (3.0 * (double)(on_this_rack - 1));
922 	    }
923 	    angle += (double)(2 * side) / (double)(3 * SHIP_SZ);
924 	}
925 
926 	/*
927 	 * Torpedoes spread like mines, except the launch direction
928 	 * is preset over the range +/- MINI_TORPEDO_SPREAD_ANGLE.
929 	 * (This is not modified by the spread, the initial velocity is)
930 	 *
931 	 * Other missiles are just launched in a different direction
932 	 * which varies over the range +/- MINI_MISSILE_SPREAD_ANGLE,
933 	 * which the spread modifier varies.
934 	 */
935 	switch (type) {
936 	case OBJ_TORPEDO:
937 	    torp = TORP_PTR(shot);
938 
939 	    angle *= (MINI_TORPEDO_SPREAD_ANGLE / 360.0) * RES;
940 	    ldir = MOD2(dir + (int)angle, RES);
941 	    mv.x = MINI_TORPEDO_SPREAD_SPEED * tcos(ldir) / spread;
942 	    mv.y = MINI_TORPEDO_SPREAD_SPEED * tsin(ldir) / spread;
943 	    /*
944 	     * This causes the added initial velocity to reduce to
945 	     * zero over the MINI_TORPEDO_SPREAD_TIME.
946 	     * FIX: torpedoes should have the same speed
947 	     *      regardless of minification.
948 	     */
949 	    torp->torp_spread_left = MINI_TORPEDO_SPREAD_TIME;
950 	    torp->acc.x = -mv.x / MINI_TORPEDO_SPREAD_TIME;
951 	    torp->acc.y = -mv.y / MINI_TORPEDO_SPREAD_TIME;
952 	    ldir = dir;
953 	    break;
954 
955 	default:
956 	    angle *= (MINI_MISSILE_SPREAD_ANGLE / 360.0) * RES / spread;
957 	    ldir = MOD2(dir + (int)angle, RES);
958 	    mv.x = mv.y = shot->acc.x = shot->acc.y = 0;
959 	    break;
960 	}
961 
962 	/*
963 	 * Option constantSpeed affects shots' initial velocity.
964 	 * This lets you accelerate shots nicely.
965 	 */
966 	if (pl && options.constantSpeed) {
967 	    pl->vel.x += options.constantSpeed * pl->acc.x;
968 	    pl->vel.y += options.constantSpeed * pl->acc.y;
969 	}
970 	if (options.ngControls && pl && ldir == pl->dir) {
971 	    /*
972 	     * If using "NG controls", use float dir when shooting
973 	     * straight ahead.
974 	     */
975 	    shot->vel.x = mv.x + pl->vel.x + pl->float_dir_cos * speed;
976 	    shot->vel.y = mv.y + pl->vel.y + pl->float_dir_sin * speed;
977 	} else {
978 	    shot->vel.x = mv.x + (pl ? pl->vel.x : 0.0) + tcos(ldir) * speed;
979 	    shot->vel.y = mv.y + (pl ? pl->vel.y : 0.0) + tsin(ldir) * speed;
980 	}
981 	/* remove constantSpeed */
982 	if (pl && options.constantSpeed) {
983 	     pl->vel.x -= options.constantSpeed * pl->acc.x;
984 	     pl->vel.y -= options.constantSpeed * pl->acc.y;
985 	}
986 
987 	shot->obj_status	= status;
988 
989 	if (shot->type == OBJ_TORPEDO
990 	    || shot->type == OBJ_HEAT_SHOT
991 	    || shot->type == OBJ_SMART_SHOT) {
992 	    missileobject_t *missile = MISSILE_PTR(shot);
993 
994 	    missile->missile_turnspeed = turnspeed;
995 	    missile->missile_max_speed = max_speed;
996 	    missile->missile_dir = ldir;
997 	}
998 
999 	shot->mods  	= mods;
1000 	shot->pl_range  = pl_range;
1001 	shot->pl_radius = pl_radius;
1002 	Cell_add_object(shot);
1003 
1004 	mini_objs[fired] = shot;
1005 	fired++;
1006     }
1007 
1008     /*
1009      * Recoil must be done instantaneously otherwise ship moves back after
1010      * firing each mini missile.
1011      */
1012     if (pl) {
1013 	double dx, dy;
1014 
1015 	dx = dy = 0;
1016 	for (i = 0; i < fired; i++) {
1017 	    dx += (mini_objs[i]->vel.x - pl->vel.x) * mini_objs[i]->mass;
1018 	    dy += (mini_objs[i]->vel.y - pl->vel.y) * mini_objs[i]->mass;
1019 	}
1020 	pl->vel.x -= dx / pl->mass;
1021 	pl->vel.y -= dy / pl->mass;
1022     }
1023 }
1024 
Can_shoot_normal_shot(player_t * pl)1025 bool Can_shoot_normal_shot(player_t *pl)
1026 {
1027     int                 i, shot_angle;
1028 
1029     /* same test as in Fire_normal_shots */
1030     if (frame_time <= pl->shot_time + options.fireRepeatRate - timeStep + 1e-3)
1031         return false;
1032     else
1033         return true;
1034 }
1035 
Fire_normal_shots(player_t * pl)1036 void Fire_normal_shots(player_t *pl)
1037 {
1038     int			i, shot_angle;
1039 
1040     /* Average non-integer repeat rates, so that smaller gap occurs first.
1041      * 1e-3 "fudge factor" because "should be equal" cases return. */
1042     if (frame_time <= pl->shot_time + options.fireRepeatRate - timeStep + 1e-3)
1043  	return;
1044     pl->shot_time = MAX(frame_time, pl->shot_time + options.fireRepeatRate);
1045 
1046     shot_angle = MODS_SPREAD_MAX - Mods_get(pl->mods, ModsSpread);
1047 
1048     Fire_main_shot(pl, OBJ_SHOT, pl->dir);
1049     for (i = 0; i < pl->item[ITEM_WIDEANGLE]; i++) {
1050 	if (pl->ship->num_l_gun > 0) {
1051 	    Fire_left_shot(pl, OBJ_SHOT, MOD2(pl->dir + (1 + i) * shot_angle,
1052 			   RES), i % pl->ship->num_l_gun);
1053 	}
1054 	else {
1055 	    Fire_main_shot(pl, OBJ_SHOT, MOD2(pl->dir + (1 + i) * shot_angle,
1056 			   RES));
1057 	}
1058 	if (pl->ship->num_r_gun > 0) {
1059 	    Fire_right_shot(pl, OBJ_SHOT, MOD2(pl->dir - (1 + i) * shot_angle,
1060 			    RES), i % pl->ship->num_r_gun);
1061 	}
1062 	else {
1063 	    Fire_main_shot(pl, OBJ_SHOT, MOD2(pl->dir - (1 + i) * shot_angle,
1064 			   RES));
1065 	}
1066     }
1067     for (i = 0; i < pl->item[ITEM_REARSHOT]; i++) {
1068 	if ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) < 0) {
1069 	    if (pl->ship->num_l_rgun > 0) {
1070 		Fire_left_rshot(pl, OBJ_SHOT, MOD2(pl->dir + RES/2
1071 		    + ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) * shot_angle) / 2,
1072 			RES), (i - (pl->item[ITEM_REARSHOT] + 1) / 2)
1073 				% pl->ship->num_l_rgun);
1074 	    }
1075 	    else {
1076 		Fire_shot(pl, OBJ_SHOT, MOD2(pl->dir + RES/2
1077 		    + ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) * shot_angle) / 2,
1078 			RES));
1079 	    }
1080 	}
1081 	if ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) > 0) {
1082 	    if (pl->ship->num_r_rgun > 0) {
1083 		Fire_right_rshot(pl, OBJ_SHOT, MOD2(pl->dir + RES/2
1084 		    + ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) * shot_angle) / 2,
1085 			RES), (pl->item[ITEM_REARSHOT] / 2 - i - 1)
1086 				 % pl->ship->num_r_rgun);
1087 	    }
1088 	    else {
1089 		Fire_shot(pl, OBJ_SHOT, MOD2(pl->dir + RES/2
1090 		    + ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) * shot_angle) / 2,
1091 			RES));
1092 	    }
1093 	}
1094 	if ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) == 0)
1095 	     Fire_shot(pl, OBJ_SHOT, MOD2(pl->dir + RES/2
1096 		+ ((pl->item[ITEM_REARSHOT] - 1 - 2 * i) * shot_angle) / 2,
1097 			RES));
1098     }
1099 }
1100 
1101 
1102 /* Removes shot from array */
Delete_shot(int ind)1103 void Delete_shot(int ind)
1104 {
1105     object_t *shot = Obj[ind];	/* Used when swapping places */
1106     ballobject_t *ball;
1107     itemobject_t *item;
1108     player_t *pl;
1109     bool addMine = false, addHeat = false, addBall = false;
1110     modifiers_t mods;
1111     int i, intensity, type, color, num_debris, status;
1112     double modv, speed_modv, life_modv, num_modv, mass, min_life, max_life;
1113 
1114     switch (shot->type) {
1115 
1116     case OBJ_SPARK:
1117     case OBJ_DEBRIS:
1118     case OBJ_WRECKAGE:
1119 	break;
1120 
1121     case OBJ_ASTEROID:
1122 	Break_asteroid(WIRE_PTR(shot));
1123 	break;
1124 
1125     case OBJ_BALL:
1126 	ball = BALL_PTR(shot);
1127 	if (ball->id != NO_ID)
1128 	    Detach_ball(Player_by_id(ball->id), ball);
1129 	else {
1130 	    /*
1131 	     * Maybe some player is still busy trying to connect to this ball.
1132 	     */
1133 	    for (i = 0; i < NumPlayers; i++) {
1134 		player_t *pl_i = Player_by_index(i);
1135 
1136 		if (pl_i->ball == ball)
1137 		    pl_i->ball = NULL;
1138 	    }
1139 	}
1140 	if (ball->ball_owner == NO_ID) {
1141 	    /*
1142 	     * If the ball has never been owned, the only way it could
1143 	     * have been destroyed is by being knocked out of the goal.
1144 	     * Therefore we force the ball to be recreated.
1145 	     */
1146 	    ball->ball_treasure->have = false;
1147 	    SET_BIT(ball->obj_status, RECREATE);
1148 	}
1149 	if (BIT(ball->obj_status, RECREATE)) {
1150 	    addBall = true;
1151 	    if (BIT(ball->obj_status, NOEXPLOSION))
1152 		break;
1153 	    sound_play_sensors(ball->pos, EXPLODE_BALL_SOUND);
1154 
1155 	    /* The ball could be inside a BallArea, check whether
1156 	     * the sparks can exist here. Should we set a team? */
1157 	    if (is_inside(ball->prevpos.cx, ball->prevpos.cy,
1158 			  NONBALL_BIT | NOTEAM_BIT, OBJ_PTR(ball)) != NO_GROUP)
1159 		break;
1160 
1161 	    Make_debris(ball->prevpos,
1162 			ball->vel,
1163 			ball->id,
1164 			ball->team,
1165 			OBJ_DEBRIS,
1166 			DEBRIS_MASS,
1167 			GRAVITY,
1168 			RED,
1169 			8,
1170 			(int)(10 + 10 * rfrac()),
1171 			0, RES - 1,
1172 			10.0, 50.0,
1173 			10.0, 54.0);
1174 	}
1175 	break;
1176 	/* Shots related to a player. */
1177 
1178     case OBJ_MINE:
1179     case OBJ_HEAT_SHOT:
1180     case OBJ_TORPEDO:
1181     case OBJ_SMART_SHOT:
1182     case OBJ_CANNON_SHOT:
1183 	if (shot->mass == 0)
1184 	    break;
1185 
1186 	status = GRAVITY;
1187 	if (shot->type == OBJ_MINE)
1188 	    status |= COLLISIONSHOVE;
1189 	if (BIT(shot->obj_status, FROMCANNON))
1190 	    status |= FROMCANNON;
1191 
1192 	if (Mods_get(shot->mods, ModsNuclear))
1193 	    sound_play_all(NUKE_EXPLOSION_SOUND);
1194 	else if (shot->type == OBJ_MINE)
1195 	    sound_play_sensors(shot->pos, MINE_EXPLOSION_SOUND);
1196 	else
1197 	    sound_play_sensors(shot->pos, OBJECT_EXPLOSION_SOUND);
1198 
1199 	if (Mods_get(shot->mods, ModsCluster)) {
1200 	    type = OBJ_SHOT;
1201 	    color = WHITE;
1202 	    mass = options.shotMass * 3;
1203 	    modv = 1 << Mods_get(shot->mods, ModsVelocity);
1204 	    num_modv = 4;
1205 	    if (Mods_get(shot->mods, ModsNuclear)) {
1206 		modv *= 4.0;
1207 		num_modv = 1;
1208 	    }
1209 	    life_modv = modv * 0.20;
1210 	    speed_modv = 1.0 / modv;
1211 	    intensity = (int)CLUSTER_MASS_SHOTS(shot->mass);
1212 	} else {
1213 	    type = OBJ_DEBRIS;
1214 	    color = RED;
1215 	    mass = DEBRIS_MASS;
1216 	    modv = 1;
1217 	    num_modv = 1;
1218 	    life_modv = modv;
1219 	    speed_modv = modv;
1220 	    if (shot->type == OBJ_MINE)
1221 		intensity = 512;
1222 	    else
1223 		intensity = 32;
1224 	    num_modv /= ((double)(Mods_get(shot->mods, ModsMini) + 1));
1225 	}
1226 
1227 	if (Mods_get(shot->mods, ModsNuclear)) {
1228 	    double nuke_factor;
1229 
1230 	    if (shot->type == OBJ_MINE)
1231 		nuke_factor = NUKE_MINE_EXPL_MULT * shot->mass / MINE_MASS;
1232 	    else
1233 		nuke_factor = NUKE_SMART_EXPL_MULT * shot->mass / MISSILE_MASS;
1234 
1235 	    nuke_factor *= ((Mods_get(shot->mods, ModsMini) + 1)
1236 			    / SHOT_MULT(shot));
1237 	    intensity = (int)(intensity * nuke_factor);
1238 	}
1239 
1240 	if (Mods_get(shot->mods, ModsImplosion))
1241 	    mass = -mass;
1242 
1243 	if (shot->type == OBJ_TORPEDO
1244 	    || shot->type == OBJ_HEAT_SHOT
1245 	    || shot->type == OBJ_SMART_SHOT)
1246 	    intensity /= (1 + Mods_get(shot->mods, ModsPower));
1247 
1248 	num_debris = (int)(intensity * num_modv * (0.20 + (0.10 * rfrac())));
1249 	min_life = 8 * life_modv;
1250 	max_life = (intensity >> 1) * life_modv;
1251 
1252 	/* Hack - make sure nuke debris don't get insanely long life time. */
1253 	if (Mods_get(shot->mods, ModsNuclear))
1254 	    max_life = options.nukeDebrisLife;
1255 
1256 #if 0
1257 	warn("type = %16s (%c%c) inten = %-6d num = %-6d life: %.1f - %.1f",
1258 	     Object_typename(shot),
1259 	     (Mods_get(shot->mods, ModsNuclear) ? 'N' : '-'),
1260 	     (Mods_get(shot->mods, ModsCluster) ? 'C' : '-'),
1261 	     intensity, num_debris, min_life, max_life);
1262 #endif
1263 
1264 	Make_debris(shot->prevpos,
1265 		    shot->vel,
1266 		    shot->id,
1267 		    shot->team,
1268 		    type,
1269 		    mass,
1270 		    status,
1271 		    color,
1272 		    6,
1273 		    num_debris,
1274 		    0, RES - 1,
1275 		    20 * speed_modv, (intensity >> 2) * speed_modv,
1276 		    min_life, max_life);
1277 	break;
1278 
1279     case OBJ_SHOT:
1280 	if (shot->id == NO_ID
1281 	    || BIT(shot->obj_status, FROMCANNON)
1282 	    || Mods_get(shot->mods, ModsCluster))
1283 	    break;
1284 	pl = Player_by_id(shot->id);
1285 	if (--pl->shots <= 0)
1286 	    pl->shots = 0;
1287 	break;
1288 
1289     case OBJ_PULSE:
1290 	if (shot->id == NO_ID
1291 	    || BIT(shot->obj_status, FROMCANNON))
1292 	    break;
1293 	pl = Player_by_id(shot->id);
1294 	if (--pl->num_pulses <= 0)
1295 	    pl->num_pulses = 0;
1296 	break;
1297 
1298 	/* Special items. */
1299     case OBJ_ITEM:
1300 	item = ITEM_PTR(shot);
1301 
1302 	switch (item->item_type) {
1303 
1304 	case ITEM_MISSILE:
1305 	    /* If -timeStep < item->life <= 0, then it died of old age. */
1306 	    /* If it was picked up, then life was set to 0 and it is now
1307 	     * -timeStep after the substract in update.c. */
1308 	    if (-timeStep < item->life && item->life <= 0) {
1309 		if (item->color != WHITE) {
1310 		    item->color = WHITE;
1311 		    item->life  = WARN_TIME;
1312 		    return;
1313 		}
1314 		if (rfrac() < options.rogueHeatProb)
1315 		    addHeat = true;
1316 	    }
1317 	    break;
1318 
1319 	case ITEM_MINE:
1320 	    /* See comment for ITEM_MISSILE above */
1321 	    if (-timeStep < item->life && item->life <= 0) {
1322 		if (item->color != WHITE) {
1323 		    item->color = WHITE;
1324 		    item->life  = WARN_TIME;
1325 		    return;
1326 		}
1327 		if (rfrac() < options.rogueMineProb)
1328 		    addMine = true;
1329 	    }
1330 	    break;
1331 
1332 	default:
1333 	    break;
1334 	}
1335 
1336 	world->items[item->item_type].num--;
1337 
1338 	break;
1339 
1340     default:
1341 	xpprintf("%s Delete_shot(): Unknown shot type %d.\n",
1342 		 showtime(), shot->type);
1343 	break;
1344     }
1345 
1346     Cell_remove_object(shot);
1347     shot->life = 0;
1348     shot->type = 0;
1349     shot->mass = 0;
1350 
1351     Object_free_ind(ind);
1352 
1353     if (addMine || addHeat) {
1354 	Mods_clear(&mods);
1355 	if (rfrac() <= 0.333)
1356 	    Mods_set(&mods, ModsCluster, 1);
1357 	if (rfrac() <= 0.333)
1358 	    Mods_set(&mods, ModsImplosion, 1);
1359 	Mods_set(&mods, ModsVelocity,
1360 		 (int)(rfrac() * (MODS_VELOCITY_MAX + 1)));
1361 	Mods_set(&mods, ModsPower,
1362 		 (int)(rfrac() * (MODS_POWER_MAX + 1)));
1363 	if (addMine) {
1364 	    long gravity_status = ((rfrac() < 0.5) ? GRAVITY : 0);
1365 	    vector_t zero_vel = { 0.0, 0.0 };
1366 
1367 	    Place_general_mine(NO_ID, TEAM_NOT_SET, gravity_status,
1368 			       shot->pos, zero_vel, mods);
1369 	}
1370 	else if (addHeat)
1371 	    Fire_general_shot(NO_ID, TEAM_NOT_SET, shot->pos,
1372 			      OBJ_HEAT_SHOT, (int)(rfrac() * RES),
1373 			      mods, NO_ID);
1374     }
1375     else if (addBall) {
1376 	ball = BALL_PTR(shot);
1377 	Make_treasure_ball(ball->ball_treasure);
1378     }
1379 }
1380 
1381 
1382 /*
1383  * The new ball movement code since XPilot version 3.4.0 as made
1384  * by Bretton Wade.  The code was submitted in context diff format
1385  * by Mark Boyns.  Here is a an excerpt from a post in
1386  * rec.games.computer.xpilot by Bretton Wade dated 27 Jun 1995:
1387  *
1388  * If I'm not mistaken (not having looked very closely at the code
1389  * because I wasn't sure what it was trying to do), the original move_ball
1390  * routine was trying to model a Hook's law spring, but squared the
1391  * deformation term, which would lead to exagerated behavior as the spring
1392  * stretched too far. Not really a divide by zero, but effectively
1393  * producing large numbers.
1394  *
1395  * When I coded up the spring myself, I found that I could recreate the
1396  * effect by using a VERY strong spring. This can be defeated, however, by
1397  * damping. Specifically, If you compute the critical damping factor, then
1398  * you could have the cable always be the correct length. This makes me
1399  * wonder how to decide when the cable snaps.
1400  *
1401  * I chose a relatively strong spring, and a small damping factor, to make
1402  * for a nice realistic bounce when you grab at the treasure. It also
1403  * gives a fairley close approximation to the "normal" feel of the
1404  * treasure.
1405  *
1406  * I modeled the cable as having zero mass, or at least insignificant mass
1407  * as compared to the ship and ball. This greatly simplifies the math, and
1408  * leads to the conclusion that there will be no change in velocity when
1409  * the cable breaks. You can check this by integrating the momentum along
1410  * the cable, and the ship or ball.
1411  *
1412  * If you assume that the cable snaps in the middle, then half of the
1413  * potential energy goes to each object attached. However, as you said, the
1414  * total momentum of the system cannot change. Because the weight of the
1415  * cable is small, the vast majority of the potential energy will become
1416  * heat. I've had two physicists verify this for me, and they both worked
1417  * really hard on the problem because they found it interesting.
1418  *
1419  * End of post.
1420  *
1421  * Changes since then:
1422  *
1423  * Comment from people was that the string snaps too soon.
1424  * Changed the value (max_spring_ratio) at which the string snaps
1425  * from 0.25 to 0.30.  Not sure if that helps enough, or too much.
1426  */
Update_connector_force(ballobject_t * ball)1427 void Update_connector_force(ballobject_t *ball)
1428 {
1429     player_t		*pl = Player_by_id(ball->id);
1430     vector_t		D;
1431     double		length, force, ratio, accell, damping;
1432     /* const double		k = 1500.0, b = 2.0; */
1433     /* const double		max_spring_ratio = 0.30; */
1434 
1435     UNUSED_PARAM(world);
1436 
1437     /* no player connected ? */
1438     if (!pl)
1439 	return;
1440 
1441     /* being connected makes the player visible */
1442     if(pl->forceVisible == 0){
1443        pl->forceVisible =2;
1444     }
1445 
1446     /* compute the normalized vector between the ball and the player */
1447     D.x = WRAP_DCX(pl->pos.cx - ball->pos.cx);
1448     D.y = WRAP_DCY(pl->pos.cy - ball->pos.cy);
1449     length = VECTOR_LENGTH(D);
1450     if (length > 0.0) {
1451 	D.x /= length;
1452 	D.y /= length;
1453     } else
1454 	D.x = D.y = 0.0;
1455 
1456     /* compute the ratio for the spring action */
1457     ratio = 1 - length / (options.ballConnectorLength * CLICK);
1458 
1459     /* compute force by spring for this length */
1460     force = options.ballConnectorSpringConstant * ratio;
1461 
1462     /* If we have string-style connectors then it is allowed to be
1463      * shorted than its natural length. */
1464     if (options.connectorIsString && ratio > 0.0)
1465 	return;
1466 
1467     /* if the tether is too long or too short, release it */
1468     if (ABS(ratio) > options.maxBallConnectorRatio) {
1469 	Detach_ball(pl, ball);
1470 	return;
1471     }
1472 
1473     damping = -options.ballConnectorDamping
1474 	* ((pl->vel.x - ball->vel.x) * D.x + (pl->vel.y - ball->vel.y) * D.y);
1475 
1476     /* compute accelleration for player, assume t = 1 */
1477     accell = (force + damping) / pl->mass;
1478     pl->vel.x += D.x * accell * timeStep;
1479     pl->vel.y += D.y * accell * timeStep;
1480 
1481     /* compute accelleration for ball, assume t = 1 */
1482     accell = (force + damping) / ball->mass;
1483     ball->vel.x += -D.x * accell * timeStep;
1484     ball->vel.y += -D.y * accell * timeStep;
1485 }
1486 
Update_torpedo(torpobject_t * torp)1487 void Update_torpedo(torpobject_t *torp)
1488 {
1489     double acc;
1490 
1491     UNUSED_PARAM(world);
1492     if (Mods_get(torp->mods, ModsNuclear))
1493 	acc = (torp->torp_count < NUKE_SPEED_TIME) ? NUKE_ACC : 0.0;
1494     else
1495 	acc = (torp->torp_count < TORPEDO_SPEED_TIME) ? TORPEDO_ACC : 0.0;
1496     torp->torp_count += timeStep;
1497     acc *= (1 + (Mods_get(torp->mods, ModsPower) * MISSILE_POWER_SPEED_FACT));
1498     if ((torp->torp_spread_left -= timeStep) <= 0) {
1499 	torp->acc.x = 0;
1500 	torp->acc.y = 0;
1501 	torp->torp_spread_left = 0;
1502     }
1503     torp->vel.x += acc * tcos(torp->missile_dir);
1504     torp->vel.y += acc * tsin(torp->missile_dir);
1505 }
1506 
Update_missile(missileobject_t * missile)1507 void Update_missile(missileobject_t *missile)
1508 {
1509     player_t *pl;
1510     int angle, theta;
1511     double range = 0.0, acc = SMART_SHOT_ACC;
1512     double x_dif = 0.0, y_dif = 0.0, shot_speed, a;
1513 
1514     if (missile->type == OBJ_HEAT_SHOT) {
1515 	heatobject_t *heat = HEAT_PTR(missile);
1516 
1517 	acc = SMART_SHOT_ACC * HEAT_SPEED_FACT;
1518 	if (heat->heat_lock_id >= 0) {
1519 	    clpos_t engine;
1520 
1521 	    /* Get player and set min to distance */
1522 	    pl = Player_by_id(heat->heat_lock_id);
1523 	    /* kps - bandaid since Player_by_id can return NULL. */
1524 	    if (!pl)
1525 		return;
1526 	    engine = Ship_get_engine_clpos(pl->ship, pl->dir);
1527 	    range = Wrap_length(pl->pos.cx + engine.cx - heat->pos.cx,
1528 				pl->pos.cy + engine.cy - heat->pos.cy)
1529 		/ CLICK;
1530 	} else {
1531 	    /* No player. Number of moves so that new target is searched */
1532 	    pl = NULL;
1533 	    heat->heat_count = HEAT_WIDE_TIMEOUT + HEAT_WIDE_ERROR;
1534 	}
1535 	if (pl && Player_is_thrusting(pl)) {
1536 	    /*
1537 	     * Target is thrusting,
1538 	     * set number to moves to correct error value
1539 	     */
1540 	    if (range < HEAT_CLOSE_RANGE)
1541 		heat->heat_count = HEAT_CLOSE_ERROR;
1542 	    else if (range < HEAT_MID_RANGE)
1543 		heat->heat_count = HEAT_MID_ERROR;
1544 	    else
1545 		heat->heat_count = HEAT_WIDE_ERROR;
1546 	} else {
1547 	    heat->heat_count += timeStep;
1548 	    /* Look for new target */
1549 	    if ((range < HEAT_CLOSE_RANGE
1550 		 && heat->heat_count > HEAT_CLOSE_TIMEOUT + HEAT_CLOSE_ERROR)
1551 		|| (range < HEAT_MID_RANGE
1552 		    && heat->heat_count > HEAT_MID_TIMEOUT + HEAT_MID_ERROR)
1553 		|| heat->heat_count > HEAT_WIDE_TIMEOUT + HEAT_WIDE_ERROR) {
1554 		double l;
1555 		int i;
1556 
1557 		range = HEAT_RANGE * (heat->heat_count / HEAT_CLOSE_TIMEOUT);
1558 		for (i = 0; i < NumPlayers; i++) {
1559 		    player_t *pl_i = Player_by_index(i);
1560 		    clpos_t engine;
1561 
1562 		    if (!Player_is_thrusting(pl_i))
1563 			continue;
1564 
1565 		    engine = Ship_get_engine_clpos(pl_i->ship, pl_i->dir);
1566 		    l = Wrap_length(pl_i->pos.cx + engine.cx - heat->pos.cx,
1567 				    pl_i->pos.cy + engine.cy - heat->pos.cy)
1568 			/ CLICK;
1569 		    /*
1570 		     * After burners can be detected easier;
1571 		     * so scale the length:
1572 		     */
1573 		    l *= MAX_AFTERBURNER + 1 - pl_i->item[ITEM_AFTERBURNER];
1574 		    l /= MAX_AFTERBURNER + 1;
1575 		    if (Player_has_afterburner(pl_i))
1576 			l *= 16 - pl_i->item[ITEM_AFTERBURNER];
1577 		    if (l < range) {
1578 			heat->heat_lock_id = pl_i->id;
1579 			range = l;
1580 			heat->heat_count =
1581 			    l < HEAT_CLOSE_RANGE ?
1582 				HEAT_CLOSE_ERROR : l < HEAT_MID_RANGE ?
1583 				    HEAT_MID_ERROR : HEAT_WIDE_ERROR;
1584 			pl = pl_i;
1585 		    }
1586 		}
1587 	    }
1588 	}
1589 	if (heat->heat_lock_id < 0)
1590 	    return;
1591 	/*
1592 	 * Heat seekers cannot fly exactly, if target is far away or thrust
1593 	 * isn't active.  So simulate the error:
1594 	 */
1595 	x_dif = rfrac() * 4 * heat->heat_count;
1596 	y_dif = rfrac() * 4 * heat->heat_count;
1597 
1598     }
1599     else if (missile->type == OBJ_SMART_SHOT) {
1600 	smartobject_t *smart = SMART_PTR(missile);
1601 
1602 	/*
1603 	 * kps - this can cause Arithmetic Exception (division by zero)
1604 	 * since CONFUSED_UPDATE_GRANULARITY / options.gameSpeed is most often
1605 	 * < 1 and when it is cast to int it will be 0, and then
1606 	 * we get frameloops % 0, which is not good.
1607 	 */
1608 	/*if (BIT(smart->obj_status, CONFUSED)
1609 	  && (!(frame_loops
1610 	  % (int)(CONFUSED_UPDATE_GRANULARITY / options.gameSpeed)
1611 	  || smart->smart_count == CONFUSED_TIME))) {*/
1612 	/* not going to fix now, I'll just remove the '/ gamespeed' part */
1613 
1614 	if (BIT(smart->obj_status, CONFUSED)
1615 	    && (!(frame_loops % CONFUSED_UPDATE_GRANULARITY)
1616 		|| smart->smart_count == CONFUSED_TIME)) {
1617 
1618 	    if (smart->smart_count > 0) {
1619 		smart->smart_lock_id
1620 		    = Player_by_index((int)(rfrac() * NumPlayers))->id;
1621 		smart->smart_count -= timeStep;
1622 	    } else {
1623 		smart->smart_count = 0;
1624 		CLR_BIT(smart->obj_status, CONFUSED);
1625 
1626 		/* range is percentage from center to periphery of ecm burst */
1627 		range = (ECM_DISTANCE - smart->smart_ecm_range) / ECM_DISTANCE;
1628 		range *= 100.0;
1629 
1630 		/*
1631 		 * range%	lock%
1632 		 * 100		100
1633 		 *  50		75
1634 		 *   0		50
1635 		 */
1636 		if ((int)(rfrac() * 100) <= ((int)(range/2)+50))
1637 		    smart->smart_lock_id = smart->smart_relock_id;
1638 	    }
1639 	}
1640 	pl = Player_by_id(smart->smart_lock_id);
1641     }
1642     else
1643 	/*NOTREACHED*/
1644 	return;
1645 
1646     /* kps - Player_by_id might return NULL. */
1647     if (!pl)
1648 	return;
1649 
1650     /*
1651      * Use a little look ahead to fly more exact
1652      */
1653     acc *= (1 + (Mods_get(missile->mods, ModsPower)
1654 		 * MISSILE_POWER_SPEED_FACT));
1655     if ((shot_speed = VECTOR_LENGTH(missile->vel)) < 1)
1656 	shot_speed = 1;
1657     range = Wrap_length(pl->pos.cx - missile->pos.cx,
1658 			pl->pos.cy - missile->pos.cy) / CLICK;
1659     x_dif += pl->vel.x * (range / shot_speed);
1660     y_dif += pl->vel.y * (range / shot_speed);
1661     a = Wrap_cfindDir(pl->pos.cx + PIXEL_TO_CLICK(x_dif) - missile->pos.cx,
1662 		      pl->pos.cy + PIXEL_TO_CLICK(y_dif) - missile->pos.cy);
1663     theta = MOD2((int) (a + 0.5), RES);
1664 
1665     {
1666 	double x, y, vx, vy;
1667 	int i, xi, yi, j, freemax, k, foundw;
1668 	static struct {
1669 	    int dx, dy;
1670 	} sur[8] = {
1671 	    {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0}, {-1,-1}, {0,-1}, {1,-1}
1672 	};
1673 	blkpos_t sbpos;
1674 
1675 #define BLOCK_PARTS 2
1676 	vx = missile->vel.x;
1677 	vy = missile->vel.y;
1678 	x = shot_speed / (BLOCK_SZ*BLOCK_PARTS);
1679 	vx /= x; vy /= x;
1680 	x = CLICK_TO_PIXEL(missile->pos.cx);
1681 	y = CLICK_TO_PIXEL(missile->pos.cy);
1682 	foundw = 0;
1683 
1684 	for (i = SMART_SHOT_LOOK_AH; i > 0 && foundw == 0; i--) {
1685 	    xi = (int)((x += vx) / BLOCK_SZ);
1686 	    yi = (int)((y += vy) / BLOCK_SZ);
1687 	    if (BIT(world->rules->mode, WRAP_PLAY)) {
1688 		if (xi < 0) xi += world->x;
1689 		else if (xi >= world->x) xi -= world->x;
1690 		if (yi < 0) yi += world->y;
1691 		else if (yi >= world->y) yi -= world->y;
1692 	    }
1693 	    if (xi < 0 || xi >= world->x || yi < 0 || yi >= world->y)
1694 		break;
1695 
1696 	    /*
1697 	     * kps -
1698 	     * Someone please write polygon based missile navigation code.
1699 	     */
1700 
1701 	    switch(world->block[xi][yi]) {
1702 	    case TARGET:
1703 	    case TREASURE:
1704 	    case FUEL:
1705 	    case FILLED:
1706 	    case REC_LU:
1707 	    case REC_RU:
1708 	    case REC_LD:
1709 	    case REC_RD:
1710 	    case CANNON:
1711 		if (range > (SMART_SHOT_LOOK_AH-i)*(BLOCK_SZ/BLOCK_PARTS)) {
1712 		    if (shot_speed > SMART_SHOT_MIN_SPEED)
1713 			shot_speed -= acc * (SMART_SHOT_DECFACT+1);
1714 		}
1715 		foundw = 1;
1716 		break;
1717 	    default:
1718 		break;
1719 	    }
1720 	}
1721 
1722 	i = ((int)(missile->missile_dir * 8 / RES)&7) + 8;
1723 	sbpos = Clpos_to_blkpos(missile->pos);
1724 	xi = sbpos.bx;
1725 	yi = sbpos.by;
1726 
1727 	for (j = 2, angle = -1, freemax = 0; j >= -2; --j) {
1728 	    int si, xt, yt;
1729 
1730 	    for (si=1, k=0; si >= -1; --si) {
1731 		xt = xi + sur[(i+j+si)&7].dx;
1732 		yt = yi + sur[(i+j+si)&7].dy;
1733 
1734 		if (xt >= 0 && xt < world->x && yt >= 0 && yt < world->y) {
1735 
1736 		    switch (world->block[xt][yt]) {
1737 		    case TARGET:
1738 		    case TREASURE:
1739 		    case FUEL:
1740 		    case FILLED:
1741 		    case REC_LU:
1742 		    case REC_RU:
1743 		    case REC_LD:
1744 		    case REC_RD:
1745 		    case CANNON:
1746 			if (!si)
1747 			    k = -32;
1748 			break;
1749 		    default:
1750 			++k;
1751 			break;
1752 		    }
1753 		}
1754 
1755 	    }
1756 	    if (k > freemax
1757 		|| (k == freemax
1758 		    && ((j == -1 && (rfrac() < 0.5)) || j == 0 || j == 1))) {
1759 		freemax = k > 2 ? 2 : k;
1760 		angle = i + j;
1761 	    }
1762 
1763 	    if (k == 3 && !j) {
1764 		angle = -1;
1765 		break;
1766 	    }
1767 	}
1768 
1769 	if (angle >= 0) {
1770 	    i = angle&7;
1771 	    a = Wrap_findDir(
1772 		(yi + sur[i].dy) * BLOCK_SZ - (CLICK_TO_PIXEL(missile->pos.cy)
1773 					       + 2 * missile->vel.y),
1774 		(xi + sur[i].dx) * BLOCK_SZ - (CLICK_TO_PIXEL(missile->pos.cx)
1775 					       - 2 * missile->vel.x));
1776 	    theta = MOD2((int) (a + 0.5), RES);
1777 #ifdef SHOT_EXTRA_SLOWDOWN
1778 	    if (!foundw && range > (SHOT_LOOK_AH-i) * BLOCK_SZ) {
1779 		if (shot_speed
1780 		    > (SMART_SHOT_MIN_SPEED + SMART_SHOT_MAX_SPEED)/2)
1781 		    shot_speed -= SMART_SHOT_DECC+SMART_SHOT_ACC;
1782 	    }
1783 #endif
1784 	}
1785     }
1786     angle = theta;
1787 
1788     if (angle < 0)
1789 	angle += RES;
1790     angle %= RES;
1791 
1792     if (angle < missile->missile_dir)
1793 	angle += RES;
1794     angle = angle - missile->missile_dir - RES/2;
1795 
1796     if (angle < 0)
1797 	missile->missile_dir += (u_byte)(((-angle < missile->missile_turnspeed)
1798 					? -angle
1799 					: missile->missile_turnspeed));
1800     else
1801 	missile->missile_dir -= (u_byte)(((angle < missile->missile_turnspeed)
1802 					? angle
1803 					: missile->missile_turnspeed));
1804 
1805     missile->missile_dir = MOD2(missile->missile_dir, RES); /* NOTE!!!! */
1806 
1807     if (shot_speed < missile->missile_max_speed)
1808 	shot_speed += acc;
1809 
1810     /*  missile->velocity = MIN(missile->velocity, missile->max_speed);  */
1811 
1812     missile->vel.x = tcos(missile->missile_dir) * shot_speed;
1813     missile->vel.y = tsin(missile->missile_dir) * shot_speed;
1814 }
1815 
Update_mine(mineobject_t * mine)1816 void Update_mine(mineobject_t *mine)
1817 {
1818     UNUSED_PARAM(world);
1819 
1820     if (BIT(mine->obj_status, CONFUSED)) {
1821 	if ((mine->mine_count -= timeStep) <= 0) {
1822 	    CLR_BIT(mine->obj_status, CONFUSED);
1823 	    mine->mine_count = 0;
1824 	}
1825     }
1826 
1827     /*
1828      * If the value of option mineFuseTicks is 0, owner immunity never expires.
1829      * In this case, mine->fuse has the special value -1.
1830      */
1831     if (BIT(mine->obj_status, OWNERIMMUNE) && mine->fuse == 0)
1832 	CLR_BIT(mine->obj_status, OWNERIMMUNE);
1833 
1834     if (Mods_get(mine->mods, ModsMini)) {
1835 	if ((mine->mine_spread_left -= timeStep) <= 0) {
1836 	    mine->acc.x = 0;
1837 	    mine->acc.y = 0;
1838 	    mine->mine_spread_left = 0;
1839 	}
1840     }
1841 }
1842