Merge remote branch 'origin/master' into newnet

Conflicts:
	lib/netplay/netlog.c
	lib/netplay/netlog.h
	lib/netplay/netplay.c
	macosx/Warzone.xcodeproj/project.pbxproj
	macosx/configs/Warzone-All.xcconfig
	macosx/configs/Warzone-Debug.xcconfig
	src/multijoin.c
master
Cyp 2010-07-10 16:59:18 +02:00
commit f0beca71cc
48 changed files with 1921 additions and 759 deletions

View File

@ -336,10 +336,6 @@ static void setFatalSignalHandler(SigActionHandler signalHandler)
if (oldAction[SIGSYS].sa_handler != SIG_IGN)
sigaction(SIGSYS, &new_handler, NULL);
sigaction(SIGTRAP, NULL, &oldAction[SIGTRAP]);
if (oldAction[SIGTRAP].sa_handler != SIG_IGN)
sigaction(SIGTRAP, &new_handler, NULL);
sigaction(SIGXCPU, NULL, &oldAction[SIGXCPU]);
if (oldAction[SIGXCPU].sa_handler != SIG_IGN)
sigaction(SIGXCPU, &new_handler, NULL);
@ -347,6 +343,10 @@ static void setFatalSignalHandler(SigActionHandler signalHandler)
sigaction(SIGXFSZ, NULL, &oldAction[SIGXFSZ]);
if (oldAction[SIGXFSZ].sa_handler != SIG_IGN)
sigaction(SIGXFSZ, &new_handler, NULL);
// ignore SIGTRAP
new_handler.sa_handler = SIG_IGN;
sigaction(SIGTRAP, &new_handler, &oldAction[SIGTRAP]);
#endif // _XOPEN_UNIX
}

View File

@ -31,7 +31,12 @@
# error Framework header files MUST be included from Frame.h ONLY.
#endif
#include "wzglobal.h"
#include <assert.h>
#if !defined(WZ_OS_WIN)
#include <signal.h>
#endif
#include "macros.h"
#include "types.h"
@ -54,13 +59,22 @@ extern char last_called_script_event[MAX_EVENT_NAME_LEN];
/** Whether asserts are currently enabled. */
extern bool assertEnabled;
/* Do the correct assert call for each compiler */
#if defined(WZ_OS_WIN)
#define wz_assert(expr) assert(expr)
#else
#define wz_assert(expr) raise(SIGTRAP)
#endif
/** Deals with failure in an assert. Expression is (re-)evaluated for output in the assert() call. */
#define ASSERT_FAILURE(expr, expr_string, location_description, function, ...) \
( \
(void)_debug(LOG_ERROR, function, __VA_ARGS__), \
(void)_debug(LOG_ERROR, function, "Assert in Warzone: %s (%s), last script event: '%s'", \
location_description, expr_string, last_called_script_event), \
( assertEnabled ? assert(expr) : (void)0 )\
( assertEnabled ? (void)wz_assert(expr) : (void)0 )\
)
/**

View File

@ -63,18 +63,6 @@ static void * mem_double(void * ptr, int size)
/*---------------------------------------------------------------------------
Function codes
---------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/**
@brief Compute the hash key for a string.
@param key Character string to use for key.
@return 1 unsigned int on at least 32 bits.
This hash function has been taken from an Article in Dr Dobbs Journal.
This is normally a collision-free function, distributing keys evenly.
The key is stored anyway in the struct so that collision can be avoided
by comparing the key itself in last resort.
*/
/*--------------------------------------------------------------------------*/
unsigned dictionary_hash(const char *key)
{
int len;
@ -94,17 +82,6 @@ unsigned dictionary_hash(const char *key)
return hash;
}
/*-------------------------------------------------------------------------*/
/**
@brief Create a new dictionary object.
@param size Optional initial size of the dictionary.
@return 1 newly allocated dictionary objet.
This function allocates a new dictionary object of given size and returns
it. If you do not know in advance (roughly) the number of entries in the
dictionary, give size=0.
*/
/*--------------------------------------------------------------------------*/
dictionary *dictionary_new(int size)
{
dictionary *d;
@ -123,15 +100,6 @@ dictionary *dictionary_new(int size)
return d;
}
/*-------------------------------------------------------------------------*/
/**
@brief Delete a dictionary object
@param d dictionary object to deallocate.
@return void
Deallocate a dictionary object and all memory associated to it.
*/
/*--------------------------------------------------------------------------*/
void dictionary_del(dictionary * d)
{
int i;
@ -150,20 +118,6 @@ void dictionary_del(dictionary * d)
free(d);
}
/*-------------------------------------------------------------------------*/
/**
@brief Get a value from a dictionary.
@param d dictionary object to search.
@param key Key to look for in the dictionary.
@param def Default value to return if key not found.
@return 1 pointer to internally allocated character string.
This function locates a key in a dictionary and returns a pointer to its
value, or the passed 'def' pointer if no such key can be found in
dictionary. The returned character pointer points to data internal to the
dictionary object, you should not try to free it or modify it.
*/
/*--------------------------------------------------------------------------*/
const char *dictionary_get(dictionary *d, const char *key, const char *def)
{
unsigned hash;
@ -187,32 +141,6 @@ const char *dictionary_get(dictionary *d, const char *key, const char *def)
return def;
}
/*-------------------------------------------------------------------------*/
/**
@brief Set a value in a dictionary.
@param d dictionary object to modify.
@param key Key to modify or add.
@param val Value to add.
@return int 0 if Ok, anything else otherwise
If the given key is found in the dictionary, the associated value is
replaced by the provided one. If the key cannot be found in the
dictionary, it is added to it.
It is Ok to provide a NULL value for val, but NULL values for the dictionary
or the key are considered as errors: the function will return immediately
in such a case.
Notice that if you dictionary_set a variable to NULL, a call to
dictionary_get will return a NULL value: the variable will be found, and
its value (NULL) is returned. In other words, setting the variable
content to NULL is equivalent to deleting the variable from the
dictionary. It is not possible (in this implementation) to have a key in
the dictionary without value.
This function returns non-zero in case of failure.
*/
/*--------------------------------------------------------------------------*/
int dictionary_set(dictionary *d, const char *key, const char *val)
{
int i;
@ -278,17 +206,14 @@ int dictionary_set(dictionary *d, const char *key, const char *val)
return 0;
}
/*-------------------------------------------------------------------------*/
/**
@brief Delete a key in a dictionary
@param d dictionary object to modify.
@param key Key to remove.
@return void
int dictionary_set_int(dictionary *d, const char *key, int val)
{
char *strval;
sasprintf(&strval, "%d", val);
return dictionary_set(d, key, strval);
}
This function deletes a key in a dictionary. Nothing is done if the
key cannot be found.
*/
/*--------------------------------------------------------------------------*/
void dictionary_unset(dictionary *d, char *key)
{
unsigned hash;
@ -330,18 +255,6 @@ void dictionary_unset(dictionary *d, char *key)
d->n--;
}
/*-------------------------------------------------------------------------*/
/**
@brief Dump a dictionary to an opened file pointer.
@param d Dictionary to dump
@param f Opened file pointer.
@return void
Dumps a dictionary onto an opened file pointer. Key pairs are printed out
as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
output file pointers.
*/
/*--------------------------------------------------------------------------*/
void dictionary_dump(dictionary * d, FILE * out)
{
int i;

View File

@ -138,6 +138,14 @@ const char * dictionary_get(dictionary * d, const char * key, const char * def);
/*--------------------------------------------------------------------------*/
int dictionary_set(dictionary * vd, const char * key, const char * val);
/**
* An overloaded member function provided for convenience. Converts the integer
* val to a char* and calls dictionary_set.
*/
int dictionary_set_int(dictionary *d, const char *key, int val);
/*-------------------------------------------------------------------------*/
/**
@brief Delete a key in a dictionary

View File

@ -9,6 +9,7 @@ noinst_HEADERS = \
ivi.h \
ivisdef.h \
ivispatch.h \
jpeg_encoder.h \
pieblitfunc.h \
pieclip.h \
piedef.h \
@ -26,6 +27,7 @@ libivis_common_a_SOURCES = \
bitimage.c \
imd.c \
imdload.c \
jpeg_encoder.c \
pieclip.c \
piestate.c \
png_util.c

View File

@ -161,6 +161,10 @@
RelativePath=".\imdload.c"
>
</File>
<File
RelativePath=".\jpeg_encoder.c"
>
</File>
<File
RelativePath=".\pieclip.c"
>
@ -199,6 +203,10 @@
RelativePath=".\ivispatch.h"
>
</File>
<File
RelativePath=".\jpeg_encoder.h"
>
</File>
<File
RelativePath=".\pieblitfunc.h"
>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
#ifndef JPEG_H_asdf
#define JPEG_H_asdf
/*
* unsigned char *encode_image(unsigned char *input_ptr, unsigned char *output_ptr, unsigned int quality_factor,
* unsigned int image_format, unsigned int image_width, unsigned int image_height)
* Example:
* output_end = encode_image(input, output_start, quality, JPEG_FORMAT_FOUR_TWO_TWO, 320, 256);
*
* output_end - output_start gives the size of the JPEG data.
*
*/
unsigned char *jpeg_encode_image (unsigned char *, unsigned char *, unsigned int, unsigned int, unsigned int, unsigned int);
#define JPEG_FORMAT_FOUR_ZERO_ZERO 0
#define JPEG_FORMAT_FOUR_TWO_TWO 2
#define JPEG_FORMAT_RGB 4
#endif

View File

@ -7,6 +7,7 @@ SRC= \
bitimage.c \
imd.c \
imdload.c \
jpeg_encoder.c \
pieclip.c \
piestate.c \
png_util.c

View File

@ -72,7 +72,7 @@ extern void pie_UniTransBoxFill(float x0, float y0, float x1, float y1, PIELIGHT
extern BOOL pie_InitRadar(void);
extern BOOL pie_ShutdownRadar(void);
extern void pie_DownLoadRadar(UDWORD *buffer, int width, int height);
extern void pie_DownLoadRadar(UDWORD *buffer, int width, int height, bool filter);
extern void pie_RenderRadar(int x, int y, int width, int height);
extern void pie_UploadDisplayBuffer(void);

View File

@ -20,6 +20,7 @@
#include "lib/framework/frame.h"
#include "lib/framework/debug.h"
#include "jpeg_encoder.h"
#include "png_util.h"
#include <png.h>
#include <physfs.h>
@ -188,7 +189,7 @@ static void internal_saveImage_PNG(const char *fileName, const iV_Image *image,
fileHandle = PHYSFS_openWrite(fileName);
if (fileHandle == NULL)
{
debug(LOG_ERROR, "pie_PNGSaveFile: PHYSFS_openWrite failed (while openening file %s) with error: %s\n", fileName, PHYSFS_getLastError());
debug(LOG_ERROR, "pie_PNGSaveFile: PHYSFS_openWrite failed (while opening file %s) with error: %s\n", fileName, PHYSFS_getLastError());
return;
}
@ -283,3 +284,53 @@ void iV_saveImage_PNG_Gray(const char *fileName, const iV_Image *image)
{
internal_saveImage_PNG(fileName, image, PNG_COLOR_TYPE_GRAY);
}
void iV_saveImage_JPEG(const char *fileName, const iV_Image *image)
{
unsigned char *buffer = NULL;
unsigned char *jpeg = NULL;
char newfilename[PATH_MAX];
unsigned int currentRow;
const unsigned int row_stride = image->width * 3; // 3 bytes per pixel
PHYSFS_file* fileHandle;
unsigned char *jpeg_end;
sstrcpy(newfilename, fileName);
memcpy(newfilename + strlen(newfilename) - 4, ".jpg", 4);
fileHandle = PHYSFS_openWrite(newfilename);
if (fileHandle == NULL)
{
debug(LOG_ERROR, "pie_JPEGSaveFile: PHYSFS_openWrite failed (while opening file %s) with error: %s\n", fileName, PHYSFS_getLastError());
return;
}
buffer = malloc(sizeof(const char*) * image->height * image->width);
if (buffer == NULL)
{
debug(LOG_ERROR, "pie_JPEGSaveFile: Couldn't allocate memory\n");
return;
}
// Create an array of scanlines
for (currentRow = 0; currentRow < image->height; ++currentRow)
{
// We're filling the scanline from the bottom up here,
// otherwise we'd have a vertically mirrored image.
memcpy(buffer + row_stride * currentRow, &image->bmp[row_stride * (image->height - currentRow - 1)], row_stride);
}
jpeg = malloc(sizeof(const char*) * image->height * image->width);
if (jpeg == NULL)
{
debug(LOG_ERROR, "pie_JPEGSaveFile: Couldn't allocate memory\n");
return;
}
jpeg_end = jpeg_encode_image(buffer, jpeg, 1, JPEG_FORMAT_RGB, image->width, image->height);
PHYSFS_write(fileHandle, jpeg, jpeg_end - jpeg, 1);
free(buffer);
free(jpeg);
PHYSFS_close(fileHandle);
}

View File

@ -50,4 +50,5 @@ void iV_saveImage_PNG(const char *fileName, const iV_Image *image);
*/
void iV_saveImage_PNG_Gray(const char *fileName, const iV_Image *image);
void iV_saveImage_JPEG(const char *fileName, const iV_Image *image);
#endif // _LIBIVIS_COMMON_PNG_H_

View File

@ -282,7 +282,7 @@ BOOL pie_ShutdownRadar(void)
}
/** Store radar texture with given width and height. */
void pie_DownLoadRadar(UDWORD *buffer, int width, int height)
void pie_DownLoadRadar(UDWORD *buffer, int width, int height, bool filter)
{
int w = 1, h = 1;
char *black;
@ -297,8 +297,16 @@ void pie_DownLoadRadar(UDWORD *buffer, int width, int height)
free(black);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
if (filter)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
radarSizeX = width;

View File

@ -556,8 +556,8 @@ void screenDoDumpToDiskIfRequired(void)
}
glReadPixels(0, 0, image.width, image.height, GL_RGB, GL_UNSIGNED_BYTE, image.bmp);
// Write the screen to a PNG
iV_saveImage_PNG(fileName, &image);
iV_saveImage_JPEG(fileName, &image);
// display message to user about screenshot
snprintf(ConsoleString,sizeof(ConsoleString),"Screenshot %s saved!",fileName);

View File

@ -85,10 +85,10 @@ BOOL NETstopLogging(void)
for (i = 0; i < NUM_GAME_PACKETS; i++)
{
snprintf(buf, sizeof(buf), "%-24s:\t received %u times, %u bytes; sent %u times, %u bytes\n", messageTypeToString(i),
packetcount[0][i], packetsize[0][i], packetcount[1][i], packetsize[1][i]);
packetcount[1][i], packetsize[1][i], packetcount[0][i], packetsize[0][i]);
PHYSFS_write(pFileHandle, buf, strlen(buf), 1);
totalBytessent += packetsize[1][i];
totalBytesrecv += packetsize[0][i];
totalBytessent += packetsize[0][i];
totalBytesrecv += packetsize[1][i];
totalPacketsent += packetcount[0][i];
totalPacketrecv += packetcount[1][i];
}
@ -140,6 +140,11 @@ BOOL NETstopLogging(void)
return true;
}
/** log packet
* \param type, uint8_t, the packet's type.
* \param size, uint32_t, the packet's size
* \param received, BOOL, true if we are receiving a packet, false if we are sending a packet.
*/
void NETlogPacket(uint8_t type, uint32_t size, BOOL received)
{
STATIC_ASSERT((1<<(8*sizeof(type))) == NUM_GAME_PACKETS); // NUM_GAME_PACKETS must be larger than maximum possible type.

View File

@ -2633,6 +2633,7 @@ BOOL NETjoinGame(UDWORD gameNumber, const char* playername)
debug(LOG_ERROR, "Failed to send 'join' command: %s", strSockError(getSockErr()));
SocketSet_DelSocket(socket_set, tcp_socket);
socketClose(tcp_socket);
tcp_socket = NULL;
deleteSocketSet(socket_set);
socket_set = NULL;
return false;
@ -2648,6 +2649,7 @@ BOOL NETjoinGame(UDWORD gameNumber, const char* playername)
// Shouldn't join; game is full
SocketSet_DelSocket(socket_set, tcp_socket);
socketClose(tcp_socket);
tcp_socket = NULL;
deleteSocketSet(socket_set);
socket_set = NULL;
setLobbyError(ERROR_FULL);

View File

@ -21,17 +21,10 @@
#include <physfs.h>
#ifndef WZ_NOSOUND
# include <vorbis/vorbisfile.h>
# if defined(VORBIS_NEEDS_HACK)
/* HACK: Dummy reference vorbisfile.h symbols to prevent warnings */
static WZ_DECL_UNUSED void MKID(dummy)(void)
{
(void)OV_CALLBACKS_DEFAULT;
(void)OV_CALLBACKS_NOCLOSE;
(void)OV_CALLBACKS_STREAMONLY;
(void)OV_CALLBACKS_STREAMONLY_NOCLOSE;
}
# if defined(WZ_OS_MAC) // FIXME: this would be better if we could only have this for PPC builds, that may come later
# define OV_EXCLUDE_STATIC_CALLBACKS
# endif
# include <vorbis/vorbisfile.h>
#endif
#ifdef __BIG_ENDIAN__

View File

@ -479,7 +479,15 @@ static inline TRACK* sound_DecodeOggVorbisTrack(TRACK *psTrack, PHYSFS_file* PHY
{
return NULL;
}
decoder = sound_CreateOggVorbisDecoder(PHYSFS_fileHandle, true);
if (decoder == NULL)
{
debug(LOG_WARNING, "Failed to open audio file for decoding");
free(psTrack);
return NULL;
}
soundBuffer = sound_DecodeOggVorbis(decoder, 0);
sound_DestroyOggVorbisDecoder(decoder);

View File

@ -4,6 +4,6 @@
GCC_OPTIMIZATION_LEVEL = 2
GCC_PREPROCESSOR_DEFINITIONS = VORBIS_NEEDS_HACK $(inherited) // FIXME: VORBIS_NEEDS_HACK is a unnessary hack; there is a better way and it will be done eventually
GCC_PREPROCESSOR_DEFINITIONS = $(inherited)
// BuildDependentFlagsForCandCpp = -fstack-protector // Needs 10.5 min before being used

View File

@ -192,6 +192,7 @@ lib/exceptionhandler/dumpinfo.cpp
lib/exceptionhandler/exceptionhandler.c
lib/exceptionhandler/exchndl.c
lib/framework/configfile.c
lib/framework/crc.cpp
lib/framework/cursors16.c
lib/framework/cursors32.c
lib/framework/cursors.c
@ -217,6 +218,7 @@ lib/iniparser/iniparser.c
lib/ivis_common/bitimage.c
lib/ivis_common/imd.c
lib/ivis_common/imdload.c
lib/ivis_common/jpeg_encoder.c
lib/ivis_common/pieclip.c
lib/ivis_common/piestate.c
lib/ivis_common/png_util.c
@ -266,7 +268,7 @@ src/action.c
src/advvis.c
src/ai.c
src/aiexperience.c
src/astar.c
src/astar.cpp
src/atmos.c
src/aud.c
src/baseobject.c

View File

@ -179,7 +179,7 @@ warzone2100_SOURCES = \
advvis.c \
ai.c \
aiexperience.c \
astar.c \
astar.cpp \
atmos.c \
aud.c \
baseobject.c \

View File

@ -158,6 +158,7 @@ static BOOL actionInAttackRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_s
{
SDWORD dx, dy, dz, radSq, rangeSq, longRange;
WEAPON_STATS *psStats;
int compIndex;
CHECK_DROID(psDroid);
if (psDroid->asWeaps[0].nStat == 0)
@ -171,7 +172,9 @@ static BOOL actionInAttackRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_s
radSq = dx*dx + dy*dy;
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
compIndex = psDroid->asWeaps[weapon_slot].nStat;
ASSERT_OR_RETURN( false, compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
if (psDroid->order == DORDER_ATTACKTARGET
&& secondaryGetState(psDroid, DSO_HALTTYPE) == DSS_HALT_HOLD)
@ -230,6 +233,7 @@ BOOL actionInRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_slot)
{
SDWORD dx, dy, dz, radSq, rangeSq, longRange;
WEAPON_STATS *psStats;
int compIndex;
CHECK_DROID(psDroid);
@ -238,7 +242,9 @@ BOOL actionInRange(DROID *psDroid, BASE_OBJECT *psObj, int weapon_slot)
return false;
}
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
compIndex = psDroid->asWeaps[weapon_slot].nStat;
ASSERT_OR_RETURN( false, compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
dx = (SDWORD)psDroid->pos.x - (SDWORD)psObj->pos.x;
dy = (SDWORD)psDroid->pos.y - (SDWORD)psObj->pos.y;
@ -495,6 +501,7 @@ BOOL actionTargetTurret(BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, WEAPON *
BOOL actionVisibleTarget(DROID *psDroid, BASE_OBJECT *psTarget, int weapon_slot)
{
WEAPON_STATS *psStats;
int compIndex;
CHECK_DROID(psDroid);
ASSERT_OR_RETURN(false, psTarget != NULL, "Target is NULL");
@ -515,8 +522,10 @@ BOOL actionVisibleTarget(DROID *psDroid, BASE_OBJECT *psTarget, int weapon_slot)
}
return false;
}
compIndex = psDroid->asWeaps[weapon_slot].nStat;
ASSERT_OR_RETURN( false, compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
psStats = asWeaponStats + psDroid->asWeaps[weapon_slot].nStat;
if (proj_Direct(psStats))
{
if (visibleObject((BASE_OBJECT*)psDroid, psTarget, true))

View File

@ -1,468 +0,0 @@
/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2009 Warzone Resurrection Project
Warzone 2100 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.
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/** @file
* A* based path finding
*/
#ifndef WZ_TESTING
#include "lib/framework/frame.h"
#include "astar.h"
#include "map.h"
#endif
/** Counter to implement lazy deletion from nodeArray.
*
* @see fpathTableReset
*/
static uint16_t resetIterationCount = 0;
/** The structure to store a node of the route in node table
*
* @ingroup pathfinding
*/
typedef struct _fp_node
{
struct _fp_node *psOpen;
struct _fp_node *psRoute; // Previous point in the route
uint16_t iteration;
short x, y; // map coords
short dist, est; // distance so far and estimate to end
short type; // open or closed node
} FP_NODE;
// types of node
#define NT_OPEN 1
#define NT_CLOSED 2
/** List of open nodes
*/
static FP_NODE* psOpen;
/** A map's maximum width & height
*/
#define MAX_MAP_SIZE (UINT8_MAX + 1)
/** Table of closed nodes
*/
static FP_NODE* nodeArray[MAX_MAP_SIZE][MAX_MAP_SIZE] = { { NULL }, { NULL } };
#define NUM_DIR 8
// Convert a direction into an offset
// dir 0 => x = 0, y = -1
static const Vector2i aDirOffset[NUM_DIR] =
{
{ 0, 1},
{-1, 1},
{-1, 0},
{-1,-1},
{ 0,-1},
{ 1,-1},
{ 1, 0},
{ 1, 1},
};
/** Add a node to the node table
*
* @param psNode to add to the table
*/
static void fpathAddNode(FP_NODE* psNode)
{
const int x = psNode->x;
const int y = psNode->y;
ASSERT(x < ARRAY_SIZE(nodeArray) && y < ARRAY_SIZE(nodeArray[x]), "X (%d) or Y %d) coordinate for path finding node is out of range!", x, y);
// Lets not leak memory
if (nodeArray[x][y])
{
free(nodeArray[x][y]);
}
nodeArray[x][y] = psNode;
// Assign this node to the current iteration (this node will only remain
// valid for as long as it's `iteration` member has the same value as
// resetIterationCount.
psNode->iteration = resetIterationCount;
}
/** See if a node is in the table
* Check whether there is a node for the given coordinates in the table
*
* @param x,y the coordinates to check for
* @return a pointer to the node if one could be found, or NULL otherwise.
*/
static FP_NODE* fpathGetNode(int x, int y)
{
FP_NODE * psFound;
if (x < 0 || y < 0 || x >= ARRAY_SIZE(nodeArray) || y >= ARRAY_SIZE(nodeArray[x]))
{
return NULL;
}
psFound = nodeArray[x][y];
if (psFound
&& psFound->iteration == resetIterationCount)
{
return psFound;
}
return NULL;
}
/** Reset the node table
*
* @NOTE The actual implementation does a lazy reset, because resetting the
* entire node table is expensive.
*/
static void fpathTableReset(void)
{
// Reset node table, simulate this by incrementing the iteration
// counter, which will invalidate all nodes currently in the table. See
// the implementation of fpathGetNode().
++resetIterationCount;
// Check to prevent overflows of resetIterationCount
if (resetIterationCount < UINT16_MAX - 1)
{
ASSERT(resetIterationCount > 0, "Integer overflow occurred!");
return;
}
// If we're about to overflow resetIterationCount, reset the entire
// table for real (not lazy for once in a while) and start counting
// at zero (0) again.
fpathHardTableReset();
}
void fpathHardTableReset()
{
int x, y;
for (x = 0; x < ARRAY_SIZE(nodeArray); ++x)
{
for (y = 0; y < ARRAY_SIZE(nodeArray[x]); ++y)
{
if (nodeArray[x][y])
{
free(nodeArray[x][y]);
nodeArray[x][y] = NULL;
}
}
}
resetIterationCount = 0;
}
/** Add a node to the open list
*/
static inline void fpathOpenAdd(FP_NODE *psNode)
{
psNode->psOpen = psOpen;
psOpen = psNode;
}
/** Get the nearest entry in the open list
*/
static FP_NODE *fpathOpenGet(void)
{
FP_NODE *psNode, *psCurr, *psPrev, *psParent = NULL;
if (psOpen == NULL)
{
return NULL;
}
// find the node with the lowest distance
psPrev = NULL;
psNode = psOpen;
for(psCurr = psOpen; psCurr; psCurr = psCurr->psOpen)
{
const int first = psCurr->dist + psCurr->est;
const int second = psNode->dist + psNode->est;
// if equal totals, give preference to node closer to target
if (first < second || (first == second && psCurr->est < psNode->est))
{
psParent = psPrev;
psNode = psCurr;
}
psPrev = psCurr;
}
// remove the node from the list
if (psNode == psOpen)
{
// node is at head of list
psOpen = psOpen->psOpen;
}
else
{
psParent->psOpen = psNode->psOpen;
}
return psNode;
}
/** Estimate the distance to the target point
*/
static SDWORD WZ_DECL_CONST fpathEstimate(SDWORD x, SDWORD y, SDWORD fx, SDWORD fy)
{
SDWORD xdiff, ydiff;
xdiff = x > fx ? x - fx : fx - x;
ydiff = y > fy ? y - fy : fy - y;
xdiff = xdiff * 10;
ydiff = ydiff * 10;
return xdiff > ydiff ? xdiff + ydiff/2 : xdiff/2 + ydiff;
}
/** Generate a new node
*/
static FP_NODE *fpathNewNode(SDWORD x, SDWORD y, SDWORD dist, FP_NODE *psRoute)
{
FP_NODE *psNode = malloc(sizeof(FP_NODE));
if (psNode == NULL)
{
debug(LOG_ERROR, "fpathNewNode: Out of memory");
return NULL;
}
psNode->x = (SWORD)x;
psNode->y = (SWORD)y;
psNode->dist = (SWORD)dist;
psNode->psRoute = psRoute;
psNode->type = NT_OPEN;
return psNode;
}
SDWORD fpathAStarRoute(MOVE_CONTROL *psMove, PATHJOB *psJob)
{
FP_NODE *psFound, *psCurr, *psNew;
FP_NODE *psNearest, *psRoute;
SDWORD dir, x, y, currDist;
SDWORD retval = ASR_OK;
const int tileSX = map_coord(psJob->origX);
const int tileSY = map_coord(psJob->origY);
const int tileFX = map_coord(psJob->destX);
const int tileFY = map_coord(psJob->destY);
fpathTableReset();
// Add the start point to the open list
psCurr = fpathNewNode(tileSX,tileSY, 0, NULL);
if (!psCurr)
{
fpathTableReset();
return ASR_FAILED;
}
// estimate the estimated distance/moves
psCurr->est = (SWORD)fpathEstimate(psCurr->x, psCurr->y, tileFX, tileFY);
psOpen = NULL;
fpathOpenAdd(psCurr);
fpathAddNode(psCurr);
psRoute = NULL;
psNearest = NULL;
// search for a route
while (psOpen != NULL)
{
psCurr = fpathOpenGet();
if (psCurr->x == tileFX && psCurr->y == tileFY)
{
// reached the target
psRoute = psCurr;
break;
}
// note the nearest node to the target so far
if (psNearest == NULL || psCurr->est < psNearest->est)
{
psNearest = psCurr;
}
// loop through possible moves in 8 directions to find a valid move
for (dir = 0; dir < NUM_DIR; dir += 1)
{
/* make non-orthogonal-adjacent moves' dist a bit longer/cost a bit more
5 6 7
\|/
4 -I- 0
/|\
3 2 1
odd:orthogonal-adjacent tiles even:non-orthogonal-adjacent tiles
odd ones get extra 4 units(1.414 times) of distance/costs
*/
if (dir % 2 == 0)
{
currDist = psCurr->dist + 10;
}
else
{
// We cannot cut corners
x = psCurr->x + aDirOffset[(dir + 1) % 8].x;
y = psCurr->y + aDirOffset[(dir + 1) % 8].y;
if (fpathBaseBlockingTile(x, y, psJob->propulsion, psJob->owner, psJob->moveType))
{
continue;
}
x = psCurr->x + aDirOffset[(dir - 1) % 8].x;
y = psCurr->y + aDirOffset[(dir - 1) % 8].y;
if (fpathBaseBlockingTile(x, y, psJob->propulsion, psJob->owner, psJob->moveType))
{
continue;
}
currDist = psCurr->dist + 14;
}
// Try a new location
x = psCurr->x + aDirOffset[dir].x;
y = psCurr->y + aDirOffset[dir].y;
// See if the node has already been visited
psFound = fpathGetNode(x, y);
if (psFound && psFound->dist <= currDist)
{
// already visited node by a shorter route
continue;
}
// If the tile hasn't been visited see if it is a blocking tile
if (!psFound && fpathBaseBlockingTile(x, y, psJob->propulsion, psJob->owner, psJob->moveType))
{
// tile is blocked, skip it
continue;
}
// Now insert the point into the appropriate list
if (!psFound)
{
// Not in open or closed lists - add to the open list
psNew = fpathNewNode(x,y, currDist, psCurr);
if (psNew)
{
psNew->est = fpathEstimate(x, y, tileFX, tileFY);
fpathOpenAdd(psNew);
fpathAddNode(psNew);
}
}
else if (psFound->type == NT_OPEN)
{
// already in the open list but this is shorter
psFound->dist = (SWORD)currDist;
psFound->psRoute = psCurr;
}
else if (psFound->type == NT_CLOSED)
{
// already in the closed list but this is shorter
psFound->type = NT_OPEN;
psFound->dist = (SWORD)currDist;
psFound->psRoute = psCurr;
fpathOpenAdd(psFound);
}
}
// add the current point to the closed nodes
psCurr->type = NT_CLOSED;
}
// return the nearest route if no actual route was found
if (!psRoute && psNearest)
{
psRoute = psNearest;
retval = ASR_NEAREST;
}
if (psRoute)
{
int count = 0; // calculated length of route
// Count length of route
psCurr = psRoute;
while (psCurr)
{
ASSERT(tileOnMap(psCurr->x, psCurr->y), "Assigned XY coordinates (%d, %d) not on map!", (int)psCurr->x, (int)psCurr->y);
psCurr = psCurr->psRoute;
count++;
}
// TODO FIXME once we can change numPoints to something larger than uchar
psMove->numPoints = MIN(255, count);
// Allocate memory
psMove->asPath = calloc(sizeof(*psMove->asPath), count);
ASSERT(psMove->asPath, "Out of memory");
if (!psMove->asPath)
{
fpathHardTableReset();
return ASR_FAILED;
}
// get the route in the correct order
// If as I suspect this is to reverse the list, then it's my suspicion that
// we could route from destination to source as opposed to source to
// destination. We could then save the reversal. to risky to try now...Alex M
//
// The idea is impractical, because you can't guarentee that the target is
// reachable. As I see it, this is the reason why psNearest got introduced.
// -- Dennis L.
psCurr = psRoute;
if (psCurr && retval == ASR_OK)
{
// Found exact path, so use exact coordinates for last point, no reason to lose precision
count--;
psMove->asPath[count].x = psJob->destX;
psMove->asPath[count].y = psJob->destY;
psMove->DestinationX = psJob->destX;
psMove->DestinationY = psJob->destY;
psCurr = psCurr->psRoute;
}
else
{
psMove->DestinationX = world_coord(psCurr->x) + TILE_UNITS / 2;
psMove->DestinationY = world_coord(psCurr->y) + TILE_UNITS / 2;
}
while (psCurr)
{
count--;
psMove->asPath[count].x = world_coord(psCurr->x) + TILE_UNITS / 2;
psMove->asPath[count].y = world_coord(psCurr->y) + TILE_UNITS / 2;
psCurr = psCurr->psRoute;
}
}
else
{
retval = ASR_FAILED;
}
fpathTableReset();
return retval;
}

463
src/astar.cpp Normal file
View File

@ -0,0 +1,463 @@
/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2010 Warzone Resurrection Project
Warzone 2100 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.
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/** @file
* A* based path finding
* See http://en.wikipedia.org/wiki/A*_search_algorithm for more information.
* How this works:
* * First time (in a given tick) that some droid wants to pathfind to a particular
* destination, the A* algorithm from source to destination is used. The desired
* destination, and the nearest reachable point to the destination is saved in a
* Context.
* * Second time (in a given tick) that some droid wants to pathfind to a particular
* destination, the appropriate Context is found, and the A* algorithm is used to
* find a path from the nearest reachable point to the destination (which was saved
* earlier), to the source.
* * Subsequent times (in a given tick) that some droid wants to pathfind to a parti-
* cular destination, the path is looked up in appropriate Context. If the path is
* not already known, the A* weights are adjusted, and the previous A* pathfinding
* is continued until the new source is reached. If the new source is not reached,
* the droid is on a different island than the previous droid, and pathfinding is
* restarted from the first step.
* Up to 30 pathfinding maps from A* are cached, in a LRU list. The PathNode heap con-
* tains the priority-heap-sorted nodes which are to be explored. The path back is
* stored in the PathExploredTile 2D array of tiles.
*/
#ifndef WZ_TESTING
#include "lib/framework/frame.h"
#include "astar.h"
#include "map.h"
#endif
#include <list>
#include <vector>
#include <algorithm>
/// A coordinate.
struct PathCoord
{
PathCoord() {}
PathCoord(int16_t x_, int16_t y_) : x(x_), y(y_) {}
bool operator ==(PathCoord const &z) const { return x == z.x && y == z.y; }
bool operator !=(PathCoord const &z) const { return !(*this == z); }
int16_t x, y;
};
/** The structure to store a node of the route in node table
*
* @ingroup pathfinding
*/
struct PathNode
{
bool operator <(PathNode const &z) const
{
// Sort decending est, fallback to ascending dist, fallback to sorting by position.
if (est != z.est) return est > z.est;
if (dist != z.dist) return dist < z.dist;
if (p.x != z.p.x) return p.x < z.p.x;
return p.y < z.p.y;
}
PathCoord p; // Map coords.
unsigned dist, est; // Distance so far and estimate to end.
};
struct PathExploredTile
{
PathExploredTile() : iteration(0xFFFF) {}
uint16_t iteration;
int8_t dx, dy; // Offset from previous point in the route.
unsigned dist; // Shortest known distance to tile.
bool visited;
};
// Data structures used for pathfinding, can contain cached results.
struct PathfindContext
{
PathfindContext() : iteration(0) { clear(); }
bool isBlocked(int x, int y) const { return fpathBaseBlockingTile(x, y, propulsion, owner, moveType); }
bool matches(PROPULSION_TYPE propulsion_, int owner_, FPATH_MOVETYPE moveType_, PathCoord tileS_) const { return propulsion == propulsion_ && owner == owner_ && moveType == moveType_ && tileS == tileS_ && gameTime == myGameTime; }
void assign(PROPULSION_TYPE propulsion_, int owner_, FPATH_MOVETYPE moveType_, PathCoord tileS_)
{
propulsion = propulsion_; owner = owner_; moveType = moveType_; tileS = tileS_;
myGameTime = gameTime;
nodes.clear();
// Make the iteration not match any value of iteration in map.
if (++iteration == 0xFFFF)
{
map.clear(); // There are no values of iteration guaranteed not to exist in map, so clear the map.
iteration = 0;
}
map.resize(mapWidth * mapHeight); // Allocate space for map, if needed.
}
void clear() { assign(PROPULSION_TYPE_NUM, -1, FMT_MOVE, PathCoord(0, 0)); }
// Type of object which needs a path.
PROPULSION_TYPE propulsion;
int owner;
FPATH_MOVETYPE moveType;
PathCoord tileS; // Start tile for pathfinding. (May be either source or target tile.)
uint32_t myGameTime;
PathCoord nearestCoord; // Nearest reachable tile to destination.
/** Counter to implement lazy deletion from map.
*
* @see fpathTableReset
*/
uint16_t iteration;
std::vector<PathNode> nodes; ///< Edge of explored region of the map.
std::vector<PathExploredTile> map; ///< Map, with paths leading back to tileS.
};
/// Last recently used list of contexts.
static std::list<PathfindContext> fpathContexts;
// Convert a direction into an offset
// dir 0 => x = 0, y = -1
static const Vector2i aDirOffset[] =
{
{ 0, 1},
{-1, 1},
{-1, 0},
{-1,-1},
{ 0,-1},
{ 1,-1},
{ 1, 0},
{ 1, 1},
};
void fpathHardTableReset()
{
fpathContexts.clear();
}
/** Get the nearest entry in the open list
*/
/// Takes the current best node, and removes from the node heap.
static inline PathNode fpathTakeNode(std::vector<PathNode> &nodes)
{
// find the node with the lowest distance
// if equal totals, give preference to node closer to target
PathNode ret = nodes.front();
// remove the node from the list
std::pop_heap(nodes.begin(), nodes.end()); // Move the best node from the front of nodes to the back of nodes, preserving the heap properties, setting the front to the next best node.
nodes.pop_back(); // Pop the best node (which we will be returning).
return ret;
}
/** Estimate the distance to the target point
*/
static inline unsigned WZ_DECL_CONST fpathEstimate(PathCoord s, PathCoord f)
{
// Cost of moving horizontal/vertical = 70, cost of moving diagonal = 99, 99/70 = 1.41428571... ≈ √2 = 1.41421356...
unsigned xDelta = abs(s.x - f.x), yDelta = abs(s.y - f.y);
return std::min(xDelta, yDelta)*(99 - 70) + std::max(xDelta, yDelta)*70;
}
/** Generate a new node
*/
static inline void fpathNewNode(PathfindContext &context, PathCoord dest, PathCoord pos, unsigned prevDist, PathCoord prevPos)
{
ASSERT((unsigned)pos.x < (unsigned)mapWidth && (unsigned)pos.y < (unsigned)mapHeight, "X (%d) or Y (%d) coordinate for path finding node is out of range!", pos.x, pos.y);
// Create the node.
PathNode node;
node.p = pos;
node.dist = prevDist + fpathEstimate(prevPos, pos);
node.est = node.dist + fpathEstimate(pos, dest);
PathExploredTile &expl = context.map[pos.x + pos.y*mapWidth];
if (expl.iteration == context.iteration && (expl.visited || expl.dist <= node.dist))
{
return; // Already visited this tile. Do nothing.
}
// Remember where we have been, and remember the way back.
expl.iteration = context.iteration;
expl.dx = pos.x - prevPos.x;
expl.dy = pos.y - prevPos.y;
expl.dist = node.dist;
expl.visited = false;
// Add the node to the node heap.
context.nodes.push_back(node); // Add the new node to nodes.
std::push_heap(context.nodes.begin(), context.nodes.end()); // Move the new node to the right place in the heap.
}
/// Recalculates estimates to new tileF tile.
static void fpathAStarReestimate(PathfindContext &context, PathCoord tileF)
{
for (std::vector<PathNode>::iterator node = context.nodes.begin(); node != context.nodes.end(); ++node)
{
node->est = node->dist + fpathEstimate(node->p, tileF);
}
// Changing the estimates breaks the heap ordering. Fix the heap ordering.
std::make_heap(context.nodes.begin(), context.nodes.end());
}
/// Returns nearest explored tile to tileF.
static PathCoord fpathAStarExplore(PathfindContext &context, PathCoord tileF)
{
PathCoord nearestCoord(0, 0);
unsigned nearestDist = 0xFFFFFFFF;
// search for a route
while (!context.nodes.empty())
{
PathNode node = fpathTakeNode(context.nodes);
if (context.map[node.p.x + node.p.y*mapWidth].visited)
{
continue; // Already been here.
}
context.map[node.p.x + node.p.y*mapWidth].visited = true;
// note the nearest node to the target so far
if (node.est - node.dist < nearestDist)
{
nearestCoord = node.p;
nearestDist = node.est - node.dist;
}
if (node.p == tileF)
{
// reached the target
nearestCoord = node.p;
break;
}
// loop through possible moves in 8 directions to find a valid move
for (unsigned dir = 0; dir < ARRAY_SIZE(aDirOffset); ++dir)
{
/*
5 6 7
\|/
4 -I- 0
/|\
3 2 1
odd:orthogonal-adjacent tiles even:non-orthogonal-adjacent tiles
*/
if (dir % 2 != 0)
{
int x, y;
// We cannot cut corners
x = node.p.x + aDirOffset[(dir + 1) % 8].x;
y = node.p.y + aDirOffset[(dir + 1) % 8].y;
if (context.isBlocked(x, y))
{
continue;
}
x = node.p.x + aDirOffset[(dir + 7) % 8].x;
y = node.p.y + aDirOffset[(dir + 7) % 8].y;
if (context.isBlocked(x, y))
{
continue;
}
}
// Try a new location
int x = node.p.x + aDirOffset[dir].x;
int y = node.p.y + aDirOffset[dir].y;
// See if the node is a blocking tile
if (context.isBlocked(x, y))
{
// tile is blocked, skip it
continue;
}
// Now insert the point into the appropriate list, if not already visited.
fpathNewNode(context, tileF, PathCoord(x, y), node.dist, node.p);
}
}
return nearestCoord;
}
static void fpathInitContext(PathfindContext &context, PROPULSION_TYPE propulsion, int owner, FPATH_MOVETYPE moveType, PathCoord tileS, PathCoord tileRealS, PathCoord tileF)
{
context.assign(propulsion, owner, moveType, tileS);
// Add the start point to the open list
fpathNewNode(context, tileF, tileRealS, 0, tileRealS);
ASSERT(!context.nodes.empty(), "fpathNewNode failed to add node.");
}
SDWORD fpathAStarRoute(MOVE_CONTROL *psMove, PATHJOB *psJob)
{
int retval = ASR_OK;
bool mustReverse = true;
const PathCoord tileOrig(map_coord(psJob->origX), map_coord(psJob->origY));
const PathCoord tileDest(map_coord(psJob->destX), map_coord(psJob->destY));
PathCoord endCoord; // Either nearest coord (mustReverse = true) or orig (mustReverse = false).
std::list<PathfindContext>::iterator contextIterator = fpathContexts.begin();
for (contextIterator = fpathContexts.begin(); contextIterator != fpathContexts.end(); ++contextIterator)
{
if (!contextIterator->matches(psJob->propulsion, psJob->owner, psJob->moveType, tileDest))
{
// This context is not for the same droid type and same destination.
continue;
}
// We have tried going to tileDest before.
if (contextIterator->map[tileOrig.x + tileOrig.y*mapWidth].iteration == contextIterator->iteration
&& contextIterator->map[tileOrig.x + tileOrig.y*mapWidth].visited)
{
// Already know the path from orig to dest.
endCoord = tileOrig;
}
else
{
// Need to find the path from orig to dest, continue previous exploration.
fpathAStarReestimate(*contextIterator, tileOrig);
endCoord = fpathAStarExplore(*contextIterator, tileOrig);
}
if (endCoord != tileOrig)
{
// orig turned out to be on a different island than what this context was used for, so can't use this context data after all.
continue;
}
mustReverse = false; // We have the path from the nearest reachable tile to dest, to orig.
break; // Found the path! Don't search more contexts.
}
if (contextIterator == fpathContexts.end())
{
// We did not find an appropriate context. Make one.
if (fpathContexts.size() < 30)
{
fpathContexts.push_back(PathfindContext());
}
--contextIterator;
// Init a new context, overwriting the oldest one if we are caching too many.
// We will be searching from orig to dest, since we don't know where the nearest reachable tile to dest is.
fpathInitContext(*contextIterator, psJob->propulsion, psJob->owner, psJob->moveType, tileOrig, tileOrig, tileDest);
endCoord = fpathAStarExplore(*contextIterator, tileDest);
contextIterator->nearestCoord = endCoord;
}
PathfindContext &context = *contextIterator;
// return the nearest route if no actual route was found
if (context.nearestCoord != tileDest)
{
retval = ASR_NEAREST;
}
// Get route, in reverse order.
static std::vector<Vector2i> path; // Declared static to save allocations.
path.clear();
for (PathCoord p = endCoord; p != context.tileS; p = PathCoord(p.x - context.map[p.x + p.y*mapWidth].dx, p.y - context.map[p.x + p.y*mapWidth].dy))
{
ASSERT(tileOnMap(p.x, p.y), "Assigned XY coordinates (%d, %d) not on map!", (int)p.x, (int)p.y);
Vector2i v = {world_coord(p.x) + TILE_UNITS / 2, world_coord(p.y) + TILE_UNITS / 2};
path.push_back(v);
}
if (path.empty())
{
// We are probably already in the destination tile. Go to the exact coordinates.
Vector2i v = {psJob->destX, psJob->destY};
path.push_back(v);
}
else if (retval == ASR_OK)
{
// Found exact path, so use exact coordinates for last point, no reason to lose precision
Vector2i v = {psJob->destX, psJob->destY};
if (mustReverse)
{
path.front() = v;
}
else
{
path.back() = v;
}
}
// TODO FIXME once we can change numPoints to something larger than uint16_t
psMove->numPoints = std::min<int>(UINT16_MAX, path.size());
// Allocate memory
psMove->asPath = static_cast<Vector2i *>(malloc(sizeof(*psMove->asPath) * path.size()));
ASSERT(psMove->asPath, "Out of memory");
if (!psMove->asPath)
{
fpathHardTableReset();
return ASR_FAILED;
}
// get the route in the correct order
// If as I suspect this is to reverse the list, then it's my suspicion that
// we could route from destination to source as opposed to source to
// destination. We could then save the reversal. to risky to try now...Alex M
//
// The idea is impractical, because you can't guarentee that the target is
// reachable. As I see it, this is the reason why psNearest got introduced.
// -- Dennis L.
//
// If many droids are heading towards the same destination, then destination
// to source would be faster if reusing the information in nodeArray. --Cyp
if (mustReverse)
{
// Copy the list, in reverse.
std::copy(path.rbegin(), path.rend(), psMove->asPath);
if (!context.isBlocked(tileOrig.x, tileOrig.y)) // If blocked, searching from tileDest to tileOrig wouldn't find the tileOrig tile.
{
// Next time, search starting from nearest reachable tile to the destination.
fpathInitContext(context, psJob->propulsion, psJob->owner, psJob->moveType, tileDest, context.nearestCoord, tileOrig);
}
}
else
{
// Copy the list.
std::copy(path.begin(), path.end(), psMove->asPath);
}
// Move context to beginning of last recently used list.
if (contextIterator != fpathContexts.begin()) // Not sure whether or not the splice is a safe noop, if equal.
{
fpathContexts.splice(fpathContexts.begin(), fpathContexts, contextIterator);
}
psMove->DestinationX = psMove->asPath[path.size() - 1].x;
psMove->DestinationY = psMove->asPath[path.size() - 1].y;
return retval;
}

View File

@ -47,7 +47,7 @@ SDWORD fpathAStarRoute(MOVE_CONTROL *psMove, PATHJOB *psJob);
/** Clean up the path finding node table.
*
* @note Call this <em>only</em> on shutdown to prevent memory from leaking.
* @note Call this on shutdown to prevent memory from leaking, or if loading/saving, to prevent stale data from being reused.
*
* @ingroup pathfinding
*/

View File

@ -70,6 +70,7 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in
DROID *psDroid = NULL;
int minOffset = 5;
SDWORD dist;
int compIndex;
CHECK_OBJECT(psAttacker);
CHECK_OBJECT(psTarget);
@ -86,7 +87,9 @@ void combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in
}
/* Get the stats for the weapon */
psStats = asWeaponStats + psWeap->nStat;
compIndex = psWeap->nStat;
ASSERT_OR_RETURN( , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
// check valid weapon/prop combination
if (!validTarget(psAttacker, psTarget, weapon_slot))

View File

@ -37,7 +37,7 @@ void closeConfig( void );
void setDefaultFrameRateLimit(void);
/// Default map for Skirmish
static const char DEFAULTSKIRMISHMAP[] = "Sk-Rush";
static const char DEFAULTSKIRMISHMAP[] = "Rush";
#ifdef __cplusplus
}

View File

@ -1280,45 +1280,46 @@ static COMPONENT_STATS *
intChooseSystemStats( DROID_TEMPLATE *psTemplate )
{
COMPONENT_STATS *psStats = NULL;
int compIndex;
// Choose correct system stats
switch (droidTemplateType(psTemplate))
{
case DROID_COMMAND:
psStats = (COMPONENT_STATS *)(asBrainStats +
psTemplate->asParts[COMP_BRAIN]);
compIndex = psTemplate->asParts[COMP_BRAIN];
ASSERT_OR_RETURN( NULL, compIndex < numBrainStats, "Invalid range referenced for numBrainStats, %d > %d", compIndex, numBrainStats);
psStats = (COMPONENT_STATS *)(asBrainStats + compIndex);
break;
case DROID_SENSOR:
psStats = (COMPONENT_STATS *)(asSensorStats +
psTemplate->asParts[COMP_SENSOR]);
compIndex = psTemplate->asParts[COMP_SENSOR];
ASSERT_OR_RETURN( NULL, compIndex < numSensorStats, "Invalid range referenced for numSensorStats, %d > %d", compIndex, numSensorStats);
psStats = (COMPONENT_STATS *)(asSensorStats + compIndex);
break;
case DROID_ECM:
psStats = (COMPONENT_STATS *)(asECMStats +
psTemplate->asParts[COMP_ECM]);
compIndex = psTemplate->asParts[COMP_ECM];
ASSERT_OR_RETURN( NULL, compIndex < numECMStats, "Invalid range referenced for numECMStats, %d > %d", compIndex, numECMStats);
psStats = (COMPONENT_STATS *)(asECMStats + compIndex);
break;
case DROID_CONSTRUCT:
case DROID_CYBORG_CONSTRUCT:
psStats = (COMPONENT_STATS *)(asConstructStats +
psTemplate->asParts[COMP_CONSTRUCT]);
compIndex = psTemplate->asParts[COMP_CONSTRUCT];
ASSERT_OR_RETURN( NULL, compIndex < numConstructStats, "Invalid range referenced for numConstructStats, %d > %d", compIndex, numConstructStats);
psStats = (COMPONENT_STATS *)(asConstructStats + compIndex);
break;
case DROID_REPAIR:
case DROID_CYBORG_REPAIR:
psStats = (COMPONENT_STATS *)(asRepairStats +
psTemplate->asParts[COMP_REPAIRUNIT]);
compIndex = psTemplate->asParts[COMP_REPAIRUNIT];
ASSERT_OR_RETURN( NULL, compIndex < numRepairStats, "Invalid range referenced for numRepairStats, %d > %d", compIndex, numRepairStats);
psStats = (COMPONENT_STATS *)(asRepairStats + compIndex);
break;
case DROID_WEAPON:
//this is naming stuff
if (psTemplate->numWeaps > 0)
{
psStats = (COMPONENT_STATS *)(asWeaponStats +
psTemplate->asWeaps[0]);
}
case DROID_PERSON:
case DROID_CYBORG:
case DROID_CYBORG_SUPER:
case DROID_DEFAULT:
psStats = (COMPONENT_STATS *)(asWeaponStats +
psTemplate->asWeaps[0]);
compIndex = psTemplate->asWeaps[0];
ASSERT_OR_RETURN( NULL, compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = (COMPONENT_STATS *)(asWeaponStats + compIndex);
break;
default:
debug( LOG_ERROR, "unrecognised droid type" );
@ -1336,6 +1337,7 @@ const char *GetDefaultTemplateName(DROID_TEMPLATE *psTemplate)
// NOTE: At this time, savegames can support a max of 60. We are using WIDG_MAXSTR (currently 80 )for display
// We are also returning a truncated string, instead of NULL if the string is too long.
COMPONENT_STATS *psStats = NULL;
int compIndex;
/*
First we check for the special cases of the Transporter & Cyborgs
@ -1367,7 +1369,9 @@ const char *GetDefaultTemplateName(DROID_TEMPLATE *psTemplate)
sstrcat(aCurrName, _("Hydra "));
}
psStats = (COMPONENT_STATS *) (asBodyStats + psTemplate->asParts[COMP_BODY]);
compIndex = psTemplate->asParts[COMP_BODY];
ASSERT_OR_RETURN( NULL, compIndex < numBodyStats, "Invalid range referenced for numBodyStats, %d > %d", compIndex, numBodyStats);
psStats = (COMPONENT_STATS *) (asBodyStats + compIndex);
if ( psTemplate->asParts[COMP_BODY] != 0 )
{
const char * pStr = getStatName( psStats );
@ -1382,7 +1386,9 @@ const char *GetDefaultTemplateName(DROID_TEMPLATE *psTemplate)
sstrcat(aCurrName, " ");
}
psStats = (COMPONENT_STATS *) (asPropulsionStats + psTemplate->asParts[COMP_PROPULSION]);
compIndex = psTemplate->asParts[COMP_PROPULSION];
ASSERT_OR_RETURN( NULL, compIndex < numPropulsionStats, "Invalid range referenced for numPropulsionStats, %d > %d", compIndex, numPropulsionStats);
psStats = (COMPONENT_STATS *) (asPropulsionStats + compIndex);
if ( psTemplate->asParts[COMP_PROPULSION] != 0 )
{
const char * pStr = getStatName( psStats );
@ -4044,7 +4050,7 @@ void intProcessDesign(UDWORD id)
switch (id)
{
/* The four component clickable forms */
/* Watermelon the six component clickable forms... */
/* the six component clickable forms... */
case IDDES_WEAPONS:
desCompID = 0;
intSetDesignMode(IDES_TURRET);
@ -4369,6 +4375,7 @@ void intRunDesign(void)
UDWORD statID;
COMPONENT_STATS *psStats;
BOOL templateButton;
int compIndex;
/* Find out which button was hilited */
templateButton = false;
@ -4383,11 +4390,15 @@ void intRunDesign(void)
}
else if (statID >= IDDES_COMPSTART && statID <= IDDES_COMPEND)
{
psStats = apsComponentList[statID - IDDES_COMPSTART];
compIndex = statID - IDDES_COMPSTART;
ASSERT_OR_RETURN( , compIndex < numComponent, "Invalid range referenced for numComponent, %d > %d", compIndex, numComponent);
psStats = apsComponentList[compIndex];
}
else if (statID >= IDDES_EXTRASYSSTART && statID <= IDDES_EXTRASYSEND)
{
psStats = apsExtraSysList[statID - IDDES_EXTRASYSSTART];
compIndex = statID - IDDES_EXTRASYSSTART;
ASSERT_OR_RETURN( , compIndex < numExtraSys, "Invalid range referenced for numExtraSys, %d > %d", compIndex, numExtraSys);
psStats = apsExtraSysList[compIndex];
}
else if (statID >= IDDES_TEMPLSTART && statID <= IDDES_TEMPLEND)
{
@ -4686,6 +4697,7 @@ void runTemplateShadowStats(UDWORD id)
COMPONENT_STATS *psStats;
DROID_TYPE templType;
UDWORD i;
int compIndex;
/* Find the template for the new button */
//currID = IDDES_TEMPLSTART;
@ -4715,24 +4727,29 @@ void runTemplateShadowStats(UDWORD id)
switch (templType)
{
case DROID_WEAPON:
psStats = (COMPONENT_STATS *)(asWeaponStats + psTempl->
asWeaps[0]);
compIndex = psTempl->asWeaps[0];
ASSERT_OR_RETURN( , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = (COMPONENT_STATS *)(asWeaponStats + compIndex);
break;
case DROID_SENSOR:
psStats = (COMPONENT_STATS *)(asSensorStats + psTempl->
asParts[COMP_SENSOR]);
compIndex = psTempl->asParts[COMP_SENSOR];
ASSERT_OR_RETURN( , compIndex < numSensorStats, "Invalid range referenced for numSensorStats, %d > %d", compIndex, numSensorStats);
psStats = (COMPONENT_STATS *)(asSensorStats + compIndex);
break;
case DROID_ECM:
psStats = (COMPONENT_STATS *)(asECMStats + psTempl->
asParts[COMP_ECM]);
compIndex = psTempl->asParts[COMP_ECM];
ASSERT_OR_RETURN( , compIndex < numECMStats, "Invalid range referenced for numECMStats, %d > %d", compIndex, numECMStats);
psStats = (COMPONENT_STATS *)(asECMStats + compIndex);
break;
case DROID_CONSTRUCT:
psStats = (COMPONENT_STATS *)(asConstructStats + psTempl->
asParts[COMP_CONSTRUCT]);
compIndex = psTempl->asParts[COMP_CONSTRUCT];
ASSERT_OR_RETURN( , compIndex < numConstructStats, "Invalid range referenced for numConstructStats, %d > %d", compIndex, numConstructStats);
psStats = (COMPONENT_STATS *)(asConstructStats + compIndex);
break;
case DROID_REPAIR:
psStats = (COMPONENT_STATS *)(asRepairStats + psTempl->
asParts[COMP_REPAIRUNIT]);
compIndex = psTempl->asParts[COMP_REPAIRUNIT];
ASSERT_OR_RETURN( , compIndex < numRepairStats, "Invalid range referenced for numRepairStats, %d > %d", compIndex, numRepairStats);
psStats = (COMPONENT_STATS *)(asRepairStats + compIndex);
break;
default:
break;

View File

@ -2748,6 +2748,7 @@ static void drawWeaponReloadBar(BASE_OBJECT *psObj, WEAPON *psWeap, int weapon_s
STRUCTURE *psStruct;
float mulH; // display unit resistance instead of reload!
DROID *psDroid;
int compIndex;
if (ctrlShiftDown() && (psObj->type == OBJ_DROID))
{
@ -2786,7 +2787,9 @@ static void drawWeaponReloadBar(BASE_OBJECT *psObj, WEAPON *psWeap, int weapon_s
return;
}
psStats = asWeaponStats + psWeap->nStat;
compIndex = psWeap->nStat;
ASSERT_OR_RETURN( , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
/* Justifiable only when greater than a one second reload or intra salvo time */
bSalvo = false;
@ -4084,11 +4087,15 @@ static void showEffectCircle(Position centre, int32_t radius, uint32_t auxVar, E
static void showWeaponRange(BASE_OBJECT *psObj)
{
uint32_t weaponRange;
int compIndex;
if (psObj->type == OBJ_DROID)
{
WEAPON_STATS *psStats = NULL;
DROID *psDroid = (DROID*)psObj;
WEAPON_STATS *psStats = asWeaponStats + psDroid->asWeaps[0].nStat;//weapon_slot
compIndex = psDroid->asWeaps[0].nStat; //weapon_slot
ASSERT_OR_RETURN( , compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
weaponRange = psStats->longRange;
}
else

View File

@ -1326,6 +1326,7 @@ BOOL droidUpdateRestore( DROID *psDroid )
STRUCTURE *psStruct;
UDWORD pointsToAdd, restorePoints;
WEAPON_STATS *psStats;
int compIndex;
CHECK_DROID(psDroid);
@ -1336,7 +1337,9 @@ BOOL droidUpdateRestore( DROID *psDroid )
ASSERT_OR_RETURN(false, psDroid->asWeaps[0].nStat > 0, "droid doesn't have any weapons");
psStats = asWeaponStats + psDroid->asWeaps[0].nStat;
compIndex = psDroid->asWeaps[0].nStat;
ASSERT_OR_RETURN(false, compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = asWeaponStats + compIndex;
ASSERT_OR_RETURN(false, psStats->weaponSubClass == WSC_ELECTRONIC, "unit's weapon is not EW");
@ -4161,6 +4164,7 @@ FIRE_SUPPORT order can be assigned*/
BOOL droidSensorDroidWeapon(BASE_OBJECT *psObj, DROID *psDroid)
{
SENSOR_STATS *psStats = NULL;
int compIndex;
CHECK_DROID(psDroid);
@ -4189,7 +4193,9 @@ BOOL droidSensorDroidWeapon(BASE_OBJECT *psObj, DROID *psDroid)
{
return false;
}
psStats = asSensorStats + ((DROID *)psObj)->asBits[COMP_SENSOR].nStat;
compIndex = ((DROID *)psObj)->asBits[COMP_SENSOR].nStat;
ASSERT_OR_RETURN( false, compIndex < numSensorStats, "Invalid range referenced for numSensorStats, %d > %d", compIndex, numSensorStats);
psStats = asSensorStats + compIndex;
break;
case OBJ_STRUCTURE:
psStats = ((STRUCTURE *)psObj)->pStructureType->pSensor;

View File

@ -752,6 +752,9 @@ static BOOL startVideoOptionsMenu(void)
addTopForm();
addBottomForm();
// Add a note about changes taking effect on restart for certain options
addTextHint(FRONTEND_TAKESEFFECT, FRONTEND_POS1X + 48, FRONTEND_POS1Y + 24, _("* Takes effect on game restart"));
// Fullscreen/windowed
addTextButton(FRONTEND_WINDOWMODE, FRONTEND_POS2X-35, FRONTEND_POS2Y, _("Graphics Mode*"), 0);
@ -785,9 +788,6 @@ static BOOL startVideoOptionsMenu(void)
addTextButton(FRONTEND_VSYNC_R, FRONTEND_POS5M-55, FRONTEND_POS5Y, _("Off"), 0);
}
// Add a note about changes taking effect on restart for certain options
addTextButton(FRONTEND_TAKESEFFECT, FRONTEND_POS6X-35, FRONTEND_POS6Y, _("* Takes effect on game restart"), WBUT_DISABLE);
// Add some text down the side of the form
addSideText(FRONTEND_SIDETEXT, FRONTEND_SIDEX, FRONTEND_SIDEY, _("VIDEO OPTIONS"));
@ -945,6 +945,8 @@ static BOOL startMouseOptionsMenu(void)
addTopForm();
addBottomForm();
addTextHint(FRONTEND_TAKESEFFECT, FRONTEND_POS1X + 48, FRONTEND_POS1Y + 24, _("* May negatively affect performance"));
////////////
// mouseflip
addTextButton(FRONTEND_MFLIP, FRONTEND_POS2X-35, FRONTEND_POS2Y, _("Reverse Rotation"), 0);
@ -993,8 +995,6 @@ static BOOL startMouseOptionsMenu(void)
addTextButton(FRONTEND_MBUTTONS_R, FRONTEND_POS2M-25, FRONTEND_POS5Y, _("Off"), 0);
}
addTextButton(FRONTEND_TAKESEFFECT, FRONTEND_POS6X-35, FRONTEND_POS6Y, _("* May negatively affect performance"), WBUT_DISABLE);
// Add some text down the side of the form
addSideText(FRONTEND_SIDETEXT, FRONTEND_SIDEX, FRONTEND_SIDEY, _("MOUSE OPTIONS"));
@ -1521,6 +1521,26 @@ void addBottomForm(void)
widgAddForm(psWScreen, &sFormInit);
}
// ////////////////////////////////////////////////////////////////////////////
void addTextHint(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt)
{
W_LABINIT sLabInit;
memset(&sLabInit, 0, sizeof(W_LABINIT));
sLabInit.formID = FRONTEND_BOTFORM;
sLabInit.id = id;
sLabInit.x = (short)PosX;
sLabInit.y = (short)PosY;
sLabInit.style = WLAB_PLAIN;
sLabInit.width = MULTIOP_READY_WIDTH;
sLabInit.height = FRONTEND_BUTHEIGHT;
sLabInit.pDisplay = displayText;
sLabInit.FontID = font_regular;
sLabInit.pText = txt;
widgAddLabel(psWScreen, &sLabInit);
}
// ////////////////////////////////////////////////////////////////////////////
void addText(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt, UDWORD formID)
{

View File

@ -79,6 +79,7 @@ void addTopForm(void);
void addBottomForm(void);
void addBackdrop(void);
void addTextButton(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt, unsigned int style);
void addTextHint(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt);
void addText(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt, UDWORD formID);
void addSideText(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt);
void addFESlider(UDWORD id, UDWORD parent, UDWORD x, UDWORD y, UDWORD stops, UDWORD pos);

View File

@ -11457,30 +11457,38 @@ static BOOL getNameFromComp(UDWORD compType, char *pDest, UDWORD compIndex)
switch (compType)
{
case COMP_BODY:
ASSERT_OR_RETURN( false, compIndex < numBodyStats, "Invalid range referenced for numBodyStats, %d > %d", compIndex, numBodyStats);
psStats = (BASE_STATS *)(asBodyStats + compIndex);
break;
case COMP_BRAIN:
ASSERT_OR_RETURN( false, compIndex < numBrainStats, "Invalid range referenced for numBrainStats, %d > %d", compIndex, numBrainStats);
psStats = (BASE_STATS *)(asBrainStats + compIndex);
break;
case COMP_PROPULSION:
ASSERT_OR_RETURN( false, compIndex < numPropulsionStats, "Invalid range referenced for numPropulsionStats, %d > %d", compIndex, numPropulsionStats);
psStats = (BASE_STATS *)(asPropulsionStats + compIndex);
break;
case COMP_REPAIRUNIT:
ASSERT_OR_RETURN( false, compIndex < numRepairStats, "Invalid range referenced for numRepairStats, %d > %d", compIndex, numRepairStats);
psStats = (BASE_STATS*)(asRepairStats + compIndex);
break;
case COMP_ECM:
ASSERT_OR_RETURN( false, compIndex < numECMStats, "Invalid range referenced for numECMStats, %d > %d", compIndex, numECMStats);
psStats = (BASE_STATS*)(asECMStats + compIndex);
break;
case COMP_SENSOR:
ASSERT_OR_RETURN( false, compIndex < numSensorStats, "Invalid range referenced for numSensorStats, %d > %d", compIndex, numSensorStats);
psStats = (BASE_STATS*)(asSensorStats + compIndex);
break;
case COMP_CONSTRUCT:
ASSERT_OR_RETURN( false, compIndex < numConstructStats, "Invalid range referenced for numConstructStats, %d > %d", compIndex, numConstructStats);
psStats = (BASE_STATS*)(asConstructStats + compIndex);
break;
/*case COMP_PROGRAM:
psStats = (BASE_STATS*)(asProgramStats + compIndex);
break;*/
case COMP_WEAPON:
ASSERT_OR_RETURN( false, compIndex < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", compIndex, numWeaponStats);
psStats = (BASE_STATS*)(asWeaponStats + compIndex);
break;
default:

View File

@ -2798,6 +2798,7 @@ static void intProcessStats(UDWORD id)
BASE_STATS *psStats;
STRUCTURE *psStruct;
FLAG_POSITION *psFlag;
int compIndex;
DROID_TEMPLATE *psNext;
@ -2820,16 +2821,15 @@ static void intProcessStats(UDWORD id)
//manufacture works differently!
if(objMode == IOBJ_MANUFACTURE)
{
compIndex = id - IDSTAT_START;
//get the stats
psStats = ppsStatsList[id - IDSTAT_START];
ASSERT( psObjSelected != NULL,
"intProcessStats: Invalid structure pointer" );
ASSERT( psStats != NULL,
"intProcessStats: Invalid template pointer" );
ASSERT_OR_RETURN( , compIndex < numStatsListEntries, "Invalid range referenced for numStatsListEntries, %d > %d", compIndex, numStatsListEntries);
psStats = ppsStatsList[compIndex];
ASSERT_OR_RETURN( , psObjSelected != NULL, "Invalid structure pointer" );
ASSERT_OR_RETURN( , psStats != NULL, "Invalid template pointer" );
if (productionPlayer == (SBYTE)selectedPlayer)
{
FACTORY *psFactory = (FACTORY *)((STRUCTURE *)psObjSelected)->
pFunctionality;
FACTORY *psFactory = (FACTORY *)((STRUCTURE *)psObjSelected)->pFunctionality;
//increase the production
factoryProdAdjust((STRUCTURE *)psObjSelected, (DROID_TEMPLATE *)psStats, true);
@ -2931,7 +2931,9 @@ static void intProcessStats(UDWORD id)
}
// Set the object stats
psStats = ppsStatsList[id - IDSTAT_START];
compIndex = id - IDSTAT_START;
ASSERT_OR_RETURN( , compIndex < numStatsListEntries, "Invalid range referenced for numStatsListEntries, %d > %d", compIndex, numStatsListEntries);
psStats = ppsStatsList[compIndex];
// Reset the button on the object form
//if this returns false, there's a problem so set the button to NULL
@ -4302,6 +4304,7 @@ static BOOL intAddObjectWindow(BASE_OBJECT *psObjects, BASE_OBJECT *psSelected,B
BOOL IsFactory;
BOOL Animate = true;
UWORD FormX,FormY;
int compIndex;
// Is the form already up?
if(widgGetFromID(psWScreen,IDOBJ_FORM) != NULL) {
@ -4643,8 +4646,10 @@ static BOOL intAddObjectWindow(BASE_OBJECT *psObjects, BASE_OBJECT *psSelected,B
if (Droid->droidType == DROID_CONSTRUCT ||
Droid->droidType == DROID_CYBORG_CONSTRUCT)
{
ASSERT( Droid->asBits[COMP_CONSTRUCT].nStat,"intAddObjectWindow: invalid droid type" );
psStats = (BASE_STATS*)(asConstructStats + Droid->asBits[COMP_CONSTRUCT].nStat);
compIndex = Droid->asBits[COMP_CONSTRUCT].nStat;
ASSERT_OR_RETURN( false, Droid->asBits[COMP_CONSTRUCT].nStat,"Invalid droid type" );
ASSERT_OR_RETURN( false, compIndex < numConstructStats, "Invalid range referenced for numConstructStats, %d > %d", compIndex, numConstructStats);
psStats = (BASE_STATS*)(asConstructStats + compIndex);
sBarInit2.size = (UWORD)constructorPoints((CONSTRUCT_STATS*)psStats, Droid->player);
if (sBarInit2.size > WBAR_SCALE)
{
@ -6508,8 +6513,7 @@ static void intStatsRMBPressed(UDWORD id)
DROID_TEMPLATE *psStats;
DROID_TEMPLATE *psNext;
ASSERT( id - IDSTAT_START < numStatsListEntries,
"intStatsRMBPressed: Invalid structure stats id" );
ASSERT_OR_RETURN( , id - IDSTAT_START < numStatsListEntries, "Invalid range referenced for numStatsListEntries, %d > %d",id - IDSTAT_START, numStatsListEntries);
if (objMode == IOBJ_MANUFACTURE)
{
@ -6517,14 +6521,11 @@ static void intStatsRMBPressed(UDWORD id)
//this now causes the production run to be decreased by one
ASSERT( psObjSelected != NULL,
"intStatsRMBPressed: Invalid structure pointer" );
ASSERT( psStats != NULL,
"intStatsRMBPressed: Invalid template pointer" );
ASSERT_OR_RETURN( , psObjSelected != NULL, "Invalid structure pointer");
ASSERT_OR_RETURN( , psStats != NULL, "Invalid template pointer");
if (productionPlayer == (SBYTE)selectedPlayer)
{
FACTORY *psFactory = (FACTORY *)((STRUCTURE *)psObjSelected)->
pFunctionality;
FACTORY *psFactory = (FACTORY *)((STRUCTURE *)psObjSelected)->pFunctionality;
//decrease the production
factoryProdAdjust((STRUCTURE *)psObjSelected, psStats, false);

View File

@ -145,12 +145,19 @@ LEVEL_DATASET* levFindDataSet(const char* name)
for (psNewLevel = psLevels; psNewLevel; psNewLevel = psNewLevel->psNext)
{
if (psNewLevel->pName != NULL
&& strcmp(psNewLevel->pName, name) == 0)
if (psNewLevel->pName != NULL)
{
if (strcmp(psNewLevel->pName, name) == 0 ||
(strncmp(psNewLevel->pName, "Sk-", 3) == 0 &&
strcmp(psNewLevel->pName+3, name) == 0) ||
(strncmp(psNewLevel->pName, "Sk-", 3) == 0 &&
strcmp(psNewLevel->pName+strlen(name)-3-3, "-T1") &&
strncmp(psNewLevel->pName+3, name, strlen(name)-3) == 0))
{
return psNewLevel;
}
}
}
return NULL;
}

View File

@ -20,7 +20,7 @@ SRC= \
advvis.c \
ai.c \
aiexperience.c \
astar.c \
astar.cpp \
atmos.c \
aud.c \
baseobject.c \

View File

@ -80,13 +80,6 @@
// Maximum size of an object for collision
#define OBJ_MAXRADIUS (TILE_UNITS * 4)
// How close to a way point the next target starts to phase in
const float WAYPOINT_DIST = TILE_UNITS*2.0f;
#define WAYPOINT_DSQ (WAYPOINT_DIST*WAYPOINT_DIST) // C does not allows this to be a const variable...
// Never get turned stronger towards the 2nd next waypoint than this times the current waypoint's strength
const float WAYPOINT_2NDNEXT_SUCKINESS = 0.5f;
// Accuracy for the boundary vector
#define BOUND_ACC 1000
@ -1445,71 +1438,21 @@ static void moveGetObstacleVector(DROID *psDroid, float *pX, float *pY)
*/
static uint16_t moveGetDirection(DROID *psDroid)
{
// Current position and destination direction
Vector2f
src = { psDroid->pos.x, psDroid->pos.y },
dest = { 0.0f, 0.0f };
// Current waypoint
Vector2f
target = { psDroid->sMove.targetX, psDroid->sMove.targetY },
delta = Vector2f_Sub(target, src);
float magnitude = Vector2f_ScalarP(delta, delta);
// Dont fade in the next target point if we are at finished or too far away from the current
if (psDroid->sMove.Position == psDroid->sMove.numPoints || magnitude > WAYPOINT_DSQ)
{
dest = Vector2f_Normalise(delta);
}
// We are in reach of the current waypoint and have further points to go
else
{
// Next waypoint
Vector2f
nextTarget = Vector2i_To2f( movePeekNextTarget(psDroid) ),
nextDelta = Vector2f_Sub(nextTarget, src);
float nextMagnitude = Vector2f_ScalarP(nextDelta, nextDelta);
// We are already there
if (magnitude < FLT_EPSILON && nextMagnitude < FLT_EPSILON)
{
return 0; // We are practically standing on our only waypoint
}
// We are passing the current waypoint, so directly head over to the next
else if (magnitude < FLT_EPSILON)
{
dest = Vector2f_Normalise(nextDelta);
}
// We are passing the next waypoint or so close we could be circling between both,
// so for now don't interpolate it. Instead head to the first unvisited waypoint as
// a silly workaround. What we should do is drop this waypoint and head to the next.
else if (nextMagnitude < WAYPOINT_DSQ)
{
dest = Vector2f_Normalise(delta);
}
// Interpolate with the next target
else
{
// Make sure we get sucked stronger towards the 2nd waypoint than the first.
// Also ensure we get never turned away from a waypoint.
float nextInterpolation = fmaxf(fminf(WAYPOINT_DSQ - magnitude, magnitude * WAYPOINT_2NDNEXT_SUCKINESS), 0.0f);
// Interpolate the vectors based on how close to them we are
delta = Vector2f_Mult(delta, magnitude);
nextDelta = Vector2f_Mult(nextDelta, nextInterpolation);
dest = Vector2f_Normalise( Vector2f_Add(delta, nextDelta) );
}
}
ASSERT(isfinite(dest.x) && isfinite(dest.y), "float infinity detected");
Position src = droidGetPrecisePosition(psDroid);
Position target = { (psDroid->sMove.targetX << EXTRA_BITS), (psDroid->sMove.targetY << EXTRA_BITS), 0 };
Position dest = Vector3i_Sub(target, src);
// Transporters don't need to avoid obstacles, but everyone else should
if ( psDroid->droidType != DROID_TRANSPORTER )
if (psDroid->droidType != DROID_TRANSPORTER)
{
moveGetObstacleVector(psDroid, &dest.x, &dest.y);
float fX = dest.x >> EXTRA_BITS;
float fY = dest.y >> EXTRA_BITS;
moveGetObstacleVector(psDroid, &fX, &fY);
dest.x = fX * EXTRA_PRECISION;
dest.y = fY * EXTRA_PRECISION;
}
return iAtan2(dest.x*10000, dest.y*10000);
return iAtan2(dest.x, dest.y);
}

View File

@ -120,7 +120,7 @@ void clearPlayer(UDWORD player,BOOL quietly)
UDWORD i;
STRUCTURE *psStruct,*psNext;
debug(LOG_INFO, "R.I.P. %s (%u). quietly is %s", getPlayerName(player), player, quietly ? "true":"false");
debug(LOG_NET, "R.I.P. %s (%u). quietly is %s", getPlayerName(player), player, quietly ? "true":"false");
ingame.JoiningInProgress[player] = false; // if they never joined, reset the flag
ingame.DataIntegrity[player] = false;
@ -209,7 +209,7 @@ BOOL MultiPlayerLeave(UDWORD playerIndex)
}
NETlogEntry("Player leaving game", SYNC_FLAG, playerIndex);
debug(LOG_INFO,"** Player %u [%s], has left the game at game time %u.", playerIndex, getPlayerName(playerIndex), gameTime);
debug(LOG_NET,"** Player %u [%s], has left the game at game time %u.", playerIndex, getPlayerName(playerIndex), gameTime);
ssprintf(buf, _("%s has Left the Game"), getPlayerName(playerIndex));

View File

@ -569,6 +569,16 @@ void addMultiRequest(const char* searchDir, const char* fileExtension, UDWORD mo
sstrcpy(tips[tip_index], mapName);
free(mapName);
// Chop off the "Sk-" if necessary
if (strncmp(tips[tip_index], "Sk-", 3) == 0)
{
memmove(tips[tip_index], tips[tip_index]+3, strlen(tips[tip_index]+3)+1);
}
if (strncmp(tips[tip_index]+strlen(tips[tip_index])-3, "-T1", 3)==0)
{
tips[tip_index][strlen(tips[tip_index])-3] = 0;
}
sButInit.pTip = tips[tip_index];
sButInit.pText = tips[tip_index];
sButInit.UserData = players;

View File

@ -102,7 +102,7 @@ static SDWORD msgStackPos = -1; //top element pointer
// Remote Prototypes
extern RESEARCH* asResearch; //list of possible research items.
extern PLAYER_RESEARCH* asPlayerResList[MAX_PLAYERS];
extern int NET_PlayerConnectionStatus;
// ////////////////////////////////////////////////////////////////////////////
// Local Prototypes
@ -743,6 +743,24 @@ BOOL recvMessage(void)
case NET_OPTIONS:
recvOptions(queue);
break;
case NET_PLAYER_DROPPED: // remote player got disconnected
{
BOOL host;
uint32_t player_id;
NETbeginDecode(queue, NET_PLAYER_DROPPED);
{
NETuint32_t(&player_id);
NETbool(&host);
}
NETend();
debug(LOG_INFO,"** player %u has dropped! Host is %s", player_id, host?"true":"false");
MultiPlayerLeave(player_id); // get rid of their stuff
NET_PlayerConnectionStatus = 2; //DROPPED_CONNECTION
break;
}
case NET_PLAYERRESPONDING: // remote player is now playing
{
uint32_t player_id;

View File

@ -385,9 +385,9 @@ BOOL proj_SendProjectile(WEAPON *psWeap, BASE_OBJECT *psAttacker, int player, Ve
Vector3f muzzle;
WEAPON_STATS *psStats = &asWeaponStats[psWeap->nStat];
ASSERT( psStats != NULL, "proj_SendProjectile: invalid weapon stats" );
ASSERT( psTarget == NULL || !psTarget->died, "Aiming at dead target!" );
ASSERT_OR_RETURN( false, psWeap->nStat < numWeaponStats, "Invalid range referenced for numWeaponStats, %d > %d", psWeap->nStat, numWeaponStats);
ASSERT_OR_RETURN( false, psStats != NULL, "Invalid weapon stats" );
ASSERT_OR_RETURN( false, psTarget == NULL || !psTarget->died, "Aiming at dead target!" );
/* get muzzle offset */
if (psAttacker == NULL)
@ -681,7 +681,7 @@ static void proj_InFlightFunc(PROJECTILE *psProj, bool bIndirect)
psProj->time = gameTime;
psStats = psProj->psWStats;
ASSERT(psStats != NULL, "proj_InFlightDirectFunc: Invalid weapon stats pointer");
ASSERT_OR_RETURN( , psStats != NULL, "Invalid weapon stats pointer");
/* we want a delay between Las-Sats firing and actually hitting in multiPlayer
magic number but that's how long the audio countdown message lasts! */

View File

@ -86,6 +86,7 @@ static SDWORD radarWidth, radarHeight, radarCenterX, radarCenterY, radarTexWidth
static uint8_t RadarZoom;
static float RadarZoomMultiplier = 1.0f;
static UDWORD radarBufferSize = 0;
static int frameSkip = 0;
static void DrawRadarTiles(void);
static void DrawRadarObjects(void);
@ -155,7 +156,14 @@ BOOL resizeRadar(void)
return false;
}
memset(radarBuffer, 0, radarBufferSize);
if (rotateRadar)
{
RadarZoomMultiplier = (float)MAX(RADWIDTH, RADHEIGHT) / (float)MAX(radarTexWidth, radarTexHeight);
}
else
{
RadarZoomMultiplier = 1.0f;
}
debug(LOG_WZ, "Setting radar zoom to %u", RadarZoom);
radarSize(RadarZoom);
@ -189,6 +197,7 @@ void SetRadarZoom(uint8_t ZoomLevel)
debug(LOG_WZ, "Setting radar zoom to %u from %u", ZoomLevel, RadarZoom);
RadarZoom = ZoomLevel;
radarSize(RadarZoom);
frameSkip = 0;
}
uint8_t GetRadarZoom(void)
@ -255,7 +264,6 @@ void CalcRadarPosition(int mX, int mY, int *PosX, int *PosY)
void drawRadar(void)
{
float pixSizeH, pixSizeV;
static int frameSkip = 0;
ASSERT(radarBuffer, "No radar buffer allocated");
if (!radarBuffer)
@ -267,9 +275,14 @@ void drawRadar(void)
if (frameSkip <= 0)
{
bool filter = true;
if (!rotateRadar)
{
filter = RadarZoom % 16 != 0;
}
DrawRadarTiles();
DrawRadarObjects();
pie_DownLoadRadar(radarBuffer, radarTexWidth, radarTexHeight);
pie_DownLoadRadar(radarBuffer, radarTexWidth, radarTexHeight, filter);
frameSkip = RADAR_FRAME_SKIP;
}
frameSkip--;

View File

@ -35,8 +35,8 @@ extern "C"
void radarColour(UDWORD tileNumber, uint8_t r, uint8_t g, uint8_t b); ///< Set radar colour for given terrain type.
#define MAX_RADARZOOM (16 * 10/4) // 2.50x
#define MIN_RADARZOOM (16 * 3/4) // 0.75x
#define MAX_RADARZOOM (16 * 4) // 3.00x
#define MIN_RADARZOOM (16 * 2/4) // 0.75x
#define DEFAULT_RADARZOOM (16) // 1.00x
#define RADARZOOM_STEP (16 * 1/4) // 0.25x

View File

@ -618,16 +618,15 @@ BOOL scrOrderDroidStatsLoc(void)
if (statIndex < 0 || statIndex >= (SDWORD)numStructureStats)
{
ASSERT( false,
"scrOrderUnitStatsLoc: invalid structure stat" );
ASSERT( false, "Invalid structure stat" );
return false;
}
ASSERT_OR_RETURN( false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats);
psStats = (BASE_STATS *)(asStructureStats + statIndex);
ASSERT( psDroid != NULL,
"scrOrderUnitStatsLoc: Invalid Unit pointer" );
ASSERT( psStats != NULL,
"scrOrderUnitStatsLoc: Invalid object pointer" );
ASSERT_OR_RETURN( false, psDroid != NULL, "Invalid Unit pointer" );
ASSERT_OR_RETURN( false, psStats != NULL, "Invalid object pointer" );
if (psDroid == NULL)
{
return false;
@ -636,15 +635,13 @@ BOOL scrOrderDroidStatsLoc(void)
if ((x < 0) || (x > (SDWORD)mapWidth*TILE_UNITS) ||
(y < 0) || (y > (SDWORD)mapHeight*TILE_UNITS))
{
ASSERT( false,
"scrOrderUnitStatsLoc: Invalid location" );
ASSERT( false, "Invalid location" );
return false;
}
if (order != DORDER_BUILD)
{
ASSERT( false,
"scrOrderUnitStatsLoc: Invalid order" );
ASSERT( false, "Invalid order" );
return false;
}
@ -1850,7 +1847,10 @@ static BOOL defenseLocation(BOOL variantB)
return false;
}
ASSERT_OR_RETURN( false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats);
psStats = (BASE_STATS *)(asStructureStats + statIndex);
ASSERT_OR_RETURN( false, statIndex2 < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex2, numStructureStats);
psWStats = (BASE_STATS *)(asStructureStats + statIndex2);
// check for wacky coords.

View File

@ -2108,6 +2108,7 @@ BOOL scrStructureBeingBuilt(void)
return false;
}
ASSERT_OR_RETURN( false, structInc < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", structInc, numStructureStats);
psStats = (STRUCTURE_STATS *)(asStructureStats + structInc);
beingBuilt = false;
if (checkStructureStatus(psStats, player, SS_BEING_BUILT))
@ -2191,6 +2192,7 @@ BOOL scrStructureBuilt(void)
return false;
}
ASSERT_OR_RETURN( false, structInc < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", structInc, numStructureStats);
psStats = (STRUCTURE_STATS *)(asStructureStats + structInc);
built = false;
@ -8351,10 +8353,10 @@ BOOL scrNumStructsByStatInArea(void)
return false;
}
ASSERT_OR_RETURN( false, index < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", index, numStructureStats);
psStats = (STRUCTURE_STATS *)(asStructureStats + index);
ASSERT( psStats != NULL,
"scrNumStructsByStatInArea: Invalid structure pointer" );
ASSERT_OR_RETURN( false, psStats != NULL, "Invalid structure pointer" );
NumStruct = 0;
@ -8959,6 +8961,7 @@ BOOL scrObjWeaponMaxRange(void)
psDroid = (DROID*)psObj;
if (psDroid->asWeaps[0].nStat != 0)
{
ASSERT_OR_RETURN(false, psDroid->asWeaps[0].nStat < numWeaponStats, "Invalid range referenced.");
psStats = asWeaponStats + psDroid->asWeaps[0].nStat;
scrFunctionResult.v.ival = psStats->longRange;
if (!stackPushResult(VAL_INT, &scrFunctionResult))
@ -8974,6 +8977,7 @@ BOOL scrObjWeaponMaxRange(void)
psStruct = (STRUCTURE*)psObj;
if (psStruct->asWeaps[0].nStat != 0)
{
ASSERT_OR_RETURN(false, psStruct->asWeaps[0].nStat < numWeaponStats, "Invalid range referenced.");
psStats = asWeaponStats + psStruct->asWeaps[0].nStat;
scrFunctionResult.v.ival = psStats->longRange;
if (!stackPushResult(VAL_INT, &scrFunctionResult))

View File

@ -3327,6 +3327,7 @@ BOOL objHasWeapon(BASE_OBJECT *psObj)
SENSOR_STATS *objActiveRadar(BASE_OBJECT *psObj)
{
SENSOR_STATS *psStats = NULL;
int compIndex;
switch (psObj->type)
{
@ -3335,7 +3336,9 @@ SENSOR_STATS *objActiveRadar(BASE_OBJECT *psObj)
{
return NULL;
}
psStats = asSensorStats + ((DROID *)psObj)->asBits[COMP_SENSOR].nStat;
compIndex = ((DROID *)psObj)->asBits[COMP_SENSOR].nStat;
ASSERT_OR_RETURN( NULL, compIndex < numSensorStats, "Invalid range referenced for numSensorStats, %d > %d", compIndex, numSensorStats);
psStats = asSensorStats + compIndex;
break;
case OBJ_STRUCTURE:
psStats = ((STRUCTURE *)psObj)->pStructureType->pSensor;

View File

@ -252,7 +252,7 @@
>
</File>
<File
RelativePath="..\src\astar.c"
RelativePath="..\src\astar.cpp"
>
</File>
<File

View File

@ -1,12 +1,12 @@
PKG_NAME:=libpng
PKG_VERSION=1.2.42
PKG_VERSION=1.2.44
PKG_SOURCEBASE:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE:=$(PKG_SOURCEBASE).tar.bz2
PKG_SOURCE_URL:= \
@SF/libpng \
http://www.il.fontys.nl/~giel/warzone/devpkg/
PKG_MD5SUM:=9a5cbe9798927fdf528f3186a8840ebe
PKG_MD5SUM:=e3ac7879d62ad166a6f0c7441390d12b
TARGET:=$(TOPDIR)/build/libs/lib/libpng.la