/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2016, 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 "common/make_unique.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 CObjectManager::CObjectManager(Gfx::CEngine* engine, Gfx::CTerrain* terrain, Gfx::COldModelManager* oldModelManager, Gfx::CModelManager* modelManager, Gfx::CParticle* particle) : m_objectFactory(MakeUnique(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(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; } 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(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; } } 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(Math::Vector pos, float angle, ObjectType type, float power) { ObjectCreateParams params; params.pos = pos; params.angle = angle; params.type = type; params.power = power; return CreateObject(params); } std::vector CObjectManager::GetObjectsOfTeam(int team) { std::vector 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(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 CObjectManager::RadarAll(CObject* pThis, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { std::vector types; if (type != OBJECT_NULL) types.push_back(type); return RadarAll(pThis, types, angle, focus, minDist, maxDist, furthest, filter, cbotTypes); } std::vector CObjectManager::RadarAll(CObject* pThis, std::vector type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { Math::Vector iPos; float iAngle; if (pThis != nullptr) { iPos = pThis->GetPosition(); iAngle = pThis->GetRotationY(); iAngle = Math::NormAngle(iAngle); // 0..2*Math::PI } else { iPos = Math::Vector(); iAngle = 0.0f; } return RadarAll(pThis, iPos, iAngle, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes); } std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector thisPosition, float thisAngle, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { std::vector types; if (type != OBJECT_NULL) types.push_back(type); return RadarAll(pThis, thisPosition, thisAngle, types, angle, focus, minDist, maxDist, furthest, filter, cbotTypes); } std::vector CObjectManager::RadarAll(CObject* pThis, Math::Vector thisPosition, float thisAngle, std::vector type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { CObject *pObj; Math::Vector iPos, oPos; 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(filter & (FILTER_ONLYLANDING | FILTER_ONLYFLYING)); RadarFilter filter_enemy = static_cast(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 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 ruin } if ( oType == OBJECT_BARRIER2 || oType == OBJECT_BARRIER3 || oType == OBJECT_BARRICADE0 || oType == OBJECT_BARRICADE1 ) // barriers? { oType = OBJECT_BARRIER1; // any barrier } // 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(pObj)->GetPhysics(); if ( physics != nullptr ) { if ( !physics->GetLand() ) continue; } } } if ( filter_flying == FILTER_ONLYFLYING ) { if ( !pObj->Implements(ObjectInterfaceType::Movable) ) continue; CPhysics* physics = dynamic_cast(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(enemy | FILTER_NEUTRAL); if ( pObj->GetTeam() != 0 && pObj->GetTeam() == pThis->GetTeam() ) enemy = static_cast(enemy | FILTER_FRIENDLY); if ( pObj->GetTeam() != 0 && pObj->GetTeam() != pThis->GetTeam() ) enemy = static_cast(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 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 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 type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { std::vector best = RadarAll(pThis, type, angle, focus, minDist, maxDist, furthest, filter, cbotTypes); return best.size() > 0 ? best[0] : nullptr; } CObject* CObjectManager::Radar(CObject* pThis, Math::Vector thisPosition, float thisAngle, ObjectType type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { std::vector 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, Math::Vector thisPosition, float thisAngle, std::vector type, float angle, float focus, float minDist, float maxDist, bool furthest, RadarFilter filter, bool cbotTypes) { std::vector 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 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, Math::Vector 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, Math::Vector thisPosition, std::vector 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); }