2009-05-08 13:28:41 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
2013-12-17 20:01:09 +00:00
|
|
|
* 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
|
|
|
*
|
2013-12-17 20:01:09 +00:00
|
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
|
|
* "COPYING" for details.
|
2009-05-08 13:28:41 +00:00
|
|
|
*
|
2013-12-17 20:01:09 +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
|
|
|
*
|
2013-12-17 20:01:09 +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 */
|
|
|
|
|
2016-04-03 18:07:56 +00:00
|
|
|
#include "C4Include.h"
|
|
|
|
#include "player/C4Player.h"
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2016-04-03 18:07:56 +00:00
|
|
|
#include "game/C4Application.h"
|
2016-04-02 17:44:44 +00:00
|
|
|
#include "object/C4Def.h"
|
2016-04-03 18:07:56 +00:00
|
|
|
#include "object/C4DefList.h"
|
|
|
|
#include "object/C4Object.h"
|
|
|
|
#include "object/C4ObjectInfo.h"
|
|
|
|
#include "object/C4Command.h"
|
|
|
|
#include "network/C4League.h"
|
|
|
|
#include "network/C4Network2Stats.h"
|
|
|
|
#include "gui/C4MessageInput.h"
|
|
|
|
#include "platform/C4GamePadCon.h"
|
|
|
|
#include "lib/C4Random.h"
|
|
|
|
#include "lib/C4Log.h"
|
|
|
|
#include "game/C4FullScreen.h"
|
|
|
|
#include "gui/C4GameOverDlg.h"
|
|
|
|
#include "object/C4ObjectMenu.h"
|
|
|
|
#include "gui/C4MouseControl.h"
|
|
|
|
#include "gui/C4GameMessage.h"
|
|
|
|
#include "graphics/C4GraphicsResource.h"
|
|
|
|
#include "game/C4GraphicsSystem.h"
|
|
|
|
#include "landscape/C4Landscape.h"
|
|
|
|
#include "game/C4Game.h"
|
|
|
|
#include "player/C4PlayerList.h"
|
|
|
|
#include "object/C4GameObjects.h"
|
|
|
|
#include "control/C4GameControl.h"
|
|
|
|
#include "game/C4Viewport.h"
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
C4Player::C4Player() : C4PlayerInfoCore()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2015-12-29 00:35:41 +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;
|
|
|
|
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
|
|
|
{
|
2015-12-29 00:35:41 +00:00
|
|
|
ClearGraphs();
|
|
|
|
Menu.Clear();
|
|
|
|
SetSoundModifier(NULL);
|
|
|
|
while (pMsgBoardQuery)
|
|
|
|
{
|
|
|
|
C4MessageBoardQuery *pNext = pMsgBoardQuery->pNext;
|
|
|
|
delete pMsgBoardQuery;
|
|
|
|
pMsgBoardQuery = pNext;
|
|
|
|
}
|
|
|
|
ClearControl();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4Player::ObjectInCrew(C4Object *tobj)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
if (!tobj) return false;
|
2014-10-24 20:50:14 +00:00
|
|
|
for (C4Object *cobj : Crew)
|
2010-03-27 16:05:02 +00:00
|
|
|
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)) {}
|
2010-03-27 16:05:02 +00:00
|
|
|
// Cursor
|
|
|
|
if (Cursor==pObj)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-08-15 18:50:32 +00:00
|
|
|
// object is to be deleted; do NOT do script calls (like in Cursor->UnSelect(true))
|
2010-03-27 16:05:02 +00:00
|
|
|
Cursor=NULL; AdjustCursorCommand(); // also selects and eventually does a script call!
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-27 16:05:02 +00:00
|
|
|
// View-Cursor
|
|
|
|
if (ViewCursor==pObj) ViewCursor = NULL;
|
|
|
|
// View
|
|
|
|
if (ViewTarget==pObj) ViewTarget=NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Menu
|
|
|
|
Menu.ClearPointers(pObj);
|
|
|
|
// 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;
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
|
|
{
|
2016-01-27 03:00:44 +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!
|
2016-04-02 15:50:49 +00:00
|
|
|
ViewX = Game.C4S.PlrStart[iPlrStartIndex-1].Position[0] * ::Landscape.GetMapZoom();
|
|
|
|
ViewY = Game.C4S.PlrStart[iPlrStartIndex-1].Position[1] * ::Landscape.GetMapZoom();
|
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
|
|
|
|
2016-02-20 17:26:30 +00:00
|
|
|
// Do we have a gamepad?
|
|
|
|
if (pGamepad)
|
|
|
|
{
|
|
|
|
// Check whether it's still connected.
|
|
|
|
if (!pGamepad->IsAttached())
|
|
|
|
{
|
|
|
|
// Allow the player to plug the gamepad back in. This allows
|
|
|
|
// battery replacement or plugging the controller back
|
|
|
|
// in after someone tripped over the wire.
|
|
|
|
if (!FindGamepad())
|
|
|
|
{
|
|
|
|
LogF("%s: No gamepad available.", Name.getData());
|
|
|
|
::Game.Pause();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Should we have one? The player may have started the game
|
|
|
|
// without turning their controller on, only noticing this
|
|
|
|
// after the game started.
|
|
|
|
else if (LocalControl && ControlSet && ControlSet->HasGamepad())
|
|
|
|
{
|
|
|
|
FindGamepad();
|
|
|
|
}
|
|
|
|
|
2009-05-08 13:28:41 +00:00
|
|
|
// Tick1
|
|
|
|
UpdateView();
|
|
|
|
ExecuteControl();
|
|
|
|
Menu.Execute();
|
|
|
|
|
2009-06-12 18:52:21 +00:00
|
|
|
// ::Game.iTick35
|
|
|
|
if (!::Game.iTick35 && Status==PS_Normal)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2014-04-16 12:06:30 +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
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4Player::Init(int32_t iNumber, int32_t iAtClient, const char *szAtClientName,
|
2011-03-26 22:59:35 +00:00
|
|
|
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);
|
2009-08-15 18:50:32 +00:00
|
|
|
return false;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// Status init
|
2010-03-27 16:05:02 +00:00
|
|
|
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);
|
|
|
|
|
2011-01-06 20:18:13 +00:00
|
|
|
if (szFilename)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Load core & crew info list
|
2011-08-05 12:58:16 +00:00
|
|
|
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
|
2016-04-02 15:50:49 +00:00
|
|
|
ViewX = ::Landscape.GetWidth()/2; ViewY = ::Landscape.GetHeight()/2;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// 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!
|
2011-09-22 18:00:17 +00:00
|
|
|
ClearControl();
|
2009-05-08 13:28:41 +00:00
|
|
|
InitControl();
|
|
|
|
|
2010-03-26 13:48:28 +00:00
|
|
|
// 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
|
|
|
{
|
2014-05-30 23:03:23 +00:00
|
|
|
pDefCallback->Call(PSF_InitializeScriptPlayer, &C4AulParSet(Number, 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
|
2014-05-30 23:03:23 +00:00
|
|
|
::Game.GRBroadcast(PSF_PreInitializePlayer, &C4AulParSet(Number));
|
2009-05-08 13:28:41 +00:00
|
|
|
// direct init
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
|
|
|
2010-03-27 16:05:02 +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());
|
2011-03-26 22:59:35 +00:00
|
|
|
assert(numbers);
|
2009-05-08 13:28:41 +00:00
|
|
|
// (compile using DefaultRuntimeData) - also check if compilation returned sane results, i.e. ID assigned
|
2011-03-26 22:59:35 +00:00
|
|
|
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
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
2014-10-24 20:50:14 +00:00
|
|
|
for (C4Object *pObj : Objects)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2015-01-02 00:06:00 +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();
|
|
|
|
|
2015-08-28 01:44:23 +00:00
|
|
|
// init sound mod
|
2015-12-02 15:38:22 +00:00
|
|
|
SetSoundModifier(SoundModifier.getPropList());
|
2015-08-28 01:44:23 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4Player::Save()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
C4Group hGroup;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Regular player saving need not be done for script players
|
2009-08-15 18:50:32 +00:00
|
|
|
if (GetType() == C4PT_Script) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Log
|
|
|
|
LogF(LoadResStr("IDS_PRC_SAVEPLR"), Config.AtRelativePath(Filename));
|
2015-12-27 21:26:40 +00:00
|
|
|
::GraphicsSystem.MessageBoard->EnsureLastMessage();
|
2009-05-08 13:28:41 +00:00
|
|
|
// copy player to save somewhere else
|
2010-03-27 16:05:02 +00:00
|
|
|
char szPath[_MAX_PATH + 1];
|
|
|
|
SCopy(Config.AtTempPath(C4CFN_TempPlayer), szPath, _MAX_PATH);
|
|
|
|
MakeTempFilename(szPath);
|
2011-08-15 09:25:37 +00:00
|
|
|
// 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
|
2010-03-27 16:05:02 +00:00
|
|
|
if (!hGroup.Open(szPath,true))
|
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Save
|
|
|
|
if (!Save(hGroup, false, !LocalControl))
|
2010-03-27 16:05:02 +00:00
|
|
|
{ hGroup.Close(); return false; }
|
2009-05-08 13:28:41 +00:00
|
|
|
// Close group
|
2010-03-27 16:05:02 +00:00
|
|
|
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;
|
2010-03-27 16:05:02 +00:00
|
|
|
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);
|
2010-03-27 16:05:02 +00:00
|
|
|
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
|
2010-03-27 16:05:02 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +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
|
2010-03-27 16:05:02 +00:00
|
|
|
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))
|
2010-03-27 16:05:02 +00:00
|
|
|
{ hGroup.Close(); return false; }
|
2009-05-08 13:28:41 +00:00
|
|
|
// Sort
|
|
|
|
hGroup.Sort(C4FLS_Player);
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
|
|
{
|
2013-01-13 14:45:01 +00:00
|
|
|
int32_t cnt,ctx,cty;
|
2010-03-27 16:05:02 +00:00
|
|
|
C4Object *nobj;
|
|
|
|
C4ObjectInfo *pInfo;
|
|
|
|
C4Def *pDef;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2013-01-13 14:45:01 +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
|
|
|
{
|
2013-01-13 14:45:01 +00:00
|
|
|
// Minimum one clonk if empty id
|
2015-11-15 12:53:01 +00:00
|
|
|
iCount = std::max<int32_t>(iCount,1);
|
2013-01-13 14:45:01 +00:00
|
|
|
|
|
|
|
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
|
2013-01-13 14:45:01 +00:00
|
|
|
while (!(pInfo=CrewInfoList.GetIdle(id,::Definitions)))
|
|
|
|
if (!CrewInfoList.New(id,&::Definitions))
|
2009-05-08 13:28:41 +00:00
|
|
|
break;
|
2013-01-13 14:45:01 +00:00
|
|
|
// Safety
|
2009-05-08 13:28:41 +00:00
|
|
|
if (!pInfo || !(pDef=C4Id2Def(pInfo->id))) continue;
|
2013-01-13 14:45:01 +00:00
|
|
|
// 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
|
2013-01-13 14:45:51 +00:00
|
|
|
Crew.Add(nobj, C4ObjectList::stNone);
|
2009-05-08 13:28:41 +00:00
|
|
|
// add visibility range
|
2015-01-02 00:06:00 +00:00
|
|
|
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)
|
2013-01-13 14:45:01 +00:00
|
|
|
C4DebugRecOff DbgRecOff;
|
2009-05-08 13:28:41 +00:00
|
|
|
#endif
|
2014-05-30 23:03:23 +00:00
|
|
|
C4AulParSet parset(Number);
|
2013-01-13 14:45:01 +00:00
|
|
|
nobj->Call(PSF_OnJoinCrew, &parset);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
int32_t cnt,cnt2,ctx,cty;
|
|
|
|
C4Def *def;
|
|
|
|
C4ID cid;
|
2011-01-02 23:03:28 +00:00
|
|
|
C4Object *cbase;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Create ready base structures
|
|
|
|
for (cnt=0; (cid=Game.C4S.PlrStart[PlrStartIndex].ReadyBase.GetID(cnt)); cnt++)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
ctx=tx; cty=ty;
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Game.C4S.PlrStart[PlrStartIndex].EnforcePosition
|
2016-01-30 13:46:14 +00:00
|
|
|
|| FindConSiteSpot(ctx,cty,def->Shape.Wdt,def->Shape.Hgt,20))
|
2010-03-27 16:05:02 +00:00
|
|
|
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
|
2010-03-27 16:05:02 +00:00
|
|
|
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
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +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);
|
2010-03-27 16:05:02 +00:00
|
|
|
if ((cobj=Game.CreateObject(cid,NULL,Number,ctx,cty)))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +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-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
int32_t cnt,cnt2,ctx,cty;
|
|
|
|
C4Def *def; C4ID cid;
|
|
|
|
|
|
|
|
// In base
|
|
|
|
if (FirstBase)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
FirstBase->CreateContentsByList(Game.C4S.PlrStart[PlrStartIndex].ReadyMaterial);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-27 16:05:02 +00:00
|
|
|
|
|
|
|
// Outside
|
|
|
|
else
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
for (cnt=0; (cid=Game.C4S.PlrStart[PlrStartIndex].ReadyMaterial.GetID(cnt)); cnt++)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +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);
|
2010-03-27 16:05:02 +00:00
|
|
|
Game.CreateObject(cid,NULL,Number,ctx,cty);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-03-27 16:05:02 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4Player::ScenarioInit()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +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();
|
2009-08-15 18:50:32 +00:00
|
|
|
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;
|
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
// Wealth, home base materials, abilities
|
|
|
|
Wealth=Game.C4S.PlrStart[PlrStartIndex].Wealth.Evaluate();
|
2014-04-16 12:06:30 +00:00
|
|
|
BaseMaterial=Game.C4S.PlrStart[PlrStartIndex].BaseMaterial;
|
|
|
|
BaseMaterial.ConsolidateValids(::Definitions);
|
|
|
|
BaseProduction=Game.C4S.PlrStart[PlrStartIndex].BaseProduction;
|
|
|
|
BaseProduction.ConsolidateValids(::Definitions);
|
2010-03-27 16:05:02 +00:00
|
|
|
Knowledge=Game.C4S.PlrStart[PlrStartIndex].BuildKnowledge;
|
|
|
|
Knowledge.ConsolidateValids(::Definitions);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-27 16:05:02 +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
|
2016-04-02 15:50:49 +00:00
|
|
|
if (ptx>-1) ptx = Clamp<int32_t>( ptx * Game.C4S.Landscape.MapZoom.Evaluate(), 0, ::Landscape.GetWidth()-1 );
|
|
|
|
if (pty>-1) pty = Clamp<int32_t>( pty * Game.C4S.Landscape.MapZoom.Evaluate(), 0, ::Landscape.GetHeight()-1 );
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
// Standard position (PrefPosition)
|
|
|
|
if (ptx<0)
|
|
|
|
if (Game.StartupPlayerCount>=2)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
int32_t iMaxPos=Game.StartupPlayerCount;
|
|
|
|
// Map preferred position to available positions
|
2015-02-12 22:05:55 +00:00
|
|
|
int32_t iStartPos=Clamp(PrefPosition*iMaxPos/C4P_MaxPosition,0,iMaxPos-1);
|
2010-03-27 16:05:02 +00:00
|
|
|
int32_t iPosition=iStartPos;
|
|
|
|
// Distribute according to availability
|
|
|
|
while (::Players.PositionTaken(iPosition))
|
2014-04-15 12:58:54 +00:00
|
|
|
{
|
|
|
|
++iPosition;
|
|
|
|
iPosition %= iMaxPos;
|
|
|
|
if (iPosition == iStartPos)
|
|
|
|
break;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
Position=iPosition;
|
2010-03-27 16:05:02 +00:00
|
|
|
// Set x position
|
2016-04-02 15:50:49 +00:00
|
|
|
ptx=Clamp(16+Position*(::Landscape.GetWidth()-32)/(iMaxPos-1),0,::Landscape.GetWidth()-16);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
// All-random position
|
2016-04-02 15:50:49 +00:00
|
|
|
if (ptx<0) ptx=16+Random(::Landscape.GetWidth()-32);
|
|
|
|
if (pty<0) pty=16+Random(::Landscape.GetHeight()-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
|
2016-01-30 13:46:14 +00:00
|
|
|
FindConSiteSpot(ptx, pty, 30, 50, 400);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-27 16:05:02 +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);
|
2010-03-27 16:05:02 +00:00
|
|
|
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
|
2014-05-30 23:03:23 +00:00
|
|
|
::Game.GRBroadcast(PSF_InitializePlayer, &C4AulParSet(Number,
|
|
|
|
ptx,
|
|
|
|
pty,
|
|
|
|
FirstBase,
|
|
|
|
Team,
|
|
|
|
C4Id2Def(GetInfo()->GetScriptPlayerExtraID())));
|
2009-08-15 18:50:32 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-21 19:12:49 +00:00
|
|
|
bool C4Player::FinalInit(bool fInitialScore)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
|
2010-04-21 19:12:49 +00:00
|
|
|
// Set initial score
|
|
|
|
if (fInitialScore)
|
|
|
|
{
|
|
|
|
InitialScore=CurrentScore;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Cursor
|
|
|
|
if (!Cursor) AdjustCursorCommand();
|
|
|
|
|
2010-04-21 19:12:49 +00:00
|
|
|
// Update counts, pointers, views
|
2010-03-27 16:05:02 +00:00
|
|
|
Execute();
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +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
|
2014-12-29 22:09:31 +00:00
|
|
|
fFogOfWar = fEnable;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +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
|
|
|
{
|
2015-12-13 21:14:55 +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);
|
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
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
|
|
|
|
2015-12-12 20:52:22 +00:00
|
|
|
Wealth=Clamp<int32_t>(iVal,0,1000000000);
|
2009-12-15 19:30:00 +00:00
|
|
|
|
2014-05-30 23:03:23 +00:00
|
|
|
::Game.GRBroadcast(PSF_OnWealthChanged,&C4AulParSet(Number));
|
2009-12-15 19:30:00 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2015-12-18 18:26:46 +00:00
|
|
|
bool C4Player::SetKnowledge(C4ID id, bool fRemove)
|
|
|
|
{
|
|
|
|
if (fRemove)
|
|
|
|
{
|
|
|
|
long iIndex = Knowledge.GetIndex(id);
|
|
|
|
if (iIndex<0) return false;
|
|
|
|
return Knowledge.DeleteItem(iIndex);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!C4Id2Def(id)) return false;
|
|
|
|
return Knowledge.SetIDCount(id, 1, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-03 03:11:28 +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
|
2010-03-27 16:05:02 +00:00
|
|
|
ViewMode=iMode; ViewTarget=pTarget;
|
2015-12-03 03:11:28 +00:00
|
|
|
// 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
|
|
|
|
2015-12-03 03:11:28 +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;
|
2015-12-03 03:11:28 +00:00
|
|
|
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;
|
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
const int32_t SuccessBonus=100;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
// Set last round
|
|
|
|
LastRound.Title = Game.ScenarioTitle;
|
|
|
|
time(reinterpret_cast<time_t *>(&LastRound.Date));
|
|
|
|
LastRound.Duration = Game.Time;
|
|
|
|
LastRound.Won = !Eliminated;
|
2009-06-15 21:47:26 +00:00
|
|
|
// Melee: personal value gain score ...check ::Objects(C4D_Goal)
|
2015-11-15 12:53:01 +00:00
|
|
|
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
|
2015-11-15 12:53:01 +00:00
|
|
|
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;
|
2010-04-21 19:12:49 +00:00
|
|
|
LastRound.TotalScore = TotalScore + LastRound.FinalScore;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Update player
|
2010-03-27 16:05:02 +00:00
|
|
|
Rounds++;
|
|
|
|
if (LastRound.Won) RoundsWon++; else RoundsLost++;
|
2010-04-21 19:12:49 +00:00
|
|
|
TotalScore=LastRound.TotalScore;
|
2010-03-27 16:05:02 +00:00
|
|
|
TotalPlayingTime+=Game.Time-GameJoinTime;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-27 16:05:02 +00:00
|
|
|
// Crew
|
2009-05-08 13:28:41 +00:00
|
|
|
CrewInfoList.Evaluate();
|
|
|
|
|
|
|
|
// league
|
|
|
|
if (Game.Parameters.isLeague())
|
2010-03-27 16:05:02 +00:00
|
|
|
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;
|
2010-03-27 16:05:02 +00:00
|
|
|
Surrendered=true;
|
|
|
|
Eliminated=true;
|
2009-05-08 13:28:41 +00:00
|
|
|
RetireDelay=C4RetireDelay;
|
2015-12-13 21:14:55 +00:00
|
|
|
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
|
|
|
|
2010-01-25 15:57:57 +00:00
|
|
|
bool C4Player::SetHostility(int32_t iOpponent, int32_t hostile, bool fSilent)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-01-25 15:57:57 +00:00
|
|
|
assert(hostile == 0 || hostile == 1);
|
2009-05-08 13:28:41 +00:00
|
|
|
// Check opponent valid
|
2010-01-25 15:57:57 +00:00
|
|
|
C4Player *opponent = ::Players.Get(iOpponent);
|
|
|
|
if (!opponent || opponent == this)
|
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Set hostility
|
2010-01-25 15:57:57 +00:00
|
|
|
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
|
2009-08-15 18:50:32 +00:00
|
|
|
if (!Game.FrameCounter || fSilent) return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Announce
|
2015-12-13 21:14:55 +00:00
|
|
|
StartSoundEffect("UI::Trumpet");
|
2010-01-25 15:57:57 +00:00
|
|
|
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
|
2010-03-27 16:05:02 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-01-25 15:57:57 +00:00
|
|
|
bool C4Player::IsHostileTowards(const C4Player *plr) const
|
|
|
|
{
|
|
|
|
assert(plr);
|
|
|
|
if (!plr) return false;
|
|
|
|
return Hostility.find(plr) != Hostility.end();
|
|
|
|
}
|
|
|
|
|
2010-07-30 20:38:21 +00:00
|
|
|
C4Object* C4Player::GetHiExpActiveCrew()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2014-10-24 20:50:14 +00:00
|
|
|
C4Object *hiexp=NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
int32_t iHighestExp=-2, iExp;
|
2014-10-24 20:50:14 +00:00
|
|
|
for (C4Object *cobj : Crew)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (!cobj->CrewDisabled)
|
2010-07-30 20:38:21 +00:00
|
|
|
{
|
|
|
|
if (cobj->Info) iExp = cobj->Info->Experience; else iExp=-1;
|
|
|
|
if (!hiexp || (iExp>iHighestExp))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-07-30 20:38:21 +00:00
|
|
|
hiexp=cobj;
|
|
|
|
iHighestExp=iExp;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-07-30 20:38:21 +00:00
|
|
|
}
|
2014-10-24 20:50:14 +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
|
|
|
|
2010-07-30 20:38:21 +00:00
|
|
|
C4Object* C4Player::GetHiRankActiveCrew()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2014-10-24 20:50:14 +00:00
|
|
|
C4Object *hirank=NULL;
|
2009-05-08 13:28:41 +00:00
|
|
|
int32_t iHighestRank=-2, iRank;
|
2014-10-24 20:50:14 +00:00
|
|
|
for (C4Object *cobj : Crew)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (!cobj->CrewDisabled)
|
2010-07-30 20:38:21 +00:00
|
|
|
{
|
|
|
|
if (cobj->Info) iRank = cobj->Info->Rank; else iRank=-1;
|
|
|
|
if (!hirank || (iRank>iHighestRank))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-07-30 20:38:21 +00:00
|
|
|
hirank=cobj;
|
|
|
|
iHighestRank=iRank;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-07-30 20:38:21 +00:00
|
|
|
}
|
2014-10-24 20:50:14 +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;
|
2010-07-30 20:38:21 +00:00
|
|
|
if ((hirank=GetHiRankActiveCrew()))
|
2009-05-08 13:28:41 +00:00
|
|
|
if (hirank->Info)
|
2010-09-09 20:38:18 +00:00
|
|
|
if (hirank->Info->Rank<1) // No Fähnrich -> except. promo.
|
2010-07-30 20:38:21 +00:00
|
|
|
if ((hirank=GetHiExpActiveCrew()))
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4Player::Message(const char *szMsg)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-08-15 18:50:32 +00:00
|
|
|
if (!szMsg) return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
SCopy(szMsg,MessageBuf,256);
|
|
|
|
MessageStatus=SLen(szMsg)*2;
|
2009-08-15 18:50:32 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2011-08-05 12:58:16 +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
|
2011-01-06 20:18:13 +00:00
|
|
|
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
|
2010-03-27 16:05:02 +00:00
|
|
|
if (!C4PlayerInfoCore::Load(hGroup))
|
2009-08-15 18:50:32 +00:00
|
|
|
{ hGroup.Close(); return false; }
|
2009-05-08 13:28:41 +00:00
|
|
|
// Load BigIcon
|
2015-09-19 01:10:39 +00:00
|
|
|
if (hGroup.FindEntry(C4CFN_BigIcon)) BigIcon.Load(hGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0);
|
2010-03-27 16:05:02 +00:00
|
|
|
// Load crew info list
|
2011-08-05 12:58:16 +00:00
|
|
|
CrewInfoList.Load(hGroup);
|
2009-05-08 13:28:41 +00:00
|
|
|
// Close group
|
|
|
|
hGroup.Close();
|
|
|
|
// Success
|
2009-08-15 18:50:32 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +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))
|
2009-08-15 18:50:32 +00:00
|
|
|
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;
|
2011-08-05 12:58:16 +00:00
|
|
|
if (!PlrInfoCore.Load(Grp) || !CrewInfoList.Load(Grp))
|
2009-08-15 18:50:32 +00:00
|
|
|
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))
|
2009-08-15 18:50:32 +00:00
|
|
|
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))
|
2009-08-15 18:50:32 +00:00
|
|
|
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)
|
2010-01-25 15:57:57 +00:00
|
|
|
if (Hostility.find(pPlr) != Hostility.end())
|
2009-06-05 15:19:15 +00:00
|
|
|
::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
|
|
|
|
2009-08-15 18:50:32 +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;
|
2009-08-15 18:50:32 +00:00
|
|
|
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))
|
2009-08-15 18:50:32 +00:00
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Set object info
|
|
|
|
pObj->Info = cInf;
|
2009-04-03 19:06:29 +00:00
|
|
|
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))
|
2013-01-13 14:45:51 +00:00
|
|
|
Crew.Add(pObj, C4ObjectList::stNone);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2015-01-02 00:06:00 +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
|
|
|
{
|
2014-05-30 23:03:23 +00:00
|
|
|
C4AulParSet parset(Number);
|
2009-05-08 13:28:41 +00:00
|
|
|
pObj->Call(PSF_OnJoinCrew, &parset);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +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
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
// Reset view
|
|
|
|
ResetCursorView();
|
2010-07-30 20:38:21 +00:00
|
|
|
// Default cursor to hirank clonk
|
|
|
|
if (!Cursor || Cursor->CrewDisabled)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-07-30 20:38:21 +00:00
|
|
|
C4Object *pHiRank = GetHiRankActiveCrew();
|
|
|
|
if (!pHiRank)
|
|
|
|
return;
|
2010-07-31 16:38:23 +00:00
|
|
|
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
|
|
|
|
2011-03-26 22:59:35 +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);
|
|
|
|
|
2009-10-14 16:46:22 +00:00
|
|
|
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));
|
2010-09-08 21:49:42 +00:00
|
|
|
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));
|
2014-04-21 17:38:05 +00:00
|
|
|
pComp->Value(mkNamingAdapt(ZoomLimitMinVal, "ZoomLimitMinVal", Fix0));
|
|
|
|
pComp->Value(mkNamingAdapt(ZoomLimitMaxVal, "ZoomLimitMaxVal", Fix0));
|
|
|
|
pComp->Value(mkNamingAdapt(ZoomVal, "ZoomVal", Fix0));
|
2009-10-14 16:46:22 +00:00
|
|
|
pComp->Value(mkNamingAdapt(fFogOfWar, "FogOfWar", false));
|
|
|
|
pComp->Value(mkNamingAdapt(ShowStartup, "ShowStartup", false));
|
|
|
|
pComp->Value(mkNamingAdapt(Wealth, "Wealth", 0));
|
2010-04-21 19:12:49 +00:00
|
|
|
pComp->Value(mkNamingAdapt(CurrentScore, "Score", 0));
|
|
|
|
pComp->Value(mkNamingAdapt(InitialScore, "InitialScore", 0));
|
2009-10-14 16:46:22 +00:00
|
|
|
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));
|
2010-03-30 21:08:15 +00:00
|
|
|
pComp->Value(mkNamingAdapt(Cursor, "Cursor", C4ObjectPtr::Null));
|
|
|
|
pComp->Value(mkNamingAdapt(ViewCursor, "ViewCursor", C4ObjectPtr::Null));
|
2009-10-14 16:46:22 +00:00
|
|
|
pComp->Value(mkNamingAdapt(MessageStatus, "MessageStatus", 0));
|
2010-03-30 21:08:15 +00:00
|
|
|
pComp->Value(mkNamingAdapt(toC4CStr(MessageBuf),"MessageBuf", ""));
|
2014-04-16 12:06:30 +00:00
|
|
|
pComp->Value(mkNamingAdapt(BaseMaterial, "BaseMaterial" ));
|
|
|
|
pComp->Value(mkNamingAdapt(BaseProduction, "BaseProduction" ));
|
2009-10-14 16:46:22 +00:00
|
|
|
pComp->Value(mkNamingAdapt(Knowledge, "Knowledge" ));
|
2011-03-26 22:59:35 +00:00
|
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(Crew, numbers), "Crew" ));
|
2009-05-08 13:28:41 +00:00
|
|
|
pComp->Value(mkNamingAdapt(CrewInfoList.iNumCreated, "CrewCreated", 0));
|
2009-10-14 16:46:22 +00:00
|
|
|
pComp->Value(mkNamingPtrAdapt( pMsgBoardQuery, "MsgBoardQueries" ));
|
2015-08-28 01:44:23 +00:00
|
|
|
pComp->Value(mkNamingAdapt(mkParAdapt(SoundModifier, numbers), "SoundModifier", C4Value()));
|
|
|
|
|
2015-12-02 15:38:22 +00:00
|
|
|
if (pComp->isCompiler())
|
|
|
|
{
|
|
|
|
SoundModifier.Denumerate(numbers);
|
|
|
|
}
|
2009-06-16 00:38:39 +00:00
|
|
|
|
|
|
|
// Keys held down
|
|
|
|
pComp->Value(Control);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2011-03-26 22:59:35 +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
|
2009-08-15 18:50:32 +00:00
|
|
|
if (!(pSource = Game.GameText.GetData())) return false;
|
2011-09-22 18:00:17 +00:00
|
|
|
// safety: Do nothing if player section is not even present (could kill initialized values)
|
2009-08-15 18:50:32 +00:00
|
|
|
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)
|
2009-06-16 00:38:39 +00:00
|
|
|
// 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>(
|
2011-03-26 22:59:35 +00:00
|
|
|
mkNamingAdapt(mkParAdapt(*this, numbers), FormatString("Player%i", ID).getData()),
|
2010-03-28 18:58:01 +00:00
|
|
|
StdStrBuf(pSource),
|
|
|
|
Game.GameText.GetFilePath()))
|
2009-08-15 18:50:32 +00:00
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Denumerate pointers
|
|
|
|
DenumeratePointers();
|
|
|
|
// Success
|
2009-08-15 18:50:32 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2014-04-16 12:06:30 +00:00
|
|
|
void C4Player::ExecBaseProduction()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2014-04-16 12:06:30 +00:00
|
|
|
const int32_t MaxBaseProduction = 25;
|
2010-03-27 16:05:02 +00:00
|
|
|
ProductionDelay++;
|
|
|
|
if (ProductionDelay>=60) // Minute Production Unit
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-03-27 16:05:02 +00:00
|
|
|
ProductionDelay=0; ProductionUnit++;
|
2014-04-16 12:06:30 +00:00
|
|
|
for (int32_t cnt=0; BaseProduction.GetID(cnt); cnt++)
|
|
|
|
if (BaseProduction.GetCount(cnt)>0)
|
2015-02-12 22:05:55 +00:00
|
|
|
if (ProductionUnit % Clamp<int32_t>(11-BaseProduction.GetCount(cnt),1,10) ==0)
|
2014-04-16 12:06:30 +00:00
|
|
|
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
|
2010-03-27 16:05:02 +00:00
|
|
|
switch (ViewMode)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
case C4PVM_Cursor:
|
|
|
|
{
|
|
|
|
C4Object *pViewObj;
|
|
|
|
if (!(pViewObj=ViewCursor)) pViewObj=Cursor;
|
|
|
|
if (pViewObj)
|
2010-03-27 16:05:02 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
ViewX=pViewObj->GetX(); ViewY=pViewObj->GetY();
|
2010-03-27 16:05:02 +00:00
|
|
|
}
|
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;
|
2009-10-14 16:46:22 +00:00
|
|
|
Eliminated=0;
|
|
|
|
Surrendered=0;
|
2009-05-08 13:28:41 +00:00
|
|
|
AtClient=C4ClientIDUnknown;
|
|
|
|
SCopy("Local",AtClientName);
|
2009-10-14 16:46:22 +00:00
|
|
|
ControlSet = NULL;
|
|
|
|
ControlSetName.Clear();
|
2009-08-15 18:50:32 +00:00
|
|
|
MouseControl=false;
|
2009-10-14 16:46:22 +00:00
|
|
|
Position=-1;
|
2009-05-08 13:28:41 +00:00
|
|
|
PlrStartIndex=0;
|
|
|
|
RetireDelay=0;
|
2009-10-14 16:46:22 +00:00
|
|
|
ViewMode=C4PVM_Cursor;
|
|
|
|
ViewX=ViewY=0;
|
|
|
|
ViewTarget=NULL;
|
|
|
|
ShowStartup=true;
|
2009-05-08 13:28:41 +00:00
|
|
|
Wealth=0;
|
2010-04-21 19:12:49 +00:00
|
|
|
CurrentScore=InitialScore=0;
|
2009-10-14 16:46:22 +00:00
|
|
|
ObjectsOwned=0;
|
2009-05-08 13:28:41 +00:00
|
|
|
ProductionDelay=ProductionUnit=0;
|
|
|
|
Cursor=ViewCursor=NULL;
|
2010-07-30 20:38:21 +00:00
|
|
|
CursorFlash=30;
|
2009-05-08 13:28:41 +00:00
|
|
|
MessageStatus=0;
|
|
|
|
MessageBuf[0]=0;
|
2010-01-25 15:57:57 +00:00
|
|
|
Hostility.clear();
|
2014-04-16 12:06:30 +00:00
|
|
|
BaseMaterial.Default();
|
|
|
|
BaseProduction.Default();
|
2009-10-14 16:46:22 +00:00
|
|
|
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);
|
|
|
|
Menu.InitRefSym(C4GUI::Icon::GetIconFacet(C4GUI::Ico_Team),LoadResStr("IDS_MSG_SELTEAM"),Number, C4MN_Extra_None, 0, fSwitch ? C4MN_TeamSwitch : C4MN_TeamSelection);
|
|
|
|
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;
|
2013-09-02 20:22:29 +00:00
|
|
|
::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
|
2011-09-19 19:02:28 +00:00
|
|
|
Crew.DenumeratePointers();
|
2009-05-08 13:28:41 +00:00
|
|
|
// Cursor
|
2010-03-30 21:08:15 +00:00
|
|
|
Cursor.DenumeratePointers();
|
2009-05-08 13:28:41 +00:00
|
|
|
// ViewCursor
|
2010-03-30 21:08:15 +00:00
|
|
|
ViewCursor.DenumeratePointers();
|
2009-05-08 13:28:41 +00:00
|
|
|
// messageboard-queries
|
|
|
|
for (C4MessageBoardQuery *pCheck = pMsgBoardQuery; pCheck; pCheck = pCheck->pNext)
|
2010-03-30 21:08:15 +00:00
|
|
|
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
|
|
|
{
|
2010-03-27 16:05:02 +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
|
|
|
|
2012-04-01 20:06:43 +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
|
|
|
{
|
2012-04-01 20:06:43 +00:00
|
|
|
int32_t iNewOwner = FindNewOwner();
|
2009-05-08 13:28:41 +00:00
|
|
|
// notify objects in all object lists
|
2009-06-15 21:47:26 +00:00
|
|
|
for (C4ObjectList *pList = &::Objects; pList; pList = ((pList == &::Objects) ? &::Objects.InactiveObjects : NULL))
|
2012-04-01 20:06:43 +00:00
|
|
|
{
|
2014-10-24 20:50:14 +00:00
|
|
|
for (C4Object *cobj : *pList)
|
2012-04-01 20:06:43 +00:00
|
|
|
{
|
|
|
|
if (cobj->Status && cobj->Owner == Number)
|
|
|
|
{
|
|
|
|
C4AulFunc *pFn = cobj->GetFunc(PSF_OnOwnerRemoved);
|
|
|
|
if (pFn)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2014-05-30 23:03:23 +00:00
|
|
|
C4AulParSet pars(iNewOwner);
|
2013-03-02 22:38:29 +00:00
|
|
|
pFn->Exec(cobj, &pars);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-04-01 20:06:43 +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
|
2013-03-02 22:38:29 +00:00
|
|
|
// Ignore StaticBack, because this would not be compatible with many internal objects such as team account
|
2015-05-25 14:17:58 +00:00
|
|
|
if ((cobj->Category & C4D_StaticBack) == 0)
|
2012-04-01 20:06:43 +00:00
|
|
|
cobj->SetOwner(iNewOwner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-21 19:12:49 +00:00
|
|
|
bool C4Player::DoScore(int32_t iChange)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2015-02-12 22:05:55 +00:00
|
|
|
CurrentScore = Clamp<int32_t>( CurrentScore+iChange, -100000, 100000 );
|
2009-08-15 18:50:32 +00:00
|
|
|
return true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-07-30 20:38:21 +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
|
2010-03-27 16:05:02 +00:00
|
|
|
Cursor=pObj;
|
2009-05-08 13:28:41 +00:00
|
|
|
// unselect previous
|
2010-07-30 20:38:21 +00:00
|
|
|
if (pPrev && fChanged) pPrev->UnSelect();
|
2009-05-08 13:28:41 +00:00
|
|
|
// Select object
|
2010-07-31 16:38:23 +00:00
|
|
|
if (fChanged && Cursor) { Cursor->DoSelect(); }
|
2009-05-08 13:28:41 +00:00
|
|
|
// View flash
|
2010-03-27 16:05:02 +00:00
|
|
|
if (fSelectArrow) CursorFlash=30;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-09-24 17:52:20 +00:00
|
|
|
void C4Player::ScrollView(float iX, float iY, float ViewWdt, float ViewHgt)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-09-08 21:49:42 +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;
|
2016-04-02 15:50:49 +00:00
|
|
|
ViewX = Clamp<C4Real>( ViewX+ftofix(iX), ftofix(ViewWdt/2.0f-ViewportScrollBorder), ftofix(::Landscape.GetWidth()+ViewportScrollBorder-ViewWdt/2.0f) );
|
|
|
|
ViewY = Clamp<C4Real>( ViewY+ftofix(iY), ftofix(ViewHgt/2.0f-ViewportScrollBorder), ftofix(::Landscape.GetHeight()+ViewportScrollBorder-ViewHgt/2.0f) );
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-10-16 21:07:43 +00:00
|
|
|
void C4Player::ClearControl()
|
2009-10-13 20:02:44 +00:00
|
|
|
{
|
2009-10-16 21:07:43 +00:00
|
|
|
// Mark any control set as unused
|
|
|
|
Control.Clear();
|
2009-10-14 16:46:22 +00:00
|
|
|
// Reset control
|
2009-10-16 21:07:43 +00:00
|
|
|
LocalControl = false;
|
2009-10-14 16:46:22 +00:00
|
|
|
ControlSetName.Clear();
|
|
|
|
ControlSet=NULL;
|
|
|
|
MouseControl = false;
|
2016-02-20 17:26:30 +00:00
|
|
|
if (pGamepad)
|
|
|
|
{
|
|
|
|
pGamepad->SetPlayer(NO_OWNER);
|
|
|
|
pGamepad.reset();
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// no controls issued yet
|
|
|
|
ControlCount = ActionCount = 0;
|
|
|
|
LastControlType = PCID_None;
|
|
|
|
LastControlID = 0;
|
2009-10-16 21:07:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void C4Player::InitControl()
|
|
|
|
{
|
|
|
|
// Check local control
|
|
|
|
if (AtClient == ::Control.ClientID())
|
|
|
|
if (!GetInfo() || GetInfo()->GetType() == C4PT_User)
|
|
|
|
if (!::Control.isReplay())
|
|
|
|
LocalControl=true;
|
2009-10-14 16:46:22 +00:00
|
|
|
// needs to init control for local players only
|
|
|
|
if (LocalControl)
|
2009-10-13 20:02:44 +00:00
|
|
|
{
|
2009-10-14 16:46:22 +00:00
|
|
|
// Preferred control
|
|
|
|
ControlSetName = PrefControl;
|
2011-03-30 20:11:47 +00:00
|
|
|
ControlSet = Game.PlayerControlUserAssignmentSets.GetSetByName(ControlSetName.getData());
|
2009-10-14 16:46:22 +00:00
|
|
|
// 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();
|
2009-10-14 16:46:22 +00:00
|
|
|
// gamepad control safety (assuming the default control set is not using gamepad)
|
|
|
|
if (ControlSet && ControlSet->HasGamepad() && !Config.General.GamepadEnabled)
|
2009-10-13 20:02:44 +00:00
|
|
|
{
|
2011-03-30 20:11:47 +00:00
|
|
|
ControlSet = Game.PlayerControlUserAssignmentSets.GetDefaultSet();
|
2009-10-13 20:02:44 +00:00
|
|
|
}
|
2009-10-14 16:46:22 +00:00
|
|
|
// Choose next while control taken
|
|
|
|
// TODO
|
|
|
|
// init gamepad
|
|
|
|
if (ControlSet && ControlSet->HasGamepad())
|
2009-10-13 20:02:44 +00:00
|
|
|
{
|
2016-02-20 17:26:30 +00:00
|
|
|
if (!FindGamepad())
|
|
|
|
{
|
|
|
|
LogF("No gamepad available for %s, please plug one in!", Name.getData());
|
|
|
|
::Game.Pause();
|
|
|
|
}
|
2009-10-13 20:02:44 +00:00
|
|
|
}
|
2009-10-14 16:46:22 +00:00
|
|
|
// Mouse
|
|
|
|
if (ControlSet && ControlSet->HasMouse() && PrefMouse)
|
2010-02-18 23:03:53 +00:00
|
|
|
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
|
2013-09-04 13:45:00 +00:00
|
|
|
::Control.DoInput(CID_PlrAction, C4ControlPlayerAction::InitPlayerControl(this, ControlSet), CDT_Queue);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2009-10-14 16:46:22 +00:00
|
|
|
// clear old control method and register new
|
|
|
|
Control.RegisterKeyset(Number, ControlSet);
|
2009-10-13 20:02:44 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2016-02-20 17:26:30 +00:00
|
|
|
bool C4Player::FindGamepad()
|
|
|
|
{
|
|
|
|
auto newPad = Application.pGamePadControl->GetAvailableGamePad();
|
|
|
|
if (!newPad) return false;
|
|
|
|
newPad->SetPlayer(ID);
|
|
|
|
// Release the old gamepad.
|
|
|
|
if (pGamepad) pGamepad->SetPlayer(NO_OWNER);
|
|
|
|
pGamepad = newPad;
|
|
|
|
LogF("%s: Using gamepad #%d.", Name.getData(), pGamepad->GetID());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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;
|
2015-11-15 12:53:01 +00:00
|
|
|
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;
|
2010-03-27 16:05:02 +00:00
|
|
|
Eliminated=true;
|
2009-05-08 13:28:41 +00:00
|
|
|
RetireDelay=C4RetireDelay;
|
2015-12-13 21:14:55 +00:00
|
|
|
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-27 16:05:02 +00:00
|
|
|
}
|
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;
|
2014-10-24 20:50:14 +00:00
|
|
|
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
|
|
|
{
|
2010-07-30 20:38:21 +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?
|
2009-08-15 18:50:32 +00:00
|
|
|
if (LeagueEvaluated) return; LeagueEvaluated=true;
|
2010-03-27 16:05:02 +00:00
|
|
|
// set fate
|
|
|
|
C4PlayerInfo *pInfo = GetInfo();
|
2010-03-28 18:58:01 +00:00
|
|
|
if (pInfo)
|
|
|
|
{
|
|
|
|
if (fDisconnected)
|
2010-03-27 16:05:02 +00:00
|
|
|
pInfo->SetDisconnected();
|
2010-03-28 18:58:01 +00:00
|
|
|
if (fWon)
|
2010-03-27 16:05:02 +00:00
|
|
|
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
|
|
|
|
2009-08-15 18:50:32 +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
|
2009-08-15 18:50:32 +00:00
|
|
|
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())
|
2009-08-15 18:50:32 +00:00
|
|
|
return false;
|
2009-05-08 13:28:41 +00:00
|
|
|
// done, success
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
|
|
|
2009-08-15 18:50:32 +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?
|
2009-08-15 18:50:32 +00:00
|
|
|
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?
|
2009-08-15 18:50:32 +00:00
|
|
|
if (!Crew.IsContained(pCrew)) return true;
|
2009-05-08 13:28:41 +00:00
|
|
|
// ...or remove
|
|
|
|
Crew.Remove(pCrew);
|
2014-05-30 23:03:23 +00:00
|
|
|
C4AulParSet parset(Number);
|
2009-12-14 21:44:55 +00:00
|
|
|
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
|
2009-08-15 18:50:32 +00:00
|
|
|
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?
|
2009-06-05 15:18:45 +00:00
|
|
|
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
|
2010-03-30 21:08:15 +00:00
|
|
|
::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;
|
2010-03-30 21:08:15 +00:00
|
|
|
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;
|
2010-03-30 21:08:15 +00:00
|
|
|
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;
|
2014-10-24 20:50:14 +00:00
|
|
|
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);
|
2009-08-15 18:50:32 +00:00
|
|
|
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
|
|
|
}
|
2010-01-25 15:57:57 +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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-09-08 21:49:42 +00:00
|
|
|
|
|
|
|
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);
|
2010-12-12 21:38:19 +00:00
|
|
|
ZoomToViewports(direct, no_decrease, no_increase); // inc/dec swapped for zoom, because it's inversely proportional to range
|
2010-09-08 21:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2010-12-12 21:38:19 +00:00
|
|
|
ZoomLimitsToViewports();
|
2010-09-08 21:49:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2010-12-12 21:38:19 +00:00
|
|
|
ZoomLimitsToViewports();
|
2010-09-08 21:49:42 +00:00
|
|
|
}
|
|
|
|
|
2014-04-21 17:38:05 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2010-12-12 21:38:19 +00:00
|
|
|
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)
|
2010-09-08 21:49:42 +00:00
|
|
|
{
|
2014-04-21 17:38:05 +00:00
|
|
|
float new_zoom = ZoomVal ? fixtof(ZoomVal) : vp->GetZoomByViewRange((ZoomWdt || ZoomHgt) ? ZoomWdt : C4VP_DefViewRangeX,ZoomHgt);
|
2010-09-08 21:49:42 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-12-12 21:38:19 +00:00
|
|
|
void C4Player::ZoomLimitsToViewports()
|
|
|
|
{
|
|
|
|
C4Viewport *vp = NULL;
|
|
|
|
while((vp = ::Viewports.GetViewport(Number, vp)) != NULL)
|
|
|
|
ZoomLimitsToViewport(vp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void C4Player::ZoomLimitsToViewport(C4Viewport* vp)
|
2010-09-08 21:49:42 +00:00
|
|
|
{
|
2014-04-21 17:38:05 +00:00
|
|
|
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);
|
2010-09-08 21:49:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-04-21 17:38:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-09-08 21:49:42 +00:00
|
|
|
void C4Player::SetViewLocked(bool to_val)
|
|
|
|
{
|
|
|
|
if ((ViewLock = to_val))
|
|
|
|
{
|
|
|
|
// view was locked - cancel any scrolling
|
|
|
|
if (ViewMode == C4PVM_Scrolling) SetViewMode(C4PVM_Cursor);
|
|
|
|
}
|
2010-09-09 20:38:18 +00:00
|
|
|
}
|
2014-09-24 21:08:40 +00:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2014-09-24 21:08:40 +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
|
|
|
}
|
2014-09-24 21:08:40 +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;
|
|
|
|
}
|
2015-08-28 01:44:23 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|