From 7f6002caba3f0a6749820c2772161caf55b8d267 Mon Sep 17 00:00:00 2001 From: neonloop Date: Fri, 7 May 2021 20:00:12 +0000 Subject: Initial commit (uqm-0.8.0) --- src/uqm/battle.c | 512 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 src/uqm/battle.c (limited to 'src/uqm/battle.c') diff --git a/src/uqm/battle.c b/src/uqm/battle.c new file mode 100644 index 0000000..f33e66d --- /dev/null +++ b/src/uqm/battle.c @@ -0,0 +1,512 @@ +//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 "battle.h" + +#include "battlecontrols.h" +#include "controls.h" +#include "init.h" +#include "element.h" +#include "ship.h" +#include "process.h" +#include "tactrans.h" + // for flee_preprocess() +#include "intel.h" +#ifdef NETPLAY +# include "supermelee/netplay/netmelee.h" +# ifdef NETPLAY_CHECKSUM +# include "supermelee/netplay/checksum.h" +# endif +# include "supermelee/netplay/notifyall.h" +#endif +#include "supermelee/pickmele.h" +#include "resinst.h" +#include "nameref.h" +#include "setup.h" +#include "settings.h" +#include "sounds.h" +#include "libs/async.h" +#include "libs/graphics/gfx_common.h" +#include "libs/log.h" +#include "libs/mathlib.h" + + +BYTE battle_counter[NUM_SIDES]; + // The number of ships still available for battle to each side. + // A ship that has warped out is no longer available. +BOOLEAN instantVictory; +size_t battleInputOrder[NUM_SIDES]; + // Indices of the sides in the order their input is processed. + // Network sides are last so that the sides will never be waiting + // on eachother, and games with a 0 frame delay are theoretically + // possible. +#ifdef NETPLAY +BattleFrameCounter battleFrameCount; + // Used for synchronisation purposes during netplay. +#endif + +static BOOLEAN +RunAwayAllowed (void) +{ + return (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER + || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) + && GET_GAME_STATE (STARBASE_AVAILABLE) + && !GET_GAME_STATE (BOMB_CARRIER); +} + +static void +DoRunAway (STARSHIP *StarShipPtr) +{ + ELEMENT *ElementPtr; + + LockElement (StarShipPtr->hShip, &ElementPtr); + if (GetPrimType (&DisplayArray[ElementPtr->PrimIndex]) == STAMP_PRIM + && ElementPtr->life_span == NORMAL_LIFE + && !(ElementPtr->state_flags & FINITE_LIFE) + && ElementPtr->mass_points != MAX_SHIP_MASS * 10 + && !(ElementPtr->state_flags & APPEARING)) + { + battle_counter[0]--; + + ElementPtr->turn_wait = 3; + ElementPtr->thrust_wait = 4; + ElementPtr->colorCycleIndex = 0; + ElementPtr->preprocess_func = flee_preprocess; + ElementPtr->mass_points = MAX_SHIP_MASS * 10; + ZeroVelocityComponents (&ElementPtr->velocity); + StarShipPtr->cur_status_flags &= + ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED); + + SetPrimColor (&DisplayArray[ElementPtr->PrimIndex], + BUILD_COLOR (MAKE_RGB15 (0x0B, 0x00, 0x00), 0x2E)); + // XXX: I think this is supposed to be the same as the + // first entry of the color cycle table in flee_preeprocess, + // but it is slightly different (0x0A as red value). - SvdB. + SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMPFILL_PRIM); + + StarShipPtr->ship_input_state = 0; + } + UnlockElement (StarShipPtr->hShip); +} + +static void +setupBattleInputOrder(void) +{ + size_t i; + +#ifndef NETPLAY + for (i = 0; i < NUM_SIDES; i++) + battleInputOrder[i] = i; +#else + int j; + + i = 0; + // First put the locally controlled players in the array. + for (j = 0; j < NUM_SIDES; j++) { + if (!(PlayerControl[j] & NETWORK_CONTROL)) { + battleInputOrder[i] = j; + i++; + } + } + + // Next put the network controlled players in the array. + for (j = 0; j < NUM_SIDES; j++) { + if (PlayerControl[j] & NETWORK_CONTROL) { + battleInputOrder[i] = j; + i++; + } + } +#endif +} + +BATTLE_INPUT_STATE +frameInputHuman (HumanInputContext *context, STARSHIP *StarShipPtr) +{ + (void) StarShipPtr; + return CurrentInputToBattleInput (context->playerNr); +} + +static void +ProcessInput (void) +{ + BOOLEAN CanRunAway; + size_t sideI; + +#ifdef NETPLAY + netInput (); +#endif + + CanRunAway = RunAwayAllowed (); + + for (sideI = 0; sideI < NUM_SIDES; sideI++) + { + HSTARSHIP hBattleShip, hNextShip; + size_t cur_player = battleInputOrder[sideI]; + + for (hBattleShip = GetHeadLink (&race_q[cur_player]); + hBattleShip != 0; hBattleShip = hNextShip) + { + BATTLE_INPUT_STATE InputState; + STARSHIP *StarShipPtr; + + StarShipPtr = LockStarShip (&race_q[cur_player], hBattleShip); + hNextShip = _GetSuccLink (StarShipPtr); + + if (StarShipPtr->hShip) + { + // TODO: review and see if we have to do this every frame, or + // if we can do this once somewhere + StarShipPtr->control = PlayerControl[cur_player]; + + InputState = PlayerInput[cur_player]->handlers->frameInput ( + PlayerInput[cur_player], StarShipPtr); + +#if CREATE_JOURNAL + JournalInput (InputState); +#endif /* CREATE_JOURNAL */ +#ifdef NETPLAY + if (!(PlayerControl[cur_player] & NETWORK_CONTROL)) + { + BattleInputBuffer *bib = getBattleInputBuffer(cur_player); + Netplay_NotifyAll_battleInput (InputState); + flushPacketQueues (); + + BattleInputBuffer_push (bib, InputState); + // Add this input to the end of the buffer. + BattleInputBuffer_pop (bib, &InputState); + // Get the input from the front of the buffer. + } +#endif + + StarShipPtr->ship_input_state = 0; + if (StarShipPtr->RaceDescPtr->ship_info.crew_level) + { + if (InputState & BATTLE_LEFT) + StarShipPtr->ship_input_state |= LEFT; + else if (InputState & BATTLE_RIGHT) + StarShipPtr->ship_input_state |= RIGHT; + if (InputState & BATTLE_THRUST) + StarShipPtr->ship_input_state |= THRUST; + if (InputState & BATTLE_WEAPON) + StarShipPtr->ship_input_state |= WEAPON; + if (InputState & BATTLE_SPECIAL) + StarShipPtr->ship_input_state |= SPECIAL; + + if (CanRunAway && cur_player == 0 && + (InputState & BATTLE_ESCAPE)) + DoRunAway (StarShipPtr); + } + } + + UnlockStarShip (&race_q[cur_player], hBattleShip); + } + } + +#ifdef NETPLAY + flushPacketQueues (); +#endif + + if (GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT)) + GLOBAL (CurrentActivity) &= ~IN_BATTLE; +} + +#if DEMO_MODE || CREATE_JOURNAL +DWORD BattleSeed; +#endif /* DEMO_MODE */ + +static MUSIC_REF BattleRef; + +void +BattleSong (BOOLEAN DoPlay) +{ + if (BattleRef == 0) + { + if (inHyperSpace ()) + BattleRef = LoadMusic (HYPERSPACE_MUSIC); + else if (inQuasiSpace ()) + BattleRef = LoadMusic (QUASISPACE_MUSIC); + else + BattleRef = LoadMusic (BATTLE_MUSIC); + } + + if (DoPlay) + PlayMusic (BattleRef, TRUE, 1); +} + +void +FreeBattleSong (void) +{ + DestroyMusic (BattleRef); + BattleRef = 0; +} + +static BOOLEAN +DoBattle (BATTLE_STATE *bs) +{ + extern UWORD nth_frame; + RECT r; + BYTE battle_speed; + + SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE); + +#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM) + if (getNumNetConnections() > 0 && + battleFrameCount % NETPLAY_CHECKSUM_INTERVAL == 0) + { + crc_State state; + Checksum checksum; + + crc_init(&state); + crc_processState (&state); + checksum = (Checksum) crc_finish (&state); + + Netplay_NotifyAll_checksum ((uint32) battleFrameCount, + (uint32) checksum); + flushPacketQueues (); + addLocalChecksum (battleFrameCount, checksum); + } +#endif + ProcessInput (); + // Also calls NetInput() +#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM) + if (getNumNetConnections() > 0) + { + size_t delay = getBattleInputDelay(); + + if (battleFrameCount >= delay + && (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0) + { + if (!(GLOBAL (CurrentActivity) & CHECK_ABORT)) + { + if (!verifyChecksums (battleFrameCount - delay)) { + GLOBAL(CurrentActivity) |= CHECK_ABORT; + resetConnections (ResetReason_syncLoss); + } + } + } + } +#endif + + if (bs->first_time) + { + r.corner.x = SIS_ORG_X; + r.corner.y = SIS_ORG_Y; + r.extent.width = SIS_SCREEN_WIDTH; + r.extent.height = SIS_SCREEN_HEIGHT; + SetTransitionSource (&r); + } + BatchGraphics (); + + // Call the callback function, if set + if (bs->frame_cb) + bs->frame_cb (); + + RedrawQueue (TRUE); + + if (bs->first_time) + { + bs->first_time = FALSE; + ScreenTransition (3, &r); + } + UnbatchGraphics (); + if ((!(GLOBAL (CurrentActivity) & IN_BATTLE)) || + (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))) + { + return FALSE; + } + + battle_speed = HIBYTE (nth_frame); + if (battle_speed == (BYTE)~0) + { // maximum speed, nothing rendered at all + Async_process (); + TaskSwitch (); + } + else + { + SleepThreadUntil (bs->NextTime + + BATTLE_FRAME_RATE / (battle_speed + 1)); + bs->NextTime = GetTimeCounter (); + } + + if ((GLOBAL (CurrentActivity) & IN_BATTLE) == 0) + return FALSE; + +#ifdef NETPLAY + battleFrameCount++; +#endif + return TRUE; +} + +#ifdef NETPLAY +COUNT +GetPlayerOrder (COUNT i) +{ + // Iff 'myTurn' is set on a connection, the local party will be + // processed first. + // If neither is network controlled, the top player (1) is handled + // first. + if (((PlayerControl[0] & NETWORK_CONTROL) && + !NetConnection_getDiscriminant (netConnections[0])) || + ((PlayerControl[1] & NETWORK_CONTROL) && + NetConnection_getDiscriminant (netConnections[1]))) + return i; + else + return 1 - i; +} +#endif + +// Let each player pick his ship. +static BOOLEAN +selectAllShips (SIZE num_ships) +{ + if (num_ships == 1) { + // HyperSpace in full game. + return GetNextStarShip (NULL, 0); + } + +#ifdef NETPLAY + if ((PlayerControl[0] & NETWORK_CONTROL) && + (PlayerControl[1] & NETWORK_CONTROL)) + { + log_add (log_Error, "Only one side at a time can be network " + "controlled.\n"); + return FALSE; + } +#endif + + return GetInitialStarShips (); +} + +BOOLEAN +Battle (BattleFrameCallback *callback) +{ + SIZE num_ships; + + +#if !(DEMO_MODE || CREATE_JOURNAL) + if (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE) { + // In Supermelee, the RNG is already initialised. + TFB_SeedRandom (GetTimeCounter ()); + } +#else /* DEMO_MODE */ + if (BattleSeed == 0) + BattleSeed = TFB_Random (); + TFB_SeedRandom (BattleSeed); + BattleSeed = TFB_Random (); /* get next battle seed */ +#endif /* DEMO_MODE */ + + BattleSong (FALSE); + + num_ships = InitShips (); + + if (instantVictory) + { + num_ships = 0; + battle_counter[0] = 1; + battle_counter[1] = 0; + instantVictory = FALSE; + } + + if (num_ships) + { + BATTLE_STATE bs; + + GLOBAL (CurrentActivity) |= IN_BATTLE; + battle_counter[0] = CountLinks (&race_q[0]); + battle_counter[1] = CountLinks (&race_q[1]); + + if (optMeleeScale != TFB_SCALE_STEP) + SetGraphicScaleMode (optMeleeScale); + + setupBattleInputOrder (); +#ifdef NETPLAY + initBattleInputBuffers (); +#ifdef NETPLAY_CHECKSUM + initChecksumBuffers (); +#endif /* NETPLAY_CHECKSUM */ + battleFrameCount = 0; + ResetWinnerStarShip (); + setBattleStateConnections (&bs); +#endif /* NETPLAY */ + + if (!selectAllShips (num_ships)) { + GLOBAL (CurrentActivity) |= CHECK_ABORT; + goto AbortBattle; + } + + BattleSong (TRUE); + bs.NextTime = 0; +#ifdef NETPLAY + initBattleStateDataConnections (); + { + bool allOk = negotiateReadyConnections (true, NetState_inBattle); + if (!allOk) { + GLOBAL (CurrentActivity) |= CHECK_ABORT; + goto AbortBattle; + } + } +#endif /* NETPLAY */ + bs.InputFunc = DoBattle; + bs.frame_cb = callback; + bs.first_time = inHQSpace (); + + DoInput (&bs, FALSE); + +AbortBattle: + if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) + { + if (GLOBAL (CurrentActivity) & CHECK_ABORT) + { + // Do not return to the main menu when a game is aborted, + // (just to the supermelee menu). +#ifdef NETPLAY + waitResetConnections(NetState_inSetup); + // A connection may already be in inSetup (set from + // GetMeleeStarship). This is not a problem, although + // it will generate a warning in debug mode. +#endif + + GLOBAL (CurrentActivity) &= ~CHECK_ABORT; + } + else + { + // Show the result of the battle. + MeleeGameOver (); + } + } + +#ifdef NETPLAY + uninitBattleInputBuffers(); +#ifdef NETPLAY_CHECKSUM + uninitChecksumBuffers (); +#endif /* NETPLAY_CHECKSUM */ + setBattleStateConnections (NULL); +#endif /* NETPLAY */ + + StopDitty (); + StopMusic (); + StopSound (); + } + + UninitShips (); + FreeBattleSong (); + + + return (BOOLEAN) (num_ships < 0); +} + -- cgit v1.2.3