Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js (revision 21166)
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js (revision 21167)
@@ -1,338 +1,340 @@
/**
* Used to create player entities prior to reading the rest of a map,
* all other initialization must be done after loading map (terrain/entities).
* DO NOT use other components here, as they may fail unpredictably.
* settings is the object containing settings for this map.
* newPlayers if true will remove old player entities or add new ones until
* the new number of player entities is obtained
* (used when loading a map or when Atlas changes the number of players).
*/
function LoadPlayerSettings(settings, newPlayers)
{
var playerDefaults = Engine.ReadJSONFile("simulation/data/settings/player_defaults.json").PlayerData;
// Default settings
if (!settings)
settings = {};
// Add gaia to simplify iteration
- if (settings.PlayerData && settings.PlayerData[0])
+ // (if gaia is not already the first civ such as when called from Atlas' ActorViewer)
+ if (settings.PlayerData && settings.PlayerData[0] &&
+ (!settings.PlayerData[0].Civ || settings.PlayerData[0].Civ != "gaia"))
settings.PlayerData.unshift(null);
var playerData = settings.PlayerData;
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var numPlayers = cmpPlayerManager.GetNumPlayers();
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
// Remove existing players or add new ones
if (newPlayers)
{
var settingsNumPlayers = 9; // default 8 players + gaia
if (playerData)
settingsNumPlayers = playerData.length; // includes gaia (see above)
else
warn("Player.js: Setup has no player data - using defaults");
while (settingsNumPlayers > numPlayers)
{
// Add player entity to engine
var entID = Engine.AddEntity(GetPlayerTemplateName(getSetting(playerData, playerDefaults, numPlayers, "Civ")));
var cmpPlayer = Engine.QueryInterface(entID, IID_Player);
if (!cmpPlayer)
throw new Error("Player.js: Error creating player entity " + numPlayers);
cmpPlayerManager.AddPlayer(entID);
++numPlayers;
}
while (settingsNumPlayers < numPlayers)
{
cmpPlayerManager.RemoveLastPlayer();
--numPlayers;
}
}
// Even when no new player, we must check the template compatibility as player template may be civ dependent
for (var i = 0; i < numPlayers; ++i)
{
var template = GetPlayerTemplateName(getSetting(playerData, playerDefaults, i, "Civ"));
var entID = cmpPlayerManager.GetPlayerByID(i);
if (cmpTemplateManager.GetCurrentTemplateName(entID) === template)
continue;
// We need to recreate this player to have the right template
entID = Engine.AddEntity(template);
cmpPlayerManager.ReplacePlayer(i, entID);
}
// Initialize the player data
for (var i = 0; i < numPlayers; ++i)
{
let cmpPlayer = QueryPlayerIDInterface(i);
cmpPlayer.SetName(getSetting(playerData, playerDefaults, i, "Name"));
cmpPlayer.SetCiv(getSetting(playerData, playerDefaults, i, "Civ"));
var color = getSetting(playerData, playerDefaults, i, "Color");
cmpPlayer.SetColor(color.r, color.g, color.b);
// Special case for gaia
if (i == 0)
{
// Gaia should be its own ally.
cmpPlayer.SetAlly(0);
// Gaia is everyone's enemy
for (var j = 1; j < numPlayers; ++j)
cmpPlayer.SetEnemy(j);
continue;
}
// Note: this is not yet implemented but I leave it commented to highlight it's easy
// If anyone ever adds handicap.
//if (getSetting(playerData, playerDefaults, i, "GatherRateMultiplier") !== undefined)
// cmpPlayer.SetGatherRateMultiplier(getSetting(playerData, playerDefaults, i, "GatherRateMultiplier"));
if (getSetting(playerData, playerDefaults, i, "PopulationLimit") !== undefined)
cmpPlayer.SetMaxPopulation(getSetting(playerData, playerDefaults, i, "PopulationLimit"));
if (getSetting(playerData, playerDefaults, i, "Resources") !== undefined)
cmpPlayer.SetResourceCounts(getSetting(playerData, playerDefaults, i, "Resources"));
if (getSetting(playerData, playerDefaults, i, "StartingTechnologies") !== undefined)
cmpPlayer.SetStartingTechnologies(getSetting(playerData, playerDefaults, i, "StartingTechnologies"));
if (getSetting(playerData, playerDefaults, i, "DisabledTechnologies") !== undefined)
cmpPlayer.SetDisabledTechnologies(getSetting(playerData, playerDefaults, i, "DisabledTechnologies"));
let disabledTemplates = [];
if (settings.DisabledTemplates !== undefined)
disabledTemplates = settings.DisabledTemplates;
if (getSetting(playerData, playerDefaults, i, "DisabledTemplates") !== undefined)
disabledTemplates = disabledTemplates.concat(getSetting(playerData, playerDefaults, i, "DisabledTemplates"));
if (disabledTemplates.length)
cmpPlayer.SetDisabledTemplates(disabledTemplates);
if (settings.DisableSpies)
{
cmpPlayer.AddDisabledTechnology("unlock_spies");
cmpPlayer.AddDisabledTemplate("special/spy");
}
// If diplomacy explicitly defined, use that; otherwise use teams
if (getSetting(playerData, playerDefaults, i, "Diplomacy") !== undefined)
cmpPlayer.SetDiplomacy(getSetting(playerData, playerDefaults, i, "Diplomacy"));
else
{
// Init diplomacy
var myTeam = getSetting(playerData, playerDefaults, i, "Team");
// Set all but self as enemies as SetTeam takes care of allies
for (var j = 0; j < numPlayers; ++j)
{
if (i == j)
cmpPlayer.SetAlly(j);
else
cmpPlayer.SetEnemy(j);
}
cmpPlayer.SetTeam(myTeam === undefined ? -1 : myTeam);
}
cmpPlayer.SetFormations(
getSetting(playerData, playerDefaults, i, "Formations") ||
Engine.ReadJSONFile("simulation/data/civs/" + cmpPlayer.GetCiv() + ".json").Formations);
var startCam = getSetting(playerData, playerDefaults, i, "StartingCamera");
if (startCam !== undefined)
cmpPlayer.SetStartingCamera(startCam.Position, startCam.Rotation);
}
// NOTE: We need to do the team locking here, as otherwise
// SetTeam can't ally the players.
if (settings.LockTeams)
for (let i = 0; i < numPlayers; ++i)
QueryPlayerIDInterface(i).SetLockTeams(true);
// Disable the AIIinterface when no AI players are present
if (playerData && !playerData.some(v => v && !!v.AI))
Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface).Disable();
}
// Get a setting if it exists or return default
function getSetting(settings, defaults, idx, property)
{
if (settings && settings[idx] && (property in settings[idx]))
return settings[idx][property];
// Use defaults
if (defaults && defaults[idx] && (property in defaults[idx]))
return defaults[idx][property];
return undefined;
}
function GetPlayerTemplateName(civ)
{
let path = "special/player/player";
if (Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).TemplateExists(path + "_" + civ))
return path + "_" + civ;
return path;
}
/**
* Similar to Engine.QueryInterface but applies to the player entity
* that owns the given entity.
* iid is typically IID_Player.
*/
function QueryOwnerInterface(ent, iid = IID_Player)
{
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
if (!cmpOwnership)
return null;
var owner = cmpOwnership.GetOwner();
if (owner == INVALID_PLAYER)
return null;
return QueryPlayerIDInterface(owner, iid);
}
/**
* Similar to Engine.QueryInterface but applies to the player entity
* with the given ID number.
* iid is typically IID_Player.
*/
function QueryPlayerIDInterface(id, iid = IID_Player)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var playerEnt = cmpPlayerManager.GetPlayerByID(id);
if (!playerEnt)
return null;
return Engine.QueryInterface(playerEnt, iid);
}
/**
* Similar to Engine.QueryInterface but first checks if the entity
* mirages the interface.
*/
function QueryMiragedInterface(ent, iid)
{
var cmp = Engine.QueryInterface(ent, IID_Mirage);
if (cmp && !cmp.Mirages(iid))
return null;
else if (!cmp)
cmp = Engine.QueryInterface(ent, iid);
return cmp;
}
/**
* Similar to Engine.QueryInterface, but checks for all interfaces
* implementing a builder list (currently Foundation and Repairable)
* TODO Foundation and Repairable could both implement a BuilderList component
*/
function QueryBuilderListInterface(ent)
{
return Engine.QueryInterface(ent, IID_Foundation) || Engine.QueryInterface(ent, IID_Repairable);
}
/**
* Returns true if the entity 'target' is owned by an ally of
* the owner of 'entity'.
*/
function IsOwnedByAllyOfEntity(entity, target)
{
return IsOwnedByEntityHelper(entity, target, "IsAlly");
}
function IsOwnedByMutualAllyOfEntity(entity, target)
{
return IsOwnedByEntityHelper(entity, target, "IsMutualAlly");
}
function IsOwnedByEntityHelper(entity, target, check)
{
// Figure out which player controls us
let owner = 0;
let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
if (cmpOwnership)
owner = cmpOwnership.GetOwner();
// Figure out which player controls the target entity
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(owner);
return cmpPlayer && cmpPlayer[check](targetOwner);
}
/**
* Returns true if the entity 'target' is owned by player
*/
function IsOwnedByPlayer(player, target)
{
var cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
return cmpOwnershipTarget && player == cmpOwnershipTarget.GetOwner();
}
function IsOwnedByGaia(target)
{
return IsOwnedByPlayer(0, target);
}
/**
* Returns true if the entity 'target' is owned by an ally of player
*/
function IsOwnedByAllyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsAlly");
}
function IsOwnedByMutualAllyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsMutualAlly");
}
function IsOwnedByNeutralOfPlayer(player,target)
{
return IsOwnedByHelper(player, target, "IsNeutral");
}
function IsOwnedByEnemyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsEnemy");
}
function IsOwnedByHelper(player, target, check)
{
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(player);
return cmpPlayer && cmpPlayer[check](targetOwner);
}
Engine.RegisterGlobal("LoadPlayerSettings", LoadPlayerSettings);
Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
Engine.RegisterGlobal("QueryPlayerIDInterface", QueryPlayerIDInterface);
Engine.RegisterGlobal("QueryMiragedInterface", QueryMiragedInterface);
Engine.RegisterGlobal("QueryBuilderListInterface", QueryBuilderListInterface);
Engine.RegisterGlobal("IsOwnedByAllyOfEntity", IsOwnedByAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfEntity", IsOwnedByMutualAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByPlayer", IsOwnedByPlayer);
Engine.RegisterGlobal("IsOwnedByGaia", IsOwnedByGaia);
Engine.RegisterGlobal("IsOwnedByAllyOfPlayer", IsOwnedByAllyOfPlayer);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfPlayer", IsOwnedByMutualAllyOfPlayer);
Engine.RegisterGlobal("IsOwnedByNeutralOfPlayer", IsOwnedByNeutralOfPlayer);
Engine.RegisterGlobal("IsOwnedByEnemyOfPlayer", IsOwnedByEnemyOfPlayer);
Index: ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp
===================================================================
--- ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp (revision 21166)
+++ ps/trunk/source/tools/atlas/GameInterface/ActorViewer.cpp (revision 21167)
@@ -1,559 +1,565 @@
-/* Copyright (C) 2017 Wildfire Games.
+/* Copyright (C) 2018 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "ActorViewer.h"
#include "View.h"
#include "graphics/ColladaManager.h"
#include "graphics/LOSTexture.h"
#include "graphics/Unit.h"
#include "graphics/Model.h"
#include "graphics/ModelDef.h"
#include "graphics/ObjectManager.h"
#include "graphics/ParticleManager.h"
#include "graphics/Patch.h"
#include "graphics/SkeletonAnimManager.h"
#include "graphics/Terrain.h"
#include "graphics/TerrainTextureEntry.h"
#include "graphics/TerrainTextureManager.h"
#include "graphics/TerritoryTexture.h"
#include "graphics/UnitManager.h"
#include "graphics/Overlay.h"
#include "maths/MathUtil.h"
#include "ps/Filesystem.h"
#include "ps/CLogger.h"
#include "ps/GameSetup/Config.h"
#include "ps/ProfileViewer.h"
#include "renderer/Renderer.h"
#include "renderer/Scene.h"
#include "renderer/SkyManager.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTerrain.h"
#include "simulation2/components/ICmpUnitMotion.h"
#include "simulation2/components/ICmpVisual.h"
#include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/helpers/Render.h"
struct ActorViewerImpl : public Scene
{
NONCOPYABLE(ActorViewerImpl);
public:
ActorViewerImpl() :
Entity(INVALID_ENTITY),
Terrain(),
ColladaManager(g_VFS),
MeshManager(ColladaManager),
SkeletonAnimManager(ColladaManager),
UnitManager(),
Simulation2(&UnitManager, g_ScriptRuntime, &Terrain),
ObjectManager(MeshManager, SkeletonAnimManager, Simulation2),
LOSTexture(Simulation2),
TerritoryTexture(Simulation2)
{
UnitManager.SetObjectManager(ObjectManager);
}
entity_id_t Entity;
CStrW CurrentUnitID;
CStrW CurrentUnitAnim;
float CurrentSpeed;
bool WalkEnabled;
bool GroundEnabled;
bool WaterEnabled;
bool ShadowsEnabled;
// Whether shadows, sky and water are enabled outside of the actor viewer.
bool OldShadows;
bool OldSky;
bool OldWater;
bool SelectionBoxEnabled;
bool AxesMarkerEnabled;
int PropPointsMode; // 0 disabled, 1 for point markers, 2 for point markers + axes
SColor4ub Background;
CTerrain Terrain;
CColladaManager ColladaManager;
CMeshManager MeshManager;
CSkeletonAnimManager SkeletonAnimManager;
CObjectManager ObjectManager;
CUnitManager UnitManager;
CSimulation2 Simulation2;
CLOSTexture LOSTexture;
CTerritoryTexture TerritoryTexture;
SOverlayLine SelectionBoxOverlay;
SOverlayLine AxesMarkerOverlays[3];
std::vector Props;
std::vector PropPointOverlays;
// Simplistic implementation of the Scene interface
virtual void EnumerateObjects(const CFrustum& frustum, SceneCollector* c)
{
if (GroundEnabled)
{
for (ssize_t pj = 0; pj < Terrain.GetPatchesPerSide(); ++pj)
for (ssize_t pi = 0; pi < Terrain.GetPatchesPerSide(); ++pi)
c->Submit(Terrain.GetPatch(pi, pj));
}
CmpPtr cmpVisual(Simulation2, Entity);
if (cmpVisual)
{
// add selection box outlines manually
if (SelectionBoxEnabled)
{
SelectionBoxOverlay.m_Color = CColor(35/255.f, 86/255.f, 188/255.f, .75f); // pretty blue
SelectionBoxOverlay.m_Thickness = 2;
SimRender::ConstructBoxOutline(cmpVisual->GetSelectionBox(), SelectionBoxOverlay);
c->Submit(&SelectionBoxOverlay);
}
// add origin axis thingy
if (AxesMarkerEnabled)
{
CMatrix3D worldSpaceAxes;
// offset from the ground a little bit to prevent fighting with the floor texture (also note: SetTranslation
// sets the identity 3x3 transformation matrix, which are the world axes)
worldSpaceAxes.SetTranslation(cmpVisual->GetPosition() + CVector3D(0, 0.02f, 0));
SimRender::ConstructAxesMarker(worldSpaceAxes, AxesMarkerOverlays[0], AxesMarkerOverlays[1], AxesMarkerOverlays[2]);
c->Submit(&AxesMarkerOverlays[0]);
c->Submit(&AxesMarkerOverlays[1]);
c->Submit(&AxesMarkerOverlays[2]);
}
// add prop point overlays
if (PropPointsMode > 0 && Props.size() > 0)
{
PropPointOverlays.clear(); // doesn't clear capacity, but should be ok since the number of prop points is usually pretty limited
for (size_t i = 0; i < Props.size(); ++i)
{
CModel::Prop& prop = Props[i];
if (prop.m_Model) // should always be the case
{
// prop point positions are automatically updated during animations etc. by CModel::ValidatePosition
const CMatrix3D& propCoordSystem = prop.m_Model->GetTransform();
SOverlayLine pointGimbal;
pointGimbal.m_Color = CColor(1.f, 0.f, 1.f, 1.f);
SimRender::ConstructGimbal(propCoordSystem.GetTranslation(), 0.05f, pointGimbal);
PropPointOverlays.push_back(pointGimbal);
if (PropPointsMode > 1)
{
// scale the prop axes coord system down a bit to distinguish them from the main world-space axes markers
CMatrix3D displayCoordSystem = propCoordSystem;
displayCoordSystem.Scale(0.5f, 0.5f, 0.5f);
// revert translation scaling
displayCoordSystem._14 = propCoordSystem._14;
displayCoordSystem._24 = propCoordSystem._24;
displayCoordSystem._34 = propCoordSystem._34;
// construct an XYZ axes marker for the prop's coordinate system
SOverlayLine xAxis, yAxis, zAxis;
SimRender::ConstructAxesMarker(displayCoordSystem, xAxis, yAxis, zAxis);
PropPointOverlays.push_back(xAxis);
PropPointOverlays.push_back(yAxis);
PropPointOverlays.push_back(zAxis);
}
}
}
for (size_t i = 0; i < PropPointOverlays.size(); ++i)
{
c->Submit(&PropPointOverlays[i]);
}
}
}
// send a RenderSubmit message so the components can submit their visuals to the renderer
Simulation2.RenderSubmit(*c, frustum, false);
}
virtual CLOSTexture& GetLOSTexture()
{
return LOSTexture;
}
virtual CTerritoryTexture& GetTerritoryTexture()
{
return TerritoryTexture;
}
/**
* Recursively fetches the props of the currently displayed entity model and its submodels, and stores them for rendering.
*/
void UpdatePropList();
void UpdatePropListRecursive(CModelAbstract* model);
};
void ActorViewerImpl::UpdatePropList()
{
Props.clear();
CmpPtr cmpVisual(Simulation2, Entity);
if (cmpVisual)
{
CUnit* unit = cmpVisual->GetUnit();
if (unit)
{
CModelAbstract& modelAbstract = unit->GetModel();
UpdatePropListRecursive(&modelAbstract);
}
}
}
void ActorViewerImpl::UpdatePropListRecursive(CModelAbstract* modelAbstract)
{
ENSURE(modelAbstract);
CModel* model = modelAbstract->ToCModel();
if (model)
{
std::vector& modelProps = model->GetProps();
for (CModel::Prop& modelProp : modelProps)
{
Props.push_back(modelProp);
if (modelProp.m_Model)
UpdatePropListRecursive(modelProp.m_Model);
}
}
}
ActorViewer::ActorViewer()
: m(*new ActorViewerImpl())
{
m.WalkEnabled = false;
m.GroundEnabled = true;
m.WaterEnabled = false;
m.ShadowsEnabled = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
m.SelectionBoxEnabled = false;
m.AxesMarkerEnabled = false;
m.PropPointsMode = 0;
m.Background = SColor4ub(0, 0, 0, 255);
// Create a tiny empty piece of terrain, just so we can put shadows
// on it without having to think too hard
m.Terrain.Initialize(2, NULL);
CTerrainTextureEntry* tex = g_TexMan.FindTexture("whiteness");
if (tex)
{
for (ssize_t pi = 0; pi < m.Terrain.GetPatchesPerSide(); ++pi)
{
for (ssize_t pj = 0; pj < m.Terrain.GetPatchesPerSide(); ++pj)
{
CPatch* patch = m.Terrain.GetPatch(pi, pj);
for (ssize_t i = 0; i < PATCH_SIZE; ++i)
{
for (ssize_t j = 0; j < PATCH_SIZE; ++j)
{
CMiniPatch& mp = patch->m_MiniPatches[i][j];
mp.Tex = tex;
mp.Priority = 0;
}
}
}
}
}
else
{
debug_warn(L"Failed to load whiteness texture");
}
- // Start the simulation
+ // Prepare the simulation
m.Simulation2.LoadDefaultScripts();
m.Simulation2.ResetState();
+ // Set player data
+ m.Simulation2.SetMapSettings(m.Simulation2.GetPlayerDefaults());
+ m.Simulation2.LoadPlayerSettings(true);
+
// Tell the simulation we've already loaded the terrain
CmpPtr cmpTerrain(m.Simulation2, SYSTEM_ENTITY);
if (cmpTerrain)
cmpTerrain->ReloadTerrain(false);
// Remove FOW since we're in Atlas
CmpPtr cmpRangeManager(m.Simulation2, SYSTEM_ENTITY);
if (cmpRangeManager)
cmpRangeManager->SetLosRevealAll(-1, true);
+
+ m.Simulation2.InitGame();
}
ActorViewer::~ActorViewer()
{
delete &m;
}
CSimulation2* ActorViewer::GetSimulation2()
{
return &m.Simulation2;
}
entity_id_t ActorViewer::GetEntity()
{
return m.Entity;
}
void ActorViewer::UnloadObjects()
{
m.ObjectManager.UnloadObjects();
}
void ActorViewer::SetActor(const CStrW& name, const CStrW& animation, player_id_t playerID)
{
bool needsAnimReload = false;
CStrW id = name;
// Recreate the entity, if we don't have one or if the new one is different
if (m.Entity == INVALID_ENTITY || id != m.CurrentUnitID)
{
// Delete the old entity (if any)
if (m.Entity != INVALID_ENTITY)
{
m.Simulation2.DestroyEntity(m.Entity);
m.Simulation2.FlushDestroyedEntities();
m.Entity = INVALID_ENTITY;
}
// Clear particles associated with deleted entity
g_Renderer.GetParticleManager().ClearUnattachedEmitters();
// If there's no actor to display, return with nothing loaded
if (id.empty())
return;
m.Entity = m.Simulation2.AddEntity(L"preview|" + id);
if (m.Entity == INVALID_ENTITY)
return;
CmpPtr cmpPosition(m.Simulation2, m.Entity);
if (cmpPosition)
{
ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
cmpPosition->JumpTo(entity_pos_t::FromInt(c), entity_pos_t::FromInt(c));
cmpPosition->SetYRotation(entity_angle_t::Pi());
}
CmpPtr cmpOwnership(m.Simulation2, m.Entity);
if (cmpOwnership)
cmpOwnership->SetOwner(playerID);
needsAnimReload = true;
}
if (animation != m.CurrentUnitAnim)
needsAnimReload = true;
if (needsAnimReload)
{
CStr anim = animation.ToUTF8().LowerCase();
// Emulate the typical simulation animation behaviour
float speed;
float repeattime = 0.f;
if (anim == "walk")
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (cmpUnitMotion)
speed = cmpUnitMotion->GetWalkSpeed().ToFloat();
else
speed = 7.f; // typical unit speed
m.CurrentSpeed = speed;
}
else if (anim == "run")
{
CmpPtr cmpUnitMotion(m.Simulation2, m.Entity);
if (cmpUnitMotion)
speed = cmpUnitMotion->GetRunSpeed().ToFloat();
else
speed = 12.f; // typical unit speed
m.CurrentSpeed = speed;
}
else if (anim == "melee")
{
speed = 1.f; // speed will be ignored if we have a repeattime
m.CurrentSpeed = 0.f;
CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Attack); " +
"if (cmp) cmp.GetTimers(cmp.GetBestAttack()).repeat; else 0;";
m.Simulation2.GetScriptInterface().Eval(code.c_str(), repeattime);
}
else
{
// Play the animation at normal speed, but movement speed is zero
speed = 1.f;
m.CurrentSpeed = 0.f;
}
CStr sound;
if (anim == "melee")
sound = "attack";
else if (anim == "build")
sound = "build";
else if (anim.Find("gather_") == 0)
sound = anim;
std::wstring soundgroup;
if (!sound.empty())
{
CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Sound); " +
"if (cmp) cmp.GetSoundGroup('"+sound+"'); else '';";
m.Simulation2.GetScriptInterface().Eval(code.c_str(), soundgroup);
}
CmpPtr cmpVisual(m.Simulation2, m.Entity);
if (cmpVisual)
{
// TODO: SetEntitySelection(anim)
cmpVisual->SelectAnimation(anim, false, fixed::FromFloat(speed), soundgroup);
if (repeattime)
cmpVisual->SetAnimationSyncRepeat(fixed::FromFloat(repeattime));
}
// update prop list for new entity/animation (relies on needsAnimReload also getting called for entire entity changes)
m.UpdatePropList();
}
m.CurrentUnitID = id;
m.CurrentUnitAnim = animation;
}
void ActorViewer::SetEnabled(bool enabled)
{
if (enabled)
{
// Set shadows, sky and water.
m.OldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled);
m.OldSky = g_Renderer.GetSkyManager()->m_RenderSky;
g_Renderer.GetSkyManager()->m_RenderSky = false;
m.OldWater = g_Renderer.GetWaterManager()->m_RenderWater;
g_Renderer.GetWaterManager()->m_RenderWater = m.WaterEnabled;
}
else
{
// Restore the old renderer state
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.OldShadows);
g_Renderer.GetSkyManager()->m_RenderSky = m.OldSky;
g_Renderer.GetWaterManager()->m_RenderWater = m.OldWater;
}
}
void ActorViewer::SetBackgroundColor(const SColor4ub& color)
{
m.Background = color;
m.Terrain.SetBaseColor(color);
}
void ActorViewer::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; }
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
void ActorViewer::SetWaterEnabled(bool enabled)
{
m.WaterEnabled = enabled;
// Adjust water level
entity_pos_t waterLevel = entity_pos_t::FromFloat(enabled ? 10.f : 0.f);
CmpPtr cmpWaterManager(m.Simulation2, SYSTEM_ENTITY);
if (cmpWaterManager)
cmpWaterManager->SetWaterLevel(waterLevel);
}
void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = enabled; }
void ActorViewer::SetBoundingBoxesEnabled(bool enabled) { m.SelectionBoxEnabled = enabled; }
void ActorViewer::SetAxesMarkerEnabled(bool enabled) { m.AxesMarkerEnabled = enabled; }
void ActorViewer::SetPropPointsMode(int mode) { m.PropPointsMode = mode; }
void ActorViewer::SetStatsEnabled(bool enabled)
{
if (enabled)
g_ProfileViewer.ShowTable("renderer");
else
g_ProfileViewer.ShowTable("");
}
void ActorViewer::Render()
{
m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR);
g_Renderer.SetClearColor(m.Background);
// Set simulation context for rendering purposes
g_Renderer.SetSimulation(&m.Simulation2);
g_Renderer.BeginFrame();
// Find the centre of the interesting region, in the middle of the patch
// and half way up the model (assuming there is one)
CVector3D centre;
CmpPtr cmpVisual(m.Simulation2, m.Entity);
if (cmpVisual)
cmpVisual->GetBounds().GetCentre(centre);
else
centre.Y = 0.f;
centre.X = centre.Z = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
CCamera camera = AtlasView::GetView_Actor()->GetCamera();
camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z);
camera.UpdateFrustum();
g_Renderer.SetSceneCamera(camera, camera);
g_Renderer.RenderScene(m);
glDisable(GL_DEPTH_TEST);
g_Logger->Render();
g_ProfileViewer.RenderProfile();
glEnable(GL_DEPTH_TEST);
g_Renderer.EndFrame();
ogl_WarnIfError();
}
void ActorViewer::Update(float simFrameLength, float realFrameLength)
{
m.Simulation2.Update((int)(simFrameLength*1000));
m.Simulation2.Interpolate(simFrameLength, 0, realFrameLength);
if (m.WalkEnabled && m.CurrentSpeed)
{
CmpPtr cmpPosition(m.Simulation2, m.Entity);
if (cmpPosition)
{
// Move the model by speed*simFrameLength forwards
float z = cmpPosition->GetPosition().Z.ToFloat();
z -= m.CurrentSpeed*simFrameLength;
// Wrap at the edges, so it doesn't run off into the horizon
ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
if (z < c - TERRAIN_TILE_SIZE*PATCH_SIZE * 0.1f)
z = c + TERRAIN_TILE_SIZE*PATCH_SIZE * 0.1f;
cmpPosition->JumpTo(cmpPosition->GetPosition().X, entity_pos_t::FromFloat(z));
}
}
}