//Copyright Paul Reiche, Fred Ford. 1992-2002 /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "hyper.h" #include "build.h" #include "collide.h" #include "colors.h" #include "controls.h" #include "gameopt.h" #include "menustat.h" // for DrawMenuStateStrings() #include "encount.h" #include "starmap.h" #include "ship.h" #include "shipcont.h" #include "process.h" #include "globdata.h" #include "sis.h" #include "units.h" #include "init.h" #include "nameref.h" #include "resinst.h" #include "setup.h" #include "sounds.h" #include "options.h" #include "libs/graphics/gfx_common.h" #include "libs/mathlib.h" #define XOFFS ((RADAR_SCAN_WIDTH + (UNIT_SCREEN_WIDTH << 2)) >> 1) #define YOFFS ((RADAR_SCAN_HEIGHT + (UNIT_SCREEN_HEIGHT << 2)) >> 1) static FRAME hyperstars[3]; static COLORMAP hypercmaps[2]; static BYTE fuel_ticks; static COUNT hyper_dx, hyper_dy, hyper_extra; // HyperspaceMenu() items enum HyperMenuItems { // XXX: Must match the enum in menustat.h STARMAP = 1, EQUIP_DEVICE, CARGO, ROSTER, GAME_MENU, NAVIGATION, }; void MoveSIS (SIZE *pdx, SIZE *pdy) { SIZE new_dx, new_dy; new_dx = *pdx; GLOBAL_SIS (log_x) -= new_dx; if (GLOBAL_SIS (log_x) < 0) { new_dx += (SIZE)GLOBAL_SIS (log_x); GLOBAL_SIS (log_x) = 0; } else if (GLOBAL_SIS (log_x) > MAX_X_LOGICAL) { new_dx += (SIZE)(GLOBAL_SIS (log_x) - MAX_X_LOGICAL); GLOBAL_SIS (log_x) = MAX_X_LOGICAL; } new_dy = *pdy; GLOBAL_SIS (log_y) -= new_dy; if (GLOBAL_SIS (log_y) < 0) { new_dy += (SIZE)GLOBAL_SIS (log_y); GLOBAL_SIS (log_y) = 0; } else if (GLOBAL_SIS (log_y) > MAX_Y_LOGICAL) { new_dy += (SIZE)(GLOBAL_SIS (log_y) - MAX_Y_LOGICAL); GLOBAL_SIS (log_y) = MAX_Y_LOGICAL; } if (new_dx != *pdx || new_dy != *pdy) { HELEMENT hElement, hNextElement; *pdx = new_dx; *pdy = new_dy; for (hElement = GetTailElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ElementPtr; LockElement (hElement, &ElementPtr); if (!(ElementPtr->state_flags & PLAYER_SHIP)) hNextElement = GetPredElement (ElementPtr); else { ElementPtr->next.location.x = (LOG_SPACE_WIDTH >> 1) - new_dx; ElementPtr->next.location.y = (LOG_SPACE_HEIGHT >> 1) - new_dy; hNextElement = 0; } UnlockElement (hElement); } } if (GLOBAL_SIS (FuelOnBoard) && GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1) { COUNT cur_fuel_ticks; COUNT hyper_dist; DWORD adj_dx, adj_dy; if (new_dx < 0) new_dx = -new_dx; hyper_dx += new_dx; if (new_dy < 0) new_dy = -new_dy; hyper_dy += new_dy; /* These macros are also used in the fuel estimate on the starmap. */ adj_dx = LOGX_TO_UNIVERSE(16 * hyper_dx); adj_dy = MAX_Y_UNIVERSE - LOGY_TO_UNIVERSE(16 * hyper_dy); hyper_dist = square_root (adj_dx * adj_dx + adj_dy * adj_dy) + hyper_extra; cur_fuel_ticks = hyper_dist >> 4; if (cur_fuel_ticks > (COUNT)fuel_ticks) { #ifndef TESTING DeltaSISGauges (0, fuel_ticks - cur_fuel_ticks, 0); #endif /* TESTING */ if (cur_fuel_ticks > 0x00FF) { hyper_dx = 0; hyper_extra = hyper_dist & ((1 << 4) - 1); hyper_dy = 0; cur_fuel_ticks = 0; } fuel_ticks = (BYTE)cur_fuel_ticks; } } } void check_hyperspace_encounter (void) { BYTE Type; POINT universe; HFLEETINFO hStarShip, hNextShip; COUNT EncounterPercent[] = { RACE_HYPERSPACE_PERCENT }; universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x)); universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y)); for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q)), Type = 0; hStarShip && (GLOBAL (CurrentActivity) & IN_BATTLE); hStarShip = hNextShip, ++Type) { COUNT encounter_radius; FLEET_INFO *FleetPtr; FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip); hNextShip = _GetSuccLink (FleetPtr); encounter_radius = FleetPtr->actual_strength; if (encounter_radius) { BYTE encounter_flags; SIZE dx, dy; COUNT percent; HENCOUNTER hEncounter; ENCOUNTER *EncounterPtr; encounter_flags = 0; percent = EncounterPercent[Type]; if (encounter_radius != INFINITE_RADIUS) { encounter_radius = (encounter_radius * SPHERE_RADIUS_INCREMENT) >> 1; } else /* encounter_radius == infinity */ { HENCOUNTER hNextEncounter; encounter_radius = (MAX_X_UNIVERSE + 1) << 1; if (Type == SLYLANDRO_SHIP) { encounter_flags = ONE_SHOT_ENCOUNTER; if (!GET_GAME_STATE (STARBASE_AVAILABLE)) percent = 100; else percent *= GET_GAME_STATE (SLYLANDRO_MULTIPLIER); } else if (Type == MELNORME_SHIP && (GLOBAL_SIS (FuelOnBoard) == 0 || GET_GAME_STATE (USED_BROADCASTER)) && GET_GAME_STATE (MELNORME_ANGER) < 3) { if (!GET_GAME_STATE (USED_BROADCASTER)) percent = 30; else percent = 100; encounter_flags = ONE_SHOT_ENCOUNTER; } // There can be only one! (of either Slylandro or Melnorme) for (hEncounter = GetHeadEncounter (); hEncounter; hEncounter = hNextEncounter) { LockEncounter (hEncounter, &EncounterPtr); hNextEncounter = GetSuccEncounter (EncounterPtr); if (EncounterPtr->race_id == Type) { percent = 0; hNextEncounter = 0; } UnlockEncounter (hEncounter); } if (percent == 100 && Type == MELNORME_SHIP) { SET_GAME_STATE (BROADCASTER_RESPONSE, 1); } } dx = universe.x - FleetPtr->loc.x; if (dx < 0) dx = -dx; dy = universe.y - FleetPtr->loc.y; if (dy < 0) dy = -dy; if ((COUNT)dx < encounter_radius && (COUNT)dy < encounter_radius && (DWORD)dx * dx + (DWORD)dy * dy < (DWORD)encounter_radius * encounter_radius && ((COUNT)TFB_Random () % 100) < percent) { // Ship spawned for encounter. hEncounter = AllocEncounter (); if (hEncounter) { LockEncounter (hEncounter, &EncounterPtr); memset (EncounterPtr, 0, sizeof (*EncounterPtr)); EncounterPtr->origin = FleetPtr->loc; EncounterPtr->radius = encounter_radius; EncounterPtr->flags = encounter_flags; EncounterPtr->race_id = Type; UnlockEncounter (hEncounter); PutEncounter (hEncounter); } } } UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip); } SET_GAME_STATE (USED_BROADCASTER, 0); } void FreeHyperData (void) { DestroyDrawable (ReleaseDrawable (hyperstars[0])); hyperstars[0] = 0; DestroyDrawable (ReleaseDrawable (hyperstars[1])); hyperstars[1] = 0; DestroyDrawable (ReleaseDrawable (hyperstars[2])); hyperstars[2] = 0; DestroyColorMap (ReleaseColorMap (hypercmaps[0])); hypercmaps[0] = 0; DestroyColorMap (ReleaseColorMap (hypercmaps[1])); hypercmaps[1] = 0; } static void LoadHyperData (void) { if (hyperstars[0] == 0) { hyperstars[0] = CaptureDrawable ( LoadGraphic (AMBIENT_MASK_PMAP_ANIM)); hyperstars[1] = CaptureDrawable ( LoadGraphic (HYPERSTARS_MASK_PMAP_ANIM)); hypercmaps[0] = CaptureColorMap (LoadColorMap (HYPER_COLOR_TAB)); hyperstars[2] = CaptureDrawable ( LoadGraphic (ARISPACE_MASK_PMAP_ANIM)); hypercmaps[1] = CaptureColorMap (LoadColorMap (ARISPACE_COLOR_TAB)); } } BOOLEAN LoadHyperspace (void) { hyper_dx = 0; hyper_dy = 0; hyper_extra = 0; fuel_ticks = 1; GLOBAL (ShipStamp.origin.x) = -MAX_X_UNIVERSE; GLOBAL (ShipStamp.origin.y) = -MAX_Y_UNIVERSE; LoadHyperData (); { FRAME F; F = hyperstars[0]; hyperstars[0] = stars_in_space; stars_in_space = F; } if (!(LastActivity & CHECK_LOAD)) RepairSISBorder (); else { if (LOBYTE (LastActivity) == 0) { DrawSISFrame (); } else { ClearSISRect (DRAW_SIS_DISPLAY); RepairSISBorder (); } } if (!(GLOBAL (autopilot.x) != ~0 && GLOBAL (autopilot.y) != ~0)) { DrawSISMessage (NULL); } SetContext (RadarContext); SetContextBackGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x00, 0x0E, 0x00), 0x6C)); SetContext (SpaceContext); if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1) { SetContextBackGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x07, 0x00, 0x00), 0x2F)); SetColorMap (GetColorMapAddress (hypercmaps[0])); } else { SetContextBackGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x00, 0x1A, 0x00), 0x2F)); SetColorMap (GetColorMapAddress (hypercmaps[1])); SET_GAME_STATE (USED_BROADCASTER, 0); SET_GAME_STATE (BROADCASTER_RESPONSE, 0); } // ClearDrawable (); ClearSISRect (CLEAR_SIS_RADAR); return TRUE; } BOOLEAN FreeHyperspace (void) { { FRAME F; F = hyperstars[0]; hyperstars[0] = stars_in_space; stars_in_space = F; } // FreeHyperData (); return TRUE; } static void ElementToUniverse (ELEMENT *ElementPtr, POINT *pPt) { SDWORD log_x, log_y; log_x = GLOBAL_SIS (log_x) + (ElementPtr->next.location.x - (LOG_SPACE_WIDTH >> 1)); log_y = GLOBAL_SIS (log_y) + (ElementPtr->next.location.y - (LOG_SPACE_HEIGHT >> 1)); pPt->x = LOGX_TO_UNIVERSE (log_x); pPt->y = LOGY_TO_UNIVERSE (log_y); } static void cleanup_hyperspace (void) { HENCOUNTER hEncounter, hNextEncounter; for (hEncounter = GetHeadEncounter (); hEncounter != 0; hEncounter = hNextEncounter) { ENCOUNTER *EncounterPtr; LockEncounter (hEncounter, &EncounterPtr); hNextEncounter = GetSuccEncounter (EncounterPtr); if (EncounterPtr->hElement) { ELEMENT *ElementPtr; LockElement (EncounterPtr->hElement, &ElementPtr); if (ElementPtr->hTarget) { // This is the encounter that collided with flagship // Move the encounter to the head of the queue so that // comm.c:RaceCommunication() gets the right one. RemoveEncounter (hEncounter); InsertEncounter (hEncounter, GetHeadEncounter ()); } UnlockElement (EncounterPtr->hElement); } EncounterPtr->hElement = 0; UnlockEncounter (hEncounter); } } typedef enum { RANDOM_ENCOUNTER_TRANSITION, INTERPLANETARY_TRANSITION, ARILOU_SPACE_TRANSITION } TRANSITION_TYPE; static void InterplanetaryTransition (ELEMENT *ElementPtr) { GLOBAL (ip_planet) = 0; GLOBAL (in_orbit) = 0; GLOBAL (ShipFacing) = 0; /* Not reentering the system */ SET_GAME_STATE (USED_BROADCASTER, 0); if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1) { // Enter a solar system from HyperSpace. GLOBAL (CurrentActivity) |= START_INTERPLANETARY; SET_GAME_STATE (ESCAPE_COUNTER, 0); } else { POINT pt; GLOBAL (autopilot.x) = ~0; GLOBAL (autopilot.y) = ~0; ElementToUniverse (ElementPtr, &pt); CurStarDescPtr = FindStar (NULL, &pt, 5, 5); if (CurStarDescPtr->star_pt.x == ARILOU_HOME_X && CurStarDescPtr->star_pt.y == ARILOU_HOME_Y) { // Meet the Arilou. GLOBAL (CurrentActivity) |= START_ENCOUNTER; } else { // Transition from QuasiSpace to HyperSpace through // one of the permanent portals. COUNT index; const POINT portal_pt[] = QUASISPACE_PORTALS_HYPERSPACE_ENDPOINTS; index = CurStarDescPtr - &star_array[NUM_SOLAR_SYSTEMS + 1]; GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (portal_pt[index].x); GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (portal_pt[index].y); SET_GAME_STATE (ARILOU_SPACE_SIDE, 0); } } } /* Enter QuasiSpace from HyperSpace by any portal, or HyperSpace from * QuasiSpace through the periodically opening portal. */ static void ArilouSpaceTransition (void) { GLOBAL (ShipFacing) = 0; /* Not reentering the system */ SET_GAME_STATE (USED_BROADCASTER, 0); GLOBAL (autopilot.x) = ~0; GLOBAL (autopilot.y) = ~0; if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1) { // From HyperSpace to QuasiSpace. GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (QUASI_SPACE_X); GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (QUASI_SPACE_Y); if (GET_GAME_STATE (PORTAL_COUNTER) == 0) { // Periodically appearing portal. SET_GAME_STATE (ARILOU_SPACE_SIDE, 3); } else { // Player-induced portal. SET_GAME_STATE (PORTAL_COUNTER, 0); SET_GAME_STATE (ARILOU_SPACE_SIDE, 3); } } else { // From QuasiSpace to HyperSpace through the periodically appearing // portal. GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (ARILOU_SPACE_X); GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (ARILOU_SPACE_Y); SET_GAME_STATE (ARILOU_SPACE_SIDE, 0); } } static void unhyper_transition (ELEMENT *ElementPtr) { COUNT frame_index; ElementPtr->state_flags |= CHANGING; frame_index = GetFrameIndex (ElementPtr->current.image.frame); if (frame_index == 0) frame_index += ANGLE_TO_FACING (FULL_CIRCLE); else if (frame_index < ANGLE_TO_FACING (FULL_CIRCLE)) frame_index = NORMALIZE_FACING (frame_index + 1); else if (++frame_index == GetFrameCount (ElementPtr->current.image.frame)) { cleanup_hyperspace (); GLOBAL (CurrentActivity) &= ~IN_BATTLE; switch ((TRANSITION_TYPE) ElementPtr->turn_wait) { case RANDOM_ENCOUNTER_TRANSITION: SaveSisHyperState (); GLOBAL (CurrentActivity) |= START_ENCOUNTER; break; case INTERPLANETARY_TRANSITION: InterplanetaryTransition (ElementPtr); break; case ARILOU_SPACE_TRANSITION: ArilouSpaceTransition (); break; } ZeroVelocityComponents (&ElementPtr->velocity); SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); return; } ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->current.image.frame, frame_index); } static void init_transition (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1, TRANSITION_TYPE which_transition) { SIZE dx, dy; SIZE num_turns; STARSHIP *StarShipPtr; dx = WORLD_TO_VELOCITY (ElementPtr0->next.location.x - ElementPtr1->next.location.x); dy = WORLD_TO_VELOCITY (ElementPtr0->next.location.y - ElementPtr1->next.location.y); ElementPtr1->state_flags |= NONSOLID; ElementPtr1->preprocess_func = unhyper_transition; ElementPtr1->postprocess_func = NULL; ElementPtr1->turn_wait = (BYTE) which_transition; GetElementStarShip (ElementPtr1, &StarShipPtr); num_turns = GetFrameCount (ElementPtr1->next.image.frame) - ANGLE_TO_FACING (FULL_CIRCLE) + NORMALIZE_FACING (ANGLE_TO_FACING (FULL_CIRCLE) - StarShipPtr->ShipFacing); if (num_turns == 0) num_turns = 1; SetVelocityComponents (&ElementPtr1->velocity, dx / num_turns, dy / num_turns); } BOOLEAN hyper_transition (ELEMENT *ElementPtr) { if (ElementPtr->state_flags & APPEARING) { if (LastActivity & CHECK_LOAD) { LastActivity &= ~CHECK_LOAD; ElementPtr->current = ElementPtr->next; SetUpElement (ElementPtr); ElementPtr->state_flags |= DEFY_PHYSICS; return FALSE; } else { ElementPtr->preprocess_func = (void (*) (struct element *ElementPtr)) hyper_transition; ElementPtr->postprocess_func = NULL; ElementPtr->state_flags |= NONSOLID; ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->current.image.frame, GetFrameCount (ElementPtr->current.image.frame) - 1); } } else { COUNT frame_index; frame_index = GetFrameIndex (ElementPtr->current.image.frame); if (frame_index-- <= ANGLE_TO_FACING (FULL_CIRCLE)) { STARSHIP *StarShipPtr; if (frame_index == ANGLE_TO_FACING (FULL_CIRCLE) - 1) frame_index = 0; else frame_index = NORMALIZE_FACING (frame_index); GetElementStarShip (ElementPtr, &StarShipPtr); if (frame_index == StarShipPtr->ShipFacing) { ElementPtr->preprocess_func = ship_preprocess; ElementPtr->postprocess_func = ship_postprocess; ElementPtr->state_flags &= ~NONSOLID; } } ElementPtr->state_flags |= CHANGING; ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->current.image.frame, frame_index); if (!(ElementPtr->state_flags & NONSOLID)) { ElementPtr->current = ElementPtr->next; SetUpElement (ElementPtr); ElementPtr->state_flags |= DEFY_PHYSICS; } } return TRUE; } static void hyper_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { if ((ElementPtr1->state_flags & PLAYER_SHIP) && GET_GAME_STATE (PORTAL_COUNTER) == 0) { SIZE dx, dy; POINT pt; STAR_DESC *SDPtr; STARSHIP *StarShipPtr; ElementToUniverse (ElementPtr0, &pt); SDPtr = FindStar (NULL, &pt, 5, 5); GetElementStarShip (ElementPtr1, &StarShipPtr); GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx, &dy); if (SDPtr == CurStarDescPtr || (ElementPtr1->state_flags & APPEARING) || !(dx || dy || (StarShipPtr->cur_status_flags & (LEFT | RIGHT | THRUST | WEAPON | SPECIAL)))) { CurStarDescPtr = SDPtr; ElementPtr0->state_flags |= DEFY_PHYSICS | COLLISION; } else if ((GLOBAL (CurrentActivity) & IN_BATTLE) && (GLOBAL (autopilot.x) == ~0 || GLOBAL (autopilot.y) == ~0 || (GLOBAL (autopilot.x) == SDPtr->star_pt.x && GLOBAL (autopilot.y) == SDPtr->star_pt.y))) { CurStarDescPtr = SDPtr; ElementPtr0->state_flags |= COLLISION; init_transition (ElementPtr0, ElementPtr1, INTERPLANETARY_TRANSITION); } } (void) pPt0; /* Satisfying compiler (unused parameter) */ (void) pPt1; /* Satisfying compiler (unused parameter) */ } static void hyper_death (ELEMENT *ElementPtr) { if (!(ElementPtr->state_flags & DEFY_PHYSICS) && (GLOBAL (CurrentActivity) & IN_BATTLE)) CurStarDescPtr = 0; } static void arilou_space_death (ELEMENT *ElementPtr) { if (!(ElementPtr->state_flags & DEFY_PHYSICS) || GET_GAME_STATE (ARILOU_SPACE_COUNTER) == 0) { if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1) { SET_GAME_STATE (ARILOU_SPACE_SIDE, 0); } else { SET_GAME_STATE (ARILOU_SPACE_SIDE, 3); } } } static void arilou_space_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { COUNT which_side; if (!(ElementPtr1->state_flags & PLAYER_SHIP)) return; which_side = GET_GAME_STATE (ARILOU_SPACE_SIDE); if (which_side == 0 || which_side == 3) { if (ElementPtr1->state_flags & DEFY_PHYSICS) { SET_GAME_STATE (ARILOU_SPACE_SIDE, which_side ^ 1); } else { init_transition (ElementPtr0, ElementPtr1, ARILOU_SPACE_TRANSITION); } } ElementPtr0->state_flags |= DEFY_PHYSICS | COLLISION; (void) pPt0; /* Satisfying compiler (unused parameter) */ (void) pPt1; /* Satisfying compiler (unused parameter) */ } static HELEMENT AllocHyperElement (const POINT *elem_pt) { HELEMENT hHyperSpaceElement; hHyperSpaceElement = AllocElement (); if (hHyperSpaceElement) { ELEMENT *HyperSpaceElementPtr; LockElement (hHyperSpaceElement, &HyperSpaceElementPtr); HyperSpaceElementPtr->playerNr = NEUTRAL_PLAYER_NUM; HyperSpaceElementPtr->state_flags = CHANGING | FINITE_LIFE; HyperSpaceElementPtr->life_span = 1; HyperSpaceElementPtr->mass_points = 1; { long lx, ly; lx = UNIVERSE_TO_LOGX (elem_pt->x) + (LOG_SPACE_WIDTH >> 1) - GLOBAL_SIS (log_x); HyperSpaceElementPtr->current.location.x = WRAP_X (lx); ly = UNIVERSE_TO_LOGY (elem_pt->y) + (LOG_SPACE_HEIGHT >> 1) - GLOBAL_SIS (log_y); HyperSpaceElementPtr->current.location.y = WRAP_Y (ly); } SetPrimType (&DisplayArray[HyperSpaceElementPtr->PrimIndex], STAMP_PRIM); HyperSpaceElementPtr->current.image.farray = &hyperstars[1 + (GET_GAME_STATE (ARILOU_SPACE_SIDE) >> 1)]; UnlockElement (hHyperSpaceElement); } return hHyperSpaceElement; } static void AddAmbientElement (void) { HELEMENT hHyperSpaceElement; hHyperSpaceElement = AllocElement (); if (hHyperSpaceElement) { SIZE dx, dy; DWORD rand_val; ELEMENT *HyperSpaceElementPtr; LockElement (hHyperSpaceElement, &HyperSpaceElementPtr); HyperSpaceElementPtr->playerNr = NEUTRAL_PLAYER_NUM; HyperSpaceElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID; SetPrimType (&DisplayArray[HyperSpaceElementPtr->PrimIndex], STAMP_PRIM); HyperSpaceElementPtr->preprocess_func = animation_preprocess; rand_val = TFB_Random (); dy = LOWORD (rand_val); dx = (SIZE)(LOBYTE (dy) % SPACE_WIDTH) - (SPACE_WIDTH >> 1); dy = (SIZE)(HIBYTE (dy) % SPACE_HEIGHT) - (SPACE_HEIGHT >> 1); HyperSpaceElementPtr->current.location.x = (LOG_SPACE_WIDTH >> 1) + DISPLAY_TO_WORLD (dx); HyperSpaceElementPtr->current.location.y = (LOG_SPACE_HEIGHT >> 1) + DISPLAY_TO_WORLD (dy); HyperSpaceElementPtr->current.image.farray = &stars_in_space; if (HIWORD (rand_val) & 7) { HyperSpaceElementPtr->life_span = 14; HyperSpaceElementPtr->current.image.frame = stars_in_space; } else { HyperSpaceElementPtr->life_span = 12; HyperSpaceElementPtr->current.image.frame = SetAbsFrameIndex (stars_in_space, 14); } UnlockElement (hHyperSpaceElement); InsertElement (hHyperSpaceElement, GetHeadElement ()); } } #define NUM_VORTEX_TRANSITIONS 9 #define VORTEX_WAIT 1 static void encounter_transition (ELEMENT *ElementPtr) { ElementPtr->state_flags &= ~DISAPPEARING; ElementPtr->life_span = 1; if (ElementPtr->turn_wait) { --ElementPtr->turn_wait; } else { FRAME f; if (ElementPtr->hit_points) { f = DecFrameIndex (ElementPtr->current.image.frame); ElementPtr->next.image.frame = f; } else { f = IncFrameIndex (ElementPtr->current.image.frame); if (f != ElementPtr->current.image.farray[0]) ElementPtr->next.image.frame = f; else ElementPtr->death_func = NULL; } ElementPtr->turn_wait = VORTEX_WAIT; } } static HELEMENT getSisElement (void) { HSTARSHIP hSis; HELEMENT hShip; STARSHIP *StarShipPtr; hSis = GetHeadLink (&race_q[RPG_PLAYER_NUM]); if (!hSis) return NULL; StarShipPtr = LockStarShip (&race_q[RPG_PLAYER_NUM], hSis); hShip = StarShipPtr->hShip; UnlockStarShip (&race_q[RPG_PLAYER_NUM], hSis); #ifdef DEBUG { ELEMENT *ElementPtr; LockElement (hShip, &ElementPtr); assert (ElementPtr->state_flags & PLAYER_SHIP); UnlockElement (hShip); } #endif return hShip; } static void encounter_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { HENCOUNTER hEncounter; HENCOUNTER hNextEncounter; if (!(ElementPtr1->state_flags & PLAYER_SHIP) || !(GLOBAL (CurrentActivity) & IN_BATTLE)) return; init_transition (ElementPtr0, ElementPtr1, RANDOM_ENCOUNTER_TRANSITION); for (hEncounter = GetHeadEncounter (); hEncounter != 0; hEncounter = hNextEncounter) { ENCOUNTER *EncounterPtr; LockEncounter (hEncounter, &EncounterPtr); hNextEncounter = GetSuccEncounter (EncounterPtr); if (EncounterPtr->hElement) { ELEMENT *ElementPtr; LockElement (EncounterPtr->hElement, &ElementPtr); ElementPtr->state_flags |= NONSOLID | IGNORE_SIMILAR; UnlockElement (EncounterPtr->hElement); } UnlockEncounter (hEncounter); } // Mark this element as collided with flagship // XXX: We could simply set hTarget to 1 or to ElementPtr1, // but that would be too hacky ;) ElementPtr0->hTarget = getSisElement (); ZeroVelocityComponents (&ElementPtr0->velocity); (void) pPt0; /* Satisfying compiler (unused parameter) */ (void) pPt1; /* Satisfying compiler (unused parameter) */ } static HELEMENT AddEncounterElement (ENCOUNTER *EncounterPtr, POINT *puniverse) { BOOLEAN NewEncounter; HELEMENT hElement; POINT enc_pt; if (GET_GAME_STATE (ARILOU_SPACE_SIDE) >= 2) return 0; if (EncounterPtr->flags & ENCOUNTER_REFORMING) { EncounterPtr->flags &= ~ENCOUNTER_REFORMING; EncounterPtr->transition_state = 100; if ((EncounterPtr->flags & ONE_SHOT_ENCOUNTER) || EncounterPtr->num_ships == 0) return 0; } if (EncounterPtr->num_ships) { NewEncounter = FALSE; enc_pt = EncounterPtr->loc_pt; } else { BYTE Type; SIZE dx, dy; COUNT i; COUNT NumShips; DWORD radius_squared; BYTE EncounterMakeup[] = { RACE_ENCOUNTER_MAKEUP }; NewEncounter = TRUE; radius_squared = (DWORD)EncounterPtr->radius * EncounterPtr->radius; Type = EncounterPtr->race_id; NumShips = LONIBBLE (EncounterMakeup[Type]); for (i = HINIBBLE (EncounterMakeup[Type]) - NumShips; i; --i) { if ((COUNT)TFB_Random () % 100 < 50) ++NumShips; } if (NumShips > MAX_HYPER_SHIPS) NumShips = MAX_HYPER_SHIPS; EncounterPtr->num_ships = NumShips; for (i = 0; i < NumShips; ++i) { BRIEF_SHIP_INFO *BSIPtr = &EncounterPtr->ShipList[i]; HFLEETINFO hStarShip = GetStarShipFromIndex (&GLOBAL (avail_race_q), Type); FLEET_INFO *FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip); BSIPtr->race_id = Type; BSIPtr->crew_level = FleetPtr->crew_level; BSIPtr->max_crew = FleetPtr->max_crew; BSIPtr->max_energy = FleetPtr->max_energy; UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip); } do { DWORD rand_val; rand_val = TFB_Random (); enc_pt.x = puniverse->x + (LOWORD (rand_val) % (XOFFS << 1)) - XOFFS; if (enc_pt.x < 0) enc_pt.x = 0; else if (enc_pt.x > MAX_X_UNIVERSE) enc_pt.x = MAX_X_UNIVERSE; enc_pt.y = puniverse->y + (HIWORD (rand_val) % (YOFFS << 1)) - YOFFS; if (enc_pt.y < 0) enc_pt.y = 0; else if (enc_pt.y > MAX_Y_UNIVERSE) enc_pt.y = MAX_Y_UNIVERSE; dx = enc_pt.x - EncounterPtr->origin.x; dy = enc_pt.y - EncounterPtr->origin.y; } while ((DWORD)((long)dx * dx + (long)dy * dy) > radius_squared); EncounterPtr->loc_pt = enc_pt; EncounterPtr->log_x = UNIVERSE_TO_LOGX (enc_pt.x); EncounterPtr->log_y = UNIVERSE_TO_LOGY (enc_pt.y); } hElement = AllocHyperElement (&enc_pt); if (hElement) { SIZE i; ELEMENT *ElementPtr; LockElement (hElement, &ElementPtr); i = EncounterPtr->transition_state; if (i || NewEncounter) { if (i < 0) { i = -i; ElementPtr->hit_points = 1; } if (i == 0 || i > NUM_VORTEX_TRANSITIONS) i = NUM_VORTEX_TRANSITIONS; ElementPtr->current.image.frame = SetRelFrameIndex ( ElementPtr->current.image.farray[0], -i); ElementPtr->death_func = encounter_transition; } else { ElementPtr->current.image.frame = DecFrameIndex (ElementPtr->current.image.farray[0]); } ElementPtr->turn_wait = VORTEX_WAIT; ElementPtr->preprocess_func = NULL; ElementPtr->postprocess_func = NULL; ElementPtr->collision_func = encounter_collision; SetUpElement (ElementPtr); ElementPtr->IntersectControl.IntersectStamp.frame = DecFrameIndex (stars_in_space); SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); ElementPtr->state_flags |= NONSOLID | IGNORE_VELOCITY; UnlockElement (hElement); InsertElement (hElement, GetTailElement ()); } EncounterPtr->hElement = hElement; return hElement; } #define GRID_OFFSET 200 static void DrawHyperGrid (COORD ux, COORD uy, COORD ox, COORD oy) { COORD sx, sy, ex, ey; RECT r; ClearDrawable (); SetContextForeGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x00, 0x10, 0x00), 0x6B)); sx = ux - (RADAR_SCAN_WIDTH >> 1); if (sx < 0) sx = 0; else sx -= sx % GRID_OFFSET; ex = ux + (RADAR_SCAN_WIDTH >> 1); if (ex > MAX_X_UNIVERSE + 1) ex = MAX_X_UNIVERSE + 1; sy = uy - (RADAR_SCAN_HEIGHT >> 1); if (sy < 0) sy = 0; else sy -= sy % GRID_OFFSET; ey = uy + (RADAR_SCAN_HEIGHT >> 1); if (ey > MAX_Y_UNIVERSE + 1) ey = MAX_Y_UNIVERSE + 1; r.corner.y = (COORD) ((long)(MAX_Y_UNIVERSE - ey) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy; r.extent.width = 1; r.extent.height = ((COORD) ((long)(MAX_Y_UNIVERSE - sy) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy) - r.corner.y + 1; for (ux = sx; ux <= ex; ux += GRID_OFFSET) { r.corner.x = (COORD) ((long)ux * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox; DrawFilledRectangle (&r); } r.corner.x = (COORD) ((long)sx * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox; r.extent.width = ((COORD) ((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox) - r.corner.x + 1; r.extent.height = 1; for (uy = sy; uy <= ey; uy += GRID_OFFSET) { r.corner.y = (COORD)((long)(MAX_Y_UNIVERSE - uy) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy; DrawFilledRectangle (&r); } } // Returns false iff the encounter is to be removed. static bool ProcessEncounter (ENCOUNTER *EncounterPtr, POINT *puniverse, COORD ox, COORD oy, STAMP *stamp) { ELEMENT *ElementPtr; COORD ex, ey; if (EncounterPtr->hElement == 0 && AddEncounterElement (EncounterPtr, puniverse) == 0) return false; LockElement (EncounterPtr->hElement, &ElementPtr); if (ElementPtr->death_func) { if (EncounterPtr->transition_state && ElementPtr->turn_wait == 0) { --EncounterPtr->transition_state; if (EncounterPtr->transition_state >= NUM_VORTEX_TRANSITIONS) ++ElementPtr->turn_wait; else if (EncounterPtr->transition_state == -NUM_VORTEX_TRANSITIONS) { ElementPtr->death_func = NULL; UnlockElement (EncounterPtr->hElement); return false; } else SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMP_PRIM); } } else { SIZE delta_x, delta_y; COUNT encounter_radius; ElementPtr->life_span = 1; GetNextVelocityComponents (&ElementPtr->velocity, &delta_x, &delta_y, 1); if (ElementPtr->thrust_wait) --ElementPtr->thrust_wait; else if (!ElementPtr->hTarget) { // This is an encounter that did not collide with flagship // The colliding encounter does not move COUNT cur_facing, delta_facing; cur_facing = ANGLE_TO_FACING ( GetVelocityTravelAngle (&ElementPtr->velocity)); delta_facing = NORMALIZE_FACING (cur_facing - ANGLE_TO_FACING ( ARCTAN (puniverse->x - EncounterPtr->loc_pt.x, puniverse->y - EncounterPtr->loc_pt.y))); if (delta_facing || (delta_x == 0 && delta_y == 0)) { SIZE speed; const SIZE RaceHyperSpeed[] = { RACE_HYPER_SPEED }; #define ENCOUNTER_TRACK_WAIT 3 speed = RaceHyperSpeed[EncounterPtr->race_id]; if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE)) --cur_facing; else ++cur_facing; if (NORMALIZE_FACING (delta_facing + ANGLE_TO_FACING (OCTANT)) > ANGLE_TO_FACING (QUADRANT)) { if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE)) --cur_facing; else ++cur_facing; speed >>= 1; } cur_facing = FACING_TO_ANGLE (cur_facing); SetVelocityComponents (&ElementPtr->velocity, COSINE (cur_facing, speed), SINE (cur_facing, speed)); GetNextVelocityComponents (&ElementPtr->velocity, &delta_x, &delta_y, 1); ElementPtr->thrust_wait = ENCOUNTER_TRACK_WAIT; } } EncounterPtr->log_x += delta_x; EncounterPtr->log_y -= delta_y; EncounterPtr->loc_pt.x = LOGX_TO_UNIVERSE (EncounterPtr->log_x); EncounterPtr->loc_pt.y = LOGY_TO_UNIVERSE (EncounterPtr->log_y); encounter_radius = EncounterPtr->radius + (GRID_OFFSET >> 1); delta_x = EncounterPtr->loc_pt.x - EncounterPtr->origin.x; if (delta_x < 0) delta_x = -delta_x; delta_y = EncounterPtr->loc_pt.y - EncounterPtr->origin.y; if (delta_y < 0) delta_y = -delta_y; if ((COUNT)delta_x >= encounter_radius || (COUNT)delta_y >= encounter_radius || (DWORD)delta_x * delta_x + (DWORD)delta_y * delta_y >= (DWORD)encounter_radius * encounter_radius) { // Encounter globe traveled outside the SoI and now disappears ElementPtr->state_flags |= NONSOLID; ElementPtr->life_span = 0; if (EncounterPtr->transition_state == 0) { ElementPtr->death_func = encounter_transition; EncounterPtr->transition_state = -1; ElementPtr->hit_points = 1; } else { ElementPtr->death_func = NULL; UnlockElement (EncounterPtr->hElement); return false; } } } ex = EncounterPtr->loc_pt.x; ey = EncounterPtr->loc_pt.y; if (ex - puniverse->x >= -UNIT_SCREEN_WIDTH && ex - puniverse->x <= UNIT_SCREEN_WIDTH && ey - puniverse->y >= -UNIT_SCREEN_HEIGHT && ey - puniverse->y <= UNIT_SCREEN_HEIGHT) { ElementPtr->next.location.x = (SIZE)(EncounterPtr->log_x - GLOBAL_SIS (log_x)) + (LOG_SPACE_WIDTH >> 1); ElementPtr->next.location.y = (SIZE)(EncounterPtr->log_y - GLOBAL_SIS (log_y)) + (LOG_SPACE_HEIGHT >> 1); if ((ElementPtr->state_flags & NONSOLID) && EncounterPtr->transition_state == 0) { ElementPtr->current.location = ElementPtr->next.location; SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMP_PRIM); if (ElementPtr->death_func == 0) { InitIntersectStartPoint (ElementPtr); ElementPtr->state_flags &= ~NONSOLID; } } } else { ElementPtr->state_flags |= NONSOLID; if (ex - puniverse->x < -XOFFS || ex - puniverse->x > XOFFS || ey - puniverse->y < -YOFFS || ey - puniverse->y > YOFFS) { ElementPtr->life_span = 0; ElementPtr->death_func = NULL; UnlockElement (EncounterPtr->hElement); return false; } SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); } UnlockElement (EncounterPtr->hElement); stamp->origin.x = (COORD)((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox; stamp->origin.y = (COORD)((long)(MAX_Y_UNIVERSE - ey) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy; DrawStamp (stamp); return true; } static void ProcessEncounters (POINT *puniverse, COORD ox, COORD oy) { STAMP stamp; HENCOUNTER hEncounter, hNextEncounter; stamp.frame = SetAbsFrameIndex (stars_in_space, 91); for (hEncounter = GetHeadEncounter (); hEncounter; hEncounter = hNextEncounter) { ENCOUNTER *EncounterPtr; LockEncounter (hEncounter, &EncounterPtr); hNextEncounter = GetSuccEncounter (EncounterPtr); if (!ProcessEncounter (EncounterPtr, puniverse, ox, oy, &stamp)) { UnlockEncounter (hEncounter); RemoveEncounter (hEncounter); FreeEncounter (hEncounter); continue; } UnlockEncounter (hEncounter); } } void SeedUniverse (void) { COORD ox, oy; COORD sx, sy, ex, ey; SWORD portalCounter, arilouSpaceCounter, arilouSpaceSide; POINT universe; FRAME blip_frame; STAMP s; STAR_DESC *SDPtr; HELEMENT hHyperSpaceElement; ELEMENT *HyperSpaceElementPtr; universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x)); universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y)); blip_frame = SetAbsFrameIndex (stars_in_space, 90); SetContext (RadarContext); BatchGraphics (); ox = (COORD)((long)universe.x * RADAR_WIDTH / RADAR_SCAN_WIDTH) - (RADAR_WIDTH >> 1); oy = (COORD)((long)(MAX_Y_UNIVERSE - universe.y) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - (RADAR_HEIGHT >> 1); ex = (COORD)((long)GLOBAL (ShipStamp.origin.x) * RADAR_WIDTH / RADAR_SCAN_WIDTH) - (RADAR_WIDTH >> 1); ey = (COORD)((long)(MAX_Y_UNIVERSE - GLOBAL (ShipStamp.origin.y)) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - (RADAR_HEIGHT >> 1); arilouSpaceCounter = GET_GAME_STATE (ARILOU_SPACE_COUNTER); arilouSpaceSide = GET_GAME_STATE (ARILOU_SPACE_SIDE); // if (ox != ex || oy != ey) { DrawHyperGrid (universe.x, universe.y, ox, oy); { SDPtr = 0; while ((SDPtr = FindStar (SDPtr, &universe, XOFFS, YOFFS))) { BYTE star_type; ex = SDPtr->star_pt.x; ey = SDPtr->star_pt.y; star_type = STAR_TYPE (SDPtr->Type); if (arilouSpaceSide >= 2 && ex == ARILOU_HOME_X && ey == ARILOU_HOME_Y) star_type = SUPER_GIANT_STAR; s.origin.x = (COORD)((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox; s.origin.y = (COORD)((long)(MAX_Y_UNIVERSE - ey) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy; s.frame = SetRelFrameIndex (blip_frame, star_type + 2); DrawStamp (&s); } } } portalCounter = GET_GAME_STATE (PORTAL_COUNTER); if (portalCounter || arilouSpaceCounter) { COUNT i; STAR_DESC SD[2]; // This array is filled with the STAR_DESC's of // QuasiSpace portals that need to be taken into account. // i is set to the number of active portals (max 2). i = 0; if (portalCounter) { // A player-created QuasiSpace portal is opening. static POINT portal_pt; SD[i].Index = ((portalCounter - 1) >> 1) + 18; if (portalCounter == 1) portal_pt = universe; SD[i].star_pt = portal_pt; ++i; if (++portalCounter == (10 + 1)) portalCounter = (9 + 1); SET_GAME_STATE (PORTAL_COUNTER, portalCounter); } if (arilouSpaceCounter) { // The periodically appearing QuasiSpace portal is open. SD[i].Index = arilouSpaceCounter >> 1; if (arilouSpaceSide <= 1) { // The player is in HyperSpace SD[i].Index += 18; SD[i].star_pt.x = ARILOU_SPACE_X; SD[i].star_pt.y = ARILOU_SPACE_Y; } else { // The player is in QuasiSpace SD[i].star_pt.x = QUASI_SPACE_X; SD[i].star_pt.y = QUASI_SPACE_Y; } ++i; } // Process the i portals from SD. do { --i; sx = SD[i].star_pt.x - universe.x + XOFFS; sy = SD[i].star_pt.y - universe.y + YOFFS; if (sx < 0 || sy < 0 || sx >= (XOFFS << 1) || sy >= (YOFFS << 1)) continue; ex = SD[i].star_pt.x; ey = SD[i].star_pt.y; s.origin.x = (COORD)((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox; s.origin.y = (COORD)((long)(MAX_Y_UNIVERSE - ey) * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy; s.frame = SetAbsFrameIndex (stars_in_space, 95); DrawStamp (&s); ex -= universe.x; if (ex < 0) ex = -ex; ey -= universe.y; if (ey < 0) ey = -ey; if (ex > (XOFFS / NUM_RADAR_SCREENS) || ey > (YOFFS / NUM_RADAR_SCREENS)) continue; hHyperSpaceElement = AllocHyperElement (&SD[i].star_pt); if (hHyperSpaceElement == 0) continue; LockElement (hHyperSpaceElement, &HyperSpaceElementPtr); HyperSpaceElementPtr->current.image.frame = SetAbsFrameIndex ( hyperstars[1 + (GET_GAME_STATE (ARILOU_SPACE_SIDE) >> 1)], SD[i].Index); HyperSpaceElementPtr->preprocess_func = NULL; HyperSpaceElementPtr->postprocess_func = NULL; HyperSpaceElementPtr->collision_func = arilou_space_collision; SetUpElement (HyperSpaceElementPtr); if (arilouSpaceSide == 1 || arilouSpaceSide == 2) HyperSpaceElementPtr->death_func = arilou_space_death; else { HyperSpaceElementPtr->death_func = NULL; HyperSpaceElementPtr->IntersectControl.IntersectStamp.frame = DecFrameIndex (stars_in_space); } UnlockElement (hHyperSpaceElement); InsertElement (hHyperSpaceElement, GetHeadElement ()); } while (i); } { SDPtr = 0; while ((SDPtr = FindStar (SDPtr, &universe, XOFFS, YOFFS))) { BYTE star_type; ex = SDPtr->star_pt.x - universe.x; if (ex < 0) ex = -ex; ey = SDPtr->star_pt.y - universe.y; if (ey < 0) ey = -ey; if (ex > (XOFFS / NUM_RADAR_SCREENS) || ey > (YOFFS / NUM_RADAR_SCREENS)) continue; hHyperSpaceElement = AllocHyperElement (&SDPtr->star_pt); if (hHyperSpaceElement == 0) continue; star_type = SDPtr->Type; LockElement (hHyperSpaceElement, &HyperSpaceElementPtr); HyperSpaceElementPtr->current.image.frame = SetAbsFrameIndex ( hyperstars[1 + (GET_GAME_STATE (ARILOU_SPACE_SIDE) >> 1)], STAR_TYPE (star_type) * NUM_STAR_COLORS + STAR_COLOR (star_type)); HyperSpaceElementPtr->preprocess_func = NULL; HyperSpaceElementPtr->postprocess_func = NULL; HyperSpaceElementPtr->collision_func = hyper_collision; SetUpElement (HyperSpaceElementPtr); if (SDPtr == CurStarDescPtr && GET_GAME_STATE (PORTAL_COUNTER) == 0) HyperSpaceElementPtr->death_func = hyper_death; else { HyperSpaceElementPtr->death_func = NULL; HyperSpaceElementPtr->IntersectControl.IntersectStamp.frame = DecFrameIndex (stars_in_space); } UnlockElement (hHyperSpaceElement); InsertElement (hHyperSpaceElement, GetHeadElement ()); } ProcessEncounters (&universe, ox, oy); } s.origin.x = RADAR_WIDTH >> 1; s.origin.y = RADAR_HEIGHT >> 1; s.frame = blip_frame; DrawStamp (&s); { // draws borders to mini-map RECT r; SetContextForeGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x0E, 0x0E, 0x0E), 0x00)); r.corner.x = 0; r.corner.y = 0; r.extent.width = RADAR_WIDTH - 1; r.extent.height = 1; DrawFilledRectangle (&r); r.extent.width = 1; r.extent.height = RADAR_HEIGHT - 1; DrawFilledRectangle (&r); SetContextForeGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x06, 0x06, 0x06), 0x00)); r.corner.x = RADAR_WIDTH - 1; r.corner.y = 1; r.extent.height = RADAR_HEIGHT - 1; DrawFilledRectangle (&r); r.corner.x = 1; r.corner.y = RADAR_HEIGHT - 1; r.extent.width = RADAR_WIDTH - 2; r.extent.height = 1; DrawFilledRectangle (&r); SetContextForeGroundColor ( BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x00)); r.corner.x = 0; DrawPoint (&r.corner); r.corner.x = RADAR_WIDTH - 1; r.corner.y = 0; DrawPoint (&r.corner); } UnbatchGraphics (); SetContext (StatusContext); if (!(LOWORD (TFB_Random ()) & 7)) AddAmbientElement (); if (universe.x != GLOBAL (ShipStamp.origin.x) || universe.y != GLOBAL (ShipStamp.origin.y)) { GLOBAL (ShipStamp.origin) = universe; DrawHyperCoords (universe); } } static BOOLEAN DoHyperspaceMenu (MENU_STATE *pMS) { BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT]; BOOLEAN handled; if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)) || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0) return FALSE; handled = DoMenuChooser (pMS, PM_STARMAP); if (handled) return TRUE; if (!select) return TRUE; SetFlashRect (NULL); switch (pMS->CurState) { case EQUIP_DEVICE: select = DevicesMenu (); if (GET_GAME_STATE (PORTAL_COUNTER)) { // A player-induced portal to QuasiSpace is opening return FALSE; } if (GLOBAL (CurrentActivity) & START_ENCOUNTER) { // Selected Talking Pet, going into conversation return FALSE; } break; case CARGO: CargoMenu (); break; case ROSTER: select = RosterMenu (); break; case GAME_MENU: if (!GameOptions ()) return FALSE; // abort or load break; case STARMAP: StarMap (); return FALSE; case NAVIGATION: return FALSE; } if (!(GLOBAL (CurrentActivity) & CHECK_ABORT)) { if (select) { // 3DO menu jumps to NAVIGATE after a successful submenu run if (optWhichMenu != OPT_PC) pMS->CurState = NAVIGATION; DrawMenuStateStrings (PM_STARMAP, pMS->CurState); } SetFlashRect (SFR_MENU_3DO); } return TRUE; } void HyperspaceMenu (void) { Color OldColor; CONTEXT OldContext; MENU_STATE MenuState; UnbatchGraphics (); OldContext = SetContext (SpaceContext); OldColor = SetContextBackGroundColor (BLACK_COLOR); memset (&MenuState, 0, sizeof (MenuState)); MenuState.InputFunc = DoHyperspaceMenu; MenuState.Initialized = TRUE; MenuState.CurState = STARMAP; DrawMenuStateStrings (PM_STARMAP, STARMAP); SetFlashRect (SFR_MENU_3DO); SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT); DoInput (&MenuState, TRUE); SetFlashRect (NULL); SetContext (SpaceContext); if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))) { ClearSISRect (CLEAR_SIS_RADAR); WaitForNoInput (ONE_SECOND / 2, FALSE); } SetContextBackGroundColor (OldColor); SetContext (OldContext); if (!(GLOBAL (CurrentActivity) & IN_BATTLE)) cleanup_hyperspace (); BatchGraphics (); } void SaveSisHyperState (void) { HELEMENT hSisElement; ELEMENT *ElementPtr; STARSHIP *StarShipPtr; // Update 'GLOBAL (ShipFacing)' to the direction the flagship is facing hSisElement = getSisElement (); if (!hSisElement) { // Happens when saving a game from Hyperspace encounter screen return; } //if (ElementPtr->state_flags & PLAYER_SHIP) LockElement (hSisElement, &ElementPtr); GetElementStarShip (ElementPtr, &StarShipPtr); // XXX: Solar system reentry test depends on ShipFacing != 0 GLOBAL (ShipFacing) = StarShipPtr->ShipFacing + 1; UnlockElement (hSisElement); }