Concurrent supermelee ship selection.

New generic, unthreaded flashing code.
Cleanups and comments.



git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2749 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
Meep-Eep
2007-05-04 22:11:54 +00:00
parent d755396e75
commit 4ba55150bb
23 changed files with 1704 additions and 446 deletions
+2
View File
@@ -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
+4
View File
@@ -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.
+8
View File
@@ -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
+1 -1
View File
@@ -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
+21 -46
View File
@@ -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;
}
// 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
+3
View File
@@ -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);
+1
View File
@@ -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)
+721
View File
@@ -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);
}
+201
View File
@@ -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 */
+1 -1
View File
@@ -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,
+1
View File
@@ -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
+31 -14
View File
@@ -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])
@@ -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;
}
}
+119 -91
View File
@@ -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] -=
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;
@@ -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;
@@ -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,7 +2603,8 @@ DoMelee (MELEE_STATE *pMS)
{
if (!pMS->flash_task)
{
pMS->flash_task = AssignTask (flash_selection_func, 2048,
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;
for (side = 0; side < NUM_SIDES; side++)
{
status = GetResFileChar (load_fp);
if (status == -1)
goto err;
PlayerControl[0] = (BYTE)status;
PlayerControl[side] = (BYTE)status;
status = ReadTeamImage (&pMS->TeamImage[0], load_fp);
status = ReadTeamImage (&pMS->SideState[side].TeamImage, 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;
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;
if (PlayerControl[side] & NETWORK_CONTROL)
PlayerControl[side] = HUMAN_CONTROL | STANDARD_RATING;
}
res_CloseResFile (load_fp);
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)
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)
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."
}
+14 -2
View File
@@ -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"
@@ -72,6 +73,9 @@ enum
extern FRAME PickMeleeFrame;
#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;
};
+1
View File
@@ -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 ||
+428 -210
View File
@@ -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;
struct {
TimeCount timeIn;
HSTARSHIP hBattleShip;
COUNT row, col, ships_left, which_player;
// 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;
#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,110 +365,122 @@ endMeleeCallback (NetConnection *conn, void *arg)
}
#endif
HSTARSHIP
GetMeleeStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
{
COUNT ships_left;
TEXT t;
UNICODE buf[40];
STAMP s;
CONTEXT OldContext;
GETMELEE_STATE gmstate;
STARSHIP *StarShipPtr;
BOOLEAN firstTime;
if (!(GLOBAL (CurrentActivity) & IN_BATTLE))
return (0);
#ifdef NETPLAY
{
NetConnection *conn = netConnections[which_player];
if (conn != NULL) {
BattleStateData *battleStateData;
battleStateData =
(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;
static COUNT
GetRaceQueueValue (const QUEUE *queue) {
COUNT result;
HSTARSHIP hBattleShip, hNextShip;
cur_bucks = 0;
for (hBattleShip = GetHeadLink (&race_q[which_player]);
result = 0;
for (hBattleShip = GetHeadLink (queue);
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;
}
STARSHIP *StarShipPtr = LockStarShip (queue, hBattleShip);
hNextShip = _GetSuccLink (StarShipPtr);
UnlockStarShip (&race_q[which_player], hBattleShip);
if (StarShipPtr->RaceResIndex == 0)
continue; // Not active any more.
result += StarShipPtr->special_counter;
UnlockStarShip (queue, 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);
return result;
}
sprintf (buf, "%d", cur_bucks);
// 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)
{
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];
value = GetRaceQueueValue (&race_q[which_player]);
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 (
BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C));
SetContextForeGroundColor (PICK_VALUE_COLOR);
font_DrawText (&t);
}
SetContext (SpaceContext);
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.
if (battle_counter[0] == 0 || battle_counter[1] == 0)
{
// One side is out of ships. Game over.
SetContext (oldContext);
}
// Pre: caller holds the graphics lock.
static void
MeleeGameOver (void)
{
COUNT playerI;
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);
// Show the battle result.
for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
DrawPickMeleeFrame (playerI);
TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
SetContext (OldContext);
UnlockMutex (GraphicsLock);
PressState = PulsedInputState.menu[KEY_MENU_SELECT] ||
@@ -428,140 +507,280 @@ GetMeleeStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
#endif
LockMutex (GraphicsLock);
}
return (0);
}
BOOLEAN
MeleeShipDeath (STARSHIP *ship, COUNT which_player) {
FRAME frame;
// 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)
// 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)
{
BYTE fade_buf[] = {FadeAllToColor};
SleepThreadUntil (XFormColorMap
((COLORMAPPTR) fade_buf, ONE_SECOND / 2) + ONE_SECOND / 60);
FlushColorXForms ();
// One side is out of ships. Game over.
MeleeGameOver ();
return FALSE;
}
ships_left = battle_counter[which_player];
return TRUE;
}
gmstate.flash_rect.extent.width = (ICON_WIDTH + 2);
gmstate.flash_rect.extent.height = (ICON_HEIGHT + 2);
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->getMeleeState = &gmstate;
}
}
#endif
ok = true;
now = GetTimeCounter ();
gmstate.InputFunc = DoGetMelee;
gmstate.Initialized = FALSE;
gmstate.ships_left = ships_left;
gmstate.which_player = which_player;
for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
{
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.randomIndex = (COUNT)TFB_Random () % ships_left;
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.remoteSelected = FALSE;
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
{
// NB. gmstate.randomIndex and gmstate.remoteSelected must be
// initialised before negotiateReadyConnections is completed,
// to ensure that it is initialised when the SelectShip packet
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);
STARSHIP *StarShipPtr;
for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
{
if (!gmstate.player[playerI].selecting)
continue;
StarShipPtr = LockStarShip (&race_q[playerI], ships[playerI]);
OwnStarShip (StarShipPtr, 1 << playerI,
StarShipPtr->captains_name_index);
StarShipPtr->captains_name_index = 0;
UnlockStarShip (&race_q[which_player], gmstate.hBattleShip);
PlayMenuSound (MENU_SOUND_SUCCESS);
WaitForSoundEnd (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;
}
+3 -2
View File
@@ -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;
+19 -1
View File
@@ -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);
+1 -3
View File
@@ -364,7 +364,6 @@ ProcessCollisions (HELEMENT hSuccElement, ELEMENT *ElementPtr,
if (TestElementPtr == ElementPtr)
{
UnlockElement (hTestElement);
continue;
}
@@ -940,8 +939,7 @@ PostProcessQueue (VIEW_STATE view_state, SIZE scroll_x,
FRAME frame =
SetAbsFrameIndex (
ElementPtr->next.image.farray
[index + 1],
ElementPtr->next.image.farray[index + 1],
GetFrameIndex (ElementPtr->next.image.frame));
if (frame && frame->image)
+4
View File
@@ -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);
+44 -1
View File
@@ -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;
}
}
+8 -6
View File
@@ -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);