graphics: basic terrain passibility atlas: lots of changes to make atlas compile under linux unix/X: more clipboard support - copy from 0AD to other programs unix/debug: use sys_get_executable_name instead of hard-coded paths ... and lots of other misc. changes This was SVN commit r4640.
430 lines
11 KiB
C++
430 lines
11 KiB
C++
#include "precompiled.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "GameAttributes.h"
|
|
#include "Game.h"
|
|
#include "ConfigDB.h"
|
|
#include "network/ServerSession.h"
|
|
#include "CLogger.h"
|
|
#include "ps/XML/Xeromyces.h"
|
|
|
|
using namespace std;
|
|
|
|
CPlayerSlot::CPlayerSlot(int slotID, CPlayer *pPlayer):
|
|
m_SlotID(slotID),
|
|
m_Assignment(SLOT_OPEN),
|
|
m_pSession(NULL),
|
|
m_SessionID(0),
|
|
m_pPlayer(pPlayer),
|
|
m_Callback(NULL)
|
|
{
|
|
ONCE(
|
|
ScriptingInit();
|
|
);
|
|
|
|
//AddProperty(L"session", (GetFn)&CPlayerSlot::JSI_GetSession);
|
|
AddLocalProperty(L"session", &m_pSession, true );
|
|
AddLocalProperty(L"player", &m_pPlayer, true );
|
|
}
|
|
|
|
CPlayerSlot::~CPlayerSlot()
|
|
{}
|
|
|
|
void CPlayerSlot::ScriptingInit()
|
|
{
|
|
AddMethod<bool, &CPlayerSlot::JSI_AssignClosed>("assignClosed", 0);
|
|
AddMethod<bool, &CPlayerSlot::JSI_AssignToSession>("assignToSession", 1);
|
|
AddMethod<bool, &CPlayerSlot::JSI_AssignLocal>("assignLocal", 0);
|
|
AddMethod<bool, &CPlayerSlot::JSI_AssignOpen>("assignOpen", 0);
|
|
AddProperty(L"assignment", &CPlayerSlot::JSI_GetAssignment);
|
|
// AddMethod<bool, &CPlayerSlot::JSI_AssignAI>("assignAI", <num_args>);
|
|
|
|
CJSObject<CPlayerSlot>::ScriptingInit("PlayerSlot");
|
|
}
|
|
|
|
jsval CPlayerSlot::JSI_GetSession(JSContext* UNUSED(cx))
|
|
{
|
|
if (m_pSession)
|
|
return OBJECT_TO_JSVAL(m_pSession->GetScript());
|
|
else
|
|
return JSVAL_NULL;
|
|
}
|
|
|
|
jsval CPlayerSlot::JSI_GetAssignment(JSContext* UNUSED(cx))
|
|
{
|
|
switch (m_Assignment)
|
|
{
|
|
case SLOT_CLOSED:
|
|
return g_ScriptingHost.UCStringToValue(L"closed");
|
|
case SLOT_OPEN:
|
|
return g_ScriptingHost.UCStringToValue(L"open");
|
|
case SLOT_SESSION:
|
|
return g_ScriptingHost.UCStringToValue(L"session");
|
|
/* case SLOT_AI:*/
|
|
default:
|
|
return INT_TO_JSVAL(m_Assignment);
|
|
}
|
|
}
|
|
|
|
bool CPlayerSlot::JSI_AssignClosed(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
|
{
|
|
AssignClosed();
|
|
return true;
|
|
}
|
|
|
|
bool CPlayerSlot::JSI_AssignOpen(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
|
{
|
|
AssignOpen();
|
|
return true;
|
|
}
|
|
|
|
bool CPlayerSlot::JSI_AssignToSession(JSContext* UNUSED(cx), uintN argc, jsval* argv)
|
|
{
|
|
if (argc != 1)
|
|
return false;
|
|
CNetServerSession *pSession=ToNative<CNetServerSession>(argv[0]);
|
|
if (pSession)
|
|
{
|
|
AssignToSession(pSession);
|
|
return true;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
bool CPlayerSlot::JSI_AssignLocal(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
|
{
|
|
AssignToSessionID(1);
|
|
return true;
|
|
}
|
|
|
|
void CPlayerSlot::CallCallback()
|
|
{
|
|
if (m_Callback)
|
|
m_Callback(m_CallbackData, this);
|
|
}
|
|
|
|
void CPlayerSlot::SetAssignment(EPlayerSlotAssignment assignment,
|
|
CNetServerSession *pSession, int sessionID)
|
|
{
|
|
m_Assignment=assignment;
|
|
m_pSession=pSession;
|
|
m_SessionID=sessionID;
|
|
|
|
CallCallback();
|
|
}
|
|
|
|
void CPlayerSlot::AssignClosed()
|
|
{
|
|
SetAssignment(SLOT_CLOSED, NULL, -1);
|
|
}
|
|
|
|
void CPlayerSlot::AssignOpen()
|
|
{
|
|
SetAssignment(SLOT_OPEN, NULL, -1);
|
|
}
|
|
|
|
void CPlayerSlot::AssignToSession(CNetServerSession *pSession)
|
|
{
|
|
SetAssignment(SLOT_SESSION, pSession, pSession->GetID());
|
|
m_pPlayer->SetName(pSession->GetName());
|
|
}
|
|
|
|
void CPlayerSlot::AssignToSessionID(int id)
|
|
{
|
|
SetAssignment(SLOT_SESSION, NULL, id);
|
|
}
|
|
|
|
void CPlayerSlot::AssignLocal()
|
|
{
|
|
AssignToSessionID(1);
|
|
}
|
|
|
|
namespace PlayerSlotArray_JS
|
|
{
|
|
JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
|
{
|
|
CGameAttributes *pInstance=(CGameAttributes *)JS_GetPrivate(cx, obj);
|
|
if (!JSVAL_IS_INT(id))
|
|
return JS_FALSE;
|
|
uint index=ToPrimitive<uint>(id);
|
|
|
|
if (index >= pInstance->m_NumSlots)
|
|
return JS_FALSE;
|
|
|
|
*vp=OBJECT_TO_JSVAL(pInstance->m_PlayerSlots[index]->GetScript());
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JSBool SetProperty( JSContext* UNUSED(cx), JSObject* UNUSED(obj), jsval UNUSED(id), jsval* UNUSED(vp) )
|
|
{
|
|
return JS_FALSE;
|
|
}
|
|
|
|
JSClass Class = {
|
|
"PlayerSlotArray", JSCLASS_HAS_PRIVATE,
|
|
JS_PropertyStub, JS_PropertyStub,
|
|
GetProperty, SetProperty,
|
|
JS_EnumerateStub, JS_ResolveStub,
|
|
JS_ConvertStub, JS_FinalizeStub
|
|
};
|
|
|
|
JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* UNUSED(argv), jsval* rval )
|
|
{
|
|
if (argc != 0)
|
|
return JS_FALSE;
|
|
|
|
JSObject *newObj=JS_NewObject(cx, &Class, NULL, obj);
|
|
*rval=OBJECT_TO_JSVAL(newObj);
|
|
return JS_TRUE;
|
|
}
|
|
};
|
|
|
|
CGameAttributes::CGameAttributes():
|
|
m_MapFile("test01.pmp"),
|
|
m_ResourceLevel("default"),
|
|
m_StartingPhase("default"),
|
|
m_LOSSetting(0),
|
|
m_FogOfWar(true),
|
|
m_ScreenshotMode(false),
|
|
m_NumSlots(8),
|
|
m_UpdateCB(NULL),
|
|
m_PlayerUpdateCB(NULL),
|
|
m_PlayerSlotAssignmentCB(NULL)
|
|
{
|
|
ONCE(
|
|
ScriptingInit();
|
|
);
|
|
|
|
m_PlayerSlotArrayJS=g_ScriptingHost.CreateCustomObject("PlayerSlotArray");
|
|
JS_AddRoot(g_ScriptingHost.GetContext(), &m_PlayerSlotArrayJS);
|
|
JS_SetPrivate(g_ScriptingHost.GetContext(), m_PlayerSlotArrayJS, this);
|
|
|
|
AddSynchedProperty(L"mapFile", &m_MapFile);
|
|
AddSynchedProperty(L"resourceLevel", &m_ResourceLevel);
|
|
AddSynchedProperty(L"startingPhase", &m_StartingPhase);
|
|
AddSynchedProperty(L"numSlots", &m_NumSlots, &CGameAttributes::OnNumSlotsUpdate);
|
|
AddSynchedProperty(L"losSetting", &m_LOSSetting);
|
|
AddSynchedProperty(L"fogOfWar", &m_FogOfWar);
|
|
AddSynchedProperty(L"screenshotMode", &m_ScreenshotMode);
|
|
|
|
CXeromyces XeroFile;
|
|
if (XeroFile.Load("temp/players.xml") != PSRETURN_OK)
|
|
{
|
|
LOG(ERROR, "", "Failed to load players list (temp/players.xml)");
|
|
|
|
// Basic default players
|
|
|
|
m_Players.push_back(new CPlayer(0));
|
|
m_Players.back()->SetName(L"Gaia");
|
|
m_Players.back()->SetColour(SPlayerColour(1.0f, 1.0f, 1.0f));
|
|
|
|
m_Players.push_back(new CPlayer(1));
|
|
m_Players.back()->SetName(L"Player 1");
|
|
m_Players.back()->SetColour(SPlayerColour(0.4375f, 0.4375f, 0.8125f));
|
|
}
|
|
else
|
|
{
|
|
int at_name = XeroFile.getAttributeID("name");
|
|
int at_rgb = XeroFile.getAttributeID("rgb");
|
|
|
|
XMBElement root = XeroFile.getRoot();
|
|
XERO_ITER_EL(root, player)
|
|
{
|
|
XMBAttributeList attr = player.getAttributes();
|
|
m_Players.push_back(new CPlayer((int)m_Players.size()));
|
|
m_Players.back()->SetName(attr.getNamedItem(at_name));
|
|
|
|
std::stringstream str;
|
|
str << (CStr)attr.getNamedItem(at_rgb);
|
|
int r, g, b;
|
|
if (str >> r >> g >> b)
|
|
m_Players.back()->SetColour(SPlayerColour(r/255.0f, g/255.0f, b/255.0f));
|
|
}
|
|
}
|
|
|
|
std::vector<CPlayer *>::iterator it=m_Players.begin();
|
|
++it; // Skip Gaia - gaia doesn't account for a slot
|
|
for (;it != m_Players.end();++it)
|
|
{
|
|
m_PlayerSlots.push_back(new CPlayerSlot((*it)->GetPlayerID()-1, *it));
|
|
}
|
|
// The first player is always the server player in MP, or the local player
|
|
// in SP
|
|
m_PlayerSlots[0]->AssignToSessionID(1);
|
|
}
|
|
|
|
CGameAttributes::~CGameAttributes()
|
|
{
|
|
std::vector<CPlayerSlot *>::iterator it=m_PlayerSlots.begin();
|
|
while (it != m_PlayerSlots.end())
|
|
{
|
|
delete (*it)->GetPlayer();
|
|
delete *it;
|
|
++it;
|
|
}
|
|
|
|
// PT: ??? - Gaia isn't a player slot, but still needs to be deleted; but
|
|
// this feels rather unpleasantly inconsistent with the above code, so maybe
|
|
// there's a better way to avoid the memory leak.
|
|
delete m_Players[0];
|
|
|
|
JS_RemoveRoot(g_ScriptingHost.GetContext(), &m_PlayerSlotArrayJS);
|
|
}
|
|
|
|
void CGameAttributes::ScriptingInit()
|
|
{
|
|
g_ScriptingHost.DefineCustomObjectType(&PlayerSlotArray_JS::Class,
|
|
PlayerSlotArray_JS::Construct, 0, NULL, NULL, NULL, NULL);
|
|
|
|
AddMethod<jsval, &CGameAttributes::JSI_GetOpenSlot>("getOpenSlot", 0);
|
|
AddProperty(L"slots", &CGameAttributes::JSI_GetPlayerSlots);
|
|
|
|
CJSObject<CGameAttributes>::ScriptingInit("GameAttributes");
|
|
}
|
|
|
|
jsval CGameAttributes::JSI_GetOpenSlot(JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
|
{
|
|
vector <CPlayerSlot *>::iterator it;
|
|
for (it = m_PlayerSlots.begin();it != m_PlayerSlots.end();++it)
|
|
{
|
|
if ((*it)->GetAssignment() == SLOT_OPEN)
|
|
return OBJECT_TO_JSVAL((*it)->GetScript());
|
|
}
|
|
return JSVAL_NULL;
|
|
}
|
|
|
|
jsval CGameAttributes::JSI_GetPlayerSlots(JSContext* UNUSED(cx))
|
|
{
|
|
return OBJECT_TO_JSVAL(m_PlayerSlotArrayJS);
|
|
}
|
|
|
|
void CGameAttributes::OnNumSlotsUpdate(CSynchedJSObjectBase *owner)
|
|
{
|
|
CGameAttributes *pInstance=(CGameAttributes*)owner;
|
|
|
|
// Clamp to a preset upper bound.
|
|
CConfigValue *val=g_ConfigDB.GetValue(CFG_MOD, "max_players");
|
|
uint maxPlayers=PS_MAX_PLAYERS;
|
|
if (val)
|
|
val->GetUnsignedInt(maxPlayers);
|
|
if (pInstance->m_NumSlots > maxPlayers)
|
|
pInstance->m_NumSlots = maxPlayers;
|
|
|
|
// Resize array according to new number of slots (note that the array
|
|
// size will go up to maxPlayers (above), but will never be made smaller -
|
|
// this to avoid losing player pointers and make sure they are all
|
|
// reclaimed in the end - it's just simpler that way ;-) )
|
|
if (pInstance->m_NumSlots > pInstance->m_PlayerSlots.size())
|
|
{
|
|
for (size_t i=pInstance->m_PlayerSlots.size();i<=pInstance->m_NumSlots;i++)
|
|
{
|
|
CPlayer *pNewPlayer=new CPlayer((uint)i+1);
|
|
pNewPlayer->SetUpdateCallback(pInstance->m_PlayerUpdateCB,
|
|
pInstance->m_PlayerUpdateCBData);
|
|
pInstance->m_Players.push_back(pNewPlayer);
|
|
|
|
CPlayerSlot *pNewSlot=new CPlayerSlot((uint)i, pNewPlayer);
|
|
pNewSlot->SetCallback(pInstance->m_PlayerSlotAssignmentCB,
|
|
pInstance->m_PlayerSlotAssignmentCBData);
|
|
pInstance->m_PlayerSlots.push_back(pNewSlot);
|
|
}
|
|
}
|
|
}
|
|
|
|
CPlayer *CGameAttributes::GetPlayer(int id)
|
|
{
|
|
if (id >= 0 && id < (int)m_Players.size())
|
|
{
|
|
return m_Players[id];
|
|
}
|
|
else
|
|
{
|
|
LOG(ERROR, "", "CGameAttributes::GetPlayer(): Attempt to get player %d (while there only are %d players)", id, m_Players.size());
|
|
return m_Players[0];
|
|
}
|
|
}
|
|
|
|
CPlayerSlot *CGameAttributes::GetSlot(int index)
|
|
{
|
|
if (index >= 0 && index < (int)m_PlayerSlots.size())
|
|
return m_PlayerSlots[index];
|
|
else
|
|
{
|
|
LOG(ERROR, "", "CGameAttributes::GetSlot(): Attempt to get slot %d (while there only are %d slots)", index, m_PlayerSlots.size());
|
|
return m_PlayerSlots[0];
|
|
}
|
|
}
|
|
|
|
void CGameAttributes::FinalizeSlots()
|
|
{
|
|
// Back up our old slots, and empty the resulting vector
|
|
vector<CPlayerSlot *> oldSlots;
|
|
oldSlots.swap(m_PlayerSlots);
|
|
vector<CPlayer *> oldPlayers;
|
|
oldPlayers.swap(m_Players);
|
|
m_Players.push_back(oldPlayers[0]); // Gaia
|
|
|
|
// Copy over the slots we want
|
|
uint assignedSlots=0;
|
|
for (size_t i=0;i<oldSlots.size();i++)
|
|
{
|
|
CPlayerSlot *slot=oldSlots[i];
|
|
EPlayerSlotAssignment assignment=slot->GetAssignment();
|
|
if (assignment != SLOT_OPEN && assignment != SLOT_CLOSED)
|
|
{
|
|
m_PlayerSlots.push_back(slot);
|
|
slot->GetPlayer()->SetPlayerID(assignedSlots+1);
|
|
m_Players.push_back(slot->GetPlayer());
|
|
|
|
assignedSlots++;
|
|
}
|
|
else
|
|
{
|
|
LOG(ERROR, "", "CGameAttributes::FinalizeSlots(): slot %d deleted", i);
|
|
delete slot->GetPlayer();
|
|
delete slot;
|
|
}
|
|
}
|
|
|
|
m_NumSlots=assignedSlots;
|
|
}
|
|
|
|
void CGameAttributes::SetValue(const CStrW& name, const CStrW& value)
|
|
{
|
|
ISynchedJSProperty *prop=GetSynchedProperty(name);
|
|
if (prop)
|
|
{
|
|
prop->FromString(value);
|
|
}
|
|
}
|
|
|
|
void CGameAttributes::Update(const CStrW& name, ISynchedJSProperty *attrib)
|
|
{
|
|
if (m_UpdateCB)
|
|
m_UpdateCB(name, attrib->ToString(), m_UpdateCBData);
|
|
}
|
|
|
|
void CGameAttributes::SetPlayerUpdateCallback(CPlayer::UpdateCallback *cb, void *userdata)
|
|
{
|
|
m_PlayerUpdateCB=cb;
|
|
m_PlayerUpdateCBData=userdata;
|
|
|
|
for (size_t i=0;i<m_Players.size();i++)
|
|
{
|
|
m_Players[i]->SetUpdateCallback(cb, userdata);
|
|
}
|
|
}
|
|
|
|
void CGameAttributes::SetPlayerSlotAssignmentCallback(PlayerSlotAssignmentCB *cb, void *userdata)
|
|
{
|
|
m_PlayerSlotAssignmentCB=cb;
|
|
m_PlayerSlotAssignmentCBData=userdata;
|
|
|
|
for (size_t i=0;i<m_PlayerSlots.size();i++)
|
|
{
|
|
m_PlayerSlots[i]->SetCallback(cb, userdata);
|
|
}
|
|
}
|