virtual bool CheckDuplicateFoundation() { CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); if (!cmpPosition) return false; // error if (!cmpPosition->IsInWorld()) return false; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return false; // error // required precondition to use SkipControlGroupsRequireFlagObstructionFilter if (m_ControlGroup == INVALID_ENTITY) { LOGERROR("[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid"); return false; } // Ignore collisions with entities unless they block foundations and match both control groups. SkipTagRequireControlGroupsAndFlagObstructionFilter filter(m_Tag, m_ControlGroup, m_ControlGroup2, ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); if (m_Type == UNIT) return !cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, m_Clearance, NULL); else return !cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, NULL ); }
void Interpolate(float UNUSED(frameTime), float frameOffset) { // Skip all the following computations if we have no sprites if (m_Sprites.empty()) { m_Enabled = false; return; } // Disable rendering of the unit if it has no position CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); if (!cmpPosition || !cmpPosition->IsInWorld()) { m_Enabled = false; return; } // Find the precise position of the unit CMatrix3D transform(cmpPosition->GetInterpolatedTransform(frameOffset)); CVector3D position(transform.GetTranslation()); // Move all the sprites to the desired offset relative to the unit for (size_t i = 0; i < m_Sprites.size(); ++i) m_Sprites[i].m_Position = position + m_SpriteOffsets[i]; m_Enabled = true; }
virtual void UpdateTurretPosition() { if (m_TurretParent == INVALID_ENTITY) return; CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (!cmpPosition) { LOGERROR("Turret with parent without position component"); return; } if (!cmpPosition->IsInWorld()) MoveOutOfWorld(); else { CFixedVector2D rotatedPosition = CFixedVector2D(m_TurretPosition.X, m_TurretPosition.Z); rotatedPosition = rotatedPosition.Rotate(cmpPosition->GetRotation().Y); CFixedVector2D rootPosition = cmpPosition->GetPosition2D(); entity_pos_t x = rootPosition.X + rotatedPosition.X; entity_pos_t z = rootPosition.Y + rotatedPosition.Y; if (!m_InWorld || m_X != x || m_Z != z) MoveTo(x, z); entity_pos_t y = cmpPosition->GetHeightOffset() + m_TurretPosition.Y; if (!m_InWorld || GetHeightOffset() != y) SetHeightOffset(y); m_InWorld = true; } }
virtual CFixedVector3D GetRotation() { entity_angle_t y = m_RotY; if (m_TurretParent != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (cmpPosition) y += cmpPosition->GetRotation().Y; } return CFixedVector3D(m_RotX, y, m_RotZ); }
virtual void SetTurretParent(entity_id_t id, const CFixedVector3D& offset) { if (m_TurretParent != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (cmpPosition) cmpPosition->GetTurrets()->erase(GetEntityId()); } m_TurretParent = id; m_TurretPosition = offset; if (m_TurretParent != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (cmpPosition) cmpPosition->GetTurrets()->insert(GetEntityId()); } UpdateTurretPosition(); }
virtual void TurnTo(entity_angle_t y) { if (m_TurretParent != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (cmpPosition) y -= cmpPosition->GetRotation().Y; } m_RotY = y; AdvertisePositionChanges(); UpdateMessageSubscriptions(); }
virtual void SetActive(bool active) { if (active && !m_Active) { m_Active = true; // Construct the obstruction shape CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return; // error CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); if (!cmpPosition) return; // error if (!cmpPosition->IsInWorld()) return; // don't need an obstruction // TODO: code duplication from message handlers CFixedVector2D pos = cmpPosition->GetPosition2D(); if (m_Type == STATIC) m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(), pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, m_Flags, m_ControlGroup, m_ControlGroup2); else if (m_Type == UNIT) m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(), pos.X, pos.Y, m_Size0, m_Clearance, (flags_t)(m_Flags | (m_Moving ? ICmpObstructionManager::FLAG_MOVING : 0)), m_ControlGroup); else AddClusterShapes(pos.X, pos.Y, cmpPosition->GetRotation().Y); } else if (!active && m_Active) { m_Active = false; // Delete the obstruction shape // TODO: code duplication from message handlers if (m_Tag.valid()) { CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return; // error cmpObstructionManager->RemoveShape(m_Tag); m_Tag = tag_t(); if (m_Type == CLUSTER) RemoveClusterShapes(); } } // else we didn't change the active status }
virtual void PlaySoundGroup(std::wstring name, entity_id_t source) { #if CONFIG2_AUDIO // Make sure the sound group is loaded CSoundGroup* group; if (m_SoundGroups.find(name) == m_SoundGroups.end()) { group = new CSoundGroup(); if (!group->LoadSoundGroup(L"audio/" + name)) { LOGERROR(L"Failed to load sound group '%ls'", name.c_str()); delete group; group = NULL; } // Cache the sound group (or the null, if it failed) m_SoundGroups[name] = group; } else { group = m_SoundGroups[name]; } // Failed to load group -> do nothing if (!group) return; // Only play the sound if the entity is visible CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext(), SYSTEM_ENTITY); ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(source, GetSimContext().GetCurrentDisplayedPlayer()); if (vis == ICmpRangeManager::VIS_VISIBLE) { // Find the source's position, if possible // (TODO: we should do something more sensible if there's no position available) CVector3D sourcePos(0, 0, 0); if (source != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), source); if (cmpPosition && cmpPosition->IsInWorld()) sourcePos = CVector3D(cmpPosition->GetPosition()); } group->PlayNext(sourcePos); } #else // !CONFIG2_AUDIO UNUSED2(name); UNUSED2(source); #endif // !CONFIG2_AUDIO }
virtual void SetActive(bool active) { if (active && !m_Active) { m_Active = true; // Construct the obstruction shape CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (cmpObstructionManager.null()) return; // error CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (cmpPosition.null()) return; // error if (!cmpPosition->IsInWorld()) return; // don't need an obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); if (m_Type == STATIC) m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(), pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, m_Flags); else m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(), pos.X, pos.Y, m_Size0, (flags_t)(m_Flags | (m_Moving ? ICmpObstructionManager::FLAG_MOVING : 0)), m_ControlGroup); } else if (!active && m_Active) { m_Active = false; // Delete the obstruction shape if (m_Tag.valid()) { CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (cmpObstructionManager.null()) return; // error cmpObstructionManager->RemoveShape(m_Tag); m_Tag = tag_t(); } } // else we didn't change the active status }
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition) return false; // error CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return false; // error if (!cmpPosition->IsInWorld()) return false; // no obstruction square CFixedVector2D pos = cmpPosition->GetPosition2D(); if (m_Type == STATIC) out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1); else out = cmpObstructionManager->GetUnitShapeObstruction(pos.X, pos.Y, m_Size0); return true; }
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<ICmpPosition> 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)); } } }
virtual void SetYRotation(entity_angle_t y) { if (m_TurretParent != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (cmpPosition) y -= cmpPosition->GetRotation().Y; } m_RotY = y; m_InterpolatedRotY = m_RotY.ToFloat(); if (m_InWorld) { UpdateXZRotation(); m_LastInterpolatedRotX = m_InterpolatedRotX; m_LastInterpolatedRotZ = m_InterpolatedRotZ; } AdvertisePositionChanges(); UpdateMessageSubscriptions(); }
void ActorViewer::Update(float dt) { m.Simulation2.Update((int)(dt*1000)); m.Simulation2.Interpolate(dt, 0); g_Renderer.GetParticleManager().Interpolate(dt); if (m.WalkEnabled && m.CurrentSpeed) { CmpPtr<ICmpPosition> cmpPosition(m.Simulation2, m.Entity); if (cmpPosition) { // Move the model by speed*dt forwards float z = cmpPosition->GetPosition().Z.ToFloat(); z -= m.CurrentSpeed*dt; // 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)); } } }
virtual std::vector<entity_id_t> GetConstructionCollisions() { std::vector<entity_id_t> ret; CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition) return ret; // error if (!cmpPosition->IsInWorld()) return ret; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return ret; // error // required precondition to use SkipControlGroupsRequireFlagObstructionFilter if (m_ControlGroup == INVALID_ENTITY) { LOGERROR(L"[CmpObstruction] Cannot test for construction obstructions; primary control group must be valid"); return ret; } // Ignore collisions within the same control group, or with other non-construction-blocking shapes. // Note that, since the control group for each entity defaults to the entity's ID, this is typically // equivalent to only ignoring the entity's own shape and other non-construction-blocking shapes. SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2, ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION); if (m_Type == STATIC) cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &ret); else cmpObstructionManager->TestUnitShape(filter, pos.X, pos.Y, m_Size0, &ret); return ret; }
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out, bool previousPosition) { CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); if (!cmpPosition) return false; // error CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return false; // error if (!cmpPosition->IsInWorld()) return false; // no obstruction square CFixedVector2D pos; if (previousPosition) pos = cmpPosition->GetPreviousPosition2D(); else pos = cmpPosition->GetPosition2D(); if (m_Type == UNIT) out = cmpObstructionManager->GetUnitShapeObstruction(pos.X, pos.Y, m_Size0); else out = cmpObstructionManager->GetStaticShapeObstruction(pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1); return true; }
virtual void PlaySoundGroup(const std::wstring& name, entity_id_t source) { if ( ! g_SoundManager || (source == INVALID_ENTITY) ) return; CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity()); int currentPlayer = GetSimContext().GetCurrentDisplayedPlayer(); if ( !cmpRangeManager || ( cmpRangeManager->GetLosVisibility(source, currentPlayer) != ICmpRangeManager::VIS_VISIBLE ) ) return; CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), source); if (cmpPosition && cmpPosition->IsInWorld()) { bool playerOwned = false; CmpPtr<ICmpOwnership> cmpOwnership( GetSimContext(), source); if (cmpOwnership) playerOwned = cmpOwnership->GetOwner() == currentPlayer; CVector3D sourcePos = CVector3D(cmpPosition->GetPosition()); g_SoundManager->PlayAsGroup(name, sourcePos, source, playerOwned); } }
virtual bool CheckFoundation(std::string className) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition) return false; // error if (!cmpPosition->IsInWorld()) return false; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); if (!cmpPathfinder) return false; // error // required precondition to use SkipControlGroupsRequireFlagObstructionFilter if (m_ControlGroup == INVALID_ENTITY) { LOGERROR(L"[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid"); return false; } // Get passability class ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className); // Ignore collisions within the same control group, or with other non-foundation-blocking shapes. // Note that, since the control group for each entity defaults to the entity's ID, this is typically // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes. SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2, ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); if (m_Type == STATIC) return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass); else return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Size0, passClass); }
virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned) { // Try to find a free space around the building's footprint. // (Note that we use the footprint, not the obstruction shape - this might be a bit dodgy // because the footprint might be inside the obstruction, but it hopefully gives us a nicer // shape.) const CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1)); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition || !cmpPosition->IsInWorld()) return error; CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return error; entity_pos_t spawnedRadius; ICmpObstructionManager::tag_t spawnedTag; CmpPtr<ICmpObstruction> cmpSpawnedObstruction(GetSimContext(), spawned); if (cmpSpawnedObstruction) { spawnedRadius = cmpSpawnedObstruction->GetUnitRadius(); spawnedTag = cmpSpawnedObstruction->GetObstruction(); } // else use zero radius // Get passability class from UnitMotion CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), spawned); if (!cmpUnitMotion) return error; ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass(); CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); if (!cmpPathfinder) return error; CFixedVector2D initialPos = cmpPosition->GetPosition2D(); entity_angle_t initialAngle = cmpPosition->GetRotation().Y; // Max spawning distance in tiles const i32 maxSpawningDistance = 4; if (m_Shape == CIRCLE) { // Expand outwards from foundation for (i32 dist = 0; dist <= maxSpawningDistance; ++dist) { // The spawn point should be far enough from this footprint to fit the unit, plus a little gap entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist); entity_pos_t radius = m_Size0 + clearance; // Try equally-spaced points around the circle in alternating directions, starting from the front const i32 numPoints = 31 + 2*dist; for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2] { entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints); fixed s, c; sincos_approx(angle, s, c); CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius)); SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS) return pos; // this position is okay, so return it } } } else { fixed s, c; sincos_approx(initialAngle, s, c); // Expand outwards from foundation for (i32 dist = 0; dist <= maxSpawningDistance; ++dist) { // The spawn point should be far enough from this footprint to fit the unit, plus a little gap entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist); for (i32 edge = 0; edge < 4; ++edge) { // Try equally-spaced points along the edge in alternating directions, starting from the middle const i32 numPoints = 9 + 2*dist; // Compute the direction and length of the current edge CFixedVector2D dir; fixed sx, sy; switch (edge) { case 0: dir = CFixedVector2D(c, -s); sx = m_Size0; sy = m_Size1; break; case 1: dir = CFixedVector2D(-s, -c); sx = m_Size1; sy = m_Size0; break; case 2: dir = CFixedVector2D(s, c); sx = m_Size1; sy = m_Size0; break; case 3: dir = CFixedVector2D(-c, s); sx = m_Size0; sy = m_Size1; break; } CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy/2 + clearance); dir = dir.Multiply((sx + clearance*2) / (int)(numPoints-1)); for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2] { CFixedVector2D pos (center + dir*i); SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS) return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it } } } } return error; }
void CMapWriter::WriteXML(const VfsPath& filename, WaterManager* pWaterMan, SkyManager* pSkyMan, CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema, CPostprocManager* pPostproc, CSimulation2* pSimulation2) { XML_Start(); { XML_Element("Scenario"); XML_Attribute("version", (int)FILE_VERSION); ENSURE(pSimulation2); CSimulation2& sim = *pSimulation2; if (!sim.GetStartupScript().empty()) { XML_Element("Script"); XML_CDATA(sim.GetStartupScript().c_str()); } { XML_Element("Environment"); XML_Setting("SkySet", pSkyMan->GetSkySet()); { XML_Element("SunColor"); XML_Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z... XML_Attribute("g", pLightEnv->m_SunColor.Y); XML_Attribute("b", pLightEnv->m_SunColor.Z); } { XML_Element("SunElevation"); XML_Attribute("angle", pLightEnv->m_Elevation); } { XML_Element("SunRotation"); XML_Attribute("angle", pLightEnv->m_Rotation); } { XML_Element("TerrainAmbientColor"); XML_Attribute("r", pLightEnv->m_TerrainAmbientColor.X); XML_Attribute("g", pLightEnv->m_TerrainAmbientColor.Y); XML_Attribute("b", pLightEnv->m_TerrainAmbientColor.Z); } { XML_Element("UnitsAmbientColor"); XML_Attribute("r", pLightEnv->m_UnitsAmbientColor.X); XML_Attribute("g", pLightEnv->m_UnitsAmbientColor.Y); XML_Attribute("b", pLightEnv->m_UnitsAmbientColor.Z); } { XML_Element("Fog"); XML_Setting("FogFactor", pLightEnv->m_FogFactor); XML_Setting("FogThickness", pLightEnv->m_FogMax); { XML_Element("FogColor"); XML_Attribute("r", pLightEnv->m_FogColor.X); XML_Attribute("g", pLightEnv->m_FogColor.Y); XML_Attribute("b", pLightEnv->m_FogColor.Z); } } { XML_Element("Water"); { XML_Element("WaterBody"); CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY); ENSURE(cmpWaterManager); XML_Setting("Type", pWaterMan->m_WaterType); { XML_Element("Color"); XML_Attribute("r", pWaterMan->m_WaterColor.r); XML_Attribute("g", pWaterMan->m_WaterColor.g); XML_Attribute("b", pWaterMan->m_WaterColor.b); } { XML_Element("Tint"); XML_Attribute("r", pWaterMan->m_WaterTint.r); XML_Attribute("g", pWaterMan->m_WaterTint.g); XML_Attribute("b", pWaterMan->m_WaterTint.b); } XML_Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0)); XML_Setting("Waviness", pWaterMan->m_Waviness); XML_Setting("Murkiness", pWaterMan->m_Murkiness); XML_Setting("WindAngle", pWaterMan->m_WindAngle); } } { XML_Element("Postproc"); { XML_Setting("Brightness", pLightEnv->m_Brightness); XML_Setting("Contrast", pLightEnv->m_Contrast); XML_Setting("Saturation", pLightEnv->m_Saturation); XML_Setting("Bloom", pLightEnv->m_Bloom); XML_Setting("PostEffect", pPostproc->GetPostEffect()); } } } { XML_Element("Camera"); { XML_Element("Position"); CVector3D pos = pCamera->m_Orientation.GetTranslation(); XML_Attribute("x", pos.X); XML_Attribute("y", pos.Y); XML_Attribute("z", pos.Z); } CVector3D in = pCamera->m_Orientation.GetIn(); // Convert to spherical coordinates float rotation = atan2(in.X, in.Z); float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - (float)M_PI/2; { XML_Element("Rotation"); XML_Attribute("angle", rotation); } { XML_Element("Declination"); XML_Attribute("angle", declination); } } { std::string settings = sim.GetMapSettingsString(); if (!settings.empty()) { XML_Element("ScriptSettings"); XML_CDATA(("\n" + settings + "\n").c_str()); } } { XML_Element("Entities"); CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY); ENSURE(cmpTemplateManager); // This will probably need to be changed in the future, but for now we'll // just save all entities that have a position CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position); for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) { entity_id_t ent = it->first; // Don't save local entities (placement previews etc) if (ENTITY_IS_LOCAL(ent)) continue; XML_Element("Entity"); XML_Attribute("uid", ent); XML_Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent)); CmpPtr<ICmpOwnership> cmpOwnership(sim, ent); if (cmpOwnership) XML_Setting("Player", (int)cmpOwnership->GetOwner()); CmpPtr<ICmpPosition> cmpPosition(sim, ent); if (cmpPosition) { CFixedVector3D pos; if (cmpPosition->IsInWorld()) pos = cmpPosition->GetPosition(); CFixedVector3D rot = cmpPosition->GetRotation(); { XML_Element("Position"); XML_Attribute("x", pos.X); XML_Attribute("z", pos.Z); // TODO: height offset etc } { XML_Element("Orientation"); XML_Attribute("y", rot.Y); // TODO: X, Z maybe } } CmpPtr<ICmpObstruction> cmpObstruction(sim, ent); if (cmpObstruction) { // TODO: Currently only necessary because Atlas // does not set up control groups for its walls. cmpObstruction->ResolveFoundationCollisions(); entity_id_t group = cmpObstruction->GetControlGroup(); entity_id_t group2 = cmpObstruction->GetControlGroup2(); // Don't waste space writing the default control groups. if (group != ent || group2 != INVALID_ENTITY) { XML_Element("Obstruction"); if (group != ent) XML_Attribute("group", group); if (group2 != INVALID_ENTITY) XML_Attribute("group2", group2); } } CmpPtr<ICmpVisual> cmpVisual(sim, ent); if (cmpVisual) { u32 seed = cmpVisual->GetActorSeed(); if (seed != (u32)ent) { XML_Element("Actor"); XML_Attribute("seed", seed); } // TODO: variation/selection strings } } } const std::map<CStrW, CCinemaPath>& paths = pCinema->GetAllPaths(); std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin(); { XML_Element("Paths"); for ( ; it != paths.end(); ++it ) { fixed timescale = it->second.GetTimescale(); const std::vector<SplineData>& nodes = it->second.GetAllNodes(); const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes(); const CCinemaData* data = it->second.GetData(); XML_Element("Path"); XML_Attribute("name", data->m_Name); XML_Attribute("timescale", timescale); XML_Attribute("orientation", data->m_Orientation); XML_Attribute("mode", data->m_Mode); XML_Attribute("style", data->m_Style); fixed last_target = fixed::Zero(); for (size_t i = 0, j = 0; i < nodes.size(); ++i) { XML_Element("Node"); fixed distance = i > 0 ? nodes[i - 1].Distance : fixed::Zero(); last_target += distance; XML_Attribute("deltatime", distance); { XML_Element("Position"); XML_Attribute("x", nodes[i].Position.X); XML_Attribute("y", nodes[i].Position.Y); XML_Attribute("z", nodes[i].Position.Z); } { XML_Element("Rotation"); XML_Attribute("x", nodes[i].Rotation.X); XML_Attribute("y", nodes[i].Rotation.Y); XML_Attribute("z", nodes[i].Rotation.Z); } if (j >= target_nodes.size()) continue; fixed target_distance = j > 0 ? target_nodes[j - 1].Distance : fixed::Zero(); if (target_distance > last_target) continue; { XML_Element("Target"); XML_Attribute("x", target_nodes[j].Position.X); XML_Attribute("y", target_nodes[j].Position.Y); XML_Attribute("z", target_nodes[j].Position.Z); } last_target = fixed::Zero(); ++j; } } } } if (!XML_StoreVFS(g_VFS, filename)) LOGERROR("Failed to write map '%s'", filename.string8()); }
void CGameView::Update(const float deltaRealTime) { // If camera movement is being handled by the touch-input system, // then we should stop to avoid conflicting with it if (g_TouchInput.IsEnabled()) return; if (!g_app_has_focus) return; // TODO: this is probably not an ideal place for this, it should probably go // in a CCmpWaterManager or some such thing (once such a thing exists) if (!m->Game->m_Paused) g_Renderer.GetWaterManager()->m_WaterTexTimer += deltaRealTime; if (m->TrackManager.IsActive() && m->TrackManager.IsPlaying()) { if (! m->TrackManager.Update(deltaRealTime)) { // ResetCamera(); } return; } // Calculate mouse movement static int mouse_last_x = 0; static int mouse_last_y = 0; int mouse_dx = g_mouse_x - mouse_last_x; int mouse_dy = g_mouse_y - mouse_last_y; mouse_last_x = g_mouse_x; mouse_last_y = g_mouse_y; if (HotkeyIsPressed("camera.rotate.cw")) m->RotateY.AddSmoothly(m->ViewRotateYSpeed * deltaRealTime); if (HotkeyIsPressed("camera.rotate.ccw")) m->RotateY.AddSmoothly(-m->ViewRotateYSpeed * deltaRealTime); if (HotkeyIsPressed("camera.rotate.up")) m->RotateX.AddSmoothly(-m->ViewRotateXSpeed * deltaRealTime); if (HotkeyIsPressed("camera.rotate.down")) m->RotateX.AddSmoothly(m->ViewRotateXSpeed * deltaRealTime); float moveRightward = 0.f; float moveForward = 0.f; if (HotkeyIsPressed("camera.pan")) { moveRightward += m->ViewDragSpeed * mouse_dx; moveForward += m->ViewDragSpeed * -mouse_dy; } if (g_mouse_active) { if (g_mouse_x >= g_xres - 2 && g_mouse_x < g_xres) moveRightward += m->ViewScrollSpeed * deltaRealTime; else if (g_mouse_x <= 3 && g_mouse_x >= 0) moveRightward -= m->ViewScrollSpeed * deltaRealTime; if (g_mouse_y >= g_yres - 2 && g_mouse_y < g_yres) moveForward -= m->ViewScrollSpeed * deltaRealTime; else if (g_mouse_y <= 3 && g_mouse_y >= 0) moveForward += m->ViewScrollSpeed * deltaRealTime; } if (HotkeyIsPressed("camera.right")) moveRightward += m->ViewScrollSpeed * deltaRealTime; if (HotkeyIsPressed("camera.left")) moveRightward -= m->ViewScrollSpeed * deltaRealTime; if (HotkeyIsPressed("camera.up")) moveForward += m->ViewScrollSpeed * deltaRealTime; if (HotkeyIsPressed("camera.down")) moveForward -= m->ViewScrollSpeed * deltaRealTime; if (g_Joystick.IsEnabled()) { // This could all be improved with extra speed and sensitivity settings // (maybe use pow to allow finer control?), and inversion settings moveRightward += g_Joystick.GetAxisValue(m->JoystickPanX) * m->ViewScrollSpeed * deltaRealTime; moveForward -= g_Joystick.GetAxisValue(m->JoystickPanY) * m->ViewScrollSpeed * deltaRealTime; m->RotateX.AddSmoothly(g_Joystick.GetAxisValue(m->JoystickRotateX) * m->ViewRotateXSpeed * deltaRealTime); m->RotateY.AddSmoothly(-g_Joystick.GetAxisValue(m->JoystickRotateY) * m->ViewRotateYSpeed * deltaRealTime); // Use a +1 bias for zoom because I want this to work with trigger buttons that default to -1 m->Zoom.AddSmoothly((g_Joystick.GetAxisValue(m->JoystickZoomIn) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime); m->Zoom.AddSmoothly(-(g_Joystick.GetAxisValue(m->JoystickZoomOut) + 1.0f) / 2.0f * m->ViewZoomSpeed * deltaRealTime); } if (moveRightward || moveForward) { // Break out of following mode when the user starts scrolling m->FollowEntity = INVALID_ENTITY; float s = sin(m->RotateY.GetSmoothedValue()); float c = cos(m->RotateY.GetSmoothedValue()); m->PosX.AddSmoothly(c * moveRightward); m->PosZ.AddSmoothly(-s * moveRightward); m->PosX.AddSmoothly(s * moveForward); m->PosZ.AddSmoothly(c * moveForward); } if (m->FollowEntity) { CmpPtr<ICmpPosition> cmpPosition(*(m->Game->GetSimulation2()), m->FollowEntity); if (cmpPosition && cmpPosition->IsInWorld()) { // Get the most recent interpolated position float frameOffset = m->Game->GetSimulation2()->GetLastFrameOffset(); CMatrix3D transform = cmpPosition->GetInterpolatedTransform(frameOffset, false); CVector3D pos = transform.GetTranslation(); if (m->FollowFirstPerson) { float x, z, angle; cmpPosition->GetInterpolatedPosition2D(frameOffset, x, z, angle); float height = 4.f; m->ViewCamera.m_Orientation.SetIdentity(); m->ViewCamera.m_Orientation.RotateX((float)M_PI/24.f); m->ViewCamera.m_Orientation.RotateY(angle); m->ViewCamera.m_Orientation.Translate(pos.X, pos.Y + height, pos.Z); m->ViewCamera.UpdateFrustum(); return; } else { // Move the camera to match the unit CCamera targetCam = m->ViewCamera; SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation); CVector3D pivot = GetSmoothPivot(targetCam); CVector3D delta = pos - pivot; m->PosX.AddSmoothly(delta.X); m->PosY.AddSmoothly(delta.Y); m->PosZ.AddSmoothly(delta.Z); } } else { // The unit disappeared (died or garrisoned etc), so stop following it m->FollowEntity = INVALID_ENTITY; } } if (HotkeyIsPressed("camera.zoom.in")) m->Zoom.AddSmoothly(-m->ViewZoomSpeed * deltaRealTime); if (HotkeyIsPressed("camera.zoom.out")) m->Zoom.AddSmoothly(m->ViewZoomSpeed * deltaRealTime); if (m->ConstrainCamera) m->Zoom.ClampSmoothly(m->ViewZoomMin, m->ViewZoomMax); float zoomDelta = -m->Zoom.Update(deltaRealTime); if (zoomDelta) { CVector3D forwards = m->ViewCamera.m_Orientation.GetIn(); m->PosX.AddSmoothly(forwards.X * zoomDelta); m->PosY.AddSmoothly(forwards.Y * zoomDelta); m->PosZ.AddSmoothly(forwards.Z * zoomDelta); } if (m->ConstrainCamera) m->RotateX.ClampSmoothly(DEGTORAD(m->ViewRotateXMin), DEGTORAD(m->ViewRotateXMax)); FocusHeight(m, true); // Ensure the ViewCamera focus is inside the map with the chosen margins // if not so - apply margins to the camera if (m->ConstrainCamera) { CCamera targetCam = m->ViewCamera; SetupCameraMatrixSmoothRot(m, &targetCam.m_Orientation); CTerrain* pTerrain = m->Game->GetWorld()->GetTerrain(); CVector3D pivot = GetSmoothPivot(targetCam); CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot; CVector3D desiredPivot = pivot; CmpPtr<ICmpRangeManager> cmpRangeManager(*m->Game->GetSimulation2(), SYSTEM_ENTITY); if (cmpRangeManager && cmpRangeManager->GetLosCircular()) { // Clamp to a circular region around the center of the map float r = pTerrain->GetMaxX() / 2; CVector3D center(r, desiredPivot.Y, r); float dist = (desiredPivot - center).Length(); if (dist > r - CAMERA_EDGE_MARGIN) desiredPivot = center + (desiredPivot - center).Normalized() * (r - CAMERA_EDGE_MARGIN); } else { // Clamp to the square edges of the map desiredPivot.X = Clamp(desiredPivot.X, pTerrain->GetMinX() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxX() - CAMERA_EDGE_MARGIN); desiredPivot.Z = Clamp(desiredPivot.Z, pTerrain->GetMinZ() + CAMERA_EDGE_MARGIN, pTerrain->GetMaxZ() - CAMERA_EDGE_MARGIN); } // Update the position so that pivot is within the margin m->PosX.SetValueSmoothly(desiredPivot.X + delta.X); m->PosZ.SetValueSmoothly(desiredPivot.Z + delta.Z); } m->PosX.Update(deltaRealTime); m->PosY.Update(deltaRealTime); m->PosZ.Update(deltaRealTime); // Handle rotation around the Y (vertical) axis { CCamera targetCam = m->ViewCamera; SetupCameraMatrixSmooth(m, &targetCam.m_Orientation); float rotateYDelta = m->RotateY.Update(deltaRealTime); if (rotateYDelta) { // We've updated RotateY, and need to adjust Pos so that it's still // facing towards the original focus point (the terrain in the center // of the screen). CVector3D upwards(0.0f, 1.0f, 0.0f); CVector3D pivot = GetSmoothPivot(targetCam); CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot; CQuaternion q; q.FromAxisAngle(upwards, rotateYDelta); CVector3D d = q.Rotate(delta) - delta; m->PosX.Add(d.X); m->PosY.Add(d.Y); m->PosZ.Add(d.Z); } } // Handle rotation around the X (sideways, relative to camera) axis { CCamera targetCam = m->ViewCamera; SetupCameraMatrixSmooth(m, &targetCam.m_Orientation); float rotateXDelta = m->RotateX.Update(deltaRealTime); if (rotateXDelta) { CVector3D rightwards = targetCam.m_Orientation.GetLeft() * -1.0f; CVector3D pivot = GetSmoothPivot(targetCam); CVector3D delta = targetCam.m_Orientation.GetTranslation() - pivot; CQuaternion q; q.FromAxisAngle(rightwards, rotateXDelta); CVector3D d = q.Rotate(delta) - delta; m->PosX.Add(d.X); m->PosY.Add(d.Y); m->PosZ.Add(d.Z); } } /* This is disabled since it doesn't seem necessary: // Ensure the camera's near point is never inside the terrain if (m->ConstrainCamera) { CMatrix3D target; target.SetIdentity(); target.RotateX(m->RotateX.GetValue()); target.RotateY(m->RotateY.GetValue()); target.Translate(m->PosX.GetValue(), m->PosY.GetValue(), m->PosZ.GetValue()); CVector3D nearPoint = target.GetTranslation() + target.GetIn() * defaultNear; float ground = m->Game->GetWorld()->GetTerrain()->GetExactGroundLevel(nearPoint.X, nearPoint.Z); float limit = ground + 16.f; if (nearPoint.Y < limit) m->PosY.AddSmoothly(limit - nearPoint.Y); } */ m->RotateY.Wrap(-(float)M_PI, (float)M_PI); // Update the camera matrix m->ViewCamera.SetProjection(m->ViewNear, m->ViewFar, m->ViewFOV); SetupCameraMatrixSmooth(m, &m->ViewCamera.m_Orientation); m->ViewCamera.UpdateFrustum(); }
virtual CMatrix3D GetInterpolatedTransform(float frameOffset) { if (m_TurretParent != INVALID_ENTITY) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), m_TurretParent); if (!cmpPosition) { LOGERROR("Turret with parent without position component"); CMatrix3D m; m.SetIdentity(); return m; } if (!cmpPosition->IsInWorld()) { LOGERROR("CCmpPosition::GetInterpolatedTransform called on turret entity when IsInWorld is false"); CMatrix3D m; m.SetIdentity(); return m; } else { CMatrix3D parentTransformMatrix = cmpPosition->GetInterpolatedTransform(frameOffset); CMatrix3D ownTransformation = CMatrix3D(); ownTransformation.SetYRotation(m_InterpolatedRotY); ownTransformation.Translate(-m_TurretPosition.X.ToFloat(), m_TurretPosition.Y.ToFloat(), -m_TurretPosition.Z.ToFloat()); return parentTransformMatrix * ownTransformation; } } if (!m_InWorld) { LOGERROR("CCmpPosition::GetInterpolatedTransform called on entity when IsInWorld is false"); CMatrix3D m; m.SetIdentity(); return m; } float x, z, rotY; GetInterpolatedPosition2D(frameOffset, x, z, rotY); float baseY = 0; if (m_RelativeToGround) { CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity()); if (cmpTerrain) baseY = cmpTerrain->GetExactGroundLevel(x, z); if (m_Floating || m_ActorFloating) { CmpPtr<ICmpWaterManager> cmpWaterManager(GetSystemEntity()); if (cmpWaterManager) baseY = std::max(baseY, cmpWaterManager->GetExactWaterLevel(x, z)); } } float y = baseY + m_Y.ToFloat() + Interpolate(-1 * m_LastYDifference.ToFloat(), 0.f, frameOffset); CMatrix3D m; // linear interpolation is good enough (for RotX/Z). // As you always stay close to zero angle. m.SetXRotation(Interpolate(m_LastInterpolatedRotX, m_InterpolatedRotX, frameOffset)); m.RotateZ(Interpolate(m_LastInterpolatedRotZ, m_InterpolatedRotZ, frameOffset)); CVector3D pos(x, y, z); pos.Y += GetConstructionProgressOffset(pos); m.RotateY(rotY + (float)M_PI); m.Translate(pos); return m; }
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) { case MT_Interpolate: { if (!m_Active) break; const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition || !cmpPosition->IsInWorld()) { // If there's no position (this usually shouldn't happen), destroy the unit immediately GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); break; } // Compute the depth the first time this is called // (This is a bit of an ugly place to do it but at least we'll be sure // the actor component was loaded already) if (m_TotalSinkDepth < 0.f) { m_TotalSinkDepth = 1.f; // minimum so we always sink at least a little CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId()); if (cmpVisual) { CBoundingBoxAligned bound = cmpVisual->GetBounds(); m_TotalSinkDepth = std::max(m_TotalSinkDepth, bound[1].Y - bound[0].Y); } // If this is a floating unit, we want it to sink all the way under the terrain, // so find the difference between its current position and the terrain CFixedVector3D pos = cmpPosition->GetPosition(); CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); if (cmpTerrain) { fixed ground = cmpTerrain->GetGroundLevel(pos.X, pos.Z); m_TotalSinkDepth += std::max(0.f, (pos.Y - ground).ToFloat()); } // Sink it further down if it sinks like a ship, as it will rotate. if (m_ShipSink) m_TotalSinkDepth += 10.f; // probably 0 in both cases but we'll remember it anyway. m_InitialXRotation = cmpPosition->GetRotation().X; m_InitialZRotation = cmpPosition->GetRotation().Z; } m_CurrentTime += msgData.deltaSimTime; if (m_CurrentTime >= m_DelayTime) { float t = m_CurrentTime - m_DelayTime; float depth = (m_SinkRate * t) + (m_SinkAccel * t * t); if (m_ShipSink) { // exponential sinking with tilting float tilt_time = t > 5.f ? 5.f : t; float tiltSink = tilt_time * tilt_time / 5.f; entity_pos_t RotX = entity_pos_t::FromFloat(((m_InitialXRotation.ToFloat() * (5.f - tiltSink)) + (1.5f * tiltSink)) / 5.f); entity_pos_t RotZ = entity_pos_t::FromFloat(((m_InitialZRotation.ToFloat() * (3.f - tilt_time)) + (-0.3f * tilt_time)) / 3.f); cmpPosition->SetXZRotation(RotX,RotZ); depth = m_SinkRate * (exp(t - 1.f) - 0.54881163609f) + (m_SinkAccel * exp(t - 4.f) - 0.01831563888f); if (depth < 0.f) depth = 0.f; } cmpPosition->SetHeightOffset(entity_pos_t::FromFloat(-depth)); if (depth > m_TotalSinkDepth) GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); } break; } } }
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<ICmpPosition> 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<ICmpOwnership> 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<ICmpUnitMotion> 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<ICmpUnitMotion> 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<ICmpVisual> 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 ResolveFoundationCollisions() { if (m_Type == UNIT) return; CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return; CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); if (!cmpPosition) return; // error if (!cmpPosition->IsInWorld()) return; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); // Ignore collisions within the same control group, or with other non-foundation-blocking shapes. // Note that, since the control group for each entity defaults to the entity's ID, this is typically // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes. SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2, ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); std::vector<entity_id_t> collisions; if (cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &collisions)) { std::vector<entity_id_t> persistentEnts, normalEnts; if (m_ControlPersist) persistentEnts.push_back(m_ControlGroup); else normalEnts.push_back(GetEntityId()); for (std::vector<entity_id_t>::iterator it = collisions.begin(); it != collisions.end(); ++it) { entity_id_t ent = *it; if (ent == INVALID_ENTITY) continue; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); if (!cmpObstruction->IsControlPersistent()) normalEnts.push_back(ent); else persistentEnts.push_back(cmpObstruction->GetControlGroup()); } // The collision can't be resolved without usable persistent control groups. if (persistentEnts.empty()) return; // Attempt to replace colliding entities' control groups with a persistent one. for (std::vector<entity_id_t>::iterator it = normalEnts.begin(); it != normalEnts.end(); ++it) { entity_id_t ent = *it; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); for (std::vector<entity_id_t>::iterator it = persistentEnts.begin(); it != persistentEnts.end(); ++it) { entity_id_t persistent = *it; entity_id_t group = cmpObstruction->GetControlGroup(); // Only clobber 'default' control groups. if (group == ent) cmpObstruction->SetControlGroup(persistent); else if (cmpObstruction->GetControlGroup2() == INVALID_ENTITY && group != persistent) cmpObstruction->SetControlGroup2(persistent); } } } }
// ApplyData: take all the input data, and rebuild the scene from it int CMapReader::ApplyData() { if (m_PatchesPerSide == 0) { // we'll probably crash when trying to use this map later throw PSERROR_Game_World_MapLoadFailed("Error loading map: no terrain data.\nCheck application log for details."); } if (!only_xml) { // initialise the terrain pTerrain->Initialize(m_PatchesPerSide, &m_Heightmap[0]); // setup the textures on the minipatches STileDesc* tileptr = &m_Tiles[0]; for (ssize_t j=0; j<m_PatchesPerSide; j++) { for (ssize_t i=0; i<m_PatchesPerSide; i++) { for (ssize_t m=0; m<PATCH_SIZE; m++) { for (ssize_t k=0; k<PATCH_SIZE; k++) { CMiniPatch& mp = pTerrain->GetPatch(i,j)->m_MiniPatches[m][k]; // can't fail mp.Tex = m_TerrainTextures[tileptr->m_Tex1Index]; mp.Priority = tileptr->m_Priority; tileptr++; } } } } } // copy over the lighting parameters if (pLightEnv) *pLightEnv = m_LightEnv; CmpPtr<ICmpPlayerManager> cmpPlayerManager(*pSimContext, SYSTEM_ENTITY); if (pGameView && cmpPlayerManager) { // Default to global camera (with constraints) pGameView->ResetCameraTarget(pGameView->GetCamera()->GetFocus()); // TODO: Starting rotation? CmpPtr<ICmpPlayer> cmpPlayer(*pSimContext, cmpPlayerManager->GetPlayerByID(m_PlayerID)); if (cmpPlayer && cmpPlayer->HasStartingCamera()) { // Use player starting camera CFixedVector3D pos = cmpPlayer->GetStartingCameraPos(); pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat())); } else if (m_StartingCameraTarget != INVALID_ENTITY) { // Point camera at entity CmpPtr<ICmpPosition> cmpPosition(*pSimContext, m_StartingCameraTarget); if (cmpPosition) { CFixedVector3D pos = cmpPosition->GetPosition(); pGameView->ResetCameraTarget(CVector3D(pos.X.ToFloat(), pos.Y.ToFloat(), pos.Z.ToFloat())); } } } CmpPtr<ICmpTerrain> cmpTerrain(*pSimContext, SYSTEM_ENTITY); if (cmpTerrain) cmpTerrain->ReloadTerrain(); return 0; }
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) { switch (msg.GetType()) { case MT_Interpolate: { if (!m_Active) break; const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (cmpPosition.null() || !cmpPosition->IsInWorld()) { // If there's no position (this usually shouldn't happen), destroy the unit immediately GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); break; } // Compute the depth the first time this is called // (This is a bit of an ugly place to do it but at least we'll be sure // the actor component was loaded already) if (m_TotalSinkDepth < 0.f) { m_TotalSinkDepth = 1.f; // minimum so we always sink at least a little CmpPtr<ICmpVisual> cmpVisual(GetSimContext(), GetEntityId()); if (!cmpVisual.null()) { CBound bound = cmpVisual->GetBounds(); m_TotalSinkDepth = std::max(m_TotalSinkDepth, bound[1].Y - bound[0].Y); } // If this is a floating unit, we want it to sink all the way under the terrain, // so find the difference between its current position and the terrain CFixedVector3D pos = cmpPosition->GetPosition(); CmpPtr<ICmpTerrain> cmpTerrain(GetSimContext(), SYSTEM_ENTITY); if (!cmpTerrain.null()) { fixed ground = cmpTerrain->GetGroundLevel(pos.X, pos.Z); m_TotalSinkDepth += std::max(0.f, (pos.Y - ground).ToFloat()); } } m_CurrentTime += msgData.frameTime; if (m_CurrentTime > m_DelayTime) { float t = m_CurrentTime - m_DelayTime; float depth = (m_SinkRate * t) + (m_SinkAccel * t * t); cmpPosition->SetHeightOffset(entity_pos_t::FromFloat(-depth)); if (depth > m_TotalSinkDepth) GetSimContext().GetComponentManager().DestroyComponentsSoon(GetEntityId()); } break; } } }