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