From 76314f522f7e880d122443b33d70e1cfe1f5ba4f Mon Sep 17 00:00:00 2001 From: immibis Date: Sun, 4 Jul 2021 16:39:49 +0200 Subject: [PATCH 1/7] Slotted object interface --- src/CMakeLists.txt | 3 +- src/graphics/engine/camera.cpp | 22 ++- src/graphics/engine/pyro.cpp | 57 +++---- src/level/robotmain.cpp | 134 ++++++++++------- src/level/scene_conditions.cpp | 11 +- src/object/auto/autolabo.cpp | 14 +- src/object/auto/autonuclearplant.cpp | 12 +- src/object/auto/autopowercaptor.cpp | 41 ++--- src/object/auto/autopowerplant.cpp | 17 ++- src/object/auto/autopowerstation.cpp | 32 +--- src/object/auto/autoresearch.cpp | 18 ++- src/object/auto/autotower.cpp | 18 +-- src/object/interface/carrier_object.h | 55 ------- src/object/interface/powered_object.h | 94 ------------ src/object/interface/slotted_object.h | 148 ++++++++++++++++++ src/object/motion/motionhuman.cpp | 2 - src/object/motion/motionvehicle.cpp | 8 +- src/object/object_interface_type.h | 3 +- src/object/old_object.cpp | 172 +++++++++++++++++---- src/object/old_object.h | 26 ++-- src/object/task/taskbuild.cpp | 1 - src/object/task/taskfire.cpp | 16 +- src/object/task/taskflag.cpp | 2 - src/object/task/taskgoto.cpp | 8 +- src/object/task/taskmanip.cpp | 208 +++++++++++++------------- src/object/task/taskmanip.h | 2 +- src/object/task/taskrecover.cpp | 15 +- src/object/task/taskshield.cpp | 16 +- src/object/task/tasktake.cpp | 153 +++++++++---------- src/object/task/tasktake.h | 2 +- src/object/task/taskterraform.cpp | 12 +- src/physics/physics.cpp | 22 +-- src/script/scriptfunc.cpp | 28 ++-- src/ui/object_interface.cpp | 18 +-- 34 files changed, 725 insertions(+), 665 deletions(-) delete mode 100644 src/object/interface/carrier_object.h delete mode 100644 src/object/interface/powered_object.h create mode 100644 src/object/interface/slotted_object.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c5f4063..28b8d1e9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -377,7 +377,6 @@ set(BASE_SOURCES object/implementation/programmable_impl.h object/implementation/task_executor_impl.cpp object/implementation/task_executor_impl.h - object/interface/carrier_object.h object/interface/controllable_object.h object/interface/damageable_object.h object/interface/destroyable_object.h @@ -388,12 +387,12 @@ set(BASE_SOURCES object/interface/jostleable_object.h object/interface/movable_object.h object/interface/power_container_object.h - object/interface/powered_object.h object/interface/program_storage_object.h object/interface/programmable_object.h object/interface/ranged_object.h object/interface/shielded_auto_regen_object.h object/interface/shielded_object.h + object/interface/slotted_object.h object/interface/task_executor_object.h object/interface/trace_drawing_object.cpp object/interface/trace_drawing_object.h diff --git a/src/graphics/engine/camera.cpp b/src/graphics/engine/camera.cpp index f3b96186..2ce3afda 100644 --- a/src/graphics/engine/camera.cpp +++ b/src/graphics/engine/camera.cpp @@ -37,10 +37,9 @@ #include "object/object.h" #include "object/object_manager.h" -#include "object/interface/carrier_object.h" #include "object/interface/controllable_object.h" #include "object/interface/movable_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" @@ -57,18 +56,15 @@ static void SetTransparency(CObject* obj, float value) { obj->SetTransparency(value); - if (obj->Implements(ObjectInterfaceType::Carrier)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*obj).GetCargo(); - if (cargo != nullptr) - cargo->SetTransparency(value); - } - - if (obj->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*obj).GetPower(); - if (power != nullptr) - power->SetTransparency(value); + CSlottedObject *slotted = dynamic_cast(obj); + for(int slot = slotted->GetNumSlots()-1; slot >= 0; slot--) + { + CObject *contained = slotted->GetSlotContainedObject(slot); + if (contained != nullptr) + SetTransparency(contained, value); + } } } diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index 33c144b8..250f7e19 100644 --- a/src/graphics/engine/pyro.cpp +++ b/src/graphics/engine/pyro.cpp @@ -35,6 +35,8 @@ #include "object/object_manager.h" #include "object/old_object.h" +#include "object/interface/slotted_object.h" + #include "object/motion/motionhuman.h" #include "object/subclass/shielder.h" @@ -127,9 +129,7 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) // Seeking the position of the battery. - CObject* power = nullptr; - if (obj->Implements(ObjectInterfaceType::Powered)) - power = dynamic_cast(*obj).GetPower(); + CObject* power = GetObjectInPowerCellSlot(obj); if (power == nullptr) { @@ -1385,25 +1385,16 @@ void CPyro::DeleteObject(bool primary, bool secondary) type != OBJECT_NUCLEAR && type != OBJECT_ENERGY ) { - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (m_object->Implements(ObjectInterfaceType::Slotted)) { - CPoweredObject* poweredObject = dynamic_cast(m_object); - CObject* sub = poweredObject->GetPower(); - if (sub != nullptr) + CSlottedObject* asSlotted = dynamic_cast(m_object); + for (int slot = asSlotted->GetNumSlots() - 1; slot >= 0; slot--) { - CObjectManager::GetInstancePointer()->DeleteObject(sub); - poweredObject->SetPower(nullptr); - } - } - - if (m_object->Implements(ObjectInterfaceType::Carrier)) - { - CCarrierObject* carrierObject = dynamic_cast(m_object); - CObject* sub = carrierObject->GetCargo(); - if (sub != nullptr) - { - CObjectManager::GetInstancePointer()->DeleteObject(sub); - carrierObject->SetCargo(nullptr); + if (CObject* sub = asSlotted->GetSlotContainedObject(slot)) + { + CObjectManager::GetInstancePointer()->DeleteObject(sub); + asSlotted->SetSlotContainedObject(slot, nullptr); + } } } } @@ -1416,18 +1407,15 @@ void CPyro::DeleteObject(bool primary, bool secondary) CObject* transporter = dynamic_cast(*m_object).GetTransporter(); if (transporter != nullptr) { - if (transporter->Implements(ObjectInterfaceType::Powered)) + assert(transporter->Implements(ObjectInterfaceType::Slotted)); + CSlottedObject* asSlotted = dynamic_cast(transporter); + for (int slotNum = asSlotted->GetNumSlots() - 1; slotNum >= 0; slotNum--) { - CPoweredObject* powered = dynamic_cast(transporter); - if (powered->GetPower() == m_object) - powered->SetPower(nullptr); - } - - if (transporter->Implements(ObjectInterfaceType::Carrier)) - { - CCarrierObject* carrier = dynamic_cast(transporter); - if (carrier->GetCargo() == m_object) - carrier->SetCargo(nullptr); + if (asSlotted->GetSlotContainedObject(slotNum) == m_object) + { + asSlotted->SetSlotContainedObject(slotNum, nullptr); + break; + } } } } @@ -2196,11 +2184,10 @@ void CPyro::BurnProgress() oldObj->SetPartRotation(m_burnPart[i].part, pos); } - if (m_object->Implements(ObjectInterfaceType::Powered)) + // TODO: should this apply to every slot? + if (CObject* sub = GetObjectInPowerCellSlot(m_object)) // is there a battery? { - CObject* sub = dynamic_cast(*m_object).GetPower(); - if (sub != nullptr) // is there a battery? - sub->SetScaleY(1.0f - m_progress); // complete flattening + sub->SetScaleY(1.0f - m_progress); // complete flattening } } diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index af04448c..e012caca 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -69,6 +69,8 @@ #include "object/auto/auto.h" +#include "object/interface/slotted_object.h" + #include "object/motion/motion.h" #include "object/motion/motionhuman.h" #include "object/motion/motiontoto.h" @@ -1392,12 +1394,8 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) CObject* object = GetSelect(); if (object != nullptr) { - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - dynamic_cast(*power).SetEnergyLevel(1.0f); - } + if (CPowerContainerObject *power = GetObjectPowerCell(object)) + power->SetEnergyLevel(1.0f); if (object->Implements(ObjectInterfaceType::Shielded)) dynamic_cast(*object).SetShield(1.0f); @@ -1414,12 +1412,8 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) if (object != nullptr) { - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - dynamic_cast(*power).SetEnergyLevel(1.0f); - } + if (CPowerContainerObject *power = GetObjectPowerCell(object)) + power->SetEnergyLevel(1.0f); } return; } @@ -2007,21 +2001,12 @@ CObject* CRobotMain::DetectObject(Math::Point pos) if (obj->GetProxyActivate()) continue; CObject* target = obj; + // TODO: should this also apply to slots other than power cell slots? if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable)) { - target = dynamic_cast(*obj).GetTransporter(); // battery connected - if (target == nullptr) - { - target = obj; // standalone battery - } - else - { - if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast(*target).GetPower() != obj) - { - // transported, but not in the power slot - target = obj; - } - } + CObject *transporter = dynamic_cast(*obj).GetTransporter(); // battery connected + if (transporter != nullptr && obj == GetObjectInPowerCellSlot(transporter)) + target = transporter; } if (!obj->Implements(ObjectInterfaceType::Old)) continue; @@ -4716,29 +4701,26 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s if (IsObjectBeingTransported(obj)) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*obj).IsDying()) continue; - if (obj->Implements(ObjectInterfaceType::Carrier)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*obj).GetCargo(); - if (cargo != nullptr) // object transported? + CSlottedObject* slotted = dynamic_cast(obj); + for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--) { - line = MakeUnique("CreateFret"); - IOWriteObject(line.get(), cargo, dirname, objRank++); - levelParser.AddLine(std::move(line)); + if (CObject *sub = slotted->GetSlotContainedObject(slot)) + { + if (slot == slotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER)) + line = MakeUnique("CreatePower"); + else if (slot == slotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING)) + line = MakeUnique("CreateFret"); + else + line = MakeUnique("CreateSlotObject"); + line->AddParam("slotNum", MakeUnique(slot)); + IOWriteObject(line.get(), sub, dirname, objRank++); + levelParser.AddLine(std::move(line)); + } } } - if (obj->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*obj).GetPower(); - if (power != nullptr) // battery transported? - { - line = MakeUnique("CreatePower"); - IOWriteObject(line.get(), power, dirname, objRank++); - levelParser.AddLine(std::move(line)); - } - } - - line = MakeUnique("CreateObject"); IOWriteObject(line.get(), obj, dirname, objRank++); levelParser.AddLine(std::move(line)); @@ -4867,7 +4849,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) CLevelParser levelParser(filename); levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank); levelParser.Load(); - int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret"); + int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret") + levelParser.CountLines("CreateSlotObject"); m_base = nullptr; @@ -4876,6 +4858,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) CObject* sel = nullptr; int objRank = 0; int objCounter = 0; + std::map slots; for (auto& line : levelParser.GetLines()) { if (line->GetCommand() == "Mission") @@ -4908,6 +4891,16 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) objCounter++; } + if (line->GetCommand() == "CreateSlotObject") + { + int slotNum = line->GetParam("slotNum")->AsInt(); + CObject *slotObject = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects)); + objCounter++; + + assert(slots.find(slotNum) == slots.end()); + slots.emplace(slotNum, slotObject); + } + if (line->GetCommand() == "CreateObject") { CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects), objRank++); @@ -4915,20 +4908,50 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) if (line->GetParam("select")->AsBool(false)) sel = obj; - if (cargo != nullptr) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - assert(obj->Implements(ObjectInterfaceType::Carrier)); // TODO: exception? - assert(obj->Implements(ObjectInterfaceType::Old)); - dynamic_cast(*obj).SetCargo(cargo); - auto task = MakeUnique(dynamic_cast(obj)); - task->Start(TMO_AUTO, TMA_GRAB); // holds the object! + CSlottedObject* asSlotted = dynamic_cast(obj); + if (cargo != nullptr) + { + int slotNum = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING); + assert(slotNum >= 0); + assert(slots.find(slotNum) == slots.end()); + asSlotted->SetSlotContainedObject(slotNum, cargo); + + // TODO: eww! + assert(obj->Implements(ObjectInterfaceType::Old)); + auto task = MakeUnique(dynamic_cast(obj)); + task->Start(TMO_AUTO, TMA_GRAB); // holds the object! + } + + if (power != nullptr) + { + int slotNum = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + assert(slotNum >= 0); + assert(slots.find(slotNum) == slots.end()); + asSlotted->SetSlotContainedObject(slotNum, power); + } + + for (std::pair& slot : slots) + { + asSlotted->SetSlotContainedObject(slot.first, slot.second); + } + + cargo = nullptr; + power = nullptr; + slots.clear(); + } + else + { + // TODO: exception? + assert(slots.empty()); + assert(power == nullptr); + assert(cargo == nullptr); } if (power != nullptr) { - assert(obj->Implements(ObjectInterfaceType::Powered)); - dynamic_cast(*obj).SetPower(power); - assert(power->Implements(ObjectInterfaceType::Transportable)); + dynamic_cast(*obj).SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, power); dynamic_cast(*power).SetTransporter(obj); } cargo = nullptr; @@ -4938,6 +4961,11 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) } } + // all slot objects assigned to parent objects + assert(slots.empty()); + assert(power == nullptr); + assert(cargo == nullptr); + m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE); // Reads the file of stacks of execution. diff --git a/src/level/scene_conditions.cpp b/src/level/scene_conditions.cpp index 36e2bdd9..c7cdafa5 100644 --- a/src/level/scene_conditions.cpp +++ b/src/level/scene_conditions.cpp @@ -26,7 +26,8 @@ #include "object/object.h" #include "object/object_manager.h" -#include "object/interface/powered_object.h" +#include "object/interface/power_container_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include @@ -80,13 +81,9 @@ bool CObjectCondition::CheckForObject(CObject* obj) { power = dynamic_cast(obj); } - else if (obj->Implements(ObjectInterfaceType::Powered)) + else { - CObject* powerObj = dynamic_cast(*obj).GetPower(); - if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) - { - power = dynamic_cast(powerObj); - } + power = GetObjectPowerCell(obj); } if (power != nullptr) diff --git a/src/object/auto/autolabo.cpp b/src/object/auto/autolabo.cpp index 0cdb4a57..2a05429e 100644 --- a/src/object/auto/autolabo.cpp +++ b/src/object/auto/autolabo.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "sound/sound.h" @@ -59,7 +59,7 @@ CAutoLabo::CAutoLabo(COldObject* object) : CAuto(object) m_soundChannel = -1; Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(object->GetNumSlots() == 1); } // Object's destructor. @@ -131,7 +131,7 @@ Error CAutoLabo::StartAction(int param) return ERR_LABO_ALREADY; } - CObject* power = m_object->GetPower(); + CObject* power = dynamic_cast(*m_object).GetSlotContainedObject(0); if (power == nullptr) { return ERR_LABO_NULL; @@ -308,7 +308,7 @@ bool CAutoLabo::EventProcess(const Event &event) { if ( m_progress < 1.0f ) { - power = m_object->GetPower(); + power = dynamic_cast(*m_object).GetSlotContainedObject(0); if ( power != nullptr ) { power->SetScale(1.0f-m_progress); @@ -366,10 +366,10 @@ bool CAutoLabo::EventProcess(const Event &event) m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); UpdateInterface(); - power = m_object->GetPower(); + power = dynamic_cast(*m_object).GetSlotContainedObject(0); if ( power != nullptr ) { - m_object->SetPower(nullptr); + dynamic_cast(*m_object).SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(power); } @@ -457,7 +457,7 @@ Error CAutoLabo::GetError() return ERR_BAT_VIRUS; } - CObject* obj = m_object->GetPower(); + CObject* obj = dynamic_cast(*m_object).GetSlotContainedObject(0); if (obj == nullptr) return ERR_LABO_NULL; ObjectType type = obj->GetType(); if ( type != OBJECT_BULLET && type != OBJECT_TNT ) return ERR_LABO_BAD; diff --git a/src/object/auto/autonuclearplant.cpp b/src/object/auto/autonuclearplant.cpp index 2c138c61..1f224323 100644 --- a/src/object/auto/autonuclearplant.cpp +++ b/src/object/auto/autonuclearplant.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "sound/sound.h" @@ -53,7 +53,7 @@ CAutoNuclearPlant::CAutoNuclearPlant(COldObject* object) : CAuto(object) m_channelSound = -1; Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -239,7 +239,7 @@ bool CAutoNuclearPlant::EventProcess(const Event &event) if ( cargo != nullptr ) { CObjectManager::GetInstancePointer()->DeleteObject(cargo); - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); } CreatePower(); // creates the atomic cell @@ -327,7 +327,7 @@ bool CAutoNuclearPlant::CreateInterface(bool bSelect) CObject* CAutoNuclearPlant::SearchUranium() { - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if (obj == nullptr) return nullptr; if (obj->GetType() == OBJECT_URANIUM) return obj; return nullptr; @@ -402,7 +402,7 @@ void CAutoNuclearPlant::CreatePower() dynamic_cast(*power).SetTransporter(m_object); power->SetPosition(Math::Vector(22.0f, 3.0f, 0.0f)); - m_object->SetPower(power); + m_object->SetSlotContainedObject(0, power); } @@ -422,7 +422,7 @@ Error CAutoNuclearPlant::GetError() //? if ( m_object->GetEnergy() < ENERGY_POWER ) return ERR_NUCLEAR_LOW; - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if ( obj == nullptr ) return ERR_NUCLEAR_EMPTY; if ( obj->GetLock() ) return ERR_OK; ObjectType type = obj->GetType(); diff --git a/src/object/auto/autopowercaptor.cpp b/src/object/auto/autopowercaptor.cpp index 614c5da4..56aa7d92 100644 --- a/src/object/auto/autopowercaptor.cpp +++ b/src/object/auto/autopowercaptor.cpp @@ -30,8 +30,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "sound/sound.h" @@ -267,34 +266,22 @@ void CAutoPowerCaptor::ChargeObject(float rTime) } } - if (obj->Implements(ObjectInterfaceType::Powered)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* power = dynamic_cast(*obj).GetPower(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) + CSlottedObject* slotted = dynamic_cast(obj); + for (int slot = slotted->GetNumSlots(); slot >= 0; slot--) { - CPowerContainerObject* powerContainer = dynamic_cast(power); - if (powerContainer->IsRechargeable()) + CObject *held = slotted->GetSlotContainedObject(slot); + if (held != nullptr && held->Implements(ObjectInterfaceType::PowerContainer)) { - float energy = powerContainer->GetEnergy(); - energy += rTime/2.0f; - if ( energy > 1.0f ) energy = 1.0f; - powerContainer->SetEnergy(energy); - } - } - } - - if (obj->Implements(ObjectInterfaceType::Carrier)) - { - CObject* power = dynamic_cast(*obj).GetCargo(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) - { - CPowerContainerObject* powerContainer = dynamic_cast(power); - if (powerContainer->IsRechargeable()) - { - float energy = powerContainer->GetEnergy(); - energy += rTime/2.0f; - if ( energy > 1.0f ) energy = 1.0f; - powerContainer->SetEnergy(energy); + CPowerContainerObject* powerContainer = dynamic_cast(held); + if (powerContainer->IsRechargeable()) + { + float energy = powerContainer->GetEnergy(); + energy += rTime/2.0f; + if ( energy > 1.0f ) energy = 1.0f; + powerContainer->SetEnergy(energy); + } } } } diff --git a/src/object/auto/autopowerplant.cpp b/src/object/auto/autopowerplant.cpp index d0006e2d..0aa76757 100644 --- a/src/object/auto/autopowerplant.cpp +++ b/src/object/auto/autopowerplant.cpp @@ -34,7 +34,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "sound/sound.h" @@ -57,7 +57,7 @@ CAutoPowerPlant::CAutoPowerPlant(COldObject* object) : CAuto(object) m_partiSphere = -1; Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -79,17 +79,18 @@ void CAutoPowerPlant::DeleteObject(bool all) if ( !all ) { + // TODO: why are we only searching for titanium and power cells? why don't we delete any object regardless of type? CObject* cargo = SearchMetal(); if ( cargo != nullptr ) { - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(cargo); } cargo = SearchPower(); if ( cargo != nullptr ) { - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(cargo); } } @@ -320,7 +321,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) cargo = SearchMetal(); if ( cargo != nullptr ) { - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(cargo); } @@ -333,7 +334,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) cargo->SetLock(false); // usable battery dynamic_cast(*cargo).SetTransporter(m_object); cargo->SetPosition(Math::Vector(0.0f, 3.0f, 0.0f)); - m_object->SetPower(cargo); + m_object->SetSlotContainedObject(0, cargo); m_main->DisplayError(INFO_ENERGY, m_object); } @@ -387,7 +388,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) CObject* CAutoPowerPlant::SearchMetal() { - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if ( obj == nullptr ) return nullptr; ObjectType type = obj->GetType(); @@ -512,7 +513,7 @@ Error CAutoPowerPlant::GetError() if ( m_object->GetEnergy() < POWERPLANT_POWER ) return ERR_ENERGY_LOW; - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if (obj == nullptr) return ERR_ENERGY_EMPTY; ObjectType type = obj->GetType(); if ( type == OBJECT_POWER ) return ERR_OK; diff --git a/src/object/auto/autopowerstation.cpp b/src/object/auto/autopowerstation.cpp index 96bed578..ac32fcdd 100644 --- a/src/object/auto/autopowerstation.cpp +++ b/src/object/auto/autopowerstation.cpp @@ -28,9 +28,6 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" - #include "sound/sound.h" #include "ui/controls/gauge.h" @@ -134,32 +131,13 @@ bool CAutoPowerStation::EventProcess(const Event &event) if (big > 0.0f) { CObject* vehicle = SearchVehicle(); - if (vehicle != nullptr) + if (vehicle != nullptr && vehicle->Implements(ObjectInterfaceType::Slotted)) { - if (vehicle->Implements(ObjectInterfaceType::Powered)) + CSlottedObject* slotted = dynamic_cast(vehicle); + for (int slot = slotted->GetNumSlots(); slot >= 0; slot--) { - CObject* power = dynamic_cast(*vehicle).GetPower(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) - { - CPowerContainerObject* powerContainer = dynamic_cast(power); - if (powerContainer->IsRechargeable()) - { - float energy = powerContainer->GetEnergy(); - float add = event.rTime*0.2f; - if ( add > big*4.0f ) add = big*4.0f; - if ( add > 1.0f-energy ) add = 1.0f-energy; - energy += add; // Charging the battery - powerContainer->SetEnergy(energy); - if ( energy < freq ) freq = energy; - big -= add/4.0f; // discharge the large battery - } - } - } - - if (vehicle->Implements(ObjectInterfaceType::Carrier)) - { - CObject* power = dynamic_cast(*vehicle).GetCargo(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) + CObject *power = slotted->GetSlotContainedObject(slot); + if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) { CPowerContainerObject* powerContainer = dynamic_cast(power); if (powerContainer->IsRechargeable()) diff --git a/src/object/auto/autoresearch.cpp b/src/object/auto/autoresearch.cpp index 83d39c66..1a2904db 100644 --- a/src/object/auto/autoresearch.cpp +++ b/src/object/auto/autoresearch.cpp @@ -32,7 +32,7 @@ #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "sound/sound.h" @@ -57,7 +57,7 @@ CAutoResearch::CAutoResearch(COldObject* object) : CAuto(object) Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -114,11 +114,11 @@ Error CAutoResearch::StartAction(int param) return ERR_RESEARCH_ALREADY; } - if (m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer)) + if (m_object->GetSlotContainedObject(0) == nullptr || !m_object->GetSlotContainedObject(0)->Implements(ObjectInterfaceType::PowerContainer)) { return ERR_RESEARCH_POWER; } - CPowerContainerObject* power = dynamic_cast(m_object->GetPower()); + CPowerContainerObject* power = dynamic_cast(m_object->GetSlotContainedObject(0)); if ( power->GetCapacity() > 1.0f ) { return ERR_RESEARCH_TYPE; @@ -222,7 +222,9 @@ bool CAutoResearch::EventProcess(const Event &event) FireStopUpdate(m_progress, true); // flashes if ( m_progress < 1.0f ) { - if ( m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) // more battery? + CObject* batteryObj = dynamic_cast(*m_object).GetSlotContainedObject(0); + + if ( batteryObj == nullptr || !batteryObj->Implements(ObjectInterfaceType::PowerContainer) ) // more battery? { SetBusy(false); UpdateInterface(); @@ -232,7 +234,7 @@ bool CAutoResearch::EventProcess(const Event &event) m_speed = 1.0f/1.0f; return true; } - power = dynamic_cast(m_object->GetPower()); + power = dynamic_cast(batteryObj); power->SetEnergyLevel(1.0f-m_progress); if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time ) @@ -302,11 +304,11 @@ Error CAutoResearch::GetError() return ERR_BAT_VIRUS; } - if (m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer)) + if (m_object->GetSlotContainedObject(0) == nullptr || !m_object->GetSlotContainedObject(0)->Implements(ObjectInterfaceType::PowerContainer)) { return ERR_RESEARCH_POWER; } - CPowerContainerObject* power = dynamic_cast(m_object->GetPower()); + CPowerContainerObject* power = dynamic_cast(m_object->GetSlotContainedObject(0)); if ( power->GetCapacity() > 1.0f ) { return ERR_RESEARCH_TYPE; diff --git a/src/object/auto/autotower.cpp b/src/object/auto/autotower.cpp index b509e7e9..7790fc6b 100644 --- a/src/object/auto/autotower.cpp +++ b/src/object/auto/autotower.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "physics/physics.h" @@ -61,7 +61,7 @@ CAutoTower::CAutoTower(COldObject* object) : CAuto(object) m_time = 0.0f; m_lastUpdateTime = 0.0f; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -125,13 +125,8 @@ bool CAutoTower::EventProcess(const Event &event) return true; } - CPowerContainerObject* power = nullptr; - float energy = 0.0f; - if ( m_object->GetPower() != nullptr && m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) - { - power = dynamic_cast(m_object->GetPower()); - energy = power->GetEnergy(); - } + CPowerContainerObject* power = GetObjectPowerCell(m_object); + float energy = power == nullptr ? 0.0f : power->GetEnergy(); UpdateInterface(event.rTime); @@ -321,12 +316,13 @@ Error CAutoTower::GetError() return ERR_BAT_VIRUS; } - if ( m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) + CPowerContainerObject *power = GetObjectPowerCell(m_object); + if ( power == nullptr ) { return ERR_TOWER_POWER; // no battery } - if ( dynamic_cast(*m_object->GetPower()).GetEnergy() < ENERGY_FIRE ) + if ( power->GetEnergy() < ENERGY_FIRE ) { return ERR_TOWER_ENERGY; // not enough energy } diff --git a/src/object/interface/carrier_object.h b/src/object/interface/carrier_object.h deleted file mode 100644 index 2cbf3d73..00000000 --- a/src/object/interface/carrier_object.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, 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 - */ - -#pragma once - -#include "object/object.h" -#include "object/object_interface_type.h" - -/** - * \class CCarrierObject - * \brief Interface for carrier objects - */ -class CCarrierObject -{ -public: - explicit CCarrierObject(ObjectInterfaceTypes& types) - { - types[static_cast(ObjectInterfaceType::Carrier)] = true; - } - virtual ~CCarrierObject() - {} - - //! Returns carried object - virtual CObject* GetCargo() = 0; - //! Sets carried object - virtual void SetCargo(CObject* cargo) = 0; - - //! Checks whether there is any cargo - inline bool IsCarryingCargo() - { - return GetCargo() != nullptr; - } -}; - -inline bool IsObjectCarryingCargo(CObject* obj) -{ - return obj->Implements(ObjectInterfaceType::Carrier) && - dynamic_cast(*obj).IsCarryingCargo(); -} diff --git a/src/object/interface/powered_object.h b/src/object/interface/powered_object.h deleted file mode 100644 index 3aea3296..00000000 --- a/src/object/interface/powered_object.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2020, 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 - */ - -#pragma once - -#include "object/object.h" -#include "object/object_interface_type.h" - -#include "object/interface/power_container_object.h" - -class CObject; - -/** - * \class CPoweredObject - * \brief Interface for objects powered using power cells - * - * TODO: It currently includes objects that take other objects as input - * and convert them, for example PowerPlant. - * We should create a dedicated interface for such uses. - */ -class CPoweredObject -{ -public: - explicit CPoweredObject(ObjectInterfaceTypes& types) - { - types[static_cast(ObjectInterfaceType::Powered)] = true; - } - virtual ~CPoweredObject() - {} - - //! Returns the power cell - virtual CObject* GetPower() = 0; - //! Sets power cell - virtual void SetPower(CObject* power) = 0; - - //! Returns the relative position of power cell - virtual Math::Vector GetPowerPosition() = 0; - //! Sets the relative position of power cell - virtual void SetPowerPosition(const Math::Vector& powerPosition) = 0; -}; - -inline float GetObjectEnergy(CObject* object) -{ - float energy = 0.0f; - - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - { - energy = dynamic_cast(*power).GetEnergy(); - } - } - - return energy; -} - -inline float GetObjectEnergyLevel(CObject* object) -{ - float energy = 0.0f; - - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - { - energy = dynamic_cast(*power).GetEnergyLevel(); - } - } - - return energy; -} - -inline bool ObjectHasPowerCell(CObject* object) -{ - return object->Implements(ObjectInterfaceType::Powered) && - dynamic_cast(*object).GetPower() != nullptr; -} diff --git a/src/object/interface/slotted_object.h b/src/object/interface/slotted_object.h new file mode 100644 index 00000000..61f8c8ec --- /dev/null +++ b/src/object/interface/slotted_object.h @@ -0,0 +1,148 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 + */ + +#pragma once + +#include "math/vector.h" + +#include "object/object.h" +#include "object/object_interface_type.h" + +#include "object/interface/power_container_object.h" + +#include + +class CObject; + +/** + * \class CSlottedObject + * \brief Interface for objects that hold other objects + */ +class CSlottedObject +{ +public: + ///! Object-independent identifiers for certain special slots + enum class Pseudoslot + { + POWER, + CARRYING + }; + + explicit CSlottedObject(ObjectInterfaceTypes& types) + { + types[static_cast(ObjectInterfaceType::Slotted)] = true; + } + virtual ~CSlottedObject() + {} + + //! Given one of the PSEUDOSLOT enums, returns real slot number, or -1 if specified pseudoslot is not present in this object. + virtual int MapPseudoSlot(Pseudoslot pseudoslot) = 0; + + //! Get number of slots. Valid slot numbers are 0 up to GetNumSlots()-1. Using invalid slot numbers in the other functions will crash the game. + virtual int GetNumSlots() = 0; + //! Get relative position of a slot. + virtual Math::Vector GetSlotPosition(int slotNum) = 0; + //! Get relative angle (in radians) where robot should be positioned when inserting into a slot. + virtual float GetSlotAngle(int slotNum) = 0; + //! Get the maximum angular distance from the ideal angle (in radians) where robot should be positioned when inserting into a slot. + virtual float GetSlotAcceptanceAngle(int slotNum) = 0; + //! Get object contained in a slot. + virtual CObject *GetSlotContainedObject(int slotNum) = 0; + //! Set object contained in a slot. + virtual void SetSlotContainedObject(int slotNum, CObject *object) = 0; + + void SetSlotContainedObjectReq(Pseudoslot pseudoslot, CObject *object) + { + int slotNum = MapPseudoSlot(pseudoslot); + assert(slotNum >= 0); + SetSlotContainedObject(slotNum, object); + } + + CObject *GetSlotContainedObjectOpt(Pseudoslot pseudoslot) + { + int slotNum = MapPseudoSlot(pseudoslot); + return slotNum < 0 ? nullptr : GetSlotContainedObject(slotNum); + } + + CObject *GetSlotContainedObjectReq(Pseudoslot pseudoslot) + { + int slotNum = MapPseudoSlot(pseudoslot); + assert(slotNum >= 0); + return GetSlotContainedObject(slotNum); + } +}; + +inline bool HasPowerCellSlot(CObject *object) +{ + if (object->Implements(ObjectInterfaceType::Slotted)) + { + return dynamic_cast(*object).MapPseudoSlot(CSlottedObject::Pseudoslot::POWER) >= 0; + } + return false; +} + +inline CObject *GetObjectInPowerCellSlot(CObject *object) +{ + if (object->Implements(ObjectInterfaceType::Slotted)) + { + return dynamic_cast(*object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER); + } + return nullptr; +} + +inline CPowerContainerObject *GetObjectPowerCell(CObject *object) +{ + if (CObject *powerSlotObj = GetObjectInPowerCellSlot(object)) + { + if (powerSlotObj->Implements(ObjectInterfaceType::PowerContainer)) + { + return dynamic_cast(powerSlotObj); + } + } + return nullptr; +} + +inline float GetObjectEnergy(CObject* object) +{ + if (CPowerContainerObject* power = GetObjectPowerCell(object)) + return power->GetEnergy(); + return 0.0f; +} + +inline float GetObjectEnergyLevel(CObject* object) +{ + if (CPowerContainerObject* power = GetObjectPowerCell(object)) + return power->GetEnergyLevel(); + return 0.0f; +} + +inline bool ObjectHasPowerCell(CObject* object) +{ + // XXX: not GetObjectPowerCell? We count e.g. titanium cubes as power cells in this function? + return GetObjectInPowerCellSlot(object) != nullptr; +} + +inline bool IsObjectCarryingCargo(CObject* object) +{ + if (object->Implements(ObjectInterfaceType::Slotted)) + { + return dynamic_cast(*object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr; + } + return false; +} diff --git a/src/object/motion/motionhuman.cpp b/src/object/motion/motionhuman.cpp index aef056c6..b1f180b4 100644 --- a/src/object/motion/motionhuman.cpp +++ b/src/object/motion/motionhuman.cpp @@ -33,8 +33,6 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" - #include "physics/physics.h" #include "sound/sound.h" diff --git a/src/object/motion/motionvehicle.cpp b/src/object/motion/motionvehicle.cpp index b9719c34..fe4c04bb 100644 --- a/src/object/motion/motionvehicle.cpp +++ b/src/object/motion/motionvehicle.cpp @@ -33,8 +33,8 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" #include "object/interface/programmable_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" @@ -1061,7 +1061,8 @@ void CMotionVehicle::Create(Math::Vector pos, float angle, ObjectType type, type != OBJECT_APOLLO2) { CObject* powerCell = nullptr; - Math::Vector powerCellPos = m_object->GetPowerPosition(); + int powerSlotIndex = m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + Math::Vector powerCellPos = m_object->GetSlotPosition(powerSlotIndex); float powerCellAngle = 0.0f; if (power <= 1.0f) { @@ -1076,8 +1077,7 @@ void CMotionVehicle::Create(Math::Vector pos, float angle, ObjectType type, powerCell->SetPosition(powerCellPos); powerCell->SetRotation(Math::Vector(0.0f, powerCellAngle, 0.0f)); dynamic_cast(*powerCell).SetTransporter(m_object); - assert(m_object->Implements(ObjectInterfaceType::Powered)); - m_object->SetPower(powerCell); + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, powerCell); } pos = m_object->GetPosition(); diff --git a/src/object/object_interface_type.h b/src/object/object_interface_type.h index fdd90233..610a8905 100644 --- a/src/object/object_interface_type.h +++ b/src/object/object_interface_type.h @@ -39,8 +39,6 @@ enum class ObjectInterfaceType Programmable, //!< objects that can be programmed in CBOT TaskExecutor, //!< objects that can execute tasks (CTask classes) Jostleable, //!< object that can be jostled - Carrier, //!< object that can carry other objects - Powered, //!< object powered with power cell Movable, //!< objects that can move Flying, //!< objects that can fly JetFlying, //!< objects that can fly using a jet engine @@ -54,6 +52,7 @@ enum class ObjectInterfaceType Shielded, //!< objects that can be destroyed after the shield goes down to 0 ShieldedAutoRegen, //!< shielded objects with auto shield regeneration Old, //!< old objects, TODO: remove once no longer necessary + Slotted, //!< objects that can carry other objects (in their gripper, power cell slot, or other slots) Max //!< maximum value (for getting number of items in enum) }; diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index 7dedda8a..a2e418e7 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -81,8 +81,7 @@ COldObject::COldObject(int id) CProgramStorageObjectImpl(m_implementedInterfaces, this), CProgrammableObjectImpl(m_implementedInterfaces, this), CJostleableObject(m_implementedInterfaces), - CCarrierObject(m_implementedInterfaces), - CPoweredObject(m_implementedInterfaces), + CSlottedObject(m_implementedInterfaces), CJetFlyingObject(m_implementedInterfaces), CControllableObject(m_implementedInterfaces), CPowerContainerObjectImpl(m_implementedInterfaces), @@ -145,6 +144,8 @@ COldObject::COldObject(int id) m_gunGoalH = 0.0f; m_shieldRadius = 0.0f; m_magnifyDamage = 1.0f; + m_hasPowerSlot = false; + m_hasCargoSlot = false; m_character = Character(); m_character.wheelFront = 1.0f; @@ -774,17 +775,34 @@ void COldObject::SetType(ObjectType type) m_type == OBJECT_MOBILEst || m_type == OBJECT_TOWER || m_type == OBJECT_RESEARCH || - m_type == OBJECT_ENERGY || - m_type == OBJECT_LABO || - m_type == OBJECT_NUCLEAR ) + m_type == OBJECT_ENERGY || // TODO not actually a power cell slot + m_type == OBJECT_LABO || // TODO not actually a power cell slot + m_type == OBJECT_NUCLEAR ) // TODO not actually a power cell slot { - m_implementedInterfaces[static_cast(ObjectInterfaceType::Powered)] = true; + m_hasPowerSlot = true; } else { - m_implementedInterfaces[static_cast(ObjectInterfaceType::Powered)] = false; + m_hasPowerSlot = false; } + if ( m_type == OBJECT_HUMAN || + m_type == OBJECT_TECH || + m_type == OBJECT_MOBILEfa || // Grabbers + m_type == OBJECT_MOBILEta || + m_type == OBJECT_MOBILEwa || + m_type == OBJECT_MOBILEia || + m_type == OBJECT_MOBILEsa) // subber + { + m_hasCargoSlot = true; + } + else + { + m_hasCargoSlot = false; + } + + m_implementedInterfaces[static_cast(ObjectInterfaceType::Slotted)] = (m_hasPowerSlot || m_hasCargoSlot); + // TODO: Hacking some more if ( m_type == OBJECT_MOBILEtg || m_type == OBJECT_STONE || @@ -1662,39 +1680,135 @@ void COldObject::SetMasterParticle(int part, int parti) // Management of the stack transport. -void COldObject::SetPower(CObject* power) -{ - m_power = power; -} -CObject* COldObject::GetPower() +int COldObject::GetNumSlots() { - return m_power; + assert(m_hasPowerSlot || m_hasCargoSlot); // otherwise implemented[CSlottedObject] is false + return (m_hasPowerSlot ? 1 : 0) + (m_hasCargoSlot ? 1 : 0); } +int COldObject::MapPseudoSlot(Pseudoslot pseudoslot) +{ + switch (pseudoslot) + { + case Pseudoslot::POWER: + return m_hasPowerSlot ? 0 : -1; + case Pseudoslot::CARRYING: + return m_hasCargoSlot ? (m_hasPowerSlot ? 1 : 0) : -1; + default: + return -1; + } +} +Math::Vector COldObject::GetSlotPosition(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + return m_powerPosition; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + int grabPartNum; + Math::Vector grabRelPos; + // See CTaskManip::TransporterTakeObject call to SetTransporterPart and SetPosition + switch (m_type) + { + case OBJECT_HUMAN: + case OBJECT_TECH: + grabPartNum = 4; + grabRelPos = Math::Vector(1.7f, -0.5f, 1.1f); + break; + case OBJECT_MOBILEsa: // subber + grabPartNum = 2; + grabRelPos = Math::Vector(1.1f, -1.0f, 1.0f); + break; + case OBJECT_MOBILEfa: // Grabbers + case OBJECT_MOBILEta: + case OBJECT_MOBILEwa: + case OBJECT_MOBILEia: + grabPartNum = 3; + grabRelPos = Math::Vector(4.7f, 0.0f, 0.0f); + break; + default: // unreachable, only the above objects have cargo slots + assert(!m_hasCargoSlot); + return m_powerPosition; + } + return Math::Transform(GetWorldMatrix(0)->Inverse(), Math::Transform(*GetWorldMatrix(grabPartNum), grabRelPos)); + } +} +float COldObject::GetSlotAngle(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + { + switch (m_type) + { + case OBJECT_TOWER: + case OBJECT_RESEARCH: + case OBJECT_ENERGY: + case OBJECT_LABO: + case OBJECT_NUCLEAR: + return 0; + default: // robots + return Math::PI; + } + } + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + return 0; + } +} +float COldObject::GetSlotAcceptanceAngle(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + { + switch (m_type) + { + case OBJECT_TOWER: + case OBJECT_RESEARCH: + return 45.0f*Math::PI/180.0f; + case OBJECT_ENERGY: + return 90.0f*Math::PI/180.0f; + case OBJECT_LABO: + return 120.0f*Math::PI/180.0f; + case OBJECT_NUCLEAR: + return 45.0f*Math::PI/180.0f; + default: + return 45.0f*Math::PI/180.0f; + } + } + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + return 0; // no acceptance angle for cargo slot + } +} +CObject *COldObject::GetSlotContainedObject(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + return m_power; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + return m_cargo; + } +} +void COldObject::SetSlotContainedObject(int slotNum, CObject *object) +{ + if (slotNum == 0 && m_hasPowerSlot) + m_power = object; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + m_cargo = object; + } +} +// not part of CSlottedObject; just used for initialization void COldObject::SetPowerPosition(const Math::Vector& powerPosition) { m_powerPosition = powerPosition; } -Math::Vector COldObject::GetPowerPosition() -{ - return m_powerPosition; -} -// Management of the object transport. - -void COldObject::SetCargo(CObject* cargo) -{ - m_cargo = cargo; -} - -CObject* COldObject::GetCargo() -{ - return m_cargo; -} - // Management of the object "transporter" that transports it. void COldObject::SetTransporter(CObject* transporter) diff --git a/src/object/old_object.h b/src/object/old_object.h index cbb25684..2c71cfb3 100644 --- a/src/object/old_object.h +++ b/src/object/old_object.h @@ -33,7 +33,6 @@ #include "object/implementation/programmable_impl.h" #include "object/implementation/task_executor_impl.h" -#include "object/interface/carrier_object.h" #include "object/interface/controllable_object.h" #include "object/interface/flying_object.h" #include "object/interface/interactive_object.h" @@ -41,10 +40,10 @@ #include "object/interface/jostleable_object.h" #include "object/interface/movable_object.h" #include "object/interface/power_container_object.h" -#include "object/interface/powered_object.h" #include "object/interface/programmable_object.h" #include "object/interface/ranged_object.h" #include "object/interface/shielded_auto_regen_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/task_executor_object.h" #include "object/interface/trace_drawing_object.h" #include "object/interface/transportable_object.h" @@ -83,8 +82,7 @@ class COldObject : public CObject, public CProgramStorageObjectImpl, public CProgrammableObjectImpl, public CJostleableObject, - public CCarrierObject, - public CPoweredObject, + public CSlottedObject, public CJetFlyingObject, public CControllableObject, public CPowerContainerObjectImpl, @@ -175,12 +173,6 @@ public: void SetMasterParticle(int part, int parti) override; - void SetPower(CObject* power) override; - CObject* GetPower() override; - Math::Vector GetPowerPosition() override; - void SetPowerPosition(const Math::Vector& powerPosition) override; - void SetCargo(CObject* cargo) override; - CObject* GetCargo() override; void SetTransporter(CObject* transporter) override; CObject* GetTransporter() override; void SetTransporterPart(int part) override; @@ -295,6 +287,17 @@ public: void SetBulletWall(bool bulletWall); bool IsBulletWall() override; + // CSlottedObject + int MapPseudoSlot(Pseudoslot pseudoslot) override; + int GetNumSlots() override; + Math::Vector GetSlotPosition(int slotNum) override; + float GetSlotAngle(int slotNum) override; + float GetSlotAcceptanceAngle(int slotNum) override; + CObject *GetSlotContainedObject(int slotNum) override; + void SetSlotContainedObject(int slotNum, CObject *object) override; + // Helper for CSlottedObject initialization + void SetPowerPosition(const Math::Vector& powerPosition); + protected: bool EventFrame(const Event &event); void VirusFrame(float rTime); @@ -393,4 +396,7 @@ protected: float m_traceWidth; bool m_bulletWall = false; + + bool m_hasCargoSlot; + bool m_hasPowerSlot; }; diff --git a/src/object/task/taskbuild.cpp b/src/object/task/taskbuild.cpp index b1ee0037..2f6afb46 100644 --- a/src/object/task/taskbuild.cpp +++ b/src/object/task/taskbuild.cpp @@ -36,7 +36,6 @@ #include "object/auto/auto.h" -#include "object/interface/carrier_object.h" #include "object/interface/transportable_object.h" #include "object/motion/motionhuman.h" diff --git a/src/object/task/taskfire.cpp b/src/object/task/taskfire.cpp index 87730880..6b3a268b 100644 --- a/src/object/task/taskfire.cpp +++ b/src/object/task/taskfire.cpp @@ -26,6 +26,8 @@ #include "object/old_object.h" +#include "object/interface/slotted_object.h" + #include "physics/physics.h" #include "sound/sound.h" @@ -42,7 +44,7 @@ CTaskFire::CTaskFire(COldObject* object) : CForegroundTask(object) { m_soundChannel = -1; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(HasPowerCellSlot(m_object)); } // Object's destructor. @@ -78,10 +80,8 @@ bool CTaskFire::EventProcess(const Event &event) m_lastSound -= event.rTime; m_progress += event.rTime*m_speed; - CPowerContainerObject* power = nullptr; - if (m_object->GetPower() != nullptr && m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer)) + if (CPowerContainerObject* power = GetObjectPowerCell(m_object)) { - power = dynamic_cast(m_object->GetPower()); energy = power->GetEnergy(); if ( m_bOrganic ) fire = ENERGY_FIREi; else if ( m_bRay ) fire = ENERGY_FIREr; @@ -313,11 +313,11 @@ Error CTaskFire::Start(float delay) } m_delay = delay; - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* power = dynamic_cast(m_object)->GetPower(); - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_FIRE_ENERGY; + assert(HasPowerCellSlot(m_object)); + CPowerContainerObject *power = GetObjectPowerCell(m_object); + if (power == nullptr) return ERR_FIRE_ENERGY; - energy = dynamic_cast(*power).GetEnergy(); + energy = power->GetEnergy(); if ( m_bOrganic ) fire = m_delay*ENERGY_FIREi; else if ( m_bRay ) fire = m_delay*ENERGY_FIREr; else fire = m_delay*ENERGY_FIRE; diff --git a/src/object/task/taskflag.cpp b/src/object/task/taskflag.cpp index b2872159..b884eb07 100644 --- a/src/object/task/taskflag.cpp +++ b/src/object/task/taskflag.cpp @@ -28,8 +28,6 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" - #include "object/motion/motionhuman.h" #include "physics/physics.h" diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index d2e64667..fe1402ea 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -33,6 +33,7 @@ #include "object/object_manager.h" #include "object/old_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "object/subclass/base_alien.h" @@ -1201,8 +1202,11 @@ bool CTaskGoto::AdjustTarget(CObject* pObj, Math::Vector &pos, float &distance) type == OBJECT_MOBILEst || type == OBJECT_MOBILEdr ) { - assert(pObj->Implements(ObjectInterfaceType::Powered)); - pos = dynamic_cast(*pObj).GetPowerPosition(); + CSlottedObject *asSlotted = dynamic_cast(pObj); + int powerSlotIndex = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + assert(powerSlotIndex >= 0); + pos = asSlotted->GetSlotPosition(powerSlotIndex); + // TODO: this only works for a certain slot angle pos.x -= TAKE_DIST+TAKE_DIST_OTHER+distance; mat = pObj->GetWorldMatrix(0); pos = Transform(*mat, pos); diff --git a/src/object/task/taskmanip.cpp b/src/object/task/taskmanip.cpp index aa62ef9e..43d3fc1c 100644 --- a/src/object/task/taskmanip.cpp +++ b/src/object/task/taskmanip.cpp @@ -30,14 +30,14 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" #include "sound/sound.h" +const int INVALID_SLOT = -1; //?const float MARGIN_FRONT = 2.0f; //?const float MARGIN_BACK = 2.0f; @@ -58,7 +58,7 @@ CTaskManip::CTaskManip(COldObject* object) : CForegroundTask(object) m_arm = TMA_NEUTRAL; m_hand = TMH_OPEN; - assert(m_object->Implements(ObjectInterfaceType::Carrier)); + assert(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0); } // Object's destructor. @@ -295,7 +295,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) type = m_object->GetType(); if ( type == OBJECT_BEE ) // bee? { - if (m_object->GetCargo() == nullptr) + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { if ( !m_physics->GetLand() ) return ERR_MANIP_FLY; @@ -303,17 +303,17 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if (other == nullptr) return ERR_MANIP_NIL; assert(other->Implements(ObjectInterfaceType::Transportable)); - m_object->SetCargo(other); // takes the ball + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, other); // takes the ball dynamic_cast(*other).SetTransporter(m_object); dynamic_cast(*other).SetTransporterPart(0); // taken with the base other->SetPosition(Math::Vector(0.0f, -3.0f, 0.0f)); } else { - other = m_object->GetCargo(); // other = ball + other = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); // other = ball assert(other->Implements(ObjectInterfaceType::Transportable)); - m_object->SetCargo(nullptr); // lick the ball + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // lick the ball dynamic_cast(*other).SetTransporter(nullptr); pos = m_object->GetPosition(); pos.y -= 3.0f; @@ -361,7 +361,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if ( order == TMO_AUTO ) { - if (m_object->GetCargo() == nullptr) + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { m_order = TMO_GRAB; } @@ -375,11 +375,11 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) m_order = order; } - if (m_order == TMO_GRAB && m_object->GetCargo() != nullptr) + if (m_order == TMO_GRAB && m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) != nullptr) { return ERR_MANIP_BUSY; } - if (m_order == TMO_DROP && m_object->GetCargo() == nullptr) + if (m_order == TMO_DROP && m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { return ERR_MANIP_EMPTY; } @@ -393,7 +393,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if ( m_arm == TMA_FFRONT ) { front = SearchTakeFrontObject(true, fPos, fDist, fAngle); - other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight); + int slotNum; + other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight, slotNum); if ( front != nullptr && fDist < oDist ) { @@ -403,7 +404,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) } else if ( other != nullptr && oDist < fDist ) { - if (! ObjectHasPowerCell(other)) return ERR_MANIP_NIL; + if (dynamic_cast(*other).GetSlotContainedObject(slotNum) == nullptr) return ERR_MANIP_NIL; m_targetPos = oPos; m_angle = oAngle; m_height = oHeight; @@ -427,8 +428,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) } if ( m_arm == TMA_POWER ) { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - if (m_object->GetPower() == nullptr) return ERR_MANIP_NIL; + assert(HasPowerCellSlot(m_object)); + if (m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER) == nullptr) return ERR_MANIP_NIL; } } @@ -436,8 +437,9 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) { if ( m_arm == TMA_FFRONT ) { - other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight); - if (other != nullptr && !ObjectHasPowerCell(other)) + int slotNum; + other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight, slotNum); + if (other != nullptr && dynamic_cast(*other).GetSlotContainedObject(slotNum) == nullptr) { m_targetPos = oPos; m_angle = oAngle; @@ -456,8 +458,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) } if ( m_arm == TMA_POWER ) { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - if (m_object->GetPower() != nullptr) return ERR_MANIP_OCC; + assert(HasPowerCellSlot(m_object)); + if (m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER) != nullptr) return ERR_MANIP_OCC; } } @@ -477,7 +479,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if ( m_timeLimit < 0.5f ) m_timeLimit = 0.5f; } - if (m_object->GetCargo() == nullptr) // not carrying anything? + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) // not carrying anything? { m_hand = TMH_OPEN; // open clamp } @@ -602,7 +604,7 @@ Error CTaskManip::IsEnded() { if ( m_bSubm ) m_speed = 1.0f/1.5f; if ( !TransporterTakeObject() && - m_object->GetCargo() == nullptr) + m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { m_hand = TMH_OPEN; // reopens the clamp m_arm = TMA_NEUTRAL; @@ -613,7 +615,7 @@ Error CTaskManip::IsEnded() { if ( (m_arm == TMA_OTHER || m_arm == TMA_POWER ) && - m_object->GetCargo()->Implements(ObjectInterfaceType::PowerContainer) ) + m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING)->Implements(ObjectInterfaceType::PowerContainer) ) { m_sound->Play(SOUND_POWEROFF, m_object->GetPosition()); } @@ -630,7 +632,7 @@ Error CTaskManip::IsEnded() if ( m_step == 1 ) { if ( m_bSubm ) m_speed = 1.0f/0.7f; - cargo = m_object->GetCargo(); + cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (TransporterDeposeObject()) { if ( (m_arm == TMA_OTHER || @@ -667,7 +669,7 @@ Error CTaskManip::IsEnded() bool CTaskManip::Abort() { - if (m_object->GetCargo() == nullptr) // not carrying anything? + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) // not carrying anything? { m_hand = TMH_OPEN; // open clamp m_arm = TMA_NEUTRAL; @@ -868,10 +870,12 @@ CObject* CTaskManip::SearchTakeBackObject(bool bAdvance, Math::Vector &pos, CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, - float &height) + float &height, int &slotNumOut) { + slotNumOut = INVALID_SLOT; + Math::Matrix* mat; - float iAngle, oAngle, oLimit, aLimit, dLimit; + float iAngle, aLimit, dLimit; distance = 1000000.0f; angle = 0.0f; @@ -900,56 +904,45 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, { if ( pObj == m_object ) continue; // yourself? - ObjectType type = pObj->GetType(); - if ( !pObj->Implements(ObjectInterfaceType::Powered) ) continue; - - CObject* power = dynamic_cast(*pObj).GetPower(); - if (power != nullptr) + if (pObj->Implements(ObjectInterfaceType::Slotted)) { - if (power->GetLock()) continue; - if (power->GetScaleY() != 1.0f) continue; - } - - mat = pObj->GetWorldMatrix(0); - Math::Vector oPos = Transform(*mat, dynamic_cast(*pObj).GetPowerPosition()); - - oAngle = pObj->GetRotationY(); - if ( type == OBJECT_TOWER || - type == OBJECT_RESEARCH ) - { - oLimit = 45.0f*Math::PI/180.0f; - } - else if ( type == OBJECT_ENERGY ) - { - oLimit = 90.0f*Math::PI/180.0f; - } - else if ( type == OBJECT_LABO ) - { - oLimit = 120.0f*Math::PI/180.0f; - } - else if ( type == OBJECT_NUCLEAR ) - { - oLimit = 45.0f*Math::PI/180.0f; - } - else - { - oLimit = 45.0f*Math::PI/180.0f; - oAngle += Math::PI; // is behind - } - oAngle = Math::NormAngle(oAngle); // 0..2*Math::PI - angle = Math::RotateAngle(iPos.x-oPos.x, oPos.z-iPos.z); // CW ! - if ( !Math::TestAngle(angle, oAngle-oLimit, oAngle+oLimit) ) continue; - - distance = fabs(Math::Distance(oPos, iPos)-TAKE_DIST); - if ( distance <= dLimit ) - { - angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! - if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + CSlottedObject &obj = dynamic_cast(*pObj); + int slotNum = obj.GetNumSlots(); + for (int slot = 0; slot < slotNum; slot++) { - Math::Vector powerPos = dynamic_cast(*pObj).GetPowerPosition(); - height = powerPos.y; - pos = oPos; - return pObj; + mat = pObj->GetWorldMatrix(0); + Math::Vector worldSlotPos = Transform(*mat, obj.GetSlotPosition(slot)); + + CObject *objectInSlot = obj.GetSlotContainedObject(slot); + if (objectInSlot != nullptr && (objectInSlot->GetLock() || objectInSlot->GetScaleY() != 1.0f)) + continue; + + float objectAngleOffsetLimit = obj.GetSlotAcceptanceAngle(slot); + if(objectAngleOffsetLimit == 0) + continue; // slot isn't take-able + + // The robot must be in the correct angle relative to the slot (it can't be on the other side of the object) + float angleFromObjectToRobot = Math::RotateAngle(iPos.x-worldSlotPos.x, worldSlotPos.z-iPos.z); // CW ! + float objectIdealAngle = Math::NormAngle(pObj->GetRotationY() + obj.GetSlotAngle(slot)); + + if ( Math::TestAngle(angleFromObjectToRobot, objectIdealAngle - objectAngleOffsetLimit, objectIdealAngle + objectAngleOffsetLimit) ) + { + distance = fabs(Math::Distance(worldSlotPos, iPos)-TAKE_DIST); + // The robot must be close enough to the slot + if ( distance <= dLimit ) + { + // The slot must be in the correct position relative to the robot (the robot must be facing towards the slot, not sideways or away) + angle = Math::RotateAngle(worldSlotPos.x-iPos.x, iPos.z-worldSlotPos.z); // CW ! + if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + Math::Vector powerPos = obj.GetSlotPosition(slot); + height = powerPos.y; + pos = worldSlotPos; + slotNumOut = slot; + return pObj; + } + } + } } } } @@ -965,7 +958,7 @@ bool CTaskManip::TransporterTakeObject() { if (m_arm == TMA_GRAB) // takes immediately? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing to take? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1005,7 +998,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationY(0.0f); } - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_FFRONT) // takes on the ground in front? @@ -1041,7 +1034,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationY(0.0f); } - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_FBACK) // takes on the ground behind? @@ -1063,13 +1056,13 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationY(0.0f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_POWER) // takes battery in the back? { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = m_object->GetPower(); + assert(m_object->Implements(ObjectInterfaceType::Slotted)); + CObject* cargo = m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER); if (cargo == nullptr) return false; // no battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1082,25 +1075,25 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationY(0.0f); dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand - m_object->SetPower(nullptr); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, nullptr); + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_OTHER) // battery takes from friend? { Math::Vector pos; float dist = 0.0f, angle = 0.0f; - CObject* other = SearchOtherObject(false, pos, dist, angle, m_height); + int slotNum; + CObject* other = SearchOtherObject(false, pos, dist, angle, m_height, slotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); - - CObject* cargo = dynamic_cast(*other).GetPower(); + assert(slotNum != INVALID_SLOT); + CObject *cargo = dynamic_cast(*other).GetSlotContainedObject(slotNum); if (cargo == nullptr) return false; // the other does not have a battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(nullptr); + dynamic_cast(*other).SetSlotContainedObject(slotNum, nullptr); dynamic_cast(*cargo).SetTransporter(m_object); dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand @@ -1110,7 +1103,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationY(0.0f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } return true; @@ -1122,7 +1115,7 @@ bool CTaskManip::TransporterDeposeObject() { if (m_arm == TMA_FFRONT) // deposits on the ground in front? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing transported? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1138,12 +1131,12 @@ bool CTaskManip::TransporterDeposeObject() cargo->FloorAdjust(); // plate well on the ground dynamic_cast(*cargo).SetTransporter(nullptr); - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } if (m_arm == TMA_FBACK) // deposited on the ground behind? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing transported? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1158,30 +1151,32 @@ bool CTaskManip::TransporterDeposeObject() cargo->SetRotationZ(0.0f); dynamic_cast(*cargo).SetTransporter(nullptr); - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } if (m_arm == TMA_POWER) // deposits battery in the back? { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = m_object->GetCargo(); + assert(m_object->Implements(ObjectInterfaceType::Slotted)); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing transported? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - if (m_object->GetPower() != nullptr) return false; + int powerSlotIndex = m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + assert(powerSlotIndex >= 0); + if (m_object->GetSlotContainedObject(powerSlotIndex) != nullptr) return false; dynamic_cast(*cargo).SetTransporter(m_object); dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base - cargo->SetPosition(m_object->GetPowerPosition()); + cargo->SetPosition(m_object->GetSlotPosition(powerSlotIndex)); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); - m_object->SetPower(cargo); // uses - m_object->SetCargo(nullptr); + m_object->SetSlotContainedObject(powerSlotIndex, cargo); // uses + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); } if (m_arm == TMA_OTHER) // deposits battery on friend? @@ -1189,29 +1184,30 @@ bool CTaskManip::TransporterDeposeObject() Math::Vector pos; float angle = 0.0f, dist = 0.0f; - CObject* other = SearchOtherObject(false, pos, dist, angle, m_height); + int slotNum; + CObject* other = SearchOtherObject(false, pos, dist, angle, m_height, slotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); + assert(slotNum != INVALID_SLOT); + CSlottedObject *otherAsSlotted = dynamic_cast(other); + if (otherAsSlotted->GetSlotContainedObject(slotNum) != nullptr) return false; // the other already has a battery? - CObject* cargo = dynamic_cast(*other).GetPower(); - if (cargo != nullptr) return false; // the other already has a battery? - - cargo = m_object->GetCargo(); + CObject *cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(cargo); + otherAsSlotted->SetSlotContainedObject(slotNum, cargo); dynamic_cast(*cargo).SetTransporter(other); - cargo->SetPosition(dynamic_cast(*other).GetPowerPosition()); + // TODO: isn't this wrong? PowerPosition (and SlotContainedPosition) is an object-local position. + cargo->SetPosition(otherAsSlotted->GetSlotPosition(slotNum)); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } return true; diff --git a/src/object/task/taskmanip.h b/src/object/task/taskmanip.h index 165ff653..033be277 100644 --- a/src/object/task/taskmanip.h +++ b/src/object/task/taskmanip.h @@ -73,7 +73,7 @@ protected: CObject* SearchTakeUnderObject(Math::Vector &pos, float dLimit); CObject* SearchTakeFrontObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle); CObject* SearchTakeBackObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle); - CObject* SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, float &height); + CObject* SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, float &height, int &slotNumOut); bool TransporterTakeObject(); bool TransporterDeposeObject(); bool IsFreeDeposeObject(Math::Vector pos); diff --git a/src/object/task/taskrecover.cpp b/src/object/task/taskrecover.cpp index f56503d5..e111b317 100644 --- a/src/object/task/taskrecover.cpp +++ b/src/object/task/taskrecover.cpp @@ -31,7 +31,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "physics/physics.h" @@ -108,11 +108,9 @@ bool CTaskRecover::EventProcess(const Event &event) if ( m_phase == TRP_OPER ) { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* powerObj = dynamic_cast(m_object)->GetPower(); - if (powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) + assert(HasPowerCellSlot(m_object)); + if (CPowerContainerObject* power = GetObjectPowerCell(m_object)) { - CPowerContainerObject* power = dynamic_cast(powerObj); energy = power->GetEnergy(); energy -= event.rTime * ENERGY_RECOVER * m_speed; power->SetEnergy(energy); @@ -190,11 +188,10 @@ Error CTaskRecover::Start() ObjectType type = m_object->GetType(); if ( type != OBJECT_MOBILErr ) return ERR_WRONG_BOT; - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* power = dynamic_cast(m_object)->GetPower(); - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_RECOVER_ENERGY; + CPowerContainerObject *power = GetObjectPowerCell(m_object); + if (power == nullptr) return ERR_RECOVER_ENERGY; - float energy = dynamic_cast(*power).GetEnergy(); + float energy = power->GetEnergy(); if ( energy < ENERGY_RECOVER+0.05f ) return ERR_RECOVER_ENERGY; Math::Matrix* mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/taskshield.cpp b/src/object/task/taskshield.cpp index 916a5158..d17e748a 100644 --- a/src/object/task/taskshield.cpp +++ b/src/object/task/taskshield.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/subclass/shielder.h" @@ -54,7 +54,7 @@ CTaskShield::CTaskShield(COldObject* object) : CBackgroundTask(object) m_soundChannel = -1; m_effectLight = -1; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(HasPowerCellSlot(m_object)); m_shielder = dynamic_cast(object); } @@ -118,12 +118,8 @@ bool CTaskShield::EventProcess(const Event &event) { energy = (1.0f/ENERGY_TIME)*event.rTime; energy *= GetRadius()/RADIUS_SHIELD_MAX; - CObject* powerObj = dynamic_cast(m_object)->GetPower(); - if (powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) - { - CPowerContainerObject* power = dynamic_cast(powerObj); + if (CPowerContainerObject *power = GetObjectPowerCell(m_object)) power->SetEnergy(power->GetEnergy()-energy); - } m_energyUsed += energy; if ( m_soundChannel == -1 ) @@ -307,9 +303,9 @@ Error CTaskShield::Start(TaskShieldMode mode, float delay) m_bError = true; // operation impossible if ( !m_physics->GetLand() ) return ERR_WRONG_BOT; - CObject* power = m_object->GetPower(); - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_SHIELD_ENERGY; - float energy = dynamic_cast(*power).GetEnergy(); + CPowerContainerObject* power = GetObjectPowerCell(m_object); + if (power == nullptr) return ERR_SHIELD_ENERGY; + float energy = power->GetEnergy(); if ( energy == 0.0f ) return ERR_SHIELD_ENERGY; Math::Matrix* mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/tasktake.cpp b/src/object/task/tasktake.cpp index 6f7360bd..41dce816 100644 --- a/src/object/task/tasktake.cpp +++ b/src/object/task/tasktake.cpp @@ -30,8 +30,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "object/motion/motionhuman.h" @@ -49,7 +48,7 @@ CTaskTake::CTaskTake(COldObject* object) : CForegroundTask(object) { m_arm = TTA_NEUTRAL; - assert(m_object->Implements(ObjectInterfaceType::Carrier)); + assert(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0); } // Object's destructor. @@ -116,7 +115,7 @@ Error CTaskTake::Start() m_physics->SetMotorSpeed(Math::Vector(0.0f, 0.0f, 0.0f)); - if (m_object->IsCarryingCargo()) + if (m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr) m_order = TTO_DEPOSE; else m_order = TTO_TAKE; @@ -128,12 +127,14 @@ Error CTaskTake::Start() float h = m_water->GetLevel(m_object); if ( pos.y < h ) return ERR_MANIP_WATER; // impossible under water - CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); - if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f, otherSlotNum); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(other == nullptr || otherSlotNum >= 0); - if (other != nullptr && dynamic_cast(*other).GetPower() != nullptr) + if (other != nullptr && otherAsSlotted->GetSlotContainedObject(otherSlotNum) != nullptr) { - CObject* power = dynamic_cast(*other).GetPower(); + CObject* power = otherAsSlotted->GetSlotContainedObject(otherSlotNum); type = power->GetType(); if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO; assert(power->Implements(ObjectInterfaceType::Transportable)); @@ -158,10 +159,12 @@ Error CTaskTake::Start() //? if ( speed.x != 0.0f || //? speed.z != 0.0f ) return ERR_MANIP_MOTOR; - CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); - if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f, otherSlotNum); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(other == nullptr || otherSlotNum >= 0); - if (other != nullptr && dynamic_cast(*other).GetPower() == nullptr ) + if (other != nullptr && otherAsSlotted->GetSlotContainedObject(otherSlotNum) == nullptr) { //? m_camera->StartCentering(m_object, Math::PI*0.3f, -Math::PI*0.1f, 0.0f, 0.8f); m_arm = TTA_FRIEND; @@ -233,7 +236,7 @@ Error CTaskTake::IsEnded() if ( TransporterTakeObject() ) { if ( m_arm == TTA_FRIEND && - m_object->GetCargo()->Implements(ObjectInterfaceType::PowerContainer) ) + m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING)->Implements(ObjectInterfaceType::PowerContainer) ) { m_sound->Play(SOUND_POWEROFF, m_object->GetPosition()); } @@ -250,7 +253,7 @@ Error CTaskTake::IsEnded() { if ( m_step == 1 ) { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); TransporterDeposeObject(); if ( m_arm == TTA_FRIEND && cargo->Implements(ObjectInterfaceType::PowerContainer) ) @@ -334,7 +337,8 @@ CObject* CTaskTake::SearchTakeObject(float &angle, // Seeks the robot on which you want take or put a battery. CObject* CTaskTake::SearchFriendObject(float &angle, - float dLimit, float aLimit) + float dLimit, float aLimit, + int &slotNumOut) { if (m_object->GetCrashSphereCount() == 0) return nullptr; @@ -348,67 +352,44 @@ CObject* CTaskTake::SearchFriendObject(float &angle, for (CObject* pObj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if ( pObj == m_object ) continue; // yourself? + if (!pObj->Implements(ObjectInterfaceType::Slotted)) continue; - ObjectType type = pObj->GetType(); - if ( type != OBJECT_MOBILEfa && - type != OBJECT_MOBILEta && - type != OBJECT_MOBILEwa && - type != OBJECT_MOBILEia && - type != OBJECT_MOBILEfb && - type != OBJECT_MOBILEtb && - type != OBJECT_MOBILEwb && - type != OBJECT_MOBILEib && - type != OBJECT_MOBILEfc && - type != OBJECT_MOBILEtc && - type != OBJECT_MOBILEwc && - type != OBJECT_MOBILEic && - type != OBJECT_MOBILEfi && - type != OBJECT_MOBILEti && - type != OBJECT_MOBILEwi && - type != OBJECT_MOBILEii && - type != OBJECT_MOBILEfs && - type != OBJECT_MOBILEts && - type != OBJECT_MOBILEws && - type != OBJECT_MOBILEis && - type != OBJECT_MOBILErt && - type != OBJECT_MOBILErc && - type != OBJECT_MOBILErr && - type != OBJECT_MOBILErs && - type != OBJECT_MOBILEsa && - type != OBJECT_MOBILEtg && - type != OBJECT_MOBILEft && - type != OBJECT_MOBILEtt && - type != OBJECT_MOBILEwt && - type != OBJECT_MOBILEit && - type != OBJECT_MOBILErp && - type != OBJECT_MOBILEst && - type != OBJECT_TOWER && - type != OBJECT_RESEARCH && - type != OBJECT_ENERGY && - type != OBJECT_LABO && - type != OBJECT_NUCLEAR ) continue; + CSlottedObject *obj = dynamic_cast(pObj); - assert(pObj->Implements(ObjectInterfaceType::Powered)); - - CObject* power = dynamic_cast(*pObj).GetPower(); - if (power != nullptr) + int slotNum = obj->GetNumSlots(); + for (int slot = 0; slot < slotNum; slot++) { - if ( power->GetLock() ) continue; - if ( power->GetScaleY() != 1.0f ) continue; - } + CObject *objectInSlot = obj->GetSlotContainedObject(slot); + if (objectInSlot != nullptr && (objectInSlot->GetLock() || objectInSlot->GetScaleY() != 1.0f)) + continue; - Math::Matrix* mat = pObj->GetWorldMatrix(0); - Math::Vector oPos = Math::Transform(*mat, dynamic_cast(*pObj).GetPowerPosition()); + float objectAngleOffsetLimit = obj->GetSlotAcceptanceAngle(slot); + if (objectAngleOffsetLimit == 0) + continue; // slot isn't take-able - float distance = fabs(Math::Distance(oPos, iPos) - (iRad+1.0f)); - if ( distance <= dLimit ) - { - angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! - if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + Math::Matrix* mat = pObj->GetWorldMatrix(0); + Math::Vector worldSlotPos = Transform(*mat, obj->GetSlotPosition(slot)); + + // The robot must be in the correct angle relative to the slot (it can't be on the other side of the object) + float angleFromObjectToRobot = Math::RotateAngle(iPos.x-worldSlotPos.x, worldSlotPos.z-iPos.z); // CW ! + float objectIdealAngle = Math::NormAngle(pObj->GetRotationY() + obj->GetSlotAngle(slot)); + + if ( Math::TestAngle(angleFromObjectToRobot, objectIdealAngle - objectAngleOffsetLimit, objectIdealAngle + objectAngleOffsetLimit) ) { - Math::Vector powerPos = dynamic_cast(*pObj).GetPowerPosition(); - m_height = powerPos.y; - return pObj; + float distance = fabs(Math::Distance(worldSlotPos, iPos)-(iRad + 1.0f)); + // The robot must be close enough to the slot + if ( distance <= dLimit ) + { + // The slot must be in the correct position relative to the robot (the robot must be facing towards the slot, not sideways or away) + angle = Math::RotateAngle(worldSlotPos.x-iPos.x, iPos.z-worldSlotPos.z); // CW ! + if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + Math::Vector powerPos = obj->GetSlotPosition(slot); + m_height = powerPos.y; + slotNumOut = slot; + return pObj; + } + } } } } @@ -439,23 +420,25 @@ bool CTaskTake::TransporterTakeObject() cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.8f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TTA_FRIEND) // takes friend's battery? { float angle = 0.0f; - CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f, otherSlotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(otherSlotNum >= -1); - CObject* cargo = dynamic_cast(*other).GetPower(); + CObject* cargo = otherAsSlotted->GetSlotContainedObject(otherSlotNum); if (cargo == nullptr) return false; // the other does not have a battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(nullptr); + otherAsSlotted->SetSlotContainedObject(otherSlotNum, nullptr); dynamic_cast(*cargo).SetTransporter(m_object); dynamic_cast(*cargo).SetTransporterPart(4); // takes with the hand @@ -465,7 +448,7 @@ bool CTaskTake::TransporterTakeObject() cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.8f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } return true; @@ -477,7 +460,7 @@ bool CTaskTake::TransporterDeposeObject() { if ( m_arm == TTA_FFRONT ) // deposes on the ground in front? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // does nothing? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -493,34 +476,36 @@ bool CTaskTake::TransporterDeposeObject() cargo->FloorAdjust(); // plate well on the ground dynamic_cast(*cargo).SetTransporter(nullptr); - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } if ( m_arm == TTA_FRIEND ) // deposes battery on friends? { float angle = 0.0f; - CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f, otherSlotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(otherSlotNum >= 0); - CObject* cargo = dynamic_cast(*other).GetPower(); + CObject* cargo = otherAsSlotted->GetSlotContainedObject(otherSlotNum); if (cargo != nullptr) return false; // the other already has a battery? - cargo = m_object->GetCargo(); + cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(cargo); + otherAsSlotted->SetSlotContainedObject(otherSlotNum, cargo); dynamic_cast(*cargo).SetTransporter(other); - cargo->SetPosition(dynamic_cast(*other).GetPowerPosition()); + cargo->SetPosition(otherAsSlotted->GetSlotPosition(otherSlotNum)); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } return true; diff --git a/src/object/task/tasktake.h b/src/object/task/tasktake.h index 9b461359..1de17364 100644 --- a/src/object/task/tasktake.h +++ b/src/object/task/tasktake.h @@ -57,7 +57,7 @@ public: protected: CObject* SearchTakeObject(float &angle, float dLimit, float aLimit); - CObject* SearchFriendObject(float &angle, float dLimit, float aLimit); + CObject* SearchFriendObject(float &angle, float dLimit, float aLimit, int &slotNumOut); bool TransporterTakeObject(); bool TransporterDeposeObject(); bool IsFreeDeposeObject(Math::Vector pos); diff --git a/src/object/task/taskterraform.cpp b/src/object/task/taskterraform.cpp index c0160bf8..bf4c1d66 100644 --- a/src/object/task/taskterraform.cpp +++ b/src/object/task/taskterraform.cpp @@ -30,7 +30,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/motion/motionant.h" #include "object/motion/motionspider.h" @@ -54,7 +54,7 @@ CTaskTerraform::CTaskTerraform(COldObject* object) : CForegroundTask(object) m_lastParticle = 0.0f; m_soundChannel = -1; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -97,7 +97,7 @@ bool CTaskTerraform::EventProcess(const Event &event) m_object->SetScale(1.0f+m_progress*0.2f); - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power != nullptr) { power->SetScale(1.0f+m_progress*1.0f); @@ -213,7 +213,7 @@ Error CTaskTerraform::Start() type = m_object->GetType(); if ( type != OBJECT_MOBILErt ) return ERR_WRONG_BOT; - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if ( power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer) ) return ERR_TERRA_ENERGY; energy = dynamic_cast(*power).GetEnergy(); if ( energy < ENERGY_TERRA+0.05f ) return ERR_TERRA_ENERGY; @@ -269,7 +269,7 @@ Error CTaskTerraform::IsEnded() m_object->SetCirVibration(Math::Vector(0.0f, 0.0f, 0.0f)); m_object->SetScale(1.0f); - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power != nullptr) { power->SetScale(1.0f); @@ -335,7 +335,7 @@ bool CTaskTerraform::Abort() m_object->SetCirVibration(Math::Vector(0.0f, 0.0f, 0.0f)); m_object->SetScale(1.0f); - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power != nullptr) { power->SetScale(1.0f); diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index d3b69cd8..8ecc0f73 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -43,9 +43,8 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" #include "object/interface/jostleable_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "object/motion/motion.h" @@ -802,9 +801,9 @@ void CPhysics::MotorUpdate(float aTime, float rTime) } } - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (HasPowerCellSlot(m_object)) { - power = dynamic_cast(dynamic_cast(*m_object).GetPower()); // searches for the object battery uses + power = GetObjectPowerCell(m_object); // searches for the object battery uses if ( GetObjectEnergy(m_object) == 0.0f ) // no battery or flat? { motorSpeed.x = 0.0f; @@ -2966,10 +2965,11 @@ void CPhysics::PowerParticle(float factor, bool bBreak) Math::Point dim; bool bCarryPower; + // TODO: it should be all slots that contain a power cell that can get recharged. Not just the carrying slot. bCarryPower = false; - if (m_object->Implements(ObjectInterfaceType::Carrier)) + if (m_object->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*m_object).GetCargo(); + CObject* cargo = dynamic_cast(*m_object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING); if ( cargo != nullptr && cargo->Implements(ObjectInterfaceType::PowerContainer) && dynamic_cast(*cargo).IsRechargeable() && m_object->GetPartRotationZ(1) == ARM_STOCK_ANGLE1 ) @@ -2980,7 +2980,7 @@ void CPhysics::PowerParticle(float factor, bool bBreak) mat = m_object->GetWorldMatrix(0); - pos = m_object->GetPowerPosition(); + pos = m_object->GetSlotPosition(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER)); pos.x -= 0.3f; pos.y += 1.0f; // battery center position pos = Transform(*mat, pos); @@ -3784,16 +3784,16 @@ Error CPhysics::GetError() } } - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (HasPowerCellSlot(m_object)) { - CObject* power = dynamic_cast(*m_object).GetPower(); // searches for the object battery used - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) + CPowerContainerObject* power = GetObjectPowerCell(m_object); // searches for the object battery used + if (power == nullptr) { return ERR_VEH_POWER; } else { - if ( dynamic_cast(*power).GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; + if ( power->GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; } } diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index ad1a3839..cc4be42b 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -744,15 +744,18 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo } else { - if (obj->Implements(ObjectInterfaceType::Old)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - COldObject* oldobj = dynamic_cast(obj); - if (oldobj->GetPower() != nullptr) - CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetPower()); - if (oldobj->GetCargo() != nullptr) - CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetCargo()); - oldobj->SetPower(nullptr); - oldobj->SetCargo(nullptr); + CSlottedObject* slotted = dynamic_cast(obj); + for (int slotNum = slotted->GetNumSlots() - 1; slotNum >= 0; slotNum--) + { + CObject* sub = slotted->GetSlotContainedObject(slotNum); + if (sub != nullptr) + { + slotted->SetSlotContainedObject(slotNum, nullptr); + CObjectManager::GetInstancePointer()->DeleteObject(sub); + } + } } CObjectManager::GetInstancePointer()->DeleteObject(obj); } @@ -3687,9 +3690,10 @@ void CScriptFunctions::uObject(CBotVar* botThis, void* user) // Updates the type of battery. pVar = pVar->GetNext(); // "energyCell" - if (object->Implements(ObjectInterfaceType::Powered)) + CSlottedObject *asSlotted = object->Implements(ObjectInterfaceType::Slotted) ? dynamic_cast(object) : nullptr; + if (asSlotted != nullptr && asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER) >= 0) { - CObject* power = dynamic_cast(object)->GetPower(); + CObject *power = asSlotted->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power == nullptr) { pVar->SetPointer(nullptr); @@ -3702,9 +3706,9 @@ void CScriptFunctions::uObject(CBotVar* botThis, void* user) // Updates the transported object's type. pVar = pVar->GetNext(); // "load" - if (object->Implements(ObjectInterfaceType::Carrier)) + if (asSlotted != nullptr && asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0) { - CObject* cargo = dynamic_cast(object)->GetCargo(); + CObject* cargo = asSlotted->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) { pVar->SetPointer(nullptr); diff --git a/src/ui/object_interface.cpp b/src/ui/object_interface.cpp index 9ca2c8b1..83c4b404 100644 --- a/src/ui/object_interface.cpp +++ b/src/ui/object_interface.cpp @@ -35,9 +35,8 @@ #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" #include "object/interface/programmable_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/task_executor_object.h" #include "object/motion/motion.h" @@ -1373,7 +1372,7 @@ bool CObjectInterface::CreateInterface(bool bSelect) pw->CreateButton(pos, dim, 10, EVENT_OBJECT_DESELECT); } - if ( m_object->Implements(ObjectInterfaceType::Powered) ) // vehicle? + if ( HasPowerCellSlot(m_object) ) // vehicle? { pos.x = ox+sx*14.5f; pos.y = oy+sy*0; @@ -1576,15 +1575,10 @@ void CObjectInterface::UpdateInterface(float rTime) float energy = 0.0f; float limit = 0.0f; - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (CPowerContainerObject* powerContainer = GetObjectPowerCell(m_object)) { - CObject* power = dynamic_cast(m_object)->GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - { - CPowerContainerObject* powerContainer = dynamic_cast(power); - energy = powerContainer->GetEnergyLevel(); - limit = powerContainer->GetEnergy(); - } + energy = powerContainer->GetEnergyLevel(); + limit = powerContainer->GetEnergy(); } icon = 0; // red/green @@ -1909,7 +1903,7 @@ void CObjectInterface::UpdateInterface() bFly = bEnable; if ( bFly && (type == OBJECT_HUMAN || type == OBJECT_TECH) ) { - if (m_object->Implements(ObjectInterfaceType::Carrier) && dynamic_cast(m_object)->IsCarryingCargo()) + if (dynamic_cast(*m_object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr) bFly = false; } EnableInterface(pw, EVENT_OBJECT_GASUP, bFly && m_main->CanPlayerInteract()); From 38b9b9be4c9edb44eea9974ecccc787f96cc845a Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 8 Dec 2021 19:15:16 +0100 Subject: [PATCH 2/7] Update data, gtest and add options to .gitmodules Update the `data` and `lib/googletest` submodules. `git submodule update --remote` command should now update the data submodule by rebasing on the remote branch named the same as the current branch. Changes in the `lib/googletest` submodule should now be ignored in `git status`. --- .gitmodules | 3 +++ data | 2 +- lib/googletest | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index f4a19857..0b5b4004 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ [submodule "data"] path = data url = git://github.com/colobot/colobot-data.git + branch = . + update = rebase [submodule "lib/googletest"] path = lib/googletest url = git://github.com/google/googletest.git + ignore = all diff --git a/data b/data index 21a45c0b..5d658744 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 21a45c0b8809accd142a83a81f1a3c92a327f319 +Subproject commit 5d658744641064e56cf489453dbf726964b2b98d diff --git a/lib/googletest b/lib/googletest index 703bd9ca..e2239ee6 160000 --- a/lib/googletest +++ b/lib/googletest @@ -1 +1 @@ -Subproject commit 703bd9caab50b139428cea1aaff9974ebee5742e +Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 From f9c275919e410359b526b27d32ebe5ca66f144ab Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 8 Dec 2021 19:25:36 +0100 Subject: [PATCH 3/7] Fix CMake warning about empty GoogleTest version The warning appeared when GoogleTest is used as a submodule. --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c4dd5808..96b534a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,11 +439,12 @@ endif() if(TESTS) # Google Test library - find_package(GTest 1.10.0 QUIET) + find_package(GTest 1.11.0 QUIET) if(NOT(FORCE_BUNDLED_GTEST) AND GTEST_FOUND) message(STATUS "Using system gtest library in ${GTEST_BOTH_LIBRARIES}") elseif(EXISTS "${colobot_SOURCE_DIR}/lib/googletest/googletest/CMakeLists.txt") message(STATUS "Using gtest git submodule") + set(GOOGLETEST_VERSION "1.11.0") add_subdirectory("${colobot_SOURCE_DIR}/lib/googletest/googletest" "lib/googletest/googletest") # Add aliases so target names are compatible with the find_package above add_library(GTest::GTest ALIAS gtest) From ea7837b4c6fbcb3827a19208b95f3dda698541a6 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 8 Dec 2021 19:35:30 +0100 Subject: [PATCH 4/7] Remove usage of a CMake 3.17 feature We currently use CMake 3.16 for compatibility with Ubuntu 18.04 LTS. The `NAME_MISMATCHED` argument which turns off a CMake warning when using `FIND_PACKAGE_HANDLE_STANDARD_ARGS`, is not available in CMake 3.16 and can prevent a successful build. See https://github.com/colobot/colobot/pull/1478#issuecomment-986204233 --- cmake/FindSDL2.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/FindSDL2.cmake b/cmake/FindSDL2.cmake index cd73f293..2445d36e 100644 --- a/cmake/FindSDL2.cmake +++ b/cmake/FindSDL2.cmake @@ -317,8 +317,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 if(SDL2MAIN_LIBRARY) FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR - VERSION_VAR SDL2_VERSION_STRING - NAME_MISMATCHED) + VERSION_VAR SDL2_VERSION_STRING) endif() From 193d105a3fde2ab0d0063d8c32e1bc5eb5ba38a7 Mon Sep 17 00:00:00 2001 From: MrSimbax Date: Wed, 8 Dec 2021 22:33:23 +0100 Subject: [PATCH 5/7] Refactor CBotUT.TestSaveStateIoFunctions Split the test into several parametrised tests for each data type. Add more tests with different values. Fix the ReadBinary() function in order to fix the ReadDouble() function for negative values (the sign bit was being lost). --- src/CBot/CBotFileUtils.cpp | 2 +- test/unit/CBot/CBotFileUtils_test.cpp | 285 ++++++++++++++++++++++++++ test/unit/CBot/CBot_test.cpp | 194 ------------------ test/unit/CMakeLists.txt | 3 +- 4 files changed, 288 insertions(+), 196 deletions(-) create mode 100644 test/unit/CBot/CBotFileUtils_test.cpp diff --git a/src/CBot/CBotFileUtils.cpp b/src/CBot/CBotFileUtils.cpp index cdbc62af..cebe9acb 100644 --- a/src/CBot/CBotFileUtils.cpp +++ b/src/CBot/CBotFileUtils.cpp @@ -59,7 +59,7 @@ static bool ReadBinary(std::istream &istr, T &value) while (true) // unsigned LEB128 { if (!istr.read(reinterpret_cast(&chr), 1)) return false; - if (shift < sizeof(T) * 8 - 1) + if (shift < sizeof(T) * 8) value |= static_cast(chr & 0x7F) << shift; shift += 7; if ((chr & 0x80) == 0) break; diff --git a/test/unit/CBot/CBotFileUtils_test.cpp b/test/unit/CBot/CBotFileUtils_test.cpp new file mode 100644 index 00000000..8f35118c --- /dev/null +++ b/test/unit/CBot/CBotFileUtils_test.cpp @@ -0,0 +1,285 @@ +#include "CBot/CBotFileUtils.h" + +#include + +namespace +{ +std::string createTestStringWithAllPossibleCharacters() +{ + std::string testString; + for (char c = std::numeric_limits::min(); ; ++c) + { + testString.push_back(c); + if (c == std::numeric_limits::max()) break; + } + return testString; +} +} + +namespace CBot +{ + +struct CBotFileUtilsTest : public testing::Test +{ + std::stringstream stream; +}; + +struct CBotFileUtilsReadWriteByteTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteByteTest, ReadByteValueShouldMatchWrittenValue) +{ + char expectedValue{GetParam()}; + ASSERT_TRUE(WriteByte(stream, expectedValue)); + char value{1}; + ASSERT_TRUE(ReadByte(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteByteTest, + testing::Values( + '\0', + static_cast(42), + std::numeric_limits::min(), + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteWordTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteWordTest, ReadWordValueShouldMatchWrittenValue) +{ + unsigned short expectedValue{GetParam()}; + ASSERT_TRUE(WriteWord(stream, expectedValue)); + unsigned short value{1}; + ASSERT_TRUE(ReadWord(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteWordTest, + testing::Values( + static_cast(0), + static_cast(42), + std::numeric_limits::min(), + std::numeric_limits::max() / 2, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteShortTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteShortTest, ReadShortValueShouldMatchWrittenValue) +{ + short expectedValue{GetParam()}; + ASSERT_TRUE(WriteShort(stream, expectedValue)); + short value{1}; + ASSERT_TRUE(ReadShort(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteShortTest, + testing::Values( + static_cast(-7), + static_cast(-1), + static_cast(0), + static_cast(42), + std::numeric_limits::min(), + std::numeric_limits::min() / 2, + std::numeric_limits::max() / 2, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteUInt32Test : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteUInt32Test, ReadUInt32ValueShouldMatchWrittenValue) +{ + uint32_t expectedValue{GetParam()}; + ASSERT_TRUE(WriteUInt32(stream, expectedValue)); + uint32_t value{1}; + ASSERT_TRUE(ReadUInt32(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteUInt32Test, + testing::Values( + static_cast(0), + static_cast(42), + std::numeric_limits::max() / 2, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteIntTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteIntTest, ReadIntValueShouldMatchWrittenValue) +{ + int expectedValue{GetParam()}; + ASSERT_TRUE(WriteInt(stream, expectedValue)); + int value{1}; + ASSERT_TRUE(ReadInt(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteIntTest, + testing::Values( + static_cast(7), + static_cast(-1), + static_cast(0), + static_cast(42), + std::numeric_limits::min(), + std::numeric_limits::min() / 2, + std::numeric_limits::max() / 2, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteLongTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteLongTest, ReadLongValueShouldMatchWrittenValue) +{ + long value{1}; + long expectedValue{GetParam()}; + ASSERT_TRUE(WriteLong(stream, expectedValue)); + ASSERT_TRUE(ReadLong(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +TEST_P(CBotFileUtilsReadWriteLongTest, ReadLongValueShouldMatchWrittenValueWithPadding) +{ + constexpr int padding = 10; + long expectedValue{GetParam()}; + ASSERT_TRUE(WriteLong(stream, expectedValue, padding)); + long value{1}; + ASSERT_TRUE(ReadLong(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +TEST_P(CBotFileUtilsReadWriteLongTest, ReadLongValueShouldMatchWrittenValueWithPaddingAndMultipleValues) +{ + constexpr int padding = 10; + long value{1}; + long expectedValue{GetParam()}; + int anotherValue{1}; + int anotherExpectedValue{2}; + ASSERT_TRUE(WriteLong(stream, expectedValue, padding)); + ASSERT_TRUE(WriteInt(stream, anotherExpectedValue)); + ASSERT_TRUE(ReadLong(stream, value)); + ASSERT_TRUE(ReadInt(stream, anotherValue)); + ASSERT_EQ(expectedValue, value); + ASSERT_EQ(anotherExpectedValue, anotherValue); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteLongTest, + testing::Values( + static_cast(7), + static_cast(-1), + static_cast(0), + static_cast(42), + std::numeric_limits::min(), + std::numeric_limits::min() / 2, + std::numeric_limits::max() / 2, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteFloatTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteFloatTest, ReadFloatValueShouldMatchWrittenValue) +{ + float expectedValue{GetParam()}; + ASSERT_TRUE(WriteFloat(stream, expectedValue)); + float value{1.0f}; + ASSERT_TRUE(ReadFloat(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteFloatTest, + testing::Values( + 7.0f, + -1.0f, + 0.0f, + 42.0f, + 3.14f, + -2.73f, + std::numeric_limits::min(), + std::numeric_limits::min() / 2.0f, + std::numeric_limits::max() / 2.0f, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteDoubleTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteDoubleTest, ReadDoubleValueShouldMatchWrittenValue) +{ + double expectedValue{GetParam()}; + ASSERT_TRUE(WriteDouble(stream, expectedValue)); + double value{1.0}; + ASSERT_TRUE(ReadDouble(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteDoubleTest, + testing::Values( + 7.0, + -1.0, + 0.0, + 42.0, + 3.14, + -2.73, + std::numeric_limits::min(), + std::numeric_limits::min() / 2.0, + std::numeric_limits::max() / 2.0, + std::numeric_limits::max())); + +struct CBotFileUtilsReadWriteStringTest : public CBotFileUtilsTest, public testing::WithParamInterface +{ +}; + +TEST_P(CBotFileUtilsReadWriteStringTest, ReadStringValueShouldMatchWrittenValue) +{ + std::string expectedValue{GetParam()}; + ASSERT_TRUE(WriteString(stream, expectedValue)); + std::string value{"test"}; + ASSERT_TRUE(ReadString(stream, value)); + ASSERT_EQ(expectedValue, value); +} + +INSTANTIATE_TEST_SUITE_P( + CBotIoReadWriteTest, + CBotFileUtilsReadWriteStringTest, + testing::Values( + "", + "123", + "abc", + createTestStringWithAllPossibleCharacters())); + +TEST_F(CBotFileUtilsTest, ReadStreamShouldMatchWrittenStream) +{ + std::string expectedValue{"Lorem ipsum dolor sit amet"}; + std::stringstream initialStream{expectedValue}; + ASSERT_TRUE(WriteStream(stream, initialStream)); + std::stringstream newStream{}; + ASSERT_TRUE(ReadStream(stream, newStream)); + ASSERT_EQ(expectedValue, newStream.str()); +} + +} diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 3d1ee516..7da15f69 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -320,200 +320,6 @@ protected: } }; -TEST_F(CBotUT, TestSaveStateIOFunctions) -{ - std::stringstream sstr(""); - std::string teststring; - - for (char c = std::numeric_limits::min() ;; ++c) - { - teststring.push_back(c); - if ( c == std::numeric_limits::max() ) break; - } - - auto CallWriteFunctions = [&sstr, &teststring]() -> bool - { - if (!WriteWord(sstr, static_cast(0))) return false; - if (!WriteWord(sstr, std::numeric_limits::max() / 2)) return false; - if (!WriteWord(sstr, std::numeric_limits::max())) return false; - - if (!WriteByte(sstr, std::numeric_limits::min())) return false; - if (!WriteByte(sstr, std::numeric_limits::max())) return false; - - if (!WriteShort(sstr, std::numeric_limits::min())) return false; - if (!WriteShort(sstr, std::numeric_limits::min() / 2)) return false; - if (!WriteShort(sstr, -1)) return false; - if (!WriteShort(sstr, 0)) return false; - if (!WriteShort(sstr, std::numeric_limits::max() / 2)) return false; - if (!WriteShort(sstr, std::numeric_limits::max())) return false; - - if (!WriteUInt32(sstr, static_cast(0))) return false; - if (!WriteUInt32(sstr, std::numeric_limits::max() / 2)) return false; - if (!WriteUInt32(sstr, std::numeric_limits::max())) return false; - - if (!WriteInt(sstr, std::numeric_limits::min())) return false; - if (!WriteInt(sstr, std::numeric_limits::min() / 2)) return false; - if (!WriteInt(sstr, -1)) return false; - if (!WriteInt(sstr, 0)) return false; - if (!WriteInt(sstr, std::numeric_limits::max() / 2)) return false; - if (!WriteInt(sstr, std::numeric_limits::max())) return false; - - if (!WriteLong(sstr, std::numeric_limits::min())) return false; - if (!WriteLong(sstr, std::numeric_limits::min() / 2L)) return false; - if (!WriteLong(sstr, -1L)) return false; - if (!WriteLong(sstr, 0L)) return false; - if (!WriteLong(sstr, std::numeric_limits::max() / 2L)) return false; - if (!WriteLong(sstr, std::numeric_limits::max())) return false; - - // test with padding bytes (not currently used anywhere) - if (!WriteLong(sstr, 1234567890L, 10)) return false; - - if (!WriteFloat(sstr, std::numeric_limits::min())) return false; - if (!WriteFloat(sstr, 0.0f)) return false; - if (!WriteFloat(sstr, std::numeric_limits::max())) return false; - - if (!WriteDouble(sstr, std::numeric_limits::min())) return false; - if (!WriteDouble(sstr, 0.0)) return false; - if (!WriteDouble(sstr, std::numeric_limits::max())) return false; - - if (!WriteString(sstr, "")) return false; - if (!WriteString(sstr, teststring)) return false; - return true; - }; - - if ( !CallWriteFunctions() ) - { - ADD_FAILURE() << "failed in CallWriteFunctions()" << std::endl; - return; - } - - std::stringstream savestream(""); - - if ( !WriteStream(savestream, sstr) ) - { - ADD_FAILURE() << "CBot::WriteStream() failed" << std::endl; - return; - } - - std::stringstream newstream(""); - - if ( !ReadStream(savestream, newstream) ) - { - ADD_FAILURE() << "CBot:ReadStream() failed" << std::endl; - return; - } - - auto CallReadFunctions = [&teststring, &newstream]() -> bool - { - unsigned short w = 1; - if (!ReadWord(newstream, w)) return false; - if (w != static_cast(0)) return false; - if (!ReadWord(newstream, w)) return false; - if (w != std::numeric_limits::max() / 2) return false; - - if (!ReadWord(newstream, w)) return false; - if (w != std::numeric_limits::max()) return false; - - char c = 1; - if (!ReadByte(newstream, c)) return false; - if (c != std::numeric_limits::min()) return false; - if (!ReadByte(newstream, c)) return false; - if (c != std::numeric_limits::max()) return false; - - short s = 1; - if (!ReadShort(newstream, s)) return false; - if (s != std::numeric_limits::min()) return false; - if (!ReadShort(newstream, s)) return false; - if (s != std::numeric_limits::min() / 2) return false; - - if (!ReadShort(newstream, s)) return false; - if (s != -1) return false; - if (!ReadShort(newstream, s)) return false; - if (s != 0) return false; - - if (!ReadShort(newstream, s)) return false; - if (s != std::numeric_limits::max() / 2) return false; - if (!ReadShort(newstream, s)) return false; - if (s != std::numeric_limits::max()) return false; - - uint32_t u = 1; - if (!ReadUInt32(newstream, u)) return false; - if (u != static_cast(0)) return false; - if (!ReadUInt32(newstream, u)) return false; - if (u != std::numeric_limits::max() / 2) return false; - - if (!ReadUInt32(newstream, u)) return false; - if (u != std::numeric_limits::max()) return false; - - int i = 1; - if (!ReadInt(newstream, i)) return false; - if (i != std::numeric_limits::min()) return false; - if (!ReadInt(newstream, i)) return false; - if (i != std::numeric_limits::min() / 2) return false; - - if (!ReadInt(newstream, i)) return false; - if (i != -1) return false; - if (!ReadInt(newstream, i)) return false; - if (i != 0) return false; - - if (!ReadInt(newstream, i)) return false; - if (i != std::numeric_limits::max() / 2) return false; - if (!ReadInt(newstream, i)) return false; - if (i != std::numeric_limits::max()) return false; - - long l = 1L; - if (!ReadLong(newstream, l)) return false; - if (l != std::numeric_limits::min()) return false; - if (!ReadLong(newstream, l)) return false; - if (l != std::numeric_limits::min() / 2L) return false; - - if (!ReadLong(newstream, l)) return false; - if (l != -1L) return false; - if (!ReadLong(newstream, l)) return false; - if (l != 0L) return false; - - if (!ReadLong(newstream, l)) return false; - if (l != std::numeric_limits::max() / 2L) return false; - if (!ReadLong(newstream, l)) return false; - if (l != std::numeric_limits::max()) return false; - - if (!ReadLong(newstream, l)) return false; - if (l != 1234567890L) return false; - - float f = 1.0f; - if (!ReadFloat(newstream, f)) return false; - if (f != std::numeric_limits::min()) return false; - if (!ReadFloat(newstream, f)) return false; - if (f != 0.0f) return false; - - if (!ReadFloat(newstream, f)) return false; - if (f != std::numeric_limits::max()) return false; - - double d = 1.0; - if (!ReadDouble(newstream, d)) return false; - if (d != std::numeric_limits::min()) return false; - if (!ReadDouble(newstream, d)) return false; - if (d != 0.0) return false; - - if (!ReadDouble(newstream, d)) return false; - if (d != std::numeric_limits::max()) return false; - - std::string newstring = "should be empty string after next read"; - if (!ReadString(newstream, newstring)) return false; - if (newstring != "") return false; - - if (!ReadString(newstream, newstring)) return false; - if (newstring != teststring) return false; - return true; - }; - - if ( !CallReadFunctions() ) - { - ADD_FAILURE() << "failed in CallReadFunctions()" << std::endl; - return; - } -} - TEST_F(CBotUT, EmptyTest) { ExecuteTest( diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index b549229e..8eb7d7a5 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -11,8 +11,9 @@ add_definitions(-DGTEST_HAS_TR1_TUPLE=0) add_executable(colobot_ut main.cpp app/app_test.cpp - CBot/CBotToken_test.cpp CBot/CBot_test.cpp + CBot/CBotFileUtils_test.cpp + CBot/CBotToken_test.cpp common/config_file_test.cpp common/timeutils_test.cpp graphics/engine/lightman_test.cpp From 0003494c9eb9d11cdc8f336d30cd2b4c34dc1736 Mon Sep 17 00:00:00 2001 From: tomangelo2 Date: Sat, 29 Jan 2022 17:16:57 +0100 Subject: [PATCH 6/7] Merge PR #1444 --- src/CMakeLists.txt | 3 +- src/graphics/engine/camera.cpp | 22 ++- src/graphics/engine/pyro.cpp | 57 +++---- src/level/robotmain.cpp | 134 ++++++++++------- src/level/scene_conditions.cpp | 11 +- src/object/auto/autolabo.cpp | 14 +- src/object/auto/autonuclearplant.cpp | 12 +- src/object/auto/autopowercaptor.cpp | 41 ++--- src/object/auto/autopowerplant.cpp | 17 ++- src/object/auto/autopowerstation.cpp | 32 +--- src/object/auto/autoresearch.cpp | 18 ++- src/object/auto/autotower.cpp | 18 +-- src/object/interface/carrier_object.h | 55 ------- src/object/interface/powered_object.h | 94 ------------ src/object/interface/slotted_object.h | 148 ++++++++++++++++++ src/object/motion/motionhuman.cpp | 2 - src/object/motion/motionvehicle.cpp | 8 +- src/object/object_interface_type.h | 3 +- src/object/old_object.cpp | 172 +++++++++++++++++---- src/object/old_object.h | 26 ++-- src/object/task/taskbuild.cpp | 1 - src/object/task/taskfire.cpp | 16 +- src/object/task/taskflag.cpp | 2 - src/object/task/taskgoto.cpp | 8 +- src/object/task/taskmanip.cpp | 208 +++++++++++++------------- src/object/task/taskmanip.h | 2 +- src/object/task/taskrecover.cpp | 15 +- src/object/task/taskshield.cpp | 16 +- src/object/task/tasktake.cpp | 153 +++++++++---------- src/object/task/tasktake.h | 2 +- src/object/task/taskterraform.cpp | 12 +- src/physics/physics.cpp | 22 +-- src/script/scriptfunc.cpp | 28 ++-- src/ui/object_interface.cpp | 18 +-- 34 files changed, 725 insertions(+), 665 deletions(-) delete mode 100644 src/object/interface/carrier_object.h delete mode 100644 src/object/interface/powered_object.h create mode 100644 src/object/interface/slotted_object.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 105bce70..b92ae24b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -246,7 +246,6 @@ add_library(colobotbase STATIC object/implementation/programmable_impl.h object/implementation/task_executor_impl.cpp object/implementation/task_executor_impl.h - object/interface/carrier_object.h object/interface/controllable_object.h object/interface/damageable_object.h object/interface/destroyable_object.h @@ -257,12 +256,12 @@ add_library(colobotbase STATIC object/interface/jostleable_object.h object/interface/movable_object.h object/interface/power_container_object.h - object/interface/powered_object.h object/interface/program_storage_object.h object/interface/programmable_object.h object/interface/ranged_object.h object/interface/shielded_auto_regen_object.h object/interface/shielded_object.h + object/interface/slotted_object.h object/interface/task_executor_object.h object/interface/trace_drawing_object.cpp object/interface/trace_drawing_object.h diff --git a/src/graphics/engine/camera.cpp b/src/graphics/engine/camera.cpp index 57e66dca..20d2e9e5 100644 --- a/src/graphics/engine/camera.cpp +++ b/src/graphics/engine/camera.cpp @@ -37,10 +37,9 @@ #include "object/object.h" #include "object/object_manager.h" -#include "object/interface/carrier_object.h" #include "object/interface/controllable_object.h" #include "object/interface/movable_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" @@ -57,18 +56,15 @@ static void SetTransparency(CObject* obj, float value) { obj->SetTransparency(value); - if (obj->Implements(ObjectInterfaceType::Carrier)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*obj).GetCargo(); - if (cargo != nullptr) - cargo->SetTransparency(value); - } - - if (obj->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*obj).GetPower(); - if (power != nullptr) - power->SetTransparency(value); + CSlottedObject *slotted = dynamic_cast(obj); + for(int slot = slotted->GetNumSlots()-1; slot >= 0; slot--) + { + CObject *contained = slotted->GetSlotContainedObject(slot); + if (contained != nullptr) + SetTransparency(contained, value); + } } } diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index dd04b9f3..caa485d1 100644 --- a/src/graphics/engine/pyro.cpp +++ b/src/graphics/engine/pyro.cpp @@ -35,6 +35,8 @@ #include "object/object_manager.h" #include "object/old_object.h" +#include "object/interface/slotted_object.h" + #include "object/motion/motionhuman.h" #include "object/subclass/shielder.h" @@ -127,9 +129,7 @@ bool CPyro::Create(PyroType type, CObject* obj, float force) // Seeking the position of the battery. - CObject* power = nullptr; - if (obj->Implements(ObjectInterfaceType::Powered)) - power = dynamic_cast(*obj).GetPower(); + CObject* power = GetObjectInPowerCellSlot(obj); if (power == nullptr) { @@ -1385,25 +1385,16 @@ void CPyro::DeleteObject(bool primary, bool secondary) type != OBJECT_NUCLEAR && type != OBJECT_ENERGY ) { - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (m_object->Implements(ObjectInterfaceType::Slotted)) { - CPoweredObject* poweredObject = dynamic_cast(m_object); - CObject* sub = poweredObject->GetPower(); - if (sub != nullptr) + CSlottedObject* asSlotted = dynamic_cast(m_object); + for (int slot = asSlotted->GetNumSlots() - 1; slot >= 0; slot--) { - CObjectManager::GetInstancePointer()->DeleteObject(sub); - poweredObject->SetPower(nullptr); - } - } - - if (m_object->Implements(ObjectInterfaceType::Carrier)) - { - CCarrierObject* carrierObject = dynamic_cast(m_object); - CObject* sub = carrierObject->GetCargo(); - if (sub != nullptr) - { - CObjectManager::GetInstancePointer()->DeleteObject(sub); - carrierObject->SetCargo(nullptr); + if (CObject* sub = asSlotted->GetSlotContainedObject(slot)) + { + CObjectManager::GetInstancePointer()->DeleteObject(sub); + asSlotted->SetSlotContainedObject(slot, nullptr); + } } } } @@ -1416,18 +1407,15 @@ void CPyro::DeleteObject(bool primary, bool secondary) CObject* transporter = dynamic_cast(*m_object).GetTransporter(); if (transporter != nullptr) { - if (transporter->Implements(ObjectInterfaceType::Powered)) + assert(transporter->Implements(ObjectInterfaceType::Slotted)); + CSlottedObject* asSlotted = dynamic_cast(transporter); + for (int slotNum = asSlotted->GetNumSlots() - 1; slotNum >= 0; slotNum--) { - CPoweredObject* powered = dynamic_cast(transporter); - if (powered->GetPower() == m_object) - powered->SetPower(nullptr); - } - - if (transporter->Implements(ObjectInterfaceType::Carrier)) - { - CCarrierObject* carrier = dynamic_cast(transporter); - if (carrier->GetCargo() == m_object) - carrier->SetCargo(nullptr); + if (asSlotted->GetSlotContainedObject(slotNum) == m_object) + { + asSlotted->SetSlotContainedObject(slotNum, nullptr); + break; + } } } } @@ -2196,11 +2184,10 @@ void CPyro::BurnProgress() oldObj->SetPartRotation(m_burnPart[i].part, pos); } - if (m_object->Implements(ObjectInterfaceType::Powered)) + // TODO: should this apply to every slot? + if (CObject* sub = GetObjectInPowerCellSlot(m_object)) // is there a battery? { - CObject* sub = dynamic_cast(*m_object).GetPower(); - if (sub != nullptr) // is there a battery? - sub->SetScaleY(1.0f - m_progress); // complete flattening + sub->SetScaleY(1.0f - m_progress); // complete flattening } } diff --git a/src/level/robotmain.cpp b/src/level/robotmain.cpp index 5f1b1599..68ecee4f 100644 --- a/src/level/robotmain.cpp +++ b/src/level/robotmain.cpp @@ -69,6 +69,8 @@ #include "object/auto/auto.h" +#include "object/interface/slotted_object.h" + #include "object/motion/motion.h" #include "object/motion/motionhuman.h" #include "object/motion/motiontoto.h" @@ -1392,12 +1394,8 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) CObject* object = GetSelect(); if (object != nullptr) { - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - dynamic_cast(*power).SetEnergyLevel(1.0f); - } + if (CPowerContainerObject *power = GetObjectPowerCell(object)) + power->SetEnergyLevel(1.0f); if (object->Implements(ObjectInterfaceType::Shielded)) dynamic_cast(*object).SetShield(1.0f); @@ -1414,12 +1412,8 @@ void CRobotMain::ExecuteCmd(const std::string& cmd) if (object != nullptr) { - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - dynamic_cast(*power).SetEnergyLevel(1.0f); - } + if (CPowerContainerObject *power = GetObjectPowerCell(object)) + power->SetEnergyLevel(1.0f); } return; } @@ -2007,21 +2001,12 @@ CObject* CRobotMain::DetectObject(Math::Point pos) if (obj->GetProxyActivate()) continue; CObject* target = obj; + // TODO: should this also apply to slots other than power cell slots? if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable)) { - target = dynamic_cast(*obj).GetTransporter(); // battery connected - if (target == nullptr) - { - target = obj; // standalone battery - } - else - { - if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast(*target).GetPower() != obj) - { - // transported, but not in the power slot - target = obj; - } - } + CObject *transporter = dynamic_cast(*obj).GetTransporter(); // battery connected + if (transporter != nullptr && obj == GetObjectInPowerCellSlot(transporter)) + target = transporter; } if (!obj->Implements(ObjectInterfaceType::Old)) continue; @@ -4716,29 +4701,26 @@ bool CRobotMain::IOWriteScene(std::string filename, std::string filecbot, std::s if (IsObjectBeingTransported(obj)) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast(*obj).IsDying()) continue; - if (obj->Implements(ObjectInterfaceType::Carrier)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*obj).GetCargo(); - if (cargo != nullptr) // object transported? + CSlottedObject* slotted = dynamic_cast(obj); + for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--) { - line = MakeUnique("CreateFret"); - IOWriteObject(line.get(), cargo, dirname, objRank++); - levelParser.AddLine(std::move(line)); + if (CObject *sub = slotted->GetSlotContainedObject(slot)) + { + if (slot == slotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER)) + line = MakeUnique("CreatePower"); + else if (slot == slotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING)) + line = MakeUnique("CreateFret"); + else + line = MakeUnique("CreateSlotObject"); + line->AddParam("slotNum", MakeUnique(slot)); + IOWriteObject(line.get(), sub, dirname, objRank++); + levelParser.AddLine(std::move(line)); + } } } - if (obj->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*obj).GetPower(); - if (power != nullptr) // battery transported? - { - line = MakeUnique("CreatePower"); - IOWriteObject(line.get(), power, dirname, objRank++); - levelParser.AddLine(std::move(line)); - } - } - - line = MakeUnique("CreateObject"); IOWriteObject(line.get(), obj, dirname, objRank++); levelParser.AddLine(std::move(line)); @@ -4867,7 +4849,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) CLevelParser levelParser(filename); levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank); levelParser.Load(); - int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret"); + int numObjects = levelParser.CountLines("CreateObject") + levelParser.CountLines("CreatePower") + levelParser.CountLines("CreateFret") + levelParser.CountLines("CreateSlotObject"); m_base = nullptr; @@ -4876,6 +4858,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) CObject* sel = nullptr; int objRank = 0; int objCounter = 0; + std::map slots; for (auto& line : levelParser.GetLines()) { if (line->GetCommand() == "Mission") @@ -4908,6 +4891,16 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) objCounter++; } + if (line->GetCommand() == "CreateSlotObject") + { + int slotNum = line->GetParam("slotNum")->AsInt(); + CObject *slotObject = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects)); + objCounter++; + + assert(slots.find(slotNum) == slots.end()); + slots.emplace(slotNum, slotObject); + } + if (line->GetCommand() == "CreateObject") { CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString(objCounter+1)+" / "+StrUtils::ToString(numObjects), static_cast(objCounter) / static_cast(numObjects), objRank++); @@ -4915,20 +4908,50 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) if (line->GetParam("select")->AsBool(false)) sel = obj; - if (cargo != nullptr) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - assert(obj->Implements(ObjectInterfaceType::Carrier)); // TODO: exception? - assert(obj->Implements(ObjectInterfaceType::Old)); - dynamic_cast(*obj).SetCargo(cargo); - auto task = MakeUnique(dynamic_cast(obj)); - task->Start(TMO_AUTO, TMA_GRAB); // holds the object! + CSlottedObject* asSlotted = dynamic_cast(obj); + if (cargo != nullptr) + { + int slotNum = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING); + assert(slotNum >= 0); + assert(slots.find(slotNum) == slots.end()); + asSlotted->SetSlotContainedObject(slotNum, cargo); + + // TODO: eww! + assert(obj->Implements(ObjectInterfaceType::Old)); + auto task = MakeUnique(dynamic_cast(obj)); + task->Start(TMO_AUTO, TMA_GRAB); // holds the object! + } + + if (power != nullptr) + { + int slotNum = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + assert(slotNum >= 0); + assert(slots.find(slotNum) == slots.end()); + asSlotted->SetSlotContainedObject(slotNum, power); + } + + for (std::pair& slot : slots) + { + asSlotted->SetSlotContainedObject(slot.first, slot.second); + } + + cargo = nullptr; + power = nullptr; + slots.clear(); + } + else + { + // TODO: exception? + assert(slots.empty()); + assert(power == nullptr); + assert(cargo == nullptr); } if (power != nullptr) { - assert(obj->Implements(ObjectInterfaceType::Powered)); - dynamic_cast(*obj).SetPower(power); - assert(power->Implements(ObjectInterfaceType::Transportable)); + dynamic_cast(*obj).SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, power); dynamic_cast(*power).SetTransporter(obj); } cargo = nullptr; @@ -4938,6 +4961,11 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot) } } + // all slot objects assigned to parent objects + assert(slots.empty()); + assert(power == nullptr); + assert(cargo == nullptr); + m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE); // Reads the file of stacks of execution. diff --git a/src/level/scene_conditions.cpp b/src/level/scene_conditions.cpp index b5c0965f..f6d2af73 100644 --- a/src/level/scene_conditions.cpp +++ b/src/level/scene_conditions.cpp @@ -26,7 +26,8 @@ #include "object/object.h" #include "object/object_manager.h" -#include "object/interface/powered_object.h" +#include "object/interface/power_container_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include @@ -80,13 +81,9 @@ bool CObjectCondition::CheckForObject(CObject* obj) { power = dynamic_cast(obj); } - else if (obj->Implements(ObjectInterfaceType::Powered)) + else { - CObject* powerObj = dynamic_cast(*obj).GetPower(); - if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) - { - power = dynamic_cast(powerObj); - } + power = GetObjectPowerCell(obj); } if (power != nullptr) diff --git a/src/object/auto/autolabo.cpp b/src/object/auto/autolabo.cpp index 5cd27656..18fe630a 100644 --- a/src/object/auto/autolabo.cpp +++ b/src/object/auto/autolabo.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "sound/sound.h" @@ -59,7 +59,7 @@ CAutoLabo::CAutoLabo(COldObject* object) : CAuto(object) m_soundChannel = -1; Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(object->GetNumSlots() == 1); } // Object's destructor. @@ -131,7 +131,7 @@ Error CAutoLabo::StartAction(int param) return ERR_LABO_ALREADY; } - CObject* power = m_object->GetPower(); + CObject* power = dynamic_cast(*m_object).GetSlotContainedObject(0); if (power == nullptr) { return ERR_LABO_NULL; @@ -308,7 +308,7 @@ bool CAutoLabo::EventProcess(const Event &event) { if ( m_progress < 1.0f ) { - power = m_object->GetPower(); + power = dynamic_cast(*m_object).GetSlotContainedObject(0); if ( power != nullptr ) { power->SetScale(1.0f-m_progress); @@ -366,10 +366,10 @@ bool CAutoLabo::EventProcess(const Event &event) m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); UpdateInterface(); - power = m_object->GetPower(); + power = dynamic_cast(*m_object).GetSlotContainedObject(0); if ( power != nullptr ) { - m_object->SetPower(nullptr); + dynamic_cast(*m_object).SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(power); } @@ -457,7 +457,7 @@ Error CAutoLabo::GetError() return ERR_BAT_VIRUS; } - CObject* obj = m_object->GetPower(); + CObject* obj = dynamic_cast(*m_object).GetSlotContainedObject(0); if (obj == nullptr) return ERR_LABO_NULL; ObjectType type = obj->GetType(); if ( type != OBJECT_BULLET && type != OBJECT_TNT ) return ERR_LABO_BAD; diff --git a/src/object/auto/autonuclearplant.cpp b/src/object/auto/autonuclearplant.cpp index d92df352..d9685c15 100644 --- a/src/object/auto/autonuclearplant.cpp +++ b/src/object/auto/autonuclearplant.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "sound/sound.h" @@ -53,7 +53,7 @@ CAutoNuclearPlant::CAutoNuclearPlant(COldObject* object) : CAuto(object) m_channelSound = -1; Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -239,7 +239,7 @@ bool CAutoNuclearPlant::EventProcess(const Event &event) if ( cargo != nullptr ) { CObjectManager::GetInstancePointer()->DeleteObject(cargo); - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); } CreatePower(); // creates the atomic cell @@ -327,7 +327,7 @@ bool CAutoNuclearPlant::CreateInterface(bool bSelect) CObject* CAutoNuclearPlant::SearchUranium() { - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if (obj == nullptr) return nullptr; if (obj->GetType() == OBJECT_URANIUM) return obj; return nullptr; @@ -402,7 +402,7 @@ void CAutoNuclearPlant::CreatePower() dynamic_cast(*power).SetTransporter(m_object); power->SetPosition(Math::Vector(22.0f, 3.0f, 0.0f)); - m_object->SetPower(power); + m_object->SetSlotContainedObject(0, power); } @@ -422,7 +422,7 @@ Error CAutoNuclearPlant::GetError() //? if ( m_object->GetEnergy() < ENERGY_POWER ) return ERR_NUCLEAR_LOW; - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if ( obj == nullptr ) return ERR_NUCLEAR_EMPTY; if ( obj->GetLock() ) return ERR_OK; ObjectType type = obj->GetType(); diff --git a/src/object/auto/autopowercaptor.cpp b/src/object/auto/autopowercaptor.cpp index 90a3b455..a292b82f 100644 --- a/src/object/auto/autopowercaptor.cpp +++ b/src/object/auto/autopowercaptor.cpp @@ -30,8 +30,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "sound/sound.h" @@ -267,34 +266,22 @@ void CAutoPowerCaptor::ChargeObject(float rTime) } } - if (obj->Implements(ObjectInterfaceType::Powered)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* power = dynamic_cast(*obj).GetPower(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) + CSlottedObject* slotted = dynamic_cast(obj); + for (int slot = slotted->GetNumSlots(); slot >= 0; slot--) { - CPowerContainerObject* powerContainer = dynamic_cast(power); - if (powerContainer->IsRechargeable()) + CObject *held = slotted->GetSlotContainedObject(slot); + if (held != nullptr && held->Implements(ObjectInterfaceType::PowerContainer)) { - float energy = powerContainer->GetEnergy(); - energy += rTime/2.0f; - if ( energy > 1.0f ) energy = 1.0f; - powerContainer->SetEnergy(energy); - } - } - } - - if (obj->Implements(ObjectInterfaceType::Carrier)) - { - CObject* power = dynamic_cast(*obj).GetCargo(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) - { - CPowerContainerObject* powerContainer = dynamic_cast(power); - if (powerContainer->IsRechargeable()) - { - float energy = powerContainer->GetEnergy(); - energy += rTime/2.0f; - if ( energy > 1.0f ) energy = 1.0f; - powerContainer->SetEnergy(energy); + CPowerContainerObject* powerContainer = dynamic_cast(held); + if (powerContainer->IsRechargeable()) + { + float energy = powerContainer->GetEnergy(); + energy += rTime/2.0f; + if ( energy > 1.0f ) energy = 1.0f; + powerContainer->SetEnergy(energy); + } } } } diff --git a/src/object/auto/autopowerplant.cpp b/src/object/auto/autopowerplant.cpp index d84949bb..39c0b43b 100644 --- a/src/object/auto/autopowerplant.cpp +++ b/src/object/auto/autopowerplant.cpp @@ -34,7 +34,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "sound/sound.h" @@ -57,7 +57,7 @@ CAutoPowerPlant::CAutoPowerPlant(COldObject* object) : CAuto(object) m_partiSphere = -1; Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -79,17 +79,18 @@ void CAutoPowerPlant::DeleteObject(bool all) if ( !all ) { + // TODO: why are we only searching for titanium and power cells? why don't we delete any object regardless of type? CObject* cargo = SearchMetal(); if ( cargo != nullptr ) { - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(cargo); } cargo = SearchPower(); if ( cargo != nullptr ) { - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(cargo); } } @@ -320,7 +321,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) cargo = SearchMetal(); if ( cargo != nullptr ) { - m_object->SetPower(nullptr); + m_object->SetSlotContainedObject(0, nullptr); CObjectManager::GetInstancePointer()->DeleteObject(cargo); } @@ -333,7 +334,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) cargo->SetLock(false); // usable battery dynamic_cast(*cargo).SetTransporter(m_object); cargo->SetPosition(Math::Vector(0.0f, 3.0f, 0.0f)); - m_object->SetPower(cargo); + m_object->SetSlotContainedObject(0, cargo); m_main->DisplayError(INFO_ENERGY, m_object); } @@ -387,7 +388,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event) CObject* CAutoPowerPlant::SearchMetal() { - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if ( obj == nullptr ) return nullptr; ObjectType type = obj->GetType(); @@ -512,7 +513,7 @@ Error CAutoPowerPlant::GetError() if ( m_object->GetEnergy() < POWERPLANT_POWER ) return ERR_ENERGY_LOW; - CObject* obj = m_object->GetPower(); + CObject* obj = m_object->GetSlotContainedObject(0); if (obj == nullptr) return ERR_ENERGY_EMPTY; ObjectType type = obj->GetType(); if ( type == OBJECT_POWER ) return ERR_OK; diff --git a/src/object/auto/autopowerstation.cpp b/src/object/auto/autopowerstation.cpp index 913cf14e..c479f589 100644 --- a/src/object/auto/autopowerstation.cpp +++ b/src/object/auto/autopowerstation.cpp @@ -28,9 +28,6 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" - #include "sound/sound.h" #include "ui/controls/gauge.h" @@ -134,32 +131,13 @@ bool CAutoPowerStation::EventProcess(const Event &event) if (big > 0.0f) { CObject* vehicle = SearchVehicle(); - if (vehicle != nullptr) + if (vehicle != nullptr && vehicle->Implements(ObjectInterfaceType::Slotted)) { - if (vehicle->Implements(ObjectInterfaceType::Powered)) + CSlottedObject* slotted = dynamic_cast(vehicle); + for (int slot = slotted->GetNumSlots(); slot >= 0; slot--) { - CObject* power = dynamic_cast(*vehicle).GetPower(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) - { - CPowerContainerObject* powerContainer = dynamic_cast(power); - if (powerContainer->IsRechargeable()) - { - float energy = powerContainer->GetEnergy(); - float add = event.rTime*0.2f; - if ( add > big*4.0f ) add = big*4.0f; - if ( add > 1.0f-energy ) add = 1.0f-energy; - energy += add; // Charging the battery - powerContainer->SetEnergy(energy); - if ( energy < freq ) freq = energy; - big -= add/4.0f; // discharge the large battery - } - } - } - - if (vehicle->Implements(ObjectInterfaceType::Carrier)) - { - CObject* power = dynamic_cast(*vehicle).GetCargo(); - if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) + CObject *power = slotted->GetSlotContainedObject(slot); + if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) { CPowerContainerObject* powerContainer = dynamic_cast(power); if (powerContainer->IsRechargeable()) diff --git a/src/object/auto/autoresearch.cpp b/src/object/auto/autoresearch.cpp index 1eb536ca..cbbb5b4b 100644 --- a/src/object/auto/autoresearch.cpp +++ b/src/object/auto/autoresearch.cpp @@ -32,7 +32,7 @@ #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "sound/sound.h" @@ -57,7 +57,7 @@ CAutoResearch::CAutoResearch(COldObject* object) : CAuto(object) Init(); - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -114,11 +114,11 @@ Error CAutoResearch::StartAction(int param) return ERR_RESEARCH_ALREADY; } - if (m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer)) + if (m_object->GetSlotContainedObject(0) == nullptr || !m_object->GetSlotContainedObject(0)->Implements(ObjectInterfaceType::PowerContainer)) { return ERR_RESEARCH_POWER; } - CPowerContainerObject* power = dynamic_cast(m_object->GetPower()); + CPowerContainerObject* power = dynamic_cast(m_object->GetSlotContainedObject(0)); if ( power->GetCapacity() > 1.0f ) { return ERR_RESEARCH_TYPE; @@ -222,7 +222,9 @@ bool CAutoResearch::EventProcess(const Event &event) FireStopUpdate(m_progress, true); // flashes if ( m_progress < 1.0f ) { - if ( m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) // more battery? + CObject* batteryObj = dynamic_cast(*m_object).GetSlotContainedObject(0); + + if ( batteryObj == nullptr || !batteryObj->Implements(ObjectInterfaceType::PowerContainer) ) // more battery? { SetBusy(false); UpdateInterface(); @@ -232,7 +234,7 @@ bool CAutoResearch::EventProcess(const Event &event) m_speed = 1.0f/1.0f; return true; } - power = dynamic_cast(m_object->GetPower()); + power = dynamic_cast(batteryObj); power->SetEnergyLevel(1.0f-m_progress); if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time ) @@ -302,11 +304,11 @@ Error CAutoResearch::GetError() return ERR_BAT_VIRUS; } - if (m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer)) + if (m_object->GetSlotContainedObject(0) == nullptr || !m_object->GetSlotContainedObject(0)->Implements(ObjectInterfaceType::PowerContainer)) { return ERR_RESEARCH_POWER; } - CPowerContainerObject* power = dynamic_cast(m_object->GetPower()); + CPowerContainerObject* power = dynamic_cast(m_object->GetSlotContainedObject(0)); if ( power->GetCapacity() > 1.0f ) { return ERR_RESEARCH_TYPE; diff --git a/src/object/auto/autotower.cpp b/src/object/auto/autotower.cpp index ac22d16a..b390fa40 100644 --- a/src/object/auto/autotower.cpp +++ b/src/object/auto/autotower.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "physics/physics.h" @@ -61,7 +61,7 @@ CAutoTower::CAutoTower(COldObject* object) : CAuto(object) m_time = 0.0f; m_lastUpdateTime = 0.0f; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -125,13 +125,8 @@ bool CAutoTower::EventProcess(const Event &event) return true; } - CPowerContainerObject* power = nullptr; - float energy = 0.0f; - if ( m_object->GetPower() != nullptr && m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) - { - power = dynamic_cast(m_object->GetPower()); - energy = power->GetEnergy(); - } + CPowerContainerObject* power = GetObjectPowerCell(m_object); + float energy = power == nullptr ? 0.0f : power->GetEnergy(); UpdateInterface(event.rTime); @@ -321,12 +316,13 @@ Error CAutoTower::GetError() return ERR_BAT_VIRUS; } - if ( m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) + CPowerContainerObject *power = GetObjectPowerCell(m_object); + if ( power == nullptr ) { return ERR_TOWER_POWER; // no battery } - if ( dynamic_cast(*m_object->GetPower()).GetEnergy() < ENERGY_FIRE ) + if ( power->GetEnergy() < ENERGY_FIRE ) { return ERR_TOWER_ENERGY; // not enough energy } diff --git a/src/object/interface/carrier_object.h b/src/object/interface/carrier_object.h deleted file mode 100644 index 82c27a5d..00000000 --- a/src/object/interface/carrier_object.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2021, 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 - */ - -#pragma once - -#include "object/object.h" -#include "object/object_interface_type.h" - -/** - * \class CCarrierObject - * \brief Interface for carrier objects - */ -class CCarrierObject -{ -public: - explicit CCarrierObject(ObjectInterfaceTypes& types) - { - types[static_cast(ObjectInterfaceType::Carrier)] = true; - } - virtual ~CCarrierObject() - {} - - //! Returns carried object - virtual CObject* GetCargo() = 0; - //! Sets carried object - virtual void SetCargo(CObject* cargo) = 0; - - //! Checks whether there is any cargo - inline bool IsCarryingCargo() - { - return GetCargo() != nullptr; - } -}; - -inline bool IsObjectCarryingCargo(CObject* obj) -{ - return obj->Implements(ObjectInterfaceType::Carrier) && - dynamic_cast(*obj).IsCarryingCargo(); -} diff --git a/src/object/interface/powered_object.h b/src/object/interface/powered_object.h deleted file mode 100644 index 73c9a616..00000000 --- a/src/object/interface/powered_object.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the Colobot: Gold Edition source code - * Copyright (C) 2001-2021, 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 - */ - -#pragma once - -#include "object/object.h" -#include "object/object_interface_type.h" - -#include "object/interface/power_container_object.h" - -class CObject; - -/** - * \class CPoweredObject - * \brief Interface for objects powered using power cells - * - * TODO: It currently includes objects that take other objects as input - * and convert them, for example PowerPlant. - * We should create a dedicated interface for such uses. - */ -class CPoweredObject -{ -public: - explicit CPoweredObject(ObjectInterfaceTypes& types) - { - types[static_cast(ObjectInterfaceType::Powered)] = true; - } - virtual ~CPoweredObject() - {} - - //! Returns the power cell - virtual CObject* GetPower() = 0; - //! Sets power cell - virtual void SetPower(CObject* power) = 0; - - //! Returns the relative position of power cell - virtual Math::Vector GetPowerPosition() = 0; - //! Sets the relative position of power cell - virtual void SetPowerPosition(const Math::Vector& powerPosition) = 0; -}; - -inline float GetObjectEnergy(CObject* object) -{ - float energy = 0.0f; - - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - { - energy = dynamic_cast(*power).GetEnergy(); - } - } - - return energy; -} - -inline float GetObjectEnergyLevel(CObject* object) -{ - float energy = 0.0f; - - if (object->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*object).GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - { - energy = dynamic_cast(*power).GetEnergyLevel(); - } - } - - return energy; -} - -inline bool ObjectHasPowerCell(CObject* object) -{ - return object->Implements(ObjectInterfaceType::Powered) && - dynamic_cast(*object).GetPower() != nullptr; -} diff --git a/src/object/interface/slotted_object.h b/src/object/interface/slotted_object.h new file mode 100644 index 00000000..61f8c8ec --- /dev/null +++ b/src/object/interface/slotted_object.h @@ -0,0 +1,148 @@ +/* + * This file is part of the Colobot: Gold Edition source code + * Copyright (C) 2001-2020, 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 + */ + +#pragma once + +#include "math/vector.h" + +#include "object/object.h" +#include "object/object_interface_type.h" + +#include "object/interface/power_container_object.h" + +#include + +class CObject; + +/** + * \class CSlottedObject + * \brief Interface for objects that hold other objects + */ +class CSlottedObject +{ +public: + ///! Object-independent identifiers for certain special slots + enum class Pseudoslot + { + POWER, + CARRYING + }; + + explicit CSlottedObject(ObjectInterfaceTypes& types) + { + types[static_cast(ObjectInterfaceType::Slotted)] = true; + } + virtual ~CSlottedObject() + {} + + //! Given one of the PSEUDOSLOT enums, returns real slot number, or -1 if specified pseudoslot is not present in this object. + virtual int MapPseudoSlot(Pseudoslot pseudoslot) = 0; + + //! Get number of slots. Valid slot numbers are 0 up to GetNumSlots()-1. Using invalid slot numbers in the other functions will crash the game. + virtual int GetNumSlots() = 0; + //! Get relative position of a slot. + virtual Math::Vector GetSlotPosition(int slotNum) = 0; + //! Get relative angle (in radians) where robot should be positioned when inserting into a slot. + virtual float GetSlotAngle(int slotNum) = 0; + //! Get the maximum angular distance from the ideal angle (in radians) where robot should be positioned when inserting into a slot. + virtual float GetSlotAcceptanceAngle(int slotNum) = 0; + //! Get object contained in a slot. + virtual CObject *GetSlotContainedObject(int slotNum) = 0; + //! Set object contained in a slot. + virtual void SetSlotContainedObject(int slotNum, CObject *object) = 0; + + void SetSlotContainedObjectReq(Pseudoslot pseudoslot, CObject *object) + { + int slotNum = MapPseudoSlot(pseudoslot); + assert(slotNum >= 0); + SetSlotContainedObject(slotNum, object); + } + + CObject *GetSlotContainedObjectOpt(Pseudoslot pseudoslot) + { + int slotNum = MapPseudoSlot(pseudoslot); + return slotNum < 0 ? nullptr : GetSlotContainedObject(slotNum); + } + + CObject *GetSlotContainedObjectReq(Pseudoslot pseudoslot) + { + int slotNum = MapPseudoSlot(pseudoslot); + assert(slotNum >= 0); + return GetSlotContainedObject(slotNum); + } +}; + +inline bool HasPowerCellSlot(CObject *object) +{ + if (object->Implements(ObjectInterfaceType::Slotted)) + { + return dynamic_cast(*object).MapPseudoSlot(CSlottedObject::Pseudoslot::POWER) >= 0; + } + return false; +} + +inline CObject *GetObjectInPowerCellSlot(CObject *object) +{ + if (object->Implements(ObjectInterfaceType::Slotted)) + { + return dynamic_cast(*object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER); + } + return nullptr; +} + +inline CPowerContainerObject *GetObjectPowerCell(CObject *object) +{ + if (CObject *powerSlotObj = GetObjectInPowerCellSlot(object)) + { + if (powerSlotObj->Implements(ObjectInterfaceType::PowerContainer)) + { + return dynamic_cast(powerSlotObj); + } + } + return nullptr; +} + +inline float GetObjectEnergy(CObject* object) +{ + if (CPowerContainerObject* power = GetObjectPowerCell(object)) + return power->GetEnergy(); + return 0.0f; +} + +inline float GetObjectEnergyLevel(CObject* object) +{ + if (CPowerContainerObject* power = GetObjectPowerCell(object)) + return power->GetEnergyLevel(); + return 0.0f; +} + +inline bool ObjectHasPowerCell(CObject* object) +{ + // XXX: not GetObjectPowerCell? We count e.g. titanium cubes as power cells in this function? + return GetObjectInPowerCellSlot(object) != nullptr; +} + +inline bool IsObjectCarryingCargo(CObject* object) +{ + if (object->Implements(ObjectInterfaceType::Slotted)) + { + return dynamic_cast(*object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr; + } + return false; +} diff --git a/src/object/motion/motionhuman.cpp b/src/object/motion/motionhuman.cpp index 906aaab6..150c16db 100644 --- a/src/object/motion/motionhuman.cpp +++ b/src/object/motion/motionhuman.cpp @@ -33,8 +33,6 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" - #include "physics/physics.h" #include "sound/sound.h" diff --git a/src/object/motion/motionvehicle.cpp b/src/object/motion/motionvehicle.cpp index 712ad18a..6b4458bc 100644 --- a/src/object/motion/motionvehicle.cpp +++ b/src/object/motion/motionvehicle.cpp @@ -33,8 +33,8 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" #include "object/interface/programmable_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" @@ -1061,7 +1061,8 @@ void CMotionVehicle::Create(Math::Vector pos, float angle, ObjectType type, type != OBJECT_APOLLO2) { CObject* powerCell = nullptr; - Math::Vector powerCellPos = m_object->GetPowerPosition(); + int powerSlotIndex = m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + Math::Vector powerCellPos = m_object->GetSlotPosition(powerSlotIndex); float powerCellAngle = 0.0f; if (power <= 1.0f) { @@ -1076,8 +1077,7 @@ void CMotionVehicle::Create(Math::Vector pos, float angle, ObjectType type, powerCell->SetPosition(powerCellPos); powerCell->SetRotation(Math::Vector(0.0f, powerCellAngle, 0.0f)); dynamic_cast(*powerCell).SetTransporter(m_object); - assert(m_object->Implements(ObjectInterfaceType::Powered)); - m_object->SetPower(powerCell); + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, powerCell); } pos = m_object->GetPosition(); diff --git a/src/object/object_interface_type.h b/src/object/object_interface_type.h index a6614f54..7cdbd478 100644 --- a/src/object/object_interface_type.h +++ b/src/object/object_interface_type.h @@ -39,8 +39,6 @@ enum class ObjectInterfaceType Programmable, //!< objects that can be programmed in CBOT TaskExecutor, //!< objects that can execute tasks (CTask classes) Jostleable, //!< object that can be jostled - Carrier, //!< object that can carry other objects - Powered, //!< object powered with power cell Movable, //!< objects that can move Flying, //!< objects that can fly JetFlying, //!< objects that can fly using a jet engine @@ -54,6 +52,7 @@ enum class ObjectInterfaceType Shielded, //!< objects that can be destroyed after the shield goes down to 0 ShieldedAutoRegen, //!< shielded objects with auto shield regeneration Old, //!< old objects, TODO: remove once no longer necessary + Slotted, //!< objects that can carry other objects (in their gripper, power cell slot, or other slots) Max //!< maximum value (for getting number of items in enum) }; diff --git a/src/object/old_object.cpp b/src/object/old_object.cpp index cd715aeb..63dcb0f8 100644 --- a/src/object/old_object.cpp +++ b/src/object/old_object.cpp @@ -81,8 +81,7 @@ COldObject::COldObject(int id) CProgramStorageObjectImpl(m_implementedInterfaces, this), CProgrammableObjectImpl(m_implementedInterfaces, this), CJostleableObject(m_implementedInterfaces), - CCarrierObject(m_implementedInterfaces), - CPoweredObject(m_implementedInterfaces), + CSlottedObject(m_implementedInterfaces), CJetFlyingObject(m_implementedInterfaces), CControllableObject(m_implementedInterfaces), CPowerContainerObjectImpl(m_implementedInterfaces), @@ -145,6 +144,8 @@ COldObject::COldObject(int id) m_gunGoalH = 0.0f; m_shieldRadius = 0.0f; m_magnifyDamage = 1.0f; + m_hasPowerSlot = false; + m_hasCargoSlot = false; m_character = Character(); m_character.wheelFront = 1.0f; @@ -774,17 +775,34 @@ void COldObject::SetType(ObjectType type) m_type == OBJECT_MOBILEst || m_type == OBJECT_TOWER || m_type == OBJECT_RESEARCH || - m_type == OBJECT_ENERGY || - m_type == OBJECT_LABO || - m_type == OBJECT_NUCLEAR ) + m_type == OBJECT_ENERGY || // TODO not actually a power cell slot + m_type == OBJECT_LABO || // TODO not actually a power cell slot + m_type == OBJECT_NUCLEAR ) // TODO not actually a power cell slot { - m_implementedInterfaces[static_cast(ObjectInterfaceType::Powered)] = true; + m_hasPowerSlot = true; } else { - m_implementedInterfaces[static_cast(ObjectInterfaceType::Powered)] = false; + m_hasPowerSlot = false; } + if ( m_type == OBJECT_HUMAN || + m_type == OBJECT_TECH || + m_type == OBJECT_MOBILEfa || // Grabbers + m_type == OBJECT_MOBILEta || + m_type == OBJECT_MOBILEwa || + m_type == OBJECT_MOBILEia || + m_type == OBJECT_MOBILEsa) // subber + { + m_hasCargoSlot = true; + } + else + { + m_hasCargoSlot = false; + } + + m_implementedInterfaces[static_cast(ObjectInterfaceType::Slotted)] = (m_hasPowerSlot || m_hasCargoSlot); + // TODO: Hacking some more if ( m_type == OBJECT_MOBILEtg || m_type == OBJECT_STONE || @@ -1662,39 +1680,135 @@ void COldObject::SetMasterParticle(int part, int parti) // Management of the stack transport. -void COldObject::SetPower(CObject* power) -{ - m_power = power; -} -CObject* COldObject::GetPower() +int COldObject::GetNumSlots() { - return m_power; + assert(m_hasPowerSlot || m_hasCargoSlot); // otherwise implemented[CSlottedObject] is false + return (m_hasPowerSlot ? 1 : 0) + (m_hasCargoSlot ? 1 : 0); } +int COldObject::MapPseudoSlot(Pseudoslot pseudoslot) +{ + switch (pseudoslot) + { + case Pseudoslot::POWER: + return m_hasPowerSlot ? 0 : -1; + case Pseudoslot::CARRYING: + return m_hasCargoSlot ? (m_hasPowerSlot ? 1 : 0) : -1; + default: + return -1; + } +} +Math::Vector COldObject::GetSlotPosition(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + return m_powerPosition; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + int grabPartNum; + Math::Vector grabRelPos; + // See CTaskManip::TransporterTakeObject call to SetTransporterPart and SetPosition + switch (m_type) + { + case OBJECT_HUMAN: + case OBJECT_TECH: + grabPartNum = 4; + grabRelPos = Math::Vector(1.7f, -0.5f, 1.1f); + break; + case OBJECT_MOBILEsa: // subber + grabPartNum = 2; + grabRelPos = Math::Vector(1.1f, -1.0f, 1.0f); + break; + case OBJECT_MOBILEfa: // Grabbers + case OBJECT_MOBILEta: + case OBJECT_MOBILEwa: + case OBJECT_MOBILEia: + grabPartNum = 3; + grabRelPos = Math::Vector(4.7f, 0.0f, 0.0f); + break; + default: // unreachable, only the above objects have cargo slots + assert(!m_hasCargoSlot); + return m_powerPosition; + } + return Math::Transform(GetWorldMatrix(0)->Inverse(), Math::Transform(*GetWorldMatrix(grabPartNum), grabRelPos)); + } +} +float COldObject::GetSlotAngle(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + { + switch (m_type) + { + case OBJECT_TOWER: + case OBJECT_RESEARCH: + case OBJECT_ENERGY: + case OBJECT_LABO: + case OBJECT_NUCLEAR: + return 0; + default: // robots + return Math::PI; + } + } + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + return 0; + } +} +float COldObject::GetSlotAcceptanceAngle(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + { + switch (m_type) + { + case OBJECT_TOWER: + case OBJECT_RESEARCH: + return 45.0f*Math::PI/180.0f; + case OBJECT_ENERGY: + return 90.0f*Math::PI/180.0f; + case OBJECT_LABO: + return 120.0f*Math::PI/180.0f; + case OBJECT_NUCLEAR: + return 45.0f*Math::PI/180.0f; + default: + return 45.0f*Math::PI/180.0f; + } + } + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + return 0; // no acceptance angle for cargo slot + } +} +CObject *COldObject::GetSlotContainedObject(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + return m_power; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + return m_cargo; + } +} +void COldObject::SetSlotContainedObject(int slotNum, CObject *object) +{ + if (slotNum == 0 && m_hasPowerSlot) + m_power = object; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + m_cargo = object; + } +} +// not part of CSlottedObject; just used for initialization void COldObject::SetPowerPosition(const Math::Vector& powerPosition) { m_powerPosition = powerPosition; } -Math::Vector COldObject::GetPowerPosition() -{ - return m_powerPosition; -} -// Management of the object transport. - -void COldObject::SetCargo(CObject* cargo) -{ - m_cargo = cargo; -} - -CObject* COldObject::GetCargo() -{ - return m_cargo; -} - // Management of the object "transporter" that transports it. void COldObject::SetTransporter(CObject* transporter) diff --git a/src/object/old_object.h b/src/object/old_object.h index ebcab3a1..2bdbf603 100644 --- a/src/object/old_object.h +++ b/src/object/old_object.h @@ -33,7 +33,6 @@ #include "object/implementation/programmable_impl.h" #include "object/implementation/task_executor_impl.h" -#include "object/interface/carrier_object.h" #include "object/interface/controllable_object.h" #include "object/interface/flying_object.h" #include "object/interface/interactive_object.h" @@ -41,10 +40,10 @@ #include "object/interface/jostleable_object.h" #include "object/interface/movable_object.h" #include "object/interface/power_container_object.h" -#include "object/interface/powered_object.h" #include "object/interface/programmable_object.h" #include "object/interface/ranged_object.h" #include "object/interface/shielded_auto_regen_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/task_executor_object.h" #include "object/interface/trace_drawing_object.h" #include "object/interface/transportable_object.h" @@ -83,8 +82,7 @@ class COldObject : public CObject, public CProgramStorageObjectImpl, public CProgrammableObjectImpl, public CJostleableObject, - public CCarrierObject, - public CPoweredObject, + public CSlottedObject, public CJetFlyingObject, public CControllableObject, public CPowerContainerObjectImpl, @@ -175,12 +173,6 @@ public: void SetMasterParticle(int part, int parti) override; - void SetPower(CObject* power) override; - CObject* GetPower() override; - Math::Vector GetPowerPosition() override; - void SetPowerPosition(const Math::Vector& powerPosition) override; - void SetCargo(CObject* cargo) override; - CObject* GetCargo() override; void SetTransporter(CObject* transporter) override; CObject* GetTransporter() override; void SetTransporterPart(int part) override; @@ -295,6 +287,17 @@ public: void SetBulletWall(bool bulletWall); bool IsBulletWall() override; + // CSlottedObject + int MapPseudoSlot(Pseudoslot pseudoslot) override; + int GetNumSlots() override; + Math::Vector GetSlotPosition(int slotNum) override; + float GetSlotAngle(int slotNum) override; + float GetSlotAcceptanceAngle(int slotNum) override; + CObject *GetSlotContainedObject(int slotNum) override; + void SetSlotContainedObject(int slotNum, CObject *object) override; + // Helper for CSlottedObject initialization + void SetPowerPosition(const Math::Vector& powerPosition); + protected: bool EventFrame(const Event &event); void VirusFrame(float rTime); @@ -393,4 +396,7 @@ protected: float m_traceWidth; bool m_bulletWall = false; + + bool m_hasCargoSlot; + bool m_hasPowerSlot; }; diff --git a/src/object/task/taskbuild.cpp b/src/object/task/taskbuild.cpp index 00680578..1057b51e 100644 --- a/src/object/task/taskbuild.cpp +++ b/src/object/task/taskbuild.cpp @@ -36,7 +36,6 @@ #include "object/auto/auto.h" -#include "object/interface/carrier_object.h" #include "object/interface/transportable_object.h" #include "object/motion/motionhuman.h" diff --git a/src/object/task/taskfire.cpp b/src/object/task/taskfire.cpp index 832703cc..681a012e 100644 --- a/src/object/task/taskfire.cpp +++ b/src/object/task/taskfire.cpp @@ -26,6 +26,8 @@ #include "object/old_object.h" +#include "object/interface/slotted_object.h" + #include "physics/physics.h" #include "sound/sound.h" @@ -42,7 +44,7 @@ CTaskFire::CTaskFire(COldObject* object) : CForegroundTask(object) { m_soundChannel = -1; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(HasPowerCellSlot(m_object)); } // Object's destructor. @@ -78,10 +80,8 @@ bool CTaskFire::EventProcess(const Event &event) m_lastSound -= event.rTime; m_progress += event.rTime*m_speed; - CPowerContainerObject* power = nullptr; - if (m_object->GetPower() != nullptr && m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer)) + if (CPowerContainerObject* power = GetObjectPowerCell(m_object)) { - power = dynamic_cast(m_object->GetPower()); energy = power->GetEnergy(); if ( m_bOrganic ) fire = ENERGY_FIREi; else if ( m_bRay ) fire = ENERGY_FIREr; @@ -313,11 +313,11 @@ Error CTaskFire::Start(float delay) } m_delay = delay; - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* power = dynamic_cast(m_object)->GetPower(); - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_FIRE_ENERGY; + assert(HasPowerCellSlot(m_object)); + CPowerContainerObject *power = GetObjectPowerCell(m_object); + if (power == nullptr) return ERR_FIRE_ENERGY; - energy = dynamic_cast(*power).GetEnergy(); + energy = power->GetEnergy(); if ( m_bOrganic ) fire = m_delay*ENERGY_FIREi; else if ( m_bRay ) fire = m_delay*ENERGY_FIREr; else fire = m_delay*ENERGY_FIRE; diff --git a/src/object/task/taskflag.cpp b/src/object/task/taskflag.cpp index 50789ed7..97768ff6 100644 --- a/src/object/task/taskflag.cpp +++ b/src/object/task/taskflag.cpp @@ -28,8 +28,6 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" - #include "object/motion/motionhuman.h" #include "physics/physics.h" diff --git a/src/object/task/taskgoto.cpp b/src/object/task/taskgoto.cpp index 0964a2e5..6cdaed8c 100644 --- a/src/object/task/taskgoto.cpp +++ b/src/object/task/taskgoto.cpp @@ -33,6 +33,7 @@ #include "object/object_manager.h" #include "object/old_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "object/subclass/base_alien.h" @@ -1201,8 +1202,11 @@ bool CTaskGoto::AdjustTarget(CObject* pObj, Math::Vector &pos, float &distance) type == OBJECT_MOBILEst || type == OBJECT_MOBILEdr ) { - assert(pObj->Implements(ObjectInterfaceType::Powered)); - pos = dynamic_cast(*pObj).GetPowerPosition(); + CSlottedObject *asSlotted = dynamic_cast(pObj); + int powerSlotIndex = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + assert(powerSlotIndex >= 0); + pos = asSlotted->GetSlotPosition(powerSlotIndex); + // TODO: this only works for a certain slot angle pos.x -= TAKE_DIST+TAKE_DIST_OTHER+distance; mat = pObj->GetWorldMatrix(0); pos = Transform(*mat, pos); diff --git a/src/object/task/taskmanip.cpp b/src/object/task/taskmanip.cpp index 6a522806..259f8b31 100644 --- a/src/object/task/taskmanip.cpp +++ b/src/object/task/taskmanip.cpp @@ -30,14 +30,14 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "physics/physics.h" #include "sound/sound.h" +const int INVALID_SLOT = -1; //?const float MARGIN_FRONT = 2.0f; //?const float MARGIN_BACK = 2.0f; @@ -58,7 +58,7 @@ CTaskManip::CTaskManip(COldObject* object) : CForegroundTask(object) m_arm = TMA_NEUTRAL; m_hand = TMH_OPEN; - assert(m_object->Implements(ObjectInterfaceType::Carrier)); + assert(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0); } // Object's destructor. @@ -295,7 +295,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) type = m_object->GetType(); if ( type == OBJECT_BEE ) // bee? { - if (m_object->GetCargo() == nullptr) + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { if ( !m_physics->GetLand() ) return ERR_MANIP_FLY; @@ -303,17 +303,17 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if (other == nullptr) return ERR_MANIP_NIL; assert(other->Implements(ObjectInterfaceType::Transportable)); - m_object->SetCargo(other); // takes the ball + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, other); // takes the ball dynamic_cast(*other).SetTransporter(m_object); dynamic_cast(*other).SetTransporterPart(0); // taken with the base other->SetPosition(Math::Vector(0.0f, -3.0f, 0.0f)); } else { - other = m_object->GetCargo(); // other = ball + other = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); // other = ball assert(other->Implements(ObjectInterfaceType::Transportable)); - m_object->SetCargo(nullptr); // lick the ball + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // lick the ball dynamic_cast(*other).SetTransporter(nullptr); pos = m_object->GetPosition(); pos.y -= 3.0f; @@ -361,7 +361,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if ( order == TMO_AUTO ) { - if (m_object->GetCargo() == nullptr) + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { m_order = TMO_GRAB; } @@ -375,11 +375,11 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) m_order = order; } - if (m_order == TMO_GRAB && m_object->GetCargo() != nullptr) + if (m_order == TMO_GRAB && m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) != nullptr) { return ERR_MANIP_BUSY; } - if (m_order == TMO_DROP && m_object->GetCargo() == nullptr) + if (m_order == TMO_DROP && m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { return ERR_MANIP_EMPTY; } @@ -393,7 +393,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if ( m_arm == TMA_FFRONT ) { front = SearchTakeFrontObject(true, fPos, fDist, fAngle); - other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight); + int slotNum; + other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight, slotNum); if ( front != nullptr && fDist < oDist ) { @@ -403,7 +404,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) } else if ( other != nullptr && oDist < fDist ) { - if (! ObjectHasPowerCell(other)) return ERR_MANIP_NIL; + if (dynamic_cast(*other).GetSlotContainedObject(slotNum) == nullptr) return ERR_MANIP_NIL; m_targetPos = oPos; m_angle = oAngle; m_height = oHeight; @@ -427,8 +428,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) } if ( m_arm == TMA_POWER ) { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - if (m_object->GetPower() == nullptr) return ERR_MANIP_NIL; + assert(HasPowerCellSlot(m_object)); + if (m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER) == nullptr) return ERR_MANIP_NIL; } } @@ -436,8 +437,9 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) { if ( m_arm == TMA_FFRONT ) { - other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight); - if (other != nullptr && !ObjectHasPowerCell(other)) + int slotNum; + other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight, slotNum); + if (other != nullptr && dynamic_cast(*other).GetSlotContainedObject(slotNum) == nullptr) { m_targetPos = oPos; m_angle = oAngle; @@ -456,8 +458,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) } if ( m_arm == TMA_POWER ) { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - if (m_object->GetPower() != nullptr) return ERR_MANIP_OCC; + assert(HasPowerCellSlot(m_object)); + if (m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER) != nullptr) return ERR_MANIP_OCC; } } @@ -477,7 +479,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) if ( m_timeLimit < 0.5f ) m_timeLimit = 0.5f; } - if (m_object->GetCargo() == nullptr) // not carrying anything? + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) // not carrying anything? { m_hand = TMH_OPEN; // open clamp } @@ -602,7 +604,7 @@ Error CTaskManip::IsEnded() { if ( m_bSubm ) m_speed = 1.0f/1.5f; if ( !TransporterTakeObject() && - m_object->GetCargo() == nullptr) + m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) { m_hand = TMH_OPEN; // reopens the clamp m_arm = TMA_NEUTRAL; @@ -613,7 +615,7 @@ Error CTaskManip::IsEnded() { if ( (m_arm == TMA_OTHER || m_arm == TMA_POWER ) && - m_object->GetCargo()->Implements(ObjectInterfaceType::PowerContainer) ) + m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING)->Implements(ObjectInterfaceType::PowerContainer) ) { m_sound->Play(SOUND_POWEROFF, m_object->GetPosition()); } @@ -630,7 +632,7 @@ Error CTaskManip::IsEnded() if ( m_step == 1 ) { if ( m_bSubm ) m_speed = 1.0f/0.7f; - cargo = m_object->GetCargo(); + cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (TransporterDeposeObject()) { if ( (m_arm == TMA_OTHER || @@ -667,7 +669,7 @@ Error CTaskManip::IsEnded() bool CTaskManip::Abort() { - if (m_object->GetCargo() == nullptr) // not carrying anything? + if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr) // not carrying anything? { m_hand = TMH_OPEN; // open clamp m_arm = TMA_NEUTRAL; @@ -868,10 +870,12 @@ CObject* CTaskManip::SearchTakeBackObject(bool bAdvance, Math::Vector &pos, CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, - float &height) + float &height, int &slotNumOut) { + slotNumOut = INVALID_SLOT; + Math::Matrix* mat; - float iAngle, oAngle, oLimit, aLimit, dLimit; + float iAngle, aLimit, dLimit; distance = 1000000.0f; angle = 0.0f; @@ -900,56 +904,45 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, { if ( pObj == m_object ) continue; // yourself? - ObjectType type = pObj->GetType(); - if ( !pObj->Implements(ObjectInterfaceType::Powered) ) continue; - - CObject* power = dynamic_cast(*pObj).GetPower(); - if (power != nullptr) + if (pObj->Implements(ObjectInterfaceType::Slotted)) { - if (power->GetLock()) continue; - if (power->GetScaleY() != 1.0f) continue; - } - - mat = pObj->GetWorldMatrix(0); - Math::Vector oPos = Transform(*mat, dynamic_cast(*pObj).GetPowerPosition()); - - oAngle = pObj->GetRotationY(); - if ( type == OBJECT_TOWER || - type == OBJECT_RESEARCH ) - { - oLimit = 45.0f*Math::PI/180.0f; - } - else if ( type == OBJECT_ENERGY ) - { - oLimit = 90.0f*Math::PI/180.0f; - } - else if ( type == OBJECT_LABO ) - { - oLimit = 120.0f*Math::PI/180.0f; - } - else if ( type == OBJECT_NUCLEAR ) - { - oLimit = 45.0f*Math::PI/180.0f; - } - else - { - oLimit = 45.0f*Math::PI/180.0f; - oAngle += Math::PI; // is behind - } - oAngle = Math::NormAngle(oAngle); // 0..2*Math::PI - angle = Math::RotateAngle(iPos.x-oPos.x, oPos.z-iPos.z); // CW ! - if ( !Math::TestAngle(angle, oAngle-oLimit, oAngle+oLimit) ) continue; - - distance = fabs(Math::Distance(oPos, iPos)-TAKE_DIST); - if ( distance <= dLimit ) - { - angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! - if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + CSlottedObject &obj = dynamic_cast(*pObj); + int slotNum = obj.GetNumSlots(); + for (int slot = 0; slot < slotNum; slot++) { - Math::Vector powerPos = dynamic_cast(*pObj).GetPowerPosition(); - height = powerPos.y; - pos = oPos; - return pObj; + mat = pObj->GetWorldMatrix(0); + Math::Vector worldSlotPos = Transform(*mat, obj.GetSlotPosition(slot)); + + CObject *objectInSlot = obj.GetSlotContainedObject(slot); + if (objectInSlot != nullptr && (objectInSlot->GetLock() || objectInSlot->GetScaleY() != 1.0f)) + continue; + + float objectAngleOffsetLimit = obj.GetSlotAcceptanceAngle(slot); + if(objectAngleOffsetLimit == 0) + continue; // slot isn't take-able + + // The robot must be in the correct angle relative to the slot (it can't be on the other side of the object) + float angleFromObjectToRobot = Math::RotateAngle(iPos.x-worldSlotPos.x, worldSlotPos.z-iPos.z); // CW ! + float objectIdealAngle = Math::NormAngle(pObj->GetRotationY() + obj.GetSlotAngle(slot)); + + if ( Math::TestAngle(angleFromObjectToRobot, objectIdealAngle - objectAngleOffsetLimit, objectIdealAngle + objectAngleOffsetLimit) ) + { + distance = fabs(Math::Distance(worldSlotPos, iPos)-TAKE_DIST); + // The robot must be close enough to the slot + if ( distance <= dLimit ) + { + // The slot must be in the correct position relative to the robot (the robot must be facing towards the slot, not sideways or away) + angle = Math::RotateAngle(worldSlotPos.x-iPos.x, iPos.z-worldSlotPos.z); // CW ! + if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + Math::Vector powerPos = obj.GetSlotPosition(slot); + height = powerPos.y; + pos = worldSlotPos; + slotNumOut = slot; + return pObj; + } + } + } } } } @@ -965,7 +958,7 @@ bool CTaskManip::TransporterTakeObject() { if (m_arm == TMA_GRAB) // takes immediately? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing to take? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1005,7 +998,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationY(0.0f); } - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_FFRONT) // takes on the ground in front? @@ -1041,7 +1034,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationY(0.0f); } - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_FBACK) // takes on the ground behind? @@ -1063,13 +1056,13 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationY(0.0f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_POWER) // takes battery in the back? { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = m_object->GetPower(); + assert(m_object->Implements(ObjectInterfaceType::Slotted)); + CObject* cargo = m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER); if (cargo == nullptr) return false; // no battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1082,25 +1075,25 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationY(0.0f); dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand - m_object->SetPower(nullptr); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, nullptr); + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TMA_OTHER) // battery takes from friend? { Math::Vector pos; float dist = 0.0f, angle = 0.0f; - CObject* other = SearchOtherObject(false, pos, dist, angle, m_height); + int slotNum; + CObject* other = SearchOtherObject(false, pos, dist, angle, m_height, slotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); - - CObject* cargo = dynamic_cast(*other).GetPower(); + assert(slotNum != INVALID_SLOT); + CObject *cargo = dynamic_cast(*other).GetSlotContainedObject(slotNum); if (cargo == nullptr) return false; // the other does not have a battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(nullptr); + dynamic_cast(*other).SetSlotContainedObject(slotNum, nullptr); dynamic_cast(*cargo).SetTransporter(m_object); dynamic_cast(*cargo).SetTransporterPart(3); // takes with the hand @@ -1110,7 +1103,7 @@ bool CTaskManip::TransporterTakeObject() cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationY(0.0f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } return true; @@ -1122,7 +1115,7 @@ bool CTaskManip::TransporterDeposeObject() { if (m_arm == TMA_FFRONT) // deposits on the ground in front? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing transported? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1138,12 +1131,12 @@ bool CTaskManip::TransporterDeposeObject() cargo->FloorAdjust(); // plate well on the ground dynamic_cast(*cargo).SetTransporter(nullptr); - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } if (m_arm == TMA_FBACK) // deposited on the ground behind? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing transported? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -1158,30 +1151,32 @@ bool CTaskManip::TransporterDeposeObject() cargo->SetRotationZ(0.0f); dynamic_cast(*cargo).SetTransporter(nullptr); - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } if (m_arm == TMA_POWER) // deposits battery in the back? { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* cargo = m_object->GetCargo(); + assert(m_object->Implements(ObjectInterfaceType::Slotted)); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // nothing transported? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - if (m_object->GetPower() != nullptr) return false; + int powerSlotIndex = m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + assert(powerSlotIndex >= 0); + if (m_object->GetSlotContainedObject(powerSlotIndex) != nullptr) return false; dynamic_cast(*cargo).SetTransporter(m_object); dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base - cargo->SetPosition(m_object->GetPowerPosition()); + cargo->SetPosition(m_object->GetSlotPosition(powerSlotIndex)); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); - m_object->SetPower(cargo); // uses - m_object->SetCargo(nullptr); + m_object->SetSlotContainedObject(powerSlotIndex, cargo); // uses + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); } if (m_arm == TMA_OTHER) // deposits battery on friend? @@ -1189,29 +1184,30 @@ bool CTaskManip::TransporterDeposeObject() Math::Vector pos; float angle = 0.0f, dist = 0.0f; - CObject* other = SearchOtherObject(false, pos, dist, angle, m_height); + int slotNum; + CObject* other = SearchOtherObject(false, pos, dist, angle, m_height, slotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); + assert(slotNum != INVALID_SLOT); + CSlottedObject *otherAsSlotted = dynamic_cast(other); + if (otherAsSlotted->GetSlotContainedObject(slotNum) != nullptr) return false; // the other already has a battery? - CObject* cargo = dynamic_cast(*other).GetPower(); - if (cargo != nullptr) return false; // the other already has a battery? - - cargo = m_object->GetCargo(); + CObject *cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(cargo); + otherAsSlotted->SetSlotContainedObject(slotNum, cargo); dynamic_cast(*cargo).SetTransporter(other); - cargo->SetPosition(dynamic_cast(*other).GetPowerPosition()); + // TODO: isn't this wrong? PowerPosition (and SlotContainedPosition) is an object-local position. + cargo->SetPosition(otherAsSlotted->GetSlotPosition(slotNum)); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } return true; diff --git a/src/object/task/taskmanip.h b/src/object/task/taskmanip.h index 6326caf2..da903c4e 100644 --- a/src/object/task/taskmanip.h +++ b/src/object/task/taskmanip.h @@ -73,7 +73,7 @@ protected: CObject* SearchTakeUnderObject(Math::Vector &pos, float dLimit); CObject* SearchTakeFrontObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle); CObject* SearchTakeBackObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle); - CObject* SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, float &height); + CObject* SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, float &height, int &slotNumOut); bool TransporterTakeObject(); bool TransporterDeposeObject(); bool IsFreeDeposeObject(Math::Vector pos); diff --git a/src/object/task/taskrecover.cpp b/src/object/task/taskrecover.cpp index 47c89d0a..45892da3 100644 --- a/src/object/task/taskrecover.cpp +++ b/src/object/task/taskrecover.cpp @@ -31,7 +31,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "physics/physics.h" @@ -108,11 +108,9 @@ bool CTaskRecover::EventProcess(const Event &event) if ( m_phase == TRP_OPER ) { - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* powerObj = dynamic_cast(m_object)->GetPower(); - if (powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) + assert(HasPowerCellSlot(m_object)); + if (CPowerContainerObject* power = GetObjectPowerCell(m_object)) { - CPowerContainerObject* power = dynamic_cast(powerObj); energy = power->GetEnergy(); energy -= event.rTime * ENERGY_RECOVER * m_speed; power->SetEnergy(energy); @@ -190,11 +188,10 @@ Error CTaskRecover::Start() ObjectType type = m_object->GetType(); if ( type != OBJECT_MOBILErr ) return ERR_WRONG_BOT; - assert(m_object->Implements(ObjectInterfaceType::Powered)); - CObject* power = dynamic_cast(m_object)->GetPower(); - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_RECOVER_ENERGY; + CPowerContainerObject *power = GetObjectPowerCell(m_object); + if (power == nullptr) return ERR_RECOVER_ENERGY; - float energy = dynamic_cast(*power).GetEnergy(); + float energy = power->GetEnergy(); if ( energy < ENERGY_RECOVER+0.05f ) return ERR_RECOVER_ENERGY; Math::Matrix* mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/taskshield.cpp b/src/object/task/taskshield.cpp index faabe85a..af936761 100644 --- a/src/object/task/taskshield.cpp +++ b/src/object/task/taskshield.cpp @@ -32,7 +32,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/subclass/shielder.h" @@ -54,7 +54,7 @@ CTaskShield::CTaskShield(COldObject* object) : CBackgroundTask(object) m_soundChannel = -1; m_effectLight = -1; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(HasPowerCellSlot(m_object)); m_shielder = dynamic_cast(object); } @@ -118,12 +118,8 @@ bool CTaskShield::EventProcess(const Event &event) { energy = (1.0f/ENERGY_TIME)*event.rTime; energy *= GetRadius()/RADIUS_SHIELD_MAX; - CObject* powerObj = dynamic_cast(m_object)->GetPower(); - if (powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer)) - { - CPowerContainerObject* power = dynamic_cast(powerObj); + if (CPowerContainerObject *power = GetObjectPowerCell(m_object)) power->SetEnergy(power->GetEnergy()-energy); - } m_energyUsed += energy; if ( m_soundChannel == -1 ) @@ -307,9 +303,9 @@ Error CTaskShield::Start(TaskShieldMode mode, float delay) m_bError = true; // operation impossible if ( !m_physics->GetLand() ) return ERR_WRONG_BOT; - CObject* power = m_object->GetPower(); - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_SHIELD_ENERGY; - float energy = dynamic_cast(*power).GetEnergy(); + CPowerContainerObject* power = GetObjectPowerCell(m_object); + if (power == nullptr) return ERR_SHIELD_ENERGY; + float energy = power->GetEnergy(); if ( energy == 0.0f ) return ERR_SHIELD_ENERGY; Math::Matrix* mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/tasktake.cpp b/src/object/task/tasktake.cpp index a586e17d..14d53236 100644 --- a/src/object/task/tasktake.cpp +++ b/src/object/task/tasktake.cpp @@ -30,8 +30,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "object/motion/motionhuman.h" @@ -49,7 +48,7 @@ CTaskTake::CTaskTake(COldObject* object) : CForegroundTask(object) { m_arm = TTA_NEUTRAL; - assert(m_object->Implements(ObjectInterfaceType::Carrier)); + assert(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0); } // Object's destructor. @@ -116,7 +115,7 @@ Error CTaskTake::Start() m_physics->SetMotorSpeed(Math::Vector(0.0f, 0.0f, 0.0f)); - if (m_object->IsCarryingCargo()) + if (m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr) m_order = TTO_DEPOSE; else m_order = TTO_TAKE; @@ -128,12 +127,14 @@ Error CTaskTake::Start() float h = m_water->GetLevel(m_object); if ( pos.y < h ) return ERR_MANIP_WATER; // impossible under water - CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); - if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f, otherSlotNum); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(other == nullptr || otherSlotNum >= 0); - if (other != nullptr && dynamic_cast(*other).GetPower() != nullptr) + if (other != nullptr && otherAsSlotted->GetSlotContainedObject(otherSlotNum) != nullptr) { - CObject* power = dynamic_cast(*other).GetPower(); + CObject* power = otherAsSlotted->GetSlotContainedObject(otherSlotNum); type = power->GetType(); if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO; assert(power->Implements(ObjectInterfaceType::Transportable)); @@ -158,10 +159,12 @@ Error CTaskTake::Start() //? if ( speed.x != 0.0f || //? speed.z != 0.0f ) return ERR_MANIP_MOTOR; - CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); - if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f, otherSlotNum); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(other == nullptr || otherSlotNum >= 0); - if (other != nullptr && dynamic_cast(*other).GetPower() == nullptr ) + if (other != nullptr && otherAsSlotted->GetSlotContainedObject(otherSlotNum) == nullptr) { //? m_camera->StartCentering(m_object, Math::PI*0.3f, -Math::PI*0.1f, 0.0f, 0.8f); m_arm = TTA_FRIEND; @@ -233,7 +236,7 @@ Error CTaskTake::IsEnded() if ( TransporterTakeObject() ) { if ( m_arm == TTA_FRIEND && - m_object->GetCargo()->Implements(ObjectInterfaceType::PowerContainer) ) + m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING)->Implements(ObjectInterfaceType::PowerContainer) ) { m_sound->Play(SOUND_POWEROFF, m_object->GetPosition()); } @@ -250,7 +253,7 @@ Error CTaskTake::IsEnded() { if ( m_step == 1 ) { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); TransporterDeposeObject(); if ( m_arm == TTA_FRIEND && cargo->Implements(ObjectInterfaceType::PowerContainer) ) @@ -334,7 +337,8 @@ CObject* CTaskTake::SearchTakeObject(float &angle, // Seeks the robot on which you want take or put a battery. CObject* CTaskTake::SearchFriendObject(float &angle, - float dLimit, float aLimit) + float dLimit, float aLimit, + int &slotNumOut) { if (m_object->GetCrashSphereCount() == 0) return nullptr; @@ -348,67 +352,44 @@ CObject* CTaskTake::SearchFriendObject(float &angle, for (CObject* pObj : CObjectManager::GetInstancePointer()->GetAllObjects()) { if ( pObj == m_object ) continue; // yourself? + if (!pObj->Implements(ObjectInterfaceType::Slotted)) continue; - ObjectType type = pObj->GetType(); - if ( type != OBJECT_MOBILEfa && - type != OBJECT_MOBILEta && - type != OBJECT_MOBILEwa && - type != OBJECT_MOBILEia && - type != OBJECT_MOBILEfb && - type != OBJECT_MOBILEtb && - type != OBJECT_MOBILEwb && - type != OBJECT_MOBILEib && - type != OBJECT_MOBILEfc && - type != OBJECT_MOBILEtc && - type != OBJECT_MOBILEwc && - type != OBJECT_MOBILEic && - type != OBJECT_MOBILEfi && - type != OBJECT_MOBILEti && - type != OBJECT_MOBILEwi && - type != OBJECT_MOBILEii && - type != OBJECT_MOBILEfs && - type != OBJECT_MOBILEts && - type != OBJECT_MOBILEws && - type != OBJECT_MOBILEis && - type != OBJECT_MOBILErt && - type != OBJECT_MOBILErc && - type != OBJECT_MOBILErr && - type != OBJECT_MOBILErs && - type != OBJECT_MOBILEsa && - type != OBJECT_MOBILEtg && - type != OBJECT_MOBILEft && - type != OBJECT_MOBILEtt && - type != OBJECT_MOBILEwt && - type != OBJECT_MOBILEit && - type != OBJECT_MOBILErp && - type != OBJECT_MOBILEst && - type != OBJECT_TOWER && - type != OBJECT_RESEARCH && - type != OBJECT_ENERGY && - type != OBJECT_LABO && - type != OBJECT_NUCLEAR ) continue; + CSlottedObject *obj = dynamic_cast(pObj); - assert(pObj->Implements(ObjectInterfaceType::Powered)); - - CObject* power = dynamic_cast(*pObj).GetPower(); - if (power != nullptr) + int slotNum = obj->GetNumSlots(); + for (int slot = 0; slot < slotNum; slot++) { - if ( power->GetLock() ) continue; - if ( power->GetScaleY() != 1.0f ) continue; - } + CObject *objectInSlot = obj->GetSlotContainedObject(slot); + if (objectInSlot != nullptr && (objectInSlot->GetLock() || objectInSlot->GetScaleY() != 1.0f)) + continue; - Math::Matrix* mat = pObj->GetWorldMatrix(0); - Math::Vector oPos = Math::Transform(*mat, dynamic_cast(*pObj).GetPowerPosition()); + float objectAngleOffsetLimit = obj->GetSlotAcceptanceAngle(slot); + if (objectAngleOffsetLimit == 0) + continue; // slot isn't take-able - float distance = fabs(Math::Distance(oPos, iPos) - (iRad+1.0f)); - if ( distance <= dLimit ) - { - angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! - if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + Math::Matrix* mat = pObj->GetWorldMatrix(0); + Math::Vector worldSlotPos = Transform(*mat, obj->GetSlotPosition(slot)); + + // The robot must be in the correct angle relative to the slot (it can't be on the other side of the object) + float angleFromObjectToRobot = Math::RotateAngle(iPos.x-worldSlotPos.x, worldSlotPos.z-iPos.z); // CW ! + float objectIdealAngle = Math::NormAngle(pObj->GetRotationY() + obj->GetSlotAngle(slot)); + + if ( Math::TestAngle(angleFromObjectToRobot, objectIdealAngle - objectAngleOffsetLimit, objectIdealAngle + objectAngleOffsetLimit) ) { - Math::Vector powerPos = dynamic_cast(*pObj).GetPowerPosition(); - m_height = powerPos.y; - return pObj; + float distance = fabs(Math::Distance(worldSlotPos, iPos)-(iRad + 1.0f)); + // The robot must be close enough to the slot + if ( distance <= dLimit ) + { + // The slot must be in the correct position relative to the robot (the robot must be facing towards the slot, not sideways or away) + angle = Math::RotateAngle(worldSlotPos.x-iPos.x, iPos.z-worldSlotPos.z); // CW ! + if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) + { + Math::Vector powerPos = obj->GetSlotPosition(slot); + m_height = powerPos.y; + slotNumOut = slot; + return pObj; + } + } } } } @@ -439,23 +420,25 @@ bool CTaskTake::TransporterTakeObject() cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.8f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } if (m_arm == TTA_FRIEND) // takes friend's battery? { float angle = 0.0f; - CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f, otherSlotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(otherSlotNum >= -1); - CObject* cargo = dynamic_cast(*other).GetPower(); + CObject* cargo = otherAsSlotted->GetSlotContainedObject(otherSlotNum); if (cargo == nullptr) return false; // the other does not have a battery? assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(nullptr); + otherAsSlotted->SetSlotContainedObject(otherSlotNum, nullptr); dynamic_cast(*cargo).SetTransporter(m_object); dynamic_cast(*cargo).SetTransporterPart(4); // takes with the hand @@ -465,7 +448,7 @@ bool CTaskTake::TransporterTakeObject() cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.8f); - m_object->SetCargo(cargo); // takes + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes } return true; @@ -477,7 +460,7 @@ bool CTaskTake::TransporterDeposeObject() { if ( m_arm == TTA_FFRONT ) // deposes on the ground in front? { - CObject* cargo = m_object->GetCargo(); + CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; // does nothing? assert(cargo->Implements(ObjectInterfaceType::Transportable)); @@ -493,34 +476,36 @@ bool CTaskTake::TransporterDeposeObject() cargo->FloorAdjust(); // plate well on the ground dynamic_cast(*cargo).SetTransporter(nullptr); - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } if ( m_arm == TTA_FRIEND ) // deposes battery on friends? { float angle = 0.0f; - CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f); + int otherSlotNum = -1; + CObject* other = SearchFriendObject(angle, 1.5f, Math::PI*0.04f, otherSlotNum); if (other == nullptr) return false; - assert(other->Implements(ObjectInterfaceType::Powered)); + CSlottedObject* otherAsSlotted = dynamic_cast(other); + assert(otherSlotNum >= 0); - CObject* cargo = dynamic_cast(*other).GetPower(); + CObject* cargo = otherAsSlotted->GetSlotContainedObject(otherSlotNum); if (cargo != nullptr) return false; // the other already has a battery? - cargo = m_object->GetCargo(); + cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) return false; assert(cargo->Implements(ObjectInterfaceType::Transportable)); m_cargoType = cargo->GetType(); - dynamic_cast(*other).SetPower(cargo); + otherAsSlotted->SetSlotContainedObject(otherSlotNum, cargo); dynamic_cast(*cargo).SetTransporter(other); - cargo->SetPosition(dynamic_cast(*other).GetPowerPosition()); + cargo->SetPosition(otherAsSlotted->GetSlotPosition(otherSlotNum)); cargo->SetRotationY(0.0f); cargo->SetRotationX(0.0f); cargo->SetRotationZ(0.0f); dynamic_cast(*cargo).SetTransporterPart(0); // carried by the base - m_object->SetCargo(nullptr); // deposit + m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit } return true; diff --git a/src/object/task/tasktake.h b/src/object/task/tasktake.h index 2948987a..dd9c5d0b 100644 --- a/src/object/task/tasktake.h +++ b/src/object/task/tasktake.h @@ -57,7 +57,7 @@ public: protected: CObject* SearchTakeObject(float &angle, float dLimit, float aLimit); - CObject* SearchFriendObject(float &angle, float dLimit, float aLimit); + CObject* SearchFriendObject(float &angle, float dLimit, float aLimit, int &slotNumOut); bool TransporterTakeObject(); bool TransporterDeposeObject(); bool IsFreeDeposeObject(Math::Vector pos); diff --git a/src/object/task/taskterraform.cpp b/src/object/task/taskterraform.cpp index 6467dee0..1a2b4eac 100644 --- a/src/object/task/taskterraform.cpp +++ b/src/object/task/taskterraform.cpp @@ -30,7 +30,7 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/motion/motionant.h" #include "object/motion/motionspider.h" @@ -54,7 +54,7 @@ CTaskTerraform::CTaskTerraform(COldObject* object) : CForegroundTask(object) m_lastParticle = 0.0f; m_soundChannel = -1; - assert(m_object->Implements(ObjectInterfaceType::Powered)); + assert(m_object->GetNumSlots() == 1); } // Object's destructor. @@ -97,7 +97,7 @@ bool CTaskTerraform::EventProcess(const Event &event) m_object->SetScale(1.0f+m_progress*0.2f); - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power != nullptr) { power->SetScale(1.0f+m_progress*1.0f); @@ -213,7 +213,7 @@ Error CTaskTerraform::Start() type = m_object->GetType(); if ( type != OBJECT_MOBILErt ) return ERR_WRONG_BOT; - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if ( power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer) ) return ERR_TERRA_ENERGY; energy = dynamic_cast(*power).GetEnergy(); if ( energy < ENERGY_TERRA+0.05f ) return ERR_TERRA_ENERGY; @@ -269,7 +269,7 @@ Error CTaskTerraform::IsEnded() m_object->SetCirVibration(Math::Vector(0.0f, 0.0f, 0.0f)); m_object->SetScale(1.0f); - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power != nullptr) { power->SetScale(1.0f); @@ -335,7 +335,7 @@ bool CTaskTerraform::Abort() m_object->SetCirVibration(Math::Vector(0.0f, 0.0f, 0.0f)); m_object->SetScale(1.0f); - power = m_object->GetPower(); + power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power != nullptr) { power->SetScale(1.0f); diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 0ec79189..77aac2dc 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -43,9 +43,8 @@ #include "object/object_manager.h" #include "object/old_object.h" -#include "object/interface/carrier_object.h" #include "object/interface/jostleable_object.h" -#include "object/interface/powered_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/transportable_object.h" #include "object/motion/motion.h" @@ -802,9 +801,9 @@ void CPhysics::MotorUpdate(float aTime, float rTime) } } - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (HasPowerCellSlot(m_object)) { - power = dynamic_cast(dynamic_cast(*m_object).GetPower()); // searches for the object battery uses + power = GetObjectPowerCell(m_object); // searches for the object battery uses if ( GetObjectEnergy(m_object) == 0.0f ) // no battery or flat? { motorSpeed.x = 0.0f; @@ -2966,10 +2965,11 @@ void CPhysics::PowerParticle(float factor, bool bBreak) Math::Point dim; bool bCarryPower; + // TODO: it should be all slots that contain a power cell that can get recharged. Not just the carrying slot. bCarryPower = false; - if (m_object->Implements(ObjectInterfaceType::Carrier)) + if (m_object->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*m_object).GetCargo(); + CObject* cargo = dynamic_cast(*m_object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING); if ( cargo != nullptr && cargo->Implements(ObjectInterfaceType::PowerContainer) && dynamic_cast(*cargo).IsRechargeable() && m_object->GetPartRotationZ(1) == ARM_STOCK_ANGLE1 ) @@ -2980,7 +2980,7 @@ void CPhysics::PowerParticle(float factor, bool bBreak) mat = m_object->GetWorldMatrix(0); - pos = m_object->GetPowerPosition(); + pos = m_object->GetSlotPosition(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER)); pos.x -= 0.3f; pos.y += 1.0f; // battery center position pos = Transform(*mat, pos); @@ -3784,16 +3784,16 @@ Error CPhysics::GetError() } } - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (HasPowerCellSlot(m_object)) { - CObject* power = dynamic_cast(*m_object).GetPower(); // searches for the object battery used - if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) + CPowerContainerObject* power = GetObjectPowerCell(m_object); // searches for the object battery used + if (power == nullptr) { return ERR_VEH_POWER; } else { - if ( dynamic_cast(*power).GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; + if ( power->GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; } } diff --git a/src/script/scriptfunc.cpp b/src/script/scriptfunc.cpp index 41952603..29277210 100644 --- a/src/script/scriptfunc.cpp +++ b/src/script/scriptfunc.cpp @@ -744,15 +744,18 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo } else { - if (obj->Implements(ObjectInterfaceType::Old)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - COldObject* oldobj = dynamic_cast(obj); - if (oldobj->GetPower() != nullptr) - CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetPower()); - if (oldobj->GetCargo() != nullptr) - CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetCargo()); - oldobj->SetPower(nullptr); - oldobj->SetCargo(nullptr); + CSlottedObject* slotted = dynamic_cast(obj); + for (int slotNum = slotted->GetNumSlots() - 1; slotNum >= 0; slotNum--) + { + CObject* sub = slotted->GetSlotContainedObject(slotNum); + if (sub != nullptr) + { + slotted->SetSlotContainedObject(slotNum, nullptr); + CObjectManager::GetInstancePointer()->DeleteObject(sub); + } + } } CObjectManager::GetInstancePointer()->DeleteObject(obj); } @@ -3686,9 +3689,10 @@ void CScriptFunctions::uObject(CBotVar* botThis, void* user) // Updates the type of battery. pVar = pVar->GetNext(); // "energyCell" - if (object->Implements(ObjectInterfaceType::Powered)) + CSlottedObject *asSlotted = object->Implements(ObjectInterfaceType::Slotted) ? dynamic_cast(object) : nullptr; + if (asSlotted != nullptr && asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER) >= 0) { - CObject* power = dynamic_cast(object)->GetPower(); + CObject *power = asSlotted->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER); if (power == nullptr) { pVar->SetPointer(nullptr); @@ -3701,9 +3705,9 @@ void CScriptFunctions::uObject(CBotVar* botThis, void* user) // Updates the transported object's type. pVar = pVar->GetNext(); // "load" - if (object->Implements(ObjectInterfaceType::Carrier)) + if (asSlotted != nullptr && asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0) { - CObject* cargo = dynamic_cast(object)->GetCargo(); + CObject* cargo = asSlotted->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); if (cargo == nullptr) { pVar->SetPointer(nullptr); diff --git a/src/ui/object_interface.cpp b/src/ui/object_interface.cpp index dbfdefcf..96427272 100644 --- a/src/ui/object_interface.cpp +++ b/src/ui/object_interface.cpp @@ -35,9 +35,8 @@ #include "object/old_object.h" -#include "object/interface/carrier_object.h" -#include "object/interface/powered_object.h" #include "object/interface/programmable_object.h" +#include "object/interface/slotted_object.h" #include "object/interface/task_executor_object.h" #include "object/motion/motion.h" @@ -1373,7 +1372,7 @@ bool CObjectInterface::CreateInterface(bool bSelect) pw->CreateButton(pos, dim, 10, EVENT_OBJECT_DESELECT); } - if ( m_object->Implements(ObjectInterfaceType::Powered) ) // vehicle? + if ( HasPowerCellSlot(m_object) ) // vehicle? { pos.x = ox+sx*14.5f; pos.y = oy+sy*0; @@ -1576,15 +1575,10 @@ void CObjectInterface::UpdateInterface(float rTime) float energy = 0.0f; float limit = 0.0f; - if (m_object->Implements(ObjectInterfaceType::Powered)) + if (CPowerContainerObject* powerContainer = GetObjectPowerCell(m_object)) { - CObject* power = dynamic_cast(m_object)->GetPower(); - if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) - { - CPowerContainerObject* powerContainer = dynamic_cast(power); - energy = powerContainer->GetEnergyLevel(); - limit = powerContainer->GetEnergy(); - } + energy = powerContainer->GetEnergyLevel(); + limit = powerContainer->GetEnergy(); } icon = 0; // red/green @@ -1909,7 +1903,7 @@ void CObjectInterface::UpdateInterface() bFly = bEnable; if ( bFly && (type == OBJECT_HUMAN || type == OBJECT_TECH) ) { - if (m_object->Implements(ObjectInterfaceType::Carrier) && dynamic_cast(m_object)->IsCarryingCargo()) + if (dynamic_cast(*m_object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr) bFly = false; } EnableInterface(pw, EVENT_OBJECT_GASUP, bFly && m_main->CanPlayerInteract()); From eff8e885d5956fb871df8f7b131ccb19d8b23eb4 Mon Sep 17 00:00:00 2001 From: tomangelo2 Date: Sun, 30 Jan 2022 20:40:04 +0100 Subject: [PATCH 7/7] Fix assert fail after PR#1444 --- src/object/auto/autopowercaptor.cpp | 2 +- src/object/auto/autopowerstation.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/object/auto/autopowercaptor.cpp b/src/object/auto/autopowercaptor.cpp index a292b82f..36db524c 100644 --- a/src/object/auto/autopowercaptor.cpp +++ b/src/object/auto/autopowercaptor.cpp @@ -269,7 +269,7 @@ void CAutoPowerCaptor::ChargeObject(float rTime) if (obj->Implements(ObjectInterfaceType::Slotted)) { CSlottedObject* slotted = dynamic_cast(obj); - for (int slot = slotted->GetNumSlots(); slot >= 0; slot--) + for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--) { CObject *held = slotted->GetSlotContainedObject(slot); if (held != nullptr && held->Implements(ObjectInterfaceType::PowerContainer)) diff --git a/src/object/auto/autopowerstation.cpp b/src/object/auto/autopowerstation.cpp index c479f589..793f3572 100644 --- a/src/object/auto/autopowerstation.cpp +++ b/src/object/auto/autopowerstation.cpp @@ -134,7 +134,7 @@ bool CAutoPowerStation::EventProcess(const Event &event) if (vehicle != nullptr && vehicle->Implements(ObjectInterfaceType::Slotted)) { CSlottedObject* slotted = dynamic_cast(vehicle); - for (int slot = slotted->GetNumSlots(); slot >= 0; slot--) + for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--) { CObject *power = slotted->GetSlotContainedObject(slot); if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))