openclonk/src/player/C4Player.cpp

1895 lines
54 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, Matthes Bender
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
2009-05-08 13:28:41 +00:00
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
2009-05-08 13:28:41 +00:00
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
2009-05-08 13:28:41 +00:00
*/
/* Player data at runtime */
#include <C4Include.h>
#include <C4Player.h>
#include <C4Application.h>
#include <C4DefList.h>
2009-05-08 13:28:41 +00:00
#include <C4Object.h>
#include <C4ObjectInfo.h>
#include <C4Command.h>
#include <C4League.h>
#include <C4Network2Stats.h>
#include <C4MessageInput.h>
#include <C4GamePadCon.h>
#include <C4Random.h>
#include <C4Log.h>
#include <C4FullScreen.h>
#include <C4GameOverDlg.h>
#include <C4MouseControl.h>
2009-06-05 18:12:43 +00:00
#include <C4GameMessage.h>
#include <C4GraphicsResource.h>
#include <C4GraphicsSystem.h>
#include <C4Landscape.h>
#include <C4Game.h>
2009-06-12 23:09:32 +00:00
#include <C4PlayerList.h>
#include <C4GameObjects.h>
2009-06-15 22:06:37 +00:00
#include <C4GameControl.h>
#include <C4Viewport.h>
2009-05-08 13:28:41 +00:00
C4Player::C4Player() : C4PlayerInfoCore()
2010-03-28 18:58:01 +00:00
{
Filename[0] = 0;
Number = C4P_Number_None;
ID = 0;
Team = 0;
DefaultRuntimeData();
Menu.Default();
Crew.Default();
CrewInfoList.Default();
LocalControl = false;
BigIcon.Default();
Next = NULL;
fFogOfWar = true;
LeagueEvaluated = false;
GameJoinTime = 0; // overwritten in Init
pstatControls = pstatActions = NULL;
ControlCount = ActionCount = 0;
LastControlType = PCID_None;
LastControlID = 0;
pMsgBoardQuery = NULL;
pGamepad = NULL;
NoEliminationCheck = false;
Evaluated = false;
ZoomLimitMinWdt = ZoomLimitMinHgt = ZoomLimitMaxWdt = ZoomLimitMaxHgt = ZoomWdt = ZoomHgt = 0;
ZoomLimitMinVal = ZoomLimitMaxVal = ZoomVal = Fix0;
ViewLock = true;
SoundModifier.Set0();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Player::~C4Player()
2010-03-28 18:58:01 +00:00
{
ClearGraphs();
Menu.Clear();
SetSoundModifier(NULL);
while (pMsgBoardQuery)
{
C4MessageBoardQuery *pNext = pMsgBoardQuery->pNext;
delete pMsgBoardQuery;
pMsgBoardQuery = pNext;
}
delete pGamepad; pGamepad = NULL;
ClearControl();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::ObjectInCrew(C4Object *tobj)
2010-03-28 18:58:01 +00:00
{
if (!tobj) return false;
for (C4Object *cobj : Crew)
if (cobj==tobj) return true;
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ClearPointers(C4Object *pObj, bool fDeath)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Crew
while (Crew.Remove(pObj)) {}
// Cursor
if (Cursor==pObj)
2010-03-28 18:58:01 +00:00
{
// object is to be deleted; do NOT do script calls (like in Cursor->UnSelect(true))
Cursor=NULL; AdjustCursorCommand(); // also selects and eventually does a script call!
2010-03-28 18:58:01 +00:00
}
// View-Cursor
if (ViewCursor==pObj) ViewCursor = NULL;
// View
if (ViewTarget==pObj) ViewTarget=NULL;
2009-05-08 13:28:41 +00:00
// messageboard-queries
RemoveMessageBoardQuery(pObj);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::ScenarioAndTeamInit(int32_t idTeam)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4PlayerInfo *pInfo = GetInfo();
if (!pInfo) return false;
C4Team *pTeam;
if (idTeam == TEAMID_New)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// creation of a new team only if allowed by scenario
if (!Game.Teams.IsAutoGenerateTeams())
pTeam = NULL;
else
2010-03-28 18:58:01 +00:00
{
2010-01-25 04:00:59 +00:00
if ((pTeam = Game.Teams.GetGenerateTeamByID(idTeam))) idTeam = pTeam->GetID();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// uage of an existing team
2010-03-28 18:58:01 +00:00
pTeam = Game.Teams.GetTeamByID(idTeam);
}
2009-05-08 13:28:41 +00:00
C4Team *pPrevTeam = Game.Teams.GetTeamByID(Team);
// check if join to team is possible; e.g. not too many players
if (pPrevTeam != pTeam && idTeam)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (!Game.Teams.IsJoin2TeamAllowed(idTeam))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pTeam = NULL;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (!pTeam && idTeam)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
OnTeamSelectionFailed();
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// team selection OK; execute it!
if (pPrevTeam) pPrevTeam->RemovePlayerByID(pInfo->GetID());
if (pTeam) pTeam->AddPlayer(*pInfo, true);
if (!ScenarioInit()) return false;
if (!FinalInit(false)) return false;
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::Execute()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (!Status) return;
// Open/refresh team menu if desired
if (Status==PS_TeamSelection)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int32_t idSelectedTeam;
2010-01-25 04:00:59 +00:00
if ((idSelectedTeam = Game.Teams.GetForcedTeamSelection(ID)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// There's only one team left to join? Join there immediately.
if (Menu.IsActive() && Menu.GetIdentification() == C4MN_TeamSelection) Menu.TryClose(false, false);
2009-06-15 22:06:37 +00:00
if (LocalControl && !::Control.isReplay())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// team selection done through queue because TeamSelection-status may not be in sync (may be TeamSelectionPending!)
DoTeamSelection(idSelectedTeam);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if (!Menu.IsActive()) ActivateMenuTeamSelection(false);
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// during team selection: Update view to selected team, if it has a position assigned
C4MenuItem *pSelectedTeamItem;
2010-01-25 04:00:59 +00:00
if ((pSelectedTeamItem = Menu.GetSelectedItem()))
2010-03-28 18:58:01 +00:00
{
int32_t idSelectedTeam = atoi(pSelectedTeamItem->GetCommand()+8);
2009-05-08 13:28:41 +00:00
if (idSelectedTeam)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Team *pSelectedTeam;
2010-01-25 04:00:59 +00:00
if ((pSelectedTeam = Game.Teams.GetTeamByID(idSelectedTeam)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int32_t iPlrStartIndex = pSelectedTeam->GetPlrStartIndex();
if (iPlrStartIndex && Inside<int32_t>(iPlrStartIndex, 1, C4S_MaxPlayer))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Game.C4S.PlrStart[iPlrStartIndex-1].Position[0] > -1)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// player has selected a team that has a valid start position assigned
// set view to this position!
2009-06-05 15:20:41 +00:00
ViewX = Game.C4S.PlrStart[iPlrStartIndex-1].Position[0] * ::Landscape.MapZoom;
ViewY = Game.C4S.PlrStart[iPlrStartIndex-1].Position[1] * ::Landscape.MapZoom;
2009-05-08 13:28:41 +00:00
}
}
}
}
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if (Menu.IsActive() && Menu.GetIdentification() == C4MN_TeamSelection)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Menu.TryClose(false, false);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Tick1
UpdateView();
ExecuteControl();
Menu.Execute();
// ::Game.iTick35
if (!::Game.iTick35 && Status==PS_Normal)
2010-03-28 18:58:01 +00:00
{
ExecBaseProduction();
2009-05-08 13:28:41 +00:00
CheckElimination();
if (pMsgBoardQuery && LocalControl) ExecMsgBoardQueries();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Delays
if (MessageStatus>0) MessageStatus--;
if (RetireDelay>0) RetireDelay--;
if (CursorFlash>0) CursorFlash--;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::Init(int32_t iNumber, int32_t iAtClient, const char *szAtClientName,
const char *szFilename, bool fScenarioInit, class C4PlayerInfo *pInfo, C4ValueNumbers * numbers)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// safety
if (!pInfo)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
LogF("ERROR: Init player %s failed: No info!", szFilename);
assert(false);
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Status init
Status=PS_Normal;
2009-05-08 13:28:41 +00:00
Number = iNumber;
ID = pInfo->GetID();
Team = pInfo->GetTeam();
NoEliminationCheck = pInfo->IsNoEliminationCheck();
// At client
AtClient=iAtClient; SCopy(szAtClientName,AtClientName,C4MaxTitle);
if (szFilename)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Load core & crew info list
if (!Load(szFilename, !fScenarioInit)) return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// no core file present: Keep defaults
// This can happen for script players only
assert(pInfo->GetType() == C4PT_Script);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Take player name from player info; forcing overloads by the league or because of doubled player names
Name.Copy(pInfo->GetName());
// view pos init: Start at center pos
ViewX = GBackWdt/2; ViewY = GBackHgt/2;
// Scenario init
if (fScenarioInit)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// mark player join in player info list
// for non-scenarioinit, player should already be marked as joined
pInfo->SetJoined(iNumber);
// Number might have changed: Recheck list sorting before scenarioinit, which will do script calls
2009-06-12 23:09:32 +00:00
::Players.RecheckPlayerSort(this);
2009-05-08 13:28:41 +00:00
// check for a postponed scenario init, if no team is specified (post-lobby-join in network, or simply non-network)
C4Team *pTeam = NULL;
if (Team)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Game.Teams.IsAutoGenerateTeams())
pTeam = Game.Teams.GetGenerateTeamByID(Team);
else
pTeam = Game.Teams.GetTeamByID(Team);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (!pTeam && Game.Teams.IsRuntimeJoinTeamChoice())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (pInfo->GetType() == C4PT_Script)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// script player without team: This can usually not happen, because RecheckPlayerInfoTeams should have been executed
// just leave this player without the team
assert(false);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// postponed init: Chose team first
Status = PS_TeamSelection;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Init control method before scenario init, because script callbacks may need to know it!
ClearControl();
2009-05-08 13:28:41 +00:00
InitControl();
// defaultdisabled controls
Control.Init();
2009-05-08 13:28:41 +00:00
// Special: Script players may skip scenario initialization altogether, and just desire a single callback to all objects
// of a given ID
if (!pInfo->IsScenarioInitDesired())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// only initialization that's done anyway is team hostility
if (Team) SetTeamHostility();
// callback definition passed?
C4ID idCallback = pInfo->GetScriptPlayerExtraID();
C4Def *pDefCallback;
if (idCallback && (pDefCallback = C4Id2Def(idCallback)))
2010-03-28 18:58:01 +00:00
{
pDefCallback->Call(PSF_InitializeScriptPlayer, &C4AulParSet(C4VInt(Number), C4VInt(Team)));
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// player preinit: In case a team needs to be chosen first, no InitializePlayer-broadcast is done
// this callback shall give scripters a chance to do stuff like starting an intro or enabling FoW, which might need to be done
::Game.GRBroadcast(PSF_PreInitializePlayer, &C4AulParSet(C4VInt(Number)));
2009-05-08 13:28:41 +00:00
// direct init
if (Status != PS_TeamSelection) if (!ScenarioInit()) return false;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Load runtime data
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
assert(pInfo->IsJoined());
assert(numbers);
2009-05-08 13:28:41 +00:00
// (compile using DefaultRuntimeData) - also check if compilation returned sane results, i.e. ID assigned
if (!LoadRuntimeData(Game.ScenarioFile, numbers) || !ID)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// for script players in non-savegames, this is OK - it means they get restored using default values
// this happens when the users saves a scenario using the "Save scenario"-option while a script player
// was joined
if (!Game.C4S.Head.SaveGame && pInfo->GetType() == C4PT_Script)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Number = pInfo->GetInGameNumber();
ColorDw = pInfo->GetColor();
ID = pInfo->GetID();
Team = pInfo->GetTeam();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Reset values default-overriden by old runtime data load (safety?)
if (Number==C4P_Number_None) Number=iNumber;
if (szFilename) SCopy(Config.AtUserDataPath(szFilename),Filename); else *Filename='\0';
// NET2: Direct joins always send accurate client IDs and names in params
// do not overwrite them with savegame data, because players might as well
// change clients
// (only call should be savegame recreation by C4PlayerInfoList::RecreatePlayers)
AtClient = iAtClient;
SCopy(szAtClientName,AtClientName,C4MaxTitle);
// Number might have changed: Recheck list sorting
2009-06-12 23:09:32 +00:00
::Players.RecheckPlayerSort(this);
2009-05-08 13:28:41 +00:00
// Init control after loading runtime data, because control init will overwrite some of the values
InitControl();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// store game joining time
GameJoinTime = Game.Time;
// Init FoW-viewobjects: NO_OWNER-FoW-repellers might need to be added
for (C4Object *pObj : Objects)
2010-03-28 18:58:01 +00:00
{
if ((pObj->lightRange || pObj->lightFadeoutRange) && pObj->Owner == NO_OWNER)
pObj->UpdateLight();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// init graphs
if (Game.pNetworkStatistics) CreateGraphs();
// init sound mod
SetSoundModifier(SoundModifier.getPropList());
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::Save()
2010-03-28 18:58:01 +00:00
{
C4Group hGroup;
2009-05-08 13:28:41 +00:00
// Regular player saving need not be done for script players
if (GetType() == C4PT_Script) return false;
2009-05-08 13:28:41 +00:00
// Log
LogF(LoadResStr("IDS_PRC_SAVEPLR"), Config.AtRelativePath(Filename));
::GraphicsSystem.MessageBoard->EnsureLastMessage();
2009-05-08 13:28:41 +00:00
// copy player to save somewhere else
char szPath[_MAX_PATH + 1];
SCopy(Config.AtTempPath(C4CFN_TempPlayer), szPath, _MAX_PATH);
MakeTempFilename(szPath);
// For local players, we save over the old player file, as there might
// be all kinds of non-essential stuff in it. For non-local players, we
// just re-create it every time (it's temporary anyway).
if (LocalControl)
{
// But make sure to copy it first so full hard (flgr stupid) disks
// won't corrupt any player files...
C4Group_CopyItem(Filename, szPath);
}
else
{
// For non-local players, we can actually use the loaded definition
// list to strip out all non-existant definitions. This is only valid
// because we know the file to be temporary.
CrewInfoList.Strip(::Definitions);
}
2009-05-08 13:28:41 +00:00
// Open group
if (!hGroup.Open(szPath,true))
return false;
2009-05-08 13:28:41 +00:00
// Save
if (!Save(hGroup, false, !LocalControl))
{ hGroup.Close(); return false; }
2009-05-08 13:28:41 +00:00
// Close group
if (!hGroup.Close()) return false;
// resource
2009-06-05 15:19:46 +00:00
C4Network2Res::Ref pRes = ::Network.ResList.getRefRes(Filename),
2010-03-28 18:58:01 +00:00
pDRes = NULL;
bool fOfficial = pRes && ::Control.isCtrlHost();
2010-03-28 18:58:01 +00:00
if (pRes) pDRes = pRes->Derive();
2009-05-08 13:28:41 +00:00
// move back
if (ItemExists(Filename)) EraseItem(Filename);
if (!C4Group_MoveItem(szPath, Filename)) return false;
// finish update
2010-03-28 18:58:01 +00:00
if (pDRes && fOfficial) pDRes->FinishDerive();
2009-05-08 13:28:41 +00:00
// Success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::Save(C4Group &hGroup, bool fSavegame, bool fStoreTiny)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Save core
if (!C4PlayerInfoCore::Save(hGroup))
return false;
2009-05-08 13:28:41 +00:00
// Save crew
2009-06-05 16:46:37 +00:00
C4DefList *pDefs = &::Definitions;
2009-05-08 13:28:41 +00:00
if (!CrewInfoList.Save(hGroup, fSavegame, fStoreTiny, pDefs))
{ hGroup.Close(); return false; }
2009-05-08 13:28:41 +00:00
// Sort
hGroup.Sort(C4FLS_Player);
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::PlaceReadyCrew(int32_t tx1, int32_t tx2, int32_t ty, C4Object *FirstBase)
2010-03-28 18:58:01 +00:00
{
int32_t cnt,ctx,cty;
C4Object *nobj;
C4ObjectInfo *pInfo;
C4Def *pDef;
2009-05-08 13:28:41 +00:00
// Place crew
int32_t iCount;
C4ID id;
for (cnt=0; (id=Game.C4S.PlrStart[PlrStartIndex].ReadyCrew.GetID(cnt,&iCount)); cnt++)
2010-03-28 18:58:01 +00:00
{
// Minimum one clonk if empty id
iCount = std::max<int32_t>(iCount,1);
for (int32_t cnt2=0; cnt2<iCount; cnt2++)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Select member from home crew, add new if necessary
while (!(pInfo=CrewInfoList.GetIdle(id,::Definitions)))
if (!CrewInfoList.New(id,&::Definitions))
2009-05-08 13:28:41 +00:00
break;
// Safety
2009-05-08 13:28:41 +00:00
if (!pInfo || !(pDef=C4Id2Def(pInfo->id))) continue;
// Crew placement location
2009-05-08 13:28:41 +00:00
ctx=tx1+Random(tx2-tx1); cty=ty;
if (!Game.C4S.PlrStart[PlrStartIndex].EnforcePosition)
FindSolidGround(ctx,cty,pDef->Shape.Wdt*3);
// Create object
2010-01-25 04:00:59 +00:00
if ((nobj=Game.CreateInfoObject(pInfo,Number,ctx,cty)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Add object to crew
Crew.Add(nobj, C4ObjectList::stNone);
2009-05-08 13:28:41 +00:00
// add visibility range
nobj->SetLightRange(C4FOW_DefLightRangeX, C4FOW_DefLightFadeoutRangeX);
2009-05-08 13:28:41 +00:00
// If base is present, enter base
if (FirstBase) { nobj->Enter(FirstBase); nobj->SetCommand(C4CMD_Exit); }
// OnJoinCrew callback
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
#if !defined(DEBUGREC_RECRUITMENT)
C4DebugRecOff DbgRecOff;
2009-05-08 13:28:41 +00:00
#endif
C4AulParSet parset(C4VInt(Number));
nobj->Call(PSF_OnJoinCrew, &parset);
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::PlaceReadyBase(int32_t &tx, int32_t &ty, C4Object **pFirstBase)
2010-03-28 18:58:01 +00:00
{
int32_t cnt,cnt2,ctx,cty;
C4Def *def;
C4ID cid;
C4Object *cbase;
// Create ready base structures
for (cnt=0; (cid=Game.C4S.PlrStart[PlrStartIndex].ReadyBase.GetID(cnt)); cnt++)
2010-03-28 18:58:01 +00:00
{
if ((def=C4Id2Def(cid)))
for (cnt2=0; cnt2<Game.C4S.PlrStart[PlrStartIndex].ReadyBase.GetCount(cnt); cnt2++)
2010-03-28 18:58:01 +00:00
{
ctx=tx; cty=ty;
2009-05-08 13:28:41 +00:00
if (Game.C4S.PlrStart[PlrStartIndex].EnforcePosition
|| FindConSiteSpot(ctx,cty,def->Shape.Wdt,def->Shape.Hgt,20))
if ((cbase=Game.CreateObjectConstruction(C4Id2Def(cid),NULL,Number,ctx,cty,FullCon,true)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// FirstBase
if (!(*pFirstBase)) if ((cbase->Def->Entrance.Wdt>0) && (cbase->Def->Entrance.Hgt>0))
2010-03-28 18:58:01 +00:00
{ *pFirstBase=cbase; tx=(*pFirstBase)->GetX(); ty=(*pFirstBase)->GetY(); }
}
}
}
}
2009-05-08 13:28:41 +00:00
void C4Player::PlaceReadyVehic(int32_t tx1, int32_t tx2, int32_t ty, C4Object *FirstBase)
2010-03-28 18:58:01 +00:00
{
int32_t cnt,cnt2,ctx,cty;
C4Def *def; C4ID cid; C4Object *cobj;
for (cnt=0; (cid=Game.C4S.PlrStart[PlrStartIndex].ReadyVehic.GetID(cnt)); cnt++)
2010-03-28 18:58:01 +00:00
{
if ((def=C4Id2Def(cid)))
for (cnt2=0; cnt2<Game.C4S.PlrStart[PlrStartIndex].ReadyVehic.GetCount(cnt); cnt2++)
2010-03-28 18:58:01 +00:00
{
ctx=tx1+Random(tx2-tx1); cty=ty;
2009-05-08 13:28:41 +00:00
if (!Game.C4S.PlrStart[PlrStartIndex].EnforcePosition)
FindLevelGround(ctx,cty,def->Shape.Wdt,6);
if ((cobj=Game.CreateObject(cid,NULL,Number,ctx,cty)))
2010-03-28 18:58:01 +00:00
{
if (FirstBase) // First base overrides target location
{ cobj->Enter(FirstBase); cobj->SetCommand(C4CMD_Exit); }
}
2010-03-28 18:58:01 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::PlaceReadyMaterial(int32_t tx1, int32_t tx2, int32_t ty, C4Object *FirstBase)
2010-03-28 18:58:01 +00:00
{
int32_t cnt,cnt2,ctx,cty;
C4Def *def; C4ID cid;
// In base
if (FirstBase)
2010-03-28 18:58:01 +00:00
{
FirstBase->CreateContentsByList(Game.C4S.PlrStart[PlrStartIndex].ReadyMaterial);
2010-03-28 18:58:01 +00:00
}
// Outside
else
2010-03-28 18:58:01 +00:00
{
for (cnt=0; (cid=Game.C4S.PlrStart[PlrStartIndex].ReadyMaterial.GetID(cnt)); cnt++)
2010-03-28 18:58:01 +00:00
{
if ((def=C4Id2Def(cid)))
for (cnt2=0; cnt2<Game.C4S.PlrStart[PlrStartIndex].ReadyMaterial.GetCount(cnt); cnt2++)
2010-03-28 18:58:01 +00:00
{
ctx=tx1+Random(tx2-tx1); cty=ty;
2009-05-08 13:28:41 +00:00
if (!Game.C4S.PlrStart[PlrStartIndex].EnforcePosition)
FindSolidGround(ctx,cty,def->Shape.Wdt);
Game.CreateObject(cid,NULL,Number,ctx,cty);
2010-03-28 18:58:01 +00:00
}
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::ScenarioInit()
2010-03-28 18:58:01 +00:00
{
int32_t ptx,pty;
2009-05-08 13:28:41 +00:00
// player start index by team, if specified. Otherwise by player number
PlrStartIndex = Number % C4S_MaxPlayer;
C4Team *pTeam; int32_t i;
2010-01-25 04:00:59 +00:00
if (Team && (pTeam = Game.Teams.GetTeamByID(Team))) if ((i=pTeam->GetPlrStartIndex())) PlrStartIndex=i-1;
2009-05-08 13:28:41 +00:00
C4PlayerInfo *pInfo = GetInfo();
if (!pInfo) { assert(false); LogF("Internal error: ScenarioInit for ghost player %s!", GetName()); return false; }
2009-05-08 13:28:41 +00:00
// set color by player info class
// re-setting, because runtime team choice may have altered color
ColorDw = pInfo->GetColor();
// any team selection is over now
Status = PS_Normal;
// Wealth, home base materials, abilities
Wealth=Game.C4S.PlrStart[PlrStartIndex].Wealth.Evaluate();
BaseMaterial=Game.C4S.PlrStart[PlrStartIndex].BaseMaterial;
BaseMaterial.ConsolidateValids(::Definitions);
BaseProduction=Game.C4S.PlrStart[PlrStartIndex].BaseProduction;
BaseProduction.ConsolidateValids(::Definitions);
Knowledge=Game.C4S.PlrStart[PlrStartIndex].BuildKnowledge;
Knowledge.ConsolidateValids(::Definitions);
2009-05-08 13:28:41 +00:00
// Starting position
ptx = Game.C4S.PlrStart[PlrStartIndex].Position[0];
pty = Game.C4S.PlrStart[PlrStartIndex].Position[1];
2009-05-08 13:28:41 +00:00
// Zoomed position
if (ptx>-1) ptx = Clamp<int32_t>( ptx * Game.C4S.Landscape.MapZoom.Evaluate(), 0, GBackWdt-1 );
if (pty>-1) pty = Clamp<int32_t>( pty * Game.C4S.Landscape.MapZoom.Evaluate(), 0, GBackHgt-1 );
2009-05-08 13:28:41 +00:00
// Standard position (PrefPosition)
if (ptx<0)
if (Game.StartupPlayerCount>=2)
2010-03-28 18:58:01 +00:00
{
int32_t iMaxPos=Game.StartupPlayerCount;
// Map preferred position to available positions
int32_t iStartPos=Clamp(PrefPosition*iMaxPos/C4P_MaxPosition,0,iMaxPos-1);
int32_t iPosition=iStartPos;
// Distribute according to availability
while (::Players.PositionTaken(iPosition))
{
++iPosition;
iPosition %= iMaxPos;
if (iPosition == iStartPos)
break;
}
2009-05-08 13:28:41 +00:00
Position=iPosition;
// Set x position
ptx=Clamp(16+Position*(GBackWdt-32)/(iMaxPos-1),0,GBackWdt-16);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// All-random position
if (ptx<0) ptx=16+Random(GBackWdt-32);
if (pty<0) pty=16+Random(GBackHgt-32);
2009-05-08 13:28:41 +00:00
// Place to solid ground
if (!Game.C4S.PlrStart[PlrStartIndex].EnforcePosition)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Use nearest above-ground...
FindSolidGround(ptx,pty,30);
// Might have hit a small lake, or similar: Seach a real site spot from here
FindConSiteSpot(ptx, pty, 30, 50, 400);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Place Readies
2009-05-08 13:28:41 +00:00
C4Object *FirstBase = NULL;
PlaceReadyBase(ptx,pty,&FirstBase);
PlaceReadyMaterial(ptx-10,ptx+10,pty,FirstBase);
PlaceReadyVehic(ptx-30,ptx+30,pty,FirstBase);
PlaceReadyCrew(ptx-30,ptx+30,pty,FirstBase);
2009-05-08 13:28:41 +00:00
// set initial hostility by team info
if (Team) SetTeamHostility();
// Scenario script initialization
::Game.GRBroadcast(PSF_InitializePlayer, &C4AulParSet(C4VInt(Number),
2010-03-28 18:58:01 +00:00
C4VInt(ptx),
C4VInt(pty),
C4VObj(FirstBase),
C4VInt(Team),
C4VPropList(C4Id2Def(GetInfo()->GetScriptPlayerExtraID()))));
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::FinalInit(bool fInitialScore)
2010-03-28 18:58:01 +00:00
{
if (!Status) return true;
2009-05-08 13:28:41 +00:00
// Init player's mouse control
if (LocalControl)
if (MouseControl)
2009-06-05 15:20:07 +00:00
::MouseControl.Init(Number);
2009-05-08 13:28:41 +00:00
// Set initial score
if (fInitialScore)
{
InitialScore=CurrentScore;
}
2009-05-08 13:28:41 +00:00
// Cursor
if (!Cursor) AdjustCursorCommand();
// Update counts, pointers, views
Execute();
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::SetFoW(bool fEnable)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// set flag
fFogOfWar = fEnable;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::DoWealth(int32_t iChange)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (LocalControl)
2010-03-28 18:58:01 +00:00
{
if (iChange>0) StartSoundEffect("UI::Cash");
if (iChange<0) StartSoundEffect("UI::UnCash");
2010-03-28 18:58:01 +00:00
}
2009-12-15 19:30:00 +00:00
SetWealth(Wealth+iChange);
return true;
2010-03-28 18:58:01 +00:00
}
2009-12-15 19:30:00 +00:00
bool C4Player::SetWealth(int32_t iVal)
2010-03-28 18:58:01 +00:00
{
if (iVal == Wealth) return true;
2009-12-15 19:30:00 +00:00
Wealth=Clamp<int32_t>(iVal,0,1000000000);
2009-12-15 19:30:00 +00:00
::Game.GRBroadcast(PSF_OnWealthChanged,&C4AulParSet(C4VInt(Number)));
2009-12-15 19:30:00 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::SetViewMode(int32_t iMode, C4Object *pTarget, bool immediate_position)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// safe back
ViewMode=iMode; ViewTarget=pTarget;
// immediate position set desired?
if (immediate_position)
{
UpdateView();
C4Viewport *vp = ::Viewports.GetViewport(this->Number);
if (vp) vp->AdjustPosition(true);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ResetCursorView(bool immediate_position)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// reset view to cursor if any cursor exists
if (!ViewCursor && !Cursor) return;
SetViewMode(C4PVM_Cursor, NULL, immediate_position);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::Evaluate()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do not evaluate twice
if (Evaluated) return;
const int32_t SuccessBonus=100;
2009-05-08 13:28:41 +00:00
// Set last round
LastRound.Title = Game.ScenarioTitle;
time(reinterpret_cast<time_t *>(&LastRound.Date));
LastRound.Duration = Game.Time;
LastRound.Won = !Eliminated;
// Melee: personal value gain score ...check ::Objects(C4D_Goal)
if (Game.C4S.Game.IsMelee()) LastRound.Score = std::max<int32_t>(CurrentScore-InitialScore,0);
2009-05-08 13:28:41 +00:00
// Cooperative: shared score
else LastRound.Score = std::max(::Players.AverageScoreGain(),0);
2009-05-08 13:28:41 +00:00
LastRound.Level = 0; // unknown...
LastRound.Bonus = SuccessBonus * LastRound.Won;
LastRound.FinalScore = LastRound.Score + LastRound.Bonus;
LastRound.TotalScore = TotalScore + LastRound.FinalScore;
2009-05-08 13:28:41 +00:00
// Update player
Rounds++;
if (LastRound.Won) RoundsWon++; else RoundsLost++;
TotalScore=LastRound.TotalScore;
TotalPlayingTime+=Game.Time-GameJoinTime;
2009-05-08 13:28:41 +00:00
// Crew
2009-05-08 13:28:41 +00:00
CrewInfoList.Evaluate();
// league
if (Game.Parameters.isLeague())
EvaluateLeague(false, Game.GameOver && !Eliminated);
2009-05-08 13:28:41 +00:00
// Player is now evaluated
Evaluated = true;
// round results
Game.RoundResults.EvaluatePlayer(this);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::Surrender()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Surrendered) return;
Surrendered=true;
Eliminated=true;
2009-05-08 13:28:41 +00:00
RetireDelay=C4RetireDelay;
StartSoundEffect("UI::Eliminated");
2009-05-08 13:28:41 +00:00
Log(FormatString(LoadResStr("IDS_PRC_PLRSURRENDERED"),GetName()).getData());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::SetHostility(int32_t iOpponent, int32_t hostile, bool fSilent)
2010-03-28 18:58:01 +00:00
{
assert(hostile == 0 || hostile == 1);
2009-05-08 13:28:41 +00:00
// Check opponent valid
C4Player *opponent = ::Players.Get(iOpponent);
if (!opponent || opponent == this)
return false;
2009-05-08 13:28:41 +00:00
// Set hostility
if (hostile)
Hostility.insert(opponent);
else
Hostility.erase(opponent);
2009-05-08 13:28:41 +00:00
// no announce in first frame, or if specified
if (!Game.FrameCounter || fSilent) return true;
2009-05-08 13:28:41 +00:00
// Announce
StartSoundEffect("UI::Trumpet");
Log(FormatString(LoadResStr(hostile ? "IDS_PLR_HOSTILITY" : "IDS_PLR_NOHOSTILITY"),
2010-03-28 18:58:01 +00:00
GetName(),opponent->GetName()).getData());
2009-05-08 13:28:41 +00:00
// Success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::IsHostileTowards(const C4Player *plr) const
{
assert(plr);
if (!plr) return false;
return Hostility.find(plr) != Hostility.end();
}
C4Object* C4Player::GetHiExpActiveCrew()
2010-03-28 18:58:01 +00:00
{
C4Object *hiexp=NULL;
2009-05-08 13:28:41 +00:00
int32_t iHighestExp=-2, iExp;
for (C4Object *cobj : Crew)
{
2009-05-08 13:28:41 +00:00
if (!cobj->CrewDisabled)
{
if (cobj->Info) iExp = cobj->Info->Experience; else iExp=-1;
if (!hiexp || (iExp>iHighestExp))
2010-03-28 18:58:01 +00:00
{
hiexp=cobj;
iHighestExp=iExp;
2010-03-28 18:58:01 +00:00
}
}
}
2009-05-08 13:28:41 +00:00
return hiexp;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Object* C4Player::GetHiRankActiveCrew()
2010-03-28 18:58:01 +00:00
{
C4Object *hirank=NULL;
2009-05-08 13:28:41 +00:00
int32_t iHighestRank=-2, iRank;
for (C4Object *cobj : Crew)
{
2009-05-08 13:28:41 +00:00
if (!cobj->CrewDisabled)
{
if (cobj->Info) iRank = cobj->Info->Rank; else iRank=-1;
if (!hirank || (iRank>iHighestRank))
2010-03-28 18:58:01 +00:00
{
hirank=cobj;
iHighestRank=iRank;
2010-03-28 18:58:01 +00:00
}
}
}
2009-05-08 13:28:41 +00:00
return hirank;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CheckCrewExPromotion()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Object *hirank;
if ((hirank=GetHiRankActiveCrew()))
2009-05-08 13:28:41 +00:00
if (hirank->Info)
if (hirank->Info->Rank<1) // No Fähnrich -> except. promo.
if ((hirank=GetHiExpActiveCrew()))
hirank->Promote(1,true,false);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::SetTeamHostility()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// team only
if (!Team) return;
// set hostilities
2009-06-12 23:09:32 +00:00
for (C4Player *pPlr = ::Players.First; pPlr; pPlr = pPlr->Next)
2009-05-08 13:28:41 +00:00
if (pPlr != this)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fHostile = (pPlr->Team != Team);
SetHostility(pPlr->Number, fHostile, true);
pPlr->SetHostility(Number, fHostile, true);
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
bool C4Player::Message(const char *szMsg)
2010-03-28 18:58:01 +00:00
{
if (!szMsg) return false;
2009-05-08 13:28:41 +00:00
SCopy(szMsg,MessageBuf,256);
MessageStatus=SLen(szMsg)*2;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::Load(const char *szFilename, bool fSavegame)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Group hGroup;
// Open group
if (!Reloc.Open(hGroup, szFilename)) return false;
2011-03-12 12:55:05 +00:00
// Remember filename
SCopy(hGroup.GetFullName().getData(), Filename, _MAX_PATH);
2009-05-08 13:28:41 +00:00
// Load core
if (!C4PlayerInfoCore::Load(hGroup))
{ hGroup.Close(); return false; }
2009-05-08 13:28:41 +00:00
// Load BigIcon
if (hGroup.FindEntry(C4CFN_BigIcon)) BigIcon.Load(hGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0);
// Load crew info list
CrewInfoList.Load(hGroup);
2009-05-08 13:28:41 +00:00
// Close group
hGroup.Close();
// Success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::Strip(const char *szFilename, bool fAggressive)
2010-03-28 18:58:01 +00:00
{
2012-01-29 02:17:11 +00:00
// Open group
2009-05-08 13:28:41 +00:00
C4Group Grp;
2010-03-28 18:58:01 +00:00
if (!Grp.Open(szFilename))
return false;
2009-05-08 13:28:41 +00:00
// Which type of stripping?
2010-03-28 18:58:01 +00:00
if (!fAggressive)
{
2009-05-08 13:28:41 +00:00
// remove bigicon, if the file size is too large
size_t iBigIconSize=0;
if (Grp.FindEntry(C4CFN_BigIcon, NULL, &iBigIconSize))
if (iBigIconSize > C4NetResMaxBigicon*1024)
Grp.Delete(C4CFN_BigIcon);
Grp.Close();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Load info core and crew info list
C4PlayerInfoCore PlrInfoCore;
C4ObjectInfoList CrewInfoList;
if (!PlrInfoCore.Load(Grp) || !CrewInfoList.Load(Grp))
return false;
2009-05-08 13:28:41 +00:00
// Strip crew info list (remove object infos that are invalid for this scenario)
2009-06-05 16:46:37 +00:00
CrewInfoList.Strip(::Definitions);
2009-05-08 13:28:41 +00:00
// Create a new group that receives the bare essentials
Grp.Close();
2010-03-28 18:58:01 +00:00
if (!EraseItem(szFilename) ||
!Grp.Open(szFilename, true))
return false;
2009-05-08 13:28:41 +00:00
// Save info core & crew info list to newly-created file
2010-03-28 18:58:01 +00:00
if (!PlrInfoCore.Save(Grp) || !CrewInfoList.Save(Grp, true, true, &::Definitions))
return false;
2009-05-08 13:28:41 +00:00
Grp.Close();
}
2010-03-28 18:58:01 +00:00
return true;
}
2009-05-08 13:28:41 +00:00
void C4Player::DrawHostility(C4Facet &cgo, int32_t iIndex)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Player *pPlr;
2010-01-25 04:00:59 +00:00
if ((pPlr=::Players.GetByIndex(iIndex)))
2010-03-28 18:58:01 +00:00
{
2010-02-22 17:35:51 +00:00
::GraphicsResource.fctCrewClr.DrawClr(cgo, true, pPlr->ColorDw);
2009-05-08 13:28:41 +00:00
// Other player and hostile
if (pPlr != this)
if (Hostility.find(pPlr) != Hostility.end())
::GraphicsResource.fctMenu.GetPhase(7).Draw(cgo);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::MakeCrewMember(C4Object *pObj, bool fForceInfo, bool fDoCalls)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4ObjectInfo *cInf = NULL;
if (!pObj || !pObj->Def->CrewMember || !pObj->Status) return false;
2009-05-08 13:28:41 +00:00
// only if info is not yet assigned
if (!pObj->Info && fForceInfo)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Find crew info by name
if (pObj->nInfo)
cInf = CrewInfoList.GetIdle(pObj->nInfo.getData());
// Find crew info by id
if (!cInf)
2009-06-05 16:46:37 +00:00
while (!( cInf = CrewInfoList.GetIdle(pObj->id,::Definitions) ))
if (!CrewInfoList.New(pObj->id,&::Definitions))
return false;
2009-05-08 13:28:41 +00:00
// Set object info
pObj->Info = cInf;
pObj->SetName(cInf->Name);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Add to crew
if (!Crew.GetLink(pObj))
Crew.Add(pObj, C4ObjectList::stNone);
2009-05-08 13:28:41 +00:00
// add light
if (!pObj->lightRange)
pObj->SetLightRange(C4FOW_DefLightRangeX, C4FOW_DefLightFadeoutRangeX);
else
pObj->UpdateLight();
2009-05-08 13:28:41 +00:00
// controlled by the player
pObj->Controller = Number;
// OnJoinCrew callback
if (fDoCalls)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4AulParSet parset(C4VInt(Number));
pObj->Call(PSF_OnJoinCrew, &parset);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ExecuteControl()
2010-03-28 18:58:01 +00:00
{
Control.Execute();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::AdjustCursorCommand()
2010-03-28 18:58:01 +00:00
{
// Reset view
ResetCursorView();
// Default cursor to hirank clonk
if (!Cursor || Cursor->CrewDisabled)
2009-05-08 13:28:41 +00:00
{
C4Object *pHiRank = GetHiRankActiveCrew();
if (!pHiRank)
return;
SetCursor(pHiRank,true);
2009-05-08 13:28:41 +00:00
UpdateView();
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::ObjectCommand(int32_t iCommand, C4Object *pTarget, int32_t iX, int32_t iY, C4Object *pTarget2, C4Value iData, int32_t iMode)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Eliminated
if (Eliminated) return false;
// Hide startup
if (ShowStartup) ShowStartup=false;
2009-05-08 13:28:41 +00:00
// Always apply to cursor, even if it's not in the crew
if (Cursor && Cursor->Status && Cursor != pTarget)
ObjectCommand2Obj(Cursor, iCommand, pTarget, iX, iY, pTarget2, iData, iMode);
2009-05-08 13:28:41 +00:00
// Success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ObjectCommand2Obj(C4Object *cObj, int32_t iCommand, C4Object *pTarget, int32_t iX, int32_t iY, C4Object *pTarget2, C4Value iData, int32_t iMode)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// forward to object
if (iMode & C4P_Command_Append) cObj->AddCommand(iCommand,pTarget,iX,iY,0,pTarget2,true,iData,true,0,NULL,C4CMD_Mode_Base); // append: by Shift-click and for dragging of multiple objects (all independant; thus C4CMD_Mode_Base)
else if (iMode & C4P_Command_Add) cObj->AddCommand(iCommand,pTarget,iX,iY,0,pTarget2,true,iData,false,0,NULL,C4CMD_Mode_Base); // append: by context menu and keyboard throw command (all independant; thus C4CMD_Mode_Base)
else if (iMode & C4P_Command_Set) cObj->SetCommand(iCommand,pTarget,iX,iY,pTarget2,true,iData);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
assert(ID);
pComp->Value(mkNamingAdapt(Status, "Status", 0));
pComp->Value(mkNamingAdapt(AtClient, "AtClient", C4ClientIDUnknown));
pComp->Value(mkNamingAdapt(toC4CStr(AtClientName),"AtClientName", "Local"));
pComp->Value(mkNamingAdapt(Number, "Index", C4P_Number_None));
pComp->Value(mkNamingAdapt(ID, "ID", 0));
pComp->Value(mkNamingAdapt(Eliminated, "Eliminated", 0));
pComp->Value(mkNamingAdapt(Surrendered, "Surrendered", 0));
pComp->Value(mkNamingAdapt(Evaluated, "Evaluated", false));
pComp->Value(mkNamingAdapt(ColorDw, "ColorDw", 0u));
pComp->Value(mkNamingAdapt(Position, "Position", 0));
pComp->Value(mkNamingAdapt(ViewMode, "ViewMode", C4PVM_Cursor));
pComp->Value(mkNamingAdapt(ViewX, "ViewX", 0));
pComp->Value(mkNamingAdapt(ViewY, "ViewY", 0));
2013-05-23 23:03:29 +00:00
pComp->Value(mkNamingAdapt(ViewLock, "ViewLock", true));
pComp->Value(mkNamingAdapt(ZoomLimitMinWdt, "ZoomLimitMinWdt", 0));
pComp->Value(mkNamingAdapt(ZoomLimitMinHgt, "ZoomLimitMinHgt", 0));
pComp->Value(mkNamingAdapt(ZoomLimitMaxWdt, "ZoomLimitMaxWdt", 0));
pComp->Value(mkNamingAdapt(ZoomLimitMaxHgt, "ZoomLimitMaxHgt", 0));
pComp->Value(mkNamingAdapt(ZoomWdt, "ZoomWdt", 0));
pComp->Value(mkNamingAdapt(ZoomHgt, "ZoomHgt", 0));
pComp->Value(mkNamingAdapt(ZoomLimitMinVal, "ZoomLimitMinVal", Fix0));
pComp->Value(mkNamingAdapt(ZoomLimitMaxVal, "ZoomLimitMaxVal", Fix0));
pComp->Value(mkNamingAdapt(ZoomVal, "ZoomVal", Fix0));
pComp->Value(mkNamingAdapt(fFogOfWar, "FogOfWar", false));
pComp->Value(mkNamingAdapt(ShowStartup, "ShowStartup", false));
pComp->Value(mkNamingAdapt(Wealth, "Wealth", 0));
pComp->Value(mkNamingAdapt(CurrentScore, "Score", 0));
pComp->Value(mkNamingAdapt(InitialScore, "InitialScore", 0));
pComp->Value(mkNamingAdapt(ObjectsOwned, "ObjectsOwned", 0));
pComp->Value(mkNamingAdapt(Hostility, "Hostile" ));
pComp->Value(mkNamingAdapt(ProductionDelay, "ProductionDelay", 0));
pComp->Value(mkNamingAdapt(ProductionUnit, "ProductionUnit", 0));
pComp->Value(mkNamingAdapt(CursorFlash, "CursorFlash", 0));
pComp->Value(mkNamingAdapt(Cursor, "Cursor", C4ObjectPtr::Null));
pComp->Value(mkNamingAdapt(ViewCursor, "ViewCursor", C4ObjectPtr::Null));
pComp->Value(mkNamingAdapt(MessageStatus, "MessageStatus", 0));
pComp->Value(mkNamingAdapt(toC4CStr(MessageBuf),"MessageBuf", ""));
pComp->Value(mkNamingAdapt(BaseMaterial, "BaseMaterial" ));
pComp->Value(mkNamingAdapt(BaseProduction, "BaseProduction" ));
pComp->Value(mkNamingAdapt(Knowledge, "Knowledge" ));
pComp->Value(mkNamingAdapt(mkParAdapt(Crew, numbers), "Crew" ));
2009-05-08 13:28:41 +00:00
pComp->Value(mkNamingAdapt(CrewInfoList.iNumCreated, "CrewCreated", 0));
pComp->Value(mkNamingPtrAdapt( pMsgBoardQuery, "MsgBoardQueries" ));
pComp->Value(mkNamingAdapt(mkParAdapt(SoundModifier, numbers), "SoundModifier", C4Value()));
if (pComp->isCompiler())
{
SoundModifier.Denumerate(numbers);
}
// Keys held down
pComp->Value(Control);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::LoadRuntimeData(C4Group &hGroup, C4ValueNumbers * numbers)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
const char *pSource;
// Use loaded game text component
if (!(pSource = Game.GameText.GetData())) return false;
// safety: Do nothing if player section is not even present (could kill initialized values)
if (!SSearch(pSource, FormatString("[Player%i]", ID).getData())) return false;
2009-05-08 13:28:41 +00:00
// Compile (Search player section - runtime data is stored by unique player ID)
// Always compile exact. Exact data will not be present for savegame load, so it does not matter
2009-05-08 13:28:41 +00:00
assert(ID);
2010-03-28 18:58:01 +00:00
if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(
mkNamingAdapt(mkParAdapt(*this, numbers), FormatString("Player%i", ID).getData()),
2010-03-28 18:58:01 +00:00
StdStrBuf(pSource),
Game.GameText.GetFilePath()))
return false;
2009-05-08 13:28:41 +00:00
// Denumerate pointers
DenumeratePointers();
// Success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ExecBaseProduction()
2010-03-28 18:58:01 +00:00
{
const int32_t MaxBaseProduction = 25;
ProductionDelay++;
if (ProductionDelay>=60) // Minute Production Unit
2010-03-28 18:58:01 +00:00
{
ProductionDelay=0; ProductionUnit++;
for (int32_t cnt=0; BaseProduction.GetID(cnt); cnt++)
if (BaseProduction.GetCount(cnt)>0)
if (ProductionUnit % Clamp<int32_t>(11-BaseProduction.GetCount(cnt),1,10) ==0)
if (BaseMaterial.GetIDCount(BaseProduction.GetID(cnt)) < MaxBaseProduction)
BaseMaterial.IncreaseIDCount(BaseProduction.GetID(cnt));
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CheckElimination()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Standard elimination: no crew
2015-01-25 15:16:06 +00:00
if (!Crew.GetFirstObject())
2009-05-08 13:28:41 +00:00
// Already eliminated safety
if (!Eliminated)
// No automatic elimination desired?
if (!NoEliminationCheck)
// Do elimination!
Eliminate();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::UpdateView()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// view target/cursor
switch (ViewMode)
2010-03-28 18:58:01 +00:00
{
case C4PVM_Cursor:
{
C4Object *pViewObj;
if (!(pViewObj=ViewCursor)) pViewObj=Cursor;
if (pViewObj)
{
2010-03-28 18:58:01 +00:00
ViewX=pViewObj->GetX(); ViewY=pViewObj->GetY();
}
2010-03-28 18:58:01 +00:00
break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
case C4PVM_Target:
if (ViewTarget)
{
ViewX=ViewTarget->GetX(); ViewY=ViewTarget->GetY();
}
break;
case C4PVM_Scrolling:
break;
}
}
2009-05-08 13:28:41 +00:00
void C4Player::DefaultRuntimeData()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Status=0;
Eliminated=0;
Surrendered=0;
2009-05-08 13:28:41 +00:00
AtClient=C4ClientIDUnknown;
SCopy("Local",AtClientName);
ControlSet = NULL;
ControlSetName.Clear();
MouseControl=false;
Position=-1;
2009-05-08 13:28:41 +00:00
PlrStartIndex=0;
RetireDelay=0;
ViewMode=C4PVM_Cursor;
ViewX=ViewY=0;
ViewTarget=NULL;
ShowStartup=true;
2009-05-08 13:28:41 +00:00
Wealth=0;
CurrentScore=InitialScore=0;
ObjectsOwned=0;
2009-05-08 13:28:41 +00:00
ProductionDelay=ProductionUnit=0;
Cursor=ViewCursor=NULL;
CursorFlash=30;
2009-05-08 13:28:41 +00:00
MessageStatus=0;
MessageBuf[0]=0;
Hostility.clear();
BaseMaterial.Default();
BaseProduction.Default();
Knowledge.Default();
2009-05-08 13:28:41 +00:00
FlashCom=0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::ActivateMenuTeamSelection(bool fFromMain)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Menu symbol/init
bool fSwitch = !(Status==PS_TeamSelection);
2016-01-27 04:16:33 +00:00
Menu.InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Team),LoadResStr("IDS_MSG_SELTEAM"),Number, fSwitch ? C4MN_TeamSwitch : C4MN_TeamSelection);
2009-05-08 13:28:41 +00:00
Menu.SetAlignment(fSwitch ? C4MN_Align_Left | C4MN_Align_Bottom : 0);
Menu.Refill();
// Go back to options menu on close
if (fFromMain) Menu.SetCloseCommand("ActivateMenu:Main");
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::DoTeamSelection(int32_t idTeam)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// stop team selection. This might close the menu forever if the control gets lost
// let's hope it doesn't!
Status = PS_TeamSelectionPending;
::Control.DoInput(CID_PlrAction, C4ControlPlayerAction::InitScenarioPlayer(this, idTeam), CDT_Queue);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::DenumeratePointers()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Crew
Crew.DenumeratePointers();
2009-05-08 13:28:41 +00:00
// Cursor
Cursor.DenumeratePointers();
2009-05-08 13:28:41 +00:00
// ViewCursor
ViewCursor.DenumeratePointers();
2009-05-08 13:28:41 +00:00
// messageboard-queries
for (C4MessageBoardQuery *pCheck = pMsgBoardQuery; pCheck; pCheck = pCheck->pNext)
pCheck->CallbackObj.DenumeratePointers();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::RemoveCrewObjects()
2010-03-28 18:58:01 +00:00
{
C4Object *pCrew;
2009-05-08 13:28:41 +00:00
// Remove all crew objects
2010-01-25 04:00:59 +00:00
while ((pCrew = Crew.GetObject())) pCrew->AssignRemoval(true);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4Player::FindNewOwner() const
{
int32_t iNewOwner = NO_OWNER;
C4Team *pTeam;
if (Team) if ((pTeam = Game.Teams.GetTeamByID(Team)))
{
for (int32_t i=0; i<pTeam->GetPlayerCount(); ++i)
{
int32_t iPlrID = pTeam->GetIndexedPlayer(i);
if (iPlrID && iPlrID != ID)
{
C4PlayerInfo *pPlrInfo = Game.PlayerInfos.GetPlayerInfoByID(iPlrID);
if (pPlrInfo) if (pPlrInfo->IsJoined())
{
// this looks like a good new owner
iNewOwner = pPlrInfo->GetInGameNumber();
break;
}
}
}
}
// if noone from the same team was found, try to find another non-hostile player
// (necessary for cooperative rounds without teams)
if (iNewOwner == NO_OWNER)
for (C4Player *pOtherPlr = ::Players.First; pOtherPlr; pOtherPlr = pOtherPlr->Next)
if (pOtherPlr != this) if (!pOtherPlr->Eliminated)
if (!::Players.Hostile(pOtherPlr->Number, Number))
iNewOwner = pOtherPlr->Number;
return iNewOwner;
}
2009-05-08 13:28:41 +00:00
void C4Player::NotifyOwnedObjects()
2010-03-28 18:58:01 +00:00
{
int32_t iNewOwner = FindNewOwner();
2009-05-08 13:28:41 +00:00
// notify objects in all object lists
for (C4ObjectList *pList = &::Objects; pList; pList = ((pList == &::Objects) ? &::Objects.InactiveObjects : NULL))
{
for (C4Object *cobj : *pList)
{
if (cobj->Status && cobj->Owner == Number)
{
C4AulFunc *pFn = cobj->GetFunc(PSF_OnOwnerRemoved);
if (pFn)
2010-03-28 18:58:01 +00:00
{
C4AulParSet pars(C4VInt(iNewOwner));
pFn->Exec(cobj, &pars);
2010-03-28 18:58:01 +00:00
}
else
{
// crew members: Those are removed later (AFTER the player has been removed, for backwards compatiblity with relaunch scripting)
if (Crew.IsContained(cobj))
continue;
// Regular objects: Try to find a new, suitable owner from the same team
// Ignore StaticBack, because this would not be compatible with many internal objects such as team account
if ((cobj->Category & C4D_StaticBack) == 0)
cobj->SetOwner(iNewOwner);
}
}
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::DoScore(int32_t iChange)
2010-03-28 18:58:01 +00:00
{
CurrentScore = Clamp<int32_t>( CurrentScore+iChange, -100000, 100000 );
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::SetCursor(C4Object *pObj, bool fSelectArrow)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check disabled
if (pObj) if (pObj->CrewDisabled) return;
bool fChanged = pObj != Cursor;
C4Object *pPrev = Cursor;
// Set cursor
Cursor=pObj;
2009-05-08 13:28:41 +00:00
// unselect previous
if (pPrev && fChanged) pPrev->UnSelect();
2009-05-08 13:28:41 +00:00
// Select object
if (fChanged && Cursor) { Cursor->DoSelect(); }
2009-05-08 13:28:41 +00:00
// View flash
if (fSelectArrow) CursorFlash=30;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ScrollView(float iX, float iY, float ViewWdt, float ViewHgt)
2010-03-28 18:58:01 +00:00
{
if (ViewLock) return;
2009-05-08 13:28:41 +00:00
SetViewMode(C4PVM_Scrolling);
2010-09-28 18:16:33 +00:00
float ViewportScrollBorder = Application.isEditor ? 0 : C4ViewportScrollBorder;
ViewX = Clamp<C4Real>( ViewX+ftofix(iX), ftofix(ViewWdt/2.0f-ViewportScrollBorder), ftofix(GBackWdt+ViewportScrollBorder-ViewWdt/2.0f) );
ViewY = Clamp<C4Real>( ViewY+ftofix(iY), ftofix(ViewHgt/2.0f-ViewportScrollBorder), ftofix(GBackHgt+ViewportScrollBorder-ViewHgt/2.0f) );
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ClearControl()
{
// Mark any control set as unused
Control.Clear();
// Reset control
LocalControl = false;
ControlSetName.Clear();
ControlSet=NULL;
2009-05-08 13:28:41 +00:00
if (pGamepad) { delete pGamepad; pGamepad=NULL; }
MouseControl = false;
2009-05-08 13:28:41 +00:00
// no controls issued yet
ControlCount = ActionCount = 0;
LastControlType = PCID_None;
LastControlID = 0;
}
void C4Player::InitControl()
{
// Check local control
if (AtClient == ::Control.ClientID())
if (!GetInfo() || GetInfo()->GetType() == C4PT_User)
if (!::Control.isReplay())
LocalControl=true;
// needs to init control for local players only
if (LocalControl)
{
// Preferred control
ControlSetName = PrefControl;
2011-03-30 20:11:47 +00:00
ControlSet = Game.PlayerControlUserAssignmentSets.GetSetByName(ControlSetName.getData());
// control set unassigned/not known? fallback to some default then (=first defined control set)
2011-03-30 20:11:47 +00:00
if (!ControlSet) ControlSet = Game.PlayerControlUserAssignmentSets.GetDefaultSet();
// gamepad control safety (assuming the default control set is not using gamepad)
if (ControlSet && ControlSet->HasGamepad() && !Config.General.GamepadEnabled)
{
2011-03-30 20:11:47 +00:00
ControlSet = Game.PlayerControlUserAssignmentSets.GetDefaultSet();
}
// Choose next while control taken
// TODO
// init gamepad
if (ControlSet && ControlSet->HasGamepad())
{
pGamepad = new C4GamePadOpener(ControlSet->GetGamepadIndex());
}
// Mouse
if (ControlSet && ControlSet->HasMouse() && PrefMouse)
if (!::Players.MouseControlTaken())
MouseControl=true;
2010-03-22 15:49:51 +00:00
// Some controls such as gamepad control need special synced GUI elements
// Do a script callback for selected control
::Control.DoInput(CID_PlrAction, C4ControlPlayerAction::InitPlayerControl(this, ControlSet), CDT_Queue);
2009-05-08 13:28:41 +00:00
}
// clear old control method and register new
Control.RegisterKeyset(Number, ControlSet);
}
2009-05-08 13:28:41 +00:00
int igOffX, igOffY;
int VisibilityCheck(int iVis, int sx, int sy, int cx, int cy)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
sx -= igOffX; sy -= igOffY; cx -= igOffX; cy -= igOffY;
int st = std::max(1, std::max(Abs(sx - cx), Abs(sy - cy)));
2010-03-28 18:58:01 +00:00
for (int i = 0; i <= st; i++)
2009-05-08 13:28:41 +00:00
{
int x = (sx * (st - i) + cx * i) / st, y = (sy * (st - i) + cy * i) / st;
2010-03-28 18:58:01 +00:00
if (GBackSolid(x, y))
{
if ((iVis -= 2) <= 0)
2010-03-21 18:34:22 +00:00
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
return iVis;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CloseMenu()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// cancel all player menus
Menu.Close(false);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::Eliminate()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Eliminated) return;
Eliminated=true;
2009-05-08 13:28:41 +00:00
RetireDelay=C4RetireDelay;
StartSoundEffect("UI::Eliminated");
2009-05-08 13:28:41 +00:00
Log(FormatString(LoadResStr("IDS_PRC_PLRELIMINATED"),GetName()).getData());
// Early client deactivation check
2010-03-28 18:58:01 +00:00
if (::Control.isCtrlHost() && AtClient > C4ClientIDHost)
{
2009-05-08 13:28:41 +00:00
// Check: Any player left at this client?
C4Player *pPlr = NULL;
2010-03-28 18:58:01 +00:00
for (int i = 0; (pPlr = ::Players.GetAtClient(AtClient, i)); i++)
if (!pPlr->Eliminated)
2009-05-08 13:28:41 +00:00
break;
// If not, deactivate the client
2010-03-28 18:58:01 +00:00
if (!pPlr)
2009-06-15 22:06:37 +00:00
::Control.DoInput(CID_ClientUpdate,
2010-03-28 18:58:01 +00:00
new C4ControlClientUpdate(AtClient, CUT_Activate, false),
CDT_Sync);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4Player::ActiveCrewCount()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get number of objects in crew that is not disabled
int32_t iNum=0;
for (C4Object *cObj : Crew)
if (cObj)
2009-05-08 13:28:41 +00:00
if (!cObj->CrewDisabled)
++iNum;
// return it
return iNum;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4Player::GetSelectedCrewCount()
2010-03-28 18:58:01 +00:00
{
if (Cursor && !Cursor->CrewDisabled)
return 1;
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::EvaluateLeague(bool fDisconnected, bool fWon)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// already evaluated?
if (LeagueEvaluated) return; LeagueEvaluated=true;
// set fate
C4PlayerInfo *pInfo = GetInfo();
2010-03-28 18:58:01 +00:00
if (pInfo)
{
if (fDisconnected)
pInfo->SetDisconnected();
2010-03-28 18:58:01 +00:00
if (fWon)
pInfo->SetWinner();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::LocalSync()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// local sync not necessary for script players
if (GetType() == C4PT_Script) return true;
2009-05-08 13:28:41 +00:00
// evaluate total playing time
TotalPlayingTime+=Game.Time-GameJoinTime;
GameJoinTime = Game.Time;
// evaluate total playing time of all the crew
for (C4ObjectInfo *pInf = CrewInfoList.GetFirst(); pInf; pInf=pInf->Next)
if (pInf->InAction)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pInf->TotalPlayingTime+=(Game.Time-pInf->InActionTime);
pInf->InActionTime = Game.Time;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// save player
if (!Save())
return false;
2009-05-08 13:28:41 +00:00
// done, success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4PlayerInfo *C4Player::GetInfo()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return Game.PlayerInfos.GetPlayerInfoByID(ID);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::SetObjectCrewStatus(C4Object *pCrew, bool fNewStatus)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// either add...
if (fNewStatus)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// is in crew already?
if (Crew.IsContained(pCrew)) return true;
2009-05-08 13:28:41 +00:00
return MakeCrewMember(pCrew, false);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// already outside?
if (!Crew.IsContained(pCrew)) return true;
2009-05-08 13:28:41 +00:00
// ...or remove
Crew.Remove(pCrew);
2009-12-14 21:44:55 +00:00
C4AulParSet parset(C4VInt(Number));
pCrew->Call(PSF_OnRemoveCrew, &parset);
2009-05-08 13:28:41 +00:00
// remove info, if assigned to this player
// theoretically, info objects could remain when the player is deleted
// but then they would be reassigned to the player crew when loaded in a savegame
// by the crew-assignment code kept for backwards compatibility with pre-4.95.2-savegames
if (pCrew->Info && CrewInfoList.IsElement(pCrew->Info))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
pCrew->Info->Retire();
pCrew->Info = NULL;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done, success
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CreateGraphs()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// del prev
ClearGraphs();
// create graphs
if (Game.pNetworkStatistics)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
DWORD dwGraphClr = ColorDw;
C4PlayerInfo *pInfo;
if (ID && (pInfo = Game.PlayerInfos.GetPlayerInfoByID(ID)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// set color by player info class
dwGraphClr = pInfo->GetColor();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4GUI::MakeColorReadableOnBlack(dwGraphClr); dwGraphClr &= 0xffffff;
pstatControls = new C4TableGraph(C4TableGraph::DefaultBlockLength * 20, Game.pNetworkStatistics->ControlCounter);
pstatControls->SetColorDw(dwGraphClr);
pstatControls->SetTitle(GetName());
pstatActions = new C4TableGraph(C4TableGraph::DefaultBlockLength * 20, Game.pNetworkStatistics->ControlCounter);
pstatActions->SetColorDw(dwGraphClr);
pstatActions->SetTitle(GetName());
// register into
Game.pNetworkStatistics->statControls.AddGraph(pstatControls);
Game.pNetworkStatistics->statActions.AddGraph(pstatActions);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ClearGraphs()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// del all assigned graphs
if (pstatControls)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Game.pNetworkStatistics) Game.pNetworkStatistics->statControls.RemoveGraph(pstatControls);
delete pstatControls;
pstatControls = NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (pstatActions)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Game.pNetworkStatistics) Game.pNetworkStatistics->statActions.RemoveGraph(pstatActions);
delete pstatActions;
pstatActions = NULL;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CountControl(ControlType eType, int32_t iID, int32_t iCntAdd)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// count it
ControlCount += iCntAdd;
// catch doubles
if (eType == LastControlType && iID == LastControlID) return;
// no double: count as action
LastControlType = eType;
LastControlID = iID;
ActionCount += iCntAdd;
// and give experience
if (Cursor && Cursor->Info)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Cursor->Info)
2010-03-28 18:58:01 +00:00
{
Cursor->Info->ControlCount++; if ((Cursor->Info->ControlCount%5) == 0) Cursor->DoExperience(+1);
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ExecMsgBoardQueries()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// already active?
if (::MessageInput.IsTypeIn()) return;
2009-05-08 13:28:41 +00:00
// find an un-evaluated query
C4MessageBoardQuery *pCheck = pMsgBoardQuery;
2010-03-28 18:58:01 +00:00
while (pCheck) if (!pCheck->fAnswered) break; else pCheck = pCheck->pNext;
2009-05-08 13:28:41 +00:00
if (!pCheck) return;
// open it
::MessageInput.StartTypeIn(true, pCheck->CallbackObj, pCheck->fIsUppercase, false, Number, pCheck->sInputQuery);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::CallMessageBoard(C4Object *pForObj, const StdStrBuf &sQueryString, bool fIsUppercase)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// remove any previous query for the same object
RemoveMessageBoardQuery(pForObj);
// sort new query to end of list
C4MessageBoardQuery **ppTarget = &pMsgBoardQuery;
while (*ppTarget) ppTarget = &((*ppTarget)->pNext);
*ppTarget = new C4MessageBoardQuery(pForObj, sQueryString, fIsUppercase);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::RemoveMessageBoardQuery(C4Object *pForObj)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get matching query
C4MessageBoardQuery **ppCheck = &pMsgBoardQuery, *pFound;
while (*ppCheck) if ((*ppCheck)->CallbackObj == pForObj) break; else ppCheck = &((*ppCheck)->pNext);
2009-05-08 13:28:41 +00:00
pFound = *ppCheck;
if (!pFound) return false;
// remove it
*ppCheck = (*ppCheck)->pNext;
delete pFound;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::MarkMessageBoardQueryAnswered(C4Object *pForObj)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get matching query
C4MessageBoardQuery *pCheck = pMsgBoardQuery;
while (pCheck) if (pCheck->CallbackObj == pForObj && !pCheck->fAnswered) break; else pCheck = pCheck->pNext;
2009-05-08 13:28:41 +00:00
if (!pCheck) return false;
// mark it
pCheck->fAnswered = true;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::HasMessageBoardQuery()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// return whether any object has a messageboard-query
return !!pMsgBoardQuery;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::OnTeamSelectionFailed()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// looks like a selected team was not available: Go back to team selection if this is not a mislead call
if (Status == PS_TeamSelectionPending)
Status = PS_TeamSelection;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::SetPlayerColor(uint32_t dwNewClr)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// no change?
if (dwNewClr == ColorDw) return;
// reflect change in all active, player-owned objects
// this can never catch everything (thinking of overlays, etc.); scenarios that allow team changes should take care of the rest
uint32_t dwOldClr = ColorDw;
ColorDw = dwNewClr;
for (C4Object *pObj : Objects)
if (pObj && pObj->Status && pObj->Owner == Number)
{
if ((pObj->Color & 0xffffff) == (dwOldClr & 0xffffff))
pObj->Color = (pObj->Color & 0xff000000u) | (dwNewClr & 0xffffff);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4PlayerType C4Player::GetType() const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// type by info
C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(ID);
if (pInfo) return pInfo->GetType(); else { assert(false); return C4PT_User; }
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4Player::IsInvisible() const
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// invisible by info
C4PlayerInfo *pInfo = Game.PlayerInfos.GetPlayerInfoByID(ID);
if (pInfo) return pInfo->IsInvisible(); else { assert(false); return false; }
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4Player::ToggleMouseControl()
{
// Activate mouse control if it's available
2009-06-12 23:09:32 +00:00
if (!MouseControl && !::Players.MouseControlTaken())
2010-03-28 18:58:01 +00:00
{
2009-06-05 15:20:07 +00:00
::MouseControl.Init(Number);
MouseControl=true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Deactivate mouse control
else if (MouseControl)
2010-03-28 18:58:01 +00:00
{
2009-06-05 15:20:07 +00:00
::MouseControl.Clear();
::MouseControl.Default();
2009-05-08 13:28:41 +00:00
MouseControl = 0;
// Scrolling isn't possible any more
if (ViewMode == C4PVM_Scrolling)
SetViewMode(C4PVM_Cursor);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
}
bool C4Player::ActivateMenuMain()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Not during game over dialog
if (C4GameOverDlg::IsShown()) return false;
// Open menu
return !!Menu.ActivateMain(Number);
2010-03-28 18:58:01 +00:00
}
void C4Player::HostilitySet::CompileFunc(StdCompiler *pComp)
{
int entries = size();
if (pComp->isCompiler())
{
clear();
pComp->Value(entries);
while (entries--)
{
int number;
pComp->Value(number);
assert(::Players.Valid(number));
C4Player *plr = ::Players.Get(number);
if (plr)
insert(plr);
}
}
else
{
pComp->Value(entries);
for (const_iterator it = begin(); it != end(); ++it)
{
int32_t num = (*it)->Number;
pComp->Value(num); // Can't use (*it)->Number directly because StdCompiler is dumb about constness
}
}
}
void C4Player::SetZoomByViewRange(int32_t range_wdt, int32_t range_hgt, bool direct, bool no_increase, bool no_decrease)
{
AdjustZoomParameter(&ZoomWdt, range_wdt, no_increase, no_decrease);
AdjustZoomParameter(&ZoomHgt, range_hgt, no_increase, no_decrease);
ZoomToViewports(direct, no_decrease, no_increase); // inc/dec swapped for zoom, because it's inversely proportional to range
}
void C4Player::SetMinZoomByViewRange(int32_t range_wdt, int32_t range_hgt, bool no_increase, bool no_decrease)
{
AdjustZoomParameter(&ZoomLimitMinWdt, range_wdt, no_increase, no_decrease);
AdjustZoomParameter(&ZoomLimitMinHgt, range_hgt, no_increase, no_decrease);
ZoomLimitsToViewports();
}
void C4Player::SetMaxZoomByViewRange(int32_t range_wdt, int32_t range_hgt, bool no_increase, bool no_decrease)
{
AdjustZoomParameter(&ZoomLimitMaxWdt, range_wdt, no_increase, no_decrease);
AdjustZoomParameter(&ZoomLimitMaxHgt, range_hgt, no_increase, no_decrease);
ZoomLimitsToViewports();
}
void C4Player::SetZoom(C4Fixed zoom, bool direct, bool no_increase, bool no_decrease)
{
AdjustZoomParameter(&ZoomVal, zoom, no_increase, no_decrease);
ZoomToViewports(direct, no_increase, no_decrease);
}
void C4Player::SetMinZoom(C4Fixed zoom, bool no_increase, bool no_decrease)
{
AdjustZoomParameter(&ZoomLimitMinVal, zoom, no_increase, no_decrease);
ZoomLimitsToViewports();
}
void C4Player::SetMaxZoom(C4Fixed zoom, bool no_increase, bool no_decrease)
{
AdjustZoomParameter(&ZoomLimitMaxVal, zoom, no_increase, no_decrease);
ZoomLimitsToViewports();
}
void C4Player::ZoomToViewports(bool direct, bool no_increase, bool no_decrease)
{
C4Viewport *vp = NULL;
while((vp = ::Viewports.GetViewport(Number, vp)) != NULL)
ZoomToViewport(vp, direct, no_increase, no_decrease);
}
void C4Player::ZoomToViewport(C4Viewport* vp, bool direct, bool no_increase, bool no_decrease)
{
float new_zoom = ZoomVal ? fixtof(ZoomVal) : vp->GetZoomByViewRange((ZoomWdt || ZoomHgt) ? ZoomWdt : C4VP_DefViewRangeX,ZoomHgt);
float old_zoom = vp->GetZoomTarget();
if (new_zoom > old_zoom && no_increase) return;
if (new_zoom < old_zoom && no_decrease) return;
vp->SetZoom(new_zoom, direct);
}
void C4Player::ZoomLimitsToViewports()
{
C4Viewport *vp = NULL;
while((vp = ::Viewports.GetViewport(Number, vp)) != NULL)
ZoomLimitsToViewport(vp);
}
void C4Player::ZoomLimitsToViewport(C4Viewport* vp)
{
float zoom_max = ZoomLimitMaxVal ? fixtof(ZoomLimitMaxVal) : vp->GetZoomByViewRange((ZoomLimitMinWdt || ZoomLimitMinHgt) ? ZoomLimitMinWdt : C4VP_DefMinViewRangeX,ZoomLimitMinHgt);
float zoom_min = ZoomLimitMinVal ? fixtof(ZoomLimitMinVal) : vp->GetZoomByViewRange((ZoomLimitMaxWdt || ZoomLimitMaxHgt) ? ZoomLimitMaxWdt : C4VP_DefMaxViewRangeX,ZoomLimitMaxHgt);
vp->SetZoomLimits(zoom_min, zoom_max);
}
bool C4Player::AdjustZoomParameter(int32_t *range_par, int32_t new_val, bool no_increase, bool no_decrease)
{
// helper function: Adjust *range_par to new_val if increase/decrease not forbidden
if (new_val < *range_par)
{
if (!no_decrease) *range_par = new_val;
return !no_decrease;
}
else if(new_val > *range_par)
{
if (!no_increase) *range_par = new_val;
return !no_increase;
}
return true;
}
bool C4Player::AdjustZoomParameter(C4Fixed *zoom_par, C4Fixed new_val, bool no_increase, bool no_decrease)
{
// helper function: Adjust *zoom_par to new_val if increase/decrease not forbidden
if (new_val < *zoom_par)
{
if (!no_decrease) *zoom_par = new_val;
return !no_decrease;
}
else if(new_val > *zoom_par)
{
if (!no_increase) *zoom_par = new_val;
return !no_increase;
}
return true;
}
void C4Player::SetViewLocked(bool to_val)
{
if ((ViewLock = to_val))
{
// view was locked - cancel any scrolling
if (ViewMode == C4PVM_Scrolling) SetViewMode(C4PVM_Cursor);
}
}
bool C4Player::GainScenarioAchievement(const char *achievement_id, int32_t value, const char *scen_name_override)
{
// Determine full ID of achievement
if (!scen_name_override)
2015-01-25 15:16:06 +00:00
{
if (::Game.C4S.Head.Origin.getLength())
scen_name_override = ::Game.C4S.Head.Origin.getData();
else
scen_name_override = ::Game.ScenarioFilename;
2015-01-25 15:16:06 +00:00
}
StdStrBuf sAchvID = C4ScenarioParameters::AddFilename2ID(scen_name_override, achievement_id);
// Gain achievement iff it's an improvement
Achievements.SetValue(sAchvID.getData(), value, true);
return true;
}
void C4Player::SetSoundModifier(C4PropList *new_modifier)
{
// set modifier to be applied to all new sounds being played in a player's viewport
// update prop list parameter
C4SoundModifier *mod;
if (new_modifier)
{
SoundModifier.SetPropList(new_modifier);
mod = ::Application.SoundSystem.Modifiers.Get(new_modifier, true);
}
else
{
SoundModifier.Set0();
mod = NULL;
}
// update in sound system
::Application.SoundSystem.Modifiers.SetGlobalModifier(mod, Number);
}