1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "ipdisp.h"
20
21 #include "collide.h"
22 #include "globdata.h"
23 #include "init.h"
24 #include "races.h"
25 #include "process.h"
26 #include "grpinfo.h"
27 #include "encount.h"
28 // for EncounterGroup, EncounterRace
29 #include "libs/mathlib.h"
30
31
32 void
NotifyOthers(COUNT which_race,BYTE target_loc)33 NotifyOthers (COUNT which_race, BYTE target_loc)
34 {
35 HSHIPFRAG hGroup, hNextGroup;
36
37 // NOTE: "Others" includes the group causing the notification too.
38
39 for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
40 hGroup; hGroup = hNextGroup)
41 {
42 IP_GROUP *GroupPtr;
43
44 GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
45 hNextGroup = _GetSuccLink (GroupPtr);
46
47 if (GroupPtr->race_id != which_race)
48 {
49 UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
50 continue;
51 }
52
53 if (target_loc == IPNL_INTERCEPT_PLAYER)
54 {
55 GroupPtr->task &= ~IGNORE_FLAGSHIP;
56 // XXX: orbit_pos is abused here to store the previous
57 // group destination, before the intercept task.
58 // Returned to dest_loc below.
59 GroupPtr->orbit_pos = GroupPtr->dest_loc;
60 GroupPtr->dest_loc = IPNL_INTERCEPT_PLAYER;
61 }
62 else if (target_loc == IPNL_ALL_CLEAR)
63 {
64 GroupPtr->task |= IGNORE_FLAGSHIP;
65
66 if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
67 { // The group was intercepting, so send it back where it came
68 // XXX: orbit_pos was abused to store the previous
69 // group destination, before the intercept task.
70 GroupPtr->dest_loc = GroupPtr->orbit_pos;
71 GroupPtr->orbit_pos = NORMALIZE_FACING (TFB_Random ());
72 #ifdef OLD
73 GroupPtr->dest_loc = (BYTE)(((COUNT)TFB_Random ()
74 % pSolarSysState->SunDesc[0].NumPlanets) + 1);
75 #endif /* OLD */
76 }
77 // If the group wasn't intercepting, it will just continue
78 // going about its business.
79
80 if (!(GroupPtr->task & REFORM_GROUP))
81 {
82 if ((GroupPtr->task & ~IGNORE_FLAGSHIP) != EXPLORE)
83 GroupPtr->group_counter = 0;
84 else
85 GroupPtr->group_counter = ((COUNT) TFB_Random ()
86 % MAX_REVOLUTIONS) << FACING_SHIFT;
87 }
88 }
89 else
90 { // Send the group to the location.
91 // XXX: There is currently no use of such notify that I know of.
92 GroupPtr->task |= IGNORE_FLAGSHIP;
93 GroupPtr->dest_loc = target_loc;
94 }
95
96 UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
97 }
98 }
99
100 static SIZE
zoomRadiusForLocation(BYTE location)101 zoomRadiusForLocation (BYTE location)
102 {
103 if (location == 0)
104 { // In outer system view; use current zoom radius
105 return pSolarSysState->SunDesc[0].radius;
106 }
107 else
108 { // In inner system view; always max zoom
109 return MAX_ZOOM_RADIUS;
110 }
111 }
112
113 static inline void
adjustDeltaVforZoom(SIZE zoom,SIZE * dx,SIZE * dy)114 adjustDeltaVforZoom (SIZE zoom, SIZE *dx, SIZE *dy)
115 {
116 if (zoom == MIN_ZOOM_RADIUS)
117 {
118 *dx >>= 2;
119 *dy >>= 2;
120 }
121 else if (zoom < MAX_ZOOM_RADIUS)
122 {
123 *dx >>= 1;
124 *dy >>= 1;
125 }
126 }
127
128 static BYTE
getFlagshipLocation(void)129 getFlagshipLocation (void)
130 {
131 if (!playerInInnerSystem ())
132 return 0;
133 else
134 return 1 + planetIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
135 }
136
137 static void
ip_group_preprocess(ELEMENT * ElementPtr)138 ip_group_preprocess (ELEMENT *ElementPtr)
139 {
140 #define TRACK_WAIT 5
141 BYTE task;
142 BYTE target_loc, group_loc, flagship_loc;
143 SIZE radius;
144 POINT dest_pt;
145 SIZE vdx, vdy;
146 ELEMENT *EPtr;
147 IP_GROUP *GroupPtr;
148
149 EPtr = ElementPtr;
150 EPtr->state_flags &= ~(DISAPPEARING | NONSOLID); // "I'm not quite dead"
151 ++EPtr->life_span; // so that it will 'die' again next time
152
153 GetElementStarShip (EPtr, &GroupPtr);
154 group_loc = GroupPtr->sys_loc; // save old location
155 DisplayArray[EPtr->PrimIndex].Object.Point = GroupPtr->loc;
156
157 radius = zoomRadiusForLocation (group_loc);
158 dest_pt = locationToDisplay (GroupPtr->loc, radius);
159 EPtr->current.location.x = DISPLAY_TO_WORLD (dest_pt.x)
160 + (COORD)(LOG_SPACE_WIDTH >> 1)
161 - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
162 EPtr->current.location.y = DISPLAY_TO_WORLD (dest_pt.y)
163 + (COORD)(LOG_SPACE_HEIGHT >> 1)
164 - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
165
166 InitIntersectStartPoint (EPtr);
167
168 flagship_loc = getFlagshipLocation ();
169
170 task = GroupPtr->task;
171
172 if ((task & REFORM_GROUP) && --GroupPtr->group_counter == 0)
173 { // Finished reforming the group
174 task &= ~REFORM_GROUP;
175 GroupPtr->task = task;
176 if ((task & ~IGNORE_FLAGSHIP) != EXPLORE)
177 GroupPtr->group_counter = 0;
178 else
179 GroupPtr->group_counter = ((COUNT)TFB_Random ()
180 % MAX_REVOLUTIONS) << FACING_SHIFT;
181 }
182
183 // If fleeing *and* ignoring flagship
184 if ((task & ~(IGNORE_FLAGSHIP | REFORM_GROUP)) == FLEE
185 && (task & IGNORE_FLAGSHIP))
186 { // Make fleeing groups non-collidable
187 EPtr->state_flags |= NONSOLID;
188 }
189
190 target_loc = GroupPtr->dest_loc;
191 if (!(task & (IGNORE_FLAGSHIP | REFORM_GROUP)))
192 {
193 if (target_loc == IPNL_INTERCEPT_PLAYER && task != FLEE)
194 {
195 /* if intercepting flagship */
196 target_loc = flagship_loc;
197 if (EPtr->thrust_wait > TRACK_WAIT)
198 {
199 EPtr->thrust_wait = 0;
200 ZeroVelocityComponents (&EPtr->velocity);
201 }
202 }
203 else if (group_loc == flagship_loc)
204 {
205 long detect_dist;
206
207 detect_dist = 1200;
208 if (group_loc != 0) /* if in planetary views */
209 {
210 detect_dist *= (MAX_ZOOM_RADIUS / MIN_ZOOM_RADIUS);
211 if (GroupPtr->race_id == URQUAN_DRONE_SHIP)
212 detect_dist <<= 1;
213 }
214 vdx = GLOBAL (ip_location.x) - GroupPtr->loc.x;
215 vdy = GLOBAL (ip_location.y) - GroupPtr->loc.y;
216 if ((long)vdx * vdx
217 + (long)vdy * vdy < (long)detect_dist * detect_dist)
218 {
219 EPtr->thrust_wait = 0;
220 ZeroVelocityComponents (&EPtr->velocity);
221
222 NotifyOthers (GroupPtr->race_id, IPNL_INTERCEPT_PLAYER);
223 task = GroupPtr->task;
224 target_loc = GroupPtr->dest_loc;
225 if (target_loc == IPNL_INTERCEPT_PLAYER)
226 target_loc = flagship_loc;
227 }
228 }
229 }
230
231 GetCurrentVelocityComponents (&EPtr->velocity, &vdx, &vdy);
232
233 task &= ~IGNORE_FLAGSHIP;
234 #ifdef NEVER
235 if (task <= FLEE || (task == ON_STATION && GroupPtr->dest_loc == 0))
236 #else
237 if (task <= ON_STATION)
238 #endif /* NEVER */
239 {
240 BOOLEAN Transition;
241 SIZE dx, dy;
242 SIZE delta_x, delta_y;
243 COUNT angle;
244
245 Transition = FALSE;
246 if (task == FLEE)
247 {
248 dest_pt.x = GroupPtr->loc.x << 1;
249 dest_pt.y = GroupPtr->loc.y << 1;
250 }
251 else if (((task != ON_STATION ||
252 GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
253 && group_loc == target_loc)
254 || (task == ON_STATION &&
255 GroupPtr->dest_loc != IPNL_INTERCEPT_PLAYER
256 && group_loc == 0))
257 {
258 if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
259 dest_pt = GLOBAL (ip_location);
260 else
261 {
262 COUNT orbit_dist;
263 POINT org;
264
265 if (task != ON_STATION)
266 {
267 orbit_dist = ORBIT_RADIUS;
268 org.x = org.y = 0;
269 }
270 else
271 {
272 orbit_dist = STATION_RADIUS;
273 org = planetOuterLocation (target_loc - 1);
274 }
275
276 angle = FACING_TO_ANGLE (GroupPtr->orbit_pos + 1);
277 dest_pt.x = org.x + COSINE (angle, orbit_dist);
278 dest_pt.y = org.y + SINE (angle, orbit_dist);
279 if (GroupPtr->loc.x == dest_pt.x
280 && GroupPtr->loc.y == dest_pt.y)
281 {
282 BYTE next_loc;
283
284 GroupPtr->orbit_pos = NORMALIZE_FACING (
285 ANGLE_TO_FACING (angle));
286 angle += FACING_TO_ANGLE (1);
287 dest_pt.x = org.x + COSINE (angle, orbit_dist);
288 dest_pt.y = org.y + SINE (angle, orbit_dist);
289
290 EPtr->thrust_wait = (BYTE)~0;
291 if (GroupPtr->group_counter)
292 --GroupPtr->group_counter;
293 else if (task == EXPLORE
294 && (next_loc = (BYTE)(((COUNT)TFB_Random ()
295 % pSolarSysState->SunDesc[0].NumPlanets)
296 + 1)) != target_loc)
297 {
298 EPtr->thrust_wait = 0;
299 target_loc = next_loc;
300 GroupPtr->dest_loc = next_loc;
301 }
302 }
303 }
304 }
305 else if (group_loc == 0)
306 {
307 if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
308 dest_pt = pSolarSysState->SunDesc[0].location;
309 else
310 dest_pt = planetOuterLocation (target_loc - 1);
311 }
312 else
313 {
314 if (task == ON_STATION)
315 target_loc = 0;
316
317 dest_pt.x = GroupPtr->loc.x << 1;
318 dest_pt.y = GroupPtr->loc.y << 1;
319 }
320
321 delta_x = dest_pt.x - GroupPtr->loc.x;
322 delta_y = dest_pt.y - GroupPtr->loc.y;
323 angle = ARCTAN (delta_x, delta_y);
324
325 if (EPtr->thrust_wait && EPtr->thrust_wait != (BYTE)~0)
326 --EPtr->thrust_wait;
327 else if ((vdx == 0 && vdy == 0)
328 || angle != GetVelocityTravelAngle (&EPtr->velocity))
329 {
330 SIZE speed;
331
332 if (EPtr->thrust_wait &&
333 GroupPtr->dest_loc != IPNL_INTERCEPT_PLAYER)
334 {
335 #define ORBIT_SPEED 60
336 speed = ORBIT_SPEED;
337 if (task == ON_STATION)
338 speed >>= 1;
339 }
340 else
341 {
342 SIZE RaceIPSpeed[] =
343 {
344 RACE_IP_SPEED
345 };
346
347 speed = RaceIPSpeed[GroupPtr->race_id];
348 EPtr->thrust_wait = TRACK_WAIT;
349 }
350
351 vdx = COSINE (angle, speed);
352 vdy = SINE (angle, speed);
353 SetVelocityComponents (&EPtr->velocity, vdx, vdy);
354 }
355
356 dx = vdx;
357 dy = vdy;
358 if (group_loc == target_loc)
359 {
360 if (target_loc == 0)
361 {
362 if (task == FLEE)
363 goto CheckGetAway;
364 }
365 else if (target_loc == GroupPtr->dest_loc)
366 {
367 PartialRevolution:
368 if ((long)((COUNT)(dx * dx) + (COUNT)(dy * dy))
369 >= (long)delta_x * delta_x + (long)delta_y * delta_y)
370 {
371 GroupPtr->loc = dest_pt;
372 vdx = 0;
373 vdy = 0;
374 ZeroVelocityComponents (&EPtr->velocity);
375 }
376 }
377 }
378 else
379 {
380 if (group_loc == 0)
381 { // In outer system
382 adjustDeltaVforZoom (radius, &dx, &dy);
383
384 if (task == ON_STATION && GroupPtr->dest_loc)
385 goto PartialRevolution;
386 else if ((long)((COUNT)(dx * dx) + (COUNT)(dy * dy))
387 >= (long)delta_x * delta_x + (long)delta_y * delta_y)
388 Transition = TRUE;
389 }
390 else
391 { // In inner system; also leaving outer CheckGetAway hack
392 CheckGetAway:
393 dest_pt = locationToDisplay (GroupPtr->loc, radius);
394 if (dest_pt.x < 0
395 || dest_pt.x >= SIS_SCREEN_WIDTH
396 || dest_pt.y < 0
397 || dest_pt.y >= SIS_SCREEN_HEIGHT)
398 Transition = TRUE;
399 }
400
401 if (Transition)
402 {
403 /* no collisions during transition */
404 EPtr->state_flags |= NONSOLID;
405
406 vdx = 0;
407 vdy = 0;
408 ZeroVelocityComponents (&EPtr->velocity);
409 if (group_loc != 0)
410 {
411 GroupPtr->loc = planetOuterLocation (group_loc - 1);
412 group_loc = 0;
413 GroupPtr->sys_loc = 0;
414 }
415 else if (target_loc == 0)
416 {
417 /* Group completely left the star system */
418 EPtr->life_span = 0;
419 EPtr->state_flags |= DISAPPEARING | NONSOLID;
420 GroupPtr->in_system = 0;
421 return;
422 }
423 else
424 {
425 POINT entryPt;
426
427 if (target_loc == GroupPtr->dest_loc)
428 {
429 GroupPtr->orbit_pos = NORMALIZE_FACING (
430 ANGLE_TO_FACING (angle + HALF_CIRCLE));
431 GroupPtr->group_counter =
432 ((COUNT)TFB_Random () % MAX_REVOLUTIONS)
433 << FACING_SHIFT;
434 }
435 // The group enters inner system exactly on the edge of a
436 // circle with radius = 9/16 * window-dim, which is
437 // different from how the flagship enters, but similar
438 // in the way that the group will never show up in any
439 // of the corners.
440 entryPt.x = (SIS_SCREEN_WIDTH >> 1) - COSINE (angle,
441 SIS_SCREEN_WIDTH * 9 / 16);
442 entryPt.y = (SIS_SCREEN_HEIGHT >> 1) - SINE (angle,
443 SIS_SCREEN_HEIGHT * 9 / 16);
444 GroupPtr->loc = displayToLocation (entryPt,
445 MAX_ZOOM_RADIUS);
446 group_loc = target_loc;
447 GroupPtr->sys_loc = target_loc;
448 }
449 }
450 }
451 }
452
453 radius = zoomRadiusForLocation (group_loc);
454 adjustDeltaVforZoom (radius, &vdx, &vdy);
455 GroupPtr->loc.x += vdx;
456 GroupPtr->loc.y += vdy;
457
458 dest_pt = locationToDisplay (GroupPtr->loc, radius);
459 EPtr->next.location.x = DISPLAY_TO_WORLD (dest_pt.x)
460 + (COORD)(LOG_SPACE_WIDTH >> 1)
461 - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
462 EPtr->next.location.y = DISPLAY_TO_WORLD (dest_pt.y)
463 + (COORD)(LOG_SPACE_HEIGHT >> 1)
464 - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
465
466 // Don't draw the group if it's not at flagship location,
467 // or flash the group while it's reforming
468 if (group_loc != flagship_loc
469 || ((task & REFORM_GROUP)
470 && (GroupPtr->group_counter & 1)))
471 {
472 SetPrimType (&DisplayArray[EPtr->PrimIndex], NO_PRIM);
473 EPtr->state_flags |= NONSOLID;
474 }
475 else
476 {
477 SetPrimType (&DisplayArray[EPtr->PrimIndex], STAMP_PRIM);
478 if (task & REFORM_GROUP)
479 EPtr->state_flags |= NONSOLID;
480 }
481
482 EPtr->state_flags |= CHANGING;
483 }
484
485 static void
flag_ship_collision(ELEMENT * ElementPtr0,POINT * pPt0,ELEMENT * ElementPtr1,POINT * pPt1)486 flag_ship_collision (ELEMENT *ElementPtr0, POINT *pPt0,
487 ELEMENT *ElementPtr1, POINT *pPt1)
488 {
489 if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
490 return; // ignore the rest of the collisions
491
492 if (!(ElementPtr1->state_flags & COLLISION))
493 { // The other element's collision has not been processed yet
494 // Defer starting the encounter until it is.
495 ElementPtr0->state_flags |= COLLISION | NONSOLID;
496 }
497 else
498 { // Both element's collisions have now been processed
499 ElementPtr1->state_flags &= ~COLLISION;
500 GLOBAL (CurrentActivity) |= START_ENCOUNTER;
501 }
502 (void) pPt0; /* Satisfying compiler (unused parameter) */
503 (void) pPt1; /* Satisfying compiler (unused parameter) */
504 }
505
506 static void
ip_group_collision(ELEMENT * ElementPtr0,POINT * pPt0,ELEMENT * ElementPtr1,POINT * pPt1)507 ip_group_collision (ELEMENT *ElementPtr0, POINT *pPt0,
508 ELEMENT *ElementPtr1, POINT *pPt1)
509 {
510 IP_GROUP *GroupPtr;
511 void *OtherPtr;
512
513 if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
514 return; // ignore the rest of the collisions
515
516 GetElementStarShip (ElementPtr0, &GroupPtr);
517 GetElementStarShip (ElementPtr1, &OtherPtr);
518 if (OtherPtr)
519 { // Collision with another group
520 // Prevent the groups from coalescing into a single ship icon
521 if ((ElementPtr0->state_flags & COLLISION)
522 || (ElementPtr1->current.location.x == ElementPtr1->next.location.x
523 && ElementPtr1->current.location.y == ElementPtr1->next.location.y))
524 {
525 ElementPtr0->state_flags &= ~COLLISION;
526 }
527 else
528 {
529 ElementPtr1->state_flags |= COLLISION;
530
531 GroupPtr->loc = DisplayArray[ElementPtr0->PrimIndex].Object.Point;
532 ElementPtr0->next.location = ElementPtr0->current.location;
533 InitIntersectEndPoint (ElementPtr0);
534 }
535 }
536 else // if (!OtherPtr)
537 { // Collision with a flagship
538 EncounterGroup = GroupPtr->group_id;
539
540 GroupPtr->task |= REFORM_GROUP;
541 GroupPtr->group_counter = 100;
542 // Send "all clear" for the time being. After the encounter, if
543 // the player battles the group, the "intercept" notify will be
544 // resent.
545 NotifyOthers (GroupPtr->race_id, IPNL_ALL_CLEAR);
546
547 if (!(ElementPtr1->state_flags & COLLISION))
548 { // The other element's collision has not been processed yet
549 // Defer starting the encounter until it is.
550 ElementPtr0->state_flags |= COLLISION | NONSOLID;
551 }
552 else
553 { // Both element's collisions have now been processed
554 ElementPtr1->state_flags &= ~COLLISION;
555 GLOBAL (CurrentActivity) |= START_ENCOUNTER;
556 }
557 }
558 (void) pPt0; /* Satisfying compiler (unused parameter) */
559 (void) pPt1; /* Satisfying compiler (unused parameter) */
560 }
561
562 static void
spawn_ip_group(IP_GROUP * GroupPtr)563 spawn_ip_group (IP_GROUP *GroupPtr)
564 {
565 HELEMENT hIPSHIPElement;
566
567 hIPSHIPElement = AllocElement ();
568 if (hIPSHIPElement)
569 {
570 ELEMENT *IPSHIPElementPtr;
571
572 LockElement (hIPSHIPElement, &IPSHIPElementPtr);
573 // Must have mass_points for collisions to work
574 IPSHIPElementPtr->mass_points = 1;
575 IPSHIPElementPtr->hit_points = 1;
576 IPSHIPElementPtr->state_flags =
577 CHANGING | FINITE_LIFE | IGNORE_VELOCITY;
578
579 SetPrimType (&DisplayArray[IPSHIPElementPtr->PrimIndex], STAMP_PRIM);
580 // XXX: Hack: farray points to FRAME[3] and given FRAME
581 IPSHIPElementPtr->current.image.farray = &GroupPtr->melee_icon;
582 IPSHIPElementPtr->current.image.frame = SetAbsFrameIndex (
583 GroupPtr->melee_icon, 1);
584 /* preprocessing has a side effect
585 * we wish to avoid. So death_func
586 * is used instead, but will achieve
587 * same result without the side
588 * effect (InitIntersectFrame)
589 */
590 IPSHIPElementPtr->death_func = ip_group_preprocess;
591 IPSHIPElementPtr->collision_func = ip_group_collision;
592
593 {
594 SIZE radius;
595 POINT pt;
596
597 radius = zoomRadiusForLocation (GroupPtr->sys_loc);
598 pt = locationToDisplay (GroupPtr->loc, radius);
599
600 IPSHIPElementPtr->current.location.x =
601 DISPLAY_TO_WORLD (pt.x)
602 + (COORD)(LOG_SPACE_WIDTH >> 1)
603 - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
604 IPSHIPElementPtr->current.location.y =
605 DISPLAY_TO_WORLD (pt.y)
606 + (COORD)(LOG_SPACE_HEIGHT >> 1)
607 - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
608 }
609
610 SetElementStarShip (IPSHIPElementPtr, GroupPtr);
611
612 SetUpElement (IPSHIPElementPtr);
613 IPSHIPElementPtr->IntersectControl.IntersectStamp.frame =
614 DecFrameIndex (stars_in_space);
615
616 UnlockElement (hIPSHIPElement);
617
618 PutElement (hIPSHIPElement);
619 }
620 }
621
622 #define FLIP_WAIT 42
623
624 static void
flag_ship_preprocess(ELEMENT * ElementPtr)625 flag_ship_preprocess (ELEMENT *ElementPtr)
626 {
627 if (--ElementPtr->thrust_wait == 0)
628 /* juggle list after flagship */
629 {
630 HELEMENT hSuccElement;
631
632 if ((hSuccElement = GetSuccElement (ElementPtr))
633 && hSuccElement != GetTailElement ())
634 {
635 HELEMENT hPredElement;
636 ELEMENT *TailPtr;
637
638 LockElement (GetTailElement (), &TailPtr);
639 hPredElement = _GetPredLink (TailPtr);
640 UnlockElement (GetTailElement ());
641
642 RemoveElement (hSuccElement);
643 PutElement (hSuccElement);
644 }
645
646 ElementPtr->thrust_wait = FLIP_WAIT;
647 }
648
649 {
650 BYTE flagship_loc, ec;
651 SIZE vdx, vdy, radius;
652 POINT pt;
653
654 GetCurrentVelocityComponents (&GLOBAL (velocity), &vdx, &vdy);
655
656 flagship_loc = getFlagshipLocation ();
657 radius = zoomRadiusForLocation (flagship_loc);
658 adjustDeltaVforZoom (radius, &vdx, &vdy);
659
660 pt = locationToDisplay (GLOBAL (ip_location), radius);
661 ElementPtr->current.location.x = DISPLAY_TO_WORLD (pt.x)
662 + (COORD)(LOG_SPACE_WIDTH >> 1)
663 - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
664 ElementPtr->current.location.y = DISPLAY_TO_WORLD (pt.y)
665 + (COORD)(LOG_SPACE_HEIGHT >> 1)
666 - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
667 InitIntersectStartPoint (ElementPtr);
668
669 GLOBAL (ip_location.x) += vdx;
670 GLOBAL (ip_location.y) += vdy;
671
672 pt = locationToDisplay (GLOBAL (ip_location), radius);
673 ElementPtr->next.location.x = DISPLAY_TO_WORLD (pt.x)
674 + (COORD)(LOG_SPACE_WIDTH >> 1)
675 - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
676 ElementPtr->next.location.y = DISPLAY_TO_WORLD (pt.y)
677 + (COORD)(LOG_SPACE_HEIGHT >> 1)
678 - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
679
680 GLOBAL (ShipStamp.origin) = pt;
681 ElementPtr->next.image.frame = GLOBAL (ShipStamp.frame);
682
683 if (ElementPtr->sys_loc == flagship_loc)
684 {
685 if (ElementPtr->state_flags & NONSOLID)
686 ElementPtr->state_flags &= ~NONSOLID;
687 }
688 else /* no collisions during transition */
689 {
690 ElementPtr->state_flags |= NONSOLID;
691 ElementPtr->sys_loc = flagship_loc;
692 }
693
694 if ((ec = GET_GAME_STATE (ESCAPE_COUNTER))
695 && !(GLOBAL (CurrentActivity) & START_ENCOUNTER))
696 {
697 ElementPtr->state_flags |= NONSOLID;
698
699 --ec;
700 SET_GAME_STATE (ESCAPE_COUNTER, ec);
701 }
702
703 ElementPtr->state_flags |= CHANGING;
704 }
705 }
706
707 static void
spawn_flag_ship(void)708 spawn_flag_ship (void)
709 {
710 HELEMENT hFlagShipElement;
711
712 hFlagShipElement = AllocElement ();
713 if (hFlagShipElement)
714 {
715 ELEMENT *FlagShipElementPtr;
716
717 LockElement (hFlagShipElement, &FlagShipElementPtr);
718 FlagShipElementPtr->hit_points = 1;
719 // Must have mass_points for collisions to work
720 FlagShipElementPtr->mass_points = 1;
721 FlagShipElementPtr->sys_loc = getFlagshipLocation ();
722 FlagShipElementPtr->state_flags = APPEARING | IGNORE_VELOCITY;
723 if (GET_GAME_STATE (ESCAPE_COUNTER))
724 FlagShipElementPtr->state_flags |= NONSOLID;
725 FlagShipElementPtr->life_span = NORMAL_LIFE;
726 FlagShipElementPtr->thrust_wait = FLIP_WAIT;
727 SetPrimType (&DisplayArray[FlagShipElementPtr->PrimIndex], STAMP_PRIM);
728 FlagShipElementPtr->current.image.farray =
729 &GLOBAL (ShipStamp.frame);
730 FlagShipElementPtr->current.image.frame =
731 GLOBAL (ShipStamp.frame);
732 FlagShipElementPtr->preprocess_func = flag_ship_preprocess;
733 FlagShipElementPtr->collision_func = flag_ship_collision;
734
735 FlagShipElementPtr->current.location.x =
736 DISPLAY_TO_WORLD (GLOBAL (ShipStamp.origin.x))
737 + (COORD)(LOG_SPACE_WIDTH >> 1)
738 - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
739 FlagShipElementPtr->current.location.y =
740 DISPLAY_TO_WORLD (GLOBAL (ShipStamp.origin.y))
741 + (COORD)(LOG_SPACE_HEIGHT >> 1)
742 - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
743
744 UnlockElement (hFlagShipElement);
745
746 PutElement (hFlagShipElement);
747 }
748 }
749
750 void
DoMissions(void)751 DoMissions (void)
752 {
753 HSHIPFRAG hGroup, hNextGroup;
754
755 spawn_flag_ship ();
756
757 if (EncounterRace >= 0)
758 { // There was a battle. Call in reinforcements.
759 NotifyOthers (EncounterRace, IPNL_INTERCEPT_PLAYER);
760 EncounterRace = -1;
761 }
762
763 for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
764 hGroup; hGroup = hNextGroup)
765 {
766 IP_GROUP *GroupPtr;
767
768 GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
769 hNextGroup = _GetSuccLink (GroupPtr);
770
771 if (GroupPtr->in_system)
772 spawn_ip_group (GroupPtr);
773
774 UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
775 }
776 }
777
778