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/CMakeLists.txt b/CMakeLists.txt index 9faa5877..099edc8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -445,11 +445,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) 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() diff --git a/data b/data index 21a45c0b..8ad5e916 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 21a45c0b8809accd142a83a81f1a3c92a327f319 +Subproject commit 8ad5e916e353094242823f716ea45b3df2cdfe5f 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 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/src/CMakeLists.txt b/src/CMakeLists.txt index cce3b78f..366f36ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -243,7 +243,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 @@ -254,12 +253,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 7198bf70..de730431 100644 --- a/src/graphics/engine/camera.cpp +++ b/src/graphics/engine/camera.cpp @@ -38,10 +38,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" @@ -58,18 +57,15 @@ static void SetGhostMode(CObject* obj, bool enabled) { obj->SetGhostMode(enabled); - if (obj->Implements(ObjectInterfaceType::Carrier)) + if (obj->Implements(ObjectInterfaceType::Slotted)) { - CObject* cargo = dynamic_cast(*obj).GetCargo(); - if (cargo != nullptr) - cargo->SetGhostMode(enabled); - } - - if (obj->Implements(ObjectInterfaceType::Powered)) - { - CObject* power = dynamic_cast(*obj).GetPower(); - if (power != nullptr) - power->SetGhostMode(enabled); + CSlottedObject *slotted = dynamic_cast(obj); + for(int slot = slotted->GetNumSlots()-1; slot >= 0; slot--) + { + CObject *contained = slotted->GetSlotContainedObject(slot); + if (contained != nullptr) + contained->SetGhostMode(enabled); + } } } diff --git a/src/graphics/engine/pyro.cpp b/src/graphics/engine/pyro.cpp index f7db745a..55895f09 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 0204e2ff..314491a2 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; } @@ -2041,21 +2035,12 @@ CObject* CRobotMain::DetectObject(const glm::vec2& 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; @@ -4752,29 +4737,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)); @@ -4903,7 +4885,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; @@ -4912,6 +4894,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") @@ -4944,6 +4927,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++); @@ -4951,20 +4944,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; @@ -4974,6 +4997,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 c71c2b7a..677f6865 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 fb952667..811e6149 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 30bed890..4e731903 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. @@ -236,7 +236,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 @@ -324,7 +324,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; @@ -399,7 +399,7 @@ void CAutoNuclearPlant::CreatePower() dynamic_cast(*power).SetTransporter(m_object); power->SetPosition(glm::vec3(22.0f, 3.0f, 0.0f)); - m_object->SetPower(power); + m_object->SetSlotContainedObject(0, power); } @@ -419,7 +419,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 5616ef6d..4688856f 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" @@ -265,34 +264,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() - 1; 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 01618b53..aa51573b 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(glm::vec3(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 ddb232a4..dac682e9 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() - 1; 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 6034202d..06e30b92 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 7a3a9557..4db5a2de 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 749b1a58..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 glm::vec3 GetPowerPosition() = 0; - //! Sets the relative position of power cell - virtual void SetPowerPosition(const glm::vec3& 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..cf24cc28 --- /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 "object/object.h" +#include "object/object_interface_type.h" + +#include "object/interface/power_container_object.h" + +#include + +#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 glm::vec3 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 d5257c9c..4e69cb32 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 0be0d687..c988df79 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(glm::vec3 pos, float angle, ObjectType type, type != OBJECT_APOLLO2) { CObject* powerCell = nullptr; - glm::vec3 powerCellPos = m_object->GetPowerPosition(); + int powerSlotIndex = m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER); + glm::vec3 powerCellPos = m_object->GetSlotPosition(powerSlotIndex); float powerCellAngle = 0.0f; if (power <= 1.0f) { @@ -1076,8 +1077,7 @@ void CMotionVehicle::Create(glm::vec3 pos, float angle, ObjectType type, powerCell->SetPosition(powerCellPos); powerCell->SetRotation(glm::vec3(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 113488bf..199cf422 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; + } +} +glm::vec3 COldObject::GetSlotPosition(int slotNum) +{ + if (slotNum == 0 && m_hasPowerSlot) + return m_powerPosition; + else + { + assert(m_hasCargoSlot && slotNum == (m_hasPowerSlot ? 1 : 0)); + int grabPartNum; + glm::vec3 grabRelPos; + // See CTaskManip::TransporterTakeObject call to SetTransporterPart and SetPosition + switch (m_type) + { + case OBJECT_HUMAN: + case OBJECT_TECH: + grabPartNum = 4; + grabRelPos = glm::vec3(1.7f, -0.5f, 1.1f); + break; + case OBJECT_MOBILEsa: // subber + grabPartNum = 2; + grabRelPos = glm::vec3(1.1f, -1.0f, 1.0f); + break; + case OBJECT_MOBILEfa: // Grabbers + case OBJECT_MOBILEta: + case OBJECT_MOBILEwa: + case OBJECT_MOBILEia: + grabPartNum = 3; + grabRelPos = glm::vec3(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(glm::inverse(GetWorldMatrix(0)), 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 glm::vec3& powerPosition) { m_powerPosition = powerPosition; } -glm::vec3 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 3c7bce54..8772c947 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; - glm::vec3 GetPowerPosition() override; - void SetPowerPosition(const glm::vec3& 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; + glm::vec3 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 glm::vec3& 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 9b0e71da..4dc6645a 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 95703e1f..2c22d87b 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. @@ -77,10 +79,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; @@ -314,11 +314,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 bedb4697..e431362d 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 54d639c9..01d3e115 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" @@ -1200,8 +1201,11 @@ bool CTaskGoto::AdjustTarget(CObject* pObj, glm::vec3 &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; glm::mat4 mat = pObj->GetWorldMatrix(0); pos = Math::Transform(mat, pos); diff --git a/src/object/task/taskmanip.cpp b/src/object/task/taskmanip.cpp index 5ffbf8b4..8d485bd9 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(glm::vec3(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,9 +870,12 @@ CObject* CTaskManip::SearchTakeBackObject(bool bAdvance, glm::vec3 &pos, CObject* CTaskManip::SearchOtherObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle, - float &height) + float &height, int &slotNumOut) { - float iAngle, oAngle, oLimit, aLimit, dLimit; + slotNumOut = INVALID_SLOT; + + glm::mat4 mat; + float iAngle, aLimit, dLimit; distance = 1000000.0f; angle = 0.0f; @@ -899,56 +904,45 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, glm::vec3 &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; - } - - glm::mat4 mat = pObj->GetWorldMatrix(0); - glm::vec3 oPos = Math::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(glm::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++) { - glm::vec3 powerPos = dynamic_cast(*pObj).GetPowerPosition(); - height = powerPos.y; - pos = oPos; - return pObj; + mat = pObj->GetWorldMatrix(0); + glm::vec3 worldSlotPos = Math::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(glm::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) ) + { + glm::vec3 powerPos = obj.GetSlotPosition(slot); + height = powerPos.y; + pos = worldSlotPos; + slotNumOut = slot; + return pObj; + } + } + } } } } @@ -964,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)); @@ -1004,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? @@ -1040,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? @@ -1062,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)); @@ -1081,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? { glm::vec3 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 @@ -1109,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; @@ -1121,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)); @@ -1137,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)); @@ -1157,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? @@ -1188,29 +1184,30 @@ bool CTaskManip::TransporterDeposeObject() glm::vec3 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 d2cd38ed..26114a14 100644 --- a/src/object/task/taskmanip.h +++ b/src/object/task/taskmanip.h @@ -73,7 +73,7 @@ protected: CObject* SearchTakeUnderObject(glm::vec3 &pos, float dLimit); CObject* SearchTakeFrontObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle); CObject* SearchTakeBackObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle); - CObject* SearchOtherObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle, float &height); + CObject* SearchOtherObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle, float &height, int &slotNumOut); bool TransporterTakeObject(); bool TransporterDeposeObject(); bool IsFreeDeposeObject(glm::vec3 pos); diff --git a/src/object/task/taskrecover.cpp b/src/object/task/taskrecover.cpp index 6c13c07c..b2ce4bc9 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; glm::mat4 mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/taskshield.cpp b/src/object/task/taskshield.cpp index 2a8f52ed..56e6702b 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); } @@ -116,12 +116,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 ) @@ -306,9 +302,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; glm::mat4 mat = m_object->GetWorldMatrix(0); diff --git a/src/object/task/tasktake.cpp b/src/object/task/tasktake.cpp index c4816713..e454a0f5 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(glm::vec3(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; - glm::mat4 mat = pObj->GetWorldMatrix(0); - glm::vec3 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(glm::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) ) + glm::mat4 mat = pObj->GetWorldMatrix(0); + glm::vec3 worldSlotPos = Math::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) ) { - glm::vec3 powerPos = dynamic_cast(*pObj).GetPowerPosition(); - m_height = powerPos.y; - return pObj; + float distance = fabs(glm::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) ) + { + glm::vec3 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 9eb60338..5ed22b9d 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(glm::vec3 pos); diff --git a/src/object/task/taskterraform.cpp b/src/object/task/taskterraform.cpp index d27846da..09e6569d 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. @@ -96,7 +96,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); @@ -211,7 +211,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; @@ -267,7 +267,7 @@ Error CTaskTerraform::IsEnded() m_object->SetCirVibration(glm::vec3(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); @@ -333,7 +333,7 @@ bool CTaskTerraform::Abort() m_object->SetCirVibration(glm::vec3(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 97ca44d7..bcfbbd0b 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; @@ -2963,10 +2962,11 @@ void CPhysics::PowerParticle(float factor, bool bBreak) glm::vec2 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 ) @@ -2977,7 +2977,7 @@ void CPhysics::PowerParticle(float factor, bool bBreak) glm::mat4 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 = Math::Transform(mat, pos); @@ -3779,16 +3779,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 f781274f..377b0607 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 b2c75985..a2e96ff6 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()); 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