/* Copyright (C) 2011 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 "simulation2/system/Component.h"
#include "ICmpObstruction.h"
#include "simulation2/components/ICmpObstructionManager.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/MessageTypes.h"
/**
* Obstruction implementation. This keeps the ICmpPathfinder's model of the world updated when the
* entities move and die, with shapes derived from ICmpFootprint.
*/
class CCmpObstruction : public ICmpObstruction
{
public:
static void ClassInit(CComponentManager& componentManager)
{
componentManager.SubscribeToMessageType(MT_PositionChanged);
componentManager.SubscribeToMessageType(MT_Destroy);
}
DEFAULT_COMPONENT_ALLOCATOR(Obstruction)
typedef ICmpObstructionManager::tag_t tag_t;
typedef ICmpObstructionManager::flags_t flags_t;
// Template state:
enum {
STATIC,
UNIT
} m_Type;
entity_pos_t m_Size0; // radius or width
entity_pos_t m_Size1; // radius or depth
flags_t m_TemplateFlags;
// Dynamic state:
bool m_Active; // whether the obstruction is obstructing or just an inactive placeholder
bool m_Moving;
entity_id_t m_ControlGroup;
tag_t m_Tag;
flags_t m_Flags;
static std::string GetSchema()
{
return
""
"Causes this entity to obstruct the motion of other units."
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
""
"";
}
virtual void Init(const CParamNode& paramNode)
{
if (paramNode.GetChild("Unit").IsOk())
{
m_Type = UNIT;
m_Size0 = m_Size1 = paramNode.GetChild("Unit").GetChild("@radius").ToFixed();
}
else
{
m_Type = STATIC;
m_Size0 = paramNode.GetChild("Static").GetChild("@width").ToFixed();
m_Size1 = paramNode.GetChild("Static").GetChild("@depth").ToFixed();
}
m_TemplateFlags = 0;
if (paramNode.GetChild("BlockMovement").ToBool())
m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_MOVEMENT;
if (paramNode.GetChild("BlockPathfinding").ToBool())
m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_PATHFINDING;
if (paramNode.GetChild("BlockFoundation").ToBool())
m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_FOUNDATION;
if (paramNode.GetChild("BlockConstruction").ToBool())
m_TemplateFlags |= ICmpObstructionManager::FLAG_BLOCK_CONSTRUCTION;
m_Flags = m_TemplateFlags;
if (paramNode.GetChild("DisableBlockMovement").ToBool())
m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
if (paramNode.GetChild("DisableBlockPathfinding").ToBool())
m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
m_Active = paramNode.GetChild("Active").ToBool();
m_Tag = tag_t();
m_Moving = false;
m_ControlGroup = GetEntityId();
}
virtual void Deinit()
{
}
template
void SerializeCommon(S& serialize)
{
serialize.Bool("active", m_Active);
serialize.Bool("moving", m_Moving);
serialize.NumberU32_Unbounded("control group", m_ControlGroup);
serialize.NumberU32_Unbounded("tag", m_Tag.n);
serialize.NumberU8_Unbounded("flags", m_Flags);
}
virtual void Serialize(ISerializer& serialize)
{
SerializeCommon(serialize);
}
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
{
Init(paramNode);
SerializeCommon(deserialize);
}
virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
{
switch (msg.GetType())
{
case MT_PositionChanged:
{
if (!m_Active)
break;
const CMessagePositionChanged& data = static_cast (msg);
if (!data.inWorld && !m_Tag.valid())
break; // nothing needs to change
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (cmpObstructionManager.null())
break; // error
if (data.inWorld && m_Tag.valid())
{
cmpObstructionManager->MoveShape(m_Tag, data.x, data.z, data.a);
}
else if (data.inWorld && !m_Tag.valid())
{
// Need to create a new pathfinder shape:
if (m_Type == STATIC)
m_Tag = cmpObstructionManager->AddStaticShape(GetEntityId(),
data.x, data.z, data.a, m_Size0, m_Size1, m_Flags);
else
m_Tag = cmpObstructionManager->AddUnitShape(GetEntityId(),
data.x, data.z, m_Size0, (flags_t)(m_Flags | (m_Moving ? ICmpObstructionManager::FLAG_MOVING : 0)), m_ControlGroup);
}
else if (!data.inWorld && m_Tag.valid())
{
cmpObstructionManager->RemoveShape(m_Tag);
m_Tag = tag_t();
}
break;
}
case MT_Destroy:
{
if (m_Tag.valid())
{
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (cmpObstructionManager.null())
break; // error
cmpObstructionManager->RemoveShape(m_Tag);
m_Tag = tag_t();
}
break;
}
}
}
virtual void SetActive(bool active)
{
if (active && !m_Active)
{
m_Active = true;
// Construct the obstruction shape
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (cmpObstructionManager.null())
return; // error
CmpPtr 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 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 void SetDisableBlockMovementPathfinding(bool disabled)
{
if (disabled)
{
// Remove the blocking flags
m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_MOVEMENT);
m_Flags &= (flags_t)(~ICmpObstructionManager::FLAG_BLOCK_PATHFINDING);
}
else
{
// Add the blocking flags if the template had enabled them
m_Flags = (flags_t)(m_Flags | (m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT));
m_Flags = (flags_t)(m_Flags | (m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_PATHFINDING));
}
// Reset the shape with the new flags (kind of inefficiently - we
// should have a ICmpObstructionManager::SetFlags function or something)
if (m_Active)
{
SetActive(false);
SetActive(true);
}
}
virtual bool GetBlockMovementFlag()
{
return (m_TemplateFlags & ICmpObstructionManager::FLAG_BLOCK_MOVEMENT) != 0;
}
virtual ICmpObstructionManager::tag_t GetObstruction()
{
return m_Tag;
}
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare& out)
{
CmpPtr cmpPosition(GetSimContext(), GetEntityId());
if (cmpPosition.null())
return false; // error
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (cmpObstructionManager.null())
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;
}
virtual entity_pos_t GetUnitRadius()
{
if (m_Type == UNIT)
return m_Size0;
else
return entity_pos_t::Zero();
}
virtual bool CheckFoundation(std::string className)
{
CmpPtr cmpPosition(GetSimContext(), GetEntityId());
if (cmpPosition.null())
return false; // error
if (!cmpPosition->IsInWorld())
return false; // no obstruction
CFixedVector2D pos = cmpPosition->GetPosition2D();
CmpPtr cmpPathfinder(GetSimContext(), SYSTEM_ENTITY);
if (cmpPathfinder.null())
return false; // error
// Get passability class
ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className);
// Ignore collisions with self, or with non-foundation-blocking obstructions
SkipTagFlagsObstructionFilter filter(m_Tag, 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 std::vector GetConstructionCollisions()
{
std::vector ret;
CmpPtr cmpPosition(GetSimContext(), GetEntityId());
if (cmpPosition.null())
return ret; // error
if (!cmpPosition->IsInWorld())
return ret; // no obstruction
CFixedVector2D pos = cmpPosition->GetPosition2D();
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (cmpObstructionManager.null())
return ret; // error
// Ignore collisions with self, or with non-construction-blocking obstructions
SkipTagFlagsObstructionFilter filter(m_Tag, 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 void SetMovingFlag(bool enabled)
{
m_Moving = enabled;
if (m_Tag.valid() && m_Type == UNIT)
{
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (!cmpObstructionManager.null())
cmpObstructionManager->SetUnitMovingFlag(m_Tag, m_Moving);
}
}
virtual void SetControlGroup(entity_id_t group)
{
m_ControlGroup = group;
if (m_Tag.valid() && m_Type == UNIT)
{
CmpPtr cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY);
if (!cmpObstructionManager.null())
cmpObstructionManager->SetUnitControlGroup(m_Tag, m_ControlGroup);
}
}
};
REGISTER_COMPONENT_TYPE(Obstruction)