diff --git a/sc2/ChangeLog b/sc2/ChangeLog index d4599e5d4..a51eec856 100644 --- a/sc2/ChangeLog +++ b/sc2/ChangeLog @@ -1,4 +1,6 @@ Changes towards version 0.7: +- Concurrent supermelee ship selection - SvdB +- New generic, unthreaded flashing code - SvdB - Cleanup of 3DO ship spin support; spin speech works now - Alex - No longer creating and mounting a temporary directory. It is no longer used, but it might be again at some point, for loadable modules. - SvdB diff --git a/sc2/content/lbm/starcon.txt b/sc2/content/lbm/starcon.txt index 5f2a1bebe..98ac9caa5 100644 --- a/sc2/content/lbm/starcon.txt +++ b/sc2/content/lbm/starcon.txt @@ -2361,6 +2361,10 @@ Connection aborted due to version mismatch. Connection aborted due to an internal protocol error. +#(Connection aborted because the remote side sent a fake signature.) +Connection aborted because the remote side sent a fake signature. + + #(Game aborted for an unspecified reason.) Game aborted for an unspecified reason. diff --git a/sc2/src/msvc++/UrQuanMasters.dsp b/sc2/src/msvc++/UrQuanMasters.dsp index 63c268dda..2e2712078 100644 --- a/sc2/src/msvc++/UrQuanMasters.dsp +++ b/sc2/src/msvc++/UrQuanMasters.dsp @@ -3972,6 +3972,14 @@ SOURCE=..\sc2code\encount.h # End Source File # Begin Source File +SOURCE=..\sc2code\flash.c +# End Source File +# Begin Source File + +SOURCE=..\sc2code\flash.h +# End Source File +# Begin Source File + SOURCE=..\sc2code\fmv.c # End Source File # Begin Source File diff --git a/sc2/src/sc2code/Makeinfo b/sc2/src/sc2code/Makeinfo index 69503e179..f61a8c973 100644 --- a/sc2/src/sc2code/Makeinfo +++ b/sc2/src/sc2code/Makeinfo @@ -1,7 +1,7 @@ uqm_SUBDIRS="comm libs planets ships" uqm_CFILES="battle.c border.c build.c cleanup.c clock.c cnctdlg.c collide.c comm.c commanim.c commglue.c confirm.c credits.c cyborg.c - demo.c displist.c dummy.c encount.c fmv.c galaxy.c gameev.c + demo.c displist.c dummy.c encount.c flash.c fmv.c galaxy.c gameev.c gameinp.c gameopt.c gendef.c getchar.c globdata.c gravity.c gravwell.c grpinfo.c hyper.c init.c intel.c intro.c ipdisp.c load.c loadship.c master.c melee.c menu.c misc.c mouse_err.c diff --git a/sc2/src/sc2code/battle.c b/sc2/src/sc2code/battle.c index 099c1d80e..5911aadfc 100644 --- a/sc2/src/sc2code/battle.c +++ b/sc2/src/sc2code/battle.c @@ -340,34 +340,34 @@ DoBattle (BATTLE_STATE *bs) 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) { -#ifndef NETPLAY - while (num_ships--) - { - if (!GetNextStarShip (NULL, num_ships == 1)) - return FALSE; - } - return TRUE; -#else - // On network play, what is the top player to one party may be - // the bottom player the other. To keep both sides synchronised - // the order is always the same. - SIZE order[NUM_PLAYERS]; - if (num_ships == 1) { // HyperSpace in full game. return GetNextStarShip (NULL, 0); } - - if (num_ships != 2) - { - log_add (log_Error, "More than two players is not supported.\n"); - return FALSE; - } - + +#ifdef NETPLAY if ((PlayerControl[0] & NETWORK_CONTROL) && (PlayerControl[1] & NETWORK_CONTROL)) { @@ -375,34 +375,9 @@ selectAllShips (SIZE num_ships) "controlled.\n"); return FALSE; } +#endif - // 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]))) - { - order[0] = 0; - order[1] = 1; - } - else - { - order[0] = 1; - order[1] = 0; - } - - { - size_t i; - for (i = 0; i < NUM_PLAYERS; i++) - { - if (!GetNextStarShip (NULL, order[i])) - return FALSE; - } - } - return TRUE; -#endif + return GetInitialStarShips (); } BOOLEAN diff --git a/sc2/src/sc2code/battle.h b/sc2/src/sc2code/battle.h index f9cd28de4..6839985d9 100644 --- a/sc2/src/sc2code/battle.h +++ b/sc2/src/sc2code/battle.h @@ -35,6 +35,9 @@ extern BOOLEAN instantVictory; typedef DWORD BattleFrameCounter; extern BattleFrameCounter battleFrameCount; extern COUNT currentDeadSide; +COUNT GetPlayerOrder (COUNT i); +#else +# define GetPlayerOrder(i) (i) #endif extern BOOLEAN Battle (void); diff --git a/sc2/src/sc2code/element.h b/sc2/src/sc2code/element.h index 8d176bcce..51a725d9e 100644 --- a/sc2/src/sc2code/element.h +++ b/sc2/src/sc2code/element.h @@ -54,6 +54,7 @@ typedef QUEUE_HANDLE HELEMENT; #define APPEARING (1 << 3) #define DISAPPEARING (1 << 4) #define CHANGING (1 << 5) + // The element's graphical representation has changed. #define NONSOLID (1 << 6) #define COLLISION (1 << 7) diff --git a/sc2/src/sc2code/flash.c b/sc2/src/sc2code/flash.c new file mode 100644 index 000000000..f27537491 --- /dev/null +++ b/sc2/src/sc2code/flash.c @@ -0,0 +1,721 @@ +/* + * 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 + */ + +// NOTE: A lot of this code is untested. Only highlite flash areas drawing +// directly to the screen, using a cache are currently in use. + +// TODO: +// - Add Flash_setHighlight() to change the brightness as in +// Flash_createHighlight(), for an already created flash area. +// - Add Flash_setOverlay() to change the overlay frame as in +// Flash_createOverlay(), for an already created flash area. + + +#define FLASH_INTERNAL +#include "flash.h" + +#include "setup.h" +#include "libs/log.h" +#include "libs/misc.h" +#include "libs/threadlib.h" + + +extern void arith_frame_blit (FRAME srcFrame, const RECT *rsrc, + FRAME dstFrame, RECT *rdst, int num, int denom); + +static FlashContext *Flash_create (CONTEXT gfxContext, FRAME parent); +static void Flash_fixState (FlashContext *context); +static void Flash_nextState (FlashContext *context); +static void Flash_clearCache (FlashContext *context); +static void Flash_initCache (FlashContext *context); +static void Flash_grabOriginal (FlashContext *context); +static void Flash_blendFraction (FlashContext *context, int numer, int denom, + int *resNumer, int *resDenom); +static void Flash_makeFrame (FlashContext *context, + FRAME dest, RECT *destRect, int numer, int denom); +static inline void Flash_prepareCacheFrame (FlashContext *context, + COUNT index); +static void Flash_drawFrame (FlashContext *context, FRAME frame); +static void Flash_drawCacheFrame (FlashContext *context, COUNT index); +static inline void Flash_drawUncachedFrame (FlashContext *context, + int numer, int denom); +static inline void Flash_drawCachedFrame (FlashContext *context, + int numer, int denom); +static void Flash_drawCurrentFrame (FlashContext *context); + +static FlashContext * +Flash_create (CONTEXT gfxContext, FRAME parent) +{ + FlashContext *context = HMalloc (sizeof (FlashContext)); + + context->gfxContext = gfxContext; + context->parent = parent; + + context->original = 0; + + context->fadeInTime = Flash_DEFAULT_FADE_IN_TIME; + context->onTime = Flash_DEFAULT_ON_TIME; + context->fadeOutTime = Flash_DEFAULT_FADE_OUT_TIME; + context->offTime = Flash_DEFAULT_OFF_TIME; + + context->frameTime = 0; + + context->state = FlashState_off; + context->lastStateTime = 0; + context->lastFrameTime = 0; + + context->started = false; + context->paused = false; + + context->cache = 0; + context->cacheSize = Flash_DEFAULT_CACHE_SIZE; + + context->lastFrameIndex = (COUNT) -1; + + return context; +} + +// 'startNumer / denom' is the brightness in the start state of the flash. +// 'endNumer / denom' is the brightness in the end state of the flash. +// These numbers are relative to the brighness of the original image. +FlashContext * +Flash_createHighlight (CONTEXT gfxContext, FRAME parent, const RECT *rect, + int startNumer, int endNumer, int denom) +{ + FlashContext *context = Flash_create (gfxContext, parent); + + if (rect == NULL) + { + // No rectangle specified. Should be later with Flash_setRect() + // before calling Flash_start(). + context->rect.corner.x = 0; + context->rect.corner.y = 0; + context->rect.extent.width = 0; + context->rect.extent.height = 0; + } + else + context->rect = *rect; + context->type = FlashType_highlight; + + context->u.highlight.startNumer = startNumer; + context->u.highlight.endNumer = endNumer; + context->u.highlight.denom = denom; + + return context; +} + +FlashContext * +Flash_createTransition (CONTEXT gfxContext, FRAME parent, + const POINT *origin, FRAME first, FRAME final) +{ + FlashContext *context = Flash_create (gfxContext, parent); + + context->type = FlashType_transition; + + context->u.transition.first = first; + context->u.transition.final = final; + GetFrameRect (final, &context->rect); + context->rect.corner = *origin; + + return context; +} + +FlashContext * +Flash_createOverlay (CONTEXT gfxContext, FRAME parent, + const POINT *origin, FRAME overlay) +{ + FlashContext *context = Flash_create (gfxContext, parent); + + context->type = FlashType_overlay; + + context->u.overlay.frame = overlay; + GetFrameRect (overlay, &context->rect); + context->rect.corner = *origin; + + return context; +} + +void +Flash_setState (FlashContext *context, FlashState state) +{ + TimeCount now; + + now = GetTimeCounter (); + + context->state = state; + Flash_fixState (context); + Flash_drawCurrentFrame (context); + + context->lastStateTime = now; + context->lastFrameTime = now; +} + +void +Flash_start (FlashContext *context) +{ + if (context->started) + { + log_add (log_Warning, "Flash_start() called on already started " + "FlashContext.\n"); + return; + } + + Flash_initCache (context); + + context->started = true; + context->paused = false; + + Flash_grabOriginal (context); + context->lastFrameIndex = 0; + + Flash_fixState (context); + Flash_drawCurrentFrame (context); +} + +void +Flash_terminate (FlashContext *context) +{ + if (context->started) + { + // Restore the flash rectangle: + Flash_drawFrame (context, context->original); + + Flash_clearCache (context); + DestroyDrawable (ReleaseDrawable (context->original)); + } + + HFree (context); +} + +void +Flash_pause (FlashContext *context) +{ + context->paused = true; +} + +void +Flash_continue (FlashContext *context) +{ + context->paused = false; +} + +// Change the state to the next state as long as the current state has +// a zero-time duration. +static void +Flash_fixState (FlashContext *context) +{ + TimeCount stateTime; + + for (;;) { + switch (context->state) { + case FlashState_fadeIn: + stateTime = context->fadeInTime; + break; + case FlashState_on: + stateTime = context->onTime; + break; + case FlashState_fadeOut: + stateTime = context->fadeOutTime; + break; + case FlashState_off: + stateTime = context->offTime; + break; + } + if (stateTime != 0) + break; + context->state = (context->state + 1) & 0x3; + } +} + +static void +Flash_nextState (FlashContext *context) +{ + context->state = (context->state + 1) & 0x3; + Flash_fixState (context); +} + +void +Flash_process (FlashContext *context) +{ + TimeCount now; + + if (!context->started || context->paused) + return; + + now = GetTimeCounter (); + + if (context->state == FlashState_fadeIn) + { + if (now >= context->lastStateTime + context->fadeInTime) + { + Flash_nextState (context); + context->lastStateTime = now; + } + context->lastFrameTime = now; + } + else if (context->state == FlashState_on) + { + if (now < context->lastStateTime + context->onTime) + return; + Flash_nextState (context); + context->lastStateTime = now; + } + else if (context->state == FlashState_fadeOut) + { + if (now >= context->lastStateTime + context->fadeOutTime) + { + Flash_nextState (context); + context->lastStateTime = now; + } + context->lastFrameTime = now; + } + else /* context->state == FlashState_off */ + { + if (now < context->lastStateTime + context->offTime) + return; + Flash_nextState (context); + context->lastStateTime = now; + } + + Flash_drawCurrentFrame (context); +} + +void +Flash_setSpeed (FlashContext *context, TimeCount fadeInTime, + TimeCount onTime, TimeCount fadeOutTime, TimeCount offTime) +{ + context->fadeInTime = fadeInTime; + context->onTime = onTime; + context->fadeOutTime = fadeOutTime; + context->offTime = offTime; +} + +// Set the time between updates of the flash area. +void +Flash_setFrameTime (FlashContext *context, TimeCount frameTime) { + context->frameTime = frameTime; +} + +// Returns the time when the flash area is to be updated. +TimeCount +Flash_nextTime (FlashContext *context) +{ + if (!context->started || context->paused) + return (TimeCount) -1; + + if (context->state == FlashState_fadeIn || + context->state == FlashState_fadeOut) + { + // When we're fading in or out, we need updates during + // the fade. + return context->lastFrameTime + context->frameTime; + } + else + { + // When the flash area is completely on or off, we don't + // need an update until we're ready to change state again. + if (context->state == FlashState_on) + return context->lastStateTime + context->onTime; + else /* context->state == FlashState_off */ + return context->lastStateTime + context->offTime; + } +} + +static void +Flash_clearCache (FlashContext *context) +{ + COUNT i; + + if (context->type == FlashType_transition || + context->type == FlashType_overlay) + { + // First frame is not allocated by the flash code, so + // we shouldn't free it. + context->cache[0] = (FRAME) 0; + } + if (context->type == FlashType_transition) + { + // Final frame is not allocated by the flash code, so + // we shouldn't free it. + context->cache[context->cacheSize - 1] = (FRAME) 0; + } + + for (i = 0; i < context->cacheSize; i++) + { + if (context->cache[i] != (FRAME) 0) + { + DestroyDrawable (ReleaseDrawable (context->cache[i])); + context->cache[i] = (FRAME) 0; + } + } +} + +void +Flash_setRect (FlashContext *context, const RECT *rect) +{ + if (context->started) + { + Flash_drawFrame (context, context->original); + Flash_clearCache (context); + } + + context->rect = *rect; + context->lastFrameIndex = (COUNT) -1; + + if (context->started) + { + Flash_grabOriginal (context); + Flash_drawCurrentFrame (context); + } +} + +void +Flash_getRect (FlashContext *context, RECT *rect) +{ + assert (!context->type == FlashType_highlight); + + *rect = context->rect; +} + +// Call before you update the graphics in the currently flashing area. +void +Flash_preUpdate (FlashContext *context) +{ + Flash_drawFrame (context, context->original); + Flash_clearCache (context); +} + +// Call after you update the graphics in the currently flashing area. +void +Flash_postUpdate (FlashContext *context) +{ + Flash_grabOriginal (context); + Flash_drawCurrentFrame (context); +} + +// Pre: context->original has been initialised. +static void +Flash_initCache (FlashContext *context) +{ + COUNT i; + + context->cache = HMalloc (context->cacheSize * sizeof (FRAME)); + for (i = 0; i < context->cacheSize; i++) + context->cache[i] = (FRAME) 0; +} + +void +Flash_setCacheSize (FlashContext *context, COUNT size) +{ + assert (size == 0 || size >= 2); + + if (context->cache != NULL) + { + Flash_clearCache (context); + HFree (context->cache); + context->cache = NULL; + } + + context->cacheSize = size; + + if (size != 0) + Flash_initCache (context); +} + +COUNT +Flash_getCacheSize (const FlashContext *context) +{ + return context->cacheSize; +} + +static void +Flash_grabOriginal (FlashContext *context) +{ + if (context->original != (FRAME) 0) + DestroyDrawable (ReleaseDrawable (context->original)); + + if (context->parent == 0) + { + // Grab from the entire screen + LockMutex (GraphicsLock); + CONTEXT oldGfxContext = SetContext (context->gfxContext); + context->original = CaptureDrawable (LoadDisplayPixmap ( + &context->rect, (FRAME) 0)); + SetContext (oldGfxContext); + UnlockMutex (GraphicsLock); + FlushGraphics (); + // LoadDisplayPixmap only queues the command to read + // a rectangle from the screen; a FlushGraphics() + // is necessary to ensure that it can actually be used. + } + else + { + // Grab from a frame. + context->original = CaptureDrawable (CreateDrawable (WANT_PIXMAP, + context->rect.extent.width, context->rect.extent.height, 1)); + arith_frame_blit (context->parent, &context->rect, + context->original, NULL, 1, 1); + } +} + +static inline void +Flash_blendFraction (FlashContext *context, int numer, int denom, + int *resNumer, int *resDenom) +{ + // This function merges two fractions (F0 and F1), + // based on another fraction (P) (yielding R). + // F0 = context->u.highlight.startNumer / context->u.highlight.denom + // F1 = context->u.highlight.endNumer / context->u.highlight.denom + // P = *numer / *denom + // R = P * F1 + (1 - P) * F0 + // = numer * context->u.highlight.endNumer / + // (denom * context->u.highlight.denom) + + // (denom - numer) * u.highlight.startNumer / + // denom * context->u.highlight.denom + + assert (numer >= 0 && numer <= denom); + + *resNumer = numer * context->u.highlight.endNumer + + (denom - numer) * context->u.highlight.startNumer; + *resDenom = denom * context->u.highlight.denom; +} + +static void +Flash_makeFrame (FlashContext *context, FRAME dest, RECT *destRect, + int numer, int denom) +{ + RECT orgRect; + orgRect.corner.x = 0; + orgRect.corner.y = 0; + orgRect.extent = context->rect.extent; + + switch (context->type) { + case FlashType_highlight: + { + int blendedNumer; + int blendedDenom; + + Flash_blendFraction (context, numer, denom, &blendedNumer, + &blendedDenom); + arith_frame_blit (context->original, &orgRect, dest, destRect, + blendedNumer, blendedDenom); + break; + } + case FlashType_transition: + { + FRAME first; + FRAME final; + + first = context->u.transition.first; + if (first == (FRAME) 0) + first = context->original; + final = context->u.transition.final; + if (final == (FRAME) 0) + final = context->original; + + arith_frame_blit (first, &orgRect, dest, destRect, + denom - numer, denom); + arith_frame_blit (final, &orgRect, dest, destRect, + numer, -denom); + break; + } + case FlashType_overlay: + arith_frame_blit (context->original, &orgRect, dest, destRect, + denom, denom); + arith_frame_blit (context->u.overlay.frame, &orgRect, + dest, destRect, numer, -denom); + break; + } +} + + +// Prepare an entry in the cache. +static inline void +Flash_prepareCacheFrame (FlashContext *context, COUNT index) +{ + if (context->cache[index] != (FRAME) 0) + return; + + if (index == 0 && context->type == FlashType_overlay) + context->cache[index] = context->original; + else if (index == 0 && context->type == FlashType_transition) + context->cache[index] = context->u.transition.first != (FRAME) 0 ? + context->u.transition.first : context->original; + else if (index == context->cacheSize - 1 && + context->type == FlashType_transition) + context->cache[index] = context->u.transition.final != (FRAME) 0 ? + context->u.transition.final : context->original; + else + { + context->cache[index] = CaptureDrawable (CreateDrawable (WANT_PIXMAP, + context->rect.extent.width, context->rect.extent.height, 1)); + Flash_makeFrame (context, context->cache[index], NULL, + index, context->cacheSize - 1); + } +} + +static void +Flash_drawFrame (FlashContext *context, FRAME frame) +{ + CONTEXT oldGfxContext; + FRAME oldFGFrame = (FRAME) 0; + STAMP stamp; + + LockMutex (GraphicsLock); + oldGfxContext = SetContext (context->gfxContext); + if (context->parent != NULL) + oldFGFrame = SetContextFGFrame (context->parent); + + stamp.origin = context->rect.corner; + stamp.frame = frame; + DrawStamp(&stamp); + + if (context->parent != NULL) + SetContextFGFrame (oldFGFrame); + SetContext (oldGfxContext); + UnlockMutex (GraphicsLock); +} + +static void +Flash_drawCacheFrame (FlashContext *context, COUNT index) +{ + FRAME frame; + + if (context->lastFrameIndex == index) + return; + + frame = context->cache[index]; + Flash_drawFrame (context, frame); + context->lastFrameIndex = index; +} + +static inline void +Flash_drawUncachedFrame (FlashContext *context, int numer, int denom) +{ + // 'lastFrameIndex' is 0 for the first image, 1 for the final + // image, and 2 otherwise. + + if (numer == 0 && context->type == FlashType_overlay) + { + if (context->lastFrameIndex != 0) + return; + + Flash_drawFrame (context, context->original); + context->lastFrameIndex = 0; + return; + } + else if (numer == 0 && context->type == FlashType_transition) + { + if (context->lastFrameIndex == 0) + return; + + Flash_drawFrame (context, context->u.transition.first); + context->lastFrameIndex = 0; + return; + } + else if (numer == denom && context->type == FlashType_transition) + { + if (context->lastFrameIndex == 1) + return; + + Flash_drawFrame (context, context->u.transition.final); + context->lastFrameIndex = 1; + return; + } + + context->lastFrameIndex = 2; + + if (context->parent == NULL) + { + // Painting to the screen; we need a temporary frame to draw to. + FRAME work; + + work = CaptureDrawable (CreateDrawable (WANT_PIXMAP, + context->rect.extent.width, context->rect.extent.height, 1)); + Flash_makeFrame (context, work, NULL, numer, denom); + Flash_drawFrame (context, work); + + DestroyDrawable (ReleaseDrawable (work)); + } + else + { + // Painting to another frame; we can directly draw to it. + CONTEXT oldGfxContext; + FRAME oldFGFrame; + + LockMutex (GraphicsLock); + oldGfxContext = SetContext (context->gfxContext); + oldFGFrame = SetContextFGFrame (context->parent); + Flash_makeFrame (context, context->parent, &context->rect, + numer, denom); + SetContextFGFrame (oldFGFrame); + SetContext (oldGfxContext); + UnlockMutex (GraphicsLock); + } +} + +static inline void +Flash_drawCachedFrame (FlashContext *context, int numer, int denom) +{ + COUNT cachePos; + + cachePos = ((context->cacheSize - 1) * numer + (denom / 2)) / denom; + Flash_prepareCacheFrame (context, cachePos); + Flash_drawCacheFrame (context, cachePos); +} + +static void +Flash_drawCurrentFrame (FlashContext *context) +{ + int numer; + int denom; + + if (context->state == FlashState_off) + { + numer = 0; + denom = 1; + } + else if (context->state == FlashState_on) + { + numer = 1; + denom = 1; + } + else + { + TimeCount now = GetTimeCounter (); + + if (context->state == FlashState_fadeIn) + denom = (int) context->fadeInTime; + else + denom = (int) context->fadeOutTime; + + numer = (int) (now - context->lastStateTime); + + if (numer > denom) + numer = denom; + + if (context->state == FlashState_fadeOut) + numer = (int) context->fadeOutTime - numer; + } + + if (context->cacheSize == 0) + Flash_drawUncachedFrame (context, numer, denom); + else + Flash_drawCachedFrame (context, numer, denom); +} + + + diff --git a/sc2/src/sc2code/flash.h b/sc2/src/sc2code/flash.h new file mode 100644 index 000000000..b59b26c6e --- /dev/null +++ b/sc2/src/sc2code/flash.h @@ -0,0 +1,201 @@ +/* + * 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 + */ + +#ifndef _FLASH_H +#define _FLASH_H + +/* + * This code can draw three kinds of flashing areas. + * - a rectangular highlight area. The brightness of the area oscilates. + * - an overlay; an image is overlayed over an area, with oscilating + * brightness. + * - a transition/cross-fade between two images. + * + * NB. The graphics lock should not be held when any of the Flash functions + * is called. + * + * + * Example: + * + * // We create the flash context; it is used to manipulate the flash + * // rectangle while it exists. + * FlashContext *fc = Flash_createHighlight (context, (FRAME) 0, rect, + * 2, 3, 2); + * + * // We change the flashing speed from the defaults. + * Flash_setSpeed (ONE_SECOND, ONE_SECOND, ONE_SECOND, ONE_SECOND); + * + * // During cross-fades, update 8 times per second. + * Flash_setFrameTime (gmstate.player[playerI].flashContext, + * ONE_SECOND / 8); + * + * // We start the flashing. The default is to start from the "off" state. + * Flash_start (fc); + * + * // Some other stuff happens + * ... + * + * // The user has activated the selection. We pause for instance while + * // a pop-up window is active. + * Flash_pause (fc); + * + * // We set the flashing rectangle full on, to indicate the current + * // selection while the popup is active. + * Flash_setState (FlashState_on); + * ... + * // Continue the flashing. + * Flash_continue (fc); + * ... + * // Modifying the graphics of the area that is flashing: + * void Flash_preUpdate (fc); + * ... // do Drawing + * void Flash_postUpdate (fc); + * ... + * // We're done. Terminating the flash restores the flash area to its + * // original state. + * Flash_terminate (fc); + * + * + * Periodically, Flash_process() should be called on the flash context, + * so that the flashing square is updated. + * You can use Flash_nextTime() to determine when the next update is needed, + * or just call Flash_process() to try (it does no updates if not needed). + * + */ + +#include "libs/gfxlib.h" +#include "libs/timelib.h" + +typedef enum { + FlashState_fadeIn = 0, + // Someway between on and off, going towards on. + FlashState_on = 1, + // The overlay image is visible at 100% opacity. + FlashState_fadeOut = 2, + // Someway between on and off, going towards off. + FlashState_off = 3, + // The overlay image is not visible. +} FlashState; + +typedef struct FlashContext FlashContext; + +#ifdef FLASH_INTERNAL +typedef enum { + FlashType_highlight, + FlashType_transition, + FlashType_overlay, +} FlashType; + +struct FlashContext { + CONTEXT gfxContext; + // The graphics context used for drawing. + FRAME parent; + // The frame that contains the flash rectangle. + + RECT rect; + // The rectangle to flash. + + FRAME original; + // The original contents of the flash area. + + FlashType type; + // The type of flash animation. + + union { + struct { + int startNumer; + // Numerator for the brightness for the on state. + int endNumer; + // Numerator for the brightness for the off state. + int denom; + // Denominator for the brightness. + } highlight; + struct { + FRAME first; + // The first image from the transition (cross-fade). + // (FRAME) 0 means that the original is to be used. + FRAME final; + // The last image from the transition. + // (FRAME) 0 means that the original is to be used. + } transition; + struct { + FRAME frame; + } overlay; + } u; + + TimeCount fadeInTime; + TimeCount onTime; + TimeCount fadeOutTime; + TimeCount offTime; + + TimeCount frameTime; + + FlashState state; + TimeCount lastStateTime; + // Time of the last state change. + TimeCount lastFrameTime; + // Time of the last frame draw. + + BOOLEAN started : 1; + BOOLEAN paused : 1; + + FRAME *cache; + COUNT cacheSize; + + COUNT lastFrameIndex; + // Last frame drawn; used to determine whether a frame needs to + // be redawn. If a cache is used, this is the index in the cache. + // If no cache is used, this is either 0, 1, or 2, for + // the respectively first, last, or other frame for the flash + // animation. +}; + +# define Flash_DEFAULT_FADE_IN_TIME 0 +# define Flash_DEFAULT_ON_TIME (ONE_SECOND / 8) +# define Flash_DEFAULT_FADE_OUT_TIME 0 +# define Flash_DEFAULT_OFF_TIME (ONE_SECOND / 8) + +# define Flash_DEFAULT_CACHE_SIZE 9 +#endif /* FLASH_INTERNAL */ + + +FlashContext *Flash_createHighlight (CONTEXT gfxContext, FRAME parent, + const RECT *rect, int startNumer, int endNumer, int denom); +FlashContext *Flash_createTransition (CONTEXT gfxContext, FRAME parent, + const POINT *origin, FRAME first, FRAME final); +FlashContext *Flash_createOverlay (CONTEXT gfxContext, FRAME parent, + const POINT *origin, FRAME overlay); + +void Flash_setState (FlashContext *context, FlashState state); +void Flash_start (FlashContext *context); +void Flash_terminate (FlashContext *context); +void Flash_pause (FlashContext *context); +void Flash_continue (FlashContext *context); +void Flash_process (FlashContext *context); +void Flash_setSpeed (FlashContext *context, TimeCount fadeInTime, + TimeCount onTime, TimeCount fadeOutTime, TimeCount offTime); +void Flash_setFrameTime (FlashContext *context, TimeCount frameTime); +TimeCount Flash_nextTime (FlashContext *context); +void Flash_setRect (FlashContext *context, const RECT *rect); +void Flash_getRect (FlashContext *context, RECT *rect); +void Flash_preUpdate (FlashContext *context); +void Flash_postUpdate (FlashContext *context); +void Flash_setCacheSize (FlashContext *context, COUNT size); +COUNT Flash_getCacheSize (const FlashContext *context); + + +#endif /* _FLASH_H */ + diff --git a/sc2/src/sc2code/gamestr.h b/sc2/src/sc2code/gamestr.h index 2367c72d0..48ff394b9 100644 --- a/sc2/src/sc2code/gamestr.h +++ b/sc2/src/sc2code/gamestr.h @@ -46,7 +46,7 @@ #define FLAGSHIP_STRING_COUNT 13 #define ORBITSCAN_STRING_COUNT 19 #define MAINMENU_STRING_COUNT 57 -#define NETMELEE_STRING_COUNT 18 +#define NETMELEE_STRING_COUNT 19 enum { STAR_STRING_BASE = 0, diff --git a/sc2/src/sc2code/igfxres.h b/sc2/src/sc2code/igfxres.h index 64b88de15..97261c3a3 100644 --- a/sc2/src/sc2code/igfxres.h +++ b/sc2/src/sc2code/igfxres.h @@ -72,6 +72,7 @@ #define UMGAH_BCS_MASK_PMAP_ANIM 0x13c04702L #define MELEE_SCREEN_PMAP_ANIM 0x14204802L #define MELEE_PICK_MASK_PMAP_ANIM 0x14404902L + // melee/melee.ani #define SEGUE_PMAP_ANIM 0x14604a02L #define SC2_PICK_PMAP_ANIM 0x14804b02L #define RESTART_PMAP_ANIM 0x18204c02L diff --git a/sc2/src/sc2code/init.c b/sc2/src/sc2code/init.c index 355f9f2bc..2e95c7dc8 100644 --- a/sc2/src/sc2code/init.c +++ b/sc2/src/sc2code/init.c @@ -146,7 +146,6 @@ InitShips (void) InitDisplayList (); InitGalaxy (); - num_ships = NUM_SIDES; if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) { ReinitQueue (&race_q[0]); @@ -156,7 +155,7 @@ InitShips (void) LoadHyperspace (); - --num_ships; + num_ships = 1; } else { @@ -193,6 +192,8 @@ InitShips (void) for (i = 0; i < NUM_PLANETS; ++i) spawn_planet (); } + + num_ships = NUM_SIDES; } // FlushInput (); @@ -200,10 +201,34 @@ InitShips (void) return (num_ships); } +// Count the crew elements in the display list. +static COUNT +CountCrewElements (void) +{ + COUNT result; + HELEMENT hElement, hNextElement; + + result = 0; + for (hElement = GetHeadElement (); + hElement != 0; hElement = hNextElement) + { + ELEMENT *ElementPtr; + + LockElement (hElement, &ElementPtr); + hNextElement = GetSuccElement (ElementPtr); + if (ElementPtr->state_flags & CREW_OBJECT) + ++result; + + UnlockElement (hElement); + } + + return result; +} + void UninitShips (void) { - BYTE crew_retrieved; + COUNT crew_retrieved; SIZE i; HELEMENT hElement, hNextElement; STARSHIP *SPtr[NUM_PLAYERS]; @@ -216,17 +241,7 @@ UninitShips (void) SPtr[i] = 0; // Count the crew floating in space. - crew_retrieved = 0; - for (hElement = GetHeadElement (); - hElement != 0; hElement = hNextElement) - { - ELEMENT *ElementPtr; - LockElement (hElement, &ElementPtr); - hNextElement = GetSuccElement (ElementPtr); - if (ElementPtr->state_flags & CREW_OBJECT) - ++crew_retrieved; - UnlockElement (hElement); - } + crew_retrieved = CountCrewElements(); for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) @@ -278,6 +293,8 @@ UninitShips (void) else if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER && !(GLOBAL (CurrentActivity) & CHECK_ABORT)) { + // XXX: What is the reason for this? At least for SuperMelee, + // it has no purpose (it will return immediately). for (i = NUM_PLAYERS - 1; i >= 0; --i) { if (SPtr[i]) diff --git a/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c b/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c index 710d6874c..dd99d7c8a 100644 --- a/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c +++ b/sc2/src/sc2code/libs/graphics/sdl/3do_getbody.c @@ -255,8 +255,8 @@ void fill_frame_rgb (FRAME FramePtr, Uint32 color, int x0, int y0, UnlockMutex (tfbImg->mutex); } -void arith_frame_blit (FRAME srcFrame, RECT *rsrc, FRAME dstFrame, - RECT *rdst, int num, int denom) +void arith_frame_blit (FRAME srcFrame, const RECT *rsrc, FRAME dstFrame, + const RECT *rdst, int num, int denom) { TFB_Image *srcImg, *dstImg; SDL_Surface *src, *dst; @@ -296,7 +296,7 @@ void arith_frame_blit (FRAME srcFrame, RECT *rsrc, FRAME dstFrame, dstRect.y = -srcFrame->HotSpot.y; dstRect.w = GetFrameWidth (srcFrame); dstRect.h = GetFrameHeight (srcFrame); - drp =&dstRect; + drp = &dstRect; } } diff --git a/sc2/src/sc2code/melee.c b/sc2/src/sc2code/melee.c index 31457b26c..97db76792 100644 --- a/sc2/src/sc2code/melee.c +++ b/sc2/src/sc2code/melee.c @@ -160,6 +160,9 @@ enum #define TEAM_NAME_EDIT_CURS_COLOR \ WHITE_COLOR +#define LOAD_TEAM_NAME_TEXT_COLOR TEAM_NAME_TEXT_COLOR +#define LOAD_TEAM_NAME_TEXT_COLOR_HILITE TEAM_NAME_EDIT_TEXT_COLOR + #define PICKSHIP_TEAM_NAME_TEXT_COLOR \ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09) #define PICKSHIP_TEAM_START_VALUE_COLOR \ @@ -185,7 +188,9 @@ enum FRAME PickMeleeFrame; static FRAME MeleeFrame; + // Loaded from melee/melebkgd.ani static FRAME BuildPickFrame; + // Constructed. extern QUEUE master_q; DWORD InTime; MELEE_STATE *pMeleeState; @@ -208,12 +213,14 @@ static BOOLEAN DoLoadTeam (MELEE_STATE *pMS); static void DrawFileStrings (MELEE_STATE *pMS, int HiLiteState); +// These icons come from melee/melebkgd.ani static void DrawMeleeIcon (COUNT which_icon) { STAMP s; - s.origin.x = s.origin.y = 0; + s.origin.x = 0; + s.origin.y = 0; s.frame = SetAbsFrameIndex (MeleeFrame, which_icon); DrawStamp (&s); } @@ -292,7 +299,7 @@ static void DrawShipBoxCurrent (MELEE_STATE *pMS, BOOLEAN HiLite) { FleetShipIndex index = GetShipIndex (pMS->row, pMS->col); - BYTE ship = pMS->TeamImage[pMS->side].ShipList[index]; + BYTE ship = pMS->SideState[pMS->side].TeamImage.ShipList[index]; DrawShipBox (pMS->side, pMS->row, pMS->col, ship, HiLite); } @@ -305,6 +312,7 @@ DrawControls (COUNT which_side, BOOLEAN HiLite) if (PlayerControl[which_side] & NETWORK_CONTROL) { DrawMeleeIcon (31 + (HiLite ? 1 : 0) + 2 * (1 - which_side)); + /* "Network Control" */ return; } @@ -388,8 +396,8 @@ RepairMeleeFrame (RECT *pRect) DrawMeleeIcon (0); /* Entire melee screen */ #ifdef NETPLAY - DrawMeleeIcon (35); - DrawMeleeIcon (37); + DrawMeleeIcon (35); /* "Net..." (top, not highlighted) */ + DrawMeleeIcon (37); /* "Net..." (bottom, not highlighted) */ #endif DrawMeleeIcon (26); /* "Battle!" (highlighted) */ { @@ -405,7 +413,8 @@ RepairMeleeFrame (RECT *pRect) for (col = 0; col < NUM_MELEE_COLUMNS; col++) { FleetShipIndex index = GetShipIndex (row, col); - BYTE ship = pMeleeState->TeamImage[side].ShipList[index]; + BYTE ship = pMeleeState->SideState[side].TeamImage. + ShipList[index]; DrawShipBox (side, row, col, ship, FALSE); } } @@ -454,7 +463,7 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState) SetContextFont (MicroFont); - lfText.pStr = pMS->TeamImage[side].TeamName; + lfText.pStr = pMS->SideState[side].TeamImage.TeamName; lfText.baseline.y = r.corner.y + r.extent.height - 3; lfText.baseline.x = r.corner.x + 1; @@ -467,7 +476,7 @@ DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState) TEXT rtText; UNICODE buf[30]; - sprintf (buf, "%d", pMS->star_bucks[side]); + sprintf (buf, "%d", pMS->SideState[side].star_bucks); rtText.pStr = buf; rtText.align = ALIGN_RIGHT; rtText.CharCount = (COUNT)~0; @@ -713,27 +722,30 @@ Deselect (BYTE opt) break; case LOAD_TOP: case LOAD_BOT: - if (pMeleeState->InputFunc != DoLoadTeam) + if (pMeleeState->InputFunc != DoLoadTeam) { DrawMeleeIcon (opt == LOAD_TOP ? 17 : 22); + /* 17: "Load" (top, not highlighted) */ + /* 22: "Load" (bottom, not highlighted) */ + } else DrawFileStrings (pMeleeState, 0); break; case SAVE_TOP: - DrawMeleeIcon (18); /* "Save" (top; not highlighted) */ + DrawMeleeIcon (18); /* "Save" (top, not highlighted) */ break; case SAVE_BOT: - DrawMeleeIcon (21); /* "Save" (bottom; not highlighted) */ + DrawMeleeIcon (21); /* "Save" (bottom, not highlighted) */ break; #ifdef NETPLAY case NET_TOP: - DrawMeleeIcon (35); + DrawMeleeIcon (35); /* "Net..." (top, not highlighted) */ break; case NET_BOT: - DrawMeleeIcon (37); + DrawMeleeIcon (37); /* "Net..." (bottom, not highlighted) */ break; #endif case QUIT_BOT: - DrawMeleeIcon (29); + DrawMeleeIcon (29); /* "Quit" (not highlighted) */ break; case CONTROLS_TOP: case CONTROLS_BOT: @@ -774,7 +786,11 @@ Select (BYTE opt) case LOAD_TOP: case LOAD_BOT: if (pMeleeState->InputFunc != DoLoadTeam) + { DrawMeleeIcon (opt == LOAD_TOP ? 19 : 24); + /* 19: "Load" (top, highlighted) */ + /* 24: "Load" (bottom, highlighted) */ + } else DrawFileStrings (pMeleeState, 1); break; @@ -786,14 +802,14 @@ Select (BYTE opt) break; #ifdef NETPLAY case NET_TOP: - DrawMeleeIcon (36); /* "Save" (top; highlighted) */ + DrawMeleeIcon (36); /* "Net..." (top; highlighted) */ break; case NET_BOT: - DrawMeleeIcon (38); /* "Save" (bottom; highlighted) */ + DrawMeleeIcon (38); /* "Net..." (bottom; highlighted) */ break; #endif case QUIT_BOT: - DrawMeleeIcon (30); + DrawMeleeIcon (30); /* "Quit" (highlighted) */ break; case CONTROLS_TOP: case CONTROLS_BOT: @@ -956,7 +972,8 @@ UpdateCurrentShip (MELEE_STATE *pMS) if (pMS->row == NUM_MELEE_ROWS) pMS->CurIndex = MELEE_NONE; else - pMS->CurIndex = pMS->TeamImage[pMS->side].ShipList[fleetShipIndex]; + pMS->CurIndex = + pMS->SideState[pMS->side].TeamImage.ShipList[fleetShipIndex]; DrawMeleeShipStrings (pMS, (BYTE)(pMS->CurIndex)); } @@ -1109,8 +1126,8 @@ DrawFileStrings (MELEE_STATE *pMS, int HiLiteState) Text.pStr = pMS->FileList[bot].TeamName; Text.CharCount = (COUNT)~0; SetContextForeGroundColor (HiLiteState == 0 ? - BUILD_COLOR (MAKE_RGB15 (15, 16, 27), 0x00) - : BUILD_COLOR (MAKE_RGB15 (23, 24, 29), 0x00)); + LOAD_TEAM_NAME_TEXT_COLOR : + LOAD_TEAM_NAME_TEXT_COLOR_HILITE); font_DrawText (&Text); rtText.baseline.y = Text.baseline.y; @@ -1122,7 +1139,7 @@ DrawFileStrings (MELEE_STATE *pMS, int HiLiteState) { COUNT teams_left; - DrawMeleeIcon (28); /* Load team frame */ + DrawMeleeIcon (28); /* The load team frame */ Text.baseline.y = y; teams_left = (COUNT)( @@ -1151,8 +1168,7 @@ DrawFileStrings (MELEE_STATE *pMS, int HiLiteState) { Text.pStr = pMS->FileList[bot - top].TeamName; Text.CharCount = (COUNT)~0; - SetContextForeGroundColor ( - BUILD_COLOR (MAKE_RGB15 (15, 16, 27), 0x00)); + SetContextForeGroundColor (TEAM_NAME_TEXT_COLOR); font_DrawText (&Text); rtText.baseline.y = Text.baseline.y; @@ -1228,10 +1244,10 @@ DoLoadTeam (MELEE_STATE *pMS) { if (!PulsedInputState.menu[KEY_MENU_CANCEL]) { - pMS->TeamImage[pMS->side] = + pMS->SideState[pMS->side].TeamImage = pMS->FileList[pMS->CurIndex - pMS->TopTeamIndex]; - pMS->star_bucks[pMS->side] = - GetTeamValue (&pMS->TeamImage[pMS->side]); + pMS->SideState[pMS->side].star_bucks = + GetTeamValue (&pMS->SideState[pMS->side].TeamImage); entireFleetChanged (pMS, pMS->side); teamStringChanged (pMS, pMS->side); } @@ -1381,7 +1397,7 @@ DoSaveTeam (MELEE_STATE *pMS) uio_Stream *save_fp; CONTEXT OldContext; - sprintf (file, "%s.mle", pMS->TeamImage[pMS->side].TeamName); + sprintf (file, "%s.mle", pMS->SideState[pMS->side].TeamImage.TeamName); LockMutex (GraphicsLock); OldContext = SetContext (ScreenContext); @@ -1392,7 +1408,7 @@ DoSaveTeam (MELEE_STATE *pMS) BOOLEAN err; err = (BOOLEAN)(WriteTeamImage ( - &pMS->TeamImage[pMS->side], save_fp) == 0); + &pMS->SideState[pMS->side].TeamImage, save_fp) == 0); if (res_CloseResFile (save_fp) == 0) err = TRUE; if (err) @@ -1434,16 +1450,17 @@ DeleteCurrentShip (MELEE_STATE *pMS) int CurIndex; fleetShipIndex = GetShipIndex (pMS->row, pMS->col); - CurIndex = pMS->TeamImage[pMS->side].ShipList[fleetShipIndex]; + CurIndex = pMS->SideState[pMS->side].TeamImage.ShipList[fleetShipIndex]; hStarShip = GetStarShipFromIndex (&master_q, CurIndex); StarShipPtr = LockStarShip (&master_q, hStarShip); if (StarShipPtr) { - pMS->star_bucks[pMS->side] -= - StarShipPtr->RaceDescPtr->ship_info.ship_cost; + pMS->SideState[pMS->side].star_bucks -= + StarShipPtr->RaceDescPtr->ship_info.ship_cost; UnlockStarShip (&master_q, hStarShip); - pMS->TeamImage[pMS->side].ShipList[fleetShipIndex] = MELEE_NONE; + pMS->SideState[pMS->side].TeamImage.ShipList[fleetShipIndex] = + MELEE_NONE; } LockMutex (GraphicsLock); GetShipBox (&r, pMS->side, pMS->row, pMS->col); @@ -1496,7 +1513,8 @@ DoEdit (MELEE_STATE *pMS) if (!pMS->Initialized) { FleetShipIndex fleetShipIndex = GetShipIndex (pMS->row, pMS->col); - pMS->CurIndex = pMS->TeamImage[pMS->side].ShipList[fleetShipIndex]; + pMS->CurIndex = + pMS->SideState[pMS->side].TeamImage.ShipList[fleetShipIndex]; DrawMeleeShipStrings (pMS, (BYTE)pMS->CurIndex); pMS->Initialized = TRUE; @@ -1570,7 +1588,7 @@ DoEdit (MELEE_STATE *pMS) tes.Initialized = FALSE; tes.MenuRepeatDelay = 0; - tes.BaseStr = pMS->TeamImage[pMS->side].TeamName; + tes.BaseStr = pMS->SideState[pMS->side].TeamImage.TeamName; tes.CursorPos = 0; tes.MaxSize = MAX_TEAM_CHARS + 1; tes.CbParam = pMS; @@ -1666,9 +1684,7 @@ DoPickShip (MELEE_STATE *pMS) if (pMS->CurIndex == (BYTE)~0 && pMS->Initialized == 0) pMS->CurIndex = 0; else if (pMS->CurIndex != (BYTE)~0) - { DeleteCurrentShip (pMS); - } if (pMS->Initialized == 0) { @@ -1686,7 +1702,8 @@ DoPickShip (MELEE_STATE *pMS) Deselect (EDIT_MELEE); pMS->Initialized = TRUE; AdvanceCursor (pMS); - pMS->CurIndex = pMS->TeamImage[pMS->side].ShipList[fleetShipIndex]; + pMS->CurIndex = pMS->SideState[pMS->side].TeamImage.ShipList[ + fleetShipIndex]; UnlockMutex (GraphicsLock); DrawMeleeShipStrings (pMS, (BYTE)(pMS->CurIndex)); @@ -1705,11 +1722,12 @@ DoPickShip (MELEE_STATE *pMS) hStarShip = GetStarShipFromIndex (&master_q, pMS->CurIndex); StarShipPtr = LockStarShip (&master_q, hStarShip); - pMS->star_bucks[pMS->side] += + pMS->SideState[pMS->side].star_bucks += StarShipPtr->RaceDescPtr->ship_info.ship_cost; UnlockStarShip (&master_q, hStarShip); - pMS->TeamImage[pMS->side].ShipList[index] = pMS->CurIndex; + pMS->SideState[pMS->side].TeamImage.ShipList[index] = + pMS->CurIndex; LockMutex (GraphicsLock); DrawTeamString (pMS, pMS->side, DTSHS_REPAIR); DrawShipBoxCurrent (pMS, FALSE); @@ -1972,12 +1990,14 @@ LoadMeleeInfo (MELEE_STATE *pMS) // create team building ship selection box s.frame = SetAbsFrameIndex (MeleeFrame, 27); + // 5x5 grid of ships to pick from GetFrameRect (s.frame, &r); BuildPickFrame = CaptureDrawable (CreateDrawable ( WANT_PIXMAP, r.extent.width, r.extent.height, 1)); SetContextFGFrame (BuildPickFrame); SetFrameHot (s.frame, MAKE_HOT_SPOT (0, 0)); DrawStamp (&s); + for (i = 0; i < NUM_PICK_COLS * NUM_PICK_ROWS; ++i) DrawPickIcon (i, 1); @@ -2056,14 +2076,14 @@ BuildAndDrawShipList (MELEE_STATE *pMS) // Team name at the bottom of the frame: t.align = ALIGN_CENTER; - t.pStr = pMS->TeamImage[i].TeamName; + t.pStr = pMS->SideState[i].TeamImage.TeamName; t.CharCount = (COUNT)~0; SetContextFont (TinyFont); SetContextForeGroundColor (PICKSHIP_TEAM_NAME_TEXT_COLOR); font_DrawText (&t); // Total team value of the starting team: - sprintf (buf, "%d", pMS->star_bucks[i]); + sprintf (buf, "%d", pMS->SideState[i].star_bucks); t.baseline.x = 4; t.baseline.y = 7; t.align = ALIGN_LEFT; @@ -2076,7 +2096,7 @@ BuildAndDrawShipList (MELEE_STATE *pMS) { BYTE StarShip; - StarShip = pMS->TeamImage[i].ShipList[index]; + StarShip = pMS->SideState[i].TeamImage.ShipList[index]; if (StarShip == MELEE_NONE) continue; @@ -2174,7 +2194,8 @@ StartMelee (MELEE_STATE *pMS) static void StartMeleeButtonPressed (MELEE_STATE *pMS) { - if (pMS->star_bucks[0] == 0 || pMS->star_bucks[1] == 0) + if (pMS->SideState[0].star_bucks == 0 || + pMS->SideState[1].star_bucks == 0) { PlayMenuSound (MENU_SOUND_FAILURE); return; @@ -2184,7 +2205,7 @@ StartMeleeButtonPressed (MELEE_STATE *pMS) if ((PlayerControl[0] & NETWORK_CONTROL) && (PlayerControl[1] & NETWORK_CONTROL)) { - DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 31)); + DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 32)); // "Only one side at a time can be network controlled." return; } @@ -2194,7 +2215,7 @@ StartMeleeButtonPressed (MELEE_STATE *pMS) ((PlayerControl[0] & COMPUTER_CONTROL) && (PlayerControl[1] & NETWORK_CONTROL))) { - DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 32)); + DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 33)); // "Netplay with a computer-controlled side is currently // not possible." return; @@ -2277,7 +2298,7 @@ DoConnectingDialog (MELEE_STATE *pMS) if (!pMS->Initialized) { RECT r; - FONT oldfont; + FONT oldfont; COLOR oldcolor; TEXT t; @@ -2304,10 +2325,12 @@ DoConnectingDialog (MELEE_STATE *pMS) if (NetConnection_getPeerOptions (conn)->isServer) { t.pStr = GAME_STRING (NETMELEE_STRING_BASE + 1); + /* "Awaiting incoming connection */ } else { t.pStr = GAME_STRING (NETMELEE_STRING_BASE + 2); + /* "Awaiting outgoing connection */ } t.baseline.y = r.corner.y + 10; t.baseline.x = SCREEN_WIDTH >> 1; @@ -2316,7 +2339,7 @@ DoConnectingDialog (MELEE_STATE *pMS) font_DrawText (&t); t.pStr = GAME_STRING (NETMELEE_STRING_BASE + 18); - /* "Press SPACE to cancel" */ + /* "Press SPACE to cancel" */ t.baseline.y += 16; font_DrawText (&t); @@ -2547,7 +2570,7 @@ DoMelee (MELEE_STATE *pMS) case SAVE_TOP: case SAVE_BOT: pMS->side = pMS->MeleeOption == SAVE_TOP ? 0 : 1; - if (pMS->star_bucks[pMS->side]) + if (pMS->SideState[pMS->side].star_bucks) DoSaveTeam (pMS); else PlayMenuSound (MENU_SOUND_FAILURE); @@ -2580,8 +2603,9 @@ DoMelee (MELEE_STATE *pMS) { if (!pMS->flash_task) { - pMS->flash_task = AssignTask (flash_selection_func, 2048, - "flash melee selection"); + pMS->flash_task = AssignTask ( + flash_selection_func, 2048, + "flash melee selection"); } } @@ -2912,40 +2936,32 @@ LoadMeleeConfig (MELEE_STATE *pMS) { uio_Stream *load_fp; int status; + COUNT side; load_fp = res_OpenResFile (configDir, "melee.cfg", "rb"); if (!load_fp) goto err; - if (LengthResFile (load_fp) != (1 + sizeof (TEAM_IMAGE)) * 2) + if (LengthResFile (load_fp) != (1 + sizeof (TEAM_IMAGE)) * NUM_SIDES) goto err; - status = GetResFileChar (load_fp); - if (status == -1) - goto err; - PlayerControl[0] = (BYTE)status; + for (side = 0; side < NUM_SIDES; side++) + { + status = GetResFileChar (load_fp); + if (status == -1) + goto err; + PlayerControl[side] = (BYTE)status; - status = ReadTeamImage (&pMS->TeamImage[0], load_fp); - if (status == -1) - goto err; - - status = GetResFileChar (load_fp); - if (status == -1) - goto err; - PlayerControl[1] = (BYTE)status; - - status = ReadTeamImage (&pMS->TeamImage[1], load_fp); - if (status == -1) - goto err; + status = ReadTeamImage (&pMS->SideState[side].TeamImage, load_fp); + if (status == -1) + goto err; + /* Do not allow netplay mode at the start. */ + if (PlayerControl[side] & NETWORK_CONTROL) + PlayerControl[side] = HUMAN_CONTROL | STANDARD_RATING; + } + res_CloseResFile (load_fp); - - /* Do not allow netplay mode at the start. */ - if (PlayerControl[0] & NETWORK_CONTROL) - PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING; - if (PlayerControl[1] & NETWORK_CONTROL) - PlayerControl[1] = HUMAN_CONTROL | STANDARD_RATING; - return 0; err: @@ -2958,22 +2974,20 @@ int WriteMeleeConfig (MELEE_STATE *pMS) { uio_Stream *save_fp; + COUNT side; save_fp = res_OpenResFile (configDir, "melee.cfg", "wb"); if (!save_fp) goto err; - if (PutResFileChar (PlayerControl[0], save_fp) == -1) - goto err; + for (side = 0; side < NUM_SIDES; side++) + { + if (PutResFileChar (PlayerControl[side], save_fp) == -1) + goto err; - if (WriteTeamImage (&pMS->TeamImage[0], save_fp) == 0) - goto err; - - if (PutResFileChar (PlayerControl[1], save_fp) == -1) - goto err; - - if (WriteTeamImage (&pMS->TeamImage[1], save_fp) == 0) - goto err; + if (WriteTeamImage (&pMS->SideState[side].TeamImage, save_fp) == 0) + goto err; + } if (!res_CloseResFile (save_fp)) goto err; @@ -3002,6 +3016,12 @@ Melee (void) MenuState.InputFunc = DoMelee; MenuState.Initialized = FALSE; + MenuState.randomContext = RandomContext_New (); + RandomContext_SeedRandom (MenuState.randomContext, + GetTimeCounter ()); + // Using the current time still leave the random state a bit + // predictable, but it is good enough. + #ifdef NETPLAY { COUNT player; @@ -3020,9 +3040,9 @@ Melee (void) if (LoadMeleeConfig (&MenuState) == -1) { PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING; - MenuState.TeamImage[0] = MenuState.PreBuiltList[0]; + MenuState.SideState[0].TeamImage = MenuState.PreBuiltList[0]; PlayerControl[1] = COMPUTER_CONTROL | STANDARD_RATING; - MenuState.TeamImage[1] = MenuState.PreBuiltList[1]; + MenuState.SideState[1].TeamImage = MenuState.PreBuiltList[1]; } SetPlayerInput (); teamStringChanged (&MenuState, 0); @@ -3031,8 +3051,10 @@ Melee (void) entireFleetChanged (&MenuState, 1); MenuState.side = 0; - MenuState.star_bucks[0] = GetTeamValue (&MenuState.TeamImage[0]); - MenuState.star_bucks[1] = GetTeamValue (&MenuState.TeamImage[1]); + MenuState.SideState[0].star_bucks = + GetTeamValue (&MenuState.SideState[0].TeamImage); + MenuState.SideState[1].star_bucks = + GetTeamValue (&MenuState.SideState[0].TeamImage); SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT); DoInput (&MenuState, TRUE); @@ -3047,6 +3069,8 @@ Melee (void) DestroyDrawable (ReleaseDrawable (PickMeleeFrame)); PickMeleeFrame = 0; + RandomContext_Delete (MenuState.randomContext); + FlushInput (); } } @@ -3060,7 +3084,7 @@ teamStringChanged (MELEE_STATE *pMS, int player) size_t len; size_t playerI; - name = pMS->TeamImage[player].TeamName; + name = pMS->SideState[player].TeamImage.TeamName; len = strlen (name); for (playerI = 0; playerI < NUM_PLAYERS; playerI++) { @@ -3102,7 +3126,7 @@ entireFleetChanged (MELEE_STATE *pMS, int player) continue; Netplay_entireFleetChanged (conn, player, - pMS->TeamImage[player].ShipList, MELEE_FLEET_SIZE); + pMS->SideState[player].TeamImage.ShipList, MELEE_FLEET_SIZE); } #else (void) player; @@ -3130,7 +3154,7 @@ fleetShipChanged (MELEE_STATE *pMS, int player, size_t index) continue; Netplay_fleetShipChanged (conn, player, index, - pMS->TeamImage[player].ShipList[index]); + pMS->SideState[player].TeamImage.ShipList[index]); } #else (void) player; @@ -3152,8 +3176,8 @@ updateTeamName (MELEE_STATE *pMS, COUNT side, const char *name, len = MAX_TEAM_CHARS; // TeamName has space for at least MAX_TEAM_CHARS + 1 bytes. - strncpy (pMS->TeamImage[side].TeamName, name, len); - pMS->TeamImage[side].TeamName[len] = '\0'; + strncpy (pMS->SideState[side].TeamImage.TeamName, name, len); + pMS->SideState[side].TeamImage.TeamName[len] = '\0'; LockMutex (GraphicsLock); #if 0 /* DTSHS_REPAIR does not combine with other options */ @@ -3178,14 +3202,14 @@ updateFleetShip (MELEE_STATE *pMS, COUNT side, COUNT index, BYTE ship) BOOLEAN isSelected; if (ship >= NUM_MELEE_SHIPS && ship != MELEE_NONE) { - fprintf (stderr, "Invalid ship type number %d (max = %d).\n", + log_add (log_Warning, "Invalid ship type number %d (max = %d).\n", ship, NUM_MELEE_SHIPS - 1); return false; } if (index >= MELEE_FLEET_SIZE) { - fprintf (stderr, "Invalid ship position number %d (max = %d).\n", + log_add (log_Warning, "Invalid ship position number %d (max = %d).\n", index, MELEE_FLEET_SIZE - 1); return false; } @@ -3194,11 +3218,11 @@ updateFleetShip (MELEE_STATE *pMS, COUNT side, COUNT index, BYTE ship) if (val == (COUNT) ~0) return false; - pMS->star_bucks[side] -= - GetShipValue (pMS->TeamImage[side].ShipList[index]); - pMS->star_bucks[side] += val; + pMS->SideState[side].star_bucks -= + GetShipValue (pMS->SideState[side].TeamImage.ShipList[index]); + pMS->SideState[side].star_bucks += val; - pMS->TeamImage[side].ShipList[index] = ship; + pMS->SideState[side].TeamImage.ShipList[index] = ship; selectedShipIndex = GetShipIndex (pMS->row, pMS->col); isSelected = (pMS->MeleeOption == EDIT_MELEE) && @@ -3285,7 +3309,7 @@ connectedFeedback (NetConnection *conn) { } const char * -abortReasonString (NetplayResetReason reason) +abortReasonString (NetplayAbortReason reason) { switch (reason) { @@ -3295,8 +3319,12 @@ abortReasonString (NetplayResetReason reason) case AbortReason_versionMismatch: return GAME_STRING (NETMELEE_STRING_BASE + 26); // "Connection aborted due to version mismatch." - case AbortReason_protocolError: + case AbortReason_invalidHash: return GAME_STRING (NETMELEE_STRING_BASE + 27); + // "Connection aborted because the remote side sent a " + // "fake signature." + case AbortReason_protocolError: + return GAME_STRING (NETMELEE_STRING_BASE + 28); // "Connection aborted due to an internal protocol " // "error." } @@ -3321,13 +3349,13 @@ resetReasonString (NetplayResetReason reason) switch (reason) { case ResetReason_unspecified: - return GAME_STRING (NETMELEE_STRING_BASE + 28); + return GAME_STRING (NETMELEE_STRING_BASE + 29); // "Game aborted for an unspecified reason." case ResetReason_syncLoss: - return GAME_STRING (NETMELEE_STRING_BASE + 29); + return GAME_STRING (NETMELEE_STRING_BASE + 30); // "Game aborted due to loss of synchronisation." case ResetReason_manualReset: - return GAME_STRING (NETMELEE_STRING_BASE + 30); + return GAME_STRING (NETMELEE_STRING_BASE + 31); // "Game aborted by the remote player." } diff --git a/sc2/src/sc2code/melee.h b/sc2/src/sc2code/melee.h index 7a673ce51..45abc27fc 100644 --- a/sc2/src/sc2code/melee.h +++ b/sc2/src/sc2code/melee.h @@ -22,6 +22,7 @@ #include "init.h" #include "libs/tasklib.h" #include "libs/gfxlib.h" +#include "libs/mathlib.h" #include "libs/sndlib.h" #include "libs/reslib.h" #include "netplay/packet.h" @@ -71,7 +72,10 @@ enum extern FRAME PickMeleeFrame; -#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x01) +#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x01) +#define PICK_VALUE_COLOR BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C) + // Used for the current fleet value in the next ship selection + // in SuperMelee. #define MAX_TEAM_CHARS 30 #define MAX_VIS_TEAMS 5 @@ -90,6 +94,12 @@ typedef struct * default name in starcon.txt is unknowingly mangled. */ } TEAM_IMAGE; +struct melee_side_state +{ + TEAM_IMAGE TeamImage; + COUNT star_bucks; +}; + struct melee_state { BOOLEAN (*InputFunc) (struct melee_state *pInputState); @@ -100,12 +110,14 @@ struct melee_state DIRENTRY TeamDE; COUNT TopTeamIndex, BotTeamIndex; COUNT side, row, col; - TEAM_IMAGE TeamImage[NUM_SIDES]; - COUNT star_bucks[NUM_SIDES]; + struct melee_side_state SideState[NUM_SIDES]; COUNT CurIndex; Task flash_task; TEAM_IMAGE FileList[MAX_VIS_TEAMS]; TEAM_IMAGE PreBuiltList[NUM_PREBUILT]; + RandomContext *randomContext; + /* RNG state for all local random decisions, i.e. those + * decisions that are not shared among network parties. */ MUSIC_REF hMusic; }; diff --git a/sc2/src/sc2code/netplay/netmisc.h b/sc2/src/sc2code/netplay/netmisc.h index c5bf6a092..fb21bc82c 100644 --- a/sc2/src/sc2code/netplay/netmisc.h +++ b/sc2/src/sc2code/netplay/netmisc.h @@ -58,6 +58,7 @@ static inline bool readyFlagsMeaningful(NetState state) { return state == NetState_init || state == NetState_preBattle || + state == NetState_selectShip || state == NetState_interBattle || state == NetState_inBattle || state == NetState_endingBattle || diff --git a/sc2/src/sc2code/pickmele.c b/sc2/src/sc2code/pickmele.c index e04dbc519..0c70e0328 100644 --- a/sc2/src/sc2code/pickmele.c +++ b/sc2/src/sc2code/pickmele.c @@ -20,6 +20,7 @@ #include "build.h" #include "controls.h" +#include "flash.h" #include "intel.h" #include "melee.h" #ifdef NETPLAY @@ -32,6 +33,7 @@ #include "setup.h" #include "sounds.h" #include "libs/inplib.h" +#include "libs/log.h" #include "libs/mathlib.h" @@ -44,13 +46,27 @@ struct getmelee_struct { BOOLEAN (*InputFunc) (struct getmelee_struct *pInputState); COUNT MenuRepeatDelay; BOOLEAN Initialized; - HSTARSHIP hBattleShip; - COUNT row, col, ships_left, which_player; - COUNT randomIndex; + + struct { + TimeCount timeIn; + HSTARSHIP hBattleShip; + // Chosen ship. + COUNT row; + COUNT col; + COUNT ships_left; + // Number of ships still available. + COUNT randomIndex; + // Pre-generated random number. + BOOLEAN selecting; + // Is this player selecting a ship? + BOOLEAN done; + // Has a selection been made for this player? + FlashContext *flashContext; + // Context for controlling the flash rectangle. #ifdef NETPLAY - BOOLEAN remoteSelected; + BOOLEAN remoteSelected; #endif - RECT flash_rect; + } player[NUM_PLAYERS]; }; #ifdef NETPLAY @@ -117,59 +133,34 @@ queueIndexFromShip (HSTARSHIP hShip) } #endif +// Pre: called does not hold the graphics lock static void -PickMelee_ChangedSelection (GETMELEE_STATE *gms) +PickMelee_ChangedSelection (GETMELEE_STATE *gms, COUNT playerI) { - LockMutex (GraphicsLock); - gms->flash_rect.corner.x = PICK_X_OFFS + ((ICON_WIDTH + 2) * gms->col); - gms->flash_rect.corner.y = PICK_Y_OFFS + ((ICON_HEIGHT + 2) * gms->row) - + ((1 - gms->which_player) * PICK_SIDE_OFFS); - SetFlashRect (&gms->flash_rect, (FRAME)0); - UnlockMutex (GraphicsLock); + RECT r; + r.corner.x = PICK_X_OFFS + ((ICON_WIDTH + 2) * gms->player[playerI].col); + r.corner.y = PICK_Y_OFFS + ((ICON_HEIGHT + 2) * gms->player[playerI].row) + + ((1 - playerI) * PICK_SIDE_OFFS); + r.extent.width = (ICON_WIDTH + 2); + r.extent.height = (ICON_HEIGHT + 2); + Flash_setRect (gms->player[playerI].flashContext, &r); } -// Select a new ship from the fleet for battle. -// Returns 'TRUE' if no choice has been made yet; this function is to be -// called again later. -// Returns 'FALSE' if a choice has been made. gms->hStarShip is set -// to the chosen (or randomly selected) ship, or to 0 if 'exit' has -// been chosen. -/* TODO: Include player timeouts */ +// Auxiliary function to DoGetMelee for a single player +// Returns FALSE if the player selected abort. static BOOLEAN -DoGetMelee (GETMELEE_STATE *gms) +DoGetMeleePlayer (GETMELEE_STATE *gms, COUNT which_player) { BOOLEAN left, right, up, down, select; - COUNT which_player = gms->which_player; - BOOLEAN done = FALSE; - - SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE); - - if (!gms->Initialized) - { - gms->Initialized = TRUE; - gms->row = 0; - gms->col = NUM_MELEE_COLS_ORIG; - - PickMelee_ChangedSelection (gms); - return TRUE; - } - - SleepThread (ONE_SECOND / 120); -#ifdef NETPLAY - netInput (); - - if (!allConnected()) - goto aborted; -#endif - - if (GLOBAL (CurrentActivity) & CHECK_ABORT) - goto aborted; if (PlayerInput[which_player] == ComputerInput) { - /* TODO: Make this a frame-by-frame thing. This code is currently - * copied from intel.c's computer_intelligence */ - SleepThread (ONE_SECOND >> 1); +#define COMPUTER_SELECTION_DELAY (ONE_SECOND >> 1) + TimeCount now = GetTimeCounter (); + if (now < gms->player[which_player].timeIn + + COMPUTER_SELECTION_DELAY) + return TRUE; + left = right = up = down = FALSE; select = TRUE; } @@ -177,8 +168,10 @@ DoGetMelee (GETMELEE_STATE *gms) else if (PlayerInput[which_player] == NetworkInput) { flushPacketQueues (); - if (gms->remoteSelected) - return FALSE; + // Sets gms->player[which_player].remoteSelected if input + // is received. + if (gms->player[which_player].remoteSelected) + gms->player[which_player].done = TRUE; return TRUE; } #endif @@ -200,39 +193,47 @@ DoGetMelee (GETMELEE_STATE *gms) if (select) { - if (gms->col == NUM_MELEE_COLS_ORIG) + if (gms->player[which_player].col == NUM_MELEE_COLS_ORIG) { - if (gms->row == 0) + // Selection is on a non-ship slot. (Random or exit). + if (gms->player[which_player].row == 0) { // Random ship - gms->hBattleShip = MeleeShipByUsedIndex ( - &race_q[which_player], gms->randomIndex); + gms->player[which_player].hBattleShip = + MeleeShipByUsedIndex (&race_q[which_player], + gms->player[which_player].randomIndex); + PlayMenuSound (MENU_SOUND_SUCCESS); #ifdef NETPLAY reportShipSelected (gms, (COUNT)~0); #endif - done = TRUE; + gms->player[which_player].done = TRUE; } else { // Selected exit if (ConfirmExit ()) - goto aborted; + return FALSE; } } else { + // Selection is on a ship slot. COUNT ship_index; + HSTARSHIP ship; - ship_index = (gms->row * NUM_MELEE_COLS_ORIG) + gms->col; - gms->hBattleShip = MeleeShipByQueueIndex ( - &race_q[which_player], ship_index); - if (gms->hBattleShip != 0) + ship_index = (gms->player[which_player].row * NUM_MELEE_COLS_ORIG) + + gms->player[which_player].col; + ship = MeleeShipByQueueIndex (&race_q[which_player], ship_index); + if (ship != 0) { // Selection contains a ship. + gms->player[which_player].hBattleShip = ship; + gms->player[which_player].done = true; + PlayMenuSound (MENU_SOUND_SUCCESS); #ifdef NETPLAY reportShipSelected (gms, ship_index); #endif - done = TRUE; + return TRUE; } } } @@ -241,8 +242,8 @@ DoGetMelee (GETMELEE_STATE *gms) // Process motion commands. COUNT new_row, new_col; - new_row = gms->row; - new_col = gms->col; + new_row = gms->player[which_player].row; + new_col = gms->player[which_player].col; if (left) { if (new_col-- == 0) @@ -264,27 +265,93 @@ DoGetMelee (GETMELEE_STATE *gms) new_row = 0; } - if (new_row != gms->row || new_col != gms->col) + if (new_row != gms->player[which_player].row || + new_col != gms->player[which_player].col) { - gms->row = new_row; - gms->col = new_col; + gms->player[which_player].row = new_row; + gms->player[which_player].col = new_col; PlayMenuSound (MENU_SOUND_MOVE); - PickMelee_ChangedSelection (gms); + PickMelee_ChangedSelection (gms, which_player); + } + } + + return TRUE; +} + +// Select a new ship from the fleet for battle. +// Returns 'TRUE' if no choice has been made yet; this function is to be +// called again later. +// Returns 'FALSE' if a choice has been made. gms->hStarShip is set +// to the chosen (or randomly selected) ship, or to 0 if 'exit' has +// been chosen. +/* TODO: Include player timeouts */ +static BOOLEAN +DoGetMelee (GETMELEE_STATE *gms) +{ + BOOLEAN done; + COUNT playerI; + + SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE); + + if (!gms->Initialized) + { + gms->Initialized = TRUE; + return TRUE; + } + + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + { + if (!gms->player[playerI].selecting) + continue; + + if (!gms->player[playerI].done) + Flash_process (gms->player[playerI].flashContext); + } + + SleepThread (ONE_SECOND / 120); + +#ifdef NETPLAY + netInput (); + + if (!allConnected ()) + goto aborted; +#endif + + if (GLOBAL (CurrentActivity) & CHECK_ABORT) + goto aborted; + + done = TRUE; + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + { + if (!gms->player[playerI].selecting) + continue; + + if (!gms->player[playerI].done) { + if (!DoGetMeleePlayer (gms, playerI)) + goto aborted; + + if (gms->player[playerI].done) + { + Flash_terminate (gms->player[playerI].flashContext); + gms->player[playerI].flashContext = NULL; + } + else + done = FALSE; } } #ifdef NETPLAY flushPacketQueues (); #endif - return !done; aborted: #ifdef NETPLAY flushPacketQueues (); #endif - gms->hBattleShip = 0; + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + gms->player[playerI].hBattleShip = 0; GLOBAL (CurrentActivity) &= ~CHECK_ABORT; return FALSE; } @@ -298,270 +365,422 @@ endMeleeCallback (NetConnection *conn, void *arg) } #endif -HSTARSHIP -GetMeleeStarShip (STARSHIP *LastStarShipPtr, COUNT which_player) +static COUNT +GetRaceQueueValue (const QUEUE *queue) { + COUNT result; + HSTARSHIP hBattleShip, hNextShip; + + result = 0; + for (hBattleShip = GetHeadLink (queue); + hBattleShip != 0; hBattleShip = hNextShip) + { + STARSHIP *StarShipPtr = LockStarShip (queue, hBattleShip); + hNextShip = _GetSuccLink (StarShipPtr); + + if (StarShipPtr->RaceResIndex == 0) + continue; // Not active any more. + + result += StarShipPtr->special_counter; + + UnlockStarShip (queue, hBattleShip); + } + + return result; +} + +// Cross out the icon for the dead ship. +// 'frame' is the PickMeleeFrame for the player. +// 'shipI' is the index in the ship list. +// Pre: caller holds the graphics lock. +static void +CrossOutShip (FRAME frame, COUNT shipI) { - COUNT ships_left; + CONTEXT OldContext; + STAMP s; + + OldContext = SetContext (OffScreenContext); + + SetContextFGFrame (frame); + + s.origin.x = 3 + ((ICON_WIDTH + 2) * (shipI % NUM_MELEE_COLS_ORIG)); + s.origin.y = 9 + ((ICON_HEIGHT + 2) * (shipI / NUM_MELEE_COLS_ORIG)); + s.frame = SetAbsFrameIndex (StatusFrame, 3); + // Cross for through the ship image. + DrawStamp (&s); + + SetContext (OldContext); +} + +// Draw the value of the fleet in the top right of the PickMeleeFrame. +// Pre: caller holds the graphics lock. +static void +UpdatePickMeleeFleetValue (FRAME frame, COUNT which_player) +{ + CONTEXT OldContext; + COUNT value; + RECT r; TEXT t; UNICODE buf[40]; - STAMP s; - CONTEXT OldContext; - GETMELEE_STATE gmstate; - STARSHIP *StarShipPtr; - BOOLEAN firstTime; + + value = GetRaceQueueValue (&race_q[which_player]); - if (!(GLOBAL (CurrentActivity) & IN_BATTLE)) - return (0); + OldContext = SetContext (OffScreenContext); + SetContextFGFrame (frame); + + // Erase the old value text. + GetFrameRect (frame, &r); + r.extent.width -= 4; + t.baseline.x = r.extent.width; + r.corner.x = r.extent.width - (6 * 3); + r.corner.y = 2; + r.extent.width = (6 * 3); + r.extent.height = 7 - 2; + SetContextForeGroundColor (PICK_BG_COLOR); + DrawFilledRectangle (&r); + + // Draw the new value text. + sprintf (buf, "%d", value); + t.baseline.y = 7; + t.align = ALIGN_RIGHT; + t.pStr = buf; + t.CharCount = (COUNT)~0; + SetContextFont (TinyFont); + SetContextForeGroundColor (PICK_VALUE_COLOR); + font_DrawText (&t); + + SetContext (OldContext); +} + +// Pre: caller holds the graphics lock. +static void +DrawPickMeleeFrame (COUNT which_player) +{ + CONTEXT oldContext; + STAMP s; + + oldContext = SetContext (SpaceContext); + s.frame = SetAbsFrameIndex (PickMeleeFrame, which_player); + s.origin.x = PICK_X_OFFS - 3; + s.origin.y = PICK_Y_OFFS - 9 + ((1 - which_player) * PICK_SIDE_OFFS); + DrawStamp (&s); + // Draw the selection box to screen. + + SetContext (oldContext); +} + +// Pre: caller holds the graphics lock. +static void +MeleeGameOver (void) +{ + COUNT playerI; + DWORD TimeOut; + BOOLEAN PressState, ButtonState; + + // Show the battle result. + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + DrawPickMeleeFrame (playerI); + + TimeOut = GetTimeCounter () + (ONE_SECOND * 4); + UnlockMutex (GraphicsLock); + + PressState = PulsedInputState.menu[KEY_MENU_SELECT] || + PulsedInputState.menu[KEY_MENU_CANCEL]; + do + { + UpdateInputState (); + ButtonState = PulsedInputState.menu[KEY_MENU_SELECT] || + PulsedInputState.menu[KEY_MENU_CANCEL]; + if (PressState) + { + PressState = ButtonState; + ButtonState = FALSE; + } + + TaskSwitch (); + } while (!(GLOBAL (CurrentActivity) & CHECK_ABORT) && (!ButtonState + && (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL) + || GetTimeCounter () < TimeOut))); #ifdef NETPLAY + setStateConnections (NetState_endMelee); + localReadyConnections (endMeleeCallback, NULL, true); +#endif + + LockMutex (GraphicsLock); +} + +BOOLEAN +MeleeShipDeath (STARSHIP *ship, COUNT which_player) { + FRAME frame; + + // Deactivate fleet position. + ship->RaceResIndex = 0; + + frame = SetAbsFrameIndex (PickMeleeFrame, which_player); + CrossOutShip (frame, ship->ShipFacing); + UpdatePickMeleeFleetValue (frame, which_player); + + if (battle_counter[0] == 0 || battle_counter[1] == 0) { - NetConnection *conn = netConnections[which_player]; + // One side is out of ships. Game over. + MeleeGameOver (); + return FALSE; + } + + return TRUE; +} + +static BOOLEAN +GetMeleeStarShips (COUNT playerMask, HSTARSHIP *ships) +{ + COUNT playerI; + BOOLEAN ok; + GETMELEE_STATE gmstate; + TimeCount now; + +#ifdef NETPLAY + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + { + NetConnection *conn; + + if ((playerMask & (1 << playerI)) == 0) + continue; + + // XXX: This does not have to be done per connection. + conn = netConnections[playerI]; if (conn != NULL) { BattleStateData *battleStateData; battleStateData = - (BattleStateData *) NetConnection_getStateData(conn); + (BattleStateData *) NetConnection_getStateData (conn); battleStateData->getMeleeState = &gmstate; } } #endif - s.frame = SetAbsFrameIndex (PickMeleeFrame, which_player); - - OldContext = SetContext (OffScreenContext); - SetContextFGFrame (s.frame); - if (LastStarShipPtr == 0 || LastStarShipPtr->special_counter == 0) - { - COUNT cur_bucks; - HSTARSHIP hBattleShip, hNextShip; - - cur_bucks = 0; - for (hBattleShip = GetHeadLink (&race_q[which_player]); - hBattleShip != 0; hBattleShip = hNextShip) - { - StarShipPtr = LockStarShip (&race_q[which_player], hBattleShip); - if (StarShipPtr == LastStarShipPtr) - { - LastStarShipPtr->RaceResIndex = 0; - - gmstate.col = LastStarShipPtr->ShipFacing; - s.origin.x = 3 - + ((ICON_WIDTH + 2) * (gmstate.col % NUM_MELEE_COLS_ORIG)); - s.origin.y = 9 - + ((ICON_HEIGHT + 2) * (gmstate.col / NUM_MELEE_COLS_ORIG)); - s.frame = SetAbsFrameIndex (StatusFrame, 3); - DrawStamp (&s); - s.frame = SetAbsFrameIndex (PickMeleeFrame, which_player); - } - else if (StarShipPtr->RaceResIndex) - { - cur_bucks += StarShipPtr->special_counter; - } - hNextShip = _GetSuccLink (StarShipPtr); - UnlockStarShip (&race_q[which_player], hBattleShip); - } - - GetFrameRect (s.frame, &gmstate.flash_rect); - gmstate.flash_rect.extent.width -= 4; - t.baseline.x = gmstate.flash_rect.extent.width; - gmstate.flash_rect.corner.x = - gmstate.flash_rect.extent.width - (6 * 3); - gmstate.flash_rect.corner.y = 2; - gmstate.flash_rect.extent.width = (6 * 3); - gmstate.flash_rect.extent.height = 7 - 2; - SetContextForeGroundColor (PICK_BG_COLOR); - DrawFilledRectangle (&gmstate.flash_rect); - - sprintf (buf, "%d", cur_bucks); - t.baseline.y = 7; - t.align = ALIGN_RIGHT; - t.pStr = buf; - t.CharCount = (COUNT)~0; - SetContextFont (TinyFont); - SetContextForeGroundColor ( - BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C)); - font_DrawText (&t); - } - - SetContext (SpaceContext); - - s.origin.x = PICK_X_OFFS - 3; - s.origin.y = PICK_Y_OFFS - 9 + ((1 - which_player) * PICK_SIDE_OFFS); - - DrawStamp (&s); - - if (battle_counter[0] == 0 || battle_counter[1] == 0) - { - // One side is out of ships. Game over. - DWORD TimeOut; - BOOLEAN PressState, ButtonState; - - s.origin.y = PICK_Y_OFFS - 9 + (which_player * PICK_SIDE_OFFS); - s.frame = SetAbsFrameIndex (PickMeleeFrame, - (COUNT) (1 - which_player)); - DrawStamp (&s); - - TimeOut = GetTimeCounter () + (ONE_SECOND * 4); - SetContext (OldContext); - UnlockMutex (GraphicsLock); - - PressState = PulsedInputState.menu[KEY_MENU_SELECT] || - PulsedInputState.menu[KEY_MENU_CANCEL]; - do - { - UpdateInputState (); - ButtonState = PulsedInputState.menu[KEY_MENU_SELECT] || - PulsedInputState.menu[KEY_MENU_CANCEL]; - if (PressState) - { - PressState = ButtonState; - ButtonState = FALSE; - } - - TaskSwitch (); - } while (!(GLOBAL (CurrentActivity) & CHECK_ABORT) && (!ButtonState - && (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL) - || GetTimeCounter () < TimeOut))); - -#ifdef NETPLAY - setStateConnections (NetState_endMelee); - localReadyConnections (endMeleeCallback, NULL, true); -#endif - - LockMutex (GraphicsLock); - - return (0); - } - - // Fade in if we're not already faded in. -#ifdef NETPLAY - // Hack. If one side is network controlled, the top player does not have - // to be the first player to get a chance to select his ship, - // As what is top locally doesn't have to be the same as what it is - // remotely. - // This check is not so nice as it replicates the code used to decide - // which party is first in selectAllShips. - firstTime = (LastStarShipPtr == 0) && ((which_player == 0) == - (((PlayerControl[0] & NETWORK_CONTROL) && - !NetConnection_getDiscriminant(netConnections[0])) || - ((PlayerControl[1] & NETWORK_CONTROL) && - NetConnection_getDiscriminant(netConnections[1])))); -#else - firstTime = LastStarShipPtr == 0 && which_player == 1; -#endif - if (firstTime) - { - BYTE fade_buf[] = {FadeAllToColor}; - - SleepThreadUntil (XFormColorMap - ((COLORMAPPTR) fade_buf, ONE_SECOND / 2) + ONE_SECOND / 60); - FlushColorXForms (); - } - - ships_left = battle_counter[which_player]; - - gmstate.flash_rect.extent.width = (ICON_WIDTH + 2); - gmstate.flash_rect.extent.height = (ICON_HEIGHT + 2); + ok = true; + now = GetTimeCounter (); gmstate.InputFunc = DoGetMelee; gmstate.Initialized = FALSE; - gmstate.ships_left = ships_left; - gmstate.which_player = which_player; - // We determine in advance which ship would be chosen if the player - // wants a random ship, to keep it simple to keep network parties - // synchronised. - gmstate.randomIndex = (COUNT)TFB_Random () % ships_left; -#ifdef NETPLAY - gmstate.remoteSelected = FALSE; + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) { - // NB. gmstate.randomIndex and gmstate.remoteSelected must be - // initialised before negotiateReadyConnections is completed, - // to ensure that it is initialised when the SelectShip packet + gmstate.player[playerI].selecting = + (playerMask & (1 << playerI)) != 0; + gmstate.player[playerI].ships_left = battle_counter[playerI]; + + // We determine in advance which ship would be chosen if the player + // wants a random ship, to keep it simple to keep network parties + // synchronised. + gmstate.player[playerI].randomIndex = + (COUNT)TFB_Random () % gmstate.player[playerI].ships_left; + gmstate.player[playerI].done = FALSE; + + if (!gmstate.player[playerI].selecting) + continue; + + gmstate.player[playerI].timeIn = now; + gmstate.player[playerI].row = 0; + gmstate.player[playerI].col = NUM_MELEE_COLS_ORIG; +#ifdef NETPLAY + gmstate.player[playerI].remoteSelected = FALSE; +#endif + + gmstate.player[playerI].flashContext = + Flash_createHighlight (ScreenContext, (FRAME) 0, NULL, + 2, 3, 2); + Flash_setFrameTime (gmstate.player[playerI].flashContext, + ONE_SECOND / 16); +#ifdef NETPLAY + if (PlayerInput[playerI] == NetworkInput) + Flash_setSpeed (gmstate.player[playerI].flashContext, + ONE_SECOND / 2, 0, ONE_SECOND / 2, 0); + else +#endif + { + Flash_setSpeed (gmstate.player[playerI].flashContext, + 0, ONE_SECOND / 16, 0, ONE_SECOND / 16); + } + PickMelee_ChangedSelection (&gmstate, playerI); + Flash_start (gmstate.player[playerI].flashContext); + } + +#ifdef NETPLAY + { + // NB. gmstate.player[].randomIndex and gmstate.player[].done must + // be initialised before negotiateReadyConnections is completed, to + // ensure that they are initialised when the SelectShip packet // arrives. bool allOk = negotiateReadyConnections (true, NetState_selectShip); if (!allOk) { // Some network connection has been reset. - gmstate.hBattleShip = 0; + ok = false; } } #endif SetDefaultMenuRepeatDelay (); + + SetContext (OffScreenContext); UnlockMutex (GraphicsLock); ResetKeyRepeat (); DoInput (&gmstate, FALSE); + WaitForSoundEnd (0); LockMutex (GraphicsLock); - SetFlashRect (NULL, (FRAME)0); - - if (gmstate.hBattleShip == 0) + + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + { + if (!gmstate.player[playerI].selecting) + continue; + + if (gmstate.player[playerI].done) + { + // Flash rectangle is already terminated. + ships[playerI] = gmstate.player[playerI].hBattleShip; + } + else + { + Flash_terminate (gmstate.player[playerI].flashContext); + gmstate.player[playerI].flashContext = NULL; + ok = false; + } + } + +#ifdef NETPLAY + if (ok) + { + if (!negotiateReadyConnections (true, NetState_interBattle)) + ok = false; + } +#endif + + if (!ok) { // Aborting. GLOBAL (CurrentActivity) &= ~IN_BATTLE; } else { - StarShipPtr = - LockStarShip (&race_q[which_player], gmstate.hBattleShip); - OwnStarShip (StarShipPtr, - 1 << which_player, StarShipPtr->captains_name_index); - StarShipPtr->captains_name_index = 0; - UnlockStarShip (&race_q[which_player], gmstate.hBattleShip); + STARSHIP *StarShipPtr; + + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + { + if (!gmstate.player[playerI].selecting) + continue; - PlayMenuSound (MENU_SOUND_SUCCESS); - - WaitForSoundEnd (0); + StarShipPtr = LockStarShip (&race_q[playerI], ships[playerI]); + OwnStarShip (StarShipPtr, 1 << playerI, + StarShipPtr->captains_name_index); + StarShipPtr->captains_name_index = 0; + UnlockStarShip (&race_q[playerI], ships[playerI]); + } } #ifdef NETPLAY + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) { - NetConnection *conn = netConnections[which_player]; - if (conn != NULL && NetConnection_isConnected(conn)) + NetConnection *conn; + + if ((playerMask & (1 << playerI)) == 0) + continue; + + // XXX: This does not have to be done per connection. + conn = netConnections[playerI]; + if (conn != NULL && NetConnection_isConnected (conn)) { BattleStateData *battleStateData; - battleStateData = (BattleStateData *) - NetConnection_getStateData(conn); + battleStateData = + (BattleStateData *) NetConnection_getStateData (conn); battleStateData->getMeleeState = NULL; } } #endif - return (gmstate.hBattleShip); + return ok; +} + +BOOLEAN +GetInitialMeleeStarShips (HSTARSHIP *result) +{ + COUNT playerI; + COUNT playerMask; + + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + { + FRAME frame; + frame = SetAbsFrameIndex (PickMeleeFrame, playerI); + UpdatePickMeleeFleetValue (frame, playerI); + DrawPickMeleeFrame (playerI); + } + + // Fade in + { + BYTE fade_buf[] = {FadeAllToColor}; + SleepThreadUntil (XFormColorMap + ((COLORMAPPTR) fade_buf, ONE_SECOND / 2) + ONE_SECOND / 60); + FlushColorXForms (); + } + + playerMask = 0; + for (playerI = 0; playerI < NUM_PLAYERS; playerI++) + playerMask |= (1 << playerI); + + return GetMeleeStarShips (playerMask, result); +} + +BOOLEAN +GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result) +{ + COUNT playerMask; + HSTARSHIP ships[NUM_PLAYERS]; + BOOLEAN ok; + + DrawPickMeleeFrame (which_player); + + playerMask = 1 << which_player; + ok = GetMeleeStarShips (playerMask, ships); + if (ok) + *result = ships[which_player]; + + return ok; } #ifdef NETPLAY // Called when a ship selection has arrived from a remote player. bool -updateMeleeSelection (GETMELEE_STATE *gms, COUNT player, COUNT ship) +updateMeleeSelection (GETMELEE_STATE *gms, COUNT playerI, COUNT ship) { - if (gms == NULL) + if (gms == NULL || !gms->player[playerI].selecting || + gms->player[playerI].done) { // This happens when we get an update message from a connection - // for who we are not selecting a ship (yet?). - fprintf (stderr, "Unexpected ship selection packet received.\n"); + // for who we are not selecting a ship. + log_add (log_Warning, "Unexpected ship selection packet " + "received.\n"); return false; } if (ship == (COUNT) ~0) { // Random selection. - gms->hBattleShip = - MeleeShipByUsedIndex (&race_q[player], gms->randomIndex); - gms->remoteSelected = TRUE; - NetConnection_setState (netConnections[player], NetState_interBattle); + gms->player[playerI].hBattleShip = + MeleeShipByUsedIndex (&race_q[playerI], + gms->player[playerI].randomIndex); + gms->player[playerI].remoteSelected = TRUE; return true; } - gms->hBattleShip = MeleeShipByQueueIndex (&race_q[player], ship); - if (gms->hBattleShip == 0) + gms->player[playerI].hBattleShip = + MeleeShipByQueueIndex (&race_q[playerI], ship); + if (gms->player[playerI].hBattleShip == 0) { - fprintf (stderr, "Invalid ship selection received from remote " + log_add (log_Warning, "Invalid ship selection received from remote " "party.\n"); return false; } - gms->remoteSelected = TRUE; - NetConnection_setState (netConnections[player], NetState_interBattle); + gms->player[playerI].remoteSelected = TRUE; return true; } @@ -580,7 +799,6 @@ reportShipSelected (GETMELEE_STATE *gms, COUNT index) continue; Netplay_selectShip (conn, index); - NetConnection_setState (conn, NetState_interBattle); } (void) gms; } diff --git a/sc2/src/sc2code/pickmele.h b/sc2/src/sc2code/pickmele.h index def2b294e..e7062f09f 100644 --- a/sc2/src/sc2code/pickmele.h +++ b/sc2/src/sc2code/pickmele.h @@ -20,8 +20,9 @@ #include "races.h" #include "libs/compiler.h" -HSTARSHIP GetMeleeStarShip (STARSHIP *LastStarShipPtr, - COUNT which_player); +BOOLEAN MeleeShipDeath (STARSHIP *ship, COUNT which_player); +BOOLEAN GetInitialMeleeStarShips (HSTARSHIP *result); +BOOLEAN GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result); typedef struct getmelee_struct GETMELEE_STATE; diff --git a/sc2/src/sc2code/pickship.c b/sc2/src/sc2code/pickship.c index 100e582cf..972c4fda4 100644 --- a/sc2/src/sc2code/pickship.c +++ b/sc2/src/sc2code/pickship.c @@ -311,7 +311,24 @@ GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player) else if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) { // Let the player chose his own ship. (May be a computer player). - hBattleShip = GetMeleeStarShip (LastStarShipPtr, which_player); + + if (!(GLOBAL (CurrentActivity) & IN_BATTLE)) + { + // XXX: This check should not be needed, but Uninitships() + // calls this function after the battle is over for some + // reason. Perhaps because one of the non-supermelee cases. + // (Note that the reason isn't to display the battle + // summary; that has already been done). + hBattleShip = 0; + } + else if (!MeleeShipDeath (LastStarShipPtr, which_player)) + hBattleShip = 0; + // Game over. + else + { + if (!GetNextMeleeStarShip (which_player, &hBattleShip)) + hBattleShip = 0; + } } else { @@ -333,6 +350,7 @@ GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player) hBattleShip = GetHeadLink (&race_q[which_player]); if (which_player == 1) { + // Select the next ship for the computer. hStarShip = GetHeadLink (&GLOBAL (npc_built_ship_q)); FragPtr = (SHIP_FRAGMENT*) LockStarShip ( &GLOBAL (npc_built_ship_q), hStarShip); diff --git a/sc2/src/sc2code/process.c b/sc2/src/sc2code/process.c index d0fb6ea2e..e659be264 100644 --- a/sc2/src/sc2code/process.c +++ b/sc2/src/sc2code/process.c @@ -364,7 +364,6 @@ ProcessCollisions (HELEMENT hSuccElement, ELEMENT *ElementPtr, if (TestElementPtr == ElementPtr) { UnlockElement (hTestElement); - continue; } @@ -938,10 +937,9 @@ PostProcessQueue (VIEW_STATE view_state, SIZE scroll_x, // (smaller) zoom level image as mipmap, // needed for trilinear scaling - FRAME frame = + FRAME frame = SetAbsFrameIndex ( - ElementPtr->next.image.farray - [index + 1], + ElementPtr->next.image.farray[index + 1], GetFrameIndex (ElementPtr->next.image.frame)); if (frame && frame->image) diff --git a/sc2/src/sc2code/races.h b/sc2/src/sc2code/races.h index ad9a7f34f..24537a14c 100644 --- a/sc2/src/sc2code/races.h +++ b/sc2/src/sc2code/races.h @@ -278,6 +278,9 @@ typedef struct BYTE weapon_counter; BYTE special_counter; + // In the ship queue: Ship cost + // In between battles: crew left + // In battle: ship dependant BYTE energy_counter; BYTE ship_input_state; @@ -596,6 +599,7 @@ extern BOOLEAN InitKernel (void); extern void DrawCaptainsWindow (STARSHIP *StarShipPtr); extern BOOLEAN GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side); +extern BOOLEAN GetInitialStarShips (void); extern HSTARSHIP GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player); extern void DrawArmadaPickShip (BOOLEAN draw_salvage_frame, RECT *pPickRect); diff --git a/sc2/src/sc2code/ship.c b/sc2/src/sc2code/ship.c index 6144d19a1..559a7e15c 100644 --- a/sc2/src/sc2code/ship.c +++ b/sc2/src/sc2code/ship.c @@ -21,6 +21,7 @@ #include "colors.h" #include "globdata.h" #include "intel.h" +#include "pickmele.h" #include "races.h" #include "setup.h" #include "sounds.h" @@ -428,7 +429,8 @@ spawn_ship (STARSHIP *StarShipPtr) ShipElementPtr->mass_points = RDPtr->characteristics.ship_mass; ShipElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR | (RDPtr->ship_info.ship_flags & (GOOD_GUY | BAD_GUY)); - ShipElementPtr->turn_wait = ShipElementPtr->thrust_wait = 0; + ShipElementPtr->turn_wait = 0; + ShipElementPtr->thrust_wait = 0; ShipElementPtr->life_span = NORMAL_LIFE; SetPrimType (&DisplayArray[ShipElementPtr->PrimIndex], STAMP_PRIM); @@ -507,7 +509,10 @@ GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side) if (LastStarShipPtr) { if (StarShipPtr == LastStarShipPtr) + { + // Ship has been recycled (on infinite ship worlds). LastStarShipPtr = 0; + } else StarShipPtr->hShip = LastStarShipPtr->hShip; } @@ -526,3 +531,41 @@ GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side) return (hBattleShip != 0); } +BOOLEAN +GetInitialStarShips (void) +{ + if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) + { + HSTARSHIP ships[NUM_PLAYERS]; + COUNT i; + + if (!GetInitialMeleeStarShips (ships)) + return FALSE; + + for (i = 0; i < NUM_PLAYERS; i++) + { + STARSHIP *StarShipPtr; + COUNT playerI = GetPlayerOrder (i); + + StarShipPtr = LockStarShip (&race_q[playerI], ships[playerI]); + if (!spawn_ship (StarShipPtr)) + { + UnlockStarShip (&race_q[which_side], ships[playerI]); + return FALSE; + } + UnlockStarShip (&race_q[which_side], ships[playerI]); + } + return TRUE; + } + else + { + COUNT num_ships = NUM_PLAYERS; + while (num_ships--) + { + if (!GetNextStarShip (NULL, num_ships == 1)) + return FALSE; + } + return TRUE; + } +} + diff --git a/sc2/src/sc2code/sis.c b/sc2/src/sc2code/sis.c index a8195780a..1c7331bf8 100644 --- a/sc2/src/sc2code/sis.c +++ b/sc2/src/sc2code/sis.c @@ -957,10 +957,7 @@ DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int resunit_delta) DrawStamp (&s); UnlockMutex (GraphicsLock); - UnlockStarShip ( - &GLOBAL (built_ship_q), - hStarShip - ); + UnlockStarShip (&GLOBAL (built_ship_q), hStarShip); } LockMutex (GraphicsLock); } @@ -1347,7 +1344,8 @@ flash_rect_func (void *data) GetFrameRect (flash_screen_frame, &screen_rect); cached_screen_frame = CaptureDrawable (CreateDrawable (WANT_PIXMAP, screen_rect.extent.width, screen_rect.extent.height, 1)); - screen_rect.corner.x = screen_rect.corner.y = 0; + screen_rect.corner.x = 0; + screen_rect.corner.y = 0; arith_frame_blit (flash_screen_frame, &screen_rect, cached_screen_frame, NULL, 0, 0); UnlockMutex (flash_mutex); @@ -1404,11 +1402,13 @@ flash_rect_func (void *data) if (fstrength < 0) { + // Subtractive blit. num = -8; denom = 8; } else { + // Additive blit. num = 8; denom = -8; } @@ -1563,9 +1563,11 @@ SetFlashRect (RECT *pRect, FRAME f) if (flash_rect.extent.width) { + // Copy the original contents of the rectangle from the screen. if (flash_screen_frame) DestroyDrawable (ReleaseDrawable (flash_screen_frame)); - flash_screen_frame = CaptureDrawable (LoadDisplayPixmap (&flash_rect, (FRAME)0)); + flash_screen_frame = + CaptureDrawable (LoadDisplayPixmap (&flash_rect, (FRAME)0)); } flash_changed = 1; UnlockMutex (flash_mutex);