465 lines
15 KiB
C++
465 lines
15 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2023, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
|
|
*
|
|
* This program 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program. If not, see http://gnu.org/licenses
|
|
*/
|
|
|
|
#include "object/object_manager.h"
|
|
|
|
#include "common/global.h"
|
|
|
|
#include "math/all.h"
|
|
|
|
#include "object/object.h"
|
|
#include "object/object_create_exception.h"
|
|
#include "object/object_create_params.h"
|
|
#include "object/object_factory.h"
|
|
#include "object/old_object.h"
|
|
|
|
#include "object/auto/auto.h"
|
|
|
|
#include "physics/physics.h"
|
|
|
|
#include <algorithm>
|
|
|
|
CObjectManager::CObjectManager(Gfx::CEngine* engine,
|
|
Gfx::CTerrain* terrain,
|
|
Gfx::COldModelManager* oldModelManager,
|
|
Gfx::CModelManager* modelManager,
|
|
Gfx::CParticle* particle)
|
|
: m_objectFactory(std::make_unique<CObjectFactory>(engine,
|
|
terrain,
|
|
oldModelManager,
|
|
modelManager,
|
|
particle)),
|
|
m_nextId(0),
|
|
m_activeObjectIterators(0),
|
|
m_shouldCleanRemovedObjects(false)
|
|
{
|
|
}
|
|
|
|
CObjectManager::~CObjectManager()
|
|
{
|
|
}
|
|
|
|
bool CObjectManager::DeleteObject(CObject* instance)
|
|
{
|
|
assert(instance != nullptr);
|
|
|
|
// TODO: temporarily...
|
|
auto oldObj = dynamic_cast<COldObject*>(instance);
|
|
if (oldObj != nullptr)
|
|
oldObj->DeleteObject();
|
|
|
|
auto it = m_objects.find(instance->GetID());
|
|
if (it != m_objects.end())
|
|
{
|
|
it->second.reset();
|
|
m_shouldCleanRemovedObjects = true;
|
|
return true;
|
|
} else assert(false);
|
|
|
|
return false;
|
|
}
|
|
|
|
void CObjectManager::CleanRemovedObjectsIfNeeded()
|
|
{
|
|
if (m_activeObjectIterators != 0)
|
|
return;
|
|
|
|
if (! m_shouldCleanRemovedObjects)
|
|
return;
|
|
|
|
auto it = m_objects.begin();
|
|
if (it != m_objects.end())
|
|
{
|
|
if (it->second == nullptr)
|
|
it = m_objects.erase(it);
|
|
}
|
|
|
|
m_shouldCleanRemovedObjects = false;
|
|
}
|
|
|
|
void CObjectManager::DeleteAllObjects()
|
|
{
|
|
for (auto& it : m_objects)
|
|
{
|
|
// TODO: temporarily...
|
|
auto oldObj = dynamic_cast<COldObject*>(it.second.get());
|
|
if (oldObj != nullptr)
|
|
{
|
|
bool all = true;
|
|
oldObj->DeleteObject(all);
|
|
}
|
|
}
|
|
|
|
m_objects.clear();
|
|
|
|
m_nextId = 0;
|
|
}
|
|
|
|
CObject* CObjectManager::GetObjectById(unsigned int id)
|
|
{
|
|
if (m_objects.count(id) == 0) return nullptr;
|
|
return m_objects[id].get();
|
|
}
|
|
|
|
CObject* CObjectManager::GetObjectByRank(unsigned int id)
|
|
{
|
|
auto objects = GetAllObjects();
|
|
auto it = objects.begin();
|
|
for (unsigned int i = 0; i < id && it != objects.end(); i++, ++it);
|
|
if (it == objects.end()) return nullptr;
|
|
return *it;
|
|
}
|
|
|
|
CObject* CObjectManager::CreateObject(ObjectCreateParams params)
|
|
{
|
|
if (params.id < 0)
|
|
{
|
|
params.id = m_nextId;
|
|
m_nextId++;
|
|
}
|
|
else
|
|
{
|
|
if (params.id >= m_nextId)
|
|
{
|
|
m_nextId = params.id + 1;
|
|
}
|
|
}
|
|
|
|
params.power = ClampPower(params.type,params.power);
|
|
|
|
assert(m_objects.find(params.id) == m_objects.end());
|
|
|
|
auto objectUPtr = m_objectFactory->CreateObject(params);
|
|
|
|
if (objectUPtr == nullptr)
|
|
throw CObjectCreateException("Something went wrong in CObjectFactory", params.type);
|
|
|
|
CObject* objectPtr = objectUPtr.get();
|
|
|
|
m_objects[params.id] = std::move(objectUPtr);
|
|
|
|
return objectPtr;
|
|
}
|
|
|
|
CObject* CObjectManager::CreateObject(glm::vec3 pos, float angle, ObjectType type, float power)
|
|
{
|
|
ObjectCreateParams params;
|
|
params.pos = pos;
|
|
params.angle = angle;
|
|
params.type = type;
|
|
params.power = power;
|
|
return CreateObject(params);
|
|
}
|
|
|
|
float CObjectManager::ClampPower(ObjectType type, float power)
|
|
{
|
|
float min = 0;
|
|
float max = 100;
|
|
if (type == OBJECT_POWER || type == OBJECT_ATOMIC)
|
|
{
|
|
max = 1;
|
|
}
|
|
return glm::clamp(power, min, max);
|
|
}
|
|
|
|
std::vector<CObject*> CObjectManager::GetObjectsOfTeam(int team)
|
|
{
|
|
std::vector<CObject*> result;
|
|
for (CObject* object : GetAllObjects())
|
|
{
|
|
if (object->GetTeam() == team)
|
|
{
|
|
result.push_back(object);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool CObjectManager::TeamExists(int team)
|
|
{
|
|
if(team == 0) return true;
|
|
|
|
for (CObject* object : GetAllObjects())
|
|
{
|
|
if (!object->GetActive())
|
|
continue;
|
|
|
|
if (object->GetTeam() == team)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CObjectManager::DestroyTeam(int team, DestructionType destructionType)
|
|
{
|
|
assert(team != 0);
|
|
|
|
for (CObject* object : GetAllObjects())
|
|
{
|
|
if (object->GetTeam() == team)
|
|
{
|
|
if (object->Implements(ObjectInterfaceType::Destroyable))
|
|
{
|
|
dynamic_cast<CDestroyableObject&>(*object).DestroyObject(destructionType);
|
|
}
|
|
else
|
|
{
|
|
DeleteObject(object);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int CObjectManager::CountObjectsImplementing(ObjectInterfaceType interface)
|
|
{
|
|
int count = 0;
|
|
for (CObject* object : GetAllObjects())
|
|
{
|
|
if (object->Implements(interface))
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
std::vector<CObject*> CObjectManager::RadarAll(CObject* pThis, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
std::vector<ObjectType> types;
|
|
if (type != OBJECT_NULL)
|
|
types.push_back(type);
|
|
return RadarAll(pThis, types, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
}
|
|
|
|
std::vector<CObject*> CObjectManager::RadarAll(CObject* pThis, std::vector<ObjectType> type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
glm::vec3 iPos{};
|
|
float iAngle;
|
|
if (pThis != nullptr)
|
|
{
|
|
iPos = pThis->GetPosition();
|
|
iAngle = pThis->GetRotationY();
|
|
iAngle = Math::NormAngle(iAngle); // 0..2*Math::PI
|
|
}
|
|
else
|
|
{
|
|
iPos = glm::vec3(0, 0, 0);
|
|
iAngle = 0.0f;
|
|
}
|
|
return RadarAll(pThis, iPos, iAngle, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
}
|
|
|
|
std::vector<CObject*> CObjectManager::RadarAll(CObject* pThis, glm::vec3 thisPosition, float thisAngle, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
std::vector<ObjectType> types;
|
|
if (type != OBJECT_NULL)
|
|
types.push_back(type);
|
|
return RadarAll(pThis, thisPosition, thisAngle, types, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
}
|
|
|
|
std::vector<CObject*> CObjectManager::RadarAll(CObject* pThis, glm::vec3 thisPosition, float thisAngle, std::vector<ObjectType> type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
CObject *pObj;
|
|
glm::vec3 iPos{ 0, 0, 0 }, oPos{ 0, 0, 0 };
|
|
float iAngle, d, a;
|
|
ObjectType oType;
|
|
|
|
minDist *= g_unit;
|
|
maxDist *= g_unit;
|
|
|
|
iPos = thisPosition;
|
|
iAngle = thisAngle+angle;
|
|
iAngle = Math::NormAngle(iAngle); // 0..2*Math::PI
|
|
|
|
int filter_team = filter & 0xFF;
|
|
RadarFilter filter_flying = static_cast<RadarFilter>(filter & (FILTER_ONLYLANDING | FILTER_ONLYFLYING));
|
|
RadarFilter filter_enemy = static_cast<RadarFilter>(filter & (FILTER_FRIENDLY | FILTER_ENEMY | FILTER_NEUTRAL));
|
|
|
|
// Use a multimap to allow for multiple objects at exactly the same distance
|
|
// from the origin to be returned.
|
|
std::multimap<float, CObject*> best;
|
|
|
|
for ( auto it = m_objects.begin() ; it != m_objects.end() ; ++it )
|
|
{
|
|
pObj = it->second.get();
|
|
if ( pObj == pThis ) continue; // pThis may be nullptr but it doesn't matter
|
|
|
|
if (pObj == nullptr) continue;
|
|
if (IsObjectBeingTransported(pObj)) continue;
|
|
if ( !pObj->GetDetectable() ) continue;
|
|
if ( pObj->GetProxyActivate() ) continue;
|
|
|
|
oType = pObj->GetType();
|
|
|
|
if (cbotTypes)
|
|
{
|
|
// TODO: handle this differently (new class describing types? CObjectType::GetBaseType()?)
|
|
if ( oType == OBJECT_RUINmobilew2 ||
|
|
oType == OBJECT_RUINmobilet1 ||
|
|
oType == OBJECT_RUINmobilet2 ||
|
|
oType == OBJECT_RUINmobiler1 ||
|
|
oType == OBJECT_RUINmobiler2 )
|
|
{
|
|
oType = OBJECT_RUINmobilew1; // any wreck
|
|
}
|
|
|
|
if ( oType == OBJECT_BARRIER2 ||
|
|
oType == OBJECT_BARRIER3 ||
|
|
oType == OBJECT_BARRICADE0 ||
|
|
oType == OBJECT_BARRICADE1 ) // barriers?
|
|
{
|
|
oType = OBJECT_BARRIER1; // any barrier
|
|
}
|
|
|
|
if ( oType == OBJECT_RUINdoor ||
|
|
oType == OBJECT_RUINsupport ||
|
|
oType == OBJECT_RUINradar ||
|
|
oType == OBJECT_RUINconvert ) // ruins?
|
|
{
|
|
oType = OBJECT_RUINfactory; // any ruin
|
|
}
|
|
|
|
if ( oType == OBJECT_PLANT1 ||
|
|
oType == OBJECT_PLANT2 ||
|
|
oType == OBJECT_PLANT3 ||
|
|
oType == OBJECT_PLANT4 ||
|
|
oType == OBJECT_PLANT15 ||
|
|
oType == OBJECT_PLANT16 ||
|
|
oType == OBJECT_PLANT17 ||
|
|
oType == OBJECT_PLANT18 ) // bushes?
|
|
{
|
|
oType = OBJECT_PLANT0; // any bush
|
|
}
|
|
|
|
if ( oType == OBJECT_QUARTZ1 ||
|
|
oType == OBJECT_QUARTZ2 ||
|
|
oType == OBJECT_QUARTZ3 ) // crystals?
|
|
{
|
|
oType = OBJECT_QUARTZ0; // any crystal
|
|
}
|
|
// END OF TODO
|
|
}
|
|
|
|
if ( std::find(type.begin(), type.end(), oType) == type.end() && type.size() > 0 ) continue;
|
|
|
|
if ( (oType == OBJECT_TOTO || oType == OBJECT_CONTROLLER) && type.size() == 0 ) continue; // allow OBJECT_TOTO and OBJECT_CONTROLLER only if explicitly asked in type parameter
|
|
|
|
if ( filter_flying == FILTER_ONLYLANDING )
|
|
{
|
|
if ( pObj->Implements(ObjectInterfaceType::Movable) )
|
|
{
|
|
CPhysics* physics = dynamic_cast<CMovableObject&>(*pObj).GetPhysics();
|
|
if ( physics != nullptr )
|
|
{
|
|
if ( !physics->GetLand() ) continue;
|
|
}
|
|
}
|
|
}
|
|
if ( filter_flying == FILTER_ONLYFLYING )
|
|
{
|
|
if ( !pObj->Implements(ObjectInterfaceType::Movable) ) continue;
|
|
CPhysics* physics = dynamic_cast<CMovableObject&>(*pObj).GetPhysics();
|
|
if ( physics == nullptr ) continue;
|
|
if ( physics->GetLand() ) continue;
|
|
}
|
|
|
|
if ( filter_team != 0 && pObj->GetTeam() != filter_team )
|
|
continue;
|
|
|
|
if( pThis != nullptr )
|
|
{
|
|
RadarFilter enemy = FILTER_NONE;
|
|
if ( pObj->GetTeam() == 0 ) enemy = static_cast<RadarFilter>(enemy | FILTER_NEUTRAL);
|
|
if ( pObj->GetTeam() != 0 && pObj->GetTeam() == pThis->GetTeam() ) enemy = static_cast<RadarFilter>(enemy | FILTER_FRIENDLY);
|
|
if ( pObj->GetTeam() != 0 && pObj->GetTeam() != pThis->GetTeam() ) enemy = static_cast<RadarFilter>(enemy | FILTER_ENEMY);
|
|
if ( filter_enemy != 0 && (filter_enemy & enemy) == 0 ) continue;
|
|
}
|
|
|
|
oPos = pObj->GetPosition();
|
|
d = Math::DistanceProjected(iPos, oPos);
|
|
if ( d < minDist || d > maxDist ) continue; // too close or too far?
|
|
|
|
a = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW !
|
|
if ( Math::TestAngle(a, iAngle-focus/2.0f, iAngle+focus/2.0f) || focus >= Math::PI*2.0f )
|
|
{
|
|
best.insert(std::make_pair(d, pObj));
|
|
}
|
|
}
|
|
|
|
std::vector<CObject*> sortedBest;
|
|
if (!furthest)
|
|
{
|
|
for (auto it = best.begin(); it != best.end(); ++it)
|
|
{
|
|
sortedBest.push_back(it->second);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto it = best.rbegin(); it != best.rend(); ++it)
|
|
{
|
|
sortedBest.push_back(it->second);
|
|
}
|
|
}
|
|
|
|
return sortedBest;
|
|
}
|
|
|
|
CObject* CObjectManager::Radar(CObject* pThis, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
std::vector<CObject*> best = RadarAll(pThis, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
return best.size() > 0 ? best[0] : nullptr;
|
|
}
|
|
|
|
CObject* CObjectManager::Radar(CObject* pThis, std::vector<ObjectType> type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
std::vector<CObject*> best = RadarAll(pThis, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
return best.size() > 0 ? best[0] : nullptr;
|
|
}
|
|
|
|
CObject* CObjectManager::Radar(CObject* pThis, glm::vec3 thisPosition, float thisAngle, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
std::vector<CObject*> best = RadarAll(pThis, thisPosition, thisAngle, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
return best.size() > 0 ? best[0] : nullptr;
|
|
}
|
|
|
|
CObject* CObjectManager::Radar(CObject* pThis, glm::vec3 thisPosition, float thisAngle, std::vector<ObjectType> type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes)
|
|
{
|
|
std::vector<CObject*> best = RadarAll(pThis, thisPosition, thisAngle, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes);
|
|
return best.size() > 0 ? best[0] : nullptr;
|
|
}
|
|
|
|
CObject* CObjectManager::FindNearest(CObject* pThis, ObjectType type, float maxDist, bool cbotTypes)
|
|
{
|
|
return Radar(pThis, type, 0.0f, Math::PI*2.0f, 0.0f, maxDist, false, FILTER_NONE, cbotTypes);
|
|
}
|
|
|
|
CObject* CObjectManager::FindNearest(CObject* pThis, std::vector<ObjectType> type, float maxDist, bool cbotTypes)
|
|
{
|
|
return Radar(pThis, type, 0.0f, Math::PI*2.0f, 0.0f, maxDist, false, FILTER_NONE, cbotTypes);
|
|
}
|
|
|
|
CObject* CObjectManager::FindNearest(CObject* pThis, glm::vec3 thisPosition, ObjectType type, float maxDist, bool cbotTypes)
|
|
{
|
|
return Radar(pThis, thisPosition, 0.0f, type, 0.0f, Math::PI*2.0f, 0.0f, maxDist, false, FILTER_NONE, cbotTypes);
|
|
}
|
|
|
|
CObject* CObjectManager::FindNearest(CObject* pThis, glm::vec3 thisPosition, std::vector<ObjectType> type, float maxDist, bool cbotTypes)
|
|
{
|
|
return Radar(pThis, thisPosition, 0.0f, type, 0.0f, Math::PI*2.0f, 0.0f, maxDist, false, FILTER_NONE, cbotTypes);
|
|
}
|