diff options
Diffstat (limited to 'src/uqm/hyper.c')
-rw-r--r-- | src/uqm/hyper.c | 1747 |
1 files changed, 1747 insertions, 0 deletions
diff --git a/src/uqm/hyper.c b/src/uqm/hyper.c new file mode 100644 index 0000000..9a9b9f4 --- /dev/null +++ b/src/uqm/hyper.c @@ -0,0 +1,1747 @@ +//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); +} + |