1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 2000-2004 Uoti Urpala <uau@users.sourceforge.net>
5  *
6  * Copyright (C) 1991-2001 by
7  *
8  *      Bj�rn Stabell        <bjoern@xpilot.org>
9  *      Ken Ronny Schouten   <ken@xpilot.org>
10  *      Bert Gijsbers        <bert@xpilot.org>
11  *      Dick Balaska         <dick@xpilot.org>
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 struct move_parameters mp;
31 static char msg[MSG_LEN];
32 
33 /* polygon map related stuff */
34 
35 /* start */
36 
37 /* Maximum line length 32767-B_CLICKS, 30000 used in checks
38  * There's a minimum map size to avoid "too much wrapping". A bit smaller
39  * than that would cause rare errors for fast-moving things. I haven't
40  * bothered to figure out what the limit is. 80k x 80k clicks should
41  * be more than enough (probably...). */
42 #define B_SHIFT 11
43 #define B_CLICKS (1 << B_SHIFT)
44 #define B_MASK (B_CLICKS - 1)
45 #define CUTOFF (2 * BLOCK_CLICKS) /* Not sure about the optimum value */
46 #define MAX_MOVE 32000
47 #define SEPARATION_DIST 64
48 /* This must be increased if the ship corners are allowed to go farther
49  * when turning! */
50 /* this is big enough for asteroids of size 4 */
51 #define MAX_SHAPE_OFFSET (52 * CLICK)
52 
53 #if ((-3) / 2 != -1) || ((-3) % 2 != -1)
54 #error "This code assumes that negative numbers round upwards."
55 #endif
56 
57 struct collans {
58     int line;
59     int point;
60     clvec_t moved;
61 };
62 
63 struct tl2 {
64     int base;
65     int x;
66     int y;
67 };
68 
69 struct bline {
70     clvec_t start;
71     clvec_t delta;
72     double c;
73     double s;
74     short group;
75 };
76 
77 struct blockinfo {
78     unsigned short distance;
79     unsigned short *lines;
80     unsigned short *points;
81 };
82 
83 struct inside_block {
84     short *y;
85     short *lines;
86     struct inside_block *next;
87     short group;
88     char base_value;
89 };
90 
91 struct inside_block *inside_table;
92 
93 struct test {
94     double distance;
95     int inside;
96     struct tempy *y;
97     struct templine *lines;
98 };
99 
100 struct test *temparray;
101 
102 shape_t ball_wire;
103 
104 #define LINEY(X, Y, BASE, ARG)  (((Y)*(ARG)+(BASE))/(X))
105 #define SIDE(X, Y, LINE) (linet[(LINE)].delta.cy * (X) - linet[(LINE)].delta.cx * (Y))
106 #define SIGN(X) ((X) >= 0 ? 1 : -1)
107 
108 struct bline *linet;
109 #define S_LINES 100 /* stupid hack */
110 
111 struct group *groups = NULL;
112 int num_groups = 0, max_groups = 0;
113 
114 struct blockinfo *blockline;
115 unsigned short *llist;
116 unsigned short *plist;
117 int num_lines = 0;
118 int num_polys = 0;
119 int mapx, mapy;
120 
can_hit(group_t * gp,const move_t * move)121 static inline bool can_hit(group_t *gp, const move_t *move)
122 {
123     if (gp->hitmask & move->hitmask)
124 	return false;
125     if (gp->hitfunc == NULL)
126 	return true;
127     return gp->hitfunc(gp, move);
128 }
129 
Move_init(void)130 void Move_init(void)
131 {
132     LIMIT(options.maxObjectWallBounceSpeed, 0, world->hypotenuse);
133     LIMIT(options.maxShieldedWallBounceSpeed, 0, world->hypotenuse);
134     LIMIT(options.maxUnshieldedWallBounceSpeed, 0, world->hypotenuse);
135 
136     LIMIT(options.playerWallBounceBrakeFactor, 0, 1);
137     LIMIT(options.playerWallFriction, 0, FLT_MAX);
138     LIMIT(options.objectWallBounceBrakeFactor, 0, 1);
139     LIMIT(options.objectWallBounceLifeFactor, 0, 1);
140 
141     mp.obj_bounce_mask = 0;
142     if (options.sparksWallBounce)
143 	SET_BIT(mp.obj_bounce_mask, OBJ_SPARK_BIT);
144     if (options.debrisWallBounce)
145 	SET_BIT(mp.obj_bounce_mask, OBJ_DEBRIS_BIT);
146     if (options.shotsWallBounce)
147 	SET_BIT(mp.obj_bounce_mask, OBJ_SHOT_BIT|OBJ_CANNON_SHOT_BIT);
148     if (options.itemsWallBounce)
149 	SET_BIT(mp.obj_bounce_mask, OBJ_ITEM_BIT);
150     if (options.missilesWallBounce)
151 	SET_BIT(mp.obj_bounce_mask,
152 		OBJ_SMART_SHOT_BIT|OBJ_TORPEDO_BIT|OBJ_HEAT_SHOT_BIT);
153     if (options.minesWallBounce)
154 	SET_BIT(mp.obj_bounce_mask, OBJ_MINE_BIT);
155     if (options.ballsWallBounce)
156 	SET_BIT(mp.obj_bounce_mask, OBJ_BALL_BIT);
157     if (options.asteroidsWallBounce)
158 	SET_BIT(mp.obj_bounce_mask, OBJ_ASTEROID_BIT);
159     if (options.pulsesWallBounce)
160 	SET_BIT(mp.obj_bounce_mask, OBJ_PULSE_BIT);
161 
162     mp.obj_cannon_mask = (KILLING_SHOTS) | OBJ_MINE_BIT | OBJ_SHOT_BIT
163 	| OBJ_PULSE_BIT | OBJ_SMART_SHOT_BIT | OBJ_TORPEDO_BIT
164 	| OBJ_HEAT_SHOT_BIT | OBJ_ASTEROID_BIT;
165     if (options.cannonsPickupItems)
166 	mp.obj_cannon_mask |= OBJ_ITEM_BIT;
167     mp.obj_target_mask = mp.obj_cannon_mask | OBJ_BALL_BIT | OBJ_SPARK_BIT;
168     mp.obj_treasure_mask = mp.obj_bounce_mask | OBJ_BALL_BIT | OBJ_PULSE_BIT;
169 }
170 
171 
Object_crash(object_t * obj,int crashtype,int mapobj_ind)172 void Object_crash(object_t *obj, int crashtype, int mapobj_ind)
173 {
174     switch (crashtype) {
175 
176     default:
177 	break;
178 
179     case CrashWormHole:
180 	Object_hits_wormhole(obj, mapobj_ind);
181 	break;
182 
183     case CrashTreasure:
184 	/*
185 	 * Ball type has already been handled.
186 	 */
187 	if (obj->type == OBJ_BALL)
188 	    break;
189 	obj->life = 0;
190 	break;
191 
192     case CrashTarget:
193 	obj->life = 0;
194 	Object_hits_target(obj, Target_by_index(mapobj_ind), -1.0);
195 	break;
196 
197     case CrashWall:
198 	obj->life = 0;
199 	/* add sparks ??? */
200 	break;
201 
202     case CrashUniverse:
203 	obj->life = 0;
204 	break;
205 
206     case CrashCannon:
207 	obj->life = 0;
208 	Object_hits_cannon(obj, Cannon_by_index(mapobj_ind));
209 	break;
210 
211     case CrashUnknown:
212 	obj->life = 0;
213 	break;
214     }
215 }
216 
217 
Player_crash(player_t * pl,int crashtype,int mapobj_ind,int pt)218 void Player_crash(player_t *pl, int crashtype, int mapobj_ind, int pt)
219 {
220     const char *howfmt = NULL;
221     const char *hudmsg = NULL;
222 
223     msg[0] = '\0';
224 
225     switch (crashtype) {
226 
227     default:
228     case NotACrash:
229 	warn("Unrecognized crash %d", crashtype);
230 	break;
231 
232     case CrashWormHole:
233 	Object_hits_wormhole(OBJ_PTR(pl), mapobj_ind);
234 	break;
235 
236     case CrashWall:
237 	howfmt = "%s crashed%s against a wall";
238 	hudmsg = "[Wall]";
239 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
240 	break;
241 
242     case CrashWallSpeed:
243 	howfmt = "%s smashed%s against a wall";
244 	hudmsg = "[Wall]";
245 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
246 	break;
247 
248     case CrashWallNoFuel:
249 	howfmt = "%s smacked%s against a wall";
250 	hudmsg = "[Wall]";
251 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
252 	break;
253 
254     case CrashWallAngle:
255 	howfmt = "%s was trashed%s against a wall";
256 	hudmsg = "[Wall]";
257 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
258 	break;
259 
260     case CrashTarget:
261 	howfmt = "%s smashed%s against a target";
262 	hudmsg = "[Target]";
263 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
264 	Object_hits_target(OBJ_PTR(pl), Target_by_index(mapobj_ind), -1.0);
265 	break;
266 
267     case CrashTreasure:
268 	howfmt = "%s smashed%s against a treasure";
269 	hudmsg = "[Treasure]";
270 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
271 	break;
272 
273     case CrashCannon:
274         {
275 	    cannon_t *cannon = Cannon_by_index(mapobj_ind);
276 
277 	    if (!Player_uses_emergency_shield(pl)) {
278 		howfmt = "%s smashed%s against a cannon";
279 		hudmsg = "[Cannon]";
280 		sound_play_sensors(pl->pos, PLAYER_HIT_CANNON_SOUND);
281 	    }
282 	    if (!BIT(cannon->used, HAS_EMERGENCY_SHIELD)) {
283 		/* pl gets points if the cannon is rammed with shields up */
284 		if (Player_uses_emergency_shield(pl))
285 		    Cannon_dies(cannon, pl);
286 		else
287 		    Cannon_dies(cannon, NULL);
288 	    }
289 	}
290 	break;
291 
292     case CrashUniverse:
293 	howfmt = "%s left the known universe%s";
294 	hudmsg = "[Universe]";
295 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
296 	break;
297 
298     case CrashUnknown:
299 	howfmt = "%s slammed%s into a programming error";
300 	hudmsg = "[Bug]";
301 	sound_play_sensors(pl->pos, PLAYER_HIT_WALL_SOUND);
302 	break;
303     }
304 
305     if (howfmt && hudmsg) {
306 	player_t	*pushers[MAX_RECORDED_SHOVES];
307 	int		cnt[MAX_RECORDED_SHOVES];
308 	int		num_pushers = 0;
309 	int		total_pusher_count = 0;
310 	double		total_pusher_score = 0;
311 	int		i, j;
312 
313 	Player_set_state(pl, PL_STATE_KILLED);
314 	sprintf(msg, howfmt, pl->name, (!pt) ? " head first" : "");
315 
316 	/* get a list of who pushed me */
317 	for (i = 0; i < MAX_RECORDED_SHOVES; i++) {
318 	    shove_t *shove = &pl->shove_record[i];
319 
320 	    if (shove->pusher_id == NO_ID)
321 		continue;
322 
323 	    if (shove->time < frame_loops - (int)((20 / timeStep) + 0.5))
324 		continue;
325 
326 	    for (j = 0; j < num_pushers; j++) {
327 		if (shove->pusher_id == pushers[j]->id) {
328 		    cnt[j]++;
329 		    break;
330 		}
331 	    }
332 	    if (j == num_pushers) {
333 		pushers[num_pushers++] = Player_by_id(shove->pusher_id);
334 		cnt[j] = 1;
335 	    }
336 	    total_pusher_count++;
337 	    total_pusher_score += Get_Score(pushers[j]);
338 	}
339 	if (num_pushers == 0) {
340 	    Handle_Scoring(SCORE_WALL_DEATH,NULL,pl,NULL,hudmsg);
341 	    strcat(msg, ".");
342 	    Set_message(msg);
343 	} else {
344 	    int		msg_len = strlen(msg);
345 	    char	*msg_ptr = &msg[msg_len];
346 	    double	average_pusher_score = total_pusher_score / total_pusher_count;
347  	    bool	was_tagged = (tagItPlayerId == pl->id),
348 			pusher_is_tagged = false;
349 	    double mult;
350 	    player_t dummy;
351 
352 	    for (i = 0; i < num_pushers; i++) {
353 		player_t	*pusher = pushers[i];
354 		const char	*sep = (!i) ? " with help from "
355 					    : (i < num_pushers - 1) ? ", "
356 					    : " and ";
357 		size_t		sep_len = strlen(sep);
358 		size_t		name_len = strlen(pusher->name);
359 
360 		if (msg_len + sep_len + name_len + 2 < sizeof msg) {
361 		    strcpy(msg_ptr, sep);
362 		    msg_len += sep_len;
363 		    msg_ptr += sep_len;
364 		    strcpy(msg_ptr, pusher->name);
365 		    msg_len += name_len;
366 		    msg_ptr += name_len;
367 		}
368 		mult = cnt[i] / total_pusher_count;
369 		if (options.tagGame) {
370 		    if (tagItPlayerId == pusher->id) {
371 			mult *= options.tagItKillScoreMult;
372  			pusher_is_tagged = true;
373  		    } else if (was_tagged && num_pushers == 1) {
374  			mult *= options.tagKillItScoreMult;
375  			Transfer_tag(pl, pusher);
376  		    }
377  		}
378 
379 		Handle_Scoring(SCORE_SHOVE_KILL,pusher,pl,&mult,NULL);
380 		if (i >= num_pushers - 1)
381 		    Rank_add_shove_kill(pusher);
382 	    }
383 
384 	    if (options.tagGame && num_pushers == 1) {
385  		if (was_tagged)
386  		    mult = options.tagKillItScoreMult;
387 		else if (pusher_is_tagged)
388  		    mult = options.tagItKillScoreMult;
389  	    }
390     	    dummy.score = average_pusher_score;
391 	    Handle_Scoring(SCORE_SHOVE_DEATH,&dummy,pl,&mult,NULL);
392 
393 	    strcpy(msg_ptr, ".");
394 	    Set_message(msg);
395 
396 	    /* Robots will declare war on anyone who shoves them. */
397 	    i = (int)(rfrac() * num_pushers);
398 	    Robot_war(pl, pushers[i]);
399 	}
400     }
401 
402     if (Player_is_killed(pl)
403 	&&  Get_Score(pl) < 0
404 	&& Player_is_robot(pl)) {
405 	pl->home_base = Base_by_index(0);
406 	Pick_startpos(pl);
407     }
408 }
409 
410 
411 
412 
ralloc(void * ptr,size_t size)413 static void *ralloc(void *ptr, size_t size)
414 {
415     if (!(ptr = realloc(ptr, size))) {
416 	warn("Realloc failed.");
417 	exit(1);
418     }
419     return ptr;
420 }
421 
Shape_lines(shape_t * s,int dir)422 static unsigned short *Shape_lines(shape_t *s, int dir)
423 {
424     int i;
425     static unsigned short foo[100];
426     static shape_t *lastshape;
427     static int lastdir;
428     const int os = num_lines;
429     clpos_t *pts;
430 
431     /* linet[i].group MUST BE INITIALIZED TO 0 */
432 
433     if (s == lastshape && dir == lastdir)
434 	return foo;
435 
436     lastshape = s;
437     lastdir = dir;
438 
439     pts = Shape_get_points((shape_t *)s, dir);
440     for (i = 0; i < s->num_points; i++) {
441 	clpos_t pt = pts[i];
442 
443 	linet[i + os].start.cx = -pt.cx;
444 	linet[i + os].start.cy = -pt.cy;
445     }
446     for (i = 0; i < s->num_points - 1; i++) {
447 	linet[i + os].delta.cx
448 	    = linet[i + os + 1].start.cx - linet[i + os].start.cx;
449 	linet[i + os].delta.cy
450 	    = linet[i + os + 1].start.cy - linet[i + os].start.cy;
451     }
452     linet[i + os].delta.cx = linet[os].start.cx - linet[i + os].start.cx;
453     linet[i + os].delta.cy = linet[os].start.cy - linet[i + os].start.cy;
454     for (i = 0; i < s->num_points; i++)
455 	foo[i] = i + os;
456     foo[i] = 65535;
457     return foo;
458 }
459 
460 
Bounce_object(object_t * obj,move_t * move,int line,int point)461 static int Bounce_object(object_t *obj, move_t *move, int line, int point)
462 {
463     double fx, fy;
464     double c, s, wall_brake_factor = options.objectWallBounceBrakeFactor;
465     int group, type;
466     int mapobj_ind;
467 
468     group = linet[line >= num_lines ? point : line].group;
469     type = groups[group].type;
470     mapobj_ind = groups[group].mapobj_ind;
471 
472     if (obj->collmode == 1) {
473 	fx = ABS(obj->extmove.cx) + ABS(obj->extmove.cy);
474 	/* If fx <= 64, there is practically no movement. Object
475 	   collision detection can ignore the bounce. */
476 	if (fx > 64) {
477 	    obj->wall_time = 1 -
478 		(ABS(move->delta.cx) + ABS(move->delta.cy)) / fx;
479 	    obj->collmode = 2;
480 	}
481     }
482 
483     if (type == TREASURE) {
484 	if (obj->type == OBJ_BALL)
485 	    Ball_hits_goal(BALL_PTR(obj), groupptr_by_id(group));
486 	obj->life = 0;
487 	return 0;
488     }
489 
490     if (type == TARGET) {
491 	obj->life = 0;
492 	Object_hits_target(obj, Target_by_index(mapobj_ind), -1.0);
493 	return 0;
494     }
495 
496     if (type == CANNON) {
497 	Object_crash(obj, CrashCannon, mapobj_ind);
498 	return 0;
499     }
500 
501     if (type == WORMHOLE) {
502 	Object_crash(obj, CrashWormHole, mapobj_ind);
503 	return 0;
504     }
505 
506     if (!BIT(mp.obj_bounce_mask, OBJ_TYPEBIT(obj->type))) {
507 	obj->life = 0;
508 	return 0;
509     }
510 
511     if (obj->type != OBJ_BALL
512 	&& obj->type != OBJ_PULSE) {
513 	obj->life *= options.objectWallBounceLifeFactor;
514 	if (obj->life <= 0)
515 	    return 0;
516     }
517 
518     /*
519      * Any bouncing sparks are no longer owner immune to give
520      * "reactive" thrust.  This is exactly like ground effect
521      * in the real world.  Very useful for stopping against walls.
522      */
523     if (obj->type != OBJ_PULSE &&
524 	obj->type != OBJ_SPARK &&
525 	sqr(obj->vel.x) + sqr(obj->vel.y)
526 	> sqr(options.maxObjectWallBounceSpeed)) {
527 	obj->life = 0;
528 	return 0;
529     }
530 
531     if (obj->type == OBJ_SPARK &&
532 	sqr(obj->vel.x) + sqr(obj->vel.y)
533 	> sqr(options.maxSparkWallBounceSpeed)) {
534 	obj->life = 0;
535 	return 0;
536     }
537 
538     if (obj->type == OBJ_SPARK)
539 	CLR_BIT(obj->obj_status, OWNERIMMUNE);
540 
541     if (line >= num_lines) {
542 	double x, y, l2;
543 
544 	x = linet[line].delta.cx;
545 	y = linet[line].delta.cy;
546 	l2 = (x*x + y*y);
547 	c = (x*x - y*y) / l2;
548 	s = 2*x*y / l2;
549     }
550     else {
551 	c = linet[line].c;
552 	s = linet[line].s;
553     }
554 
555     if (obj->type == OBJ_PULSE)
556 	wall_brake_factor = 1.0;
557     fx = move->delta.cx * c + move->delta.cy * s;
558     fy = move->delta.cx * s - move->delta.cy * c;
559     move->delta.cx = (click_t)(fx * wall_brake_factor);
560     move->delta.cy = (click_t)(fy * wall_brake_factor);
561     fx = obj->vel.x * c + obj->vel.y * s;
562     fy = obj->vel.x * s - obj->vel.y * c;
563     obj->vel.x = fx * wall_brake_factor;
564     obj->vel.y = fy * wall_brake_factor;
565     if (obj->collmode == 2)
566 	obj->collmode = 3;
567 
568     /* find direction of pulse after bounce */
569     if (obj->type == OBJ_PULSE) {
570 	pulseobject_t *pulse = PULSE_PTR(obj);
571 
572 	pulse->pulse_dir = (int)Wrap_findDir(pulse->vel.x, pulse->vel.y);
573 	pulse->pulse_len = 0;
574 	pulse->pulse_refl = true;
575     }
576 
577     return 1;
578 }
579 
580 
581 
Bounce_player(player_t * pl,move_t * move,int line,int point)582 static void Bounce_player(player_t *pl, move_t *move, int line, int point)
583 {
584     double c, s;		/* cosine and sine of 2 times line angle */
585     double cl, sl;		/* cosine and sine of line angle */
586     double x, y, l2, l;
587     int group, type, mapobj_ind;
588     bool constant_speed_subtracted
589     	    = (pl->last_wall_touch == frame_loops ? true : false);
590 
591     x = linet[line].delta.cx;
592     y = linet[line].delta.cy;
593     l2 = (x*x + y*y);
594     c = (x*x - y*y) / l2;
595     s = 2*x*y / l2;
596     l = sqrt(l2);
597     cl = x / l;
598     sl = y / l;
599 
600     group = linet[line >= num_lines ? point : line].group;
601     type = groups[group].type;
602     mapobj_ind = groups[group].mapobj_ind;
603     if (type == TREASURE && options.treasureCollisionKills) {
604 	Player_crash(pl, CrashTreasure, NO_IND, 1);
605 	return;
606     }
607 
608     if (type == WORMHOLE) {
609 	Player_crash(pl, CrashWormHole, mapobj_ind, 1);
610 	return;
611     }
612 
613     if (type == CANNON) {
614 	Player_crash(pl, CrashCannon, mapobj_ind, 1);
615 	if (Player_is_killed(pl))
616 	    return;
617 	/* The player may bounce from the cannon if both have shields up. */
618     }
619 
620     pl->last_wall_touch = frame_loops;
621     {
622 	double	speed = VECTOR_LENGTH(pl->vel);
623 	double	v = speed * 0.25;
624 	double	m = pl->mass - pl->emptymass * 0.75;
625 	double	b = 1.0 - 0.5 * options.playerWallBounceBrakeFactor;
626 	double	cost = b * m * v;
627 	double	max_speed = BIT(pl->used, HAS_SHIELD)
628 		? options.maxShieldedWallBounceSpeed
629 		: options.maxUnshieldedWallBounceSpeed;
630 
631 	if (Player_uses_emergency_shield(pl)
632 	    && max_speed < 100.0)
633 	    max_speed = 100.0;
634 
635 	/* only use armor if neccessary */
636 	if (speed > max_speed
637 	    && max_speed < options.maxShieldedWallBounceSpeed
638 	    && !BIT(pl->used, HAS_SHIELD)
639 	    && Player_has_armor(pl)) {
640 	    max_speed = options.maxShieldedWallBounceSpeed;
641 	    Player_hit_armor(pl);
642 	}
643 
644 	if (speed > max_speed) {
645 	    if (type == TARGET)
646 		Player_crash(pl, CrashTarget, mapobj_ind, 1);
647 	    else
648 		Player_crash(pl, CrashWallSpeed, NO_IND, 1);
649 	    return;
650 	}
651 
652 	if (!Player_uses_emergency_shield(pl))
653 	    Item_damage(pl, options.wallBounceDestroyItemProb);
654 
655 	sound_play_sensors(pl->pos, PLAYER_BOUNCED_SOUND);
656 
657 	if (cost && type == TARGET)
658 	    Object_hits_target(OBJ_PTR(pl),
659 			       Target_by_index(mapobj_ind),
660 			       cost / 4.0);
661     }
662 
663     /*
664      * Remove constantSpeed before bounce.
665      * Otherwise constantSpeed will accelerate your ship when you
666      * slide along a wall.
667      */
668     if (options.constantSpeed && !constant_speed_subtracted) {
669 	pl->vel.x -= options.constantSpeed * pl->acc.x;
670 	pl->vel.y -= options.constantSpeed * pl->acc.y;
671     }
672 
673 
674     if (options.playerWallBounceType == 0) {
675 	double fx, fy;
676 
677 	fx = move->delta.cx * c + move->delta.cy * s;
678 	fy = move->delta.cx * s - move->delta.cy * c;
679 	move->delta.cx = (click_t)(fx * options.playerWallBounceBrakeFactor);
680 	move->delta.cy = (click_t)(fy * options.playerWallBounceBrakeFactor);
681 	fx = pl->vel.x * c + pl->vel.y * s;
682 	fy = pl->vel.x * s - pl->vel.y * c;
683 	pl->vel.x = fx * options.playerWallBounceBrakeFactor;
684 	pl->vel.y = fy * options.playerWallBounceBrakeFactor;
685     } else {
686 	/*
687 	 * Determine new velocity vector and move->delta after bounce.
688 	 * The vector move->delta is the remaining amount left to move
689 	 * in this frame.
690 	 */
691 	vector_t vel, vel1, mvd, mvd1;
692 
693 	/*
694 	 * i. Rotate velocity and move->delta clockwise by line angle.
695 	 */
696 	vel.x = pl->vel.x *   cl  + pl->vel.y * sl;
697 	vel.y = pl->vel.x * (-sl) + pl->vel.y * cl;
698 	vel1 = vel;
699 	mvd.x = move->delta.cx *   cl  + move->delta.cy * sl;
700 	mvd.y = move->delta.cx * (-sl) + move->delta.cy * cl;
701 	mvd1 = mvd;
702 
703 	/* ii. Reverse direction of perpendicular component. */
704 	vel.y = -vel.y;
705 	mvd.y = -mvd.y;
706 
707 	/*
708 	 * iii. Determine how much perpendicular and parallel components
709 	 * change.
710 	 */
711 	vel.y *= options.playerWallBounceBrakeFactor;
712 	mvd.y *= options.playerWallBounceBrakeFactor;
713 
714 	if (options.playerWallBounceType == 1) {
715 	    double factor = 1.0 - options.playerWallFriction;
716 	    /*
717 	     * kps: simple "separate multipliers" implementation
718 	     */
719 	    if (factor < 0.0)
720 		factor = 0.0;
721 	    vel.x *= factor;
722 	    mvd.x *= factor;
723 	} else if (options.playerWallBounceType == 2) {
724 	    double vtotal1 = VECTOR_LENGTH(vel1);
725 	    double vnormal1 = ABS(vel1.y);
726 	    double wallfriction = options.playerWallFriction;
727 	    double factor;
728 
729 	    /* Avoid division by 0 */
730 	    if (vtotal1 < 0.001)
731 		factor = 1.0 - wallfriction;
732 	    else
733 		factor = 1.0 - vnormal1 / vtotal1 * wallfriction;
734 	    /*
735 	     * mara:
736 	     * Vtangent2 = (1-Vnormal1/Vtotal1*wallfriction)*Vtangent1;
737 	     */
738 	    if (factor < 0.0)
739 		factor = 0.0;
740 	    vel.x *= factor;
741 	    mvd.x *= factor;
742 	} else if (options.playerWallBounceType == 3) {
743 	    double change;
744 	    double C1 = options.playerWallFriction;
745 	    double C2 = 1.0 - options.playerWallBounceBrakeFactor;
746 	    double perpendicular_change, parallel_speed;
747 	    /*
748 	     * uau:
749 	     * change the parallel one by
750 	     * MIN(C1*perpendicular_change, C2*parallel_speed)
751 	     * if you assume the wall has a coefficient of friction C1
752 	     */
753 	    perpendicular_change = ABS(vel1.y - vel.y);
754 	    parallel_speed = ABS(vel.x);
755 	    change = MIN(C1*perpendicular_change, C2*parallel_speed);
756 	    if (vel.x > 0)
757 		vel.x -= change;
758 	    else
759 		vel.x += change;
760 
761 	    perpendicular_change = ABS(mvd1.y - mvd.y);
762 	    parallel_speed = ABS(mvd.x);
763 	    change = MIN(C1*perpendicular_change, C2*parallel_speed);
764 	    if (mvd.x > 0)
765 		mvd.x -= change;
766 	    else
767 		mvd.x += change;
768 	}
769 	/* iv. Rotate the whole thing anti-clockwise. */
770 	pl->vel.x = vel.x * cl + vel.y * (-sl);
771 	pl->vel.y = vel.x * sl + vel.y *   cl;
772 	move->delta.cx = (click_t)(mvd.x * cl + mvd.y * (-sl));
773 	move->delta.cy = (click_t)(mvd.x * sl + mvd.y *   cl);
774     }
775 }
776 
777 
778 /* Used internally by the movement routines to find the first line
779  * (in the list given by *lines) that the given trajectory hits. */
Lines_check(int msx,int msy,int mdx,int mdy,int * mindone,const unsigned short * lines,int chx,int chy,int chxy,const move_t * move,int * minline,int * height)780 static int Lines_check(int msx, int msy, int mdx, int mdy, int *mindone,
781 		       const unsigned short *lines, int chx, int chy,
782 		       int chxy, const move_t *move, int *minline,
783 		       int *height)
784 {
785     int lsx, lsy, ldx, ldy, temp, mirror, start, end, i, x, sy, ey, prod;
786     int mbase = mdy >> 1, hit = 0;
787 
788     while ( (i = *lines++) != 65535) {
789 	if (linet[i].group
790 	    && (!can_hit(&groups[linet[i].group], move)))
791 	    continue;
792 	lsx = linet[i].start.cx;
793 	lsy = linet[i].start.cy;
794 	ldx = linet[i].delta.cx;
795 	ldy = linet[i].delta.cy;
796 
797 	if (chx) {
798 	    lsx = -lsx;
799 	    ldx = -ldx;
800 	}
801 	if (chy) {
802 	    lsy = -lsy;
803 	    ldy = -ldy;
804 	}
805 	if (chxy) {
806 	    temp = ldx;
807 	    ldx = ldy;
808 	    ldy = temp;
809 	    temp = lsx;
810 	    lsx = lsy;
811 	    lsy = temp;
812 	}
813 	lsx -= msx;
814 	lsy -= msy;
815 	if (chxy) {
816 	    lsx = CENTER_YCLICK(lsx);
817 	    lsy = CENTER_XCLICK(lsy);
818 	}
819 	else {
820 	    lsx = CENTER_XCLICK(lsx);
821 	    lsy = CENTER_YCLICK(lsy);
822 	}
823 	if (*height < lsy + (ldy < 0 ? ldy : 0))
824 	    continue;
825 	if (0 > lsy + (ldy < 0 ? 0 : ldy))
826 	    continue;
827 
828 	mirror = chx ^ chy ^ chxy;
829 	if (ldx < 0) {
830 	    lsx += ldx;
831 	    ldx = -ldx;
832 	    lsy += ldy;
833 	    ldy = -ldy;
834 	    mirror ^= 1;
835 	}
836 
837 	start = MAX(0, lsx);
838 	end = MIN(*mindone + 1, lsx + ldx);
839 	if (start > end)
840 	    continue;
841 
842 	sy = LINEY(mdx, mdy, mbase, start);
843 	prod = (start - lsx) * ldy - (sy - lsy) * ldx;
844 
845 	if (!prod) {
846 	    if (!ldx && (lsy + (ldy < 0 ? ldy : 0) > sy ||
847 			 lsy + (ldy < 0 ? 0 : ldy) < sy))
848 		continue;
849 	    if ( (prod = -lsx * ldy + lsy * ldx) > 0 == mirror || prod == 0)
850 		continue;
851 	    start--;
852 	}
853 	else {
854 	    if (prod > 0 == mirror)
855 		continue;
856 	    ey = LINEY(mdx, mdy, mbase, end);
857 	    if ( ABS(prod) >= ldx
858 		 && ABS( (prod = (end - lsx) * ldy - (ey - lsy) * ldx) )
859 		 >= ldx && prod > 0 != mirror)
860 		continue;
861 	    {
862 		int schs, sche;
863 		double diff = ((double)(-mbase)/mdx-(double)(lsx)*ldy/ldx+lsy);
864 		double diff2 = (double)mdy / mdx - (double)ldy / ldx;
865 
866 		if (ABS(diff2) < 1. / (50000.*50000)) {
867 		    if (diff > 0 || diff < -1)
868 			continue;
869 		    else {
870 			schs = start + 1;
871 			sche = end;
872 		    }
873 		}
874 		/* Can this float->int conversion cause overflows?
875 		 * If so, calculate min/max before conversion. */
876 		else if (diff2 < 0) {
877 		    schs = MAX(start + 1, (int) ((diff + 1) / diff2 + .9));
878 		    sche = MIN(end, (int) (diff / diff2 + 1.1));
879 		}
880 		else {
881 		    schs = MAX(start + 1, (int) (diff / diff2 + .9));
882 		    sche = MIN(end, (int) ((diff + 1) / diff2 + 1.1));
883 		}
884 
885 		for (x = schs; x <= sche; x++)
886 		    if ( (prod = (x - lsx) * ldy
887 			  - (LINEY(mdx, mdy, mbase, x) - lsy) * ldx)
888 			 >= 0 == mirror || prod == 0)
889 			goto found;
890 		continue;
891 	    found:
892 		start = x - 1;
893 	    }
894 	}
895 
896 	/* delta components can be big, so (float) to avoid overflow */
897 	if (start < *mindone
898 	    || (start == *mindone && *minline != -1
899 	      && SIDE((float)move->delta.cx, (float)move->delta.cy, i) < 0)) {
900 	    hit = 1;
901 	    *mindone = start;
902 	    *minline = i;
903 	    *height = LINEY(mdx, mdy, mbase, start);
904 	}
905     }
906     return hit;
907 }
908 
909 
910 /* Try to move a pointlike object along the path determined by *move.
911  * The amount moved is returned in *answer. If the movement hits a line
912  * the number of that line is included. The 'point' parameter in
913  * struct collans is not relevant to this function. Even if the movement
914  * doesn't hit a line, the amount moved can be less than the requested
915  * amount (so that this function needs to be called again for the rest).
916  * If the movement hits several lines at the same click, the line returned
917  * in collans will be such that it is hit "from the outside" if possible.
918  * Example:
919  * X
920  * X inside of polygon
921  * X
922  * *YYYYYYYYYY
923  *  3
924  *   2
925  *    1
926  * Here the lines X and Y meet at click *, and the movement marked with
927  * numbers hits the common point. The function will return line Y as the
928  * one that was hit, because it was hit "from the outside" as opposed to
929  * line X which was hit from the side facing the inside of the polygon.
930  * See  the comments for function 'Away' to see how the trajectory can
931  * hit a line even though it is moving away from the line. */
932 /* Do not call this with no movement. */
933 /* May not be called with point already on top of line.
934  * Maybe I should change that to allow lines that could be crossed. */
Move_point(const move_t * move,struct collans * answer)935 extern void Move_point(const move_t *move, struct collans *answer)
936 {
937     int minline, mindone, minheight;
938     int block;
939     int msx = move->start.cx, msy = move->start.cy;
940     int mdx = move->delta.cx, mdy = move->delta.cy;
941     int mbase;
942     int chxy = 0, chx = 0, chy = 0;
943     int x, temp;
944     unsigned short *lines;
945 
946     block = (move->start.cx >> B_SHIFT) + mapx * (move->start.cy >> B_SHIFT);
947     x = blockline[block].distance;
948     lines = blockline[block].lines;
949 
950     if (mdx < 0) {
951 	mdx = -mdx;
952 	msx = -msx;
953 	chx = 1;
954     }
955     if (mdy < 0) {
956 	mdy = -mdy;
957 	msy = -msy;
958 	chy = 1;
959     }
960     if (mdx < mdy) {
961 	temp = mdx;
962 	mdx = mdy;
963 	mdy = temp;
964 	temp = msx;
965 	msx = msy;
966 	msy = temp;
967 	chxy = 1;
968     }
969 
970     /* 46341*46341 overflows signed 32-bit int */
971     if (mdx > 45000) {
972 	/* might overflow without float */
973 	mdy = (int)((float)mdy * 45000 / mdx);
974 	mdx = 45000;
975     }
976 
977     mindone = mdx;
978     minheight = mdy;
979     mdx++;
980     mdy++;
981     mbase = mdy >> 1;
982 
983     if (mindone > x) {
984 	if (x < MAX_MOVE) {
985 	    /* !@# change this so that the point always moves away from
986 	       the current block */
987 	    temp = msx > 0 ? B_CLICKS - (msx & B_MASK) : (-msx) & B_MASK;
988 	    temp = MIN(temp,
989 		       (msy > 0 ? B_CLICKS - (msy & B_MASK) : -msy & B_MASK));
990 	    x += temp;
991 	    x = MIN(x, MAX_MOVE);
992 	}
993 	if (mindone > x) {
994 	    mindone = x;
995 	    minheight = LINEY(mdx, mdy, mbase, mindone);
996 	}
997     }
998     minline = -1;
999 
1000     Lines_check(msx, msy, mdx, mdy, &mindone, lines, chx, chy, chxy,
1001 		move, &minline, &minheight);
1002 
1003     answer->line = minline;
1004     if (chxy) {
1005 	temp = mindone;
1006 	mindone = minheight;
1007 	minheight = temp;
1008     }
1009     if (chx)
1010 	mindone = -mindone;
1011     if (chy)
1012 	minheight = -minheight;
1013     answer->moved.cx = mindone;
1014     answer->moved.cy = minheight;
1015 
1016     return;
1017 }
1018 
1019 
1020 /* Similar to Move_point above, except that it gets the shape parameter
1021  * (and direction of that shape), and in case of collision, the 'point'
1022  * field in struct collans is used. A corner in the shape can hit a map
1023  * line, or a corner in the map can hit a shape line. The 'line' field
1024  * will contain the line, the 'point' field the corner (given as a line
1025  * whose first point is that corner - that is always possible because
1026  * polygons are closed and there is at least one line ending at and one
1027  * line starting from a given point). */
1028 /* Do not call this with no movement. */
1029 /* May not be called with point already on top of line.
1030    maybe I should change that to allow lines which could be
1031    crossed. */
1032 /* This could be sped up by a lot in several ways if needed.
1033  * For example, there's no need to consider all the points
1034  * separately if the shape is not close to a wall.
1035  */
Shape_move(const move_t * move,shape_t * s,int dir,struct collans * answer)1036 static void Shape_move(const move_t *move, shape_t *s,
1037 		       int dir, struct collans *answer)
1038 {
1039     int minline, mindone, minheight, minpoint;
1040     int i, block;
1041     int msx = move->start.cx, msy = move->start.cy;
1042     int mdx = move->delta.cx, mdy = move->delta.cy;
1043     int mbase;
1044     int chxy = 0, chx = 0, chy = 0;
1045     int x, temp;
1046     unsigned short *lines;
1047     unsigned short *points;
1048     clpos_t *pts;
1049 
1050     if (mdx < 0) {
1051 	mdx = -mdx;
1052 	chx = 1;
1053     }
1054     if (mdy < 0) {
1055 	mdy = -mdy;
1056 	chy = 1;
1057     }
1058     if (mdx < mdy) {
1059 	temp = mdx;
1060 	mdx = mdy;
1061 	mdy = temp;
1062 	chxy = 1;
1063     }
1064 
1065     /* 46341*46341 overflows signed 32-bit int */
1066     if (mdx > 45000) {
1067 	/* might overflow without float */
1068 	mdy = (int)((float)mdy * 45000 / mdx);
1069 	mdx = 45000;
1070     }
1071 
1072     mindone = mdx;
1073     minheight = mdy;
1074 
1075     mdx++;
1076     mdy++;
1077     mbase = mdy >> 1;
1078     minline = -1;
1079     minpoint = -1;
1080 
1081     pts = Shape_get_points((shape_t *)s, dir);
1082     for (i = 0; i < s->num_points; i++) {
1083 	clpos_t pt = pts[i];
1084 
1085 	msx = WRAP_XCLICK(move->start.cx + pt.cx);
1086 	msy = WRAP_YCLICK(move->start.cy + pt.cy);
1087 	block = (msx >> B_SHIFT) + mapx * (msy >> B_SHIFT);
1088 	if (chx)
1089 	    msx = -msx;
1090 	if (chy)
1091 	    msy = -msy;
1092 	if (chxy) {
1093 	    temp = msx;
1094 	    msx = msy;
1095 	    msy = temp;
1096 	}
1097 
1098 	x = blockline[block].distance;
1099 	lines = blockline[block].lines;
1100 
1101 	if (mindone > x) {
1102 	    if (x < MAX_MOVE) {
1103 		temp = msx > 0 ? B_CLICKS - (msx & B_MASK) : -msx & B_MASK;
1104 		temp = MIN(temp,
1105 			   (msy > 0 ?
1106 			    B_CLICKS - (msy & B_MASK)
1107 			    : -msy & B_MASK));
1108 		x += temp;
1109 		x = MIN(x, MAX_MOVE);
1110 	    }
1111 	    if (mindone > x) {
1112 		mindone = x;
1113 		minheight = LINEY(mdx, mdy, mbase, mindone);
1114 	    }
1115 	}
1116 
1117 	if (Lines_check(msx, msy, mdx, mdy, &mindone, lines, chx, chy,
1118 			chxy, move, &minline, &minheight))
1119 	    minpoint = i;
1120     }
1121 
1122     block = (move->start.cx >> B_SHIFT) + mapx * (move->start.cy >> B_SHIFT);
1123     points = blockline[block].points;
1124     lines = Shape_lines(s, dir);
1125     x = -1;
1126     while ( ( i = *points++) != 65535) {
1127 	if (linet[i].group
1128 	    && (!can_hit(&groups[linet[i].group], move)))
1129 	    continue;
1130 	msx = move->start.cx - linet[i].start.cx;
1131 	msy = move->start.cy - linet[i].start.cy;
1132 	if (chx)
1133 	    msx = -msx;
1134 	if (chy)
1135 	    msy = -msy;
1136 	if (chxy) {
1137 	    temp = msx;
1138 	    msx = msy;
1139 	    msy = temp;
1140 	}
1141 	if (Lines_check(msx, msy, mdx, mdy, &mindone, lines, chx, chy,
1142 			chxy, move, &minline, &minheight))
1143 	    minpoint = i;
1144     }
1145 
1146     answer->point = minpoint;
1147     answer->line = minline;
1148     answer->point = minpoint;
1149     if (chxy) {
1150 	temp = mindone;
1151 	mindone = minheight;
1152 	minheight = temp;
1153     }
1154     if (chx)
1155 	mindone = -mindone;
1156     if (chy)
1157 	minheight = -minheight;
1158     answer->moved.cx = mindone;
1159     answer->moved.cy = minheight;
1160 
1161     return;
1162 }
1163 
1164 
1165 /* Check whether there is room at the given position (x, y) to change
1166  * from the shape shape1/dir1 to shape shape2/dir2. The shapes must have
1167  * the same number of points. The morphing of the shapes happens linearly
1168  * for each point. This should work correctly even if the intermediate
1169  * shapes are degenerate or illegal shapes (the intermediate stages are
1170  * not explicitly constructed in the algorithm). Return the number of a group
1171  * that would be hit during morphing or NO_GROUP if there is enough room. */
1172 /* This might be useful elsewhere in the code, need not be kept static */
Shape_morph(shape_t * shape1,int dir1,shape_t * shape2,int dir2,hitmask_t hitmask,const object_t * obj,int x,int y,struct collans * myanswer)1173 static int Shape_morph(shape_t *shape1, int dir1,
1174 		       shape_t *shape2, int dir2,
1175 		       hitmask_t hitmask, const object_t *obj,
1176 		       int x, int y,
1177 		       struct collans *myanswer)
1178 {
1179     struct collans answer;
1180     int i, p, xo1, xo2, yo1, yo2, xn1, xn2, yn1, yn2, xp, yp, s, t;
1181     unsigned short *points;
1182     move_t mv;
1183     /*clpos_t *pts1, *pts2;*/
1184     int num_points;
1185 
1186     mv.hitmask = hitmask;
1187     mv.obj = obj;
1188     /*pts1 = Shape_get_points((shape_t *)shape1, dir1);
1189       pts2 = Shape_get_points((shape_t *)shape2, dir2);*/
1190 
1191     /* kps - can this happen ?? */
1192     if (shape1->num_points != shape2->num_points)
1193 	warn("Shape_morph: shapes have different number of points!");
1194 
1195     num_points = shape1->num_points;
1196 
1197     for (i = 0; i < num_points; i++) {
1198 	clpos_t pt1, pt2;
1199 	/*clpos_t ptx1, ptx2;
1200 
1201 	  ptx1 = pts1[i];
1202 	  ptx2 = pts2[i];*/
1203 	pt1 = Ship_get_point_clpos((shipshape_t *)shape1, i, dir1);
1204 	pt2 = Ship_get_point_clpos((shipshape_t *)shape2, i, dir2);
1205 
1206 	/*assert(ptx1.cx == pt1.cx);
1207 	  assert(ptx1.cy == pt1.cy);
1208 	  assert(ptx2.cx == pt2.cx);
1209 	  assert(ptx2.cy == pt2.cy);*/
1210 
1211 	mv.start.cx = x + pt1.cx;
1212 	mv.start.cy = y + pt1.cy;
1213 	mv.delta.cx = x + pt2.cx - mv.start.cx;
1214 	mv.delta.cy = y + pt2.cy - mv.start.cy;
1215 	mv.start.cx = WRAP_XCLICK(mv.start.cx);
1216 	mv.start.cy = WRAP_YCLICK(mv.start.cy);
1217 	while (mv.delta.cx || mv.delta.cy) {
1218 	    Move_point(&mv, &answer);
1219     	    if (answer.line != -1){
1220 	     /* report what lines/points/vectors caused the move to fail*/
1221     	    	myanswer->line = answer.line;
1222     	    	myanswer->point = i;
1223     	    	myanswer->moved.cx = mv.delta.cx;
1224     	    	myanswer->moved.cy = mv.delta.cy;
1225     	    	return linet[answer.line].group;
1226     	    }
1227 	    mv.start.cx = WRAP_XCLICK(mv.start.cx + answer.moved.cx);
1228 	    mv.start.cy = WRAP_YCLICK(mv.start.cy + answer.moved.cy);
1229 	    mv.delta.cx -= answer.moved.cx;
1230 	    mv.delta.cy -= answer.moved.cy;
1231 	}
1232     }
1233 
1234     /* Convex shapes would be much easier. */
1235     points = blockline[(x >> B_SHIFT) + mapx * (y >> B_SHIFT)].points;
1236     while ( (p = *points++) != 65535) {
1237 	clpos_t pto1, ptn1;
1238 
1239 	if (linet[p].group && (!can_hit(&groups[linet[p].group], &mv)))
1240 	    continue;
1241 	xp = CENTER_XCLICK(linet[p].start.cx - x);
1242 	yp = CENTER_YCLICK(linet[p].start.cy - y);
1243 
1244 	/*pto1 = pts1[num_points - 1];
1245 	  ptn1 = pts2[num_points - 1];*/
1246 	pto1 = Ship_get_point_clpos((shipshape_t *)shape1, num_points - 1, dir1);
1247 	ptn1 = Ship_get_point_clpos((shipshape_t *)shape2, num_points - 1, dir2);
1248 
1249 	xo1 = pto1.cx - xp;
1250 	yo1 = pto1.cy - yp;
1251 	xn1 = ptn1.cx - xp;
1252 	yn1 = ptn1.cy - yp;
1253 	t = 0;
1254 
1255 	for (i = 0; i < num_points; i++) {
1256 	    clpos_t pto2, ptn2;
1257 
1258 	    /*pto2 = pts1[i];
1259 	      ptn2 = pts2[i];*/
1260 	    pto2 = Ship_get_point_clpos((shipshape_t *)shape1, i, dir1);
1261 	    ptn2 = Ship_get_point_clpos((shipshape_t *)shape2, i, dir2);
1262 
1263 	    xo2 = pto2.cx - xp;
1264 	    yo2 = pto2.cy - yp;
1265 	    xn2 = ptn2.cx - xp;
1266 	    yn2 = ptn2.cy - yp;
1267 
1268 #define TEMPFUNC(X1, Y1, X2, Y2)    	    	    	    	    	    \
1269     	    myanswer->line = -1;    	    	    	    	    	    \
1270     	    myanswer->point = i;    	    	    	    	    	    \
1271     	    myanswer->moved.cx = (X2) - (X1);	    	    	    	    \
1272     	    myanswer->moved.cy = (Y2) - (Y1);	    	    	    	    \
1273 	    if ((X1) < 0) { 	    	    	    	    	    	    \
1274 		if ((X2) >= 0) {    	    	    	    	    	    \
1275 		    if ((Y1) > 0 && (Y2) >= 0)	    	    	    	    \
1276 			t++;	    	    	    	    	    	    \
1277 		    else if (((Y1) >= 0 || (Y2) >= 0) &&    	    	    \
1278     	    	    	    (s = (X1)*((Y1)-(Y2))-(Y1)*((X1)-(X2))) >= 0){  \
1279     	    	    	if (s == 0){	    	    	    	    	    \
1280      	    	    	    return linet[p].group;  	    	    	    \
1281 	    	    	}   	    	    	    	    	    	    \
1282 			else	    	    	    	    	    	    \
1283 			    t++;    	    	    	    	    	    \
1284 		    }	    	    	    	    	    	    	    \
1285 		}   	    	    	    	    	    	    	    \
1286 	    } else {	    	    	    	    	    	    	    \
1287 		if ((X2) <= 0) {    	    	    	    	    	    \
1288 		    if ((X2) == 0) {	    	    	    	    	    \
1289 			if ((Y2)==0||((X1)==0 && (((Y1)<=0 && (Y2)>= 0) ||  \
1290     	    	    	    	    	    	((Y1) >= 0 && (Y2)<=0)))){  \
1291 			    return linet[p].group;  	    	    	    \
1292     	    	    	}   	    	    	    	    	    	    \
1293 		    }	    	    	    	    	    	    	    \
1294 		    else if ((Y1) > 0 && (Y2) >= 0) 	    	    	    \
1295 			t++;	    	    	    	    	    	    \
1296 		    else if (((Y1) >= 0 || (Y2) >= 0) &&    	    	    \
1297 			     (s = (X1)*((Y1)-(Y2))-(Y1)*((X1)-(X2))) <= 0){ \
1298 			if (s == 0) 	    	    	    	    	    \
1299 			    return linet[p].group;  	    	    	    \
1300 			else	    	    	    	    	    	    \
1301 			    t++;    	    	    	    	    	    \
1302 		    }	    	    	    	    	    	    	    \
1303 		}   	    	    	    	    	    	    	    \
1304     	    }
1305 
1306 	    TEMPFUNC(xo1, yo1, xn1, yn1);
1307 	    TEMPFUNC(xn1, yn1, xn2, yn2);
1308 	    TEMPFUNC(xn2, yn2, xo2, yo2);
1309 	    TEMPFUNC(xo2, yo2, xo1, yo1);
1310 #undef TEMPFUNC
1311 
1312     	    if (t & 1){
1313     	    	myanswer->line = -1;  /*p not a line, but point*/
1314     	    	myanswer->point = i;
1315     	    	myanswer->moved.cx = p;
1316     	    	myanswer->moved.cy = 0;
1317     	    	return linet[p].group;
1318     	    }
1319 	    xo1 = xo2;
1320 	    yo1 = yo2;
1321 	    xn1 = xn2;
1322 	    yn1 = yn2;
1323 	}
1324     }
1325     return NO_GROUP;
1326 }
1327 
1328 
1329 /* Try to move one click away from a line after a collision. Needed because
1330  * otherwise we could keep hitting it even though direction of movement
1331  * was away from the line. Example case:
1332  * XXX
1333  *   1*XX
1334  *     34XXX
1335  *       56 XXX
1336  *         78  XXX
1337  * Line (marked with 'X') and path of object (marked with numbers) collide
1338  * at '*' because of discreteness even though the object is moving away
1339  * from the line.
1340  * Return -1 if successful, number of another line blocking movement
1341  * otherwise (the number of the line is not used when writing this). */
Away(move_t * move,int line)1342 static int Away(move_t *move, int line)
1343 {
1344     int dx, dy, lsx, lsy;
1345     clvec_t delta_saved;
1346     struct collans ans;
1347 
1348     lsx = linet[line].start.cx - move->start.cx;
1349     lsy = linet[line].start.cy - move->start.cy;
1350     lsx = CENTER_XCLICK(lsx);
1351     lsy = CENTER_YCLICK(lsy);
1352 
1353     if (ABS(linet[line].delta.cx) >= ABS(linet[line].delta.cy)) {
1354 	dx = 0;
1355 	dy = -SIGN(linet[line].delta.cx);
1356     }
1357     else {
1358 	dy = 0;
1359 	dx = SIGN(linet[line].delta.cy);
1360     }
1361 
1362     if ((ABS(lsx) > SEPARATION_DIST || ABS(lsy) > SEPARATION_DIST)
1363 	&& (ABS(lsx + linet[line].delta.cx) > SEPARATION_DIST
1364 	    || ABS(lsy + linet[line].delta.cy) > SEPARATION_DIST)) {
1365 	move->start.cx = WRAP_XCLICK(move->start.cx + dx);
1366 	move->start.cy = WRAP_YCLICK(move->start.cy + dy);
1367 	return -1;
1368     }
1369 
1370     delta_saved = move->delta;
1371     move->delta.cx = dx;
1372     move->delta.cy = dy;
1373     Move_point(move, &ans);
1374     move->start.cx = WRAP_XCLICK(move->start.cx + ans.moved.cx);
1375     move->start.cy = WRAP_YCLICK(move->start.cy + ans.moved.cy);
1376     move->delta = delta_saved;
1377     return ans.line;
1378 }
1379 
1380 
1381 /* Used internally to get a point out of a tight corner where there is
1382  * no room to move it. Situation like this:
1383  * E***X
1384  *     Y***XX
1385  *         YY**XXX
1386  *             YYY*XXXX
1387  *                 YYYYXXXXX
1388  *                     YYYY XXXXX
1389  *                         YYYY  XXXXX
1390  *                             YYYY   XXXXX
1391  *                                 YYYY !  XXXXX
1392  *                                     YYYY     XXXXX
1393  *                                         YYYY      XXXXX
1394  *                                             YYYY       XXXXX
1395  * X and Y are lines meeting at E. '*' is where lines overlap. An object
1396  * can get to position !, but is hard to move out of there (and the situation
1397  * can be worse than shown here - the angles of the lines don't need to
1398  * differ so much that the gap would get 1 click bigger for each step of
1399  * the y coordinate). This function is used to move the object a bit
1400  * outwards from the corner so discreteness doesn't make moving it so hard. */
1401 /* This function should get called only rarely, so it doesn't need to
1402  * be too efficient. */
Clear_corner(move_t * move,object_t * obj,int l1,int l2)1403 static int Clear_corner(move_t *move, object_t *obj, int l1, int l2)
1404 {
1405     int x, y, xm, ym;
1406     int l1sx, l2sx, l1sy, l2sy;
1407 
1408     l1sx = move->start.cx - linet[l1].start.cx;
1409     l1sy = move->start.cy - linet[l1].start.cy;
1410     l1sx = CENTER_XCLICK(l1sx);
1411     l1sy = CENTER_YCLICK(l1sy);
1412     l2sx = move->start.cx - linet[l2].start.cx;
1413     l2sy = move->start.cy - linet[l2].start.cy;
1414     l2sx = CENTER_XCLICK(l2sx);
1415     l2sy = CENTER_YCLICK(l2sy);
1416 
1417     for (;;) {
1418 	if (SIDE(obj->vel.x, obj->vel.y, l1) < 0) {
1419 	    if (!Bounce_object(obj, move, l1, 0))
1420 		return 0;
1421 	}
1422 	if (SIDE(obj->vel.x, obj->vel.y, l2) < 0) {
1423 	    if (!Bounce_object(obj, move, l2, 0))
1424 		return 0;
1425 	    continue;
1426 	}
1427 	break;
1428     }
1429 
1430     xm = SIGN(obj->vel.x);
1431     ym = SIGN(obj->vel.y);
1432 
1433 #define TMPFUNC(X, Y) (SIDE((X) + l1sx, (Y) + l1sy, l1) <= 0 || SIDE((X) + l2sx, (Y) + l2sy, l2) <= 0)
1434 
1435     if (ABS(obj->vel.x) >= ABS(obj->vel.y)) {
1436 	x = xm;
1437 	y = 0;
1438 	for (;;) {
1439 	    if (TMPFUNC(x, y)) {
1440 		y += ym;
1441 		if (!TMPFUNC(x, y + ym))
1442 		    break;
1443 		else
1444 		    x += xm;
1445 	    }
1446 	    else {
1447 		if (TMPFUNC(x, y + 1) && TMPFUNC(x, y - 1))
1448 		    x += xm;
1449 		else
1450 		    break;
1451 	    }
1452 	}
1453 	move->delta.cx -= x;
1454 	move->delta.cy -= y;
1455 	if ((obj->vel.x >= 0) ^ (move->delta.cx >= 0)) {
1456 	    move->delta.cx = 0;
1457 	    move->delta.cy = 0;
1458 	}
1459     }
1460     else {
1461 	x = 0;
1462 	y = ym;
1463 	for (;;) {
1464 	    if (TMPFUNC(x, y)) {
1465 		x += xm;
1466 		if (!TMPFUNC(x + xm, y))
1467 		    break;
1468 		else
1469 		    y += ym;
1470 	    }
1471 	    else {
1472 		if (TMPFUNC(x + 1, y) && TMPFUNC(x - 1, y))
1473 		    y += ym;
1474 		else
1475 		    break;
1476 	    }
1477 	}
1478 
1479 #undef TMPFUNC
1480 
1481 	move->delta.cx -= x;
1482 	move->delta.cy -= y;
1483 	if ((obj->vel.y >= 0) ^ (move->delta.cy >= 0)) {
1484 	    move->delta.cx = 0;
1485 	    move->delta.cy = 0;
1486 	}
1487     }
1488     move->start.cx = WRAP_XCLICK(move->start.cx + x);
1489     move->start.cy = WRAP_YCLICK(move->start.cy + y);
1490     return 1;
1491 }
1492 
1493 
1494 /* Move a shape away from a line after a collision. Needed for the same
1495  * reason as Away(). */
Shape_away(move_t * move,shape_t * s,int dir,int line,struct collans * ans)1496 static int Shape_away(move_t *move, shape_t *s,
1497 		      int dir, int line, struct collans *ans)
1498 {
1499     int dx, dy;
1500     clvec_t delta_saved;
1501 
1502     if (ABS(linet[line].delta.cx) >= ABS(linet[line].delta.cy)) {
1503 	dx = 0;
1504 	dy = -SIGN(linet[line].delta.cx);
1505     }
1506     else {
1507 	dy = 0;
1508 	dx = SIGN(linet[line].delta.cy);
1509     }
1510 
1511     delta_saved = move->delta;
1512     move->delta.cx = dx;
1513     move->delta.cy = dy;
1514     Shape_move(move, s, dir, ans);
1515     move->start.cx = WRAP_XCLICK(move->start.cx + ans->moved.cx);
1516     move->start.cy = WRAP_YCLICK(move->start.cy + ans->moved.cy);
1517     move->delta = delta_saved;
1518     return ans->line == -1 && ans->point == -1;
1519 }
1520 
1521 
store_byte(int value,unsigned char ** start,int * offset,int * sz)1522 static void store_byte(int value, unsigned char **start, int *offset, int *sz)
1523 {
1524     (*start)[(*offset)++] = value;
1525     if (*offset == *sz) {
1526 	*sz *= 2;
1527 	*start = (unsigned char *)ralloc(*start, *sz);
1528     }
1529 }
1530 
store_2byte(int value,unsigned char ** start,int * offset,int * sz)1531 static void store_2byte(int value, unsigned char **start, int *offset, int *sz)
1532 {
1533     store_byte(value >> 8, start, offset, sz);
1534     store_byte(value & 0xff, start, offset, sz);
1535 }
1536 
1537 
store_4byte(int value,unsigned char ** start,int * offset,int * sz)1538 static void store_4byte(int value, unsigned char **start, int *offset, int *sz)
1539 {
1540     store_2byte(value >> 16, start, offset, sz);
1541     store_2byte(value & 0xffff, start, offset, sz);
1542 }
1543 
1544 
Polys_to_client(unsigned char ** start)1545 int Polys_to_client(unsigned char **start)
1546 {
1547     int i, j, startx, starty, dx, dy;
1548     int *edges;
1549     int size, offset;
1550 #define STORE1(x) store_byte(x, start, &offset, &size)
1551 #define STORE2(x) store_2byte(x, start, &offset, &size)
1552 #define STORE4(x) store_4byte(x, start, &offset, &size)
1553 
1554     *start = (unsigned char *)ralloc(NULL, 100);
1555     size = 100;
1556     offset = 0;
1557 
1558     STORE1(num_pstyles);
1559     STORE1(num_estyles);
1560     STORE1(num_bstyles);
1561     for (i = 0; i < num_pstyles; i++) {
1562 	STORE4(pstyles[i].color);
1563 	STORE1(pstyles[i].texture_id);
1564 	STORE1(pstyles[i].defedge_id);
1565 	STORE1(pstyles[i].flags);
1566     }
1567     for (i = 0; i < num_estyles; i++) {
1568 	STORE1(estyles[i].width);
1569 	STORE4(estyles[i].color);
1570 	STORE1(estyles[i].style);
1571     }
1572     for (i = 0; i < num_bstyles; i++) {
1573 	j = 0;
1574 	while (1) {
1575 	    STORE1(bstyles[i].filename[j]);
1576 	    if (!bstyles[i].filename[j])
1577 		break;
1578 	    j++;
1579 	}
1580 	STORE1(bstyles[i].flags);
1581     }
1582     STORE2(num_polys);
1583     for (i = 0; i < num_polys; i++) {
1584 	STORE1(pdata[i].style);
1585 	j = pdata[i].num_points;
1586 	STORE2(pdata[i].num_echanges);
1587 	edges = estyleptr + pdata[i].estyles_start;
1588 	while (*edges != INT_MAX)
1589 	    STORE2(*edges++);
1590 	startx = pdata[i].pos.cx;
1591 	starty = pdata[i].pos.cy;
1592 	edges = edgeptr + pdata[i].edges;
1593 	STORE2(j);
1594 	STORE2(startx >> CLICK_SHIFT);
1595 	STORE2(starty >> CLICK_SHIFT);
1596 	dx = startx;
1597 	dy = starty;
1598 	for (; j > 0; j--) {
1599 	    dx += *edges++;
1600 	    dy += *edges++;
1601 	    if (j != 1) {
1602 		STORE2((dx >> CLICK_SHIFT) - (startx>>CLICK_SHIFT));
1603 		STORE2((dy >> CLICK_SHIFT) - (starty>>CLICK_SHIFT));
1604 	    }
1605 	    startx = dx;
1606 	    starty = dy;
1607 	}
1608     }
1609     STORE1(Num_bases());
1610     for (i = 0; i < Num_bases(); i++) {
1611 	base_t *base = Base_by_index(i);
1612 	if (base->team == TEAM_NOT_SET)
1613 	    STORE1(0);
1614 	else
1615 	    STORE1(base->team);
1616 	STORE2(base->pos.cx >> CLICK_SHIFT);
1617 	STORE2(base->pos.cy >> CLICK_SHIFT);
1618 	STORE1(base->dir);
1619     }
1620     STORE2(Num_fuels());
1621     for (i = 0; i < Num_fuels(); i++) {
1622 	fuel_t *fs = Fuel_by_index(i);
1623 
1624 	STORE2(fs->pos.cx >> CLICK_SHIFT);
1625 	STORE2(fs->pos.cy >> CLICK_SHIFT);
1626     }
1627     STORE1(world->NumChecks);
1628     for (i = 0; i < world->NumChecks; i++) {
1629 	STORE2(world->checks[i].pos.cx >> CLICK_SHIFT);
1630 	STORE2(world->checks[i].pos.cy >> CLICK_SHIFT);
1631     }
1632     return offset;
1633 }
1634 
1635 
1636 struct tempy {
1637     short y;
1638     struct tempy *next;
1639 };
1640 
1641 
1642 struct templine {
1643     short x1, x2, y1, y2;
1644     struct templine *next;
1645 };
1646 
1647 
1648 /* Check whether the given position (cx, cy) is such that it is inside
1649  * a polygon belonging to a group that could be hit by the given
1650  * hitmask/object.
1651  * Return the number of a group that would be hit or NO_GROUP. */
is_inside(int cx,int cy,hitmask_t hitmask,const object_t * obj)1652 int is_inside(int cx, int cy, hitmask_t hitmask, const object_t *obj)
1653 {
1654     short *ptr;
1655     int inside, cx1, cx2, cy1, cy2, s;
1656     struct inside_block *gblock;
1657     move_t mv;
1658 
1659 #if 0
1660     /* kps - is_inside seems to assume that cx and cy are inside the map */
1661     clpos_t pos;
1662 
1663     pos.cx = cx;
1664     pos.cy = cy;
1665 
1666     assert(World_contains_clpos(&World, pos));
1667 #endif
1668 
1669     mv.hitmask = hitmask;
1670     mv.obj = obj;
1671     gblock = &inside_table[(cx >> B_SHIFT) + mapx * (cy >> B_SHIFT)];
1672     if (gblock->group == NO_GROUP)
1673 	return NO_GROUP;
1674     do {
1675 	if (gblock->group && (!can_hit(&groups[gblock->group], &mv))) {
1676 	    gblock = gblock->next;
1677 	    continue;
1678 	}
1679 	inside = gblock->base_value;
1680 	if (gblock->lines == NULL) {
1681 	    if (inside)
1682 		return gblock->group;
1683 	    else {
1684 		gblock = gblock->next;
1685 		continue;
1686 	    }
1687 	}
1688 	cx &= B_MASK;
1689 	cy &= B_MASK;
1690 	ptr = gblock->y;
1691 	if (ptr)
1692 	    while (cy > *ptr++)
1693 		inside++;
1694 	ptr = gblock->lines;
1695 	while (*ptr != 32767) {
1696 	    cx1 = *ptr++ - cx;
1697 	    cy1 = *ptr++ - cy;
1698 	    cx2 = *ptr++ - cx;
1699 	    cy2 = *ptr++ - cy;
1700 	    if (cy1 < 0) {
1701 		if (cy2 >= 0) {
1702 		    if (cx1 > 0 && cx2 >= 0)
1703 			inside++;
1704 		    else if ((cx1 >= 0 || cx2 >= 0) &&
1705 			     (s = cy1 * (cx1 - cx2) - cx1 * (cy1 - cy2)) >= 0) {
1706 			if (s == 0)
1707 			    return gblock->group;
1708 			else
1709 			    inside++;
1710 		    }
1711 		}
1712 	    }
1713 	    else
1714 		if (cy2 <= 0) {
1715 		    if (cy2 == 0) {
1716 			if (cx2 == 0 || (cy1 ==0 && ((cx1 <= 0 && cx2 >= 0) ||
1717 						     (cx1 >= 0 && cx2 <= 0))))
1718 			    return gblock->group;
1719 		    }
1720 		    else if (cx1 > 0 && cx2 >= 0)
1721 			inside++;
1722 		    else if ((cx1 >= 0 || cx2 >= 0) &&
1723 			     (s = cy1 * (cx1 - cx2) - cx1 * (cy1 - cy2)) <= 0) {
1724 			if (s == 0)
1725 			    return gblock->group;
1726 			else
1727 			    inside++;
1728 		    }
1729 		}
1730 	}
1731 	if (inside & 1)
1732 	    return gblock->group;
1733 	gblock = gblock->next;
1734     } while (gblock);
1735     return NO_GROUP;
1736 }
1737 
1738 
1739 /* Similar to the above, except check whether any part of the shape
1740  * (edge or inside) would hit the group. */
shape_is_inside(int cx,int cy,hitmask_t hitmask,const object_t * obj,shape_t * s,int dir)1741 int shape_is_inside(int cx, int cy, hitmask_t hitmask, const object_t *obj,
1742 		    shape_t *s, int dir)
1743 {
1744     static clpos_t zeropos;
1745     static shape_t zeroshape;
1746     int i, group;
1747     struct collans ans;
1748 
1749     /* Implemented by first checking whether the middle point of the
1750      * shape is on top of something. If not, check whether it is possible
1751      * to enlarge a degenerate shape where all points are on top of each
1752      * other (at the middle point) to the given one. (So it relies on the
1753      * rule that the shape must contain the middle point. */
1754 
1755     if ( (group = is_inside(cx, cy, hitmask, obj)) != NO_GROUP)
1756 	return group;
1757 
1758     /*
1759      * kps - Ship numpoints can be > MAX_SHIP_PTS because of
1760      * SSHACK. This should somehow be fixed.
1761      */
1762     zeroshape.num_points = s->num_points;
1763 
1764     if (zeroshape.pts[0] == NULL) {
1765 	for (i = 0; i < MAX_SHIP_PTS2; i++)
1766 	    zeroshape.pts[i] = &zeropos;
1767     }
1768 
1769     return Shape_morph(&zeroshape, 0, s, dir, hitmask, obj, cx, cy, &ans);
1770 }
1771 
1772 
closest_line(int bx,int by,double dist,int inside)1773 static void closest_line(int bx, int by, double dist, int inside)
1774 {
1775     if (dist <= temparray[bx + mapx *by].distance) {
1776 	if (dist == temparray[bx + mapx * by].distance)
1777 	    /* Must be joined polygons(s) if the map is legal
1778 	     * (the same line appears in both directions).
1779 	     * Both sides of this line are inside. */
1780 	    /* These lines could be removed from the table as a minor
1781 	     * optimization. */
1782 	     inside = 1;
1783 	temparray[bx + mapx * by].distance = dist;
1784 	temparray[bx + mapx * by].inside = inside;
1785     }
1786 }
1787 
1788 
insert_y(int block,int y)1789 static void insert_y(int block, int y)
1790 {
1791     struct tempy *ptr;
1792     struct tempy **prev;
1793 
1794     ptr = temparray[block].y;
1795     prev = &temparray[block].y;
1796     while (ptr && ptr->y < y) {
1797 	prev = &ptr->next;
1798 	ptr = ptr->next;
1799     }
1800     if (ptr && ptr->y == y) {
1801 	*prev = ptr->next;
1802 	free(ptr);
1803 	return;
1804     }
1805     *prev = (struct tempy *)ralloc(NULL, sizeof(struct tempy));
1806     (*prev)->y = y;
1807     (*prev)->next = ptr;
1808 }
1809 
1810 
store_inside_line(int bx,int by,int ox,int oy,int dx,int dy)1811 static void store_inside_line(int bx, int by, int ox, int oy, int dx, int dy)
1812 {
1813     int block;
1814     struct templine *s;
1815 
1816     block = bx + mapx * by;
1817     ox = CENTER_XCLICK(ox - bx * B_CLICKS);
1818     oy = CENTER_YCLICK(oy - by * B_CLICKS);
1819     if (oy >= 0 && oy < B_CLICKS && ox >= B_CLICKS)
1820 	insert_y(block, oy);
1821     if (oy + dy >= 0 && oy + dy < B_CLICKS && ox + dx >= B_CLICKS)
1822 	insert_y(block, oy + dy);
1823     s = (struct templine *)ralloc(NULL, sizeof(struct templine));
1824     s->x1 = ox;
1825     s->x2 = ox + dx;
1826     s->y1 = oy;
1827     s->y2 = oy + dy;
1828     s->next = temparray[block].lines;
1829     temparray[block].lines = s;
1830 }
1831 
1832 
finish_inside(int block,int group)1833 static void finish_inside(int block, int group)
1834 {
1835     int inside;
1836     struct inside_block *gblock;
1837     short *ptr;
1838     int cx1, cx2, cy1, cy2, s, j;
1839     struct tempy *yptr;
1840     struct templine *lptr;
1841     void *tofree;
1842 
1843     gblock = &inside_table[block];
1844     if (gblock->group != NO_GROUP) {
1845 	while (gblock->next) /* Maintain group order*/
1846 	    gblock = gblock->next;
1847 	gblock->next
1848 	    = (struct inside_block *)ralloc(NULL, sizeof(struct inside_block));
1849 	gblock = gblock->next;
1850     }
1851     gblock->group = group;
1852     gblock->next = NULL;
1853     j = 0;
1854     yptr = temparray[block].y;
1855     while (yptr) {
1856 	j++;
1857 	yptr = yptr->next;
1858     }
1859     if (j > 0) {
1860 	ptr = (short *)ralloc(NULL, (j + 1) * sizeof(short));
1861 	gblock->y = ptr;
1862 	yptr = temparray[block].y;
1863 	while (yptr) {
1864 	    *ptr++ = yptr->y;
1865 	    tofree = yptr;
1866 	    yptr = yptr->next;
1867 	    free(tofree);
1868 	}
1869 	*ptr = 32767;
1870     }
1871     else
1872 	gblock->y = NULL;
1873     j = 0;
1874     lptr = temparray[block].lines;
1875     while (lptr) {
1876 	j++;
1877 	lptr = lptr->next;
1878     }
1879     if (j > 0) {
1880 	ptr = (short *)ralloc(NULL, (j * 4 + 1) * sizeof(short));
1881 	gblock->lines = ptr;
1882 	lptr = temparray[block].lines;
1883 	while (lptr) {
1884 	    *ptr++ = lptr->x1;
1885 	    *ptr++ = lptr->y1;
1886 	    *ptr++ = lptr->x2;
1887 	    *ptr++ = lptr->y2;
1888 	    tofree = lptr;
1889 	    lptr = lptr->next;
1890 	    free(tofree);
1891 	}
1892 	*ptr = 32767;
1893     }
1894     else
1895 	gblock->lines = NULL;
1896     inside = temparray[block].inside;
1897     if ( (ptr = gblock->lines) != NULL) {
1898 	while (*ptr != 32767) {
1899 	    cx1 = *ptr++ * 2 - B_CLICKS * 2 + 1;
1900 	    cy1 = *ptr++ * 2 + 1;
1901 	    cx2 = *ptr++ * 2 - B_CLICKS * 2 + 1;
1902 	    cy2 = *ptr++ * 2 + 1;
1903 	    if (cy1 < 0) {
1904 		if (cy2 >= 0) {
1905 		    if (cx1 > 0 && cx2 >= 0)
1906 			inside++;
1907 		    else if ((cx1 >= 0 || cx2 >= 0) &&
1908 			     (s = cy1 * (cx1 - cx2) - cx1 * (cy1 - cy2)) > 0)
1909 			inside++;
1910 		}
1911 	    }
1912 	    else
1913 		if (cy2 <= 0) {
1914 		    if (cx1 > 0 && cx2 >= 0)
1915 			inside++;
1916 		    else if ((cx1 >= 0 || cx2 >= 0) &&
1917 			     (s = cy1 * (cx1 - cx2) - cx1 * (cy1 - cy2)) < 0)
1918 			inside++;
1919 		}
1920 	}
1921     }
1922     gblock->base_value = inside & 1;
1923     temparray[block].y = NULL;
1924     temparray[block].lines = NULL;
1925     temparray[block].inside = 2;
1926     temparray[block].distance = 1e20;
1927 }
1928 
1929 
allocate_inside(void)1930 static void allocate_inside(void)
1931 {
1932     int i;
1933 
1934     inside_table = (struct inside_block *)
1935 	ralloc(NULL, mapx * mapy * sizeof(struct inside_block));
1936     temparray = (struct test *)
1937 	ralloc(NULL, mapx * mapy * sizeof(struct test));
1938     for (i = 0; i < mapx * mapy; i++) {
1939 	temparray[i].distance = 1e20;
1940 	temparray[i].inside = 2;
1941 	temparray[i].y = NULL;
1942 	temparray[i].lines = NULL;
1943 	inside_table[i].y = NULL;
1944 	inside_table[i].lines = NULL;
1945 	inside_table[i].base_value = 0;
1946 	inside_table[i].group = NO_GROUP;
1947 	inside_table[i].next = NULL;
1948     }
1949 }
1950 
1951 
1952 /* Calculate distance of intersection from lower right corner of the
1953  * block counterclockwise along the edge. We don't return the true lengths
1954  * but values which compare the same with each other.
1955  * 'dir' is used to return whether the block is left through a horizontal
1956  * or a vertical side or a corner. */
edge_distance(int bx,int by,int ox,int oy,int dx,int dy,int * dir)1957 static double edge_distance(int bx, int by, int ox, int oy, int dx, int dy,
1958 			  int *dir)
1959 {
1960     int last_width = (world->cwidth - 1) % B_CLICKS + 1;
1961     int last_height = (world->cheight - 1) % B_CLICKS + 1;
1962     double xdist, ydist, dist;
1963 
1964     ox = CENTER_XCLICK(ox - bx * B_CLICKS);
1965     oy = CENTER_YCLICK(oy - by * B_CLICKS);
1966     if (dx > 0)
1967 	xdist = ((bx == mapx - 1) ? last_width : B_CLICKS) - .5 - ox;
1968     else if (dx < 0)
1969 	xdist = ox + .5;
1970     else
1971 	xdist = 1e20; /* Something big enough to be > ydist, dx */
1972     if (dy > 0)
1973 	ydist = ((by == mapy - 1) ? last_height : B_CLICKS) - .5 - oy;
1974     else if (dy < 0)
1975 	ydist = oy + .5;
1976     else
1977 	ydist = 1e20;
1978     if (xdist > ABS(dx) && ydist > ABS(dy))
1979 	return -1;	/* Doesn't cross box boundary */
1980     if (ABS(dy) * xdist == ABS(dx) * ydist)
1981 	*dir = 3;
1982     else if (ABS(dy) * xdist < ABS(dx) * ydist)
1983 	*dir = 1;
1984     else
1985 	*dir = 2;
1986     if (*dir == 1)
1987 	if (dx > 0)
1988 	    dist = oy + dy * xdist / dx;
1989 	else
1990 	    dist = 5 * B_CLICKS - oy + dy * xdist / dx;
1991     else
1992 	if (dy > 0)
1993 	    dist = 3 * B_CLICKS - ox - dx * ydist / dy;
1994 	else
1995 	    dist = 6 * B_CLICKS + ox - dx * ydist / dy;
1996     return dist;
1997 }
1998 
1999 
2000 #define POSMOD(x, y) ((x) >= 0 ? (x) % (y) : ((x) + 1) % (y) + (y) - 1)
Inside_init(void)2001 static void Inside_init(void)
2002 {
2003     int dx, dy, bx, by, ox, oy, startx, starty;
2004     int i, j, num_points, minx = -1, miny = -1, poly, group;
2005     int bx2, by2, maxx = -1, maxy = -1, dir;
2006     double dist;
2007     int *edges;
2008 
2009     allocate_inside();
2010     for (group = 0; group < num_groups; group++) {
2011 	minx = -1;
2012 	for (poly = 0; poly < num_polys; poly++) {
2013 	    if (pdata[poly].is_decor || pdata[poly].group != group)
2014 		continue;
2015 	    num_points = pdata[poly].num_points;
2016 	    dx = 0;
2017 	    dy = 0;
2018 	    startx = pdata[poly].pos.cx;
2019 	    starty = pdata[poly].pos.cy;
2020 	    /* Better wrapping for bx2/by2 could be selected for speed here,
2021 	     * but this keeping track of min/max at all is probably
2022 	     * unnoticeable in practice. */
2023 	    bx2 = bx = startx >> B_SHIFT;
2024 	    by2 = by = starty >> B_SHIFT;
2025 	    if (minx == -1) {
2026 		minx = maxx = bx2;
2027 		miny = maxy = by2;
2028 	    }
2029 	    edges = edgeptr + pdata[poly].edges;
2030 	    closest_line(bx, by, 1e10, 0); /* For polygons within one block */
2031 	    for (j = 0; j < num_points; j++) {
2032 		if (((startx >> B_SHIFT) != bx)
2033 		    || ((starty >> B_SHIFT) != by)) {
2034 		    warn("Inside_init: went into infinite loop...");
2035 		    while (1);
2036 		}
2037 		ox = startx & B_MASK;
2038 		oy = starty & B_MASK;
2039 		dx = *edges++;
2040 		dy = *edges++;
2041 		while (1) {  /* All blocks containing a part of this line */
2042 		    store_inside_line(bx, by, startx, starty, dx, dy);
2043 		    dist = edge_distance(bx, by, WRAP_XCLICK(startx + dx),
2044 				 WRAP_YCLICK(starty + dy), -dx, -dy, &dir);
2045 		    if (dist != -1)
2046 			closest_line(bx, by, dist, 1);
2047 		    dist = edge_distance(bx, by, startx, starty, dx, dy, &dir);
2048 		    if (dist == -1)
2049 			break;
2050 		    closest_line(bx, by, dist, 0);
2051 		    if (dir == 1 || dir == 3)
2052 			bx2 += (dx > 0) ? 1 : -1;
2053 		    if (bx2 > maxx)
2054 			maxx = bx2;
2055 		    if (bx2 < minx)
2056 			minx = bx2;
2057 		    bx = POSMOD(bx2, mapx);
2058 		    if (dir == 2 || dir == 3)
2059 			by2 += (dy > 0) ? 1 : -1;
2060 		    if (by2 > maxy)
2061 			maxy = by2;
2062 		    if (by2 < miny)
2063 			miny = by2;
2064 		    by = POSMOD(by2, mapy);
2065 		}
2066 		startx = WRAP_XCLICK(startx + dx);
2067 		starty = WRAP_YCLICK(starty + dy);
2068 	    }
2069 	}
2070 	if (minx == -1)
2071 	    continue;
2072 	bx = maxx - minx + 1;
2073 	if (bx > 2 * mapx)
2074 	    bx = 2 * mapx;
2075 	by = maxy - miny + 1;
2076 	if (by > mapy)
2077 	    by = mapy;
2078 	for (i = POSMOD(miny, mapy); by-- > 0; i++) {
2079 	    if (i == mapy)
2080 		i = 0;
2081 	    bx2 = bx;
2082 	    dir = 0;
2083 	    for (j = POSMOD(minx, mapx); bx2-- > 0; j++) {
2084 		if (j == mapx)
2085 		    j = 0;
2086 		if (temparray[j + mapx * i].inside < 2) {
2087 		    dir = temparray[j + mapx * i].distance > B_CLICKS &&
2088 			temparray[j + mapx * i].inside == 1;
2089 		}
2090 		else {
2091 		    if (dir)
2092 			temparray[i * mapx + j].inside = 1;
2093 		}
2094 		if (bx2 < mapx)
2095 		    finish_inside(j + mapx * i, group);
2096 	    }
2097 	}
2098     }
2099     return;
2100 }
2101 
2102 
2103 /* Include NCLLIN - 1 closest lines or all closer than CUTOFF (whichever
2104  * is less) in the line table for this block.
2105  * Include all lines closer than DICLOSE, however many there are.
2106  * LINSIZE tells the amout of temporary memory to reserve for the algorithm.
2107  * If it is not large enough to hold the required lines, print error and
2108  * exit.
2109  */
2110 
2111 #define DICLOSE (5 * CLICK)
2112 #define LINSIZE 100
2113 #define NCLLIN (10 + 1)
Distance_init(void)2114 static void Distance_init(void)
2115 {
2116     int cx,cy;
2117     int *lineno, *dis;
2118     int lsx, lsy, ldx, ldy, temp, dist, n, i, bx, by, j, k;
2119     int base, height2, by2, width, height;
2120     int distbound, size;
2121     unsigned short *lptr;
2122 
2123     /* max line delta 30000 */
2124 
2125     blockline = (struct blockinfo *)
2126 	ralloc(NULL, mapx * mapy * sizeof(struct blockinfo));
2127     lineno = (int *)ralloc(NULL, mapx * mapy * LINSIZE * sizeof(int));
2128     dis = (int *)ralloc(NULL, mapx * mapy * LINSIZE * sizeof(int));
2129     size = 1; /* start with end marker */
2130     for (bx = 0; bx < mapx; bx++)
2131 	for (by = 0; by < mapy; by++)
2132 	    for (i = 0; i < LINSIZE; i++) {
2133 		dis[(by * mapx + bx) * LINSIZE + i] = MAX_MOVE + B_CLICKS / 2;
2134 		lineno[(by * mapx + bx) * LINSIZE +i] = 65535;
2135 	    }
2136     for (i = 0; i < num_lines; i++) {
2137 	bx = linet[i].start.cx;
2138 	by = linet[i].start.cy;
2139 	width = linet[i].delta.cx;
2140 	height = linet[i].delta.cy;
2141 	if (width < 0) {
2142 	    bx += width;
2143 	    width = -width;
2144 	    bx = WRAP_XCLICK(bx);
2145 	}
2146 	if (height < 0) {
2147 	    by += height;
2148 	    height = -height;
2149 	    by = WRAP_YCLICK(by);
2150 	}
2151 	width = (width + 2 * MAX_MOVE) / B_CLICKS + 5;
2152 	if (width >= mapx)
2153 	    width = mapx;
2154 	height = (height + 2 * MAX_MOVE) / B_CLICKS + 5;
2155 	if (height >= mapy)
2156 	    height = mapy;
2157 	bx = (bx - MAX_MOVE) / B_CLICKS - 2;
2158 	by = (by - MAX_MOVE) / B_CLICKS - 2;
2159 	while (bx < 0)
2160 	    bx += mapx;
2161 	while (by < 0)
2162 	    by += mapy;
2163 	height2 = height;
2164 	by2 = by;
2165 	for (; width-- > 0; bx = bx == mapx - 1 ? 0 : bx + 1)
2166 	    for (by = by2, height = height2;
2167 		 height -- > 0;
2168 		 by = by == mapy - 1? 0 : by + 1) {
2169 		cx = bx * B_CLICKS + B_CLICKS / 2;
2170 		cy = by * B_CLICKS + B_CLICKS / 2;
2171 		base = (by * mapx + bx) * LINSIZE;
2172 		lsx = CENTER_XCLICK(linet[i].start.cx - cx);
2173 		if (ABS(lsx) > 32767 + MAX_MOVE + B_CLICKS / 2)
2174 		    continue;
2175 		lsy = CENTER_YCLICK(linet[i].start.cy - cy);
2176 		if (ABS(lsy) > 32767 + MAX_MOVE + B_CLICKS / 2)
2177 		    continue;
2178 		ldx = linet[i].delta.cx;
2179 		ldy = linet[i].delta.cy;
2180 		if (MAX(ABS(lsx), ABS(lsy)) > MAX(ABS(lsx + ldx),
2181 						  ABS(lsy + ldy))) {
2182 		    lsx += ldx;
2183 		    ldx = -ldx;
2184 		    lsy += ldy;
2185 		    ldy = -ldy;
2186 		}
2187 		if (ABS(lsx) < ABS(lsy)) {
2188 		    temp = lsx;
2189 		    lsx = lsy;
2190 		    lsy = temp;
2191 		    temp = ldx;
2192 		    ldx = ldy;
2193 		    ldy = temp;
2194 		}
2195 		if (lsx < 0) {
2196 		    lsx = -lsx;
2197 		    ldx = -ldx;
2198 		}
2199 		if (ldx >= 0)
2200 		    dist = lsx - 1;
2201 		else {
2202 		    if (lsy + ldy < 0) {
2203 			lsy = -lsy;
2204 			ldy = -ldy;
2205 		    }
2206 		    temp = lsy - lsx;
2207 		    lsx += lsy;
2208 		    lsy = temp;
2209 		    temp = ldy - ldx;
2210 		    ldx += ldy;
2211 		    ldy = temp;
2212 		    dist = lsx - ldx * lsy / ldy;
2213 		    if (lsx + ldx < 0)
2214 			dist = MIN(ABS(dist), ABS(lsy - ldy * lsx / ldx));
2215 		    dist = dist / 2 - 3; /* 3? didn't bother to get the right value */
2216 		}
2217 		if (dist < CUTOFF + B_CLICKS / 2) {
2218 		    if (dist < B_CLICKS / 2 + DICLOSE)
2219 			distbound = LINSIZE;
2220 		    else
2221 			distbound = NCLLIN;
2222 		    for (j = 1; j < distbound; j++) {
2223 			if (dis[base + j] <= dist)
2224 			    continue;
2225 			k = dis[base + j];
2226 			n = j;
2227 			for (j++; j < distbound; j++)
2228 			    if (dis[base + j] > k) {
2229 				k = dis[base + j];
2230 				n = j;
2231 			    }
2232 			if (dis[base + 0] > dis[base + n])
2233 			    dis[base + 0] = dis[base + n];
2234 			if (lineno[base + n] == 65535) {
2235 			    size++; /* more saved lines */
2236 			    if (n == 1)
2237 				size++; /* first this block, for 65535 */
2238 			}
2239 			dis[base + n] = dist;
2240 			lineno[base + n] = i;
2241 			goto stored;
2242 		    }
2243 		}
2244 		if (dist < dis[base + 0])
2245 		    dis[base + 0] = dist;
2246 		if (dist < B_CLICKS / 2 + DICLOSE) {
2247 		    printf("Not enough space in line table. "
2248 			   "Fix allocation in walls.c\n");
2249 		    exit(1);
2250 		}
2251 	    stored:
2252 		; /* semicolon for ansi compatibility */
2253 	    }
2254 	}
2255     llist = (unsigned short *)ralloc(NULL, size * sizeof(unsigned short));
2256     lptr = llist;
2257     *lptr++ = 65535; /* All blocks with no lines stored point to this. */
2258     for (bx = 0; bx < mapx; bx++)
2259 	for (by = 0; by < mapy; by++) {
2260 	    base = (by * mapx + bx) * LINSIZE;
2261 	    k = bx + mapx * by;
2262 	    blockline[k].distance = dis[base + 0] - B_CLICKS / 2;
2263 	    if (lineno[base + 1] == 65535)
2264 		blockline[k].lines = llist;
2265 	    else {
2266 		blockline[k].lines = lptr;
2267 		for (j = 1; j < LINSIZE && lineno[base + j] != 65535; j++)
2268 		    *lptr++ = lineno[base + j];
2269 		*lptr++ = 65535;
2270 	    }
2271 	}
2272     free(lineno);
2273     free(dis);
2274 }
2275 
2276 /*
2277   cut and paste from #xpilot irc channel:
2278 
2279 <uau> the current values used for creating the wall tables are unoptimal
2280 <kps> what wall tables ?
2281 <kps> corners, etc ?
2282 <uau> especially after you made the "ship size" value bigger
2283       (for asteroids IIRC?) it can be bad
2284 <kps> do i make that ship size smaller or what do you suggest ?
2285 <kps> or how should this be done if one wanted to make it right ?
2286 <uau> the server creates a table for "nearby" lines for each 32x32 pixel block
2287 <uau> where "nearby" depends on how close closest lines are
2288 <uau> and then later creates a table of corners for the same distance+shipsize
2289 <kps> is it struct blockinfo *blockline; ?
2290 <uau> if shipsize is big then the latter can include lots of corners
2291 <uau> blockline.distance is the distance for which features are listed for
2292       that block
2293 <uau> blockline.lines contains all lines that are within that distance of
2294       the block (away from the block edges, whole inside has value 0)
2295 <uau> distance is measured as MIN of x,y distance
2296 <uau> blockline.points contains all corners (identified as a line starting
2297       from that point) within distance blockline.distance+C from that block
2298 <uau> where C was some constant which depends on ship size
2299 <uau> the heuristic for choosing a suitable blockline.distance could be
2300       improved
2301 <kps> that would help how ?
2302 <uau> now it always includes some lines IIRC; it would probably be better
2303       to choose the maximum distance such that no lines are included instead
2304       if that is big enough
2305 <uau> MAX_SHAPE_OFFSET being big is a separate problem
2306 <uau> now blockline.distance is chosen based on the lines only,
2307       blockline.points is then calculated afterwards to allow shapes to be
2308       moved "compatibly"
2309 <uau> if MAX_SHAPE_OFFSET is big that means a value for blockline.distance
2310       which is good for moving objects can create inefficiently big corner
2311       lists for in blockline.points
2312 <kps> maybe it says there is not enough corner space since my xp map to
2313       polygon conversion function creates many polygons where one could
2314       have only one
2315 <kps> so there is a lot of corners close to each other
2316 <uau> the problem place is one where there is a lot of free space around,
2317       so that blockline.distance can be chosen large
2318 <uau> but then there are suddenly a lot of corners inside
2319       distance+MAX_SHAPE_OFFSET
2320  */
2321 
Corner_init(void)2322 static void Corner_init(void)
2323 {
2324     int bx, by, cx, cy, dist, i;
2325     unsigned short *ptr, *temp;
2326     int block, size = mapx * mapy;
2327     int height, height2, width, by2;
2328 
2329 #define DISIZE 350
2330     temp = (unsigned short *)
2331 	ralloc(NULL, mapx * mapy * DISIZE * sizeof(unsigned short)); /* !@# */
2332     for (i = 0; i < mapx * mapy; i++)
2333 	temp[i * DISIZE] = 0;
2334     for (i = 0; i < num_lines; i++) {
2335 	bx = linet[i].start.cx;
2336 	by = linet[i].start.cy;
2337 	width = height = (2 * MAX_MOVE) / B_CLICKS + 7;
2338 	if (width >= mapx)
2339 	    width = mapx;
2340 	if (height >= mapy)
2341 	    height = mapy;
2342 	bx = (bx - MAX_MOVE) / B_CLICKS - 3;
2343 	by = (by - MAX_MOVE) / B_CLICKS - 3;
2344 	while (bx < 0)
2345 	    bx += mapx;
2346 	while (by < 0)
2347 	    by += mapy;
2348 	height2 = height;
2349 	by2 = by;
2350 	for (; width-- > 0; bx = bx == mapx - 1 ? 0 : bx + 1)
2351 	    for (by = by2, height = height2;
2352 		 height -- > 0;
2353 		 by = by == mapy - 1? 0 : by + 1) {
2354 		block = bx + mapx * by;
2355 		dist = blockline[block].distance
2356 		    + MAX_SHAPE_OFFSET + B_CLICKS / 2;
2357 		cx = bx * B_CLICKS + B_CLICKS / 2;
2358 		cy = by * B_CLICKS + B_CLICKS / 2;
2359 		if (ABS(CENTER_XCLICK(linet[i].start.cx - cx)) > dist)
2360 		    continue;
2361 		if (ABS(CENTER_YCLICK(linet[i].start.cy - cy)) > dist)
2362 		    continue;
2363 		temp[++temp[DISIZE * block] + DISIZE * block] = i;
2364 		size++;
2365 	    }
2366     }
2367     plist = (unsigned short *)ralloc(NULL, size * sizeof(unsigned short));
2368     ptr = plist;
2369     for (block = 0; block < mapx * mapy; block++) {
2370 	blockline[block].points = ptr;
2371 	i = temp[block * DISIZE];
2372 	if (i > DISIZE - 1) {
2373 	    warn("Not enough corner space in walls.c, add more.");
2374 	    exit(1);
2375 	}
2376 	while (i > 0) {
2377 	    *ptr++ = temp[block * DISIZE + i];
2378 	    i--;
2379 	}
2380 	*ptr++ = 65535;
2381     }
2382     free(temp);
2383 #undef DISIZE
2384 }
2385 
2386 
Ball_line_init(void)2387 void Ball_line_init(void)
2388 {
2389     int i;
2390     static clpos_t coords[MAX_SHIP_PTS];
2391 
2392     LIMIT(options.ballRadius, 0, BALL_RADIUS);
2393     ball_wire.num_points = MAX_SHIP_PTS;
2394     for (i = 0; i < MAX_SHIP_PTS; i++) {
2395 	ball_wire.pts[i] = coords + i;
2396 	coords[i].cx = (click_t) (cos(i * 2 * PI / MAX_SHIP_PTS)
2397 				  * options.ballRadius * CLICK);
2398 	coords[i].cy = (click_t) (sin(i * 2 * PI / MAX_SHIP_PTS)
2399 				  * options.ballRadius * CLICK);
2400     }
2401 
2402     return;
2403 }
2404 
2405 
Poly_to_lines(void)2406 static void Poly_to_lines(void)
2407 {
2408     int i, np, j, startx, starty, dx, dy, group, *styleptr, style;
2409     int *edges;
2410 
2411     num_lines = 0;
2412     for (i = 0; i < num_polys; i++) {
2413 	if (pdata[i].is_decor)
2414 	    continue;
2415 	group = pdata[i].group;
2416 	np = pdata[i].num_points;
2417 	styleptr = estyleptr + pdata[i].estyles_start;
2418 	style = pstyles[pdata[i].style].defedge_id;
2419 	dx = 0;
2420 	dy = 0;
2421 	startx = pdata[i].pos.cx;
2422 	starty = pdata[i].pos.cy;
2423 	edges = edgeptr + pdata[i].edges;
2424 	for (j = 0; j < np; j++) {
2425 	    if (j == *styleptr) {
2426 		styleptr++;
2427 		style = *styleptr++;
2428 	    }
2429 	    if (style == 0) {
2430 		dx += *edges++;
2431 		dy += *edges++;
2432 		continue;
2433 	    }
2434 	    if (!(num_lines % 2000))
2435 		linet = (struct bline *)
2436 		    ralloc(linet, (num_lines + 2000) * sizeof(struct bline));
2437 	    linet[num_lines].group = group;
2438 	    linet[num_lines].start.cx = TWRAP_XCLICK(startx + dx);
2439 	    linet[num_lines].start.cy = TWRAP_YCLICK(starty + dy);
2440 	    linet[num_lines].delta.cx = *edges;
2441 	    dx += *edges++;
2442 	    linet[num_lines++].delta.cy = *edges;
2443 	    dy += *edges++;
2444 	}
2445 	if (dx || dy) {
2446 	    warn("Broken map: Polygon %d (%d points) doesn't form a "
2447 		 "closed loop", i + 1, np);
2448 	    exit(1);
2449 	}
2450     }
2451     linet = (struct bline *)
2452 	ralloc(linet, (num_lines + S_LINES) * sizeof(struct bline));
2453     for (i = num_lines; i < num_lines + S_LINES; i++)
2454 	linet[i].group = 0; /* initialize for Shape_lines */
2455     return;
2456 }
2457 
Walls_init(void)2458 void Walls_init(void)
2459 {
2460     double x, y, l2;
2461     int i;
2462 
2463     mapx = (world->cwidth + B_MASK) >> B_SHIFT;
2464     mapy = (world->cheight + B_MASK) >> B_SHIFT;
2465 
2466     /* Break polygons down to a list of separate lines. */
2467     Poly_to_lines();
2468 
2469     /* For each B_CLICKS x B_CLICKS rectangle on the map, find a list of
2470      * nearby lines that need to be checked for collision when moving
2471      * in that area. */
2472     Distance_init();
2473 
2474     /* Like above, except list the map corners that could be hit by the
2475      * sides of a moving polygon shape. */
2476     Corner_init();
2477 
2478     Ball_line_init();
2479 
2480     /* Initialize the data structures used when determining whether a given
2481      * arbitrary point on the map is inside something. */
2482     Inside_init();
2483 
2484     /* Precalculate the .c and .s values used when calculating a bounce
2485      * from the line. */
2486     for (i = 0; i < num_lines; i++) {
2487 	x = linet[i].delta.cx;
2488 	y = linet[i].delta.cy;
2489 	l2 = (x*x + y*y);
2490 	linet[i].c = (x*x - y*y) / l2;
2491 	linet[i].s = 2*x*y / l2;
2492     }
2493 
2494     if (is_polygon_map) {
2495 	if (options.mapData) {
2496 	    warn("Option mapData is not supported on polygon maps.");
2497 	    warn("Server automatically creates block map from polygons.");
2498 	}
2499 	Create_blockmap_from_polygons();
2500     }
2501 }
2502 
2503 
Move_asteroid(object_t * obj)2504 static void Move_asteroid(object_t *obj)
2505 {
2506     move_t mv;
2507     struct collans ans;
2508     wireobject_t *asteroid = (wireobject_t *)obj;
2509 
2510     mv.delta.cx = FLOAT_TO_CLICK(obj->vel.x * timeStep);
2511     mv.delta.cy = FLOAT_TO_CLICK(obj->vel.y * timeStep);
2512     mv.obj = obj;
2513     obj->extmove.cx = mv.delta.cx;
2514     obj->extmove.cy = mv.delta.cy;
2515 
2516     /* asteroid can't phaze */
2517 
2518     mv.hitmask = NONBALL_BIT; /* hit everything for now */
2519 
2520     mv.start.cx = obj->pos.cx;
2521     mv.start.cy = obj->pos.cy;
2522     while (mv.delta.cx || mv.delta.cy) {
2523 	shape_t *shape = Asteroid_get_shape_by_size(asteroid->wire_size);
2524 
2525 	assert(shape);
2526 	Shape_move(&mv, shape, 0, &ans);
2527 	mv.start.cx = WRAP_XCLICK(mv.start.cx + ans.moved.cx);
2528 	mv.start.cy = WRAP_YCLICK(mv.start.cy + ans.moved.cy);
2529 	mv.delta.cx -= ans.moved.cx;
2530 	mv.delta.cy -= ans.moved.cy;
2531 	if (ans.line != -1) {
2532 	    if (SIDE(obj->vel.x, obj->vel.y, ans.line) < 0) {
2533 		if (!Bounce_object(obj, &mv, ans.line, ans.point))
2534 		    break;
2535 	    }
2536 	    else if (!Shape_away(&mv, shape, 0, ans.line, &ans)) {
2537 		if (SIDE(obj->vel.x, obj->vel.y, ans.line) < 0) {
2538 		    if (!Bounce_object(obj, &mv, ans.line, ans.point))
2539 			break;
2540 		}
2541 		else {
2542 		    /* This case could be handled better,
2543 		     * I'll write the code for that if this
2544 		     * happens too often. */
2545 		    mv.delta.cx = 0;
2546 		    mv.delta.cy = 0;
2547 		    obj->vel.x = 0;
2548 		    obj->vel.y = 0;
2549 		}
2550 	    }
2551 	}
2552     }
2553     Object_position_set_clvec(obj, mv.start);
2554     Cell_add_object(obj);
2555     return;
2556 }
2557 
2558 
Move_ball(object_t * obj)2559 static void Move_ball(object_t *obj)
2560 {
2561     move_t mv;
2562     struct collans ans;
2563     int owner;
2564 
2565     mv.delta.cx = FLOAT_TO_CLICK(obj->vel.x * timeStep);
2566     mv.delta.cy = FLOAT_TO_CLICK(obj->vel.y * timeStep);
2567     mv.obj = obj;
2568     obj->extmove.cx = mv.delta.cx;
2569     obj->extmove.cy = mv.delta.cy;
2570 
2571     if (obj->id != NO_ID
2572 	&& Player_is_phasing(Player_by_id(obj->id))) {
2573 	clpos_t pos;
2574 
2575 	pos.cx = obj->pos.cx + mv.delta.cx;
2576 	pos.cy = obj->pos.cy + mv.delta.cy;
2577 	pos = World_wrap_clpos(pos);
2578 	Object_position_set_clpos(obj, pos);
2579 	Cell_add_object(obj);
2580 	return;
2581     }
2582     owner = BALL_PTR(obj)->ball_owner;
2583     if (owner == NO_ID)
2584 	mv.hitmask = BALL_BIT | NOTEAM_BIT;
2585     else
2586 	mv.hitmask = BALL_BIT | HITMASK(Player_by_id(owner)->team);
2587     mv.start.cx = obj->pos.cx;
2588     mv.start.cy = obj->pos.cy;
2589     while (mv.delta.cx || mv.delta.cy) {
2590 	Shape_move(&mv, &ball_wire, 0, &ans);
2591 	mv.start.cx = WRAP_XCLICK(mv.start.cx + ans.moved.cx);
2592 	mv.start.cy = WRAP_YCLICK(mv.start.cy + ans.moved.cy);
2593 	mv.delta.cx -= ans.moved.cx;
2594 	mv.delta.cy -= ans.moved.cy;
2595 	if (ans.line != -1) {
2596 	    if (SIDE(obj->vel.x, obj->vel.y, ans.line) < 0) {
2597 		if (!Bounce_object(obj, &mv, ans.line, ans.point))
2598 		    break;
2599 	    }
2600 	    else if (!Shape_away(&mv, &ball_wire, 0, ans.line, &ans)) {
2601 		if (SIDE(obj->vel.x, obj->vel.y, ans.line) < 0) {
2602 		    if (!Bounce_object(obj, &mv, ans.line, ans.point))
2603 			break;
2604 		}
2605 		else {
2606 		    /* This case could be handled better,
2607 		     * I'll write the code for that if this
2608 		     * happens too often. */
2609 		    mv.delta.cx = 0;
2610 		    mv.delta.cy = 0;
2611 		    obj->vel.x = 0;
2612 		    obj->vel.y = 0;
2613 		}
2614 	    }
2615 	}
2616     }
2617     Object_position_set_clvec(obj, mv.start);
2618     Cell_add_object(obj);
2619     return;
2620 }
2621 
2622 
Move_object(object_t * obj)2623 void Move_object(object_t *obj)
2624 {
2625     int t;
2626     move_t mv;
2627     struct collans ans;
2628     int trycount = 5000;
2629     int team;            /* !@# should make TEAM_NOT_SET 0 */
2630 
2631     mv.obj = obj;
2632     Object_position_remember(obj);
2633 
2634     obj->collmode = 1;
2635 
2636     if (obj->type == OBJ_ASTEROID) {
2637 	Move_asteroid(obj);
2638 	return;
2639     }
2640 
2641 #if 1
2642     if (obj->type == OBJ_BALL) {
2643 	Move_ball(obj);
2644 	return;
2645     }
2646 #else
2647     if (obj->type == OBJ_BALL) {
2648 	if (obj->owner != NO_ID)
2649 	    team =  Player_by_id(obj->owner).team;
2650 	else
2651 	    team = TEAM_NOT_SET;
2652 	mv.hitmask = BALL_BIT;
2653     }
2654     else
2655 #endif
2656 	{
2657 	    mv.hitmask = NONBALL_BIT;
2658 	    team = obj->team;
2659 	}
2660     mv.hitmask |= HITMASK(team);
2661     mv.start.cx = obj->pos.cx;
2662     mv.start.cy = obj->pos.cy;
2663     mv.delta.cx = FLOAT_TO_CLICK(obj->vel.x * timeStep);
2664     mv.delta.cy = FLOAT_TO_CLICK(obj->vel.y * timeStep);
2665     obj->extmove.cx = mv.delta.cx;
2666     obj->extmove.cy = mv.delta.cy;
2667     while (mv.delta.cx || mv.delta.cy) {
2668 	if (!trycount--) {
2669 	    sprintf(msg, "COULDN'T MOVE OBJECT!!!! Type = %s, x = %d, y = %d. "
2670 		    "Object was DELETED. [*DEBUG*]",
2671 		    Object_typename(obj), mv.start.cx, mv.start.cy);
2672 	    warn(msg);
2673 	    Set_message(msg);
2674 	    obj->life = 0;
2675 	    return;
2676 	}
2677 	Move_point(&mv, &ans);
2678 	mv.delta.cx -= ans.moved.cx;
2679 	mv.delta.cy -= ans.moved.cy;
2680 	mv.start.cx = WRAP_XCLICK(mv.start.cx + ans.moved.cx);
2681 	mv.start.cy = WRAP_YCLICK(mv.start.cy + ans.moved.cy);
2682 	if (ans.line != -1) {
2683 	    if (SIDE(obj->vel.x, obj->vel.y, ans.line) < 0) {
2684 		if (!Bounce_object(obj, &mv, ans.line, 0))
2685 		    break;	/* Object destroyed by bounce */
2686 	    }
2687 	    else if ( (t = Away(&mv, ans.line)) != -1) {
2688 		if (!Clear_corner(&mv, obj, ans.line, t))
2689 		    break;	/* Object destroyed by bounces */
2690 	    }
2691 	}
2692     }
2693     Object_position_set_clvec(obj, mv.start);
2694     Cell_add_object(obj);
2695     return;
2696 }
2697 
2698 bool in_move_player = false;
2699 
Move_player(player_t * pl)2700 void Move_player(player_t *pl)
2701 {
2702     clpos_t  pos;
2703     move_t mv;
2704     struct collans ans;
2705     double fric = friction;
2706     vector_t oldv;
2707 
2708     if (!Player_is_alive(pl)) {
2709 	pos.cx = pl->pos.cx + FLOAT_TO_CLICK(pl->vel.x * timeStep);
2710 	pos.cy = pl->pos.cy + FLOAT_TO_CLICK(pl->vel.y * timeStep);
2711 	pos.cx = WRAP_XCLICK(pos.cx);
2712 	pos.cy = WRAP_YCLICK(pos.cy);
2713 	if (pos.cx != pl->pos.cx || pos.cy != pl->pos.cy) {
2714 	    Object_position_remember(OBJ_PTR(pl));
2715 	    Object_position_set_clpos(OBJ_PTR(pl), pos);
2716 	}
2717 	pl->velocity = VECTOR_LENGTH(pl->vel);
2718 	return;
2719     }
2720 
2721     /* Figure out which friction to use. */
2722     if (Player_is_phasing(pl))
2723 	fric = friction;
2724     else if (Num_frictionAreas() > 0) {
2725 	int group, i;
2726 
2727 	in_move_player = true;
2728 
2729 	group = is_inside(pl->pos.cx, pl->pos.cy, NONBALL_BIT, (object_t *)pl);
2730 	if (group != NO_GROUP) {
2731 	    for (i = 0; i < Num_frictionAreas(); i++) {
2732 		friction_area_t *fa = FrictionArea_by_index(i);
2733 
2734 		if (fa->group == group) {
2735 		    fric = fa->friction;
2736 		    break;
2737 		}
2738 
2739 	    }
2740 	}
2741 
2742 	in_move_player = false;
2743     }
2744 
2745     /* Velocity vector might change as a result of friction and options.coriolis. */
2746     if (options.coriolis != 0.0) {
2747 	oldv = pl->vel;
2748 	pl->vel.x = oldv.x * coriolisCosine + oldv.y * coriolisSine;
2749 	pl->vel.y = oldv.y * coriolisCosine - oldv.x * coriolisSine;
2750     }
2751 
2752     if (fric != 0.0) {
2753 	pl->vel.x *= (1.0 - fric);
2754 	pl->vel.y *= (1.0 - fric);
2755     }
2756 
2757     Object_position_remember(OBJ_PTR(pl));
2758 
2759     pl->collmode = 1;
2760 
2761     mv.obj = OBJ_PTR(pl);
2762     mv.delta.cx = FLOAT_TO_CLICK(pl->vel.x * timeStep);
2763     mv.delta.cy = FLOAT_TO_CLICK(pl->vel.y * timeStep);
2764 #if 0
2765     pl->extmove.cx = mv.delta.cx;
2766     pl->extmove.cy = mv.delta.cy;
2767 #endif
2768 
2769     if (Player_is_phasing(pl)) {
2770 	pos.cx = pl->pos.cx + mv.delta.cx;
2771 	pos.cy = pl->pos.cy + mv.delta.cy;
2772 	pos = World_wrap_clpos(pos);
2773 	Object_position_set_clpos(OBJ_PTR(pl), pos);
2774     } else {
2775 	mv.hitmask = NONBALL_BIT | HITMASK(pl->team);
2776 	mv.start.cx = pl->pos.cx;
2777 	mv.start.cy = pl->pos.cy;
2778 	while (mv.delta.cx || mv.delta.cy) {
2779 	    Shape_move(&mv, (shape_t *)pl->ship, pl->dir, &ans);
2780 	    mv.start.cx = WRAP_XCLICK(mv.start.cx + ans.moved.cx);
2781 	    mv.start.cy = WRAP_YCLICK(mv.start.cy + ans.moved.cy);
2782 	    mv.delta.cx -= ans.moved.cx;
2783 	    mv.delta.cy -= ans.moved.cy;
2784 	    if (ans.line != -1) {
2785 		if (SIDE(pl->vel.x, pl->vel.y, ans.line) < 0) {
2786 		    Bounce_player(pl, &mv, ans.line, ans.point);
2787 		    if (Player_is_killed(pl))
2788 			break;
2789 		} else if (!Shape_away(&mv, (shape_t *)pl->ship, pl->dir,
2790 				     ans.line, &ans)) {
2791 		    if (SIDE(pl->vel.x, pl->vel.y, ans.line) < 0) {
2792 			Bounce_player(pl, &mv, ans.line, ans.point);
2793 			if (Player_is_killed(pl))
2794 			    break;
2795 		    } else {
2796 			/* This case could be handled better,
2797 			 * I'll write the code for that if this
2798 			 * happens too often. */
2799 			/* At the moment it can be caused by illegal shipshapes
2800 			 * too, because they're not checked */
2801 #if 0
2802 			sprintf(msg, "%s got stuck (Illegal shape? "
2803 				"Shapes aren't checked) [*Notice*]", pl->name);
2804 			Set_message(msg);
2805 #endif
2806 			mv.delta.cx = 0;
2807 			mv.delta.cy = 0;
2808 			pl->vel.x = 0;
2809 			pl->vel.y = 0;
2810 		    }
2811 		}
2812 	    }
2813 	}
2814 	Object_position_set_clvec(OBJ_PTR(pl), mv.start);
2815     }
2816     pl->velocity = VECTOR_LENGTH(pl->vel);
2817     /* !@# Better than ignoring collisions after wall touch for players,
2818      * but might cause some erroneous hits */
2819     pl->extmove.cx = CENTER_XCLICK(pl->pos.cx - pl->prevpos.cx);
2820     pl->extmove.cy = CENTER_YCLICK(pl->pos.cy - pl->prevpos.cy);
2821     return;
2822 }
2823 
2824 
Turn_player(player_t * pl,bool push)2825 void Turn_player(player_t *pl, bool push)
2826 {
2827     int	new_dir = MOD2((int)(pl->float_dir + 0.5), RES);
2828     int next_dir, sign, group;
2829     hitmask_t hitmask;
2830     struct collans ans;
2831     double length, relturn;
2832     clpos_t p,p2;
2833 
2834     if (recOpt) {
2835 	if (record)
2836 	    *playback_data++ = new_dir;
2837 	else if (playback)
2838 	    new_dir = *playback_data++;
2839     }
2840 
2841     if (new_dir == pl->dir)
2842 	return;
2843 
2844     if (!Player_is_alive(pl)) {
2845 	/* kps - what is the point of this ??? */
2846 	/* virus - it prevents you from turning ship while ur dead ;) */
2847 	/* kps - why is pl->dir set to new_dir then ? */
2848 	pl->dir = new_dir;
2849 	return;
2850     }
2851 
2852     if (Player_is_phasing(pl)) {
2853 	pl->dir = new_dir;
2854 	return;
2855     }
2856 
2857     if (new_dir > pl->dir) {
2858     	if (new_dir - pl->dir <= RES + pl->dir - new_dir) {
2859 	    sign = 1;
2860 	    relturn = (new_dir - pl->dir)/(double)RES;
2861 	} else {
2862 	    sign = -1;
2863 	    relturn = (RES + pl->dir - new_dir)/(double)RES;
2864 	}
2865     } else {
2866     	if (pl->dir - new_dir <= RES + new_dir - pl->dir) {
2867 	    sign = -1;
2868 	    relturn = (pl->dir - new_dir)/(double)RES;
2869 	} else {
2870 	    sign = 1;
2871 	    relturn = (RES + new_dir - pl->dir)/(double)RES;
2872 	}
2873     }
2874 
2875     hitmask = NONBALL_BIT | HITMASK(pl->team);
2876 
2877     while (pl->dir != new_dir) {
2878 	next_dir = MOD2(pl->dir + sign, RES);
2879 	group = Shape_morph((shape_t *)pl->ship, pl->dir, (shape_t *)pl->ship,
2880 			    next_dir, hitmask, OBJ_PTR(pl),
2881 			    pl->pos.cx, pl->pos.cy, &ans);
2882 	if (group != NO_GROUP) {
2883     	    double /*fact, */velon, velot;
2884     	    double cl, sl;  	/* cosine and sine of line angle    	    */
2885     	    double cln, sln;	/* cosine and sine of line normal   	    */
2886     	    double pc, ps;	/* cosine and sine of the points    	    */
2887     	    double pdc, pds;	/* cosine and sine of the points direction  */
2888     	    double x, y, l/*, v*/;
2889 	    double power = pl->power;
2890 	    int a = (BIT(pl->used, USES_EMERGENCY_THRUST)
2891 		     ? MAX_AFTERBURNER
2892 		     : pl->item[ITEM_AFTERBURNER]);
2893 	    double inert = pl->mass;
2894 	    double cx,cy;
2895 	    static move_t move;
2896 
2897 	    Player_set_float_dir(pl, (double)pl->dir);
2898 
2899 	    if (!push)
2900 		break;
2901 
2902 	    length = 0;
2903 	    l = 0.0;
2904 
2905     	    p = Ship_get_point_clpos((shipshape_t *)pl->ship, ans.point, pl->dir);
2906     	    p2 = Ship_get_point_clpos((shipshape_t *)pl->ship, (ans.point + 1)%(((shape_t *)pl->ship)->num_points), pl->dir);
2907 
2908 	    /* x,y defines the direction of the line that prevented turning */
2909 	    if (ans.line != -1) {
2910 		length = Wrap_length(linet[ans.line].delta.cx,
2911 				     linet[ans.line].delta.cy);
2912     	    	x = linet[ans.line].delta.cx;
2913     	    	y = linet[ans.line].delta.cy;
2914 	    } else {
2915     	    	x = p2.cx - p.cx;
2916     	    	y = p2.cy - p.cy;
2917 	    }
2918 
2919     	    l = sqrt(x*x + y*y);
2920 
2921 	    if (l==0.0) {
2922 	    	warn("A zero length line somehow prevented turning!");
2923 		break;
2924 	    }
2925 
2926 	    /* cosine and sine of the line's tangent */
2927     	    cl = x / l;
2928     	    sl = y / l;
2929 
2930 	    /* cosine and sine of the line's normal */
2931     	    cln = sl;
2932     	    sln = -cl;
2933 
2934 	    /* pc and ps defines cosine and sine of the point vector */
2935 	    if (ans.line != -1) {
2936     	    	l = sqrt(p.cx * p.cx + p.cy * p.cy);
2937 	    	pc = p.cx / l;
2938     	    	ps = p.cy / l;
2939 	    } else {
2940 	    	cx = CENTER_XCLICK(linet[ans.moved.cx].start.cx - pl->pos.cx);
2941 	    	cy = CENTER_YCLICK(linet[ans.moved.cx].start.cy - pl->pos.cy);
2942     	    	l = sqrt(cx * cx + cy * cy);
2943     	    	pc = cx / l;
2944     	    	ps = cy / l;
2945 	    }
2946 
2947 	    /*
2948 	     * pdc pds defines cosine and sine of the points direction
2949 	     * (if it is a wall point, its the direction of the spot
2950 	     * of the shipline that hit it)
2951 	     */
2952     	    if (sign == 1) {
2953     	    	pdc = -ps;
2954     	    	pds = pc;
2955     	    } else {
2956     	    	pdc = ps;
2957     	    	pds = -pc;
2958     	    }
2959 
2960 	    /*
2961 	     * Shipshapes can be hit from the "inside", fixing this
2962 	     */
2963 	    if ((ans.line == -1) && ((pdc * cln + pds * sln) > 0)) {
2964 	    	cln = -cln;
2965 		sln = -sln;
2966 	    }
2967 
2968 	    /*
2969 	     * Push strength depends on burner power, ship mass and
2970 	     * constantSpeed
2971 	     */
2972 	    if (a) {
2973 		power = AFTER_BURN_POWER(power, a);
2974 	    }
2975 
2976      	    velon = (power / inert)*(1 + options.constantSpeed);
2977 
2978 	    /*
2979 	     * If we are approaching the wall that stops our turn we
2980 	     * need to bounce the ship. (we do this with no movement
2981 	     * since we just want to bounce it, not move it)
2982 	     */
2983     	    if ((pl->vel.x*cln + pl->vel.y*sln) < 0) {
2984 	    	move.delta.cx = 0;
2985 	    	move.delta.cy = 0;
2986     	    	if (ans.line != -1)
2987     	    	    Bounce_player(pl, &move, ans.line, 0);
2988 		else
2989     	    	    Bounce_player(pl, &move, ans.point, 0);
2990 	    }
2991 
2992 	    velot = velon * options.playerWallFriction * ((-pdc) * cl + (-pds) * sl);
2993 
2994 	    /*
2995 	     * First store away the old speed vector, then do the turnpush move
2996 	     * and then restore speed
2997 	     */
2998 	    x = pl->vel.x;
2999 	    y = pl->vel.y;
3000      	    pl->vel.x = velon * cln + options.turnGrip * velot * cl;
3001     	    pl->vel.y = velon * sln + options.turnGrip * velot * sl;
3002 	    Move_player(pl);
3003  	    pl->vel.x = x + options.turnPushPersistence * pl->vel.x;
3004 	    pl->vel.y = y + options.turnPushPersistence * pl->vel.y;
3005 
3006 	    break;
3007 	}
3008 	pl->dir = next_dir;
3009     }
3010 
3011     return;
3012 }
3013