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