Files
source/Multiplayer/client.cpp
T
Wanne 7ca53fadc7 *** MERGED source code from development trunk revision 4890 up to latest current revision 5105 ***
Development Trunk (now obsolete): https://ja2svn.dyndns.org/source/ja2/branches/Wanne/JA2%201.13%20MP
A detailed list of all the features (changes) is visible in the history log of the development trunk:

INFO: There will be no further development in the development source trunk, so is obsolete from now on!

This commit contains the following features (bugfixes are not listed):

- New Feature: Show merc ranks in Strategy Screen before merc name
o Toggle on/off in the ingame options screen
o For each experience level (0-10) there are defined rank names in the language specific text files
o Additional language text that is not translated yet is marked with // TODO.Translate

- New Feature: Faster Bobby Ray Shipments
o This feature is selectable in the ja2_options.ini
o When activated Bobby Ray shipments will be really fast
-> Overnight Express: 5 - 7 hours
-> Air Service: 10, 12 or 14 hours
-> Standard Service: 15, 18 or 21 hours
o In a multiplayer game, this feature is disabled (because shipment arrive immediately in the destination sector)

- New Feature: AP cost for manipulating the merc inventory
o This feature is selectable in the start new game screen
o For example, putting a weapon from hand in the backpack costs APs
o The AP costs are defined in the APBPConstants.ini
o In a multiplayer game, this feature is disabled

- New Feature: Added possibility to set individual Gear Kit names
o Set individual names in the optional new tag <mGearKitName> inside the <GEARKIT> root element (File: TableData\MercStartingGear.xml)

- New Feature: Externalized the maximum purchase amount (JA2 vanilla: 10) for Bobby Rays purchases per shipment
o Define the max. amount in the ja2_options.ini: BOBBY_RAY_MAX_PURCHASE_AMOUNT, Range: 10 - 100)
o Nice scrolling bars to scroll through all the purchases

- New Feature: Now it is possible to walk on "water" tiles or handle those tiles like "normal" tiles 
o (instead of showing the swimming animation and counting the APs for swimming), when the soldier is located on the second level (level > 0)

- New Feature: Externalized AIM Veterans laptop site
o Add maximum of 4 pages
o Add  new AIM to veterans page, and remove AIM from veterans page.
o New XML file: OldAIMArchive.xml (language specific XML files: *.OldAIMArchive.xml)

- New Feature: Added possibility to choose from 254 IMP faces
o Modified IMPPortraits.xml
o Added new GameDir folder "IMPFaces" which contains the IMP face portraits
o Moved IMP faces in the new "IMPFaces" folder

- New Feature: ARSP - All Resolution Support Project
o See here: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=298698&page=1
o Support any kind of screen resolution. This works by choosing the matching full supported resolution (640x480, 800x600, 1024x768) and then centering the graphics
o See ja2.ini for defining the screen resolution

- New Feature: AI Turn Speed Up (FF-MOD)
o see here: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=298699#Post298699
o When this feature is activated it allows fast enemy turns
o New entry in ja2.ini to enable this feature: HIGHSPEED_TIMER = TRUE
o Turn on/off auto fast turns in ingame options
o New ja2_options.ini settings for fine tuning: [Clock Settings] ini block

- New Feature: "Lock" / "Release" mouse cursor in tactical windowed mode, so the mouse can stay within window boundary
o This is very helpful for doing scrolling with the mouse (by moving the mouse to the window edge) in windowed mode
o You can toggle on/off the feature by pressing CTRL + Z. It is also possible to press CTRL + Y. So the user can decide which fits better :)
o Whenever you toggle on/off a screen message appears on the tactical log to notify the player

- New Feature: Weapon overheating
o Basically, every shot fired from a gun increases its temperature. On higher temperatures, the gun jams more frequently
o On/off toggle in the ingame options
o Graphical thermometer indication on the weapon picture
o New ja2_options.ini section with a few options, Section: [Tactical Weapon Overheating Settings]
o XML changes (New Tags)
--> AmmoTypes.xml: <temperatureModificator>
--> Items.xml: <barrel>, <usOverheatingCooldownFactor>
--> Weapons.xml: <usOverheatingJamThreshold>, <usOverheatingDamageThreshold>, <usOverheatingSingleShotTemperature>
o Added a few barells to Items.xml (end of the file)
o more infos: http://www.ja-galaxy-forum.com/board/ubbthreads.php?ubb=showflat&Number=301059&page=1

- New Feature: Resting weapons on a surfaces
o You can now rest your weapon on crates, low walls, rocks, debris, open windows etc. while you are crouched. Resting will give yu the superior gun handling modifiers from PRONE position.
o A little 'A' on your gun indicates if you are resting it or not.
o Both settings can be turned on and off in Ja2_Options.ini under 'Tactical Gameplay Settings'

- New Feature: Externalized option on whether mercs can exit sector using grid exit in turn based mode
o new ja2_options.ini property: GRID_EXIT_IN_TURNBASED
o This ini setting does not affect leaving sectors on map edges

- Rework/Update: Ammo boxes and Ammo crates
o Ammo crate items added for remaining calibers.  Tweaked prices to correspond to magazines, tweaked capacities and weights to be reasonable (~800 or less).
o Shift-R reload now looks at both boxes and crates in sector items and merc's inventory to reload, and tops off magazines in inventory as well.  Only possible outside of combat.
o Ctrl-Shift-A still creates crates as per usual, with some fixes to ensure all ammo is caught on the first press.
o Shift-A now creates ammo boxes in the same loop.  They mostly behave like crates, but fit in mercs inventories.
o Ammo boxes are available to purchase at BR and tony, sam, etc.  Individual magazines/drums/etc are no longer available.
o Removed ammo crate xml tag.  Added ubMagType tag to Magazines array.  This allows us to differentiate between magazines, bullets, boxes and crates.
o Changed ammo filtering in BR's to display boxes appropriately.  Added back the "Mag:" display (changed text to "Size:")
o Added ja2set.dat.xml file (WF map version in WF maps folder) to be used instead of ja2set.dat file (ja2.ini setting added) For now, if you want to play vanilla instead, just change the setting in the ini (see comments in file).
o Added P4-P9Items image files (up to 5k images each) and tileslots.  1.13 core items will continue to use Guns and P1-P3Items for the foreseeable future.  P4-P9 can be used by customizers/modders to avoid conflicts with Guns/P1-P3 items.  TODO: Need to discuss a strategy for this in the forums.
o Added in Ambient/0.bad and 32.bad to avoid an exception in the code.
o Updated the XML Editor
o Standardized the text on all the ammo items.

- New Multiplayer Feature: Sharing same FOV (field of view) in a coop/team-dm (for friendly teams) game
o You will also see enemies, that your teammate sees

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5108 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-03-23 18:46:54 +00:00

5141 lines
163 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#include "strategic.h"
#else
#include "builddefines.h"
#include "bullets.h"
#include <stdio.h>
#include <string.h>
#include "wcheck.h"
#include "stdlib.h"
#include "debug.h"
#include "math.h"
#include "worlddef.h"
#include "worldman.h"
#include "renderworld.h"
#include "Assignments.h"
#include "Soldier Control.h"
#include "Animation Control.h"
#include "Animation Data.h"
#include "Isometric Utils.h"
#include "Event Pump.h"
#include "Timer Control.h"
#include "Render Fun.h"
#include "Render Dirty.h"
#include "mousesystem.h"
#include "interface.h"
#include "sysutil.h"
#include "FileMan.h"
#include "points.h"
#include "Random.h"
#include "ai.h"
#include "Interactive Tiles.h"
#include "soldier ani.h"
#include "english.h"
#include "overhead.h"
#include "Soldier Profile.h"
#include "Game Clock.h"
#include "soldier create.h"
#include "Merc Hiring.h"
#include "Game Event Hook.h"
#include "message.h"
#include "strategicmap.h"
#include "strategic.h"
#include "items.h"
#include "Soldier Add.h"
#include "History.h"
#include "Squads.h"
#include "Strategic Merc Handler.h"
#include "Dialogue Control.h"
#include "Map Screen Interface.h"
#include "Map Screen Interface Map.h"
#include "screenids.h"
#include "jascreens.h"
#include "text.h"
#include "Merc Contract.h"
#include "LaptopSave.h"
#include "personnel.h"
#include "Auto Resolve.h"
#include "Map Screen Interface Bottom.h"
#include "Quests.h"
#include "GameSettings.h"
#include "Mercs.h"
#include "Handle Doors.h"
#include "Campaign Types.h"
#include "Tactical Save.h"
#include "BobbyRMailOrder.h"
#include "Finances.h"
#include "TeamTurns.h"
#include "gameloop.h"
#include "Options Screen.h"
#include "physics.h"
#include "Explosion Control.h"
#include "SmokeEffects.h"
#include "MPChatScreen.h"
#include "sgp_logger.h"
#endif
#include "MessageIdentifiers.h"
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"
#include "RakNetStatistics.h"
#include "RakNetTypes.h"
#include "RakSleep.h"
#include "FileListTransfer.h"
#include "FileListTransferCBInterface.h"
#include "FileOperations.h"
#include "SuperFastHash.h"
#include "RakAssert.h"
#include "IncrementalReadInterface.h"
#include "BitStream.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include "music control.h"
#include "map edgepoints.h"
#include "fresh_header.h"
#include "network.h"
#include "opplist.h"
#include "tactical placement gui.h"
#include "prebattle interface.h"
#include "mapscreen.h"
#include "MessageBoxScreen.h"
#include <vfs/Core/vfs.h>
#include <vfs/Core/vfs_init.h>
#include <vfs/Core/vfs_profile.h>
#include <vfs/Core/vfs_file_raii.h>
#include <vfs/Core/vfs_os_functions.h>
#include <vfs/Core/File/vfs_file.h>
#include "transfer_rules.h"
#include "keys.h"
#include "new.h"
#include "Types.h"
#include "connect.h"
#include "message.h"
#include "Event pump.h"
#include "Soldier Init List.h"
#include "Overhead.h"
#include "weapons.h"
#include "Merc Hiring.h"
#include "soldier profile.h"
#include "environment.h"
#include "lighting.h"
#include "laptop.h"
#include "interface panels.h"
#include "game init.h"
#include "Debug Control.h"
#include "MPConnectScreen.h"
#include "IniReader.h"
#include "Map Screen Interface Border.h"
extern CHAR16 gzFileTransferDirectory[100];
// WANNE: FILE TRANSFER
BOOLEAN fClientReceivedAllFiles;
STRING512 client_executableDir;
STRING512 client_fileTransferDirectoryPath; // the clients file transfer directory absolut path
STRING512 server_fileTransferDirectoryPath; // the server file transfer directory absolut path
INT16 fileTransferProgress = 0;
INT16 serverSyncClientsDirectory = 0;
unsigned char GetPacketIdentifier(Packet *p);
unsigned char packetIdentifier;
// OJW - 20090405
STRING512 gCurrentTransferFilename;
INT32 gCurrentTransferBytes = 0;
INT32 gTotalTransferBytes = 0;
extern BOOLEAN gfTemporaryDisablingOfLoadPendingFlag;
extern INT8 SquadMovementGroups[ ];
RakPeerInterface *client;
// WANNE: FILE TRANSFER
FileListTransfer fltClient; // flt2
char *ReplaceCharactersInString_Client(char *str, char *orig, char *rep)
{
static char buffer[4096];
char *p;
if(!(p = strstr(str, orig))) // Is 'orig' even in 'str'?
return str;
strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' st$
buffer[p-str] = '\0';
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
return buffer;
}
class ClientTransferCB : public FileListTransferCBInterface
{
public:
// This method gets called for each file when it is completly received by the client.
// Now the file will be saved on the client
bool OnFile(OnFileStruct *onFileStruct)
{
if(!transferRules)
{
transferRules = new CTransferRules();
transferRules->initFromTxtFile("transfer_rules.txt");
}
// Get the directory path of the file and output it to the user!
char* targetFileName = ExtractFilename(onFileStruct->fileName);
//ScreenMsg( FONT_BCOLOR_ORANGE, MSG_CHAT, MPClientMessage[58], targetFileName);
vfs::Path fileName(onFileStruct->fileName);
vfs::String::str_t const& valid_str = fileName.c_wcs();
vfs::String::size_t pos = valid_str.find(L":");
if(pos != vfs::String::str_t::npos)
{
// absolute path?? these are invalid and the server is not supposed to send us such paths
// potentialy malicious server -> output error
return false;
}
if(valid_str.substr(0,2) == vfs::Const::DOTDOT())
{
// trying to break out from the profile?
// potentialy malicious server -> output error
return false;
}
if(transferRules && (transferRules->applyRule(valid_str) == CTransferRules::DENY))
{
// sent file was on our ignore list
// it may be OK that the server's list and the clients' lists diverge
// send message to server that we cannot accept this file
return false;
}
strcpy(gCurrentTransferFilename,onFileStruct->fileName);
try
{
vfs::COpenWriteFile wfile(fileName,true,true);
wfile->write(onFileStruct->fileData,onFileStruct->finalDataLength);
}
catch(vfs::Exception& ex)
{
SGP_ERROR(ex.what());
ScreenMsg( FONT_BCOLOR_BLUE, MSG_CHAT, MPClientMessage[70], targetFileName);
}
/*
// Make sure it worked
unsigned int hash1 = SuperFastHashFile(fileToSend);
if (RakNet::BitStream::DoEndianSwap())
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash1, sizeof(hash1));
unsigned int hash2 = SuperFastHashFile(targetFileName);
if (RakNet::BitStream::DoEndianSwap())
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash2, sizeof(hash2));
RakAssert(hash1==hash2);
*/
//ScreenMsg( FONT_BCOLOR_ORANGE, MSG_MPSYSTEM, L"Saved file local in %S", file);
// Return true to have RakNet delete the memory allocated to hold this file.
// False if you hold onto the memory, and plan to delete it yourself later
return true;
}
// WANNE: FILE TRANSFER: This method gets called each periodically
virtual void OnFileProgress(OnFileStruct *onFileStruct,unsigned int partCount,unsigned int partTotal,unsigned int partLength, char *firstDataChunk)
{
static UINT32 iNextTransferProgressUpdateTime;
gCurrentTransferBytes += (INT32)(onFileStruct->finalDataLength * (float)(1.0f/(float)partTotal));
if (guiBaseJA2NoPauseClock >= iNextTransferProgressUpdateTime)
{
char *relativeFname = ReplaceCharactersInString_Client(onFileStruct->fileName, server_fileTransferDirectoryPath,"");
strcpy(gCurrentTransferFilename,relativeFname);
iNextTransferProgressUpdateTime = guiBaseJA2NoPauseClock + 100;
// update all clients of our file transfer progress
INT8 currentProgress = (INT8)(100.0f * (float)((float)gCurrentTransferBytes/(float)gTotalTransferBytes));
SetConnectScreenSubMessageA(gCurrentTransferFilename); // setting this also causes connect screen to refresh
if (recieved_settings && CLIENT_NUM > 0 && CLIENT_NUM <= 4)
{
client_progress[CLIENT_NUM-1] = currentProgress;
client_downloading[CLIENT_NUM-1] = 1;
fDrawCharacterList = true;
progress_struct prog;
prog.client_num = CLIENT_NUM;
prog.downloading = 1;
prog.progress = currentProgress;
client->RPC("sendDOWNLOADSTATUS",(const char*)&prog, (int)sizeof(progress_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
/*INT16 currentProgress = 100 * partCount/partTotal;
INT16 fileTransferProgressNew = 0;
// WANNE: FILE TRANSFER: Only output (0, 25, 50, 75) percentage
if (currentProgress < 25)
fileTransferProgressNew = 0;
else if (currentProgress < 50)
fileTransferProgressNew = 25;
else if (currentProgress < 75)
fileTransferProgressNew = 50;
else
fileTransferProgressNew = 75;
if (fileTransferProgressNew != fileTransferProgress)
{
char* targetFileName = ExtractFilename(onFileStruct->fileName);
fileTransferProgress = fileTransferProgressNew;
ScreenMsg( FONT_BCOLOR_BROWN, MSG_MPSYSTEM, MPClientMessage[59], targetFileName, fileTransferProgress);
}*/
//ScreenMsg( FONT_BLUE, MSG_MPSYSTEM, L"(%i%%) %S", 100*partCount/partTotal, onFileStruct->fileName);
//ScreenMsg( FONT_BLUE, MSG_MPSYSTEM, L"%i (%i%%) %i/%i %S %ib->%ib / %ib->%ib\n", onFileStruct->setID, 100*partCount/partTotal, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength, firstDataChunk);
//printf("%i (%i%%) %i/%i %s %ib->%ib / %ib->%ib\n", onFileStruct->setID, 100*partCount/partTotal, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength, firstDataChunk);
}
virtual bool OnDownloadComplete(void)
{
//printf("Download complete.\n");
if (serverSyncClientsDirectory)
{
ScreenMsg( FONT_RED, MSG_MPSYSTEM, MPClientMessage[60]);
SetConnectScreenSubMessageW(MPClientMessage[60]); // setting this also causes connect screen to refresh
//fileTransferProgress = 0;
if(transferRules)
{
delete transferRules;
transferRules = NULL;
}
if (recieved_settings && CLIENT_NUM > 0 && CLIENT_NUM <= 4)
{
// notify ourselves
client_progress[CLIENT_NUM-1] = 0;
client_downloading[CLIENT_NUM-1] = 0;
// notify others
progress_struct prog;
prog.client_num = CLIENT_NUM;
prog.downloading = 0; // notify clients we have finished
prog.progress = 0;
fDrawCharacterList = true;
client->RPC("sendDOWNLOADSTATUS",(const char*)&prog, (int)sizeof(progress_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
// Set a flag that we have received all the files. This is used that when another client connects we do not want to receive the files again!
fClientReceivedAllFiles = TRUE;
// Returning false automatically deallocates the automatically allocated handler that was created by DirectoryDeltaTransfer
return false;
}
private:
char *ExtractFilename(char *pathname)
{
char *s;
if ((s=strrchr(pathname, '\\')) != NULL) s++;
else if ((s=strrchr(pathname, '/')) != NULL) s++;
else if ((s=strrchr(pathname, ':')) != NULL) s++;
else s = pathname;
return s;
}
// client has an ignore list too, as the server doesn't have to play by the rules
static CTransferRules* transferRules;
} transferCallback;
CTransferRules* ClientTransferCB::transferRules = NULL;
ClientTransferCB transferCBClient;
// OJW - 20081222
player_stats gMPPlayerStats[5];
// OJW - 20090503 - get rid of compile error
#pragma pack (1)
typedef struct
{
UINT8 ubProfileID;
int team;
BOOLEAN fCopyProfileItemsOver;
INT8 bTeam;
} send_hire_struct;
typedef struct
{
UINT8 ubProfileID;
} send_dismiss_struct;
typedef struct
{
UINT8 usSoldierID;
FLOAT dNewXPos;
FLOAT dNewYPos;
} gui_pos;
typedef struct
{
UINT8 usSoldierID;
INT16 usNewDirection;
} gui_dir;
typedef struct
{
UINT8 tsnetbTeam;
UINT8 tsubNextTeam;
} turn_struct;
typedef struct
{
SOLDIERCREATE_STRUCT standard_data;
OBJECTTYPE slot[55];
} AI_STRUCT;
typedef struct
{
BULLET net_bullet;
UINT16 usHandItem;
}netb_struct;
typedef struct
{
UINT8 ubID;
INT8 bTeam;
UINT8 gubOutOfTurnPersons;
UINT8 gubOutOfTurnOrder[MAXMERCS];
BOOLEAN fMarkInterruptOccurred;
UINT8 Interrupted;
} INT_STRUCT;
typedef struct
{
UINT8 client_num;
bool status;
UINT8 ready_stage;
} ready_struct;
typedef struct
{
INT32 remote_id;
INT32 local_id;
}bullets_table;
typedef struct
{
UINT8 ubResult;
}kickR;
typedef struct
{
UINT8 client_num;
BOOLEAN bToAll;
CHAR16 msg[512];
} chat_msg;
// OJW - 20091002 - explosions
typedef struct
{
float dLifeSpan;
float dX;
float dY;
float dZ;
float dForceX;
float dForceY;
float dForceZ;
UINT32 sTargetGridNo;
UINT8 ubID;
UINT8 ubActionCode;
UINT32 uiActionData;
UINT16 usItem;
INT32 RealObjectID; // the local ID on the initiating client
bool IsThrownGrenade; // could be mortar
UINT32 uiPreRandomIndex;
} physics_object;
typedef struct
{
float dX;
float dY;
float dZ;
INT32 sGridNo;
bool bWasDud;
UINT8 ubOwnerID;
INT32 RealObjectID; // the local ID on the initiating client
UINT32 uiPreRandomIndex; // send out our current pre-random index
} grenade_result;
typedef struct
{
UINT32 sGridNo;
UINT8 ubID;
UINT16 usItem;
UINT8 ubItemStatus;
UINT32 uiWorldIndex; // the local World Index of this bomb on its creators client
UINT16 usFlags;
UINT8 ubLevel;
INT8 bDetonatorType;
INT8 bDelayFreq;
} explosive_obj;
typedef struct
{
UINT8 ubID;
UINT32 uiWorldItemIndex;
UINT8 ubMPTeamIndex;
UINT32 uiPreRandomIndex; // send out our current pre-random index
} detonate_struct;
typedef struct
{
UINT32 uiWorldItemIndex;
UINT8 ubMPTeamIndex;
UINT8 ubID;
UINT32 sGridNo;
UINT32 uiPreRandomIndex; // send out our current pre-random index
} disarm_struct;
typedef struct
{
INT32 sGridNo;
UINT8 ubRadius;
UINT16 usItem;
UINT8 ubOwner;
BOOLEAN fSubsequent;
INT8 bLevel;
INT32 iSmokeEffectID;
UINT32 uiPreRandomIndex;
} spreadeffect_struct;
typedef struct
{
UINT8 ubDamageFunc; // 1 - gas damage , 2 - explosive damage
UINT8 ubSoldierID;
UINT16 usExplosiveClassID;
INT16 sSubsequent;
BOOL fRecompileMovementCosts;
INT16 sWoundAmt;
INT16 sBreathAmt;
UINT8 ubAttackerID;
UINT16 usItem;
INT32 sBombGridNo;
UINT32 uiDist;
UINT32 uiPreRandomIndex;
} explosiondamage_struct;
bullets_table bTable[11][50];
char client_names[4][30];
// OJW - 20081204
int client_ready[4];
int client_edges[5];
int client_teams[4];
int random_mercs[7];
// OJW - 20090305
int client_downloading[4];
int client_progress[4];
int TEAM;
UINT8 netbTeam;
UINT8 ubID_prefix;
bool is_connected=false;
bool is_connecting=false;
bool is_client=false;
bool is_server=false;
bool is_networked=false;
bool is_host=false; // OJW - added 20081130 - new flag to signal our intention to host, coming in from the HOST screen
bool auto_retry=true;
int giNumTries = MAX_CONNECT_RETRIES; // default is 5 retries
UINT32 giNextRetryTime = 0;
bool requested=false;
int kit[20];
bool allowlaptop=false;
bool recieved_settings=false;
// OJW - 20090422
bool recieved_transfer_settings = false;
bool getReal=false;
int CLIENT_NUM;
// --------------------
// Global Client Variables
// --------------------
UINT8 cStartingSectorEdge;
char cKitBag[100];
char cClientName[30];
UINT8 gRandomStartingEdge;
UINT8 gRandomMercs;
char cServerName[30];
char cGameDataSyncDirectory[100];
UINT8 cReportHiredMerc;
UINT8 cWeaponReadyBonus;
UINT8 cDisableSpectatorMode;
UINT8 gEnemyEnabled;
UINT8 gCreatureEnabled;
UINT8 gMilitiaEnabled;
UINT8 gCivEnabled;
UINT8 cAllowMercEquipment;
UINT8 cSameMercAllowed;
UINT8 cGameType;
UINT8 cMaxMercs;
FLOAT cDamageMultiplier;
UINT8 cMaxClients;
UINT8 cDisableMorale;
// --------------------
// --------------------
// Other client variables
// --------------------
bool goahead = 0;
int numready = 0;
int readystage = 0;
bool status = 0;
bool wiped;
UINT32 iScoreScreenTime = 0;
int iTeamsWiped = 0; // counts how many teams have wiped
bool is_game_over = false;
bool isOwnTeamWipedOut = false;
int iDisconnectedScreen = 0;
int bClosingChatBoxToStartGame = false;
UINT32 iCCStartGameTime = 0;
bool is_game_started = false;
void overide_callback( UINT8 ubResult );
void kick_callback( UINT8 ubResult );
void turn_callback (UINT8 ubResult);
void ChatCallback (UINT8 ubResult);
void HandleClientConnectionLost();
void disconnected_callback(UINT8 ubResult);
// OJW - 20081222
void send_gameover( void );
void StartScoreScreen(); // this screen will send us to the multiplayer score screen
// OJW - 20090403
extern BOOLEAN gfMPSScoreScreenCanContinue; // can the score screen continue
// OJW - 20090430
SystemAddress serverAddr;
// OJW - 20090507
settings_struct gMPServerSettings; // store a copy of our settings after we receive them
CHAR16 gszDisconnectReason[255]; // the reason we were disconnected from the server
// OJW - added 20081130
// <TODO> - add retry timer and notification
void NetworkAutoStart()
{
if (!is_networked)
return; // not networked, bad call
if (!is_host || is_server)
{
// is pure client or the server has been started
// so do client connection checking...though probably not needed for the server client. cant hurt.
// user JOIN'd game
if (is_client && is_connected)
{
return; // should be set up and running
}
else if (is_connecting)
{
return; // we're waiting on a connection , do nothing
}
else
{
if (giNumTries <= 0 || !auto_retry)
return; // dont auto-retry
if (guiBaseJA2NoPauseClock < giNextRetryTime)
return; // we are waiting for a retry timer
// try and connect
giNumTries--;
connect_client();
// next rety time is set in client_packet()
}
}
else
{
// is host and server isnt started so start it
start_server();
}
}
void InvalidClientSettingsOkBoxCallback( UINT8 bExitValue )
{
// yes, load the game
if( bExitValue == MSG_BOX_RETURN_OK )
{
// gracefully disconnect to the main menu
client_disconnect();
guiPendingScreen = MP_JOIN_SCREEN;
}
return;
}
// OJW - this is just a fudge for now and needs to be improved later
bool are_clients_downloading()
{
bool bDownloading = false;
for (int i=0; i < 4; i++)
{
if (client_downloading[i] == 1)
bDownloading = true;
}
return bDownloading;
}
void HireRandomMercs()
{
MERC_HIRE_STRUCT HireMercStruct;
for (int i=0; i < cMaxMercs; i++)
{
memset(&HireMercStruct, 0, sizeof(MERC_HIRE_STRUCT));
HireMercStruct.ubProfileID = random_mercs[i];// use the merc list recieved from the server
//DEF: temp
HireMercStruct.sSectorX = gsMercArriveSectorX;
HireMercStruct.sSectorY = gsMercArriveSectorY;
HireMercStruct.fUseLandingZoneForArrival = TRUE;
HireMercStruct.ubInsertionCode = INSERTION_CODE_ARRIVING_GAME;
HireMercStruct.iTotalContractLength = 1;
HireMercStruct.fCopyProfileItemsOver = true;
gMercProfiles[ HireMercStruct.ubProfileID ].ubMiscFlags |= PROFILE_MISC_FLAG_ALREADY_USED_ITEMS;
HireMerc(&HireMercStruct);
}
fDrawCharacterList = true;
fTeamPanelDirty = true;
ReBuildCharactersList();
}
//*****************
//RPC sends and recieves:
//********************
void send_path ( SOLDIERTYPE *pSoldier, INT32 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestartAnim )
{
if(pSoldier->ubID < 120)
{
//ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Sending new path" );
EV_S_SENDPATHTONETWORK SNetPath;
if(pSoldier->ubID < 20)
SNetPath.usSoldierID = (pSoldier->ubID)+ubID_prefix;
else
SNetPath.usSoldierID = pSoldier->ubID;
SNetPath.sAtGridNo=pSoldier->sGridNo;
SNetPath.ubNewState=usMovementAnim;
SNetPath.usCurrentPathIndex=pSoldier->pathing.usPathIndex;
memcpy(SNetPath.usPathData, pSoldier->pathing.usPathingData, sizeof(UINT16)*30);
SNetPath.usPathDataSize=pSoldier->pathing.usPathDataSize;
SNetPath.sDestGridNo=sDestGridNo;
client->RPC("sendPATH",(const char*)&SNetPath, (int)sizeof(EV_S_SENDPATHTONETWORK)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void recievePATH(RPCParameters *rpcParameters)
{
//ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"Recieving new path," );
EV_S_SENDPATHTONETWORK* SNetPath = (EV_S_SENDPATHTONETWORK*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SNetPath->usSoldierID ];
memcpy(pSoldier->pathing.usPathingData, SNetPath->usPathData,sizeof(UINT16)*30);
pSoldier->pathing.sDestination = SNetPath->sDestGridNo;
pSoldier->pathing.sFinalDestination = SNetPath->sDestGridNo;
pSoldier->pathing.usPathIndex=SNetPath->usCurrentPathIndex;
pSoldier->pathing.usPathDataSize=SNetPath->usPathDataSize;
SendGetNewSoldierPathEvent( pSoldier, SNetPath->sDestGridNo, SNetPath->ubNewState );
INT16 sCellX, sCellY;
sCellX = CenterX( SNetPath->sAtGridNo );
sCellY = CenterY( SNetPath->sAtGridNo );
if (( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_MOVING | ANIM_SPECIALMOVE ) ) && !(pSoldier->flags.fNoAPToFinishMove ) )
{
}
else
{
pSoldier->EVENT_InternalSetSoldierPosition( sCellX, sCellY ,FALSE, FALSE, FALSE );
}
if(pSoldier)pSoldier->EVENT_InitNewSoldierAnim( SNetPath->ubNewState, 0, FALSE );
}
void send_stance ( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance )
{
if(pSoldier->ubID < 120)
{
EV_S_CHANGESTANCE SChangeStance;
SChangeStance.ubNewStance = ubDesiredStance;
//SChangeStance.usSoldierID = pSoldier->ubID;
if(pSoldier->ubID < 20)
SChangeStance.usSoldierID = (pSoldier->ubID)+ubID_prefix;
else
SChangeStance.usSoldierID = pSoldier->ubID;
SChangeStance.sXPos = pSoldier->sX;
SChangeStance.sYPos = pSoldier->sY;
SChangeStance.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
//ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, L"change stance: %d",ubDesiredStance );
client->RPC("sendSTANCE",(const char*)&SChangeStance, (int)sizeof(EV_S_CHANGESTANCE)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void recieveSTANCE(RPCParameters *rpcParameters)
{
EV_S_CHANGESTANCE* SChangeStance = (EV_S_CHANGESTANCE*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SChangeStance->usSoldierID ];
pSoldier->ChangeSoldierStance( SChangeStance->ubNewStance );
//SendChangeSoldierStanceEvent( pSoldier, SChangeStance->ubNewStance );
//AddGameEvent( S_CHANGESTANCE, 0, &SChangeStance );
//********* implemented using event pump system ... :)
}
void send_dir ( SOLDIERTYPE *pSoldier, UINT16 usDesiredDirection )
{
if((is_server && pSoldier->ubID < 120) || (!is_server && pSoldier->ubID < 20))
{
EV_S_SETDESIREDDIRECTION SSetDesiredDirection;
//SSetDesiredDirection.usSoldierID = pSoldier->ubID;
if(pSoldier->ubID < 20)
SSetDesiredDirection.usSoldierID = (pSoldier->ubID)+ubID_prefix;
else
SSetDesiredDirection.usSoldierID = pSoldier->ubID;
SSetDesiredDirection.usDesiredDirection = usDesiredDirection;
SSetDesiredDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
client->RPC("sendDIR",(const char*)&SSetDesiredDirection, (int)sizeof(EV_S_SETDESIREDDIRECTION)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void recieveDIR(RPCParameters *rpcParameters)
{
EV_S_SETDESIREDDIRECTION* SSetDesiredDirection = (EV_S_SETDESIREDDIRECTION*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SSetDesiredDirection->usSoldierID ];
pSoldier->EVENT_SetSoldierDesiredDirection( SSetDesiredDirection->usDesiredDirection );
//AddGameEvent( S_SETDESIREDDIRECTION, 0, &SSetDesiredDirection );
//********* implemented using event pump system ... :)
}
void send_fire( SOLDIERTYPE *pSoldier, INT32 sTargetGridNo )
{
if(pSoldier->ubID < 120)
{
EV_S_BEGINFIREWEAPON SBeginFireWeapon;
if(pSoldier->ubID < 20)
SBeginFireWeapon.usSoldierID = (pSoldier->ubID)+ubID_prefix;
else
SBeginFireWeapon.usSoldierID = pSoldier->ubID;
SBeginFireWeapon.sTargetGridNo = sTargetGridNo;
SBeginFireWeapon.bTargetLevel = pSoldier->bTargetLevel;
SBeginFireWeapon.bTargetCubeLevel = pSoldier->bTargetCubeLevel;
SBeginFireWeapon.uiUniqueId = pSoldier->usAttackingWeapon;
client->RPC("sendFIRE",(const char*)&SBeginFireWeapon, (int)sizeof(EV_S_BEGINFIREWEAPON)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void recieveFIRE(RPCParameters *rpcParameters)
{
EV_S_BEGINFIREWEAPON* SBeginFireWeapon = (EV_S_BEGINFIREWEAPON*)rpcParameters->input;
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"SendBeginFireWeaponEvent" );
SOLDIERTYPE *pSoldier = MercPtrs[ SBeginFireWeapon->usSoldierID ];
pSoldier->sTargetGridNo = SBeginFireWeapon->sTargetGridNo;
pSoldier->bTargetLevel = SBeginFireWeapon->bTargetLevel;
pSoldier->bTargetCubeLevel = SBeginFireWeapon->bTargetCubeLevel;
pSoldier->usAttackingWeapon = SBeginFireWeapon->uiUniqueId; //cheap hack to pass wep id.
SendBeginFireWeaponEvent( pSoldier, SBeginFireWeapon->sTargetGridNo );
}
void send_hit( EV_S_WEAPONHIT *SWeaponHit )
{
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"sendHIT" );
//EV_S_WEAPONHIT* pWeaponHit = (EV_S_WEAPONHIT*)pEventData;
//SOLDIERTYPE *pSoldier = MercPtrs[ usSoldierID ];
EV_S_WEAPONHIT weaphit_struct;
memcpy( &weaphit_struct , SWeaponHit, sizeof( EV_S_WEAPONHIT ));
UINT16 usSoldierID=weaphit_struct.usSoldierID;
if(SWeaponHit->usSoldierID < 20)weaphit_struct.usSoldierID = weaphit_struct.usSoldierID+ubID_prefix;
if(SWeaponHit->ubAttackerID < 20)weaphit_struct.ubAttackerID = weaphit_struct.ubAttackerID+ubID_prefix;
client->RPC("sendHIT",(const char*)&weaphit_struct, (int)sizeof(EV_S_WEAPONHIT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveHIT(RPCParameters *rpcParameters)
{
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"recieveHIT" );
EV_S_WEAPONHIT* SWeaponHit = (EV_S_WEAPONHIT*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SWeaponHit->usSoldierID ];
UINT16 usSoldierID;
UINT8 ubAttackerID;
if((SWeaponHit->usSoldierID >= ubID_prefix) && (SWeaponHit->usSoldierID < (ubID_prefix+6))) // within our netbTeam range...
usSoldierID = (SWeaponHit->usSoldierID - ubID_prefix);
else
usSoldierID = SWeaponHit->usSoldierID;
if((SWeaponHit->ubAttackerID >= ubID_prefix) && (SWeaponHit->ubAttackerID < (ubID_prefix+6)))
ubAttackerID = (SWeaponHit->ubAttackerID - ubID_prefix);
else
ubAttackerID = SWeaponHit->ubAttackerID;
WeaponHit( usSoldierID, SWeaponHit->usWeaponIndex, SWeaponHit->sDamage, SWeaponHit->sBreathLoss, SWeaponHit->usDirection, SWeaponHit->sXPos, SWeaponHit->sYPos, SWeaponHit->sZPos, SWeaponHit->sRange, ubAttackerID, SWeaponHit->fHit, SWeaponHit->ubSpecial, SWeaponHit->ubLocation );
if(SWeaponHit->fStopped)
{
INT8 bTeam=pSoldier->bTeam;
INT32 iBullet = bTable[bTeam][SWeaponHit->iBullet].local_id;
RemoveBullet(iBullet);
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"removed bullet" );
}
}
void send_dismiss(UINT8 ubCurrentSoldierID)
{
send_dismiss_struct sDismissMerc;
sDismissMerc.ubProfileID = ubCurrentSoldierID + ubID_prefix;
client->RPC("sendDISMISS",(const char*)&sDismissMerc, (int)sizeof(send_dismiss_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void send_hire( UINT8 iNewIndex, UINT8 ubCurrentSoldier, INT16 iTotalContractLength, BOOLEAN fCopyProfileItemsOver)
{
send_hire_struct sHireMerc;
sHireMerc.ubProfileID=ubCurrentSoldier;
sHireMerc.team=TEAM;
sHireMerc.fCopyProfileItemsOver=fCopyProfileItemsOver;
sHireMerc.bTeam=netbTeam;
SOLDIERTYPE *pSoldier = MercPtrs[ iNewIndex ];
UINT8 sectorEdge = cStartingSectorEdge;
// WANNE - MP: Center
if (sectorEdge == MP_EDGE_CENTER)
sectorEdge = INSERTION_CODE_CENTER;
pSoldier->ubStrategicInsertionCode = sectorEdge;
if(ubCurrentSoldier==64)//slay
{
pSoldier->ubBodyType = REGMALE;
gMercProfiles[ pSoldier->ubProfile ].ubBodyType = REGMALE;
}
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[44], pSoldier->name);
AddCharacterToAnySquad( pSoldier );
//add recruited flag
gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags |= PROFILE_MISC_FLAG_RECRUITED;
OBJECTTYPE Object;
int cnt;
for (cnt=0; cnt<20;cnt++)
{
int item=kit[cnt];
if(item != 0)
{
CreateItem( item, 100, &Object );
AutoPlaceObject( pSoldier, &Object, TRUE );
}
}
client->RPC("sendHIRE",(const char*)&sHireMerc, (int)sizeof(send_hire_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveDISMISS(RPCParameters *rpcParameters)
{
send_dismiss_struct* sDismissMerc = (send_dismiss_struct*)rpcParameters->input;
// Get soldier we should dismiss
SOLDIERTYPE * pSoldier=MercPtrs[ sDismissMerc->ubProfileID ];
TacticalRemoveSoldier( pSoldier->ubID );
}
void recieveHIRE(RPCParameters *rpcParameters)
{
send_hire_struct* sHireMerc = (send_hire_struct*)rpcParameters->input;
SOLDIERTYPE *pSoldier;
UINT8 iNewIndex;
UINT8 ubCount=0;
SOLDIERCREATE_STRUCT MercCreateStruct;
BOOLEAN fReturn = FALSE;
MercCreateStruct.ubProfile = sHireMerc->ubProfileID;
MercCreateStruct.fPlayerMerc = 0;
MercCreateStruct.sSectorX = gsMercArriveSectorX;
MercCreateStruct.sSectorY = gsMercArriveSectorY;
MercCreateStruct.bSectorZ = 0;
MercCreateStruct.bTeam = sHireMerc->bTeam;
MercCreateStruct.fCopyProfileItemsOver = sHireMerc->fCopyProfileItemsOver;
TacticalCreateSoldier( &MercCreateStruct, &iNewIndex ) ;
pSoldier = &Menptr[iNewIndex];
pSoldier->flags.uiStatusFlags |= SOLDIER_PC;
gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags |= PROFILE_MISC_FLAG_RECRUITED;
if(!cSameMercAllowed)
gMercProfiles[ pSoldier->ubProfile ].bMercStatus = MERC_WORKING_ELSEWHERE;
pSoldier->bSide=0; //default coop only
gTacticalStatus.Team[MercCreateStruct.bTeam ].bSide=0;
pSoldier->bVisible = 1;
if(MercCreateStruct.ubProfile==64)//slay
{
pSoldier->ubBodyType = REGMALE;
gMercProfiles[ pSoldier->ubProfile ].ubBodyType = REGMALE;
}
if(cGameType==MP_TYPE_DEATHMATCH)//all vs all only
{
pSoldier->bSide=1;
pSoldier->bVisible = 0;
gTacticalStatus.Team[MercCreateStruct.bTeam ].bSide=1;
}
if(cGameType==MP_TYPE_TEAMDEATMATCH) //allow teams
{
if(sHireMerc->team != TEAM)
{
pSoldier->bSide=1;
pSoldier->bVisible = 0;
gTacticalStatus.Team[MercCreateStruct.bTeam ].bSide=1;
}
}
AddSoldierToSector( iNewIndex );
if(cReportHiredMerc == 1)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[5],MercCreateStruct.bTeam-5,client_names[MercCreateStruct.bTeam-6],pSoldier->name );
}
else
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[6],MercCreateStruct.bTeam-5,client_names[MercCreateStruct.bTeam-6] );
}
}
void send_gui_pos(SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos)
{
gui_pos gnPOS;
gnPOS.usSoldierID = (pSoldier->ubID)+ubID_prefix;
gnPOS.dNewXPos = dNewXPos;
gnPOS.dNewYPos = dNewYPos;
client->RPC("sendguiPOS",(const char*)&gnPOS, (int)sizeof(gui_pos)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveguiPOS(RPCParameters *rpcParameters)
{
gui_pos* gnPOS = (gui_pos*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ gnPOS->usSoldierID ];
INT32 sNewGridNo;
sNewGridNo = GETWORLDINDEXFROMWORLDCOORDS(gnPOS->dNewXPos, gnPOS->dNewYPos );
pSoldier->usStrategicInsertionData=sNewGridNo;
pSoldier->ubStrategicInsertionCode=INSERTION_CODE_GRIDNO;
pSoldier->sInsertionGridNo = pSoldier->usStrategicInsertionData;
pSoldier->EVENT_SetSoldierPosition( gnPOS->dNewXPos, gnPOS->dNewYPos );
}
void send_gui_dir(SOLDIERTYPE *pSoldier, UINT16 usNewDirection)
{
gui_dir gnDIR;
gnDIR.usSoldierID = (pSoldier->ubID)+ubID_prefix;
gnDIR.usNewDirection = usNewDirection;
client->RPC("sendguiDIR",(const char*)&gnDIR, (int)sizeof(gui_dir)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveguiDIR(RPCParameters *rpcParameters)
{
gui_dir* gnDIR = (gui_dir*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ gnDIR->usSoldierID ];
pSoldier->EVENT_SetSoldierDirection( gnDIR->usNewDirection );
}
void send_EndTurn( UINT8 ubNextTeam )
{
if(ubNextTeam==0)
{
// translate next team id for clients
ubNextTeam=netbTeam;
}
turn_struct tStruct;
if(is_server)
Sawarded=false;
tStruct.tsnetbTeam = netbTeam;
tStruct.tsubNextTeam = ubNextTeam;
client->RPC("sendEndTurn",(const char*)&tStruct, (int)sizeof(turn_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveEndTurn(RPCParameters *rpcParameters)
{
turn_struct* tStruct = (turn_struct*)rpcParameters->input;
UINT8 sender_bTeam;
UINT8 ubTeam;
sender_bTeam=tStruct->tsnetbTeam;
ubTeam=tStruct->tsubNextTeam;
if(is_server)
Sawarded=false;
// if the message was recieved from the server..
if(is_server || sender_bTeam==6)
{
// if not the server and we're not in combat...
if (!is_server && !(gTacticalStatus.uiFlags & INCOMBAT))
{
EnterCombatMode(0);
}
if(ubTeam==netbTeam)ubTeam=0;
{
if(!is_server && is_client)
EndTurnEvents();
}
if(!is_server && is_client)
EndTurn( ubTeam );
requested=false ;//request for realtime made or not
BeginTeamTurn( ubTeam );
}
}
UINT8 numenemyLAN( UINT8 ubSectorX, UINT8 ubSectorY )
{
SOLDIERTYPE *pSoldier;
UINT8 cnt ; //first posible lan player
UINT8 ubNumEnemies = 0;
for ( cnt=120 ; cnt <= 155; cnt++ )
{
pSoldier = MercPtrs[cnt];
if ( pSoldier->bActive && pSoldier->bInSector && pSoldier->stats.bLife > 0 )
{
if ( !pSoldier->aiData.bNeutral && (pSoldier->bSide != 0 ) )
{
ubNumEnemies++;
}
}
}
return ubNumEnemies;
}
void send_AI( SOLDIERCREATE_STRUCT *pCreateStruct, UINT8 *pubID )
{
AI_STRUCT send_inv;
send_inv.standard_data = *pCreateStruct;
for(int x=0;x<55;x++)
{
send_inv.slot[x] = pCreateStruct->Inv[x];
}
client->RPC("sendAI",(const char*)&send_inv, (int)sizeof(AI_STRUCT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveAI (RPCParameters *rpcParameters)
{
UINT8 iNewIndex;
SOLDIERTYPE *pSoldier;
AI_STRUCT* send_inv = (AI_STRUCT*)rpcParameters->input;
SOLDIERCREATE_STRUCT new_standard_data; //as originally my soldiercreate_struct would get its INV mangled through the RPC, so i send it packaged with it, then create a new struct and add in the bits that are still ok from the original struct, along with the packaged INV components...
new_standard_data.bAgility = send_inv->standard_data.bAgility;
new_standard_data.bAIMorale = send_inv->standard_data.bAIMorale;
new_standard_data.bAttitude = send_inv->standard_data.bAttitude;
new_standard_data.bBodyType = send_inv->standard_data.bBodyType;
new_standard_data.bDexterity = send_inv->standard_data.bDexterity;
new_standard_data.ubDirection = send_inv->standard_data.ubDirection;
new_standard_data.bExpLevel = send_inv->standard_data.bExpLevel;
new_standard_data.bExplosive = send_inv->standard_data.bExplosive;
new_standard_data.bLeadership = send_inv->standard_data.bLeadership;
new_standard_data.bLife = send_inv->standard_data.bLife;
new_standard_data.bLifeMax = send_inv->standard_data.bLifeMax;
new_standard_data.bMarksmanship = send_inv->standard_data.bMarksmanship;
new_standard_data.bMechanical = send_inv->standard_data.bMechanical;
new_standard_data.bMedical = send_inv->standard_data.bMedical;
new_standard_data.bMorale = send_inv->standard_data.bMorale;
new_standard_data.bOrders = send_inv->standard_data.bOrders;
new_standard_data.bPatrolCnt = send_inv->standard_data.bPatrolCnt;
new_standard_data.bSectorZ = send_inv->standard_data.bSectorZ;
new_standard_data.bStrength = send_inv->standard_data.bStrength;
new_standard_data.bTeam = send_inv->standard_data.bTeam;
new_standard_data.bUseGivenVehicleID = send_inv->standard_data.bUseGivenVehicleID;
new_standard_data.bWisdom = send_inv->standard_data.bWisdom;
new_standard_data.endOfPOD = send_inv->standard_data.endOfPOD;
new_standard_data.fCopyProfileItemsOver = send_inv->standard_data.fCopyProfileItemsOver;
new_standard_data.fHasKeys = send_inv->standard_data.fHasKeys;
new_standard_data.fKillSlotIfOwnerDies = send_inv->standard_data.fKillSlotIfOwnerDies;
new_standard_data.fOnRoof = send_inv->standard_data.fOnRoof;
new_standard_data.fPlayerMerc = send_inv->standard_data.fPlayerMerc;
new_standard_data.fPlayerPlan = send_inv->standard_data.fPlayerPlan;
new_standard_data.fStatic = send_inv->standard_data.fStatic;
new_standard_data.fUseExistingSoldier = send_inv->standard_data.fUseExistingSoldier;
new_standard_data.fUseGivenVehicle = send_inv->standard_data.fUseGivenVehicle;
new_standard_data.fVisible = send_inv->standard_data.fVisible;
memcpy( new_standard_data.HeadPal , send_inv->standard_data.HeadPal, sizeof( PaletteRepID ));
memcpy( new_standard_data.MiscPal , send_inv->standard_data.MiscPal, sizeof( PaletteRepID ));
memcpy( new_standard_data.name , send_inv->standard_data.name, sizeof( CHAR16 ) * 10 );
memcpy( new_standard_data.PantsPal , send_inv->standard_data.PantsPal, sizeof( PaletteRepID ));
new_standard_data.pExistingSoldier = send_inv->standard_data.pExistingSoldier;
new_standard_data.sInsertionGridNo = send_inv->standard_data.sInsertionGridNo;
memcpy( new_standard_data.SkinPal , send_inv->standard_data.SkinPal, sizeof( PaletteRepID ));
memcpy( new_standard_data.sPatrolGrid, send_inv->standard_data.sPatrolGrid, sizeof( INT32 ) * MAXPATROLGRIDS );//dnl ch27 230909
new_standard_data.sSectorX = send_inv->standard_data.sSectorX;
new_standard_data.sSectorY = send_inv->standard_data.sSectorY;
new_standard_data.ubCivilianGroup = send_inv->standard_data.ubCivilianGroup;
new_standard_data.ubProfile = send_inv->standard_data.ubProfile;
new_standard_data.ubScheduleID = send_inv->standard_data.ubScheduleID;
new_standard_data.ubSoldierClass = send_inv->standard_data.ubSoldierClass;
memcpy( new_standard_data.VestPal , send_inv->standard_data.VestPal, sizeof( PaletteRepID ));
for(int x=0;x<55;x++)
{
if(send_inv->slot[x].usItem != 0)
CreateItems( send_inv->slot[x].usItem, 100, send_inv->slot[x].ubNumberOfObjects, &new_standard_data.Inv[x] );
}
new_standard_data.fPlayerPlan=1;
TacticalCreateSoldier( &new_standard_data, &iNewIndex );
pSoldier = &Menptr[iNewIndex];
pSoldier->flags.uiStatusFlags |= SOLDIER_PC;
AddSoldierToSector( iNewIndex );
}
void send_ready ( void )
{
ready_struct info;
info.client_num = CLIENT_NUM;
if(readystage==0)
{
info.ready_stage = 0;
if(status==0)
{
info.status = 1;
status=1;
numready = numready+1;
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[7],numready,cMaxClients );
// OJW - 20081204
client_ready[info.client_num-1]=1;
fDrawCharacterList = true; // set the character list to be redrawn
}
else
{
info.status = 0;
status=0;
numready = numready-1;
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[8],numready,cMaxClients );
// OJW - 20081204
client_ready[info.client_num-1]=0;
fDrawCharacterList = true; // set the character list to be redrawn
}
}
if(is_server && numready == cMaxClients) //all ready. and server tells all to load...
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[9] );
goahead=1;
readystage=1;
info.ready_stage = 1;
info.status = 1;
}
client->RPC("sendREADY",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
if(is_server && numready == cMaxClients)
{
status=0;//reset
numready=0;
start_battle();//server loads
}
}
void recieveREADY (RPCParameters *rpcParameters)
{
ready_struct* info = (ready_struct*)rpcParameters->input;
if(info->ready_stage==1)//recived ok for go ahead from server for level load
{
numready++;
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,cMaxClients );
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[9] );
status=0;//reset
numready=0;
goahead=1;
start_battle();
}
else if (info->ready_stage != 36) // recieved status update from client
{
if (info->status==1)
{
numready = numready+1;
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[10], info->client_num,client_names[info->client_num-1],numready,cMaxClients );
// OJW - 20081204
client_ready[info->client_num-1]=1;
fDrawCharacterList = true; // set the character list to be redrawn
}
else
{
numready = numready-1;
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[11], info->client_num,client_names[info->client_num-1],numready,cMaxClients );
// OJW - 20081204
client_ready[info->client_num-1]=0;
fDrawCharacterList = true; // set the character list to be redrawn
}
if(is_server && numready == cMaxClients) //all ready. and server tells all to load...and loads himself
{
goahead=1;
readystage=1;
send_ready();
start_battle();
}
}
else if(info->ready_stage==36)//server allows laptop access
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[36] );
allowlaptop=1;
if (gRandomMercs)
HireRandomMercs();
// server is starting the game, adjust the games max players
// this will allow the game to be started, rather than saying 2/4 players are connected
// where there are only two players connected
int iPlayersConnected = 0;
for (int i=0; i< 4; i++)
if (client_names[i] != NULL && strcmp(client_names[i],"") != 0)
iPlayersConnected++;
cMaxClients = iPlayersConnected;
}
}
void send_loaded (void)
{
ready_struct info;
info.client_num = CLIENT_NUM;
info.ready_stage = 1;//done loading level
info.status=1;
numready++;
if(numready==cMaxClients && is_server)
{
lockui(1);//unlock ui
readystage=0;
numready=0;
info.ready_stage = 2;//done placing mercs
info.status=1;
}
client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void send_donegui ( UINT8 ubResult )
{
if(ubResult==1 && status==0)
return;//avoid double remove callback response from final message box removal
ready_struct info;
info.client_num = CLIENT_NUM;
if(ubResult==1)
DialogRemoved(1);//cleanup msgbox after not ready
if(status==0)//now ready
{
status=1;
numready++;
info.ready_stage = 3;//done placing mercs
info.status=1;
SGPRect CenterRect = { 100 + xResOffset, 100, SCREEN_WIDTH - 100 - xResOffset, 300 };
DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[12], guiCurrentScreen, MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT, send_donegui, &CenterRect );
if(numready==cMaxClients && is_server)//all done
{
numready=0;
status=0;
info.ready_stage = 4;//done placing mercs
info.status=1;
gMsgBox.bHandled = MSG_BOX_RETURN_OK;
KillTacticalPlacementGUI(); //send and kill
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]);
}
}
else if(status==1)//was ready
{
status=0;
numready--;
info.ready_stage = 3;//not done placing mercs
info.status=0;
}
client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveGUI (RPCParameters *rpcParameters)
{
ready_struct* info = (ready_struct*)rpcParameters->input;
if(info->ready_stage==1 && info->status==1)
{
numready++;
if(numready==cMaxClients && is_server)
{
lockui(1);//unlock ui
readystage=0;
numready=0;
ready_struct info;
info.client_num = CLIENT_NUM;
info.ready_stage = 2;//done placing mercs
info.status=1;
client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
if(info->ready_stage==2 && info->status ==1)
{
lockui(1);//unlock ui
readystage=0;
numready=0;
}
if(info->ready_stage==3 && info->status==1)//recieved client done placement
{
numready++;
if(numready==cMaxClients && is_server)//all done
{
numready=0;
ready_struct info;
info.client_num = CLIENT_NUM;
info.ready_stage = 4;//all done placing mercs, kill all
info.status=1;
gMsgBox.bHandled = MSG_BOX_RETURN_OK;
status=0;
KillTacticalPlacementGUI();
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]);
client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
if(info->ready_stage==3 && info->status==0)//recieved client retracted place ready...
{
numready--;
}
if(info->ready_stage==4 && info->status==1)
{
gMsgBox.bHandled = MSG_BOX_RETURN_OK;
KillTacticalPlacementGUI();
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]);
numready=0;
status=0;
}
}
void allowlaptop_callback ( UINT8 ubResult )
{
if(ubResult==2)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[36] );
allowlaptop=1;
ready_struct info;
info.client_num = CLIENT_NUM;
info.ready_stage=36;
info.status=1;
if (gRandomMercs)
HireRandomMercs();
// server client is starting the game, adjust the games max players
// this will allow the game to be started, rather than saying 2/4 players are connected
// where there are only two players connected
int iPlayersConnected = 0;
for (int i=0; i< 4; i++)
if (client_names[i] != NULL && strcmp(client_names[i],"") != 0)
iPlayersConnected++;
cMaxClients = iPlayersConnected;
client->RPC("sendREADY",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void StartBattleChatBoxClosedCallback(void)
{
// The chat box has been closed, now we can start
SetCurrentWorldSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ );
is_game_started = true;
}
void start_battle ( void )
{
if(!is_client)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[14] );
}
else if(!allowlaptop && is_server)
{
bool numPlayersValid = TRUE;
bool clientsFinishedDownloading = TRUE;
bool teamsValid = TRUE;
// check that another player is actually connected
int iPlayersConnected = 0;
for (int i=0; i< 4; i++)
if (client_names[i] != NULL && strcmp(client_names[i],"") != 0)
iPlayersConnected++;
if (iPlayersConnected <= 1)
{
numPlayersValid = FALSE;
// notify the server that at least one other player must be connected
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[51] );
}
else if (are_clients_downloading())
{
clientsFinishedDownloading = FALSE;
// notify the server that some of the clients are still downloading
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[63] );
}
// WANNE - MP: If choosen team deathmatch, there must be at least 2 different teams
else if (cGameType == MP_TYPE_TEAMDEATMATCH)
{
bool areTeamsValid = FALSE;
int clientTeam = client_teams[0];
for (int i = 1; i < iPlayersConnected; i++)
{
if (clientTeam != client_teams[i])
{
areTeamsValid = TRUE;
break;
}
}
if (!areTeamsValid)
{
teamsValid = FALSE;
// notify the server that the teams are not different for the choosen team-deathmatch
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[68] );
}
}
// Go to "ready" state!
if (numPlayersValid && clientsFinishedDownloading && teamsValid)
{
SGPRect CenterRect = { 100 + xResOffset, 100, SCREEN_WIDTH - 100 - xResOffset, 300 };
DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[35], guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowlaptop_callback, &CenterRect );
}
}
else if(allowlaptop)
{
if ( NumberOfMercsOnPlayerTeam() ==0)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[15] );
}
else if(goahead==1)
{
goahead=0; // this ensures that we dont reload the sector again the next time this function is called
if (is_game_started)
return; // 20090317 - OJW - there is a bug or out of sync condition that rarely somestimes causes a client to reach here
// twice, and reload the sector, which causes an assertion failure on line 1782 of strategicmap.cpp in SetCurrentWorldSector()
// therefore i'm adding this to make sure if they client loaded the map once, it wont do it again
status=0;//reset
numready=0;
// Find first active soldier. This is needed, because if the soldier is dismissed it does not belong to any group
SOLDIERTYPE *pSoldier;
for (int i = 0; i <= 19; i++)
{
if (MercPtrs[ i ]->bActive)
{
pSoldier = MercPtrs[i];
break;
}
}
//SOLDIERTYPE *pSoldier = MercPtrs[ 0 ];
UINT8 ubGroupID = pSoldier->ubGroupID;
GROUP *pGroup;
pGroup = GetGroup( ubGroupID );
gpBattleGroup = pGroup;
gubPBSectorX = gpBattleGroup->ubSectorX;
gubPBSectorY = gpBattleGroup->ubSectorY;
gubPBSectorZ = gpBattleGroup->ubSectorZ;
gfEnterTacticalPlacementGUI = 1;
// OJW - 20090205
iTeamsWiped = 0; // reset the number of wiped teams to Zero
UINT32 i;
for(i=0; i<4;i++)
{
CHAR16 name[30];
int nm = mbstowcs( name, client_names[i], sizeof (char)*30 );
//copy in client specified name for the player turn bar :)
if(nm)
{
// OJW - 20090318 - fixed name copying bug with multiple games
CHAR16 full[255];
memset(full,0,sizeof(CHAR16)*255);
swprintf(full, MPClientMessage[57],i+1,name);
memcpy( TeamTurnString[ (i+6) ] , full, sizeof( CHAR16) * 255 );
}
}
// this closes the chat window if its open and the game is starting
// if this is open then the client will crash when the screen returns
if (guiCurrentScreen == MP_CHAT_SCREEN)
{
gChatBox.bHandled = MSG_BOX_RETURN_NO;
bClosingChatBoxToStartGame = true;
iCCStartGameTime = guiBaseJA2NoPauseClock+1000;
}
else
{
SetCurrentWorldSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ );
is_game_started = true;
}
}
else
{
send_ready();
}
}
else if(!allowlaptop && is_client && !is_server)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[16] );
}
}
void DropOffItemsInSector( UINT8 ubOrderNum )
{
BOOLEAN fSectorLoaded = FALSE;
OBJECTTYPE Object;
UINT32 uiCount = 0;
OBJECTTYPE *pObject=NULL;
UINT16 usNumberOfItems=0, usItem;
UINT8 ubItemsDelivered, ubTempNumItems;
UINT32 i;
// determine if the sector is loaded
if( ( gWorldSectorX == gsMercArriveSectorX ) && ( gWorldSectorY == gsMercArriveSectorY ) && ( gbWorldSectorZ == 0 ) )
fSectorLoaded = TRUE;
else
fSectorLoaded = FALSE;
SetSectorFlag( gsMercArriveSectorX, gsMercArriveSectorY, 0, SF_ALREADY_VISITED);//allows update of item count
for(i=0; i<gpNewBobbyrShipments[ ubOrderNum ].ubNumberPurchases; i++)
{
// Count how many items were purchased
usNumberOfItems += gpNewBobbyrShipments[ ubOrderNum ].BobbyRayPurchase[i].ubNumberPurchased;
}
//if we are NOT currently in the right sector
if( !fSectorLoaded )
{
//build an array of objects to be added
pObject = new OBJECTTYPE[ usNumberOfItems ];
if( pObject == NULL )
return;
}
uiCount = 0;
//loop through the number of purchases
for( i=0; i< gpNewBobbyrShipments->ubNumberPurchases; i++)
{
ubItemsDelivered = gpNewBobbyrShipments[ ubOrderNum ].BobbyRayPurchase[i].ubNumberPurchased;
usItem = gpNewBobbyrShipments[ ubOrderNum ].BobbyRayPurchase[i].usItemIndex;
while ( ubItemsDelivered )
{
// treat 0s as 1s :-)
ubTempNumItems = __min( ubItemsDelivered, __max( 1, Item[ usItem ].ubPerPocket ) );
CreateItems( usItem, gpNewBobbyrShipments[ ubOrderNum ].BobbyRayPurchase[i].bItemQuality, ubTempNumItems, &Object );
// stack as many as possible
if( fSectorLoaded )
{
AddItemToPool( 12880, &Object, 1, 0, WOLRD_ITEM_FIND_SWEETSPOT_FROM_GRIDNO | WORLD_ITEM_REACHABLE, 0 );
}
else
{
pObject[ uiCount ] = Object;
uiCount++;
}
ubItemsDelivered -= ubTempNumItems;
}
}
//if the sector WASNT loaded
if( !fSectorLoaded )
{
//add all the items from the array that was built above
//The item are to be added to the Top part of Drassen, grid loc's 10112, 9950
if( !AddItemsToUnLoadedSector( gsMercArriveSectorX, gsMercArriveSectorY, 0, 12880, uiCount, pObject, 0, WOLRD_ITEM_FIND_SWEETSPOT_FROM_GRIDNO | WORLD_ITEM_REACHABLE, 0, 1, FALSE ) )
{
//error
Assert( 0 );
}
delete[] pObject ;
pObject = NULL;
}
//mark that the shipment has arrived
gpNewBobbyrShipments[ ubOrderNum ].fActive = FALSE;
}
void send_stop (EV_S_STOP_MERC *SStopMerc) // used to stop a merc when he spots new enemies...
{
EV_S_STOP_MERC stop_struct;
if(SStopMerc->usSoldierID < 120)
{
if(SStopMerc->usSoldierID < 20)
{
stop_struct.usSoldierID = (SStopMerc->usSoldierID)+ubID_prefix;
}
else
stop_struct.usSoldierID = SStopMerc->usSoldierID;
stop_struct.sGridNo=SStopMerc->sGridNo;
stop_struct.ubDirection=SStopMerc->ubDirection;
stop_struct.fset=SStopMerc->fset;
if(stop_struct.fset==FALSE)
{
return;
}
stop_struct.sXPos=SStopMerc->sXPos;
stop_struct.sYPos=SStopMerc->sYPos;
client->RPC("sendSTOP",(const char*)&stop_struct, (int)sizeof(EV_S_STOP_MERC)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void recieveSTOP (RPCParameters *rpcParameters)
{
EV_S_STOP_MERC* SStopMerc =(EV_S_STOP_MERC*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SStopMerc->usSoldierID ];
pSoldier->EVENT_InternalSetSoldierPosition( SStopMerc->sXPos, SStopMerc->sYPos,FALSE, FALSE, FALSE );
pSoldier->EVENT_SetSoldierDirection( SStopMerc->ubDirection );
pSoldier->AdjustNoAPToFinishMove( SStopMerc->fset );
pSoldier->flags.bTurningFromPronePosition = FALSE;
}
void send_interrupt (SOLDIERTYPE *pSoldier)
{
INT_STRUCT INT;
INT.ubID = pSoldier->ubID;
INT.bTeam = pSoldier->bTeam;
memcpy(INT.gubOutOfTurnOrder, gubOutOfTurnOrder, sizeof(UINT8) * MAXMERCS);
INT.gubOutOfTurnPersons = gubOutOfTurnPersons;
INT.Interrupted=gusSelectedSoldier+ubID_prefix;
if(INT.bTeam==0)
{
INT.bTeam=netbTeam;
INT.ubID=INT.ubID+ubID_prefix;
}
for(int i=0; i <= INT.gubOutOfTurnPersons; i++)
{
if(INT.gubOutOfTurnOrder[i] < 20)
{
INT.gubOutOfTurnOrder[i]=INT.gubOutOfTurnOrder[i]+ubID_prefix;
}
}
if(INT.bTeam !=netbTeam)
gTacticalStatus.ubCurrentTeam=INT.bTeam;
client->RPC("sendINTERRUPT",(const char*)&INT, (int)sizeof(INT_STRUCT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
// WANNE - MP: Here we have to add AddTopMessage() on the clients
void recieveINTERRUPT (RPCParameters *rpcParameters)
{
INT_STRUCT* INT = (INT_STRUCT*)rpcParameters->input;
SOLDIERTYPE* pOpponent = MercPtrs[ INT->Interrupted];
if(INT->bTeam==netbTeam)//for us or AI
{
INT->bTeam=0;
INT->ubID=INT->ubID - ubID_prefix;
for(int i=0; i <= INT->gubOutOfTurnPersons; i++)
{
if((INT->gubOutOfTurnOrder[i] >= ubID_prefix) && (INT->gubOutOfTurnOrder[i] < (ubID_prefix+6)))
{
INT->gubOutOfTurnOrder[i]=INT->gubOutOfTurnOrder[i]-ubID_prefix;
}
}
memcpy(gubOutOfTurnOrder,INT->gubOutOfTurnOrder, sizeof(UINT8) * MAXMERCS);
gubOutOfTurnPersons = INT->gubOutOfTurnPersons;
// AI has interrupted
if (INT->bTeam == 1)
{
//AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, TeamTurnString[ INT->bTeam ] );
}
else
{
AddTopMessage( PLAYER_INTERRUPT_MESSAGE, TeamTurnString[ INT->bTeam ] );
}
}
// WANNE - MP: This seems to cause the HANG on AI interrupt where we have to press ALT + E on the server!
if( INT->bTeam != 0)
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[17] );
//stop moving merc who was interrupted and init UI bar
SOLDIERTYPE* pMerc = MercPtrs[ INT->ubID ];
pMerc->HaultSoldierFromSighting(TRUE);
FreezeInterfaceForEnemyTurn();
InitEnemyUIBar( 0, 0 );
fInterfacePanelDirty = DIRTYLEVEL2;
gTacticalStatus.fInterruptOccurred = TRUE;
}
else
{
//it for us ! :)
if(INT->gubOutOfTurnPersons==0)//indicates finished interrupt maybe can just call end interrupt
{
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"old int finish" );
}
else //start our interrupt turn
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[37] );
SOLDIERTYPE* pSoldier = MercPtrs[ INT->ubID ];
ManSeesMan(pSoldier,pOpponent,pOpponent->sGridNo,pOpponent->pathing.bLevel,2,1);
StartInterrupt();
}
}
}
void intAI (SOLDIERTYPE *pSoldier )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[17] );
AddTopMessage( COMPUTER_INTERRUPT_MESSAGE, TeamTurnString[ pSoldier->bTeam ] );
gTacticalStatus.fInterruptOccurred = TRUE;
}
void end_interrupt ( BOOLEAN fMarkInterruptOccurred )
{
SOLDIERTYPE * pSoldier = MercPtrs[(gubOutOfTurnOrder[gubOutOfTurnPersons])];
INT_STRUCT INT;
INT.ubID = pSoldier->ubID;
INT.bTeam = pSoldier->bTeam;
memcpy(INT.gubOutOfTurnOrder, gubOutOfTurnOrder, sizeof(UINT8) * MAXMERCS);
INT.gubOutOfTurnPersons = gubOutOfTurnPersons;
INT.fMarkInterruptOccurred=fMarkInterruptOccurred;
if(is_server)Sawarded=false;
if(INT.bTeam==0)
{
INT.bTeam=netbTeam;
INT.ubID=INT.ubID+ubID_prefix;
}
for(int i=0; i <= INT.gubOutOfTurnPersons; i++)
{
if(INT.gubOutOfTurnOrder[i] < 20)
{
INT.gubOutOfTurnOrder[i]=INT.gubOutOfTurnOrder[i]+ubID_prefix;
}
}
client->RPC("endINTERRUPT",(const char*)&INT, (int)sizeof(INT_STRUCT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void resume_turn(RPCParameters *rpcParameters)
{
INT_STRUCT* INT = (INT_STRUCT*)rpcParameters->input;
if(is_server)
Sawarded=false;
if(INT->bTeam==netbTeam || (INT->bTeam==1 && is_server))//may need working
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[18] );
for(int i=0; i <= INT->gubOutOfTurnPersons; i++)
{
if((INT->gubOutOfTurnOrder[i] >= ubID_prefix) && (INT->gubOutOfTurnOrder[i] < (ubID_prefix+6)))
{
INT->gubOutOfTurnOrder[i]=INT->gubOutOfTurnOrder[i]-ubID_prefix;
}
}
memcpy(gubOutOfTurnOrder,INT->gubOutOfTurnOrder, sizeof(UINT8) * MAXMERCS);
gubOutOfTurnPersons = INT->gubOutOfTurnPersons;
EndInterrupt( INT->fMarkInterruptOccurred );
}
// WANNE - MP: This happens, when client 1 (=server) has done its interrupt and now it is enemies turn!
else if(INT->bTeam==1)
{
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"im not server but ai just got back its turn after being interrupted..." );
AddTopMessage( COMPUTER_TURN_MESSAGE, TeamTurnString[ 1 ] );
}
}
void grid_display ( void ) //print mouse coordinates, helpfull for crate placement.
{
INT16 sGridX, sGridY;
UINT32 usMapPos;
GetMouseXY( &sGridX, &sGridY );
usMapPos = MAPROWCOLTOPOS( sGridY, sGridX );
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[19] );
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[20], sGridX, sGridY );
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[21], usMapPos );
}
void overide_callback( UINT8 ubResult )
{
if(is_server)
{
if(ubResult==1)
{
allowlaptop=true;
}
if(ubResult==2)//overide stop #1 (awaiting client ready for launch/load)
{
goahead=0;
status=0;//reset
numready=0;
SOLDIERTYPE *pSoldier = MercPtrs[ 0 ];
UINT8 ubGroupID = pSoldier->ubGroupID;
GROUP *pGroup;
pGroup = GetGroup( ubGroupID );
gpBattleGroup = pGroup;
gubPBSectorX = gpBattleGroup->ubSectorX;
gubPBSectorY = gpBattleGroup->ubSectorY;
gubPBSectorZ = gpBattleGroup->ubSectorZ;
gfEnterTacticalPlacementGUI = 1;
goahead=1;
readystage=1;
ready_struct info; //send
info.client_num = CLIENT_NUM;
info.ready_stage = 1;
info.status = 1;
client->RPC("sendREADY",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
status=0;//reset
numready=0;
SetCurrentWorldSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ ); //load
}
if(ubResult==3)
{
lockui(1);//unlock ui paused while wainting for loaders
ready_struct info;
info.client_num = CLIENT_NUM;
readystage=0;
numready=0;
info.ready_stage = 2;//done placing mercs
info.status=1;
client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
if(ubResult==4) //overide waiting on merc placement
{
numready=0;
ready_struct info; //send
info.client_num = CLIENT_NUM;
info.ready_stage = 4;//all done placing mercs, kill all
info.status=1;
gMsgBox.bHandled = MSG_BOX_RETURN_OK;
status=0;
KillTacticalPlacementGUI(); //kill
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[13]);
client->RPC("sendGUI",(const char*)&info, (int)sizeof(ready_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
goahead = 0;
numready = 0;
readystage = 0;
status = 0;
}
}
void requestFILE_TRANSFER_SETTINGS(void)
{
client->RPC("requestFILE_TRANSFER_SETTINGS","", 0, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
// OJW - 20090430
// give clients a choice to accept the security risk
void allowDownloadCallback( UINT8 ubResult )
{
if (ubResult == 2)
{
// yes
// begin downloading of files
setID = fltClient.SetupReceive(&transferCBClient, false, serverAddr);
char buffer[3];
sprintf(buffer, "%i", setID);
client->RPC("receiveSETID", (const char*) buffer, (int)sizeof(char*), HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
else
{
// no
// gracefully disconnect to the main menu
client_disconnect();
guiPendingScreen = MP_JOIN_SCREEN;
}
}
// THIS METHOD IS CALLED FROM THE SERVER WHENEVER A NEW CLIENT CONNECTS
void requestSETID(RPCParameters *rpcParameters)
{
// WANNE: FILE TRANSFER
if (!is_server)
{
if (is_connected) // added this here for version disconnections
{
// We did not recieved the files, get them!
if (!fClientReceivedAllFiles)
{
serverAddr = rpcParameters->sender;
SGPRect CenteringRect= {0 + xResOffset, 0, SCREEN_WIDTH - xResOffset, SCREEN_HEIGHT };
DoMessageBox( MSG_BOX_BASIC_STYLE , MPClientMessage[64] , guiCurrentScreen, MSG_BOX_FLAG_YESNO | MSG_BOX_FLAG_USE_CENTERING_RECT, allowDownloadCallback, &CenteringRect );
}
}
}
}
void requestSETTINGS(void)
{
client_info cl_name;
strcpy(cl_name.client_name , cClientName);
cl_name.team = TEAM;
cl_name.cl_edge = cStartingSectorEdge;
// OJW - 20090507
// send client version to server
strcpy(cl_name.client_version,MPVERSION);
client->RPC("requestSETTINGS",(const char*)&cl_name, (int)sizeof(client_info)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
// OJW: FILE TRANSFER: Clients get notified of other clients transfer progress
void recieveDOWNLOADSTATUS(RPCParameters *rpcParameters)
{
progress_struct* prog = (progress_struct*)rpcParameters->input;
int i = prog->client_num - 1;
if (client_downloading[i] != prog->downloading)
{
if (prog->downloading == 0)
ScreenMsg( FONT_RED, MSG_MPSYSTEM, (is_server? MPServerMessage[11] : MPClientMessage[61]), client_names[i]); // message client has recieved all files
else
ScreenMsg( FONT_RED, MSG_MPSYSTEM, (is_server? MPServerMessage[12] : MPClientMessage[62]), client_names[i]);// send message client has started downloading files
}
client_downloading[i] = prog->downloading;
client_progress[i] = prog->progress;
fDrawCharacterList = true;
}
// WANNE: FILE TRANSFER: Get executable Directory from Server. This is used to get corret file location on client side
void recieveFILE_TRANSFER_SETTINGS (RPCParameters *rpcParameters)
{
if (!is_server && recieved_transfer_settings == 0)
{
filetransfersettings_struct* fts = (filetransfersettings_struct*)rpcParameters->input;
gCurrentTransferBytes = 0;
gTotalTransferBytes = fts->totalTransferBytes;
// Now get directory
strcpy( server_fileTransferDirectoryPath, fts->fileTransferDirectory );
vfs::Path profileRoot = vfs::Path(gzFileTransferDirectory) + vfs::Path(server_fileTransferDirectoryPath);
/////////////////////////////////////////////////////////////////////
SGP_TRYCATCH_RETHROW( ja2::mp::InitializeMultiplayerProfile(profileRoot), L"" );
/////////////////////////////////////////////////////////////////////
recieved_transfer_settings = 1;
serverSyncClientsDirectory = fts->syncClientsDirectory;
// We sync our MP game dir from the server
if (fts->syncClientsDirectory == 1)
{
// profile is setup, nothing to do here
}
}
}
void recieveSETTINGS (RPCParameters *rpcParameters) //recive settings from server
{
int startingEdge = MP_EDGE_NORTH;
settings_struct* cl_lan = (settings_struct*)rpcParameters->input;
char szDefault[30];
sprintf(szDefault, "%s",cl_lan->client_name);
// OJW - 20081204
// get complete client data from the server
memcpy( client_edges, cl_lan->client_edges , sizeof(int) * 5);
memcpy( client_teams, cl_lan->client_teams , sizeof(int) * 4);
if(!recieved_settings && strcmp(cl_lan->client_name, cClientName)==0)
{
// This settings packet contains information and settings specifically for us
recieved_settings=1;
// save the settings so if we need to re-apply them after a reinitialisation ( after file transfer ) we can
memcpy ( &gMPServerSettings , cl_lan , sizeof(settings_struct) );
CLIENT_NUM=cl_lan->client_num;//assign client number from server
netbTeam = (CLIENT_NUM)+5;
ubID_prefix = gTacticalStatus.Team[ netbTeam ].bFirstID;//over here now
memcpy( client_names , cl_lan->client_names, sizeof( char ) * 4 * 30 );
strcpy(client_names[cl_lan->client_num-1],szDefault);
// OJW - 20081204
strcpy(cServerName,cl_lan->server_name);
gRandomMercs = cl_lan->randomMercs;
gRandomStartingEdge = cl_lan->randomStartingEdge;
cStartingSectorEdge = cl_lan->startingSectorEdge;
if(gRandomMercs)
{
// copy the random merc list locally
memcpy(random_mercs,cl_lan->random_mercs,sizeof(int) * 7);
}
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[2] );
cMaxClients=cl_lan->maxClients;
memcpy( cKitBag, cl_lan->kitBag, sizeof(char)*100);
int cnt;
int numnum = 0;
int kitnum = 0;
char tempstring[4];
memset( &kit, 0, sizeof( int )*20 );
if (strcmp(cKitBag, "") != 0)
{
for(cnt=0; cnt < 100; cnt++)
{
char tempc = cKitBag[cnt];
if( _strnicmp(&tempc, "[",1) == 0)
{
continue;
}
else if( _strnicmp(&tempc, ",",1) == 0)
{
numnum=0;
kit[kitnum]=atoi(tempstring);
memset( &tempstring, 0, sizeof( char )*4 );
kitnum++;
continue;
}
else if( _strnicmp(&tempc, "]",1) == 0)
{
kit[kitnum]=atoi(tempstring);
break;
}
else
{
strncpy(&tempstring[numnum],&tempc,1);
numnum++;
}
}
}
cDamageMultiplier=cl_lan->damageMultiplier;
cSameMercAllowed=cl_lan->sameMercAllowed;
gsMercArriveSectorX=cl_lan->gsMercArriveSectorX;
gsMercArriveSectorY=cl_lan->gsMercArriveSectorY;
// WANNE - BMP: We have to initialize the map size here!!
InitializeWorldSize(gsMercArriveSectorX, gsMercArriveSectorY, 0);
cGameType = cl_lan->gameType;
cDisableMorale=cl_lan->disableMorale;
ChangeSelectedMapSector( gsMercArriveSectorX, gsMercArriveSectorY, 0 );
CHAR16 str[128];
GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, str, TRUE );
// WANNE - MP: If time set to 0, then no time turns
INT32 secs_per_tick;
if (cl_lan->secondsPerTick == 0)
{
gGameOptions.fTurnTimeLimit=FALSE;
secs_per_tick=1;
}
else
{
gGameOptions.fTurnTimeLimit=cl_lan->sofTurnTimeLimit;
secs_per_tick=cl_lan->secondsPerTick;
}
PLAYER_TEAM_TIMER_SEC_PER_TICKS=secs_per_tick;
INT32 clstarting_balance=cl_lan->startingCash;//set starting balance
if(LaptopSaveInfo.iCurrentBalance<clstarting_balance)
{
AddTransactionToPlayersBook( ANONYMOUS_DEPOSIT, 0, GetWorldTotalMin(), clstarting_balance-LaptopSaveInfo.iCurrentBalance );
}
else
{
AddTransactionToPlayersBook( TRANSACTION_FEE, 0, GetWorldTotalMin(), clstarting_balance-LaptopSaveInfo.iCurrentBalance );
}
gGameOptions.fGunNut=cl_lan->sofGunNut;
gGameOptions.ubGameStyle=cl_lan->soubGameStyle;
gGameOptions.ubDifficultyLevel=cl_lan->soubDifficultyLevel;
gGameOptions.fNewTraitSystem = cl_lan->soubSkillTraits;
gGameOptions.fIronManMode=cl_lan->sofIronManMode;
gGameOptions.ubBobbyRay=cl_lan->soubBobbyRay;
// Set Bobby Ray "Under Construction"?
if(!cl_lan->disableBobbyRay)
{
SetBookMark( BOBBYR_BOOKMARK );
LaptopSaveInfo.fBobbyRSiteCanBeAccessed = TRUE;
}
// Enable "AIM" and "MERC" only if random merc is false!
if (!cl_lan->randomMercs)
{
SetBookMark( AIM_BOOKMARK );
SetBookMark( MERC_BOOKMARK );
}
if(!cl_lan->disableMercEquipment)
cAllowMercEquipment = 1;
cMaxMercs = cl_lan->maxMercs;
cReportHiredMerc = cl_lan->reportHiredMerc;
// WANNE: Removed
/*
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[24],str,cMaxClients,cMaxMercs,cGameType,cSameMercAllowed,cDamageMultiplier,gGameOptions.fTurnTimeLimit,secs_per_tick,cl_lan->disableBobbyRay,cl_lan->disableMercEquipment,cDisableMorale,0);
*/
// WANNE - MP: I just added the NUM_SEC_IN_DAY so the game starts at Day 1 instead of Day 0
gGameExternalOptions.iGameStartingTime= NUM_SEC_IN_DAY + int(cl_lan->startingTime*3600);
// WANNE - MP: In mulitplayer the hired merc arrive immediatly, so iFirstArrivalDelay must be set to 0
// This also fixed the bug of the wrong hired hours!
gGameExternalOptions.iFirstArrivalDelay = 0;
// Disable Reinforcements
gGameExternalOptions.gfAllowReinforcements = false;
gGameExternalOptions.gfAllowReinforcementsOnlyInCity = false;
// WANNE: The new improved interrupt system (IIS) does not work with multiplayer, so disable it
//gGameOptions.fImprovedInterruptSystem = false;
// Disable Real-Time Mode
// SANDRO - huh? real-time sneak is in preferences
//gGameExternalOptions.fAllowRealTimeSneak = false;
gGameSettings.fOptions[TOPTION_ALLOW_REAL_TIME_SNEAK] = false;
gGameExternalOptions.fQuietRealTimeSneak = false;
// WANNE: Take the settings from the ja2_options.ini
// WANNE: Enable fast loading
/*gGameExternalOptions.fDisableLaptopTransition = true;
gGameExternalOptions.fFastWWWSitesLoading = true;
gGameExternalOptions.fDisableStrategicTransition = true;*/
// WANNE: fix HOT DAY in night at arrival by night.
// Explanation: If game starting time + first arrival delay < 07:00 (111600) -> we arrive before the sun rises or
// if game starting time + first arrival delay >= 21:00 (162000) -> we arrive after the sun goes down
if( (gGameExternalOptions.iGameStartingTime + gGameExternalOptions. iFirstArrivalDelay) < 111600 ||
(gGameExternalOptions.iGameStartingTime + gGameExternalOptions.iFirstArrivalDelay >= 162000))
{
// Default: Night
gubEnvLightValue = 12;
}
else
{
// Default: Day
gubEnvLightValue = 3;
}
LightSetBaseLevel(gubEnvLightValue);
InitNewGameClock( );
cWeaponReadyBonus = cl_lan->weaponReadyBonus;
cDisableSpectatorMode = cl_lan->disableSpectatorMode;
// -----------------
// RW: Inventory
switch (cl_lan->inventoryAttachment)
{
case 0: // Old/Old
gGameOptions.ubInventorySystem = INVENTORY_OLD;
gGameOptions.ubAttachmentSystem = ATTACHMENT_OLD;
break;
case 1: // New/Old
gGameOptions.ubInventorySystem = INVENTORY_NEW;
gGameOptions.ubAttachmentSystem = ATTACHMENT_OLD;
break;
case 2: // New/New
gGameOptions.ubInventorySystem = INVENTORY_NEW;
gGameOptions.ubAttachmentSystem = ATTACHMENT_NEW;
break;
}
gGameOptions.ubSquadSize = 6;
//gGameOptions.fBobbyRayFastShipments = FALSE;
gGameOptions.fInventoryCostsAP = FALSE;
gGameOptions.fUseNCTH = FALSE;
gGameOptions.fImprovedInterruptSystem = FALSE;
gGameOptions.fWeaponOverheating = FALSE;
// Server forces us to play with new Inventory, but NIV is not allowed on the client,
// because of wrong resolution or other stuff
if ( UsingNewInventorySystem() == true && !IsNIVModeValid(true) )
{
SGPRect CenteringRect= {0 + xResOffset, 0, SCREEN_WIDTH-1 - 2 * xResOffset, SCREEN_HEIGHT-1 };
DoMessageBox( MSG_BOX_BASIC_STYLE , MPClientMessage[69] , guiCurrentScreen, MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT, InvalidClientSettingsOkBoxCallback, &CenteringRect );
}
else
{
// WANNE - MP: We have to re-initialize the correct interface
if((UsingNewInventorySystem() == true))
{
InitNewInventorySystem();
InitializeSMPanelCoordsNew();
InitializeInvPanelCoordsNew();
}
else
{
gGameOptions.ubInventorySystem = INVENTORY_OLD;
InitOldInventorySystem();
InitializeSMPanelCoordsOld();
InitializeInvPanelCoordsOld();
}
// WANNE - MP: We also have to reinitialize the merc profiles because
// they depend on the inventory!
LoadMercProfiles();
InitializeFaceGearGraphics();
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[26],cl_lan->client_num,szDefault );
fDrawCharacterList = true; // set the character list to be redrawn
strcpy(client_names[cl_lan->client_num-1],szDefault);
// OJW - 20091024 - extract random table
if (!is_server)
memcpy(guiPreRandomNums,cl_lan->random_table,sizeof(UINT32)*MAX_PREGENERATED_NUMS);
// WANNE: Turn on airspace mode (to switch maps) for the server!
if (is_server)
TurnOnAirSpaceMode();
}
}
else
{
fDrawCharacterList = true; // set the character list to be redrawn
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[26],cl_lan->client_num,szDefault );
strcpy(client_names[cl_lan->client_num-1],szDefault);
}
}
void reapplySETTINGS()
{
// reapply some settings that would be lost if a game is re-initialised
// after joining ( from downloading files )
// Stuff from connect_client()
//**********************
//here some nifty little tweaks
LaptopSaveInfo.guiNumberOfMercPaymentsInDays += 20;
LaptopSaveInfo.gubLastMercIndex = LAST_MERC_ID;
LaptopSaveInfo.ubLastMercAvailableId = 7;
gGameExternalOptions.fEnableSlayForever =1;
LaptopSaveInfo.gubPlayersMercAccountStatus = 4;
// WANNE: This should fix the bug playing a "tilt" sound and showing the mini laptop graphic on the screen, when opening the laptop / option screen from map screen
gfDontStartTransitionFromLaptop = TRUE;
gMercProfiles[ 57 ].sSalary = 2000;
gMercProfiles[ 58 ].sSalary = 1500;
gMercProfiles[ 59 ].sSalary = 600;
gMercProfiles[ 60 ].sSalary = 500;
gMercProfiles[ 64 ].sSalary = 1500;
gMercProfiles[ 72 ].sSalary = 1000;
gMercProfiles[ 148 ].sSalary = 100;
gMercProfiles[ 68 ].sSalary = 2200; //iggy;
// Stuff from recieveSETTINGS
gsMercArriveSectorX=gMPServerSettings.gsMercArriveSectorX;
gsMercArriveSectorY=gMPServerSettings.gsMercArriveSectorY;
cGameType=gMPServerSettings.gameType;
cDisableMorale=gMPServerSettings.disableMorale;
ChangeSelectedMapSector( gsMercArriveSectorX, gsMercArriveSectorY, 0 );
CHAR16 str[128];
GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, str, TRUE );
// WANNE - MP: If time set to 0, then no time turns
INT32 secs_per_tick;
if (gMPServerSettings.secondsPerTick == 0)
{
gGameOptions.fTurnTimeLimit=FALSE;
secs_per_tick=1;
}
else
{
gGameOptions.fTurnTimeLimit=gMPServerSettings.sofTurnTimeLimit;
secs_per_tick=gMPServerSettings.secondsPerTick;
}
PLAYER_TEAM_TIMER_SEC_PER_TICKS=secs_per_tick;
INT32 clstarting_balance=gMPServerSettings.startingCash;//set starting balance
if(LaptopSaveInfo.iCurrentBalance<clstarting_balance)
{
AddTransactionToPlayersBook( ANONYMOUS_DEPOSIT, 0, GetWorldTotalMin(), clstarting_balance-LaptopSaveInfo.iCurrentBalance );
}
else
{
AddTransactionToPlayersBook( TRANSACTION_FEE, 0, GetWorldTotalMin(), clstarting_balance-LaptopSaveInfo.iCurrentBalance );
}
gGameOptions.fGunNut=gMPServerSettings.sofGunNut;
gGameOptions.ubGameStyle=gMPServerSettings.soubGameStyle;
gGameOptions.ubDifficultyLevel=gMPServerSettings.soubDifficultyLevel;
gGameOptions.fNewTraitSystem = gMPServerSettings.soubSkillTraits;
gGameOptions.fIronManMode=gMPServerSettings.sofIronManMode;
gGameOptions.ubBobbyRay=gMPServerSettings.soubBobbyRay;
// Set Bobby Ray "Under Construction"?
if(!gMPServerSettings.disableBobbyRay)
{
SetBookMark( BOBBYR_BOOKMARK );
LaptopSaveInfo.fBobbyRSiteCanBeAccessed = TRUE;
}
// Enable "AIM" and "MERC" only if random merc is false!
if (!gMPServerSettings.randomMercs)
{
SetBookMark( AIM_BOOKMARK );
SetBookMark( MERC_BOOKMARK );
}
if(!gMPServerSettings.disableMercEquipment)
cAllowMercEquipment = 1;
cMaxMercs = gMPServerSettings.maxMercs;
cReportHiredMerc = gMPServerSettings.reportHiredMerc;
// WANNE - MP: I just added the NUM_SEC_IN_DAY so the game starts at Day 1 instead of Day 0
gGameExternalOptions.iGameStartingTime= NUM_SEC_IN_DAY + int(gMPServerSettings.startingTime*3600);
// WANNE - MP: In mulitplayer the hired merc arrive immediatly, so iFirstArrivalDelay must be set to 0
// This also fixed the bug of the wrong hired hours!
gGameExternalOptions.iFirstArrivalDelay = 0;
// Disable Reinforcements
gGameExternalOptions.gfAllowReinforcements = false;
gGameExternalOptions.gfAllowReinforcementsOnlyInCity = false;
// WANNE: The new improved interrupt system (IIS) does not work with multiplayer, so disable it
//gGameOptions.fImprovedInterruptSystem = false;
// Disable Real-Time Mode
// SANDRO - real-time sneak is in preferences
//gGameExternalOptions.fAllowRealTimeSneak = false;
gGameSettings.fOptions[TOPTION_ALLOW_REAL_TIME_SNEAK] = false;
gGameExternalOptions.fQuietRealTimeSneak = false;
// WANNE: Take the settings from the ja2_options.ini
// WANNE: Enable fast loading
/*gGameExternalOptions.fDisableLaptopTransition = true;
gGameExternalOptions.fFastWWWSitesLoading = true;
gGameExternalOptions.fDisableStrategicTransition = true;*/
// WANNE: fix HOT DAY in night at arrival by night.
// Explanation: If game starting time + first arrival delay < 07:00 (111600) -> we arrive before the sun rises or
// if game starting time + first arrival delay >= 21:00 (162000) -> we arrive after the sun goes down
if( (gGameExternalOptions.iGameStartingTime + gGameExternalOptions. iFirstArrivalDelay) < 111600 ||
(gGameExternalOptions.iGameStartingTime + gGameExternalOptions.iFirstArrivalDelay >= 162000))
{
// Default: Night
gubEnvLightValue = 12;
}
else
{
// Default: Day
gubEnvLightValue = 3;
}
LightSetBaseLevel(gubEnvLightValue);
InitNewGameClock( );
cWeaponReadyBonus = gMPServerSettings.weaponReadyBonus;
cDisableSpectatorMode = gMPServerSettings.disableSpectatorMode;
// -----------------
// RW: Inventory
switch (gMPServerSettings.inventoryAttachment)
{
case 0: // Old/Old
gGameOptions.ubInventorySystem = INVENTORY_OLD;
gGameOptions.ubAttachmentSystem = ATTACHMENT_OLD;
break;
case 1: // New/Old
gGameOptions.ubInventorySystem = INVENTORY_NEW;
gGameOptions.ubAttachmentSystem = ATTACHMENT_OLD;
break;
case 2: // New/New
gGameOptions.ubInventorySystem = INVENTORY_NEW;
gGameOptions.ubAttachmentSystem = ATTACHMENT_NEW;
break;
}
gGameOptions.ubSquadSize = 6;
//gGameOptions.fBobbyRayFastShipments = FALSE;
gGameOptions.fInventoryCostsAP = FALSE;
gGameOptions.fUseNCTH = FALSE;
gGameOptions.fImprovedInterruptSystem = FALSE;
gGameOptions.fWeaponOverheating = FALSE;
// WANNE - MP: We have to re-initialize the correct interface
if((UsingNewInventorySystem() == true) && IsNIVModeValid(true))
{
InitNewInventorySystem();
InitializeSMPanelCoordsNew();
InitializeInvPanelCoordsNew();
}
else
{
gGameOptions.ubInventorySystem = INVENTORY_OLD;
InitOldInventorySystem();
InitializeSMPanelCoordsOld();
InitializeInvPanelCoordsOld();
}
// WANNE - MP: We also have to reinitialize the merc profiles because
// they depend on the inventory!
LoadMercProfiles();
InitializeFaceGearGraphics();
fDrawCharacterList = true; // set the character list to be redrawn
}
void recieveTEAMCHANGE( RPCParameters *rpcParameters )
{
teamchange_struct* cl_lan = (teamchange_struct*)rpcParameters->input;
if (!can_teamchange())
{
// error
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"An error occured in recieveTEAMCHANGE that should not occur");
}
else
{
// redraw the character list on the map screen
fTeamPanelDirty = true;
fDrawCharacterList = true;
client_teams[cl_lan->client_num-1] = cl_lan->newteam;
if (cl_lan->client_num == CLIENT_NUM)
{
TEAM = cl_lan->newteam;
}
}
}
void recieveEDGECHANGE( RPCParameters *rpcParameters )
{
edgechange_struct* cl_lan = (edgechange_struct*)rpcParameters->input;
if (!can_edgechange())
{
// error
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"An error occured in recieveEDGECHANGE that should not occur");
}
else
{
// redraw the character list on the map screen
fTeamPanelDirty = true;
fDrawCharacterList = true;
client_edges[cl_lan->client_num-1] = cl_lan->newedge;
if (cl_lan->client_num == CLIENT_NUM)
{
// store the setting locally
cStartingSectorEdge = cl_lan->newedge;
}
}
}
void recieveMAPCHANGE( RPCParameters *rpcParameters )
{
mapchange_struct* cl_lan = (mapchange_struct*)rpcParameters->input;
if (!is_client || allowlaptop)
{
// error
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"An error occured in recieveMAPCHANGE that should not occur");
}
else
{
// copy new map settings locally
gsMercArriveSectorX=cl_lan->gsMercArriveSectorX;
gsMercArriveSectorY=cl_lan->gsMercArriveSectorY;
ChangeSelectedMapSector( gsMercArriveSectorX, gsMercArriveSectorY, 0 );
// WANNE - BMP:
InitializeWorldSize( gsMercArriveSectorX, gsMercArriveSectorY, 0 );
gGameExternalOptions.iGameStartingTime= NUM_SEC_IN_DAY + int(cl_lan->startingTime*3600);
CHAR16 str[128];
GetSectorIDString( gsMercArriveSectorX, gsMercArriveSectorY, 0, str, TRUE );
// notify clients of map change in console
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[46],str);
}
}
// 20091002 - OJW - Explosives
void send_grenade (OBJECTTYPE *pGameObj, float dLifeLength, float xPos, float yPos, float zPos, float xForce, float yForce, float zForce, UINT32 sTargetGridNo, UINT8 ubOwner, UINT8 ubActionCode, UINT32 uiActionData, INT32 iRealObjectID , bool bIsThrownGrenade)
{
ubOwner = MPEncodeSoldierID(ubOwner); // translate our soldier to the "network" version
SOLDIERTYPE* pSoldier = MercPtrs[ubOwner];
if (pSoldier != NULL)
{
if ((pSoldier->bTeam == 1 && is_server) || IsOurSoldier(pSoldier))
{
physics_object gren;
gren.dForceX = xForce;
gren.dForceY = yForce;
gren.dForceZ = zForce;
gren.dX = xPos;
gren.dY = yPos;
gren.dZ = zPos;
gren.dLifeSpan = dLifeLength;
gren.ubActionCode = ubActionCode;
gren.uiActionData = uiActionData;
gren.ubID = ubOwner;
gren.usItem = pGameObj->usItem;
gren.sTargetGridNo = sTargetGridNo;
gren.ubID = ubOwner;
gren.RealObjectID = iRealObjectID;
gren.IsThrownGrenade = bIsThrownGrenade;
gren.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_grenade ( usItem : %i , sGridNo : %i , ubSoldierID : %i , uiPreRandomIndex : %i )\n",gren.usItem, gren.sTargetGridNo , gren.ubID , guiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
client->RPC("sendGRENADE",(const char*)&gren, (int)sizeof(physics_object)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
}
void recieveGRENADE (RPCParameters *rpcParameters)
{
physics_object* gren = (physics_object*)rpcParameters->input;
gren->ubID = MPDecodeSoldierID(gren->ubID);
SOLDIERTYPE* pThrower = MercPtrs[ gren->ubID ];
if (pThrower != NULL)
{
guiPreRandomIndex = gren->uiPreRandomIndex;
// grenade wasnt thrown by one of our guys, so we should do it on the client
if (!IsOurSoldier(pThrower) && (pThrower->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveGRENADE ( usItem : %i , sGridNo : %i , ubSoldierID : %i , uiPreRandomIndex : %i )\n",gren->usItem, gren->sTargetGridNo , gren->ubID , guiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
// this is a bit of a hack until we do the inventory sync
OBJECTTYPE* newObj = new OBJECTTYPE();
CreateItem( gren->usItem, 99, newObj );
OBJECTTYPE::CopyToOrCreateAt(&pThrower->pTempObject, newObj);
// this will create a grenade and launch it
INT32 i = CreatePhysicalObject( pThrower->pTempObject , gren->dLifeSpan , gren->dX , gren->dY , gren->dZ , gren->dForceX , gren->dForceY , gren->dForceZ , pThrower->ubID , gren->ubActionCode , gren->uiActionData, false);
// save extra state info so we can check and feed it result later
ObjectSlots[ i ].mpRealObjectID = gren->RealObjectID;
ObjectSlots[ i ].mpTeam = pThrower->bTeam;
ObjectSlots[ i ].mpIsFromRemoteClient = true;
ObjectSlots[ i ].mpHaveClientResult = false;
ObjectSlots[ i ].mpWasDud = false;
// Do grenade animation (todo fix this for mortars)
if (gren->IsThrownGrenade)
{
{
Assert(pThrower->pThrowParams == NULL);
// not a mem leak
// will be freed in AdjustToNextAnimationFrame(SOLDIERTYPE*), case 461
pThrower->pThrowParams = (THROW_PARAMS*) malloc(sizeof(THROW_PARAMS));
pThrower->pThrowParams->dForceX = gren->dForceX;
pThrower->pThrowParams->dForceY = gren->dForceY;
pThrower->pThrowParams->dForceZ = gren->dForceZ;
pThrower->pThrowParams->dLifeSpan = gren->dLifeSpan;
pThrower->pThrowParams->dX = gren->dX;
pThrower->pThrowParams->dY = gren->dY;
pThrower->pThrowParams->dZ = gren->dZ;
pThrower->pThrowParams->ubActionCode = gren->ubActionCode;
pThrower->pThrowParams->uiActionData = gren->uiActionData;
}
HandleSoldierThrowItem( pThrower, gren->sTargetGridNo );
}
}
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg[128];
sprintf(tmpMsg,"ERROR! - Invalid Soldier pointer from ubID %i in recieveGRENADE()",gren->ubID);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg);
#endif
}
}
// we send a grenade result out to the clients as it may have been a fizzer
void send_grenade_result (float xPos, float yPos, float zPos, INT32 sGridNo, UINT8 ubOwnerID, INT32 iRealObjectID, bool bIsDud)
{
ubOwnerID = MPEncodeSoldierID(ubOwnerID); // translate our soldier to the "network" version
SOLDIERTYPE* pSoldier = MercPtrs[ubOwnerID];
if (pSoldier != NULL)
{
if ((pSoldier->bTeam == 1 && is_server) || IsOurSoldier(pSoldier))
{
grenade_result gres;
gres.dX = xPos;
gres.dY = yPos;
gres.dZ = zPos;
gres.sGridNo = sGridNo;
gres.ubOwnerID = ubOwnerID;
gres.RealObjectID = iRealObjectID;
gres.bWasDud = bIsDud;
gres.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_grenade_result ( RealObjectID : %i , sGridNo : %i , ubSoldierID : %i , uiPreRandomIndex : %i )\n",gres.RealObjectID, gres.sGridNo , gres.ubOwnerID , guiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
client->RPC("sendGRENADERESULT",(const char*)&gres, (int)sizeof(grenade_result)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
}
void recieveGRENADERESULT (RPCParameters *rpcParameters)
{
grenade_result* gres = (grenade_result*)rpcParameters->input;
gres->ubOwnerID = MPDecodeSoldierID(gres->ubOwnerID);
SOLDIERTYPE* pThrower = MercPtrs[ gres->ubOwnerID ];
if (pThrower != NULL)
{
// grenade wasnt thrown by one of our guys, so we should do it on the client
if (!IsOurSoldier(pThrower) && (pThrower->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveGRENADERESULT ( RealObjectID : %i , sGridNo : %i , ubSoldierID : %i , uiPreRandomIndex : %i )\n",gres->RealObjectID, gres->sGridNo , gres->ubOwnerID , gres->uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
bool bFound = false;
INT32 usCnt;
// loop through and find the local object we assigned for the remote grenade
for( usCnt=0; usCnt<NUM_OBJECT_SLOTS; usCnt++ )
{
if (ObjectSlots[ usCnt ].mpTeam == pThrower->bTeam && ObjectSlots[ usCnt ].mpRealObjectID == gres->RealObjectID)
{
bFound = true;
break;
}
}
if (bFound)
{
// override the local predictions with the ones from the client that threw it
ObjectSlots[ usCnt ].mpHaveClientResult = true;
ObjectSlots[ usCnt ].mpWasDud = gres->bWasDud;
ObjectSlots[ usCnt ].Position.x = gres->dX;
ObjectSlots[ usCnt ].Position.y = gres->dY;
ObjectSlots[ usCnt ].Position.z = gres->dZ;
ObjectSlots[ usCnt ].sGridNo = gres->sGridNo;
HandleArmedObjectImpact( &ObjectSlots[ usCnt ] );
guiPreRandomIndex = gres->uiPreRandomIndex; // do this here because it should be in the same sequence as the sending computer which sends the grenade result at the end of HandleArmedObjectImpact()
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Couldnt find a local PhysicsObject for the RealObjectID %i sent remotely from Team %i in recievePLANTEXPLOSIVE()",gres->RealObjectID, pThrower->bTeam );
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
#endif
}
}
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg[128];
sprintf(tmpMsg,"ERROR! - Invalid Soldier pointer from ubID %i in recieveGRENADERESULT()",gres->ubOwnerID);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg);
#endif
}
}
void send_plant_explosive (UINT8 ubID,UINT16 usItem,UINT8 ubItemStatus,UINT16 usFlags, UINT32 sGridNo,UINT8 ubLevel, UINT32 uiWorldItemIndex)
{
explosive_obj exp;
exp.sGridNo = sGridNo;
exp.ubID = MPEncodeSoldierID(ubID);
exp.usItem = usItem;
exp.ubItemStatus = ubItemStatus;
exp.usFlags = usFlags;
exp.uiWorldIndex = uiWorldItemIndex;
exp.ubLevel = ubLevel;
exp.bDetonatorType = gWorldItems[ uiWorldItemIndex ].object[0]->data.misc.bDetonatorType;
if (exp.bDetonatorType == BOMB_REMOTE)
exp.bDelayFreq = gWorldItems[ uiWorldItemIndex ].object[0]->data.misc.bFrequency;
else
exp.bDelayFreq = gWorldItems[ uiWorldItemIndex ].object[0]->data.misc.bDelay;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_plant_explosive ( usItem : %i , sGridNo : %i , ubSoldierID : %i , uiPreRandomIndex : %i , uiWorldItemIndex : %i )\n",exp.usItem, exp.sGridNo , exp.ubID , guiPreRandomIndex , uiWorldItemIndex );
MPDebugMsg(tmpMPDbgString);
#endif
client->RPC("sendPLANTEXPLOSIVE",(const char*)&exp, (int)sizeof(explosive_obj)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recievePLANTEXPLOSIVE (RPCParameters *rpcParameters)
{
explosive_obj* exp = (explosive_obj*)rpcParameters->input;
exp->ubID = MPDecodeSoldierID( exp->ubID );
SOLDIERTYPE* pSoldier = MercPtrs[ exp->ubID ];
if (pSoldier != NULL)
{
// explosive wasnt planted on our client, so we should do it on the client
if (!IsOurSoldier(pSoldier) && (pSoldier->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recievePLANTEXPLOSIVE ( usItem : %i , sGridNo : %i , ubSoldierID : %i , uiPreRandomIndex : %i )\n",exp->usItem, exp->sGridNo , exp->ubID , guiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
#endif
// this is a bit of a hack until we do the inventory sync
OBJECTTYPE* newObj = new OBJECTTYPE();
CreateItem( exp->usItem, exp->ubItemStatus, newObj );
INT32 iNewItemIndex;
OBJECTTYPE* pObj = AddItemToPoolAndGetIndex( exp->sGridNo, newObj, VISIBLE, exp->ubLevel, exp->usFlags,0, exp->ubID ,&iNewItemIndex);
// need to save Item Type metadata agaist the world item
(*pObj)[0]->data.misc.ubBombOwner = exp->ubID + 2; // this is a hack the designers put into the game, storing the side as well (which isnt relevant in MP, but still have to do it)
(*pObj)[0]->data.misc.usBombItem = exp->usItem;
(*pObj)[0]->data.misc.bDetonatorType = exp->bDetonatorType;
if (exp->bDetonatorType == BOMB_REMOTE)
(*pObj)[0]->data.misc.bFrequency = exp->bDelayFreq;
else
(*pObj)[0]->data.misc.bDelay = exp->bDelayFreq;
// save old clients WorldID if we can
// loop through world bombs and find the one linked to the item we just created
UINT32 uiCount;
bool bFound = false;
for(uiCount=0; uiCount < guiNumWorldBombs; uiCount++)
{
if ( gWorldBombs[ uiCount ].fExists == TRUE && gWorldBombs[ uiCount ].iItemIndex == iNewItemIndex)
{
bFound = true;
gWorldBombs[uiCount].iMPWorldItemIndex = exp->uiWorldIndex;
gWorldBombs[uiCount].ubMPTeamIndex = pSoldier->bTeam;
gWorldBombs[uiCount].bIsFromRemotePlayer = true;
break;
}
}
if (!bFound)
{
#ifdef JA2BETAVERSION
// this is a local failure really and will probably NEVER happen
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Couldnt link our local WorldBomb to the ID sent remotely from Team %i in recievePLANTEXPLOSIVE()", pSoldier->bTeam );
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
#endif
}
}
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg[128];
sprintf(tmpMsg,"ERROR! - Invalid Soldier pointer from ubID %i in recievePLANTEXPLOSIVE()",exp->ubID);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg);
#endif
}
}
void send_detonate_explosive (UINT32 uiWorldIndex, UINT8 ubID)
{
ubID = MPEncodeSoldierID(ubID);
SOLDIERTYPE* pSoldier = MercPtrs[ubID];
if (pSoldier != NULL)
{
// explosive detonated on this client, notify the other clients
if ((pSoldier->bTeam == 1 && is_server) || IsOurSoldier(pSoldier))
{
// find the appropriate world bomb for the world item
UINT8 uiBombIndex = -1;
UINT32 uiCount;
for(uiCount=0; uiCount < guiNumWorldBombs; uiCount++)
{
if (gWorldBombs[uiCount].iItemIndex == uiWorldIndex)
{
uiBombIndex = uiCount;
break;
}
}
if (uiBombIndex > -1)
{
detonate_struct det;
det.ubID = ubID;
if ( gWorldBombs[ uiBombIndex ].bIsFromRemotePlayer )
{
// it is possible for players from other teams to set off a bomb that does not belong to them if they fail disarming it
// but we must send the ID for the world item of the bomb that all the other clients recognise
det.ubMPTeamIndex = gWorldBombs[ uiBombIndex ].ubMPTeamIndex;
det.uiWorldItemIndex = gWorldBombs[ uiBombIndex ].iMPWorldItemIndex;
}
else
{
// it is a bomb that originated on our client
det.uiWorldItemIndex = uiWorldIndex;
det.ubMPTeamIndex = pSoldier->bTeam;
}
det.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_detonate_explosive ( MPTeam : %i , uiWorldIndex : %i , uiPreRandomIndex : %i )\n",det.ubMPTeamIndex, det.uiWorldItemIndex , det.uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
client->RPC("sendDETONATEEXPLOSIVE",(const char*)&det, (int)sizeof(detonate_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
else
{
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Cant find a local WorldBomb for WorldIndex (locally) %i in send_detonate_explosive()",uiWorldIndex);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
}
}
}
}
void recieveDETONATEEXPLOSIVE (RPCParameters *rpcParameters)
{
detonate_struct* det = (detonate_struct*)rpcParameters->input;
det->ubID = MPDecodeSoldierID(det->ubID);
SOLDIERTYPE* pSoldier = MercPtrs[det->ubID];
if (pSoldier != NULL)
{
// if explosive detonation didnt originate from this client then its need to be performed here
if (pSoldier->bTeam != netbTeam && (pSoldier->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveDETONATEEXPLOSIVE ( MPTeam : %i , uiWorldIndex : %i , uiPreRandomIndex : %i , ubID : %i )\n",det->ubMPTeamIndex, det->uiWorldItemIndex , det->uiPreRandomIndex , det->ubID );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
guiPreRandomIndex = det->uiPreRandomIndex; // syncronise random number generator
UINT32 uiCount;
UINT32 ubWorldIndexToCheck = -1;
bool bFound = false;
for(uiCount=0; uiCount < guiNumWorldBombs; uiCount++)
{
// we could be recieving a message that a player from another team has detonated our bomb (while disarming), in this case we would check the local ids
// otherwise we check MPCreatorID's like normal
ubWorldIndexToCheck = (det->ubMPTeamIndex == netbTeam ? gWorldBombs[ uiCount ].iItemIndex : gWorldBombs[ uiCount ].iMPWorldItemIndex);
if ( gWorldBombs[ uiCount ].fExists == TRUE &&
ubWorldIndexToCheck == det->uiWorldItemIndex &&
(gWorldBombs[ uiCount ].ubMPTeamIndex == det->ubMPTeamIndex || det->ubMPTeamIndex == netbTeam) )
{
bFound = true;
AddBombToQueue(uiCount, guiBaseJA2Clock, TRUE); // blow up now :)
break;
}
}
if (!bFound)
{
#ifdef JA2BETAVERSION
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Cant find a local WorldBomb for remote WorldIndex %i from Team %i in recieveDETONATEEXPLOSIVE()",det->uiWorldItemIndex,det->ubMPTeamIndex );
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
#endif
}
}
}
}
void send_disarm_explosive(UINT32 sGridNo, UINT32 uiWorldItem, UINT8 ubID)
{
ubID = MPEncodeSoldierID(ubID);
SOLDIERTYPE* pSoldier = MercPtrs[ubID];
if (pSoldier != NULL)
{
// explosive disarmed on this client, notify the other clients
if ((pSoldier->bTeam == 1 && is_server) || IsOurSoldier(pSoldier))
{
// find the appropriate world bomb for the world item
UINT8 uiBombIndex = -1;
UINT32 uiCount;
for(uiCount=0; uiCount < guiNumWorldBombs; uiCount++)
{
if (gWorldBombs[uiCount].iItemIndex == uiWorldItem)
{
uiBombIndex = uiCount;
break;
}
}
if (uiBombIndex > -1)
{
disarm_struct disarm;
disarm.ubID = ubID;
if ( gWorldBombs[ uiBombIndex ].bIsFromRemotePlayer )
{
// it is possible for players from other teams to defuse a bomb in the world
// but we must send the ID for the world item of the bomb that all the other clients recognise
disarm.ubMPTeamIndex = gWorldBombs[ uiBombIndex ].ubMPTeamIndex;
disarm.uiWorldItemIndex = gWorldBombs[ uiBombIndex ].iMPWorldItemIndex;
}
else
{
disarm.ubMPTeamIndex = pSoldier->bTeam;
disarm.uiWorldItemIndex = uiWorldItem;
}
disarm.sGridNo = sGridNo;
disarm.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_disarm_explosive ( MPTeam : %i , uiWorldIndex : %i , uiPreRandomIndex : %i , sGridNo : %i )\n",disarm.ubMPTeamIndex, disarm.uiWorldItemIndex , disarm.uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
client->RPC("sendDISARMEXPLOSIVE",(const char*)&disarm, (int)sizeof(disarm_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
else
{
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Cant find a local WorldBomb for WorldIndex (locally) %i in send_disarm_explosive()",uiWorldItem);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
}
}
}
}
void recieveDISARMEXPLOSIVE (RPCParameters *rpcParameters)
{
disarm_struct* disarm = (disarm_struct*)rpcParameters->input;
disarm->ubID = MPDecodeSoldierID(disarm->ubID);
SOLDIERTYPE* pSoldier = MercPtrs[disarm->ubID];
if (pSoldier != NULL)
{
// if explosive disarm didnt originate from this client then its need to be performed here
if (pSoldier->bTeam != netbTeam && (pSoldier->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveDISARMEXPLOSIVE ( MPTeam : %i , uiWorldItemIndex : %i , uiPreRandomIndex : %i , ubID : %i , sGridNo : %i )\n",disarm->ubMPTeamIndex, disarm->uiWorldItemIndex , disarm->uiPreRandomIndex , disarm->ubID , disarm->sGridNo );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
guiPreRandomIndex = disarm->uiPreRandomIndex; // syncronise random number generator
UINT32 uiCount;
UINT32 ubWorldIndexToCheck = -1;
bool bFound = false;
for(uiCount=0; uiCount < guiNumWorldBombs; uiCount++)
{
ubWorldIndexToCheck = (disarm->ubMPTeamIndex == netbTeam ? gWorldBombs[ uiCount ].iItemIndex : gWorldBombs[ uiCount ].iMPWorldItemIndex);
if ( gWorldBombs[ uiCount ].fExists == TRUE &&
disarm->uiWorldItemIndex == ubWorldIndexToCheck &&
(gWorldBombs[ uiCount ].ubMPTeamIndex == disarm->ubMPTeamIndex || disarm->ubMPTeamIndex == netbTeam) )
{
bFound = true;
// print out a screen message if it was our bomb
if (disarm->ubMPTeamIndex == netbTeam)
{
SOLDIERTYPE * pBombOwner = MercPtrs[gWorldItems[ gWorldBombs[ uiCount ].iItemIndex ].soldierID];
if (pBombOwner != NULL)
{
ScreenMsg( FONT_LTBLUE , MSG_MPSYSTEM , MPClientMessage[71], pBombOwner->name, pSoldier->name);
}
}
// removing from the item pool will remove world item and world bomb
UINT8 ubLevel = gWorldItems[ gWorldBombs[ uiCount ].iItemIndex ].ubLevel;
RemoveItemFromPool( disarm->sGridNo , gWorldBombs[ uiCount ].iItemIndex, ubLevel );
break;
}
}
if (!bFound)
{
#ifdef JA2BETAVERSION
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Cant find a local WorldBomb for remote WorldIndex %i from Team %i in recieveDISARMEXPLOSIVE()",disarm->uiWorldItemIndex,disarm->ubMPTeamIndex );
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
#endif
}
}
}
}
void send_spreadeffect ( INT32 sGridNo, UINT8 ubRadius, UINT16 usItem, UINT8 ubOwner, BOOLEAN fSubsequent, INT8 bLevel, INT32 iSmokeEffectID )
{
spreadeffect_struct sef;
sef.sGridNo = sGridNo;
sef.ubRadius = ubRadius;
sef.usItem = usItem;
sef.ubOwner = MPEncodeSoldierID(ubOwner);
sef.fSubsequent = fSubsequent;
sef.bLevel = bLevel;
sef.iSmokeEffectID = iSmokeEffectID;
sef.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_spreadeffect ( sGridNo : %i , ubRadius : %i , usItem : %i , ubOwner : %i , fSubsequent : %i , bLevel : %i , iSmokeEffectID : %i , uiPreRandomIndex : %i )\n",sef.sGridNo, sef.ubRadius ,sef.usItem, sef.ubOwner, sef.fSubsequent, sef.bLevel, sef.iSmokeEffectID, sef.uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
#endif
client->RPC("sendSPREADEFFECT",(const char*)&sef, (int)sizeof(spreadeffect_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveSPREADEFFECT (RPCParameters *rpcParameters)
{
spreadeffect_struct* sef = (spreadeffect_struct*)rpcParameters->input;
sef->ubOwner = MPDecodeSoldierID(sef->ubOwner);
SOLDIERTYPE* pSoldier = MercPtrs[sef->ubOwner];
if (pSoldier != NULL)
{
// spread effect didnt originate from us
if (!IsOurSoldier(pSoldier) && (pSoldier->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveSPREADEFFECT ( sGridNo : %i , ubRadius : %i , usItem : %i , ubOwner : %i , fSubsequent : %i , bLevel : %i , iSmokeEffectID : %i , uiPreRandomIndex : %i )\n",sef->sGridNo, sef->ubRadius ,sef->usItem, sef->ubOwner, sef->fSubsequent, sef->bLevel, sef->iSmokeEffectID, sef->uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
#endif
guiPreRandomIndex = sef->uiPreRandomIndex; // syncronise random number generator
// translate SmokeEffectID
if (sef->iSmokeEffectID >= 0)
{
UINT32 uiCount;
bool bFound = false;
for(uiCount=0; uiCount < guiNumSmokeEffects; uiCount++)
{
if ( gSmokeEffectData[ uiCount ].fAllocated == TRUE && gSmokeEffectData[ uiCount ].iMPTeamIndex == pSoldier->bTeam && gSmokeEffectData[ uiCount ].iMPSmokeEffectID == sef->iSmokeEffectID)
{
bFound = true;
SpreadEffect( sef->sGridNo , sef->ubRadius , sef->usItem , sef->ubOwner , sef->fSubsequent , sef->bLevel , uiCount , TRUE);
break;
}
}
if (!bFound)
{
#ifdef JA2BETAVERSION
char tmpMsg1[128];
sprintf(tmpMsg1,"ERROR! - Cant find a local SmokeEffectID for remote ID %i from team %i in recieveSPREADEFFECT()",sef->iSmokeEffectID, pSoldier->bTeam);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg1);
#endif
}
}
else
{
SpreadEffect( sef->sGridNo , sef->ubRadius , sef->usItem , sef->ubOwner , sef->fSubsequent , sef->bLevel , sef->iSmokeEffectID , TRUE);
}
}
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg2[128];
sprintf(tmpMsg2,"ERROR! - Invalid Soldier pointer from ubID %i in recieveSPREADEFFECT()",sef->ubOwner);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg2);
#endif
}
}
void send_newsmokeeffect(INT32 sGridNo, UINT16 usItem, INT8 bLevel, UINT8 ubOwner, INT32 iSmokeEffectID)
{
// i'm reusing this struct, the parameters are essentially the same
spreadeffect_struct sef;
sef.sGridNo = sGridNo;
sef.usItem = usItem;
sef.ubOwner = MPEncodeSoldierID(ubOwner);
sef.bLevel = bLevel;
sef.iSmokeEffectID = iSmokeEffectID;
sef.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_newsmokeeffect ( sGridNo : %i , usItem : %i , ubOwner : %i , bLevel : %i , iSmokeEffectID : %i , uiPreRandomIndex : %i )\n",sef.sGridNo, sef.usItem, sef.ubOwner, sef.bLevel, sef.iSmokeEffectID, sef.uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
#endif
client->RPC("sendNEWSMOKEEFFECT",(const char*)&sef, (int)sizeof(spreadeffect_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveNEWSMOKEEFFECT (RPCParameters *rpcParameters)
{
spreadeffect_struct* sef = (spreadeffect_struct*)rpcParameters->input;
// translate any of our soldier ids back to the correct local copy
sef->ubOwner = MPDecodeSoldierID(sef->ubOwner);
SOLDIERTYPE* pSoldier = MercPtrs[sef->ubOwner];
if (pSoldier != NULL)
{
// new smoke effect didnt originate from us
if (!IsOurSoldier(pSoldier) && (pSoldier->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveNEWSMOKEEFFECT ( sGridNo : %i , usItem : %i , ubOwner : %i , bLevel : %i , iSmokeEffectID : %i , uiPreRandomIndex : %i )\n",sef->sGridNo, sef->usItem, sef->ubOwner, sef->bLevel, sef->iSmokeEffectID, sef->uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
#endif
guiPreRandomIndex = sef->uiPreRandomIndex;
// start new smoke effect
INT32 iNewSmokeIndex = NewSmokeEffect( sef->sGridNo , sef->usItem , sef->bLevel , sef->ubOwner , TRUE );
// attach remote id to local smoke effect
gSmokeEffectData[iNewSmokeIndex].iMPTeamIndex = pSoldier->bTeam;
gSmokeEffectData[iNewSmokeIndex].iMPSmokeEffectID = sef->iSmokeEffectID;
}
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg[128];
sprintf(tmpMsg,"ERROR! - Invalid Soldier pointer from ubID %i in recieveNEWSMOKEEFFECT()",sef->ubOwner);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg);
#endif
}
}
void send_gasdamage( SOLDIERTYPE * pSoldier, UINT16 usExplosiveClassID, INT16 sSubsequent, BOOLEAN fRecompileMovementCosts, INT16 sWoundAmt, INT16 sBreathAmt, UINT8 ubOwner )
{
explosiondamage_struct exp;
exp.ubDamageFunc = 1;
exp.ubSoldierID = MPEncodeSoldierID(pSoldier->ubID);
exp.usExplosiveClassID = usExplosiveClassID;
exp.sSubsequent = sSubsequent;
exp.fRecompileMovementCosts = fRecompileMovementCosts;
exp.sWoundAmt = sWoundAmt;
exp.sBreathAmt = sBreathAmt;
exp.ubAttackerID = MPEncodeSoldierID(ubOwner);
exp.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_gasdamage ( ubSoldierID : %i , usExplosiveClassID : %i , sSubsequent : %i , recompileMoveCosts : %i , sWoundAmt : %i , sBreathAmt : %i , ubOwner : %i )\n", exp.ubSoldierID , usExplosiveClassID , sSubsequent , fRecompileMovementCosts , sWoundAmt , sBreathAmt , exp.ubAttackerID );
MPDebugMsg(tmpMPDbgString);
#endif
client->RPC("sendEXPLOSIONDAMAGE",(const char*)&exp, (int)sizeof(explosiondamage_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void send_explosivedamage( UINT8 ubPerson, UINT8 ubOwner, INT32 sBombGridNo, INT16 sWoundAmt, INT16 sBreathAmt, UINT32 uiDist, UINT16 usItem, INT16 sSubsequent )
{
explosiondamage_struct exp;
exp.ubDamageFunc = 2;
exp.ubSoldierID = MPEncodeSoldierID(ubPerson);
exp.usItem = usItem;
exp.uiDist = uiDist;
exp.sSubsequent = sSubsequent;
exp.sBombGridNo = sBombGridNo;
exp.sWoundAmt = sWoundAmt;
exp.sBreathAmt = sBreathAmt;
exp.ubAttackerID = MPEncodeSoldierID(ubOwner);
exp.uiPreRandomIndex = guiPreRandomIndex;
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - send_explosivedamage ( ubPerson : %i , ubOwner : %i , sBombGridNo : %i , sWoundAmt : %i , sBreathAmt : %i , uiDist : %i , usItem : %i , sSubs : %i )\n",ubPerson, ubOwner , sBombGridNo , sWoundAmt , sBreathAmt , uiDist , usItem , sSubsequent );
MPDebugMsg(tmpMPDbgString);
#endif
client->RPC("sendEXPLOSIONDAMAGE",(const char*)&exp, (int)sizeof(explosiondamage_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveEXPLOSIONDAMAGE (RPCParameters *rpcParameters)
{
explosiondamage_struct* exp = (explosiondamage_struct*)rpcParameters->input;
exp->ubSoldierID = MPDecodeSoldierID(exp->ubSoldierID);
exp->ubAttackerID = MPDecodeSoldierID(exp->ubAttackerID);
SOLDIERTYPE* pSoldier = MercPtrs[exp->ubSoldierID];
if (pSoldier != NULL)
{
// damage isnt for our merc (or we wouldve handled it locally) or it is for an AI but we are NOT the server
if (!IsOurSoldier(pSoldier) && (pSoldier->bTeam != 1 || !is_server))
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"MP - recieveEXPLOSIONDAMAGE ( ubDamageFunc : %i , ubSoldierID : %i , ubAttackerID : %i , usItem : %i , usExplosiveClassID : %i , sWoundAmt : %i , sBreathAmt : %i , uiDist : %i , sSubs : %i , sBombGridNo : %i , uiPreRandomIndex : %i )\n", exp->ubDamageFunc , exp->ubSoldierID , exp->ubAttackerID , exp->usItem , exp->usExplosiveClassID , exp->sWoundAmt , exp->sBreathAmt , exp->uiDist , exp->sSubsequent , exp->sBombGridNo , exp->uiPreRandomIndex );
MPDebugMsg(tmpMPDbgString);
#endif
guiPreRandomIndex = exp->uiPreRandomIndex;
if (exp->ubDamageFunc == 1)
{
//EXPLOSIVETYPE* pExplosive = &(Explosive[ exp->usExplosiveClassID ] );
//DishOutGasDamage(pSoldier, pExplosive, exp->sSubsequent , exp->fRecompileMovementCosts , exp->sWoundAmt , exp->sBreathAmt , exp->ubAttackerID , TRUE );
// can use DishOutGasDamage() as it is dependant on the local state of the gas cloud which is not always in sync
// but we have the definite results of damage on a merc, so :
pSoldier->SoldierTakeDamage( ANIM_STAND, exp->sWoundAmt, exp->sBreathAmt, TAKE_DAMAGE_GAS, NOBODY, NOWHERE, 0, TRUE );
}
else if (exp->ubDamageFunc == 2)
{
DamageSoldierFromBlast( exp->ubSoldierID , exp->ubAttackerID , exp->sBombGridNo , exp->sWoundAmt , exp->sBreathAmt , exp->uiDist , exp->usItem , exp->sSubsequent , TRUE);
}
}
}
else
{
#ifdef JA2BETAVERSION
char tmpMsg[128];
sprintf(tmpMsg,"ERROR! - Invalid Soldier pointer from ubID %i in recieveEXPLOSIONDAMAGE()",exp->ubAttackerID);
//ScreenMsg(FONT_RED,MSG_MPSYSTEM,tmpMsg);
MPDebugMsg(tmpMsg);
#endif
}
}
void send_bullet( BULLET * pBullet,UINT16 usHandItem )
{
netb_struct netb;
netb.net_bullet=*pBullet;
netb.usHandItem=usHandItem;
//ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Sent Bullet Id: %d",pBullet->iBullet);
if(pBullet->ubTargetID < 20)netb.net_bullet.ubTargetID = netb.net_bullet.ubTargetID+ubID_prefix;
if(pBullet->ubFirerID < 20)netb.net_bullet.ubFirerID = netb.net_bullet.ubFirerID+ubID_prefix;
client->RPC("sendBULLET",(const char*)&netb, (int)sizeof(netb_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveBULLET(RPCParameters *rpcParameters)
{
netb_struct* netb = (netb_struct*)rpcParameters->input;
INT32 net_iBullet=netb->net_bullet.iBullet;
SOLDIERTYPE * pFirer=MercPtrs[ netb->net_bullet.ubFirerID ];
INT8 bTeam=pFirer->bTeam;
INT32 iBullet;
BULLET * pBullet;
iBullet = CreateBullet( netb->net_bullet.ubFirerID, 0, netb->net_bullet.usFlags,netb->usHandItem );
if (iBullet == -1)
{
#ifdef BETAVERSION
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Failed to create bullet");
#endif
}
//add bullet to bullet table for translation
bTable[bTeam][net_iBullet].remote_id = net_iBullet;
bTable[bTeam][net_iBullet].local_id = iBullet;
pBullet = GetBulletPtr( iBullet );
//ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"Created Bullet Id: %d",iBullet);
pBullet->fCheckForRoof=netb->net_bullet.fCheckForRoof;
pBullet->qIncrX=netb->net_bullet.qIncrX;
pBullet->qIncrY=netb->net_bullet.qIncrY;
pBullet->qIncrZ=netb->net_bullet.qIncrZ;
pBullet->sHitBy=netb->net_bullet.sHitBy;
pBullet->ddHorizAngle=netb->net_bullet.ddHorizAngle;
pBullet->fAimed=netb->net_bullet.fAimed;
pBullet->ubItemStatus=netb->net_bullet.ubItemStatus;
pBullet->qCurrX=netb->net_bullet.qCurrX;
pBullet->qCurrY=netb->net_bullet.qCurrY;
pBullet->qCurrZ=netb->net_bullet.qCurrZ;
pBullet->iImpact=netb->net_bullet.iImpact;
pBullet->iRange=netb->net_bullet.iRange;
pBullet->sTargetGridNo=netb->net_bullet.sTargetGridNo;
pBullet->bStartCubesAboveLevelZ=netb->net_bullet.bStartCubesAboveLevelZ;
pBullet->bEndCubesAboveLevelZ=netb->net_bullet.bEndCubesAboveLevelZ;
pBullet->iDistanceLimit=netb->net_bullet.iDistanceLimit;
FireBullet( pFirer, pBullet, FALSE );
}
void send_changestate (EV_S_CHANGESTATE * SChangeState)
{
EV_S_CHANGESTATE new_state;
memcpy( &new_state , SChangeState, sizeof( EV_S_CHANGESTATE ));
if(new_state.usSoldierID < 20)
new_state.usSoldierID = new_state.usSoldierID+ubID_prefix;
client->RPC("sendSTATE",(const char*)&new_state, (int)sizeof(EV_S_CHANGESTATE)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveSTATE(RPCParameters *rpcParameters)
{
EV_S_CHANGESTATE* new_state = (EV_S_CHANGESTATE*)rpcParameters->input;
SOLDIERTYPE * pSoldier=MercPtrs[ new_state->usSoldierID ];
if(pSoldier->bActive)
{
// Start first AID
if(new_state->usNewState==START_AID)
{
pSoldier->EVENT_InternalSetSoldierPosition( new_state->sXPos, new_state->sYPos ,FALSE, FALSE, FALSE );
pSoldier->ChangeSoldierStance( ANIM_CROUCH );
pSoldier->EVENT_SetSoldierDirection( new_state->usNewDirection );
}
// Start cutting fence
else if (new_state->usNewState==CUTTING_FENCE)
{
// The usTargetGridNo holds the GridNo of the fence tile
pSoldier->sTargetGridNo = new_state->usTargetGridNo;
pSoldier->EVENT_InternalSetSoldierPosition( new_state->sXPos, new_state->sYPos ,FALSE, FALSE, FALSE );
pSoldier->ChangeSoldierStance( ANIM_CROUCH );
pSoldier->EVENT_SetSoldierDirection( new_state->usNewDirection );
}
pSoldier->EVENT_InitNewSoldierAnim( new_state->usNewState, new_state->usStartingAniCode, new_state->fForce );
}
}
void send_death( SOLDIERTYPE *pSoldier )
{
death_struct nDeath;
nDeath.soldier_id=pSoldier->ubID;
nDeath.attacker_id=pSoldier->ubAttackerID;
// Translate soldier id for other clients if the soldier was one of ours
if(pSoldier->ubID<20)nDeath.soldier_id=nDeath.soldier_id+ubID_prefix;
// if soldier died from bleeding
if(pSoldier->ubAttackerID==NULL || pSoldier->ubAttackerID == NOBODY)
{
if (pSoldier->ubPreviousAttackerID != NOBODY && pSoldier->ubPreviousAttackerID != NULL)
nDeath.attacker_id = pSoldier->ubPreviousAttackerID;
else if (pSoldier->ubNextToPreviousAttackerID != NOBODY && pSoldier->ubNextToPreviousAttackerID != NULL)
nDeath.attacker_id = pSoldier->ubNextToPreviousAttackerID;
}
SOLDIERTYPE * pAttacker=MercPtrs[ nDeath.attacker_id ];
INT8 pA_bTeam;
CHAR16 pA_name[ 10 ];
INT8 pS_bTeam;
CHAR16 pS_name[ 10 ];
// OJW - 20081222
// save stats
if(pAttacker)
{
// if attacker was one of our own mercs, use the last hostile attacker as the killer if there is one
if (pAttacker->bTeam == pSoldier->bTeam && pSoldier->ubPreviousAttackerID != NULL && pSoldier->ubPreviousAttackerID != NOBODY)
{
pAttacker=MercPtrs[ pSoldier->ubPreviousAttackerID ];
// check if the new attacker was also a friendly...
if (pAttacker->bTeam == pSoldier->bTeam && pSoldier->ubNextToPreviousAttackerID != NULL && pSoldier->ubNextToPreviousAttackerID != NOBODY)
pAttacker=MercPtrs[ pSoldier->ubNextToPreviousAttackerID ];
// if its still a friendly, use the original attacker id...for posterity
// guy must snore too loudly if all his mates wanna kill him :)
if (pAttacker->bTeam == pSoldier->bTeam && pSoldier->ubAttackerID != NULL && pSoldier->ubAttackerID != NOBODY)
pAttacker = MercPtrs[ pSoldier->ubAttackerID ];
nDeath.attacker_id = pAttacker->ubID;
}
// Translate attacker id for other clients if attacker was one of ours
if(pAttacker->ubID<20)nDeath.attacker_id=pAttacker->ubID+ubID_prefix;
pA_bTeam=pAttacker->bTeam;
memcpy(pA_name,pAttacker->name,sizeof(CHAR16)*10);
if (pA_bTeam==1 && cGameType==MP_TYPE_COOP)
{
// CO-OP Kill by an enemy ai
pA_bTeam = 5; // the server subtracts 1 from these numbers to score in scoreboard, AI is index 4
}
else
{
// Any mode, kill by a players merc
if(pA_bTeam>5)
pA_bTeam=pA_bTeam-5;
if(pA_bTeam==0)
pA_bTeam= CLIENT_NUM;
}
}
if(pSoldier)
{
pS_bTeam=pSoldier->bTeam;
memcpy(pS_name,pSoldier->name,sizeof(CHAR16)*10);
if(pS_bTeam==1 && cGameType==MP_TYPE_COOP)
{
// CO-OP Death of an enemy ai
pS_bTeam = 5; // the server subtracts 1 from these numbers to score in scoreboard, AI is index 4
}
else
{
// Any mode death of a players merc
if(pS_bTeam>5)
pS_bTeam=pS_bTeam-5;
if(pS_bTeam==0)
pS_bTeam=CLIENT_NUM;
}
}
nDeath.attacker_team = pA_bTeam;
nDeath.soldier_team = pS_bTeam;
// notify other clients of death
client->RPC("sendDEATH",(const char*)&nDeath, (int)sizeof(death_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
// print kill notice to screen
if (pSoldier->bTeam==1)
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[67]);
else
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] );
#ifdef JA2BETAVERSION
char s_name[10];
char a_name[10];
WideCharToMultiByte(CP_UTF8,0,pS_name,-1, s_name,10,NULL,NULL);
WideCharToMultiByte(CP_UTF8,0,pA_name,-1, a_name,10,NULL,NULL);
if (pSoldier->bTeam==1)
MPDebugMsg( String ( "MPDEBUG SEND - Enemy AI #%d was killed by ('%s' - %d) (client %d - '%s')\n",nDeath.soldier_id,a_name,nDeath.attacker_id,pA_bTeam,client_names[pA_bTeam-1]) );
else if (pAttacker->bTeam==1)
MPDebugMsg( String ( "MPDEBUG SEND - '%s' (client %d - '%S') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,"Queens Army") );
else
MPDebugMsg( String ( "MPDEBUG SEND - '%s' (client %d - '%S') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,client_names[(pA_bTeam-1)]) );
#endif
}
void recieveDEATH (RPCParameters *rpcParameters)
{
death_struct* nDeath = (death_struct*)rpcParameters->input;
SOLDIERTYPE * pSoldier=MercPtrs[ nDeath->soldier_id ];
UINT16 ubAttackerID;
if((nDeath->attacker_id >= ubID_prefix) && (nDeath->attacker_id < (ubID_prefix+6)))
ubAttackerID = (nDeath->attacker_id - ubID_prefix);
else
ubAttackerID = nDeath->attacker_id;
SOLDIERTYPE * pAttacker=MercPtrs[ ubAttackerID ];
INT8 pA_bTeam;
CHAR16 pA_name[ 10 ];
INT8 pS_bTeam;
CHAR16 pS_name[ 10 ];
if(pAttacker)
{
pA_bTeam=pAttacker->bTeam;
memcpy(pA_name,pAttacker->name,sizeof(CHAR16)*10);
if(pA_bTeam>5)
pA_bTeam=pA_bTeam-5;
if(pA_bTeam==0)
pA_bTeam=CLIENT_NUM;
}
if(pSoldier)
{
pS_bTeam=pSoldier->bTeam;
memcpy(pS_name,pSoldier->name,sizeof(CHAR16)*10);
if(pS_bTeam>5)
pS_bTeam=pS_bTeam-5;
if(pS_bTeam==0)
pS_bTeam=CLIENT_NUM;
}
if(pSoldier->bActive)
{
pSoldier->usAnimState=50;
#ifdef JA2BETAVERSION
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"made merc corpse/dead");
#endif
RemoveManAsTarget(pSoldier);
TurnSoldierIntoCorpse( pSoldier, TRUE, TRUE );
if ( CheckForEndOfBattle( FALSE ) )
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[72]);
}
if (pSoldier->bTeam==1)
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[67]);
else
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[28],pS_name,(pS_bTeam),client_names[(pS_bTeam-1)],pA_name,(pA_bTeam),client_names[(pA_bTeam-1)] );
}
else
{
#ifdef JA2BETAVERSION
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, L"merc already corpse/dead");
#endif
if (pSoldier->bTeam==1)
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[67]);
CheckForEndOfBattle( FALSE );
}
#ifdef JA2BETAVERSION
char s_name[10];
char a_name[10];
WideCharToMultiByte(CP_UTF8,0,pS_name,-1, s_name,10,NULL,NULL);
WideCharToMultiByte(CP_UTF8,0,pA_name,-1, a_name,10,NULL,NULL);
if (pSoldier->bTeam==1)
MPDebugMsg( String ( "MPDEBUG RECV - Enemy AI #%d was killed by ('%s' - #%d) (client %d - '%s')\n",nDeath->soldier_id,a_name,nDeath->attacker_id,pA_bTeam,client_names[pA_bTeam-1]) );
else if (pAttacker->bTeam==1)
MPDebugMsg( String ( "MPDEBUG RECV - '%s' (client %d - '%s') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,"Queens Army") );
else
MPDebugMsg( String ( "MPDEBUG RECV - '%s' (client %d - '%s') was killed by '%s' (client %d - '%s')\n",s_name,pS_bTeam,client_names[(pS_bTeam-1)],a_name,pA_bTeam,client_names[(pA_bTeam-1)]) );
#endif
}
void send_hitstruct(EV_S_STRUCTUREHIT * SStructureHit)
{
EV_S_STRUCTUREHIT struct_hit;
memcpy( &struct_hit , SStructureHit, sizeof( EV_S_STRUCTUREHIT ));
if(SStructureHit->ubAttackerID <20)struct_hit.ubAttackerID = SStructureHit->ubAttackerID+ubID_prefix;
client->RPC("sendhitSTRUCT",(const char*)&struct_hit, (int)sizeof(EV_S_STRUCTUREHIT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void send_hitwindow(EV_S_WINDOWHIT * SWindowHit)
{
EV_S_WINDOWHIT window_hit;
memcpy( &window_hit , SWindowHit, sizeof( EV_S_WINDOWHIT ));
if(SWindowHit->ubAttackerID <20)
window_hit.ubAttackerID = SWindowHit->ubAttackerID+ubID_prefix;
client->RPC("sendhitWINDOW",(const char*)&window_hit, (int)sizeof(EV_S_WINDOWHIT)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void send_miss(EV_S_MISS * SMiss)
{
EV_S_MISS shot_miss;
memcpy( &shot_miss , SMiss, sizeof( EV_S_MISS ));
if(SMiss->ubAttackerID <20)
shot_miss.ubAttackerID = SMiss->ubAttackerID+ubID_prefix;
client->RPC("sendMISS",(const char*)&shot_miss, (int)sizeof(EV_S_MISS)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recievehitSTRUCT (RPCParameters *rpcParameters)
{
EV_S_STRUCTUREHIT* struct_hit = (EV_S_STRUCTUREHIT*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ struct_hit->ubAttackerID ];
INT8 bTeam=pSoldier->bTeam;
INT32 iBullet = bTable[bTeam][struct_hit->iBullet].local_id;
if(struct_hit->fStopped)
StopBullet( iBullet );
StructureHit( iBullet, struct_hit->usWeaponIndex, struct_hit->bWeaponStatus, struct_hit->ubAttackerID, struct_hit->sXPos, struct_hit->sYPos, struct_hit->sZPos, struct_hit->usStructureID, struct_hit->iImpact, struct_hit->fStopped );
if(struct_hit->fStopped)
RemoveBullet(iBullet);
}
void recievehitWINDOW (RPCParameters *rpcParameters)
{
EV_S_WINDOWHIT* window_hit = (EV_S_WINDOWHIT*)rpcParameters->input;
WindowHit( window_hit->sGridNo, window_hit->usStructureID, window_hit->fBlowWindowSouth, window_hit->fLargeForce );
}
void recieveMISS (RPCParameters *rpcParameters)
{
EV_S_MISS* shot_miss = (EV_S_MISS*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ shot_miss->ubAttackerID ];
INT8 bTeam=pSoldier->bTeam;
INT32 iBullet = bTable[bTeam][shot_miss->iBullet].local_id;
ShotMiss( shot_miss->ubAttackerID, iBullet );
}
BOOLEAN check_status (void)// any 'enemies' and clients left to fight ??
{
SOLDIERTYPE *pSoldier;
int cnt;
int soldiers= 0 ;
int numActiveSides = 0;
int dm_teams[4] = {0 , 0 , 0 , 0};
for(int x=0;x < MAXTEAMS; x++)
{
soldiers=0;
for(cnt = gTacticalStatus.Team[ x ].bFirstID;cnt <= gTacticalStatus.Team[ x ].bLastID; cnt++)
{
pSoldier = MercPtrs[ cnt ];
if(pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive && pSoldier->bInSector)
{
soldiers++;
}
}
if(soldiers>0)
{
gTacticalStatus.Team[ x ].bTeamActive=1;
gTacticalStatus.Team[ x ].bMenInSector=soldiers;
if (cGameType==MP_TYPE_TEAMDEATMATCH && (x==0 || x>5))
{
// store the number of active DM teams, and number of players per team still alive
int cl_num = CLIENT_NUM; // 1 based
if (x>5) cl_num = x - 5;
dm_teams[ client_teams[ cl_num - 1 ] ]++;
}
else if (cGameType != MP_TYPE_TEAMDEATMATCH)
{
// count number of active teams
numActiveSides++;
}
}
else
{
gTacticalStatus.Team[ x ].bTeamActive=0;
gTacticalStatus.Team[x].bMenInSector=0;
}
}
if( (gTacticalStatus.Team[ 0 ].bTeamActive == 0) && wiped==0)//server's team has been knocked out
{
wiped=1;
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[40] );
if(!cDisableSpectatorMode)
{
gTacticalStatus.uiFlags |= SHOW_ALL_MERCS;//hayden
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[41] );
}
else
{
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, MPClientMessage[73]);
}
teamwiped();
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[42] );
}
if (cGameType == MP_TYPE_DEATHMATCH)
{
// check game end for DeathMatch
return (numActiveSides > 1);
}
else if (cGameType == MP_TYPE_TEAMDEATMATCH)
{
// check game end for Team Deathmatch
// count how many active deathmatch teams are alive (two players could be alive but might be on the same TEAM)
for (int i=0; i < 4; i++)
{
if (dm_teams[i] > 0)
{
numActiveSides++;
}
}
return (numActiveSides > 1);
}
else if (cGameType == MP_TYPE_COOP)
{
// check for game end for CO-OP
// If any player team is alive && the number of enemies > 0 then continue game (true), else quit (false)
return ((gTacticalStatus.Team[ 0 ].bTeamActive==1 || gTacticalStatus.Team[ 6 ].bTeamActive==1 || gTacticalStatus.Team[ 7 ].bTeamActive==1 || gTacticalStatus.Team[ 8 ].bTeamActive==1 || gTacticalStatus.Team[ 9 ].bTeamActive==1 )&& NumEnemyInSector() > 0);
}
// dont stop the game by default
return true;
}
void UpdateSoldierToNetwork ( SOLDIERTYPE *pSoldier )
{
//this send stats to other clients at intervals
UINT8 id = pSoldier->ubID;
UINT32 time = GetJA2Clock();
if(id < 20 || (is_server && id <120))
{
if(pSoldier->usLastUpdateTime==0)
{
pSoldier->usLastUpdateTime = time;
}
if((time - (pSoldier->usLastUpdateTime)) > 2000 && pSoldier->stats.bLife!=0)
{
pSoldier->usLastUpdateTime = time;
EV_S_UPDATENETWORKSOLDIER SUpdateNetworkSoldier;
SUpdateNetworkSoldier.usSoldierID=pSoldier->ubID;
if(pSoldier->ubID < 20)
SUpdateNetworkSoldier.usSoldierID=pSoldier->ubID+ubID_prefix;
SUpdateNetworkSoldier.sAtGridNo=pSoldier->sGridNo;
SUpdateNetworkSoldier.bBreath=pSoldier->bBreath;
SUpdateNetworkSoldier.ubDirection=pSoldier->ubDirection;
SUpdateNetworkSoldier.bLife=pSoldier->stats.bLife;
SUpdateNetworkSoldier.bBleeding=pSoldier->bBleeding;
SUpdateNetworkSoldier.ubNewStance= gAnimControl[ pSoldier->usAnimState ].ubEndHeight;
if((gTacticalStatus.ubTopMessageType == PLAYER_TURN_MESSAGE || gTacticalStatus.ubTopMessageType == PLAYER_INTERRUPT_MESSAGE || ((gTacticalStatus.ubTopMessageType == COMPUTER_INTERRUPT_MESSAGE || gTacticalStatus.ubTopMessageType == COMPUTER_TURN_MESSAGE )&& is_server)) && (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT))//update progress bar, not supporting coop yet...
{
SUpdateNetworkSoldier.usTactialTurnLimitCounter = gTacticalStatus.usTactialTurnLimitCounter;
SUpdateNetworkSoldier.usTactialTurnLimitMax = gTacticalStatus.usTactialTurnLimitMax;
}
else
SUpdateNetworkSoldier.usTactialTurnLimitCounter = 9999;
client->RPC("updatenetworksoldier",(const char*)&SUpdateNetworkSoldier, (int)sizeof(EV_S_UPDATENETWORKSOLDIER)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
}
void UpdateSoldierFromNetwork (RPCParameters *rpcParameters)
{
EV_S_UPDATENETWORKSOLDIER* SUpdateNetworkSoldier = (EV_S_UPDATENETWORKSOLDIER*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SUpdateNetworkSoldier->usSoldierID ];
pSoldier->bBreath=SUpdateNetworkSoldier->bBreath;
pSoldier->stats.bLife=SUpdateNetworkSoldier->bLife;
INT16 sCellX, sCellY;
sCellX = CenterX( SUpdateNetworkSoldier->sAtGridNo );
sCellY = CenterY( SUpdateNetworkSoldier->sAtGridNo );
if( pSoldier->sGridNo != SUpdateNetworkSoldier->sAtGridNo)
{
pSoldier->EVENT_InternalSetSoldierPosition( sCellX, sCellY ,FALSE, FALSE, FALSE );//new syncing call to correct network lag/drift
}
if(pSoldier->ubDirection != SUpdateNetworkSoldier->ubDirection)
{
pSoldier->EVENT_SetSoldierDesiredDirection( SUpdateNetworkSoldier->ubDirection );
}
if(gAnimControl[ pSoldier->usAnimState ].ubEndHeight != SUpdateNetworkSoldier->ubNewStance && pSoldier->bCollapsed != TRUE)
{
pSoldier->ChangeSoldierStance( SUpdateNetworkSoldier->ubNewStance );
}
pSoldier->bBleeding=SUpdateNetworkSoldier->bBleeding;
if( (SUpdateNetworkSoldier->usTactialTurnLimitCounter != 9999) && (gTacticalStatus.ubTopMessageType != PLAYER_TURN_MESSAGE) && (gTacticalStatus.ubTopMessageType != PLAYER_INTERRUPT_MESSAGE))
{
gTacticalStatus.usTactialTurnLimitCounter = SUpdateNetworkSoldier->usTactialTurnLimitCounter;
gTacticalStatus.usTactialTurnLimitMax = SUpdateNetworkSoldier->usTactialTurnLimitMax;
}
}
void kick_player (void)
{
if(is_server)
{
CHAR16 Cmsg[255];
if (cMaxClients == 2)
swprintf(Cmsg, MPClientMessage[74], client_names[1],"<?>","<?>");
else if (cMaxClients == 3)
swprintf(Cmsg, MPClientMessage[74], client_names[1],client_names[2],"<?>");
else
swprintf(Cmsg, MPClientMessage[74], client_names[1],client_names[2],client_names[3]);
SGPRect CenterRect = { 100 + xResOffset, 100, SCREEN_WIDTH - xResOffset, 300 };
DoMessageBox( MSG_BOX_BASIC_STYLE, Cmsg, guiCurrentScreen, MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS | MSG_BOX_FLAG_USE_CENTERING_RECT, kick_callback, &CenterRect );
}
else
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, MPClientMessage[22] );
}
void kick_callback (UINT8 ubResult)
{
if (is_server)
{
// Pressed '1'
if(ubResult ==1)
{
// WANNE: Nothing should happen
}
else
{
if (ubResult <= cMaxClients)
{
kickR kick;
kick.ubResult=ubResult+5;
client->RPC("Snull_team",(const char*)&kick, (int)sizeof(kickR)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
// If the team that should be kicked has the turn, give the turn to the server
if (gTacticalStatus.ubCurrentTeam == kick.ubResult)
{
EndTurn(0);
}
}
else
{
// The client to which we should give the turn doe not exists. Do nothinig!
}
}
}
}
void null_team (RPCParameters *rpcParameters)
{
kickR* kick = (kickR*)rpcParameters->input;
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, MPClientMessage[29],(kick->ubResult-5),client_names[kick->ubResult-6] );
int fID = gTacticalStatus.Team[ kick->ubResult ].bFirstID;
int lID = gTacticalStatus.Team[ kick->ubResult ].bLastID;
if(kick->ubResult==netbTeam)
fID=0,lID=19;
int cnt;
for ( cnt=fID ; cnt <= lID; cnt++ )
{
TacticalRemoveSoldier( cnt );
}
if (kick->ubResult==netbTeam)
{
gTacticalStatus.uiFlags |= SHOW_ALL_MERCS;//hayden
ScreenMsg( FONT_YELLOW, MSG_MPSYSTEM, MPClientMessage[41] );
}
}
void overide_turn (void)
{
if(is_server)
{
//manual overide command for server
CHAR16 Cmsg[255];
if (cMaxClients == 2)
swprintf(Cmsg, MPClientMessage[30], client_names[1],"<?>","<?>");
else if (cMaxClients == 3)
swprintf(Cmsg, MPClientMessage[30], client_names[1],client_names[2],"<?>");
else
swprintf(Cmsg, MPClientMessage[30], client_names[1],client_names[2],client_names[3]);
SGPRect CenterRect = { 100 + xResOffset, 100, SCREEN_WIDTH - 100 - xResOffset, 300 };
DoMessageBox( MSG_BOX_BASIC_STYLE, Cmsg, guiCurrentScreen, MSG_BOX_FLAG_FOUR_NUMBERED_BUTTONS | MSG_BOX_FLAG_USE_CENTERING_RECT | MSG_BOX_FLAG_OK, turn_callback, &CenterRect );
}
else
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, MPClientMessage[22] );
}
void turn_callback (UINT8 ubResult)
{
if(is_server)
{
// Pressed '1'
if(ubResult ==1)
{
// WANNE: Nothing should happen. Do not give the turn to the server!
//EndTurn( 0 );
}
else
{
if (ubResult <= cMaxClients)
{
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, MPClientMessage[31],ubResult );
if(!( gTacticalStatus.uiFlags & INCOMBAT ))
{
gTacticalStatus.uiFlags |= INCOMBAT;
}
EndTurn( ubResult+5 );
}
else
{
// The client to which we should give the turn doe not exists. Do nothinig!
}
}
}
}
void send_fireweapon (EV_S_FIREWEAPON *SFireWeapon)
{
EV_S_FIREWEAPON sFire;
if(SFireWeapon->usSoldierID < 20)
sFire.usSoldierID = (SFireWeapon->usSoldierID)+ubID_prefix;
else
sFire.usSoldierID = SFireWeapon->usSoldierID;
sFire.sTargetGridNo=SFireWeapon->sTargetGridNo;
sFire.bTargetCubeLevel=SFireWeapon->bTargetCubeLevel;
sFire.bTargetLevel=SFireWeapon->bTargetLevel;
client->RPC("sendFIREW",(const char*)&sFire, (int)sizeof(EV_S_FIREWEAPON)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieve_fireweapon (RPCParameters *rpcParameters)
{
EV_S_FIREWEAPON* SFireWeapon = (EV_S_FIREWEAPON*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ SFireWeapon->usSoldierID ];
pSoldier->sTargetGridNo = SFireWeapon->sTargetGridNo;
pSoldier->bTargetLevel = SFireWeapon->bTargetLevel;
pSoldier->bTargetCubeLevel = SFireWeapon->bTargetCubeLevel;
FireWeapon( pSoldier, SFireWeapon->sTargetGridNo );
}
void send_door ( SOLDIERTYPE *pSoldier, INT32 sGridNo, BOOLEAN fNoAnimations )
{
if((is_server && pSoldier->ubID<120) || (!is_server && is_client && pSoldier->ubID<20) || (!is_server && !is_client) )
{
doors sDoor;
sDoor.ubID=pSoldier->ubID;
sDoor.sGridNo=sGridNo;
sDoor.fNoAnimations=fNoAnimations;
client->RPC("sendDOOR",(const char*)&sDoor, (int)sizeof(doors)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void recieve_door (RPCParameters *rpcParameters)
{
doors* sDoor = (doors*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ sDoor->ubID ];
BOOLEAN fNoAnimations = FALSE;
if ( !AllMercsLookForDoor( sDoor->sGridNo, FALSE ) )//check for los
{
fNoAnimations = TRUE;
}
HandleDoorChangeFromGridNo( pSoldier, sDoor->sGridNo, fNoAnimations );
}
void recieveCHATMSG(RPCParameters* rpcParameters)
{
chat_msg* cmsg = (chat_msg*)rpcParameters->input;
if (cGameType==MP_TYPE_TEAMDEATMATCH && cmsg->bToAll == false)
{
// If Team deathmatch && msg is allies only
if (client_teams[cmsg->client_num-1] == TEAM)
{
// only display on an ally client
ChatLogMessage( CHAT_FONT_COLOR, MSG_CHAT, cmsg->msg);
}
}
else
{
// display to all clients
ChatLogMessage( CHAT_FONT_COLOR, MSG_CHAT, cmsg->msg);
}
}
/// OJW - 20081223
// recieveDISCONNECT - Handle disconnection
void recieveDISCONNECT(RPCParameters* rpcParameters)
{
// for starters - we shouldnt get a message for ourselves :)
int cl_num = (int) *rpcParameters->input; // cl_num starts at 1
wchar_t szPlayerName[30];
memset(szPlayerName,0,30*sizeof(wchar_t));
mbstowcs( szPlayerName,client_names[cl_num-1],30);
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[47], szPlayerName );
// clear our records from this client
memset(client_names[cl_num-1],NULL,sizeof(char)*30);
memset(&client_ready[cl_num-1],0,sizeof(int));
memset(&client_teams[cl_num-1],0,sizeof(int));
if (guiCurrentScreen == MAP_SCREEN && !(gTacticalStatus.uiFlags & INCOMBAT))
{
// in the map screen and not in combat
// refresh player list to remove from the game
fDrawCharacterList = true; // set the character list to be redrawn
fTeamPanelDirty = true; // redraw the background
}
else if (guiCurrentScreen == GAME_SCREEN) // <TODO> get a more valid check that the game is in progress here
{
// in tactical screen and in combat
// kill the dead clients mercs out of the game
UINT8 iNetbTeam = (cl_num)+5;
UINT8 iubID_prefix = gTacticalStatus.Team[ iNetbTeam ].bFirstID;//over here now
// kill any alive soldiers for the disconnected team
SOLDIERTYPE *pTeamSoldier;
INT32 cnt = 0;
for ( pTeamSoldier = Menptr, cnt = 0; cnt < TOTAL_SOLDIERS; pTeamSoldier++, cnt++ )
{
if ( pTeamSoldier->bActive && pTeamSoldier->bInSector && !( pTeamSoldier->flags.uiStatusFlags & SOLDIER_DEAD ) )
{
// Checkf for any more bacguys
if ( !pTeamSoldier->aiData.bNeutral && (pTeamSoldier->bTeam == iNetbTeam ) )
{
// KIll......
pTeamSoldier->SoldierTakeDamage( ANIM_CROUCH, pTeamSoldier->stats.bLife, 100, TAKE_DAMAGE_BLOODLOSS, NOBODY, NOWHERE, 0, TRUE );
}
}
}
// if it was that teams turn then end it
if(is_server)
{
if(gTacticalStatus.ubCurrentTeam==iNetbTeam)EndTurn( iNetbTeam+1 );
}
}
}
// OJW - 20090507
// this function stores a reason from the server that we were disconnected
void recieveDISCONNECTREASON(RPCParameters *rpcParameters )
{
CHAR16* reason = (CHAR16*)rpcParameters->input;
wcscpy(gszDisconnectReason,reason);
is_connected=false;
auto_retry = false;
}
void disconnected_callback(UINT8 ubResult)
{
if (iDisconnectedScreen == MAP_SCREEN)
{
// clean up all resources and exit from map to main menu
RequestTriggerExitFromMapscreen(MAP_EXIT_TO_MAINMENU);
// game is restarted in HandleExitsFromMapScreen()
}
else if (iDisconnectedScreen == GAME_SCREEN)
{
// clean up all resources and exit from tactical to main menu
LeaveTacticalScreen(MAINMENU_SCREEN);
}
else if (iDisconnectedScreen == OPTIONS_SCREEN)
{
// Re-initialise the game
ReStartingGame();
// clean up all resources and exit from options to main menu
SetOptionsExitScreen( MAINMENU_SCREEN );
}
else if (iDisconnectedScreen == LAPTOP_SCREEN)
{
// Re-initialise the game
ReStartingGame();
// clean up all resources and exit from laptop to main menu
SetPendingNewScreen(MAINMENU_SCREEN); // Laptop screen is always cleaned up on screen change in gameloop
}
else if (iDisconnectedScreen == MP_CONNECT_SCREEN)
{
// Re-initialise the game
ReStartingGame();
// else dont clean "everything" but still exit to main menu
SetPendingNewScreen(MP_JOIN_SCREEN);
}
else
{
// Re-initialise the game
ReStartingGame();
// else dont clean "everything" but still exit to main menu
SetPendingNewScreen(MAINMENU_SCREEN);
}
}
// Gracefully handle self-disconnection of the client by Dropout
void HandleClientConnectionLost()
{
if (guiCurrentScreen != MP_SCORE_SCREEN)
{
// cleanup client
client_disconnect();
auto_retry = false;
if (is_server)
server_disconnect();
// connection lost, let user know via popup then quit to main menu
iDisconnectedScreen = guiCurrentScreen;
SGPRect CenteringRect= {0 + xResOffset, 0, SCREEN_WIDTH - xResOffset, SCREEN_HEIGHT };
if (wcscmp(gszDisconnectReason,L"")==0)
{
UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, MPClientMessage[48], guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ),disconnected_callback, &CenteringRect );
}
else
{
UINT32 giMPHMessageBox = DoMessageBox( MSG_BOX_BASIC_STYLE, gszDisconnectReason, guiCurrentScreen, ( UINT16 ) ( MSG_BOX_FLAG_OK | MSG_BOX_FLAG_USE_CENTERING_RECT ),disconnected_callback, &CenteringRect );
}
}
else
{
// Tell the score screen it can continue
gfMPSScoreScreenCanContinue = TRUE;
}
}
void sendRT(void)
{
if(!requested)
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[32] );
requested=true;
real_struct rData;
rData.bteam=netbTeam;
client->RPC("sendREAL",(const char*)&rData, (int)sizeof(real_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
}
void gotoRT(RPCParameters *rpcParameters)
{
getReal=true;//MAY NOT BE NEEDED ANY MORE
gTacticalStatus.bConsNumTurnsNotSeen = 0;
gTacticalStatus.ubCurrentTeam = OUR_TEAM;
guiPendingOverrideEvent = LA_ENDUIOUTURNLOCK;
ExitCombatMode();
fInterfacePanelDirty = DIRTYLEVEL2;
if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[34] );
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, MPClientMessage[33] );
}
getReal=false;
}
void startCombat(UINT8 ubStartingTeam)
{
sc_struct data;
if(ubStartingTeam==0)
ubStartingTeam=netbTeam;
data.ubStartingTeam=ubStartingTeam;
client->RPC("startCOMBAT",(const char*)&data, (int)sizeof(sc_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void teamwiped ( void )
{
isOwnTeamWipedOut = true;
sc_struct data;
data.ubStartingTeam=netbTeam;
client->RPC("sendWIPE",(const char*)&data, (int)sizeof(sc_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
if (is_server)
{
// end the co-op game if all player teams have wiped
if (cGameType==MP_TYPE_COOP)
{
iTeamsWiped++;
if (iTeamsWiped >= cMaxClients)
game_over();
}
}
}
void recieve_wipe (RPCParameters *rpcParameters)
{
sc_struct* data = (sc_struct*)rpcParameters->input;
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, MPClientMessage[75], data->ubStartingTeam );
if(is_server)
{
if(gTacticalStatus.ubCurrentTeam==data->ubStartingTeam)EndTurn( data->ubStartingTeam+1 );
// end the co-op game if all player teams have wiped
if (cGameType==MP_TYPE_COOP)
{
iTeamsWiped++;
if (iTeamsWiped >= cMaxClients)
game_over();
}
}
}
void send_heal (SOLDIERTYPE *pSoldier )
{
heal data;
data.ubID=pSoldier->ubID;
data.bLife=pSoldier->stats.bLife;
data.bBleeding=pSoldier->bBleeding;
client->RPC("sendHEAL",(const char*)&data, (int)sizeof(heal)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieve_heal (RPCParameters *rpcParameters)
{
heal* data = (heal*)rpcParameters->input;
UINT16 healed;
if((data->ubID >= ubID_prefix) && (data->ubID < (ubID_prefix+6)))
healed = (data->ubID - ubID_prefix);
else
healed = data->ubID;
SOLDIERTYPE *pSoldier = MercPtrs[ healed ];
pSoldier->bBleeding=data->bBleeding;
pSoldier->stats.bLife=data->bLife;
#ifdef BETAVERSION
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, L"healing..." );
#endif
}
void requestAIint(SOLDIERTYPE *pSoldier )
{
AIint data;
data.ubID=pSoldier->ubID;
data.bteam=netbTeam;
#ifdef BETAVERSION
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, L"interrupt requested" );
#endif
client->RPC("rINT",(const char*)&data, (int)sizeof(AIint)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void awardINT (RPCParameters *rpcParameters)
{
AIint* data= (AIint*)rpcParameters->input;
SOLDIERTYPE *pSoldier = MercPtrs[ data->ubID ];
StartInterrupt();
#ifdef BETAVERSION
ScreenMsg( FONT_LTGREEN, MSG_INTERFACE, L"interrupt awarded" );
#endif
}
void game_over()
{
// wait 3 seconds then notify all clients
// reset
isOwnTeamWipedOut = false;
is_game_over = true;
iScoreScreenTime = guiBaseJA2NoPauseClock + 5000;
}
// OJW - note: i dont use the internal timer callback for this because
// death notices use it and i want all those to go through normally
// so all clients have time to receive death notice of the last merc
// which creates victory condition
void send_gameover()
{
// handle the user calling the wrong function first
if (!is_game_over)
{
game_over(); // start the event
return;
}
// stop the event from firing again
is_game_over = false;
iScoreScreenTime = 0;
// notify all the clients that the game is over
client->RPC("sendGAMEOVER",(const char*)&CLIENT_NUM, (int)sizeof(CLIENT_NUM)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
void recieveGAMEOVER(RPCParameters *rpcParameters)
{
player_stats* data= (player_stats*)rpcParameters->input;
memcpy(gMPPlayerStats,data,sizeof(player_stats)*5);
// fire the score screen
StartScoreScreen();
}
//***************************
//*** client connection*****
//*************************
void connect_client ( void )
{
if(!is_client)
{
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, MPClientMessage[0] );
client = RakNetworkFactory::GetRakPeerInterface();
bool b = client->Startup(1,30,&SocketDescriptor(), 1);
//RPC's
REGISTER_STATIC_RPC(client, recievePATH);
REGISTER_STATIC_RPC(client, recieveSTANCE);
REGISTER_STATIC_RPC(client, recieveDIR);
REGISTER_STATIC_RPC(client, recieveFIRE);
REGISTER_STATIC_RPC(client, recieveHIT);
REGISTER_STATIC_RPC(client, recieveHIRE);
REGISTER_STATIC_RPC(client, recieveDISMISS);
REGISTER_STATIC_RPC(client, recieveguiPOS);
REGISTER_STATIC_RPC(client, recieveguiDIR);
REGISTER_STATIC_RPC(client, recieveEndTurn);
REGISTER_STATIC_RPC(client, recieveAI);
REGISTER_STATIC_RPC(client, recieveSTOP);
REGISTER_STATIC_RPC(client, recieveINTERRUPT);
REGISTER_STATIC_RPC(client, recieveREADY);
REGISTER_STATIC_RPC(client, recieveGUI);
REGISTER_STATIC_RPC(client, recieveSETTINGS);
REGISTER_STATIC_RPC(client, recieveDOWNLOADSTATUS);
REGISTER_STATIC_RPC(client, recieveFILE_TRANSFER_SETTINGS);
REGISTER_STATIC_RPC(client, recieveTEAMCHANGE);
REGISTER_STATIC_RPC(client, recieveEDGECHANGE);
REGISTER_STATIC_RPC(client, recieveMAPCHANGE);
REGISTER_STATIC_RPC(client, recieveBULLET);
REGISTER_STATIC_RPC(client, recieveGRENADE);
REGISTER_STATIC_RPC(client, recieveGRENADERESULT);
REGISTER_STATIC_RPC(client, recievePLANTEXPLOSIVE);
REGISTER_STATIC_RPC(client, recieveDETONATEEXPLOSIVE);
REGISTER_STATIC_RPC(client, recieveDISARMEXPLOSIVE);
REGISTER_STATIC_RPC(client, recieveSPREADEFFECT);
REGISTER_STATIC_RPC(client, recieveNEWSMOKEEFFECT);
REGISTER_STATIC_RPC(client, recieveEXPLOSIONDAMAGE);
REGISTER_STATIC_RPC(client, recieveSTATE);
REGISTER_STATIC_RPC(client, recieveDEATH);
REGISTER_STATIC_RPC(client, recievehitSTRUCT);
REGISTER_STATIC_RPC(client, recievehitWINDOW);
REGISTER_STATIC_RPC(client, recieveMISS);
REGISTER_STATIC_RPC(client, resume_turn);
REGISTER_STATIC_RPC(client, UpdateSoldierFromNetwork);
REGISTER_STATIC_RPC(client, recieve_fireweapon);
REGISTER_STATIC_RPC(client, recieve_door);
REGISTER_STATIC_RPC(client, null_team);
REGISTER_STATIC_RPC(client, gotoRT);
REGISTER_STATIC_RPC(client, recieve_wipe);
REGISTER_STATIC_RPC(client, recieve_heal);
REGISTER_STATIC_RPC(client, awardINT);
REGISTER_STATIC_RPC(client, recieveGAMEOVER);
REGISTER_STATIC_RPC(client, recieveDISCONNECT);
REGISTER_STATIC_RPC(client, recieveCHATMSG);
REGISTER_STATIC_RPC(client, requestSETID);
REGISTER_STATIC_RPC(client, recieveDISCONNECTREASON);
//***
if (b)
{
is_client=true;
}
else
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[76]);
}
is_connected=false;
}
//reconnect/connect
if( !is_connected && !is_connecting)
{
gTacticalStatus.uiFlags&= (~SHOW_ALL_MERCS );
memset( &readyteamreg , 0 , sizeof (int) * 10);
//OJW - 20081204
memset ( &client_names,NULL,sizeof(int)*4);
memset ( &client_ready,0,sizeof(int)*4);
memset ( &client_teams,0,sizeof(int)*4);
if (!gRandomStartingEdge)
memset ( &client_edges,0,sizeof(int)*5);
if (gRandomMercs)
memset (random_mercs,0,sizeof(int)*7);
memset( gMPPlayerStats,0,sizeof(player_stats)*5);
memset ( &client_downloading,0,sizeof(int)*4);
memset ( &client_progress,0,sizeof(int)*4);
memset( &gszDisconnectReason,0,sizeof(CHAR16)*255);
// ----------------------------
// Read from ja2_mp.ini
// ----------------------------
char serverIP[30];
CIniReader iniReader(JA2MP_INI_FILENAME);
strncpy(serverIP, iniReader.ReadString(JA2MP_INI_INITIAL_SECTION,JA2MP_SERVER_IP, ""), 30);
strncpy(cClientName, iniReader.ReadString(JA2MP_INI_INITIAL_SECTION,JA2MP_CLIENT_NAME, ""), 30);
strncpy(cGameDataSyncDirectory, iniReader.ReadString(JA2MP_INI_INITIAL_SECTION,JA2MP_FILE_TRANSFER_DIRECTORY, "MULTIPLAYER/Servers/My Server"), 100);
vfs::PropertyContainer props;
props.initFromIniFile(JA2MP_INI_FILENAME);
UINT16 serverPort = (UINT16)props.getIntProperty(JA2MP_INI_INITIAL_SECTION, JA2MP_SERVER_PORT, 60005);
// ----------------------------
// Save to global values
// ----------------------------
recieved_settings=0;
recieved_transfer_settings=0;
goahead = 0;
numready = 0;
readystage = 0;
status = 0;
is_game_started = false;
TEAM = 0;
isOwnTeamWipedOut = false;
wiped=0;
//disable cheating
gubCheatLevel = 0;
cMaxClients = 0; //reset server only set settings.
cDamageMultiplier=0;
cSameMercAllowed=0;
cDisableSpectatorMode=0;
if(is_server)
strcpy(serverIP, "127.0.0.1" );
//**********************
//here some nifty little tweaks
LaptopSaveInfo.guiNumberOfMercPaymentsInDays += 20;
LaptopSaveInfo.gubLastMercIndex = LAST_MERC_ID;
LaptopSaveInfo.ubLastMercAvailableId = 7;
gGameExternalOptions.fEnableSlayForever = 1;
LaptopSaveInfo.gubPlayersMercAccountStatus = 4;
// WANNE: This should fix the bug playing a "tilt" sound and showing the mini laptop graphic on the screen, when opening the laptop / option screen from map screen from map screen
gfDontStartTransitionFromLaptop = TRUE;
//**********************
// WANNE: FILE TRANSFER: Build the absolut file transfer directory path for the client
GetExecutableDirectory(client_executableDir);
strcpy(client_fileTransferDirectoryPath, client_executableDir);
strcat(client_fileTransferDirectoryPath, "\\");
strcat(client_fileTransferDirectoryPath, cGameDataSyncDirectory);
// WANNE: FILE TRANSFER
client->AttachPlugin(&fltClient);
client->SetSplitMessageProgressInterval(1);
CHAR16 tmpMessage[512];
swprintf( tmpMessage, MPClientMessage[1],serverIP );
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, tmpMessage); // we are connecting
SetConnectScreenSubMessageW( tmpMessage );
client->Connect(serverIP, serverPort, 0,0);
is_connecting = true;
}
else if (is_connecting)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[4] );
}
else if (is_connected)
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[3] );
}
}
void client_packet ( void )
{
Packet* p;
if (is_client)
{
p = client->Receive();
while(p)
{
// We got a packet, get the identifier with our handy function
packetIdentifier = GetPacketIdentifier(p);
// Check if this is a network message packet
switch (packetIdentifier)
{
case ID_DISCONNECTION_NOTIFICATION:
// Connection lost normally
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_DISCONNECTION_NOTIFICATION");
is_connected=false;
//OJW - 20081223
//Gracefully notify and disconnect the client
client->DeallocatePacket(p); // HandleClientConnectionLost shuts down the client
HandleClientConnectionLost();
//OJW - 2009
return;
break;
case ID_ALREADY_CONNECTED:
// Connection lost normally
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_ALREADY_CONNECTED");
break;
case ID_REMOTE_DISCONNECTION_NOTIFICATION: // Server telling the clients of another client disconnecting gracefully. You can manually broadcast this in a peer to peer enviroment if you want.
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_REMOTE_DISCONNECTION_NOTIFICATION");
break;
case ID_REMOTE_CONNECTION_LOST: // Server telling the clients of another client disconnecting forcefully. You can manually broadcast this in a peer to peer enviroment if you want.
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_REMOTE_CONNECTION_LOST");
break;
case ID_REMOTE_NEW_INCOMING_CONNECTION: // Server telling the clients of another client connecting. You can manually broadcast this in a peer to peer enviroment if you want.
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_REMOTE_NEW_INCOMING_CONNECTION");
break;
case ID_CONNECTION_ATTEMPT_FAILED:
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_CONNECTION_ATTEMPT_FAILED");
is_connected=false;
is_connecting=false;
//OJW - 20081224
CHAR16 msgString[512];
// handle auto retry
if (auto_retry && giNumTries > 0)
{
swprintf( msgString, MPClientMessage[49],giNumTries );
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, msgString); // we already tried once, let the user know we are retrying
SetConnectScreenSubMessageW( msgString );
}
else
{
swprintf( msgString, MPClientMessage[50],giNumTries );
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, msgString); // we already tried once, let the user know we are retrying
SetConnectScreenSubMessageW( msgString );
}
giNextRetryTime = guiBaseJA2NoPauseClock + 5000; // 5 seconds?
break;
case ID_NO_FREE_INCOMING_CONNECTIONS:
// Sorry, the server is full. I don't do anything here but
// A real app should tell the user
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_NO_FREE_INCOMING_CONNECTIONS");
break;
case ID_CONNECTION_LOST:
// Couldn't deliver a reliable packet - i.e. the other system was abnormally
// terminated
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_CONNECTION_LOST");
is_connected=false;
//OJW - 20081223
//Gracefully notify and disconnect the client
client->DeallocatePacket(p); // HandleClientConnectionLost shuts down the client
HandleClientConnectionLost();
return;
break;
case ID_CONNECTION_REQUEST_ACCEPTED:
// This tells the client they have connected
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_CONNECTION_REQUEST_ACCEPTED");
is_connected=true;
is_connecting=false;
// WANNE: FILE TRANSFER: Send all the data that is needed for the file transfer to the client,
// before the actual file transfer begins
requestFILE_TRANSFER_SETTINGS();
requestSETTINGS();
break;
case ID_NEW_INCOMING_CONNECTION:
//tells server client has connected
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_NEW_INCOMING_CONNECTION");
break;
case ID_MODIFIED_PACKET:
// Cheater!
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"ID_MODIFIED_PACKET");
break;
default:
ScreenMsg( FONT_BEIGE, MSG_MPSYSTEM, L"** a packet has been recieved for which i dont know what to do... **");
break;
}
// We're done with the packet, get more :)
client->DeallocatePacket(p);
p = client->Receive();
}
// OJW - 20081223
if (is_game_over)
{
if (guiBaseJA2NoPauseClock >= iScoreScreenTime)
{
send_gameover();
}
}
// OJW - 20090203
// Using the built in callback functions didnt work, so doing manually here
if (bClosingChatBoxToStartGame)
{
if (guiBaseJA2NoPauseClock >= iCCStartGameTime)
{
bClosingChatBoxToStartGame = false;
StartBattleChatBoxClosedCallback();
}
}
}
}
// Copied from Multiplayer.cpp
// If the first byte is ID_TIMESTAMP, then we want the 5th byte
// Otherwise we want the 1st byte
unsigned char GetPacketIdentifier(Packet *p)
{
if (p==0)
return 255;
if ((unsigned char)p->data[0] == ID_TIMESTAMP)
{
assert(p->length > sizeof(unsigned char) + sizeof(unsigned long));
return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
}
else
return (unsigned char) p->data[0];
}
void client_disconnect (void)
{
if(is_client)
{
client->DetachPlugin(&fltClient);
client->Shutdown(300);
is_client = false;
is_connected=false;
is_connecting=false;
fileTransferProgress = 0;
allowlaptop=false;
TEAM=0;
// clear local client cache
memset(client_names,0,sizeof(char)*4*30);
memset(client_edges,0,sizeof(int)*5);
memset(client_ready,0,sizeof(int)*4);
memset(client_teams,0,sizeof(int)*4);
memset(gMPPlayerStats,0,sizeof(player_stats)*5);
memset(random_mercs,0,sizeof(int)*7);
memset ( &client_downloading,0,sizeof(int)*4);
memset ( &client_progress,0,sizeof(int)*4);
// We're done with the network
RakNetworkFactory::DestroyRakPeerInterface(client);
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[77]);
#ifdef JA2BETAVERSION
MPDebugMsg( "client_disconnect()\n" );
#endif
}
else
{
ScreenMsg( FONT_LTGREEN, MSG_MPSYSTEM, MPClientMessage[78]);
}
}
//OJW - 20081204 - send a starting edge change to all the clients
void send_edgechange(int newedge)
{
// <TODO> remove this godawful hack with a proper game status check
if (can_edgechange())
{
edgechange_struct lan;
lan.client_num = CLIENT_NUM;
lan.newedge = newedge;
client->RPC("sendEDGECHANGE",(const char*)&lan, (int)sizeof(edgechange_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
// redraw the character list on the map screen
fDrawCharacterList = true;
fTeamPanelDirty = true;
client_edges[CLIENT_NUM-1] =newedge;
cStartingSectorEdge = newedge;
}
else
{
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[3]);
}
}
bool can_edgechange()
{
return (is_game_started != 1 && client_ready[CLIENT_NUM-1] == 0 && !allowlaptop && !gRandomStartingEdge);
}
//OJW - 20081204 - send a starting team change to all the clients
void send_teamchange(int newteam)
{
// <TODO> remove this godawful hack with a proper game status check
if (can_teamchange())
{
teamchange_struct lan;
lan.client_num = CLIENT_NUM;
lan.newteam = newteam;
client->RPC("sendTEAMCHANGE",(const char*)&lan, (int)sizeof(teamchange_struct)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
// redraw the character list on the map screen
fDrawCharacterList = true;
fTeamPanelDirty = true;
client_teams[lan.client_num-1] = lan.newteam;
TEAM = newteam;
}
else
{
ScreenMsg( FONT_LTBLUE, MSG_MPSYSTEM, gszMPMapscreenText[4]);
}
}
bool can_teamchange()
{
bool isGeneralTeamchangeValid = (is_game_started != 1 && client_ready[CLIENT_NUM-1] == 0 && !allowlaptop);
if (isGeneralTeamchangeValid && cGameType == MP_TYPE_TEAMDEATMATCH)
return TRUE;
else
return FALSE;
}
// 20081222 - OJW
void StartScoreScreen( void )
{
// pause game
// set main screen as score screen
LeaveTacticalScreen( MP_SCORE_SCREEN );
}
void ChatCallback( UINT8 ubResult )
{
if (ubResult == MSG_BOX_RETURN_OK && wcscmp(gszChatBoxInputString,L"") > 0)
{
chat_msg cmsg;
wchar_t szPlayerName[30];
memset(szPlayerName,0,30*sizeof(wchar_t));
mbstowcs( szPlayerName,cClientName,30);
cmsg.bToAll = gbChatSendToAll;
if (cGameType==MP_TYPE_TEAMDEATMATCH && !cmsg.bToAll)
swprintf(cmsg.msg,MPClientMessage[56], szPlayerName, gszChatBoxInputString);
else
swprintf(cmsg.msg,MPClientMessage[52], szPlayerName, gszChatBoxInputString);
cmsg.client_num = CLIENT_NUM;
// notify all of the chat message
client->RPC("sendCHATMSG",(const char*)&cmsg, (int)sizeof(chat_msg)*8, HIGH_PRIORITY, RELIABLE, 0, UNASSIGNED_SYSTEM_ADDRESS, true, 0, UNASSIGNED_NETWORK_ID,0);
}
memset(gszMsgBoxInputString,0,sizeof(gszMsgBoxInputString));
}
void OpenChatMsgBox( void )
{
DoChatBox((guiCurrentScreen == GAME_SCREEN? true : false),gzMPChatboxText[1],guiCurrentScreen,ChatCallback,NULL);
}