2007-01-15 12:09:25 -08:00
/*
This file is part of Warzone 2100.
Copyright ( C ) 1999 - 2004 Eidos Interactive
2013-01-16 12:34:57 -08:00
Copyright ( C ) 2005 - 2013 Warzone 2100 Project
2007-01-15 12:09:25 -08:00
Warzone 2100 is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
Warzone 2100 is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Warzone 2100 ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
2007-06-28 10:47:08 -07:00
/*
* Multistruct . c
*
* Alex Lee 98 , Pumpkin Studios .
*
2006-05-27 09:37:17 -07:00
* files to cope with multiplayer structure related stuff . .
2007-06-28 10:47:08 -07:00
*/
2006-11-06 06:40:07 -08:00
# include <string.h>
2006-05-27 09:37:17 -07:00
# include "lib/framework/frame.h"
# include "droid.h"
2007-06-28 10:47:08 -07:00
# include "droiddef.h"
2007-07-12 12:56:16 -07:00
# include "basedef.h"
2006-05-27 09:37:17 -07:00
# include "power.h"
2007-06-28 10:47:08 -07:00
# include "geometry.h" // for gettilestructure
# include "anim_id.h"
# include "stats.h"
# include "map.h"
# include "console.h"
# include "action.h"
# include "order.h"
# include "projectile.h"
2006-05-27 09:37:17 -07:00
# include "lib/netplay/netplay.h" // the netplay library.
2007-06-28 10:47:08 -07:00
# include "multiplay.h"
# include "multigifts.h"
2006-11-26 03:58:01 -08:00
# include "multirecv.h"
2007-03-27 11:59:03 -07:00
# include "lib/sound/audio_id.h"
2007-07-13 15:14:47 -07:00
# include "lib/sound/audio.h"
2010-02-24 11:07:56 -08:00
# include "research.h"
2012-01-16 14:10:00 -08:00
# include "qtscript.h"
2012-02-13 00:26:00 -08:00
# include "keymap.h"
2012-02-13 03:39:34 -08:00
# include "combat.h"
2007-06-28 10:47:08 -07:00
// ////////////////////////////////////////////////////////////////////////////
// structures
// ////////////////////////////////////////////////////////////////////////////
// INFORM others that a building has been completed.
2011-03-12 17:32:15 -08:00
bool SendBuildFinished ( STRUCTURE * psStruct )
2008-05-13 16:32:07 -07:00
{
2009-02-27 21:17:41 -08:00
uint8_t player = psStruct - > player ;
ASSERT ( player < MAX_PLAYERS , " invalid player %u " , player ) ;
2009-02-22 12:08:49 -08:00
2012-02-13 00:26:00 -08:00
NETbeginEncode ( NETgameQueue ( selectedPlayer ) , GAME_DEBUG_ADD_STRUCTURE ) ;
2009-02-22 12:08:49 -08:00
NETuint32_t ( & psStruct - > id ) ; // ID of building
2008-05-13 16:32:07 -07:00
2007-12-24 06:57:58 -08:00
// Along with enough info to build it (if needed)
NETuint32_t ( & psStruct - > pStructureType - > ref ) ;
2010-02-28 03:53:33 -08:00
NETPosition ( & psStruct - > pos ) ;
2009-02-27 21:17:41 -08:00
NETuint8_t ( & player ) ;
2007-12-24 06:57:58 -08:00
return NETend ( ) ;
2007-06-28 10:47:08 -07:00
}
// ////////////////////////////////////////////////////////////////////////////
2011-03-12 17:32:15 -08:00
bool recvBuildFinished ( NETQUEUE queue )
2007-06-28 10:47:08 -07:00
{
2009-02-22 12:08:49 -08:00
uint32_t structId ;
STRUCTURE * psStruct ;
2010-03-05 07:09:21 -08:00
Position pos ;
2009-02-22 12:08:49 -08:00
uint32_t type , typeindex ;
uint8_t player ;
2007-06-28 10:47:08 -07:00
2012-02-13 00:26:00 -08:00
NETbeginDecode ( queue , GAME_DEBUG_ADD_STRUCTURE ) ;
2008-01-19 10:26:17 -08:00
NETuint32_t ( & structId ) ; // get the struct id.
NETuint32_t ( & type ) ; // Kind of building.
2010-03-05 07:09:21 -08:00
NETPosition ( & pos ) ; // pos
2008-01-19 10:26:17 -08:00
NETuint8_t ( & player ) ;
NETend ( ) ;
2012-02-13 00:26:00 -08:00
ASSERT_OR_RETURN ( false , player < MAX_PLAYERS , " invalid player %u " , player ) ;
2012-02-21 11:41:15 -08:00
if ( ! getDebugMappingStatus ( ) & & bMultiPlayer )
2012-02-13 00:26:00 -08:00
{
debug ( LOG_WARNING , " Failed to add structure for player %u. " , NetPlay . players [ queue . index ] . position ) ;
return false ;
}
2009-02-27 21:17:41 -08:00
2007-12-24 06:57:58 -08:00
psStruct = IdToStruct ( structId , ANYPLAYER ) ;
if ( psStruct )
2012-01-16 14:10:00 -08:00
{
// make it complete.
2007-12-24 06:57:58 -08:00
psStruct - > currentBuildPts = psStruct - > pStructureType - > buildPoints + 1 ;
2006-05-27 09:37:17 -07:00
2007-12-24 06:57:58 -08:00
if ( psStruct - > status ! = SS_BUILT )
2007-06-28 10:47:08 -07:00
{
2010-02-22 13:41:56 -08:00
debug ( LOG_SYNC , " Synch error, structure %u was not complete, and should have been. " , structId ) ;
2007-12-24 06:57:58 -08:00
psStruct - > status = SS_BUILT ;
buildingComplete ( psStruct ) ;
2007-06-28 10:47:08 -07:00
}
2009-02-22 12:08:49 -08:00
debug ( LOG_SYNC , " Created normal building %u for player %u " , psStruct - > id , player ) ;
2008-03-24 09:51:17 -07:00
return true ;
2007-06-28 10:47:08 -07:00
}
2006-05-27 09:37:17 -07:00
2007-12-24 06:57:58 -08:00
// The building wasn't started, so we'll have to just plonk it down in the map.
2007-06-28 10:47:08 -07:00
2007-12-24 06:57:58 -08:00
// Find the structures stats
2010-12-20 09:16:45 -08:00
for ( typeindex = 0 ; typeindex < numStructureStats & & asStructureStats [ typeindex ] . ref ! = type ; typeindex + + ) { } // Find structure target
2007-06-28 10:47:08 -07:00
2007-12-24 06:57:58 -08:00
// Check for similar buildings, to avoid overlaps
2010-03-05 07:09:21 -08:00
if ( TileHasStructure ( mapTile ( map_coord ( pos . x ) , map_coord ( pos . y ) ) ) )
2007-06-28 10:47:08 -07:00
{
2007-12-24 06:57:58 -08:00
// Get the current structure
2010-03-05 07:09:21 -08:00
psStruct = getTileStructure ( map_coord ( pos . x ) , map_coord ( pos . y ) ) ;
2007-12-24 06:57:58 -08:00
if ( asStructureStats [ typeindex ] . type = = psStruct - > pStructureType - > type )
2007-06-28 10:47:08 -07:00
{
2007-12-24 06:57:58 -08:00
// Correct type, correct location, just rename the id's to sync it.. (urgh)
psStruct - > id = structId ;
psStruct - > status = SS_BUILT ;
buildingComplete ( psStruct ) ;
2009-02-22 12:08:49 -08:00
debug ( LOG_SYNC , " Created modified building %u for player %u " , psStruct - > id , player ) ;
2010-04-20 17:59:26 -07:00
# if defined (DEBUG)
2010-04-14 20:01:09 -07:00
NETlogEntry ( " structure id modified " , SYNC_FLAG , player ) ;
2010-04-20 17:59:26 -07:00
# endif
2008-03-24 09:51:17 -07:00
return true ;
2007-06-28 10:47:08 -07:00
}
}
2007-12-24 06:57:58 -08:00
// Build the structure
2010-03-05 07:09:21 -08:00
psStruct = buildStructure ( & ( asStructureStats [ typeindex ] ) , pos . x , pos . y , player , true ) ;
2008-05-13 16:32:07 -07:00
2007-12-24 06:57:58 -08:00
if ( psStruct )
2007-06-28 10:47:08 -07:00
{
2007-12-24 06:57:58 -08:00
psStruct - > id = structId ;
psStruct - > status = SS_BUILT ;
buildingComplete ( psStruct ) ;
2010-02-22 13:41:56 -08:00
debug ( LOG_SYNC , " Huge synch error, forced to create building %u for player %u " , psStruct - > id , player ) ;
2010-04-20 17:59:26 -07:00
# if defined (DEBUG)
2010-06-14 23:35:52 -07:00
NETlogEntry ( " had to plonk down a building " , SYNC_FLAG , player ) ;
2010-04-20 17:59:26 -07:00
# endif
2012-01-16 14:10:00 -08:00
triggerEventStructBuilt ( psStruct , NULL ) ;
2007-06-28 10:47:08 -07:00
}
else
{
2010-02-22 13:41:56 -08:00
debug ( LOG_SYNC , " Gigantic synch error, unable to create building for player %u " , player ) ;
2010-06-14 23:35:52 -07:00
NETlogEntry ( " had to plonk down a building, BUT FAILED! " , SYNC_FLAG , player ) ;
2007-06-28 10:47:08 -07:00
}
2008-05-13 16:32:07 -07:00
2008-03-24 09:51:17 -07:00
return false ;
2007-06-28 10:47:08 -07:00
}
// ////////////////////////////////////////////////////////////////////////////
// Inform others that a structure has been destroyed
2011-03-12 17:32:15 -08:00
bool SendDestroyStructure ( STRUCTURE * s )
2007-06-28 10:47:08 -07:00
{
2012-02-13 00:26:00 -08:00
NETbeginEncode ( NETgameQueue ( selectedPlayer ) , GAME_DEBUG_REMOVE_STRUCTURE ) ;
2007-12-24 07:15:10 -08:00
// Struct to destroy
NETuint32_t ( & s - > id ) ;
2007-06-28 10:47:08 -07:00
2007-12-24 07:15:10 -08:00
return NETend ( ) ;
2007-06-28 10:47:08 -07:00
}
// ////////////////////////////////////////////////////////////////////////////
// acknowledge the destruction of a structure, from another player.
2011-03-12 17:32:15 -08:00
bool recvDestroyStructure ( NETQUEUE queue )
2007-06-28 10:47:08 -07:00
{
2007-12-24 07:15:10 -08:00
uint32_t structID ;
STRUCTURE * psStruct ;
2007-06-28 10:47:08 -07:00
2012-02-13 00:26:00 -08:00
NETbeginDecode ( queue , GAME_DEBUG_REMOVE_STRUCTURE ) ;
2007-12-24 07:15:10 -08:00
NETuint32_t ( & structID ) ;
2007-12-27 07:14:26 -08:00
NETend ( ) ;
2008-05-13 16:32:07 -07:00
2012-02-21 11:41:15 -08:00
if ( ! getDebugMappingStatus ( ) & & bMultiPlayer )
2012-02-13 00:26:00 -08:00
{
debug ( LOG_WARNING , " Failed to remove structure for player %u. " , NetPlay . players [ queue . index ] . position ) ;
return false ;
}
2007-12-27 07:14:26 -08:00
// Struct to destory
psStruct = IdToStruct ( structID , ANYPLAYER ) ;
if ( psStruct )
{
2008-03-24 09:51:17 -07:00
turnOffMultiMsg ( true ) ;
2007-12-27 07:14:26 -08:00
// Remove the struct from remote players machine
2012-02-24 00:12:21 -08:00
destroyStruct ( psStruct , gameTime - deltaGameTime + 1 ) ; // deltaGameTime is actually 0 here, since we're between updates. However, the value of gameTime - deltaGameTime + 1 will not change when we start the next tick.
2008-03-24 09:51:17 -07:00
turnOffMultiMsg ( false ) ;
2007-12-27 07:14:26 -08:00
// NOTE: I do not think this should be here!
technologyGiveAway ( psStruct ) ;
}
2008-05-13 16:32:07 -07:00
2008-03-24 09:51:17 -07:00
return true ;
2007-06-28 10:47:08 -07:00
}
// ////////////////////////////////////////////////////////////////////////////
//lassat is firing
2011-03-12 17:32:15 -08:00
bool sendLasSat ( UBYTE player , STRUCTURE * psStruct , BASE_OBJECT * psObj )
2007-06-28 10:47:08 -07:00
{
2010-02-12 09:40:13 -08:00
NETbeginEncode ( NETgameQueue ( selectedPlayer ) , GAME_LASSAT ) ;
2007-06-28 10:47:08 -07:00
2007-12-24 06:41:25 -08:00
NETuint8_t ( & player ) ;
NETuint32_t ( & psStruct - > id ) ;
NETuint32_t ( & psObj - > id ) ; // Target
NETuint8_t ( & psObj - > player ) ; // Target player
2007-06-28 10:47:08 -07:00
2007-12-24 06:41:25 -08:00
return NETend ( ) ;
2007-06-28 10:47:08 -07:00
}
// recv lassat info on the receiving end.
2011-03-12 17:32:15 -08:00
bool recvLasSat ( NETQUEUE queue )
2007-06-28 10:47:08 -07:00
{
BASE_OBJECT * psObj ;
UBYTE player , targetplayer ;
STRUCTURE * psStruct ;
2007-12-24 06:41:25 -08:00
uint32_t id , targetid ;
2008-05-13 16:32:07 -07:00
2010-02-12 09:40:13 -08:00
NETbeginDecode ( queue , GAME_LASSAT ) ;
2007-12-24 06:41:25 -08:00
NETuint8_t ( & player ) ;
NETuint32_t ( & id ) ;
NETuint32_t ( & targetid ) ;
NETuint8_t ( & targetplayer ) ;
2008-01-19 10:26:17 -08:00
NETend ( ) ;
2008-05-13 16:32:07 -07:00
psStruct = IdToStruct ( id , player ) ;
psObj = IdToPointer ( targetid , targetplayer ) ;
2012-03-31 11:34:15 -07:00
if ( psStruct & & ! canGiveOrdersFor ( queue . index , psStruct - > player ) )
{
syncDebug ( " Wrong player. " ) ;
2013-10-04 10:16:49 -07:00
return false ;
2012-03-31 11:34:15 -07:00
}
2008-05-13 16:32:07 -07:00
2011-02-27 01:56:01 -08:00
if ( psStruct & & psObj & & psStruct - > pStructureType - > psWeapStat [ 0 ] - > weaponSubClass = = WSC_LAS_SAT )
2008-05-13 16:32:07 -07:00
{
2012-02-13 03:39:34 -08:00
// Lassats have just one weapon
unsigned firePause = weaponFirePause ( & asWeaponStats [ psStruct - > asWeaps [ 0 ] . nStat ] , player ) ;
unsigned damLevel = PERCENT ( psStruct - > body , structureBody ( psStruct ) ) ;
if ( damLevel < HEAVY_DAMAGE_LEVEL )
{
firePause + = firePause ;
}
if ( isHumanPlayer ( player ) & & gameTime - psStruct - > asWeaps [ 0 ] . lastFired < = firePause )
{
/* Too soon to fire again */
return true ^ false ; // Return value meaningless and ignored.
}
2008-05-13 16:32:07 -07:00
// Give enemy no quarter, unleash the lasat
2010-02-28 03:53:33 -08:00
proj_SendProjectile ( & psStruct - > asWeaps [ 0 ] , NULL , player , psObj - > pos , psObj , true , 0 ) ;
2010-10-27 21:42:31 -07:00
psStruct - > asWeaps [ 0 ] . lastFired = gameTime ;
2012-01-19 13:42:15 -08:00
psStruct - > asWeaps [ 0 ] . ammo = 1 ; // abducting this field for keeping track of triggers
2008-05-13 16:32:07 -07:00
// Play 5 second countdown message
2010-02-28 03:53:33 -08:00
audio_QueueTrackPos ( ID_SOUND_LAS_SAT_COUNTDOWN , psObj - > pos . x , psObj - > pos . y , psObj - > pos . z ) ;
2008-05-13 16:32:07 -07:00
}
2007-06-28 10:47:08 -07:00
2008-03-24 09:51:17 -07:00
return true ;
2007-06-28 10:47:08 -07:00
}
2010-02-23 06:04:31 -08:00
2010-02-24 11:07:56 -08:00
void sendStructureInfo ( STRUCTURE * psStruct , STRUCTURE_INFO structureInfo_ , DROID_TEMPLATE * psTempl )
2010-02-23 06:04:31 -08:00
{
uint8_t player = psStruct - > player ;
uint32_t structId = psStruct - > id ;
2010-02-24 11:07:56 -08:00
uint8_t structureInfo = structureInfo_ ;
2010-02-23 06:04:31 -08:00
2010-02-24 11:07:56 -08:00
NETbeginEncode ( NETgameQueue ( selectedPlayer ) , GAME_STRUCTUREINFO ) ;
2010-02-23 06:04:31 -08:00
NETuint8_t ( & player ) ;
2010-08-15 11:25:02 -07:00
NETuint32_t ( & structId ) ;
2010-02-24 11:07:56 -08:00
NETuint8_t ( & structureInfo ) ;
if ( structureInfo_ = = STRUCTUREINFO_MANUFACTURE )
2010-02-23 06:04:31 -08:00
{
2010-02-24 11:07:56 -08:00
uint32_t templateId = psTempl ! = NULL ? psTempl - > multiPlayerID : 0 ;
2010-02-23 09:43:18 -08:00
2010-08-15 11:25:02 -07:00
NETuint32_t ( & templateId ) ;
2010-02-23 06:04:31 -08:00
}
NETend ( ) ;
}
2010-02-24 11:07:56 -08:00
void recvStructureInfo ( NETQUEUE queue )
2010-02-23 06:04:31 -08:00
{
uint8_t player = 0 ;
uint32_t structId = 0 ;
uint32_t templateId = 0 ;
2010-02-24 11:07:56 -08:00
uint8_t structureInfo ;
2010-02-23 06:04:31 -08:00
STRUCTURE * psStruct ;
DROID_TEMPLATE * psTempl = NULL ;
2010-02-24 11:07:56 -08:00
NETbeginDecode ( queue , GAME_STRUCTUREINFO ) ;
2010-02-23 06:04:31 -08:00
NETuint8_t ( & player ) ;
2010-08-15 11:25:02 -07:00
NETuint32_t ( & structId ) ;
2010-02-24 11:07:56 -08:00
NETuint8_t ( & structureInfo ) ;
if ( structureInfo = = STRUCTUREINFO_MANUFACTURE )
2010-02-23 06:04:31 -08:00
{
2010-08-15 11:25:02 -07:00
NETuint32_t ( & templateId ) ;
2010-02-24 11:07:56 -08:00
if ( templateId ! = 0 )
{
2012-04-26 17:42:40 -07:00
// For autogames, where we want the AI to take us over, our templates are not setup... so let's use any AI's templates.
if ( ! NetPlay . players [ player ] . autoGame )
{
psTempl = IdToTemplate ( templateId , player ) ;
}
else
{
psTempl = IdToTemplate ( templateId , ANYPLAYER ) ;
}
2010-02-24 11:07:56 -08:00
if ( psTempl = = NULL )
{
debug ( LOG_SYNC , " Synch error, don't have tempate id %u, so can't change production of factory %u! " , templateId , structId ) ;
}
}
2010-02-23 06:04:31 -08:00
}
NETend ( ) ;
psStruct = IdToStruct ( structId , player ) ;
2010-11-30 14:04:22 -08:00
syncDebug ( " player%d,structId%u%c,structureInfo%u,templateId%u%c " , player , structId , psStruct = = NULL ? ' ^ ' : ' * ' , structureInfo , templateId , psTempl = = NULL ? ' ^ ' : ' * ' ) ;
2010-02-23 06:04:31 -08:00
if ( psStruct = = NULL )
{
debug ( LOG_SYNC , " Couldn't find structure %u to change production. " , structId ) ;
return ;
}
2012-03-31 11:34:15 -07:00
if ( ! canGiveOrdersFor ( queue . index , psStruct - > player ) )
{
syncDebug ( " Wrong player. " ) ;
return ;
}
2010-02-23 06:04:31 -08:00
2010-07-22 00:34:42 -07:00
CHECK_STRUCTURE ( psStruct ) ;
if ( StructIsFactory ( psStruct ) )
2010-07-20 13:57:19 -07:00
{
2011-12-05 05:22:18 -08:00
popStatusPending ( psStruct - > pFunctionality - > factory ) ;
}
else if ( psStruct - > pStructureType - > type = = REF_RESEARCH )
{
popStatusPending ( psStruct - > pFunctionality - > researchFacility ) ;
2010-07-20 13:57:19 -07:00
}
2010-02-25 08:27:09 -08:00
syncDebugStructure ( psStruct , ' < ' ) ;
2010-02-24 11:07:56 -08:00
switch ( structureInfo )
2010-02-23 06:04:31 -08:00
{
2010-10-22 09:13:04 -07:00
case STRUCTUREINFO_MANUFACTURE : structSetManufacture ( psStruct , psTempl , ModeImmediate ) ; break ;
2011-12-05 05:22:18 -08:00
case STRUCTUREINFO_CANCELPRODUCTION : cancelProduction ( psStruct , ModeImmediate , false ) ; break ;
2010-10-22 09:13:04 -07:00
case STRUCTUREINFO_HOLDPRODUCTION : holdProduction ( psStruct , ModeImmediate ) ; break ;
case STRUCTUREINFO_RELEASEPRODUCTION : releaseProduction ( psStruct , ModeImmediate ) ; break ;
case STRUCTUREINFO_HOLDRESEARCH : holdResearch ( psStruct , ModeImmediate ) ; break ;
case STRUCTUREINFO_RELEASERESEARCH : releaseResearch ( psStruct , ModeImmediate ) ; break ;
2010-02-24 11:07:56 -08:00
default :
debug ( LOG_ERROR , " Invalid structureInfo %d " , structureInfo ) ;
2010-02-23 06:04:31 -08:00
}
2010-02-25 08:27:09 -08:00
syncDebugStructure ( psStruct , ' > ' ) ;
2010-07-22 00:34:42 -07:00
CHECK_STRUCTURE ( psStruct ) ;
2010-02-23 06:04:31 -08:00
}