Merge branch 'dev' into dev-graphics-overhaul

# Conflicts:
#	src/graphics/engine/camera.cpp
#	src/object/auto/autonuclearplant.cpp
#	src/object/auto/autopowerplant.cpp
#	src/object/interface/powered_object.h
#	src/object/motion/motionvehicle.cpp
#	src/object/old_object.cpp
#	src/object/old_object.h
#	src/object/task/taskmanip.cpp
#	src/object/task/taskmanip.h
#	src/object/task/tasktake.cpp
dev
Tomasz Kapuściński 2022-02-02 15:45:56 +01:00
commit 3bd4fee844
43 changed files with 1022 additions and 866 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,9 @@
[submodule "data"] [submodule "data"]
path = data path = data
url = git://github.com/colobot/colobot-data.git url = git://github.com/colobot/colobot-data.git
branch = .
update = rebase
[submodule "lib/googletest"] [submodule "lib/googletest"]
path = lib/googletest path = lib/googletest
url = git://github.com/google/googletest.git url = git://github.com/google/googletest.git
ignore = all

View File

@ -445,11 +445,12 @@ endif()
if(TESTS) if(TESTS)
# Google Test library # 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) if(NOT(FORCE_BUNDLED_GTEST) AND GTEST_FOUND)
message(STATUS "Using system gtest library in ${GTEST_BOTH_LIBRARIES}") message(STATUS "Using system gtest library in ${GTEST_BOTH_LIBRARIES}")
elseif(EXISTS "${colobot_SOURCE_DIR}/lib/googletest/googletest/CMakeLists.txt") elseif(EXISTS "${colobot_SOURCE_DIR}/lib/googletest/googletest/CMakeLists.txt")
message(STATUS "Using gtest git submodule") message(STATUS "Using gtest git submodule")
set(GOOGLETEST_VERSION "1.11.0")
add_subdirectory("${colobot_SOURCE_DIR}/lib/googletest/googletest" "lib/googletest/googletest") add_subdirectory("${colobot_SOURCE_DIR}/lib/googletest/googletest" "lib/googletest/googletest")
# Add aliases so target names are compatible with the find_package above # Add aliases so target names are compatible with the find_package above
add_library(GTest::GTest ALIAS gtest) add_library(GTest::GTest ALIAS gtest)

View File

@ -317,8 +317,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2
if(SDL2MAIN_LIBRARY) if(SDL2MAIN_LIBRARY)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2main
REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR REQUIRED_VARS SDL2MAIN_LIBRARY SDL2_INCLUDE_DIR
VERSION_VAR SDL2_VERSION_STRING VERSION_VAR SDL2_VERSION_STRING)
NAME_MISMATCHED)
endif() endif()

2
data

@ -1 +1 @@
Subproject commit 21a45c0b8809accd142a83a81f1a3c92a327f319 Subproject commit 8ad5e916e353094242823f716ea45b3df2cdfe5f

@ -1 +1 @@
Subproject commit 703bd9caab50b139428cea1aaff9974ebee5742e Subproject commit e2239ee6043f73722e7aa812a459f54a28552929

View File

@ -59,7 +59,7 @@ static bool ReadBinary(std::istream &istr, T &value)
while (true) // unsigned LEB128 while (true) // unsigned LEB128
{ {
if (!istr.read(reinterpret_cast<char*>(&chr), 1)) return false; if (!istr.read(reinterpret_cast<char*>(&chr), 1)) return false;
if (shift < sizeof(T) * 8 - 1) if (shift < sizeof(T) * 8)
value |= static_cast<T>(chr & 0x7F) << shift; value |= static_cast<T>(chr & 0x7F) << shift;
shift += 7; shift += 7;
if ((chr & 0x80) == 0) break; if ((chr & 0x80) == 0) break;

View File

@ -243,7 +243,6 @@ add_library(colobotbase STATIC
object/implementation/programmable_impl.h object/implementation/programmable_impl.h
object/implementation/task_executor_impl.cpp object/implementation/task_executor_impl.cpp
object/implementation/task_executor_impl.h object/implementation/task_executor_impl.h
object/interface/carrier_object.h
object/interface/controllable_object.h object/interface/controllable_object.h
object/interface/damageable_object.h object/interface/damageable_object.h
object/interface/destroyable_object.h object/interface/destroyable_object.h
@ -254,12 +253,12 @@ add_library(colobotbase STATIC
object/interface/jostleable_object.h object/interface/jostleable_object.h
object/interface/movable_object.h object/interface/movable_object.h
object/interface/power_container_object.h object/interface/power_container_object.h
object/interface/powered_object.h
object/interface/program_storage_object.h object/interface/program_storage_object.h
object/interface/programmable_object.h object/interface/programmable_object.h
object/interface/ranged_object.h object/interface/ranged_object.h
object/interface/shielded_auto_regen_object.h object/interface/shielded_auto_regen_object.h
object/interface/shielded_object.h object/interface/shielded_object.h
object/interface/slotted_object.h
object/interface/task_executor_object.h object/interface/task_executor_object.h
object/interface/trace_drawing_object.cpp object/interface/trace_drawing_object.cpp
object/interface/trace_drawing_object.h object/interface/trace_drawing_object.h

View File

@ -38,10 +38,9 @@
#include "object/object.h" #include "object/object.h"
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/interface/carrier_object.h"
#include "object/interface/controllable_object.h" #include "object/interface/controllable_object.h"
#include "object/interface/movable_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 "object/interface/transportable_object.h"
#include "physics/physics.h" #include "physics/physics.h"
@ -58,18 +57,15 @@ static void SetGhostMode(CObject* obj, bool enabled)
{ {
obj->SetGhostMode(enabled); obj->SetGhostMode(enabled);
if (obj->Implements(ObjectInterfaceType::Carrier)) if (obj->Implements(ObjectInterfaceType::Slotted))
{ {
CObject* cargo = dynamic_cast<CCarrierObject&>(*obj).GetCargo(); CSlottedObject *slotted = dynamic_cast<CSlottedObject*>(obj);
if (cargo != nullptr) for(int slot = slotted->GetNumSlots()-1; slot >= 0; slot--)
cargo->SetGhostMode(enabled); {
} CObject *contained = slotted->GetSlotContainedObject(slot);
if (contained != nullptr)
if (obj->Implements(ObjectInterfaceType::Powered)) contained->SetGhostMode(enabled);
{ }
CObject* power = dynamic_cast<CPoweredObject&>(*obj).GetPower();
if (power != nullptr)
power->SetGhostMode(enabled);
} }
} }

View File

@ -35,6 +35,8 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/slotted_object.h"
#include "object/motion/motionhuman.h" #include "object/motion/motionhuman.h"
#include "object/subclass/shielder.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. // Seeking the position of the battery.
CObject* power = nullptr; CObject* power = GetObjectInPowerCellSlot(obj);
if (obj->Implements(ObjectInterfaceType::Powered))
power = dynamic_cast<CPoweredObject&>(*obj).GetPower();
if (power == nullptr) if (power == nullptr)
{ {
@ -1385,25 +1385,16 @@ void CPyro::DeleteObject(bool primary, bool secondary)
type != OBJECT_NUCLEAR && type != OBJECT_NUCLEAR &&
type != OBJECT_ENERGY ) type != OBJECT_ENERGY )
{ {
if (m_object->Implements(ObjectInterfaceType::Powered)) if (m_object->Implements(ObjectInterfaceType::Slotted))
{ {
CPoweredObject* poweredObject = dynamic_cast<CPoweredObject*>(m_object); CSlottedObject* asSlotted = dynamic_cast<CSlottedObject*>(m_object);
CObject* sub = poweredObject->GetPower(); for (int slot = asSlotted->GetNumSlots() - 1; slot >= 0; slot--)
if (sub != nullptr)
{ {
CObjectManager::GetInstancePointer()->DeleteObject(sub); if (CObject* sub = asSlotted->GetSlotContainedObject(slot))
poweredObject->SetPower(nullptr); {
} CObjectManager::GetInstancePointer()->DeleteObject(sub);
} asSlotted->SetSlotContainedObject(slot, nullptr);
}
if (m_object->Implements(ObjectInterfaceType::Carrier))
{
CCarrierObject* carrierObject = dynamic_cast<CCarrierObject*>(m_object);
CObject* sub = carrierObject->GetCargo();
if (sub != nullptr)
{
CObjectManager::GetInstancePointer()->DeleteObject(sub);
carrierObject->SetCargo(nullptr);
} }
} }
} }
@ -1416,18 +1407,15 @@ void CPyro::DeleteObject(bool primary, bool secondary)
CObject* transporter = dynamic_cast<CTransportableObject&>(*m_object).GetTransporter(); CObject* transporter = dynamic_cast<CTransportableObject&>(*m_object).GetTransporter();
if (transporter != nullptr) if (transporter != nullptr)
{ {
if (transporter->Implements(ObjectInterfaceType::Powered)) assert(transporter->Implements(ObjectInterfaceType::Slotted));
CSlottedObject* asSlotted = dynamic_cast<CSlottedObject*>(transporter);
for (int slotNum = asSlotted->GetNumSlots() - 1; slotNum >= 0; slotNum--)
{ {
CPoweredObject* powered = dynamic_cast<CPoweredObject*>(transporter); if (asSlotted->GetSlotContainedObject(slotNum) == m_object)
if (powered->GetPower() == m_object) {
powered->SetPower(nullptr); asSlotted->SetSlotContainedObject(slotNum, nullptr);
} break;
}
if (transporter->Implements(ObjectInterfaceType::Carrier))
{
CCarrierObject* carrier = dynamic_cast<CCarrierObject*>(transporter);
if (carrier->GetCargo() == m_object)
carrier->SetCargo(nullptr);
} }
} }
} }
@ -2196,11 +2184,10 @@ void CPyro::BurnProgress()
oldObj->SetPartRotation(m_burnPart[i].part, pos); 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<CPoweredObject&>(*m_object).GetPower(); sub->SetScaleY(1.0f - m_progress); // complete flattening
if (sub != nullptr) // is there a battery?
sub->SetScaleY(1.0f - m_progress); // complete flattening
} }
} }

View File

@ -69,6 +69,8 @@
#include "object/auto/auto.h" #include "object/auto/auto.h"
#include "object/interface/slotted_object.h"
#include "object/motion/motion.h" #include "object/motion/motion.h"
#include "object/motion/motionhuman.h" #include "object/motion/motionhuman.h"
#include "object/motion/motiontoto.h" #include "object/motion/motiontoto.h"
@ -1392,12 +1394,8 @@ void CRobotMain::ExecuteCmd(const std::string& cmd)
CObject* object = GetSelect(); CObject* object = GetSelect();
if (object != nullptr) if (object != nullptr)
{ {
if (object->Implements(ObjectInterfaceType::Powered)) if (CPowerContainerObject *power = GetObjectPowerCell(object))
{ power->SetEnergyLevel(1.0f);
CObject* power = dynamic_cast<CPoweredObject&>(*object).GetPower();
if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
dynamic_cast<CPowerContainerObject&>(*power).SetEnergyLevel(1.0f);
}
if (object->Implements(ObjectInterfaceType::Shielded)) if (object->Implements(ObjectInterfaceType::Shielded))
dynamic_cast<CShieldedObject&>(*object).SetShield(1.0f); dynamic_cast<CShieldedObject&>(*object).SetShield(1.0f);
@ -1414,12 +1412,8 @@ void CRobotMain::ExecuteCmd(const std::string& cmd)
if (object != nullptr) if (object != nullptr)
{ {
if (object->Implements(ObjectInterfaceType::Powered)) if (CPowerContainerObject *power = GetObjectPowerCell(object))
{ power->SetEnergyLevel(1.0f);
CObject* power = dynamic_cast<CPoweredObject&>(*object).GetPower();
if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
dynamic_cast<CPowerContainerObject&>(*power).SetEnergyLevel(1.0f);
}
} }
return; return;
} }
@ -2041,21 +2035,12 @@ CObject* CRobotMain::DetectObject(const glm::vec2& pos)
if (obj->GetProxyActivate()) continue; if (obj->GetProxyActivate()) continue;
CObject* target = obj; CObject* target = obj;
// TODO: should this also apply to slots other than power cell slots?
if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable)) if (obj->Implements(ObjectInterfaceType::PowerContainer) && obj->Implements(ObjectInterfaceType::Transportable))
{ {
target = dynamic_cast<CTransportableObject&>(*obj).GetTransporter(); // battery connected CObject *transporter = dynamic_cast<CTransportableObject&>(*obj).GetTransporter(); // battery connected
if (target == nullptr) if (transporter != nullptr && obj == GetObjectInPowerCellSlot(transporter))
{ target = transporter;
target = obj; // standalone battery
}
else
{
if (!target->Implements(ObjectInterfaceType::Powered) || dynamic_cast<CPoweredObject&>(*target).GetPower() != obj)
{
// transported, but not in the power slot
target = obj;
}
}
} }
if (!obj->Implements(ObjectInterfaceType::Old)) continue; 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 (IsObjectBeingTransported(obj)) continue;
if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject&>(*obj).IsDying()) continue; if (obj->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject&>(*obj).IsDying()) continue;
if (obj->Implements(ObjectInterfaceType::Carrier)) if (obj->Implements(ObjectInterfaceType::Slotted))
{ {
CObject* cargo = dynamic_cast<CCarrierObject&>(*obj).GetCargo(); CSlottedObject* slotted = dynamic_cast<CSlottedObject*>(obj);
if (cargo != nullptr) // object transported? for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--)
{ {
line = MakeUnique<CLevelParserLine>("CreateFret"); if (CObject *sub = slotted->GetSlotContainedObject(slot))
IOWriteObject(line.get(), cargo, dirname, objRank++); {
levelParser.AddLine(std::move(line)); if (slot == slotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER))
line = MakeUnique<CLevelParserLine>("CreatePower");
else if (slot == slotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING))
line = MakeUnique<CLevelParserLine>("CreateFret");
else
line = MakeUnique<CLevelParserLine>("CreateSlotObject");
line->AddParam("slotNum", MakeUnique<CLevelParserParam>(slot));
IOWriteObject(line.get(), sub, dirname, objRank++);
levelParser.AddLine(std::move(line));
}
} }
} }
if (obj->Implements(ObjectInterfaceType::Powered))
{
CObject* power = dynamic_cast<CPoweredObject&>(*obj).GetPower();
if (power != nullptr) // battery transported?
{
line = MakeUnique<CLevelParserLine>("CreatePower");
IOWriteObject(line.get(), power, dirname, objRank++);
levelParser.AddLine(std::move(line));
}
}
line = MakeUnique<CLevelParserLine>("CreateObject"); line = MakeUnique<CLevelParserLine>("CreateObject");
IOWriteObject(line.get(), obj, dirname, objRank++); IOWriteObject(line.get(), obj, dirname, objRank++);
levelParser.AddLine(std::move(line)); levelParser.AddLine(std::move(line));
@ -4903,7 +4885,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot)
CLevelParser levelParser(filename); CLevelParser levelParser(filename);
levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank); levelParser.SetLevelPaths(m_levelCategory, m_levelChap, m_levelRank);
levelParser.Load(); 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; m_base = nullptr;
@ -4912,6 +4894,7 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot)
CObject* sel = nullptr; CObject* sel = nullptr;
int objRank = 0; int objRank = 0;
int objCounter = 0; int objCounter = 0;
std::map<int, CObject*> slots;
for (auto& line : levelParser.GetLines()) for (auto& line : levelParser.GetLines())
{ {
if (line->GetCommand() == "Mission") if (line->GetCommand() == "Mission")
@ -4944,6 +4927,16 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot)
objCounter++; objCounter++;
} }
if (line->GetCommand() == "CreateSlotObject")
{
int slotNum = line->GetParam("slotNum")->AsInt();
CObject *slotObject = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects));
objCounter++;
assert(slots.find(slotNum) == slots.end());
slots.emplace(slotNum, slotObject);
}
if (line->GetCommand() == "CreateObject") if (line->GetCommand() == "CreateObject")
{ {
CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects), objRank++); CObject* obj = IOReadObject(line.get(), dirname, StrUtils::ToString<int>(objCounter+1)+" / "+StrUtils::ToString<int>(numObjects), static_cast<float>(objCounter) / static_cast<float>(numObjects), objRank++);
@ -4951,20 +4944,50 @@ CObject* CRobotMain::IOReadScene(std::string filename, std::string filecbot)
if (line->GetParam("select")->AsBool(false)) if (line->GetParam("select")->AsBool(false))
sel = obj; sel = obj;
if (cargo != nullptr) if (obj->Implements(ObjectInterfaceType::Slotted))
{ {
assert(obj->Implements(ObjectInterfaceType::Carrier)); // TODO: exception? CSlottedObject* asSlotted = dynamic_cast<CSlottedObject*>(obj);
assert(obj->Implements(ObjectInterfaceType::Old)); if (cargo != nullptr)
dynamic_cast<CCarrierObject&>(*obj).SetCargo(cargo); {
auto task = MakeUnique<CTaskManip>(dynamic_cast<COldObject*>(obj)); int slotNum = asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING);
task->Start(TMO_AUTO, TMA_GRAB); // holds the object! assert(slotNum >= 0);
assert(slots.find(slotNum) == slots.end());
asSlotted->SetSlotContainedObject(slotNum, cargo);
// TODO: eww!
assert(obj->Implements(ObjectInterfaceType::Old));
auto task = MakeUnique<CTaskManip>(dynamic_cast<COldObject*>(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<const int, CObject*>& 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) if (power != nullptr)
{ {
assert(obj->Implements(ObjectInterfaceType::Powered)); dynamic_cast<CSlottedObject&>(*obj).SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, power);
dynamic_cast<CPoweredObject&>(*obj).SetPower(power);
assert(power->Implements(ObjectInterfaceType::Transportable));
dynamic_cast<CTransportableObject&>(*power).SetTransporter(obj); dynamic_cast<CTransportableObject&>(*power).SetTransporter(obj);
} }
cargo = nullptr; 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); m_ui->GetLoadingScreen()->SetProgress(0.95f, RT_LOADING_CBOT_SAVE);
// Reads the file of stacks of execution. // Reads the file of stacks of execution.

View File

@ -26,7 +26,8 @@
#include "object/object.h" #include "object/object.h"
#include "object/object_manager.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 "object/interface/transportable_object.h"
#include <limits> #include <limits>
@ -80,13 +81,9 @@ bool CObjectCondition::CheckForObject(CObject* obj)
{ {
power = dynamic_cast<CPowerContainerObject*>(obj); power = dynamic_cast<CPowerContainerObject*>(obj);
} }
else if (obj->Implements(ObjectInterfaceType::Powered)) else
{ {
CObject* powerObj = dynamic_cast<CPoweredObject&>(*obj).GetPower(); power = GetObjectPowerCell(obj);
if(powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer))
{
power = dynamic_cast<CPowerContainerObject*>(powerObj);
}
} }
if (power != nullptr) if (power != nullptr)

View File

@ -32,7 +32,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h" #include "object/interface/slotted_object.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -59,7 +59,7 @@ CAutoLabo::CAutoLabo(COldObject* object) : CAuto(object)
m_soundChannel = -1; m_soundChannel = -1;
Init(); Init();
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(object->GetNumSlots() == 1);
} }
// Object's destructor. // Object's destructor.
@ -131,7 +131,7 @@ Error CAutoLabo::StartAction(int param)
return ERR_LABO_ALREADY; return ERR_LABO_ALREADY;
} }
CObject* power = m_object->GetPower(); CObject* power = dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObject(0);
if (power == nullptr) if (power == nullptr)
{ {
return ERR_LABO_NULL; return ERR_LABO_NULL;
@ -308,7 +308,7 @@ bool CAutoLabo::EventProcess(const Event &event)
{ {
if ( m_progress < 1.0f ) if ( m_progress < 1.0f )
{ {
power = m_object->GetPower(); power = dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObject(0);
if ( power != nullptr ) if ( power != nullptr )
{ {
power->SetScale(1.0f-m_progress); power->SetScale(1.0f-m_progress);
@ -366,10 +366,10 @@ bool CAutoLabo::EventProcess(const Event &event)
m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE)); m_eventQueue->AddEvent(Event(EVENT_UPDINTERFACE));
UpdateInterface(); UpdateInterface();
power = m_object->GetPower(); power = dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObject(0);
if ( power != nullptr ) if ( power != nullptr )
{ {
m_object->SetPower(nullptr); dynamic_cast<CSlottedObject&>(*m_object).SetSlotContainedObject(0, nullptr);
CObjectManager::GetInstancePointer()->DeleteObject(power); CObjectManager::GetInstancePointer()->DeleteObject(power);
} }
@ -457,7 +457,7 @@ Error CAutoLabo::GetError()
return ERR_BAT_VIRUS; return ERR_BAT_VIRUS;
} }
CObject* obj = m_object->GetPower(); CObject* obj = dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObject(0);
if (obj == nullptr) return ERR_LABO_NULL; if (obj == nullptr) return ERR_LABO_NULL;
ObjectType type = obj->GetType(); ObjectType type = obj->GetType();
if ( type != OBJECT_BULLET && type != OBJECT_TNT ) return ERR_LABO_BAD; if ( type != OBJECT_BULLET && type != OBJECT_TNT ) return ERR_LABO_BAD;

View File

@ -32,7 +32,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.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 "object/interface/transportable_object.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -53,7 +53,7 @@ CAutoNuclearPlant::CAutoNuclearPlant(COldObject* object) : CAuto(object)
m_channelSound = -1; m_channelSound = -1;
Init(); Init();
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->GetNumSlots() == 1);
} }
// Object's destructor. // Object's destructor.
@ -236,7 +236,7 @@ bool CAutoNuclearPlant::EventProcess(const Event &event)
if ( cargo != nullptr ) if ( cargo != nullptr )
{ {
CObjectManager::GetInstancePointer()->DeleteObject(cargo); CObjectManager::GetInstancePointer()->DeleteObject(cargo);
m_object->SetPower(nullptr); m_object->SetSlotContainedObject(0, nullptr);
} }
CreatePower(); // creates the atomic cell CreatePower(); // creates the atomic cell
@ -324,7 +324,7 @@ bool CAutoNuclearPlant::CreateInterface(bool bSelect)
CObject* CAutoNuclearPlant::SearchUranium() CObject* CAutoNuclearPlant::SearchUranium()
{ {
CObject* obj = m_object->GetPower(); CObject* obj = m_object->GetSlotContainedObject(0);
if (obj == nullptr) return nullptr; if (obj == nullptr) return nullptr;
if (obj->GetType() == OBJECT_URANIUM) return obj; if (obj->GetType() == OBJECT_URANIUM) return obj;
return nullptr; return nullptr;
@ -399,7 +399,7 @@ void CAutoNuclearPlant::CreatePower()
dynamic_cast<CTransportableObject&>(*power).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*power).SetTransporter(m_object);
power->SetPosition(glm::vec3(22.0f, 3.0f, 0.0f)); 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; //? 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 == nullptr ) return ERR_NUCLEAR_EMPTY;
if ( obj->GetLock() ) return ERR_OK; if ( obj->GetLock() ) return ERR_OK;
ObjectType type = obj->GetType(); ObjectType type = obj->GetType();

View File

@ -30,8 +30,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h" #include "object/interface/slotted_object.h"
#include "object/interface/powered_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "sound/sound.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<CPoweredObject&>(*obj).GetPower(); CSlottedObject* slotted = dynamic_cast<CSlottedObject*>(obj);
if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--)
{ {
CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(power); CObject *held = slotted->GetSlotContainedObject(slot);
if (powerContainer->IsRechargeable()) if (held != nullptr && held->Implements(ObjectInterfaceType::PowerContainer))
{ {
float energy = powerContainer->GetEnergy(); CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(held);
energy += rTime/2.0f; if (powerContainer->IsRechargeable())
if ( energy > 1.0f ) energy = 1.0f; {
powerContainer->SetEnergy(energy); 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<CCarrierObject&>(*obj).GetCargo();
if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) )
{
CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(power);
if (powerContainer->IsRechargeable())
{
float energy = powerContainer->GetEnergy();
energy += rTime/2.0f;
if ( energy > 1.0f ) energy = 1.0f;
powerContainer->SetEnergy(energy);
} }
} }
} }

View File

@ -34,7 +34,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.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 "object/interface/transportable_object.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -57,7 +57,7 @@ CAutoPowerPlant::CAutoPowerPlant(COldObject* object) : CAuto(object)
m_partiSphere = -1; m_partiSphere = -1;
Init(); Init();
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->GetNumSlots() == 1);
} }
// Object's destructor. // Object's destructor.
@ -79,17 +79,18 @@ void CAutoPowerPlant::DeleteObject(bool all)
if ( !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(); CObject* cargo = SearchMetal();
if ( cargo != nullptr ) if ( cargo != nullptr )
{ {
m_object->SetPower(nullptr); m_object->SetSlotContainedObject(0, nullptr);
CObjectManager::GetInstancePointer()->DeleteObject(cargo); CObjectManager::GetInstancePointer()->DeleteObject(cargo);
} }
cargo = SearchPower(); cargo = SearchPower();
if ( cargo != nullptr ) if ( cargo != nullptr )
{ {
m_object->SetPower(nullptr); m_object->SetSlotContainedObject(0, nullptr);
CObjectManager::GetInstancePointer()->DeleteObject(cargo); CObjectManager::GetInstancePointer()->DeleteObject(cargo);
} }
} }
@ -320,7 +321,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event)
cargo = SearchMetal(); cargo = SearchMetal();
if ( cargo != nullptr ) if ( cargo != nullptr )
{ {
m_object->SetPower(nullptr); m_object->SetSlotContainedObject(0, nullptr);
CObjectManager::GetInstancePointer()->DeleteObject(cargo); CObjectManager::GetInstancePointer()->DeleteObject(cargo);
} }
@ -333,7 +334,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event)
cargo->SetLock(false); // usable battery cargo->SetLock(false); // usable battery
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object);
cargo->SetPosition(glm::vec3(0.0f, 3.0f, 0.0f)); 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); m_main->DisplayError(INFO_ENERGY, m_object);
} }
@ -387,7 +388,7 @@ bool CAutoPowerPlant::EventProcess(const Event &event)
CObject* CAutoPowerPlant::SearchMetal() CObject* CAutoPowerPlant::SearchMetal()
{ {
CObject* obj = m_object->GetPower(); CObject* obj = m_object->GetSlotContainedObject(0);
if ( obj == nullptr ) return nullptr; if ( obj == nullptr ) return nullptr;
ObjectType type = obj->GetType(); ObjectType type = obj->GetType();
@ -512,7 +513,7 @@ Error CAutoPowerPlant::GetError()
if ( m_object->GetEnergy() < POWERPLANT_POWER ) return ERR_ENERGY_LOW; 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; if (obj == nullptr) return ERR_ENERGY_EMPTY;
ObjectType type = obj->GetType(); ObjectType type = obj->GetType();
if ( type == OBJECT_POWER ) return ERR_OK; if ( type == OBJECT_POWER ) return ERR_OK;

View File

@ -28,9 +28,6 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h"
#include "object/interface/powered_object.h"
#include "sound/sound.h" #include "sound/sound.h"
#include "ui/controls/gauge.h" #include "ui/controls/gauge.h"
@ -134,32 +131,13 @@ bool CAutoPowerStation::EventProcess(const Event &event)
if (big > 0.0f) if (big > 0.0f)
{ {
CObject* vehicle = SearchVehicle(); CObject* vehicle = SearchVehicle();
if (vehicle != nullptr) if (vehicle != nullptr && vehicle->Implements(ObjectInterfaceType::Slotted))
{ {
if (vehicle->Implements(ObjectInterfaceType::Powered)) CSlottedObject* slotted = dynamic_cast<CSlottedObject*>(vehicle);
for (int slot = slotted->GetNumSlots() - 1; slot >= 0; slot--)
{ {
CObject* power = dynamic_cast<CPoweredObject&>(*vehicle).GetPower(); CObject *power = slotted->GetSlotContainedObject(slot);
if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) ) if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
{
CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(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<CCarrierObject&>(*vehicle).GetCargo();
if ( power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer) )
{ {
CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(power); CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(power);
if (powerContainer->IsRechargeable()) if (powerContainer->IsRechargeable())

View File

@ -32,7 +32,7 @@
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h" #include "object/interface/slotted_object.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -57,7 +57,7 @@ CAutoResearch::CAutoResearch(COldObject* object) : CAuto(object)
Init(); Init();
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->GetNumSlots() == 1);
} }
// Object's destructor. // Object's destructor.
@ -114,11 +114,11 @@ Error CAutoResearch::StartAction(int param)
return ERR_RESEARCH_ALREADY; 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; return ERR_RESEARCH_POWER;
} }
CPowerContainerObject* power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower()); CPowerContainerObject* power = dynamic_cast<CPowerContainerObject*>(m_object->GetSlotContainedObject(0));
if ( power->GetCapacity() > 1.0f ) if ( power->GetCapacity() > 1.0f )
{ {
return ERR_RESEARCH_TYPE; return ERR_RESEARCH_TYPE;
@ -222,7 +222,9 @@ bool CAutoResearch::EventProcess(const Event &event)
FireStopUpdate(m_progress, true); // flashes FireStopUpdate(m_progress, true); // flashes
if ( m_progress < 1.0f ) if ( m_progress < 1.0f )
{ {
if ( m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) ) // more battery? CObject* batteryObj = dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObject(0);
if ( batteryObj == nullptr || !batteryObj->Implements(ObjectInterfaceType::PowerContainer) ) // more battery?
{ {
SetBusy(false); SetBusy(false);
UpdateInterface(); UpdateInterface();
@ -232,7 +234,7 @@ bool CAutoResearch::EventProcess(const Event &event)
m_speed = 1.0f/1.0f; m_speed = 1.0f/1.0f;
return true; return true;
} }
power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower()); power = dynamic_cast<CPowerContainerObject*>(batteryObj);
power->SetEnergyLevel(1.0f-m_progress); power->SetEnergyLevel(1.0f-m_progress);
if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time ) if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
@ -302,11 +304,11 @@ Error CAutoResearch::GetError()
return ERR_BAT_VIRUS; 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; return ERR_RESEARCH_POWER;
} }
CPowerContainerObject* power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower()); CPowerContainerObject* power = dynamic_cast<CPowerContainerObject*>(m_object->GetSlotContainedObject(0));
if ( power->GetCapacity() > 1.0f ) if ( power->GetCapacity() > 1.0f )
{ {
return ERR_RESEARCH_TYPE; return ERR_RESEARCH_TYPE;

View File

@ -32,7 +32,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h" #include "object/interface/slotted_object.h"
#include "physics/physics.h" #include "physics/physics.h"
@ -61,7 +61,7 @@ CAutoTower::CAutoTower(COldObject* object) : CAuto(object)
m_time = 0.0f; m_time = 0.0f;
m_lastUpdateTime = 0.0f; m_lastUpdateTime = 0.0f;
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->GetNumSlots() == 1);
} }
// Object's destructor. // Object's destructor.
@ -125,13 +125,8 @@ bool CAutoTower::EventProcess(const Event &event)
return true; return true;
} }
CPowerContainerObject* power = nullptr; CPowerContainerObject* power = GetObjectPowerCell(m_object);
float energy = 0.0f; float energy = power == nullptr ? 0.0f : power->GetEnergy();
if ( m_object->GetPower() != nullptr && m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) )
{
power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower());
energy = power->GetEnergy();
}
UpdateInterface(event.rTime); UpdateInterface(event.rTime);
@ -321,12 +316,13 @@ Error CAutoTower::GetError()
return ERR_BAT_VIRUS; 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 return ERR_TOWER_POWER; // no battery
} }
if ( dynamic_cast<CPowerContainerObject&>(*m_object->GetPower()).GetEnergy() < ENERGY_FIRE ) if ( power->GetEnergy() < ENERGY_FIRE )
{ {
return ERR_TOWER_ENERGY; // not enough energy return ERR_TOWER_ENERGY; // not enough energy
} }

View File

@ -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<int>(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<CCarrierObject&>(*obj).IsCarryingCargo();
}

View File

@ -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<int>(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<CPoweredObject&>(*object).GetPower();
if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
{
energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergy();
}
}
return energy;
}
inline float GetObjectEnergyLevel(CObject* object)
{
float energy = 0.0f;
if (object->Implements(ObjectInterfaceType::Powered))
{
CObject* power = dynamic_cast<CPoweredObject&>(*object).GetPower();
if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer))
{
energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergyLevel();
}
}
return energy;
}
inline bool ObjectHasPowerCell(CObject* object)
{
return object->Implements(ObjectInterfaceType::Powered) &&
dynamic_cast<CPoweredObject&>(*object).GetPower() != nullptr;
}

View File

@ -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 <assert.h>
#include <glm/glm.hpp>
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<int>(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<CSlottedObject&>(*object).MapPseudoSlot(CSlottedObject::Pseudoslot::POWER) >= 0;
}
return false;
}
inline CObject *GetObjectInPowerCellSlot(CObject *object)
{
if (object->Implements(ObjectInterfaceType::Slotted))
{
return dynamic_cast<CSlottedObject&>(*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<CPowerContainerObject*>(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<CSlottedObject&>(*object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr;
}
return false;
}

View File

@ -33,8 +33,6 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h"
#include "physics/physics.h" #include "physics/physics.h"
#include "sound/sound.h" #include "sound/sound.h"

View File

@ -33,8 +33,8 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h"
#include "object/interface/programmable_object.h" #include "object/interface/programmable_object.h"
#include "object/interface/slotted_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "physics/physics.h" #include "physics/physics.h"
@ -1061,7 +1061,8 @@ void CMotionVehicle::Create(glm::vec3 pos, float angle, ObjectType type,
type != OBJECT_APOLLO2) type != OBJECT_APOLLO2)
{ {
CObject* powerCell = nullptr; 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; float powerCellAngle = 0.0f;
if (power <= 1.0f) if (power <= 1.0f)
{ {
@ -1076,8 +1077,7 @@ void CMotionVehicle::Create(glm::vec3 pos, float angle, ObjectType type,
powerCell->SetPosition(powerCellPos); powerCell->SetPosition(powerCellPos);
powerCell->SetRotation(glm::vec3(0.0f, powerCellAngle, 0.0f)); powerCell->SetRotation(glm::vec3(0.0f, powerCellAngle, 0.0f));
dynamic_cast<CTransportableObject&>(*powerCell).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*powerCell).SetTransporter(m_object);
assert(m_object->Implements(ObjectInterfaceType::Powered)); m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, powerCell);
m_object->SetPower(powerCell);
} }
pos = m_object->GetPosition(); pos = m_object->GetPosition();

View File

@ -39,8 +39,6 @@ enum class ObjectInterfaceType
Programmable, //!< objects that can be programmed in CBOT Programmable, //!< objects that can be programmed in CBOT
TaskExecutor, //!< objects that can execute tasks (CTask classes) TaskExecutor, //!< objects that can execute tasks (CTask classes)
Jostleable, //!< object that can be jostled Jostleable, //!< object that can be jostled
Carrier, //!< object that can carry other objects
Powered, //!< object powered with power cell
Movable, //!< objects that can move Movable, //!< objects that can move
Flying, //!< objects that can fly Flying, //!< objects that can fly
JetFlying, //!< objects that can fly using a jet engine 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 Shielded, //!< objects that can be destroyed after the shield goes down to 0
ShieldedAutoRegen, //!< shielded objects with auto shield regeneration ShieldedAutoRegen, //!< shielded objects with auto shield regeneration
Old, //!< old objects, TODO: remove once no longer necessary 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) Max //!< maximum value (for getting number of items in enum)
}; };

View File

@ -81,8 +81,7 @@ COldObject::COldObject(int id)
CProgramStorageObjectImpl(m_implementedInterfaces, this), CProgramStorageObjectImpl(m_implementedInterfaces, this),
CProgrammableObjectImpl(m_implementedInterfaces, this), CProgrammableObjectImpl(m_implementedInterfaces, this),
CJostleableObject(m_implementedInterfaces), CJostleableObject(m_implementedInterfaces),
CCarrierObject(m_implementedInterfaces), CSlottedObject(m_implementedInterfaces),
CPoweredObject(m_implementedInterfaces),
CJetFlyingObject(m_implementedInterfaces), CJetFlyingObject(m_implementedInterfaces),
CControllableObject(m_implementedInterfaces), CControllableObject(m_implementedInterfaces),
CPowerContainerObjectImpl(m_implementedInterfaces), CPowerContainerObjectImpl(m_implementedInterfaces),
@ -145,6 +144,8 @@ COldObject::COldObject(int id)
m_gunGoalH = 0.0f; m_gunGoalH = 0.0f;
m_shieldRadius = 0.0f; m_shieldRadius = 0.0f;
m_magnifyDamage = 1.0f; m_magnifyDamage = 1.0f;
m_hasPowerSlot = false;
m_hasCargoSlot = false;
m_character = Character(); m_character = Character();
m_character.wheelFront = 1.0f; m_character.wheelFront = 1.0f;
@ -774,17 +775,34 @@ void COldObject::SetType(ObjectType type)
m_type == OBJECT_MOBILEst || m_type == OBJECT_MOBILEst ||
m_type == OBJECT_TOWER || m_type == OBJECT_TOWER ||
m_type == OBJECT_RESEARCH || m_type == OBJECT_RESEARCH ||
m_type == OBJECT_ENERGY || m_type == OBJECT_ENERGY || // TODO not actually a power cell slot
m_type == OBJECT_LABO || m_type == OBJECT_LABO || // TODO not actually a power cell slot
m_type == OBJECT_NUCLEAR ) m_type == OBJECT_NUCLEAR ) // TODO not actually a power cell slot
{ {
m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Powered)] = true; m_hasPowerSlot = true;
} }
else else
{ {
m_implementedInterfaces[static_cast<int>(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<int>(ObjectInterfaceType::Slotted)] = (m_hasPowerSlot || m_hasCargoSlot);
// TODO: Hacking some more // TODO: Hacking some more
if ( m_type == OBJECT_MOBILEtg || if ( m_type == OBJECT_MOBILEtg ||
m_type == OBJECT_STONE || m_type == OBJECT_STONE ||
@ -1662,39 +1680,135 @@ void COldObject::SetMasterParticle(int part, int parti)
// Management of the stack transport. // 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) void COldObject::SetPowerPosition(const glm::vec3& powerPosition)
{ {
m_powerPosition = 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. // Management of the object "transporter" that transports it.
void COldObject::SetTransporter(CObject* transporter) void COldObject::SetTransporter(CObject* transporter)

View File

@ -33,7 +33,6 @@
#include "object/implementation/programmable_impl.h" #include "object/implementation/programmable_impl.h"
#include "object/implementation/task_executor_impl.h" #include "object/implementation/task_executor_impl.h"
#include "object/interface/carrier_object.h"
#include "object/interface/controllable_object.h" #include "object/interface/controllable_object.h"
#include "object/interface/flying_object.h" #include "object/interface/flying_object.h"
#include "object/interface/interactive_object.h" #include "object/interface/interactive_object.h"
@ -41,10 +40,10 @@
#include "object/interface/jostleable_object.h" #include "object/interface/jostleable_object.h"
#include "object/interface/movable_object.h" #include "object/interface/movable_object.h"
#include "object/interface/power_container_object.h" #include "object/interface/power_container_object.h"
#include "object/interface/powered_object.h"
#include "object/interface/programmable_object.h" #include "object/interface/programmable_object.h"
#include "object/interface/ranged_object.h" #include "object/interface/ranged_object.h"
#include "object/interface/shielded_auto_regen_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/task_executor_object.h"
#include "object/interface/trace_drawing_object.h" #include "object/interface/trace_drawing_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
@ -83,8 +82,7 @@ class COldObject : public CObject,
public CProgramStorageObjectImpl, public CProgramStorageObjectImpl,
public CProgrammableObjectImpl, public CProgrammableObjectImpl,
public CJostleableObject, public CJostleableObject,
public CCarrierObject, public CSlottedObject,
public CPoweredObject,
public CJetFlyingObject, public CJetFlyingObject,
public CControllableObject, public CControllableObject,
public CPowerContainerObjectImpl, public CPowerContainerObjectImpl,
@ -175,12 +173,6 @@ public:
void SetMasterParticle(int part, int parti) override; 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; void SetTransporter(CObject* transporter) override;
CObject* GetTransporter() override; CObject* GetTransporter() override;
void SetTransporterPart(int part) override; void SetTransporterPart(int part) override;
@ -295,6 +287,17 @@ public:
void SetBulletWall(bool bulletWall); void SetBulletWall(bool bulletWall);
bool IsBulletWall() override; 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: protected:
bool EventFrame(const Event &event); bool EventFrame(const Event &event);
void VirusFrame(float rTime); void VirusFrame(float rTime);
@ -393,4 +396,7 @@ protected:
float m_traceWidth; float m_traceWidth;
bool m_bulletWall = false; bool m_bulletWall = false;
bool m_hasCargoSlot;
bool m_hasPowerSlot;
}; };

View File

@ -36,7 +36,6 @@
#include "object/auto/auto.h" #include "object/auto/auto.h"
#include "object/interface/carrier_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "object/motion/motionhuman.h" #include "object/motion/motionhuman.h"

View File

@ -26,6 +26,8 @@
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/slotted_object.h"
#include "physics/physics.h" #include "physics/physics.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -42,7 +44,7 @@ CTaskFire::CTaskFire(COldObject* object) : CForegroundTask(object)
{ {
m_soundChannel = -1; m_soundChannel = -1;
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(HasPowerCellSlot(m_object));
} }
// Object's destructor. // Object's destructor.
@ -77,10 +79,8 @@ bool CTaskFire::EventProcess(const Event &event)
m_lastSound -= event.rTime; m_lastSound -= event.rTime;
m_progress += event.rTime*m_speed; m_progress += event.rTime*m_speed;
CPowerContainerObject* power = nullptr; if (CPowerContainerObject* power = GetObjectPowerCell(m_object))
if (m_object->GetPower() != nullptr && m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer))
{ {
power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower());
energy = power->GetEnergy(); energy = power->GetEnergy();
if ( m_bOrganic ) fire = ENERGY_FIREi; if ( m_bOrganic ) fire = ENERGY_FIREi;
else if ( m_bRay ) fire = ENERGY_FIREr; else if ( m_bRay ) fire = ENERGY_FIREr;
@ -314,11 +314,11 @@ Error CTaskFire::Start(float delay)
} }
m_delay = delay; m_delay = delay;
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(HasPowerCellSlot(m_object));
CObject* power = dynamic_cast<CPoweredObject*>(m_object)->GetPower(); CPowerContainerObject *power = GetObjectPowerCell(m_object);
if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_FIRE_ENERGY; if (power == nullptr) return ERR_FIRE_ENERGY;
energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergy(); energy = power->GetEnergy();
if ( m_bOrganic ) fire = m_delay*ENERGY_FIREi; if ( m_bOrganic ) fire = m_delay*ENERGY_FIREi;
else if ( m_bRay ) fire = m_delay*ENERGY_FIREr; else if ( m_bRay ) fire = m_delay*ENERGY_FIREr;
else fire = m_delay*ENERGY_FIRE; else fire = m_delay*ENERGY_FIRE;

View File

@ -28,8 +28,6 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h"
#include "object/motion/motionhuman.h" #include "object/motion/motionhuman.h"
#include "physics/physics.h" #include "physics/physics.h"

View File

@ -33,6 +33,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/slotted_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "object/subclass/base_alien.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_MOBILEst ||
type == OBJECT_MOBILEdr ) type == OBJECT_MOBILEdr )
{ {
assert(pObj->Implements(ObjectInterfaceType::Powered)); CSlottedObject *asSlotted = dynamic_cast<CSlottedObject*>(pObj);
pos = dynamic_cast<CPoweredObject&>(*pObj).GetPowerPosition(); 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; pos.x -= TAKE_DIST+TAKE_DIST_OTHER+distance;
glm::mat4 mat = pObj->GetWorldMatrix(0); glm::mat4 mat = pObj->GetWorldMatrix(0);
pos = Math::Transform(mat, pos); pos = Math::Transform(mat, pos);

View File

@ -30,14 +30,14 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h" #include "object/interface/slotted_object.h"
#include "object/interface/powered_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "physics/physics.h" #include "physics/physics.h"
#include "sound/sound.h" #include "sound/sound.h"
const int INVALID_SLOT = -1;
//?const float MARGIN_FRONT = 2.0f; //?const float MARGIN_FRONT = 2.0f;
//?const float MARGIN_BACK = 2.0f; //?const float MARGIN_BACK = 2.0f;
@ -58,7 +58,7 @@ CTaskManip::CTaskManip(COldObject* object) : CForegroundTask(object)
m_arm = TMA_NEUTRAL; m_arm = TMA_NEUTRAL;
m_hand = TMH_OPEN; m_hand = TMH_OPEN;
assert(m_object->Implements(ObjectInterfaceType::Carrier)); assert(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0);
} }
// Object's destructor. // Object's destructor.
@ -295,7 +295,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
type = m_object->GetType(); type = m_object->GetType();
if ( type == OBJECT_BEE ) // bee? 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; 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; if (other == nullptr) return ERR_MANIP_NIL;
assert(other->Implements(ObjectInterfaceType::Transportable)); assert(other->Implements(ObjectInterfaceType::Transportable));
m_object->SetCargo(other); // takes the ball m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, other); // takes the ball
dynamic_cast<CTransportableObject&>(*other).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*other).SetTransporter(m_object);
dynamic_cast<CTransportableObject&>(*other).SetTransporterPart(0); // taken with the base dynamic_cast<CTransportableObject&>(*other).SetTransporterPart(0); // taken with the base
other->SetPosition(glm::vec3(0.0f, -3.0f, 0.0f)); other->SetPosition(glm::vec3(0.0f, -3.0f, 0.0f));
} }
else else
{ {
other = m_object->GetCargo(); // other = ball other = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING); // other = ball
assert(other->Implements(ObjectInterfaceType::Transportable)); assert(other->Implements(ObjectInterfaceType::Transportable));
m_object->SetCargo(nullptr); // lick the ball m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // lick the ball
dynamic_cast<CTransportableObject&>(*other).SetTransporter(nullptr); dynamic_cast<CTransportableObject&>(*other).SetTransporter(nullptr);
pos = m_object->GetPosition(); pos = m_object->GetPosition();
pos.y -= 3.0f; pos.y -= 3.0f;
@ -361,7 +361,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
if ( order == TMO_AUTO ) if ( order == TMO_AUTO )
{ {
if (m_object->GetCargo() == nullptr) if (m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr)
{ {
m_order = TMO_GRAB; m_order = TMO_GRAB;
} }
@ -375,11 +375,11 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
m_order = order; 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; 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; return ERR_MANIP_EMPTY;
} }
@ -393,7 +393,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
if ( m_arm == TMA_FFRONT ) if ( m_arm == TMA_FFRONT )
{ {
front = SearchTakeFrontObject(true, fPos, fDist, fAngle); 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 ) if ( front != nullptr && fDist < oDist )
{ {
@ -403,7 +404,7 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
} }
else if ( other != nullptr && oDist < fDist ) else if ( other != nullptr && oDist < fDist )
{ {
if (! ObjectHasPowerCell(other)) return ERR_MANIP_NIL; if (dynamic_cast<CSlottedObject&>(*other).GetSlotContainedObject(slotNum) == nullptr) return ERR_MANIP_NIL;
m_targetPos = oPos; m_targetPos = oPos;
m_angle = oAngle; m_angle = oAngle;
m_height = oHeight; m_height = oHeight;
@ -427,8 +428,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
} }
if ( m_arm == TMA_POWER ) if ( m_arm == TMA_POWER )
{ {
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(HasPowerCellSlot(m_object));
if (m_object->GetPower() == nullptr) return ERR_MANIP_NIL; 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 ) if ( m_arm == TMA_FFRONT )
{ {
other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight); int slotNum;
if (other != nullptr && !ObjectHasPowerCell(other)) other = SearchOtherObject(true, oPos, oDist, oAngle, oHeight, slotNum);
if (other != nullptr && dynamic_cast<CSlottedObject&>(*other).GetSlotContainedObject(slotNum) == nullptr)
{ {
m_targetPos = oPos; m_targetPos = oPos;
m_angle = oAngle; m_angle = oAngle;
@ -456,8 +458,8 @@ Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
} }
if ( m_arm == TMA_POWER ) if ( m_arm == TMA_POWER )
{ {
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(HasPowerCellSlot(m_object));
if (m_object->GetPower() != nullptr) return ERR_MANIP_OCC; 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_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 m_hand = TMH_OPEN; // open clamp
} }
@ -602,7 +604,7 @@ Error CTaskManip::IsEnded()
{ {
if ( m_bSubm ) m_speed = 1.0f/1.5f; if ( m_bSubm ) m_speed = 1.0f/1.5f;
if ( !TransporterTakeObject() && if ( !TransporterTakeObject() &&
m_object->GetCargo() == nullptr) m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING) == nullptr)
{ {
m_hand = TMH_OPEN; // reopens the clamp m_hand = TMH_OPEN; // reopens the clamp
m_arm = TMA_NEUTRAL; m_arm = TMA_NEUTRAL;
@ -613,7 +615,7 @@ Error CTaskManip::IsEnded()
{ {
if ( (m_arm == TMA_OTHER || if ( (m_arm == TMA_OTHER ||
m_arm == TMA_POWER ) && 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()); m_sound->Play(SOUND_POWEROFF, m_object->GetPosition());
} }
@ -630,7 +632,7 @@ Error CTaskManip::IsEnded()
if ( m_step == 1 ) if ( m_step == 1 )
{ {
if ( m_bSubm ) m_speed = 1.0f/0.7f; if ( m_bSubm ) m_speed = 1.0f/0.7f;
cargo = m_object->GetCargo(); cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING);
if (TransporterDeposeObject()) if (TransporterDeposeObject())
{ {
if ( (m_arm == TMA_OTHER || if ( (m_arm == TMA_OTHER ||
@ -667,7 +669,7 @@ Error CTaskManip::IsEnded()
bool CTaskManip::Abort() 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_hand = TMH_OPEN; // open clamp
m_arm = TMA_NEUTRAL; m_arm = TMA_NEUTRAL;
@ -868,9 +870,12 @@ CObject* CTaskManip::SearchTakeBackObject(bool bAdvance, glm::vec3 &pos,
CObject* CTaskManip::SearchOtherObject(bool bAdvance, glm::vec3 &pos, CObject* CTaskManip::SearchOtherObject(bool bAdvance, glm::vec3 &pos,
float &distance, float &angle, 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; distance = 1000000.0f;
angle = 0.0f; angle = 0.0f;
@ -899,56 +904,45 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, glm::vec3 &pos,
{ {
if ( pObj == m_object ) continue; // yourself? if ( pObj == m_object ) continue; // yourself?
ObjectType type = pObj->GetType(); if (pObj->Implements(ObjectInterfaceType::Slotted))
if ( !pObj->Implements(ObjectInterfaceType::Powered) ) continue;
CObject* power = dynamic_cast<CPoweredObject&>(*pObj).GetPower();
if (power != nullptr)
{ {
if (power->GetLock()) continue; CSlottedObject &obj = dynamic_cast<CSlottedObject&>(*pObj);
if (power->GetScaleY() != 1.0f) continue; int slotNum = obj.GetNumSlots();
} for (int slot = 0; slot < slotNum; slot++)
glm::mat4 mat = pObj->GetWorldMatrix(0);
glm::vec3 oPos = Math::Transform(mat, dynamic_cast<CPoweredObject&>(*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) )
{ {
glm::vec3 powerPos = dynamic_cast<CPoweredObject&>(*pObj).GetPowerPosition(); mat = pObj->GetWorldMatrix(0);
height = powerPos.y; glm::vec3 worldSlotPos = Math::Transform(mat, obj.GetSlotPosition(slot));
pos = oPos;
return pObj; 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? 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? if (cargo == nullptr) return false; // nothing to take?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
@ -1004,7 +998,7 @@ bool CTaskManip::TransporterTakeObject()
cargo->SetRotationY(0.0f); 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? if (m_arm == TMA_FFRONT) // takes on the ground in front?
@ -1040,7 +1034,7 @@ bool CTaskManip::TransporterTakeObject()
cargo->SetRotationY(0.0f); 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? if (m_arm == TMA_FBACK) // takes on the ground behind?
@ -1062,13 +1056,13 @@ bool CTaskManip::TransporterTakeObject()
cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationZ(Math::PI/2.0f);
cargo->SetRotationY(0.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? if (m_arm == TMA_POWER) // takes battery in the back?
{ {
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->Implements(ObjectInterfaceType::Slotted));
CObject* cargo = m_object->GetPower(); CObject* cargo = m_object->GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::POWER);
if (cargo == nullptr) return false; // no battery? if (cargo == nullptr) return false; // no battery?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
@ -1081,25 +1075,25 @@ bool CTaskManip::TransporterTakeObject()
cargo->SetRotationY(0.0f); cargo->SetRotationY(0.0f);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(3); // takes with the hand dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(3); // takes with the hand
m_object->SetPower(nullptr); m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER, nullptr);
m_object->SetCargo(cargo); // takes m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes
} }
if (m_arm == TMA_OTHER) // battery takes from friend? if (m_arm == TMA_OTHER) // battery takes from friend?
{ {
glm::vec3 pos; glm::vec3 pos;
float dist = 0.0f, angle = 0.0f; 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; if (other == nullptr) return false;
assert(other->Implements(ObjectInterfaceType::Powered)); assert(slotNum != INVALID_SLOT);
CObject *cargo = dynamic_cast<CSlottedObject&>(*other).GetSlotContainedObject(slotNum);
CObject* cargo = dynamic_cast<CPoweredObject&>(*other).GetPower();
if (cargo == nullptr) return false; // the other does not have a battery? if (cargo == nullptr) return false; // the other does not have a battery?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
m_cargoType = cargo->GetType(); m_cargoType = cargo->GetType();
dynamic_cast<CPoweredObject&>(*other).SetPower(nullptr); dynamic_cast<CSlottedObject&>(*other).SetSlotContainedObject(slotNum, nullptr);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(3); // takes with the hand dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(3); // takes with the hand
@ -1109,7 +1103,7 @@ bool CTaskManip::TransporterTakeObject()
cargo->SetRotationZ(Math::PI/2.0f); cargo->SetRotationZ(Math::PI/2.0f);
cargo->SetRotationY(0.0f); cargo->SetRotationY(0.0f);
m_object->SetCargo(cargo); // takes m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes
} }
return true; return true;
@ -1121,7 +1115,7 @@ bool CTaskManip::TransporterDeposeObject()
{ {
if (m_arm == TMA_FFRONT) // deposits on the ground in front? 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? if (cargo == nullptr) return false; // nothing transported?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
@ -1137,12 +1131,12 @@ bool CTaskManip::TransporterDeposeObject()
cargo->FloorAdjust(); // plate well on the ground cargo->FloorAdjust(); // plate well on the ground
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(nullptr); dynamic_cast<CTransportableObject&>(*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? 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? if (cargo == nullptr) return false; // nothing transported?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
@ -1157,30 +1151,32 @@ bool CTaskManip::TransporterDeposeObject()
cargo->SetRotationZ(0.0f); cargo->SetRotationZ(0.0f);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(nullptr); dynamic_cast<CTransportableObject&>(*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? if (m_arm == TMA_POWER) // deposits battery in the back?
{ {
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->Implements(ObjectInterfaceType::Slotted));
CObject* cargo = m_object->GetCargo(); CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING);
if (cargo == nullptr) return false; // nothing transported? if (cargo == nullptr) return false; // nothing transported?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
m_cargoType = cargo->GetType(); 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<CTransportableObject&>(*cargo).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(0); // carried by the base dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(0); // carried by the base
cargo->SetPosition(m_object->GetPowerPosition()); cargo->SetPosition(m_object->GetSlotPosition(powerSlotIndex));
cargo->SetRotationY(0.0f); cargo->SetRotationY(0.0f);
cargo->SetRotationX(0.0f); cargo->SetRotationX(0.0f);
cargo->SetRotationZ(0.0f); cargo->SetRotationZ(0.0f);
m_object->SetPower(cargo); // uses m_object->SetSlotContainedObject(powerSlotIndex, cargo); // uses
m_object->SetCargo(nullptr); m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr);
} }
if (m_arm == TMA_OTHER) // deposits battery on friend? if (m_arm == TMA_OTHER) // deposits battery on friend?
@ -1188,29 +1184,30 @@ bool CTaskManip::TransporterDeposeObject()
glm::vec3 pos; glm::vec3 pos;
float angle = 0.0f, dist = 0.0f; 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; if (other == nullptr) return false;
assert(other->Implements(ObjectInterfaceType::Powered)); assert(slotNum != INVALID_SLOT);
CSlottedObject *otherAsSlotted = dynamic_cast<CSlottedObject*>(other);
if (otherAsSlotted->GetSlotContainedObject(slotNum) != nullptr) return false; // the other already has a battery?
CObject* cargo = dynamic_cast<CPoweredObject&>(*other).GetPower(); CObject *cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING);
if (cargo != nullptr) return false; // the other already has a battery?
cargo = m_object->GetCargo();
if (cargo == nullptr) return false; if (cargo == nullptr) return false;
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
m_cargoType = cargo->GetType(); m_cargoType = cargo->GetType();
dynamic_cast<CPoweredObject&>(*other).SetPower(cargo); otherAsSlotted->SetSlotContainedObject(slotNum, cargo);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(other); dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(other);
cargo->SetPosition(dynamic_cast<CPoweredObject&>(*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->SetRotationY(0.0f);
cargo->SetRotationX(0.0f); cargo->SetRotationX(0.0f);
cargo->SetRotationZ(0.0f); cargo->SetRotationZ(0.0f);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(0); // carried by the base dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(0); // carried by the base
m_object->SetCargo(nullptr); // deposit m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit
} }
return true; return true;

View File

@ -73,7 +73,7 @@ protected:
CObject* SearchTakeUnderObject(glm::vec3 &pos, float dLimit); CObject* SearchTakeUnderObject(glm::vec3 &pos, float dLimit);
CObject* SearchTakeFrontObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle); CObject* SearchTakeFrontObject(bool bAdvance, glm::vec3 &pos, float &distance, float &angle);
CObject* SearchTakeBackObject(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 TransporterTakeObject();
bool TransporterDeposeObject(); bool TransporterDeposeObject();
bool IsFreeDeposeObject(glm::vec3 pos); bool IsFreeDeposeObject(glm::vec3 pos);

View File

@ -31,7 +31,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h" #include "object/interface/slotted_object.h"
#include "physics/physics.h" #include "physics/physics.h"
@ -108,11 +108,9 @@ bool CTaskRecover::EventProcess(const Event &event)
if ( m_phase == TRP_OPER ) if ( m_phase == TRP_OPER )
{ {
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(HasPowerCellSlot(m_object));
CObject* powerObj = dynamic_cast<CPoweredObject*>(m_object)->GetPower(); if (CPowerContainerObject* power = GetObjectPowerCell(m_object))
if (powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer))
{ {
CPowerContainerObject* power = dynamic_cast<CPowerContainerObject*>(powerObj);
energy = power->GetEnergy(); energy = power->GetEnergy();
energy -= event.rTime * ENERGY_RECOVER * m_speed; energy -= event.rTime * ENERGY_RECOVER * m_speed;
power->SetEnergy(energy); power->SetEnergy(energy);
@ -190,11 +188,10 @@ Error CTaskRecover::Start()
ObjectType type = m_object->GetType(); ObjectType type = m_object->GetType();
if ( type != OBJECT_MOBILErr ) return ERR_WRONG_BOT; if ( type != OBJECT_MOBILErr ) return ERR_WRONG_BOT;
assert(m_object->Implements(ObjectInterfaceType::Powered)); CPowerContainerObject *power = GetObjectPowerCell(m_object);
CObject* power = dynamic_cast<CPoweredObject*>(m_object)->GetPower(); if (power == nullptr) return ERR_RECOVER_ENERGY;
if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_RECOVER_ENERGY;
float energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergy(); float energy = power->GetEnergy();
if ( energy < ENERGY_RECOVER+0.05f ) return ERR_RECOVER_ENERGY; if ( energy < ENERGY_RECOVER+0.05f ) return ERR_RECOVER_ENERGY;
glm::mat4 mat = m_object->GetWorldMatrix(0); glm::mat4 mat = m_object->GetWorldMatrix(0);

View File

@ -32,7 +32,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h" #include "object/interface/slotted_object.h"
#include "object/subclass/shielder.h" #include "object/subclass/shielder.h"
@ -54,7 +54,7 @@ CTaskShield::CTaskShield(COldObject* object) : CBackgroundTask(object)
m_soundChannel = -1; m_soundChannel = -1;
m_effectLight = -1; m_effectLight = -1;
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(HasPowerCellSlot(m_object));
m_shielder = dynamic_cast<CShielder*>(object); m_shielder = dynamic_cast<CShielder*>(object);
} }
@ -116,12 +116,8 @@ bool CTaskShield::EventProcess(const Event &event)
{ {
energy = (1.0f/ENERGY_TIME)*event.rTime; energy = (1.0f/ENERGY_TIME)*event.rTime;
energy *= GetRadius()/RADIUS_SHIELD_MAX; energy *= GetRadius()/RADIUS_SHIELD_MAX;
CObject* powerObj = dynamic_cast<CPoweredObject*>(m_object)->GetPower(); if (CPowerContainerObject *power = GetObjectPowerCell(m_object))
if (powerObj != nullptr && powerObj->Implements(ObjectInterfaceType::PowerContainer))
{
CPowerContainerObject* power = dynamic_cast<CPowerContainerObject*>(powerObj);
power->SetEnergy(power->GetEnergy()-energy); power->SetEnergy(power->GetEnergy()-energy);
}
m_energyUsed += energy; m_energyUsed += energy;
if ( m_soundChannel == -1 ) if ( m_soundChannel == -1 )
@ -306,9 +302,9 @@ Error CTaskShield::Start(TaskShieldMode mode, float delay)
m_bError = true; // operation impossible m_bError = true; // operation impossible
if ( !m_physics->GetLand() ) return ERR_WRONG_BOT; if ( !m_physics->GetLand() ) return ERR_WRONG_BOT;
CObject* power = m_object->GetPower(); CPowerContainerObject* power = GetObjectPowerCell(m_object);
if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) return ERR_SHIELD_ENERGY; if (power == nullptr) return ERR_SHIELD_ENERGY;
float energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergy(); float energy = power->GetEnergy();
if ( energy == 0.0f ) return ERR_SHIELD_ENERGY; if ( energy == 0.0f ) return ERR_SHIELD_ENERGY;
glm::mat4 mat = m_object->GetWorldMatrix(0); glm::mat4 mat = m_object->GetWorldMatrix(0);

View File

@ -30,8 +30,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h" #include "object/interface/slotted_object.h"
#include "object/interface/powered_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "object/motion/motionhuman.h" #include "object/motion/motionhuman.h"
@ -49,7 +48,7 @@ CTaskTake::CTaskTake(COldObject* object) : CForegroundTask(object)
{ {
m_arm = TTA_NEUTRAL; m_arm = TTA_NEUTRAL;
assert(m_object->Implements(ObjectInterfaceType::Carrier)); assert(m_object->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0);
} }
// Object's destructor. // Object's destructor.
@ -116,7 +115,7 @@ Error CTaskTake::Start()
m_physics->SetMotorSpeed(glm::vec3(0.0f, 0.0f, 0.0f)); 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; m_order = TTO_DEPOSE;
else else
m_order = TTO_TAKE; m_order = TTO_TAKE;
@ -128,12 +127,14 @@ Error CTaskTake::Start()
float h = m_water->GetLevel(m_object); float h = m_water->GetLevel(m_object);
if ( pos.y < h ) return ERR_MANIP_WATER; // impossible under water if ( pos.y < h ) return ERR_MANIP_WATER; // impossible under water
CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); int otherSlotNum = -1;
if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f, otherSlotNum);
CSlottedObject* otherAsSlotted = dynamic_cast<CSlottedObject*>(other);
assert(other == nullptr || otherSlotNum >= 0);
if (other != nullptr && dynamic_cast<CPoweredObject&>(*other).GetPower() != nullptr) if (other != nullptr && otherAsSlotted->GetSlotContainedObject(otherSlotNum) != nullptr)
{ {
CObject* power = dynamic_cast<CPoweredObject&>(*other).GetPower(); CObject* power = otherAsSlotted->GetSlotContainedObject(otherSlotNum);
type = power->GetType(); type = power->GetType();
if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO; if ( type == OBJECT_URANIUM ) return ERR_MANIP_RADIO;
assert(power->Implements(ObjectInterfaceType::Transportable)); assert(power->Implements(ObjectInterfaceType::Transportable));
@ -158,10 +159,12 @@ Error CTaskTake::Start()
//? if ( speed.x != 0.0f || //? if ( speed.x != 0.0f ||
//? speed.z != 0.0f ) return ERR_MANIP_MOTOR; //? speed.z != 0.0f ) return ERR_MANIP_MOTOR;
CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f); int otherSlotNum = -1;
if (other != nullptr) assert(other->Implements(ObjectInterfaceType::Powered)); CObject* other = SearchFriendObject(oAngle, 1.5f, Math::PI*0.50f, otherSlotNum);
CSlottedObject* otherAsSlotted = dynamic_cast<CSlottedObject*>(other);
assert(other == nullptr || otherSlotNum >= 0);
if (other != nullptr && dynamic_cast<CPoweredObject&>(*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_camera->StartCentering(m_object, Math::PI*0.3f, -Math::PI*0.1f, 0.0f, 0.8f);
m_arm = TTA_FRIEND; m_arm = TTA_FRIEND;
@ -233,7 +236,7 @@ Error CTaskTake::IsEnded()
if ( TransporterTakeObject() ) if ( TransporterTakeObject() )
{ {
if ( m_arm == TTA_FRIEND && 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()); m_sound->Play(SOUND_POWEROFF, m_object->GetPosition());
} }
@ -250,7 +253,7 @@ Error CTaskTake::IsEnded()
{ {
if ( m_step == 1 ) if ( m_step == 1 )
{ {
CObject* cargo = m_object->GetCargo(); CObject* cargo = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING);
TransporterDeposeObject(); TransporterDeposeObject();
if ( m_arm == TTA_FRIEND && if ( m_arm == TTA_FRIEND &&
cargo->Implements(ObjectInterfaceType::PowerContainer) ) 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. // Seeks the robot on which you want take or put a battery.
CObject* CTaskTake::SearchFriendObject(float &angle, CObject* CTaskTake::SearchFriendObject(float &angle,
float dLimit, float aLimit) float dLimit, float aLimit,
int &slotNumOut)
{ {
if (m_object->GetCrashSphereCount() == 0) return nullptr; if (m_object->GetCrashSphereCount() == 0) return nullptr;
@ -348,67 +352,44 @@ CObject* CTaskTake::SearchFriendObject(float &angle,
for (CObject* pObj : CObjectManager::GetInstancePointer()->GetAllObjects()) for (CObject* pObj : CObjectManager::GetInstancePointer()->GetAllObjects())
{ {
if ( pObj == m_object ) continue; // yourself? if ( pObj == m_object ) continue; // yourself?
if (!pObj->Implements(ObjectInterfaceType::Slotted)) continue;
ObjectType type = pObj->GetType(); CSlottedObject *obj = dynamic_cast<CSlottedObject*>(pObj);
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;
assert(pObj->Implements(ObjectInterfaceType::Powered)); int slotNum = obj->GetNumSlots();
for (int slot = 0; slot < slotNum; slot++)
CObject* power = dynamic_cast<CPoweredObject&>(*pObj).GetPower();
if (power != nullptr)
{ {
if ( power->GetLock() ) continue; CObject *objectInSlot = obj->GetSlotContainedObject(slot);
if ( power->GetScaleY() != 1.0f ) continue; if (objectInSlot != nullptr && (objectInSlot->GetLock() || objectInSlot->GetScaleY() != 1.0f))
} continue;
glm::mat4 mat = pObj->GetWorldMatrix(0); float objectAngleOffsetLimit = obj->GetSlotAcceptanceAngle(slot);
glm::vec3 oPos = Math::Transform(mat, dynamic_cast<CPoweredObject&>(*pObj).GetPowerPosition()); if (objectAngleOffsetLimit == 0)
continue; // slot isn't take-able
float distance = fabs(glm::distance(oPos, iPos) - (iRad+1.0f)); glm::mat4 mat = pObj->GetWorldMatrix(0);
if ( distance <= dLimit ) glm::vec3 worldSlotPos = Math::Transform(mat, obj->GetSlotPosition(slot));
{
angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW ! // The robot must be in the correct angle relative to the slot (it can't be on the other side of the object)
if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) ) 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<CPoweredObject&>(*pObj).GetPowerPosition(); float distance = fabs(glm::distance(worldSlotPos, iPos)-(iRad + 1.0f));
m_height = powerPos.y; // The robot must be close enough to the slot
return pObj; 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->SetRotationX(0.0f);
cargo->SetRotationZ(0.8f); 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? if (m_arm == TTA_FRIEND) // takes friend's battery?
{ {
float angle = 0.0f; 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; if (other == nullptr) return false;
assert(other->Implements(ObjectInterfaceType::Powered)); CSlottedObject* otherAsSlotted = dynamic_cast<CSlottedObject*>(other);
assert(otherSlotNum >= -1);
CObject* cargo = dynamic_cast<CPoweredObject&>(*other).GetPower(); CObject* cargo = otherAsSlotted->GetSlotContainedObject(otherSlotNum);
if (cargo == nullptr) return false; // the other does not have a battery? if (cargo == nullptr) return false; // the other does not have a battery?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
m_cargoType = cargo->GetType(); m_cargoType = cargo->GetType();
dynamic_cast<CPoweredObject&>(*other).SetPower(nullptr); otherAsSlotted->SetSlotContainedObject(otherSlotNum, nullptr);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object); dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(m_object);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(4); // takes with the hand dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(4); // takes with the hand
@ -465,7 +448,7 @@ bool CTaskTake::TransporterTakeObject()
cargo->SetRotationX(0.0f); cargo->SetRotationX(0.0f);
cargo->SetRotationZ(0.8f); cargo->SetRotationZ(0.8f);
m_object->SetCargo(cargo); // takes m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, cargo); // takes
} }
return true; return true;
@ -477,7 +460,7 @@ bool CTaskTake::TransporterDeposeObject()
{ {
if ( m_arm == TTA_FFRONT ) // deposes on the ground in front? 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? if (cargo == nullptr) return false; // does nothing?
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
@ -493,34 +476,36 @@ bool CTaskTake::TransporterDeposeObject()
cargo->FloorAdjust(); // plate well on the ground cargo->FloorAdjust(); // plate well on the ground
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(nullptr); dynamic_cast<CTransportableObject&>(*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? if ( m_arm == TTA_FRIEND ) // deposes battery on friends?
{ {
float angle = 0.0f; 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; if (other == nullptr) return false;
assert(other->Implements(ObjectInterfaceType::Powered)); CSlottedObject* otherAsSlotted = dynamic_cast<CSlottedObject*>(other);
assert(otherSlotNum >= 0);
CObject* cargo = dynamic_cast<CPoweredObject&>(*other).GetPower(); CObject* cargo = otherAsSlotted->GetSlotContainedObject(otherSlotNum);
if (cargo != nullptr) return false; // the other already has a battery? 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; if (cargo == nullptr) return false;
assert(cargo->Implements(ObjectInterfaceType::Transportable)); assert(cargo->Implements(ObjectInterfaceType::Transportable));
m_cargoType = cargo->GetType(); m_cargoType = cargo->GetType();
dynamic_cast<CPoweredObject&>(*other).SetPower(cargo); otherAsSlotted->SetSlotContainedObject(otherSlotNum, cargo);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(other); dynamic_cast<CTransportableObject&>(*cargo).SetTransporter(other);
cargo->SetPosition(dynamic_cast<CPoweredObject&>(*other).GetPowerPosition()); cargo->SetPosition(otherAsSlotted->GetSlotPosition(otherSlotNum));
cargo->SetRotationY(0.0f); cargo->SetRotationY(0.0f);
cargo->SetRotationX(0.0f); cargo->SetRotationX(0.0f);
cargo->SetRotationZ(0.0f); cargo->SetRotationZ(0.0f);
dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(0); // carried by the base dynamic_cast<CTransportableObject&>(*cargo).SetTransporterPart(0); // carried by the base
m_object->SetCargo(nullptr); // deposit m_object->SetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING, nullptr); // deposit
} }
return true; return true;

View File

@ -57,7 +57,7 @@ public:
protected: protected:
CObject* SearchTakeObject(float &angle, float dLimit, float aLimit); 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 TransporterTakeObject();
bool TransporterDeposeObject(); bool TransporterDeposeObject();
bool IsFreeDeposeObject(glm::vec3 pos); bool IsFreeDeposeObject(glm::vec3 pos);

View File

@ -30,7 +30,7 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.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/motionant.h"
#include "object/motion/motionspider.h" #include "object/motion/motionspider.h"
@ -54,7 +54,7 @@ CTaskTerraform::CTaskTerraform(COldObject* object) : CForegroundTask(object)
m_lastParticle = 0.0f; m_lastParticle = 0.0f;
m_soundChannel = -1; m_soundChannel = -1;
assert(m_object->Implements(ObjectInterfaceType::Powered)); assert(m_object->GetNumSlots() == 1);
} }
// Object's destructor. // Object's destructor.
@ -96,7 +96,7 @@ bool CTaskTerraform::EventProcess(const Event &event)
m_object->SetScale(1.0f+m_progress*0.2f); m_object->SetScale(1.0f+m_progress*0.2f);
power = m_object->GetPower(); power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER);
if (power != nullptr) if (power != nullptr)
{ {
power->SetScale(1.0f+m_progress*1.0f); power->SetScale(1.0f+m_progress*1.0f);
@ -211,7 +211,7 @@ Error CTaskTerraform::Start()
type = m_object->GetType(); type = m_object->GetType();
if ( type != OBJECT_MOBILErt ) return ERR_WRONG_BOT; 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; if ( power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer) ) return ERR_TERRA_ENERGY;
energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergy(); energy = dynamic_cast<CPowerContainerObject&>(*power).GetEnergy();
if ( energy < ENERGY_TERRA+0.05f ) return ERR_TERRA_ENERGY; 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->SetCirVibration(glm::vec3(0.0f, 0.0f, 0.0f));
m_object->SetScale(1.0f); m_object->SetScale(1.0f);
power = m_object->GetPower(); power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER);
if (power != nullptr) if (power != nullptr)
{ {
power->SetScale(1.0f); power->SetScale(1.0f);
@ -333,7 +333,7 @@ bool CTaskTerraform::Abort()
m_object->SetCirVibration(glm::vec3(0.0f, 0.0f, 0.0f)); m_object->SetCirVibration(glm::vec3(0.0f, 0.0f, 0.0f));
m_object->SetScale(1.0f); m_object->SetScale(1.0f);
power = m_object->GetPower(); power = m_object->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER);
if (power != nullptr) if (power != nullptr)
{ {
power->SetScale(1.0f); power->SetScale(1.0f);

View File

@ -43,9 +43,8 @@
#include "object/object_manager.h" #include "object/object_manager.h"
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/carrier_object.h"
#include "object/interface/jostleable_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/interface/transportable_object.h"
#include "object/motion/motion.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<CPowerContainerObject*>(dynamic_cast<CPoweredObject&>(*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? if ( GetObjectEnergy(m_object) == 0.0f ) // no battery or flat?
{ {
motorSpeed.x = 0.0f; motorSpeed.x = 0.0f;
@ -2963,10 +2962,11 @@ void CPhysics::PowerParticle(float factor, bool bBreak)
glm::vec2 dim; glm::vec2 dim;
bool bCarryPower; bool bCarryPower;
// TODO: it should be all slots that contain a power cell that can get recharged. Not just the carrying slot.
bCarryPower = false; bCarryPower = false;
if (m_object->Implements(ObjectInterfaceType::Carrier)) if (m_object->Implements(ObjectInterfaceType::Slotted))
{ {
CObject* cargo = dynamic_cast<CCarrierObject&>(*m_object).GetCargo(); CObject* cargo = dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING);
if ( cargo != nullptr && cargo->Implements(ObjectInterfaceType::PowerContainer) && if ( cargo != nullptr && cargo->Implements(ObjectInterfaceType::PowerContainer) &&
dynamic_cast<CPowerContainerObject&>(*cargo).IsRechargeable() && dynamic_cast<CPowerContainerObject&>(*cargo).IsRechargeable() &&
m_object->GetPartRotationZ(1) == ARM_STOCK_ANGLE1 ) 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); 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.x -= 0.3f;
pos.y += 1.0f; // battery center position pos.y += 1.0f; // battery center position
pos = Math::Transform(mat, pos); 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<CPoweredObject&>(*m_object).GetPower(); // searches for the object battery used CPowerContainerObject* power = GetObjectPowerCell(m_object); // searches for the object battery used
if (power == nullptr || !power->Implements(ObjectInterfaceType::PowerContainer)) if (power == nullptr)
{ {
return ERR_VEH_POWER; return ERR_VEH_POWER;
} }
else else
{ {
if ( dynamic_cast<CPowerContainerObject&>(*power).GetEnergy() == 0.0f ) return ERR_VEH_ENERGY; if ( power->GetEnergy() == 0.0f ) return ERR_VEH_ENERGY;
} }
} }

View File

@ -744,15 +744,18 @@ bool CScriptFunctions::rDelete(CBotVar* var, CBotVar* result, int& exception, vo
} }
else else
{ {
if (obj->Implements(ObjectInterfaceType::Old)) if (obj->Implements(ObjectInterfaceType::Slotted))
{ {
COldObject* oldobj = dynamic_cast<COldObject*>(obj); CSlottedObject* slotted = dynamic_cast<CSlottedObject*>(obj);
if (oldobj->GetPower() != nullptr) for (int slotNum = slotted->GetNumSlots() - 1; slotNum >= 0; slotNum--)
CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetPower()); {
if (oldobj->GetCargo() != nullptr) CObject* sub = slotted->GetSlotContainedObject(slotNum);
CObjectManager::GetInstancePointer()->DeleteObject(oldobj->GetCargo()); if (sub != nullptr)
oldobj->SetPower(nullptr); {
oldobj->SetCargo(nullptr); slotted->SetSlotContainedObject(slotNum, nullptr);
CObjectManager::GetInstancePointer()->DeleteObject(sub);
}
}
} }
CObjectManager::GetInstancePointer()->DeleteObject(obj); CObjectManager::GetInstancePointer()->DeleteObject(obj);
} }
@ -3686,9 +3689,10 @@ void CScriptFunctions::uObject(CBotVar* botThis, void* user)
// Updates the type of battery. // Updates the type of battery.
pVar = pVar->GetNext(); // "energyCell" pVar = pVar->GetNext(); // "energyCell"
if (object->Implements(ObjectInterfaceType::Powered)) CSlottedObject *asSlotted = object->Implements(ObjectInterfaceType::Slotted) ? dynamic_cast<CSlottedObject*>(object) : nullptr;
if (asSlotted != nullptr && asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::POWER) >= 0)
{ {
CObject* power = dynamic_cast<CPoweredObject*>(object)->GetPower(); CObject *power = asSlotted->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::POWER);
if (power == nullptr) if (power == nullptr)
{ {
pVar->SetPointer(nullptr); pVar->SetPointer(nullptr);
@ -3701,9 +3705,9 @@ void CScriptFunctions::uObject(CBotVar* botThis, void* user)
// Updates the transported object's type. // Updates the transported object's type.
pVar = pVar->GetNext(); // "load" pVar = pVar->GetNext(); // "load"
if (object->Implements(ObjectInterfaceType::Carrier)) if (asSlotted != nullptr && asSlotted->MapPseudoSlot(CSlottedObject::Pseudoslot::CARRYING) >= 0)
{ {
CObject* cargo = dynamic_cast<CCarrierObject*>(object)->GetCargo(); CObject* cargo = asSlotted->GetSlotContainedObjectReq(CSlottedObject::Pseudoslot::CARRYING);
if (cargo == nullptr) if (cargo == nullptr)
{ {
pVar->SetPointer(nullptr); pVar->SetPointer(nullptr);

View File

@ -35,9 +35,8 @@
#include "object/old_object.h" #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/programmable_object.h"
#include "object/interface/slotted_object.h"
#include "object/interface/task_executor_object.h" #include "object/interface/task_executor_object.h"
#include "object/motion/motion.h" #include "object/motion/motion.h"
@ -1373,7 +1372,7 @@ bool CObjectInterface::CreateInterface(bool bSelect)
pw->CreateButton(pos, dim, 10, EVENT_OBJECT_DESELECT); 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.x = ox+sx*14.5f;
pos.y = oy+sy*0; pos.y = oy+sy*0;
@ -1576,15 +1575,10 @@ void CObjectInterface::UpdateInterface(float rTime)
float energy = 0.0f; float energy = 0.0f;
float limit = 0.0f; float limit = 0.0f;
if (m_object->Implements(ObjectInterfaceType::Powered)) if (CPowerContainerObject* powerContainer = GetObjectPowerCell(m_object))
{ {
CObject* power = dynamic_cast<CPoweredObject*>(m_object)->GetPower(); energy = powerContainer->GetEnergyLevel();
if (power != nullptr && power->Implements(ObjectInterfaceType::PowerContainer)) limit = powerContainer->GetEnergy();
{
CPowerContainerObject* powerContainer = dynamic_cast<CPowerContainerObject*>(power);
energy = powerContainer->GetEnergyLevel();
limit = powerContainer->GetEnergy();
}
} }
icon = 0; // red/green icon = 0; // red/green
@ -1909,7 +1903,7 @@ void CObjectInterface::UpdateInterface()
bFly = bEnable; bFly = bEnable;
if ( bFly && (type == OBJECT_HUMAN || type == OBJECT_TECH) ) if ( bFly && (type == OBJECT_HUMAN || type == OBJECT_TECH) )
{ {
if (m_object->Implements(ObjectInterfaceType::Carrier) && dynamic_cast<CCarrierObject*>(m_object)->IsCarryingCargo()) if (dynamic_cast<CSlottedObject&>(*m_object).GetSlotContainedObjectOpt(CSlottedObject::Pseudoslot::CARRYING) != nullptr)
bFly = false; bFly = false;
} }
EnableInterface(pw, EVENT_OBJECT_GASUP, bFly && m_main->CanPlayerInteract()); EnableInterface(pw, EVENT_OBJECT_GASUP, bFly && m_main->CanPlayerInteract());

View File

@ -0,0 +1,285 @@
#include "CBot/CBotFileUtils.h"
#include <gtest/gtest.h>
namespace
{
std::string createTestStringWithAllPossibleCharacters()
{
std::string testString;
for (char c = std::numeric_limits<char>::min(); ; ++c)
{
testString.push_back(c);
if (c == std::numeric_limits<char>::max()) break;
}
return testString;
}
}
namespace CBot
{
struct CBotFileUtilsTest : public testing::Test
{
std::stringstream stream;
};
struct CBotFileUtilsReadWriteByteTest : public CBotFileUtilsTest, public testing::WithParamInterface<char>
{
};
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<char>(42),
std::numeric_limits<char>::min(),
std::numeric_limits<char>::max()));
struct CBotFileUtilsReadWriteWordTest : public CBotFileUtilsTest, public testing::WithParamInterface<unsigned short>
{
};
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<unsigned short>(0),
static_cast<unsigned short>(42),
std::numeric_limits<unsigned short>::min(),
std::numeric_limits<unsigned short>::max() / 2,
std::numeric_limits<unsigned short>::max()));
struct CBotFileUtilsReadWriteShortTest : public CBotFileUtilsTest, public testing::WithParamInterface<short>
{
};
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<short>(-7),
static_cast<short>(-1),
static_cast<short>(0),
static_cast<short>(42),
std::numeric_limits<short>::min(),
std::numeric_limits<short>::min() / 2,
std::numeric_limits<short>::max() / 2,
std::numeric_limits<short>::max()));
struct CBotFileUtilsReadWriteUInt32Test : public CBotFileUtilsTest, public testing::WithParamInterface<uint32_t>
{
};
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<uint32_t>(0),
static_cast<uint32_t>(42),
std::numeric_limits<uint32_t>::max() / 2,
std::numeric_limits<uint32_t>::max()));
struct CBotFileUtilsReadWriteIntTest : public CBotFileUtilsTest, public testing::WithParamInterface<int>
{
};
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<int>(7),
static_cast<int>(-1),
static_cast<int>(0),
static_cast<int>(42),
std::numeric_limits<int>::min(),
std::numeric_limits<int>::min() / 2,
std::numeric_limits<int>::max() / 2,
std::numeric_limits<int>::max()));
struct CBotFileUtilsReadWriteLongTest : public CBotFileUtilsTest, public testing::WithParamInterface<long>
{
};
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<long>(7),
static_cast<long>(-1),
static_cast<long>(0),
static_cast<long>(42),
std::numeric_limits<long>::min(),
std::numeric_limits<long>::min() / 2,
std::numeric_limits<long>::max() / 2,
std::numeric_limits<long>::max()));
struct CBotFileUtilsReadWriteFloatTest : public CBotFileUtilsTest, public testing::WithParamInterface<float>
{
};
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<float>::min(),
std::numeric_limits<float>::min() / 2.0f,
std::numeric_limits<float>::max() / 2.0f,
std::numeric_limits<float>::max()));
struct CBotFileUtilsReadWriteDoubleTest : public CBotFileUtilsTest, public testing::WithParamInterface<double>
{
};
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<double>::min(),
std::numeric_limits<double>::min() / 2.0,
std::numeric_limits<double>::max() / 2.0,
std::numeric_limits<double>::max()));
struct CBotFileUtilsReadWriteStringTest : public CBotFileUtilsTest, public testing::WithParamInterface<std::string>
{
};
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());
}
}

View File

@ -320,200 +320,6 @@ protected:
} }
}; };
TEST_F(CBotUT, TestSaveStateIOFunctions)
{
std::stringstream sstr("");
std::string teststring;
for (char c = std::numeric_limits<char>::min() ;; ++c)
{
teststring.push_back(c);
if ( c == std::numeric_limits<char>::max() ) break;
}
auto CallWriteFunctions = [&sstr, &teststring]() -> bool
{
if (!WriteWord(sstr, static_cast<unsigned short>(0))) return false;
if (!WriteWord(sstr, std::numeric_limits<unsigned short>::max() / 2)) return false;
if (!WriteWord(sstr, std::numeric_limits<unsigned short>::max())) return false;
if (!WriteByte(sstr, std::numeric_limits<char>::min())) return false;
if (!WriteByte(sstr, std::numeric_limits<char>::max())) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::min())) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::min() / 2)) return false;
if (!WriteShort(sstr, -1)) return false;
if (!WriteShort(sstr, 0)) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::max() / 2)) return false;
if (!WriteShort(sstr, std::numeric_limits<short>::max())) return false;
if (!WriteUInt32(sstr, static_cast<uint32_t>(0))) return false;
if (!WriteUInt32(sstr, std::numeric_limits<uint32_t>::max() / 2)) return false;
if (!WriteUInt32(sstr, std::numeric_limits<uint32_t>::max())) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::min())) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::min() / 2)) return false;
if (!WriteInt(sstr, -1)) return false;
if (!WriteInt(sstr, 0)) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::max() / 2)) return false;
if (!WriteInt(sstr, std::numeric_limits<int>::max())) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::min())) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::min() / 2L)) return false;
if (!WriteLong(sstr, -1L)) return false;
if (!WriteLong(sstr, 0L)) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::max() / 2L)) return false;
if (!WriteLong(sstr, std::numeric_limits<long>::max())) return false;
// test with padding bytes (not currently used anywhere)
if (!WriteLong(sstr, 1234567890L, 10)) return false;
if (!WriteFloat(sstr, std::numeric_limits<float>::min())) return false;
if (!WriteFloat(sstr, 0.0f)) return false;
if (!WriteFloat(sstr, std::numeric_limits<float>::max())) return false;
if (!WriteDouble(sstr, std::numeric_limits<double>::min())) return false;
if (!WriteDouble(sstr, 0.0)) return false;
if (!WriteDouble(sstr, std::numeric_limits<double>::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<unsigned short>(0)) return false;
if (!ReadWord(newstream, w)) return false;
if (w != std::numeric_limits<unsigned short>::max() / 2) return false;
if (!ReadWord(newstream, w)) return false;
if (w != std::numeric_limits<unsigned short>::max()) return false;
char c = 1;
if (!ReadByte(newstream, c)) return false;
if (c != std::numeric_limits<char>::min()) return false;
if (!ReadByte(newstream, c)) return false;
if (c != std::numeric_limits<char>::max()) return false;
short s = 1;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::min()) return false;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::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<short>::max() / 2) return false;
if (!ReadShort(newstream, s)) return false;
if (s != std::numeric_limits<short>::max()) return false;
uint32_t u = 1;
if (!ReadUInt32(newstream, u)) return false;
if (u != static_cast<uint32_t>(0)) return false;
if (!ReadUInt32(newstream, u)) return false;
if (u != std::numeric_limits<uint32_t>::max() / 2) return false;
if (!ReadUInt32(newstream, u)) return false;
if (u != std::numeric_limits<uint32_t>::max()) return false;
int i = 1;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::min()) return false;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::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<int>::max() / 2) return false;
if (!ReadInt(newstream, i)) return false;
if (i != std::numeric_limits<int>::max()) return false;
long l = 1L;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::min()) return false;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::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<long>::max() / 2L) return false;
if (!ReadLong(newstream, l)) return false;
if (l != std::numeric_limits<long>::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<float>::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<float>::max()) return false;
double d = 1.0;
if (!ReadDouble(newstream, d)) return false;
if (d != std::numeric_limits<double>::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<double>::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) TEST_F(CBotUT, EmptyTest)
{ {
ExecuteTest( ExecuteTest(

View File

@ -11,8 +11,9 @@ add_definitions(-DGTEST_HAS_TR1_TUPLE=0)
add_executable(colobot_ut add_executable(colobot_ut
main.cpp main.cpp
app/app_test.cpp app/app_test.cpp
CBot/CBotToken_test.cpp
CBot/CBot_test.cpp CBot/CBot_test.cpp
CBot/CBotFileUtils_test.cpp
CBot/CBotToken_test.cpp
common/config_file_test.cpp common/config_file_test.cpp
common/timeutils_test.cpp common/timeutils_test.cpp
graphics/engine/lightman_test.cpp graphics/engine/lightman_test.cpp