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.cmaster
commit
f0beca71cc
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 )\
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -7,6 +7,7 @@ SRC= \
|
|||
bitimage.c \
|
||||
imd.c \
|
||||
imdload.c \
|
||||
jpeg_encoder.c \
|
||||
pieclip.c \
|
||||
piestate.c \
|
||||
png_util.c
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -179,7 +179,7 @@ warzone2100_SOURCES = \
|
|||
advvis.c \
|
||||
ai.c \
|
||||
aiexperience.c \
|
||||
astar.c \
|
||||
astar.cpp \
|
||||
atmos.c \
|
||||
aud.c \
|
||||
baseobject.c \
|
||||
|
|
15
src/action.c
15
src/action.c
|
@ -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))
|
||||
|
|
468
src/astar.c
468
src/astar.c
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
83
src/design.c
83
src/design.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
10
src/droid.c
10
src/droid.c
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
37
src/hci.c
37
src/hci.c
|
@ -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);
|
||||
|
|
11
src/levels.c
11
src/levels.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ SRC= \
|
|||
advvis.c \
|
||||
ai.c \
|
||||
aiexperience.c \
|
||||
astar.c \
|
||||
astar.cpp \
|
||||
atmos.c \
|
||||
aud.c \
|
||||
baseobject.c \
|
||||
|
|
77
src/move.c
77
src/move.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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! */
|
||||
|
|
17
src/radar.c
17
src/radar.c
|
@ -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--;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -252,7 +252,7 @@
|
|||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\astar.c"
|
||||
RelativePath="..\src\astar.cpp"
|
||||
>
|
||||
</File>
|
||||
<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
|
||||
|
||||
|
|
Loading…
Reference in New Issue