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)); } } }