Compare commits

...

12 Commits

25 changed files with 757 additions and 91 deletions

View File

@ -369,6 +369,7 @@ set(BASE_SOURCES
object/interface/flying_object.h object/interface/flying_object.h
object/interface/fragile_object.h object/interface/fragile_object.h
object/interface/interactive_object.h object/interface/interactive_object.h
object/interface/liquid_container_object.h
object/interface/jet_flying_object.h object/interface/jet_flying_object.h
object/interface/jostleable_object.h object/interface/jostleable_object.h
object/interface/movable_object.h object/interface/movable_object.h
@ -426,12 +427,15 @@ set(BASE_SOURCES
object/subclass/base_robot.h object/subclass/base_robot.h
object/subclass/base_vehicle.cpp object/subclass/base_vehicle.cpp
object/subclass/base_vehicle.h object/subclass/base_vehicle.h
object/subclass/bucket.cpp
object/subclass/exchange_post.cpp object/subclass/exchange_post.cpp
object/subclass/exchange_post.h object/subclass/exchange_post.h
object/subclass/shielder.cpp object/subclass/shielder.cpp
object/subclass/shielder.h object/subclass/shielder.h
object/subclass/static_object.cpp object/subclass/static_object.cpp
object/subclass/static_object.h object/subclass/static_object.h
object/subclass/water_pump.cpp
object/subclass/water_pump.h
object/task/task.cpp object/task/task.cpp
object/task/task.h object/task/task.h
object/task/taskadvance.cpp object/task/taskadvance.cpp

View File

@ -77,6 +77,8 @@ enum Error
ERR_RESEARCH_ENERGY = 321, //!< more energy ERR_RESEARCH_ENERGY = 321, //!< more energy
ERR_RESEARCH_TYPE = 322, //!< the wrong type of battery ERR_RESEARCH_TYPE = 322, //!< the wrong type of battery
ERR_RESEARCH_ALREADY = 323, //!< research already done ERR_RESEARCH_ALREADY = 323, //!< research already done
ERR_RESEARCH_NEED_COOLANT = 324, //!< need coolant
ERR_RESEARCH_MORE_COOLANT = 325, //!< need more coolant
ERR_ENERGY_NULL = 330, //!< no energy underground ERR_ENERGY_NULL = 330, //!< no energy underground
ERR_ENERGY_LOW = 331, //!< not enough energy ERR_ENERGY_LOW = 331, //!< not enough energy
ERR_ENERGY_EMPTY = 332, //!< lack of metal ERR_ENERGY_EMPTY = 332, //!< lack of metal

View File

@ -567,6 +567,8 @@ void InitializeRestext()
stringsObject[OBJECT_APOLLO4] = TR("Remains of Apollo mission"); stringsObject[OBJECT_APOLLO4] = TR("Remains of Apollo mission");
stringsObject[OBJECT_APOLLO5] = TR("Remains of Apollo mission"); stringsObject[OBJECT_APOLLO5] = TR("Remains of Apollo mission");
stringsObject[OBJECT_APOLLO2] = TR("Lunar Roving Vehicle"); stringsObject[OBJECT_APOLLO2] = TR("Lunar Roving Vehicle");
stringsObject[OBJECT_WATERPUMP] = TR("Water pump");
stringsObject[OBJECT_BUCKET] = TR("Bucket");
@ -614,6 +616,8 @@ void InitializeRestext()
stringsErr[ERR_RESEARCH_ENERGY] = TR("Not enough energy"); stringsErr[ERR_RESEARCH_ENERGY] = TR("Not enough energy");
stringsErr[ERR_RESEARCH_TYPE] = TR("Inappropriate cell type"); stringsErr[ERR_RESEARCH_TYPE] = TR("Inappropriate cell type");
stringsErr[ERR_RESEARCH_ALREADY]= TR("Research program already performed"); stringsErr[ERR_RESEARCH_ALREADY]= TR("Research program already performed");
stringsErr[ERR_RESEARCH_NEED_COOLANT] = TR("No coolant cell");
stringsErr[ERR_RESEARCH_MORE_COOLANT] = TR("Not enough coolant");
stringsErr[ERR_ENERGY_NULL] = TR("No energy in the subsoil"); stringsErr[ERR_ENERGY_NULL] = TR("No energy in the subsoil");
stringsErr[ERR_ENERGY_LOW] = TR("Not enough energy yet"); stringsErr[ERR_ENERGY_LOW] = TR("Not enough energy yet");
stringsErr[ERR_ENERGY_EMPTY] = TR("No titanium to transform"); stringsErr[ERR_ENERGY_EMPTY] = TR("No titanium to transform");

View File

@ -924,7 +924,8 @@ void CCamera::IsCollisionFix(Math::Vector &eye, Math::Vector lookat)
type == OBJECT_ANT || type == OBJECT_ANT ||
type == OBJECT_SPIDER || type == OBJECT_SPIDER ||
type == OBJECT_BEE || type == OBJECT_BEE ||
type == OBJECT_WORM ) continue; type == OBJECT_WORM ||
type == OBJECT_BUCKET ) continue;
Math::Sphere objSphere = obj->GetCameraCollisionSphere(); Math::Sphere objSphere = obj->GetCameraCollisionSphere();
Math::Vector objPos = objSphere.pos; Math::Vector objPos = objSphere.pos;

View File

@ -57,7 +57,18 @@ bool COldModelManager::LoadModel(const std::string& fileName, bool mirrored, int
if (!stream.is_open()) if (!stream.is_open())
throw CModelIOException(std::string("Could not open file '") + fileName + "'"); throw CModelIOException(std::string("Could not open file '") + fileName + "'");
model = ModelInput::Read(stream, ModelFormat::Old); std::string::size_type extension_index = fileName.find_last_of('.');
if (extension_index == std::string::npos)
throw CModelIOException(std::string("Filename '") + fileName + "' has no extension");
std::string extension = fileName.substr(extension_index + 1);
if (extension == "mod")
model = ModelInput::Read(stream, ModelFormat::Old);
else if (extension == "txt")
model = ModelInput::Read(stream, ModelFormat::Text);
else
throw CModelIOException(std::string("Filename '") + fileName + "' has unknown extension");
} }
catch (const CModelIOException& e) catch (const CModelIOException& e)
{ {

View File

@ -533,6 +533,8 @@ ObjectType CLevelParserParam::ToObjectType(std::string value)
if (value == "Me" ) return OBJECT_HUMAN; if (value == "Me" ) return OBJECT_HUMAN;
if (value == "Tech" ) return OBJECT_TECH; if (value == "Tech" ) return OBJECT_TECH;
if (value == "MissionController" ) return OBJECT_CONTROLLER; if (value == "MissionController" ) return OBJECT_CONTROLLER;
if (value == "WaterPump" ) return OBJECT_WATERPUMP;
if (value == "Bucket" ) return OBJECT_BUCKET;
return static_cast<ObjectType>(Cast<int>(value, "object")); return static_cast<ObjectType>(Cast<int>(value, "object"));
} }
@ -730,6 +732,8 @@ const std::string CLevelParserParam::FromObjectType(ObjectType value)
if (value == OBJECT_HUMAN ) return "Me"; if (value == OBJECT_HUMAN ) return "Me";
if (value == OBJECT_TECH ) return "Tech"; if (value == OBJECT_TECH ) return "Tech";
if (value == OBJECT_CONTROLLER ) return "MissionController"; if (value == OBJECT_CONTROLLER ) return "MissionController";
if (value == OBJECT_WATERPUMP ) return "WaterPump";
if (value == OBJECT_BUCKET ) return "Bucket";
return boost::lexical_cast<std::string>(static_cast<int>(value)); return boost::lexical_cast<std::string>(static_cast<int>(value));
} }

View File

@ -33,6 +33,8 @@
#include "object/old_object.h" #include "object/old_object.h"
#include "object/interface/powered_object.h" #include "object/interface/powered_object.h"
#include "object/interface/liquid_container_object.h"
#include "object/interface/slotted_object.h"
#include "sound/sound.h" #include "sound/sound.h"
@ -127,6 +129,12 @@ Error CAutoResearch::StartAction(int param)
{ {
return ERR_RESEARCH_ENERGY; return ERR_RESEARCH_ENERGY;
} }
CObject* coolantObj = dynamic_cast<CSlottedObject*>(m_object)->GetSlotContainedObject(0);
if (coolantObj == nullptr || !coolantObj->Implements(ObjectInterfaceType::LiquidContainer))
return ERR_RESEARCH_NEED_COOLANT;
CLiquidContainerObject *coolantAsLiq = dynamic_cast<CLiquidContainerObject*>(coolantObj);
if (coolantAsLiq->GetLiquidAmount() < 1.0f || coolantAsLiq->GetLiquidType() != LiquidType::WATER)
return ERR_RESEARCH_MORE_COOLANT;
float time = SEARCH_TIME; float time = SEARCH_TIME;
if ( m_research == RESEARCH_TANK ) time *= 0.3f; if ( m_research == RESEARCH_TANK ) time *= 0.3f;
@ -221,7 +229,11 @@ 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* coolantObj = dynamic_cast<CSlottedObject*>(m_object)->GetSlotContainedObject(0);
if ( m_object->GetPower() == nullptr || !m_object->GetPower()->Implements(ObjectInterfaceType::PowerContainer) // more battery?
|| coolantObj == nullptr || !coolantObj->Implements(ObjectInterfaceType::LiquidContainer) // coolant cell removed?
|| dynamic_cast<CLiquidContainerObject*>(coolantObj)->GetLiquidType() != LiquidType::WATER)
{ {
SetBusy(false); SetBusy(false);
UpdateInterface(); UpdateInterface();
@ -233,6 +245,7 @@ bool CAutoResearch::EventProcess(const Event &event)
} }
power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower()); power = dynamic_cast<CPowerContainerObject*>(m_object->GetPower());
power->SetEnergyLevel(1.0f-m_progress); power->SetEnergyLevel(1.0f-m_progress);
dynamic_cast<CLiquidContainerObject*>(coolantObj)->SetLiquid(LiquidType::WATER, 1.0f - m_progress);
if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time ) if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
{ {

View File

@ -0,0 +1,56 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2018, 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_interface_type.h"
#include <assert.h>
enum class LiquidType {
EMPTY,
WATER
};
/**
* \class CLiquidContainerObject
* \brief Interface for buckets
*/
class CLiquidContainerObject
{
protected:
LiquidType m_liquidType = LiquidType::EMPTY;
float m_liquidAmount = 0.0f;
public:
explicit CLiquidContainerObject(ObjectInterfaceTypes& types)
{
types[static_cast<int>(ObjectInterfaceType::LiquidContainer)] = true;
}
virtual ~CLiquidContainerObject()
{}
void SetLiquid(LiquidType type, float level) {
assert(level >= 0.0f && level <= 1.0f);
assert(type != LiquidType::EMPTY || level == 0.0f);
m_liquidType = (level == 0.0f ? LiquidType::EMPTY : type);
m_liquidAmount = level;
}
LiquidType GetLiquidType() const {return m_liquidType;}
float GetLiquidAmount() const {return m_liquidAmount;}
};

View File

@ -0,0 +1,53 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2018, 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_interface_type.h"
#include "math/vector.h"
class CObject;
/**
* \class CSlottedObject
* \brief Interface for objects that hold other objects
*/
class CSlottedObject
{
public:
explicit CSlottedObject(ObjectInterfaceTypes& types)
{
types[static_cast<int>(ObjectInterfaceType::Slotted)] = true;
}
virtual ~CSlottedObject()
{}
//! Get number of slots. Valid slot numbers are 0 up to GetNumSlots()-1. Using invalid slot numbers in the other functions will crash the game.
virtual int GetNumSlots() = 0;
//! Get relative position of a slot.
virtual Math::Vector GetSlotPosition(int slotNum) = 0;
//! Get relative angle (in radians) where robot should be positioned when inserting into a slot.
virtual float GetSlotAngle(int slotNum) = 0;
//! Get the maximum angular distance from the ideal angle (in radians) where robot should be positioned when inserting into a slot.
virtual float GetSlotAcceptanceAngle(int slotNum) = 0;
//! Get object contained in a slot.
virtual CObject *GetSlotContainedObject(int slotNum) = 0;
//! Set object contained in a slot.
virtual void SetSlotContainedObject(int slotNum, CObject *object) = 0;
};

View File

@ -211,6 +211,7 @@ public:
//! \todo It will work like this for now but later I'd like to refactor this to something more manageable ~krzys_h //! \todo It will work like this for now but later I'd like to refactor this to something more manageable ~krzys_h
virtual bool IsBulletWall() { return false; } virtual bool IsBulletWall() { return false; }
protected: protected:
//! Transform crash sphere by object's world matrix //! Transform crash sphere by object's world matrix
virtual void TransformCrashSphere(Math::Sphere& crashSphere) = 0; virtual void TransformCrashSphere(Math::Sphere& crashSphere) = 0;

View File

@ -46,6 +46,7 @@
#include "object/subclass/base_building.h" #include "object/subclass/base_building.h"
#include "object/subclass/base_robot.h" #include "object/subclass/base_robot.h"
#include "object/subclass/exchange_post.h" #include "object/subclass/exchange_post.h"
#include "object/subclass/water_pump.h"
#include "object/subclass/shielder.h" #include "object/subclass/shielder.h"
#include "object/subclass/static_object.h" #include "object/subclass/static_object.h"
@ -87,6 +88,13 @@ CObjectUPtr CObjectFactory::CreateObject(const ObjectCreateParams& params)
case OBJECT_INFO: case OBJECT_INFO:
return CExchangePost::Create(params, m_oldModelManager, m_engine); return CExchangePost::Create(params, m_oldModelManager, m_engine);
case OBJECT_WATERPUMP:
return CreateObjectWaterPump(params, m_oldModelManager, m_modelManager, m_engine);
case OBJECT_BUCKET:
std::unique_ptr<CObject> CreateObjectBucket(const ObjectCreateParams&, Gfx::COldModelManager*, Gfx::CEngine*);
return CreateObjectBucket(params, m_oldModelManager, m_engine);
case OBJECT_PORTICO: case OBJECT_PORTICO:
case OBJECT_BASE: case OBJECT_BASE:
case OBJECT_DERRICK: case OBJECT_DERRICK:

View File

@ -45,6 +45,8 @@ struct ObjectCreateParams;
using CObjectUPtr = std::unique_ptr<CObject>; using CObjectUPtr = std::unique_ptr<CObject>;
std::unique_ptr<CObject> CreateObjectBucket(const ObjectCreateParams &params, Gfx::COldModelManager *modelManager, Gfx::CEngine *graphicsEngine);
class CObjectFactory class CObjectFactory
{ {
public: public:

View File

@ -54,6 +54,8 @@ 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
LiquidContainer, //!< liquid container
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

@ -230,6 +230,8 @@ enum ObjectType
OBJECT_APOLLO4 = 903, //!< ApolloModule OBJECT_APOLLO4 = 903, //!< ApolloModule
OBJECT_APOLLO5 = 904, //!< ApolloAntenna OBJECT_APOLLO5 = 904, //!< ApolloAntenna
OBJECT_HOME1 = 910, //!< Home OBJECT_HOME1 = 910, //!< Home
OBJECT_WATERPUMP = 920, //!< WaterPump
OBJECT_BUCKET = 921, //!< Bucket
OBJECT_MAX = 1000 //!< number of values OBJECT_MAX = 1000 //!< number of values
}; };

View File

@ -747,7 +747,8 @@ void COldObject::SetType(ObjectType type)
m_type == OBJECT_RESEARCH || m_type == OBJECT_RESEARCH ||
m_type == OBJECT_ENERGY || m_type == OBJECT_ENERGY ||
m_type == OBJECT_LABO || m_type == OBJECT_LABO ||
m_type == OBJECT_NUCLEAR ) m_type == OBJECT_NUCLEAR ||
m_type == OBJECT_WATERPUMP)
{ {
m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Powered)] = true; m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Powered)] = true;
} }
@ -772,6 +773,7 @@ void COldObject::SetType(ObjectType type)
m_type == OBJECT_SPIDER || m_type == OBJECT_SPIDER ||
m_type == OBJECT_BEE || m_type == OBJECT_BEE ||
m_type == OBJECT_TEEN28 ) m_type == OBJECT_TEEN28 )
// TODO OBJECT_BUCKET
{ {
m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Damageable)] = true; m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Damageable)] = true;
m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Destroyable)] = true; m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Destroyable)] = true;
@ -3204,13 +3206,15 @@ float COldObject::GetLightningHitProbability()
m_type == OBJECT_NUCLEAR || m_type == OBJECT_NUCLEAR ||
m_type == OBJECT_PARA || m_type == OBJECT_PARA ||
m_type == OBJECT_SAFE || m_type == OBJECT_SAFE ||
m_type == OBJECT_HUSTON ) // building? m_type == OBJECT_HUSTON ||
m_type == OBJECT_WATERPUMP) // building?
{ {
return 1.0f; return 1.0f;
} }
if ( m_type == OBJECT_METAL || if ( m_type == OBJECT_METAL ||
m_type == OBJECT_POWER || m_type == OBJECT_POWER ||
m_type == OBJECT_ATOMIC ) // resource? m_type == OBJECT_ATOMIC ||
m_type == OBJECT_BUCKET ) // resource?
{ {
return 0.3f; return 0.3f;
} }

View File

@ -18,6 +18,7 @@
*/ */
#include "object/subclass/base_building.h" #include "object/subclass/base_building.h"
#include "object/interface/slotted_object.h"
#include "common/make_unique.h" #include "common/make_unique.h"
@ -47,6 +48,41 @@
#include "object/auto/autotower.h" #include "object/auto/autotower.h"
#include "object/auto/autovault.h" #include "object/auto/autovault.h"
class CResearchLab : public CBaseBuilding, public CSlottedObject {
public:
CResearchLab(int id)
: CBaseBuilding(id, OBJECT_RESEARCH)
, CSlottedObject(m_implementedInterfaces)
{
}
CObject *m_extraSlotObject = nullptr;
virtual int GetNumSlots() override {
return 1;
}
virtual Math::Vector GetSlotPosition(int slotNum) override {
assert(slotNum == 0);
return Math::Vector(-7.5f, 3.0f, 0.0f); // opposite the power cell slot
}
virtual float GetSlotAngle(int slotNum) override {
assert(slotNum == 0);
return Math::PI; // 180 degrees
}
virtual float GetSlotAcceptanceAngle(int slotNum) override {
assert(slotNum == 0);
return 45.0f*Math::PI/180.0f; // up to 45 degree offset is allowed, in either direction
}
virtual CObject *GetSlotContainedObject(int slotNum) override {
assert(slotNum == 0);
return m_extraSlotObject;
}
virtual void SetSlotContainedObject(int slotNum, CObject *object) override {
assert(slotNum == 0);
m_extraSlotObject = object;
}
};
CBaseBuilding::CBaseBuilding(int id, ObjectType type) CBaseBuilding::CBaseBuilding(int id, ObjectType type)
: COldObject(id) : COldObject(id)
@ -62,7 +98,7 @@ std::unique_ptr<CBaseBuilding> CBaseBuilding::Create(
Gfx::COldModelManager* modelManager, Gfx::COldModelManager* modelManager,
Gfx::CEngine* engine) Gfx::CEngine* engine)
{ {
auto obj = MakeUnique<CBaseBuilding>(params.id, params.type); auto obj = (params.type == OBJECT_RESEARCH ? MakeUnique<CResearchLab>(params.id) : MakeUnique<CBaseBuilding>(params.id, params.type));
obj->SetTeam(params.team); obj->SetTeam(params.team);
@ -272,7 +308,7 @@ std::unique_ptr<CBaseBuilding> CBaseBuilding::Create(
if ( params.type == OBJECT_RESEARCH ) if ( params.type == OBJECT_RESEARCH )
{ {
modelManager->AddModelReference("search1.mod", false, rank, params.team); modelManager->AddModelReference("search1_with_water.txt", false, rank, params.team);
obj->SetPosition(params.pos); obj->SetPosition(params.pos);
obj->SetRotationY(params.angle); obj->SetRotationY(params.angle);
obj->SetFloorHeight(0.0f); obj->SetFloorHeight(0.0f);

View File

@ -0,0 +1,112 @@
/*
* 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
*/
#include "object/object.h"
#include "object/old_object.h"
#include "object/interface/liquid_container_object.h"
#include "object/object_factory.h"
#include "graphics/engine/engine.h"
#include "graphics/engine/oldmodelmanager.h"
#include "common/make_unique.h"
#include "level/parser/parserexceptions.h"
#include "level/parser/parserline.h"
#include "level/parser/parserparam.h"
#include "object/object_create_params.h"
#include <boost/lexical_cast.hpp>
class CBucket
: public COldObject
, public CLiquidContainerObject
{
LiquidType m_displayedLiquidType = LiquidType::EMPTY;
float m_displayedLiquidAmount = -100; // force initial update
public:
CBucket(int id)
: COldObject(id)
, CLiquidContainerObject(m_implementedInterfaces)
{
SetType(OBJECT_BUCKET);
m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Transportable)] = true;
}
bool EventProcess(const Event& event) override {
if (!COldObject::EventProcess(event))
return false;
if (event.type == EVENT_FRAME && (m_displayedLiquidType != m_liquidType || !Math::IsEqual(m_displayedLiquidAmount, m_liquidAmount, 0.003f))) {
UpdateTextureMapping();
m_displayedLiquidType = m_liquidType;
m_displayedLiquidAmount = m_liquidAmount;
}
return true;
}
private:
void UpdateTextureMapping() {
Gfx::Material mat;
mat.diffuse = Gfx::Color(1.0f, 1.0f, 1.0f); // white
mat.ambient = Gfx::Color(0.5f, 0.5f, 0.5f);
int type = static_cast<int>(m_liquidType) - 1;
if(type > 0) type--; // don't use a texture slot for EMPTY liquid type, just reuse whatever liquid comes first
float vcoord = (type + 0.5f) / 128.0f;
// Y coordinate ranges from 0 to 2.
// If m_liquidAmount is 0, u coordinate ranges from 0 to 0.5.
// If m_liquidAmount is 1, u coordinate ranges from 0.5 to 1.
// In any case, v is a constant depending on the liquid type.
// The highest u coordinate needs to be at the lowest Y coordinate.
m_engine->ChangeTextureMapping(m_objectPart[0].object,
mat, Gfx::ENG_RSTATE_PART3, "objects/liquid.png", "",
Gfx::ENG_TEX_MAPPING_X,
0.0f, vcoord, -0.25f, 0.5f + 0.5f*m_liquidAmount);
}
};
std::unique_ptr<CObject> CreateObjectBucket(const ObjectCreateParams &params, Gfx::COldModelManager *modelManager, Gfx::CEngine *graphicsEngine)
{
std::unique_ptr<CBucket> newObj = MakeUnique<CBucket>(params.id);
int rootGraphObj = graphicsEngine->CreateObject();
graphicsEngine->SetObjectType(rootGraphObj, Gfx::ENG_OBJTYPE_FIX); // XXX: why is ENG_OBJTYPE_FIX used for movable objects? (this is the same as for powercells, titanium, etc)
newObj->SetObjectRank(0, rootGraphObj);
modelManager->AddModelCopy("bucket.txt", false, rootGraphObj);
newObj->SetPosition(params.pos);
newObj->SetRotationY(params.angle);
// standard settings for transportable objects
newObj->AddCrashSphere(CrashSphere(Math::Vector(0.0f, 1.0f, 0.0f), 1.0f, SOUND_BOUMm, 0.45f));
newObj->SetCameraCollisionSphere(Math::Sphere(Math::Vector(0.0f, 1.0f, 0.0f), 1.5f));
newObj->CreateShadowCircle(1.5f, 1.0f);
newObj->SetFloorHeight(0.0f);
newObj->FloorAdjust();
graphicsEngine->LoadAllTextures();
return newObj;
}

View File

@ -0,0 +1,162 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2018, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "object/subclass/water_pump.h"
#include "object/interface/liquid_container_object.h"
#include "common/make_unique.h"
#include "common/regex_utils.h"
#include "graphics/engine/oldmodelmanager.h" // XXX delete
#include "graphics/model/model_manager.h"
#include "level/parser/parserexceptions.h"
#include "level/parser/parserline.h"
#include "level/parser/parserparam.h"
#include "object/object_create_params.h"
#include "sound/sound.h"
#include "ui/controls/interface.h"
#include "ui/controls/list.h"
#include "ui/controls/window.h"
#include <boost/lexical_cast.hpp>
class CWaterPump : public CBaseBuilding
{
public:
CWaterPump(int id)
: CBaseBuilding(id, OBJECT_WATERPUMP)
{
m_implementedInterfaces[static_cast<int>(ObjectInterfaceType::Powered)] = true;
}
void Write(CLevelParserLine* line) override;
void Read(CLevelParserLine* line) override;
using COldObject::SetAuto;
};
struct CAutoWaterPump : public CAuto
{
CAutoWaterPump(CWaterPump* object) : CAuto(object) {}
~CAutoWaterPump() {}
float cycle = 0.0f;
bool EventProcess(const Event &event) override {
CAuto::EventProcess(event);
if (event.type == EVENT_FRAME && !m_engine->GetPause()) {
CObject *powerCell = m_object->GetPower();
if (powerCell != nullptr) {
if (powerCell->Implements(ObjectInterfaceType::LiquidContainer)) {
// test code
CLiquidContainerObject *asPC = dynamic_cast<CLiquidContainerObject*>(powerCell);
if (asPC->GetLiquidType() == LiquidType::WATER || asPC->GetLiquidType() == LiquidType::EMPTY) {
float water = asPC->GetLiquidAmount();
if (water < 1.0f)
{
// animation
cycle = fmodf(cycle + event.rTime, 1.0f);
if (cycle < 0.5f)
m_object->SetPartPosition(1, Math::Vector(0.0f, 4.0f * cycle + 1.5f, 0.0f));
else
m_object->SetPartPosition(1, Math::Vector(0.0f, 4.0f - (4.0f * cycle) + 1.5f, 0.0f));
}
water = water + (0.2f * event.rTime);
if(water > 1.0f) water = 1.0f;
asPC->SetLiquid(LiquidType::WATER, water);
}
}
}
}
return true; // XXX what does this mean?
}
};
std::unique_ptr<CObject> CreateObjectWaterPump(
const ObjectCreateParams& params,
Gfx::COldModelManager* oldModelManager, // XXX remove
Gfx::CModelManager* modelManager,
Gfx::CEngine* engine)
{
auto obj = MakeUnique<CWaterPump>(params.id);
obj->SetTeam(params.team);
/*static int info1_base_rank = -1;
if (info1_base_rank == -1) {
Gfx::CModel& model_info1 = modelManager->GetModel("info1");
info1_base_rank = engine->CreateBaseObject();
engine->AddBaseObjTriangles(info1_base_rank, model_info1.GetMesh("main")->GetTriangles());
}*/
int rank = engine->CreateObject();
engine->SetObjectType(rank, Gfx::ENG_OBJTYPE_FIX); // it is a stationary object
obj->SetObjectRank(0, rank);
//engine->SetObjectBaseRank(rank, info1_base_rank);
oldModelManager->AddModelReference("waterpump.txt", false, rank);
obj->SetPosition(params.pos);
obj->SetRotationY(params.angle);
obj->SetFloorHeight(0.0f);
rank = engine->CreateObject();
engine->SetObjectType(rank, Gfx::ENG_OBJTYPE_DESCENDANT);
obj->SetObjectRank(1, rank);
obj->SetObjectParent(1, 0);
//engine->SetObjectBaseRank(rank, info1_base_rank);
oldModelManager->AddModelReference("info2.mod", false, rank);
obj->SetPartPosition(1, Math::Vector(0.0f, 1.0f, 0.0f));
obj->SetPartRotationY(1, 0.0f);
obj->AddCrashSphere(CrashSphere(Math::Vector(0.0f, 3.0f, 0.0f), 6.0f, SOUND_BOUMm, 0.45f));
obj->AddCrashSphere(CrashSphere(Math::Vector(7.0f, 1.0f, 0.0f), 1.5f, SOUND_BOUMm, 0.45f));
obj->SetCameraCollisionSphere(Math::Sphere(Math::Vector(0.0f, 5.0f, 0.0f), 6.0f));
obj->SetPowerPosition(Math::Vector(7.0, 2.5, 0.0));
obj->CreateShadowCircle(8.0f, 1.0f);
Math::Vector pos = obj->GetPosition();
pos.y += params.height;
obj->SetPosition(pos); // to display the shadows immediately
auto objAuto = MakeUnique<CAutoWaterPump>(obj.get());
objAuto->Init();
obj->SetAuto(std::move(objAuto));
engine->LoadAllTextures();
return obj;
}
void CWaterPump::Write(CLevelParserLine* line)
{
CBaseBuilding::Write(line);
}
void CWaterPump::Read(CLevelParserLine* line)
{
CBaseBuilding::Read(line);
}

View File

@ -0,0 +1,81 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2018, 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/subclass/base_building.h"
#include "object/auto/auto.h"
#include <string>
#include <vector>
#include <boost/optional.hpp>
struct ObjectCreateParams;
namespace Gfx
{
class COldModelManager;
class CModelManager;
class CEngine;
}
std::unique_ptr<CObject> CreateObjectWaterPump(
const ObjectCreateParams& params,
Gfx::COldModelManager* oldModelManager,
Gfx::CModelManager* modelManager,
Gfx::CEngine* engine);
// TODO: integrate this with CExchangePost
/*class CAutoInfo : public CAuto
{
public:
CAutoInfo(CExchangePost* object);
~CAutoInfo();
void DeleteObject(bool all=false) override;
void Init() override;
void Start(int param) override;
bool EventProcess(const Event &event) override;
Error GetError() override;
bool CreateInterface(bool select) override;
bool Write(CLevelParserLine* line) override;
bool Read(CLevelParserLine* line) override;
protected:
void UpdateInterface(float rTime);
void UpdateList();
void UpdateListVirus();
protected:
CExchangePost* m_exchangePost;
enum class Phase : unsigned int;
Phase m_phase;
float m_progress;
float m_speed;
float m_timeVirus;
float m_lastParticle;
Math::Vector m_goal;
bool m_lastVirus;
};*/

View File

@ -1274,6 +1274,16 @@ bool CTaskGoto::GetHotPoint(CObject *pObj, Math::Vector &pos,
return true; return true;
} }
if ( type == OBJECT_WATERPUMP )
{
mat = pObj->GetWorldMatrix(0);
pos.x += 7.0f;
if ( bTake && distance != 0.0f ) suppl = 2.5f;
if ( bTake ) pos.x += TAKE_DIST+TAKE_DIST_OTHER+distance+suppl;
pos = Transform(*mat, pos);
return true;
}
if ( type == OBJECT_ENERGY ) if ( type == OBJECT_ENERGY )
{ {
mat = pObj->GetWorldMatrix(0); mat = pObj->GetWorldMatrix(0);

View File

@ -33,11 +33,14 @@
#include "object/interface/carrier_object.h" #include "object/interface/carrier_object.h"
#include "object/interface/powered_object.h" #include "object/interface/powered_object.h"
#include "object/interface/transportable_object.h" #include "object/interface/transportable_object.h"
#include "object/interface/slotted_object.h"
#include "physics/physics.h" #include "physics/physics.h"
#include "sound/sound.h" #include "sound/sound.h"
const int INVALID_SLOT = -1;
const int ENERGY_CELL_SLOT = -2;
//?const float MARGIN_FRONT = 2.0f; //?const float MARGIN_FRONT = 2.0f;
//?const float MARGIN_BACK = 2.0f; //?const float MARGIN_BACK = 2.0f;
@ -263,6 +266,16 @@ void CTaskManip::InitAngle()
} }
} }
static bool ObjectSlotOccupied(CObject *obj, int slotnum) {
if(slotnum == ENERGY_CELL_SLOT)
return ObjectHasPowerCell(obj);
else {
if (!obj->Implements(ObjectInterfaceType::Slotted))
return false;
return dynamic_cast<CSlottedObject*>(obj)->GetSlotContainedObject(slotnum) != nullptr;
}
}
// Assigns the goal was achieved. // Assigns the goal was achieved.
Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm) Error CTaskManip::Start(TaskManipOrder order, TaskManipArm arm)
@ -393,7 +406,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 +417,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 (! ObjectSlotOccupied(other, slotNum)) return ERR_MANIP_NIL;
m_targetPos = oPos; m_targetPos = oPos;
m_angle = oAngle; m_angle = oAngle;
m_height = oHeight; m_height = oHeight;
@ -436,8 +450,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 && !ObjectSlotOccupied(other, slotNum))
{ {
m_targetPos = oPos; m_targetPos = oPos;
m_angle = oAngle; m_angle = oAngle;
@ -868,8 +883,10 @@ CObject* CTaskManip::SearchTakeBackObject(bool bAdvance, Math::Vector &pos,
CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos, CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos,
float &distance, float &angle, float &distance, float &angle,
float &height) float &height, int &slotNumOut)
{ {
slotNumOut = INVALID_SLOT;
Math::Matrix* mat; Math::Matrix* mat;
float iAngle, oAngle, oLimit, aLimit, dLimit; float iAngle, oAngle, oLimit, aLimit, dLimit;
@ -901,55 +918,96 @@ CObject* CTaskManip::SearchOtherObject(bool bAdvance, Math::Vector &pos,
if ( pObj == m_object ) continue; // yourself? if ( pObj == m_object ) continue; // yourself?
ObjectType type = pObj->GetType(); ObjectType type = pObj->GetType();
if ( !pObj->Implements(ObjectInterfaceType::Powered) ) continue; if ( pObj->Implements(ObjectInterfaceType::Powered) ) {
CObject* power = dynamic_cast<CPoweredObject*>(pObj)->GetPower(); CObject* power = dynamic_cast<CPoweredObject*>(pObj)->GetPower();
if (power != nullptr) if (power == nullptr || (!power->GetLock() && power->GetScaleY() == 1.0f))
{
if (power->GetLock()) continue;
if (power->GetScaleY() != 1.0f) continue;
}
mat = pObj->GetWorldMatrix(0);
Math::Vector oPos = 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(Math::Distance(oPos, iPos)-TAKE_DIST);
if ( distance <= dLimit )
{
angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW !
if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) )
{ {
Math::Vector powerPos = dynamic_cast<CPoweredObject*>(pObj)->GetPowerPosition(); mat = pObj->GetWorldMatrix(0);
height = powerPos.y; Math::Vector oPos = Transform(*mat, dynamic_cast<CPoweredObject*>(pObj)->GetPowerPosition());
pos = oPos;
return pObj; 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 if ( type == OBJECT_WATERPUMP )
{
oLimit = 3; //Math::PI doesn't work
}
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) )
{
distance = fabs(Math::Distance(oPos, iPos)-TAKE_DIST);
if ( distance <= dLimit )
{
angle = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW !
if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) )
{
Math::Vector powerPos = dynamic_cast<CPoweredObject*>(pObj)->GetPowerPosition();
height = powerPos.y;
pos = oPos;
slotNumOut = ENERGY_CELL_SLOT;
return pObj;
}
}
}
}
}
if (pObj->Implements(ObjectInterfaceType::Slotted)) {
CSlottedObject *obj = dynamic_cast<CSlottedObject*>(pObj);
int slotNum = obj->GetNumSlots();
for (int slot = 0; slot < slotNum; slot++) {
mat = pObj->GetWorldMatrix(0);
Math::Vector worldSlotPos = Transform(*mat, obj->GetSlotPosition(slot));
// TODO: check GetLock and GetScaleY, like for energy cell slot
CObject *objectInSlot = obj->GetSlotContainedObject(slot);
if (objectInSlot != nullptr && (objectInSlot->GetLock() || objectInSlot->GetScaleY() != 1.0f))
continue;
// 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 objectAngleOffsetLimit = obj->GetSlotAcceptanceAngle(slot);
float objectIdealAngle = obj->GetSlotAngle(slot);
if ( Math::TestAngle(angleFromObjectToRobot, objectIdealAngle - objectAngleOffsetLimit, objectIdealAngle + objectAngleOffsetLimit) ) {
distance = fabs(Math::Distance(worldSlotPos, iPos)-TAKE_DIST);
// The robot must be close enough to the slot
if ( distance <= dLimit )
{
// The slot must be in the correct position relative to the robot (the robot must be facing towards the slot, not sideways or away)
angle = Math::RotateAngle(worldSlotPos.x-iPos.x, iPos.z-worldSlotPos.z); // CW !
if ( Math::TestAngle(angle, iAngle-aLimit, iAngle+aLimit) )
{
Math::Vector powerPos = obj->GetSlotPosition(slot);
height = powerPos.y;
pos = worldSlotPos;
slotNumOut = slot;
return pObj;
}
}
}
} }
} }
} }
@ -1090,19 +1148,36 @@ bool CTaskManip::TransporterTakeObject()
{ {
Math::Vector pos; Math::Vector 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;
if (slotNum == ENERGY_CELL_SLOT) {
assert(other->Implements(ObjectInterfaceType::Powered));
CObject* cargo = dynamic_cast<CPoweredObject*>(other)->GetPower(); 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<CPoweredObject*>(other)->SetPower(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
} else {
assert(other->Implements(ObjectInterfaceType::Slotted));
cargo = dynamic_cast<CSlottedObject*>(other)->GetSlotContainedObject(slotNum);
if (cargo == nullptr) return false; // the other does not have a battery?
assert(cargo->Implements(ObjectInterfaceType::Transportable));
m_cargoType = cargo->GetType();
dynamic_cast<CSlottedObject*>(other)->SetSlotContainedObject(slotNum, nullptr);
dynamic_cast<CTransportableObject*>(cargo)->SetTransporter(m_object);
dynamic_cast<CTransportableObject*>(cargo)->SetTransporterPart(3); // takes with the hand
}
pos = Math::Vector(4.7f, 0.0f, 0.0f); // relative to the hand (lem4) pos = Math::Vector(4.7f, 0.0f, 0.0f); // relative to the hand (lem4)
cargo->SetPosition(pos); cargo->SetPosition(pos);
@ -1189,23 +1264,38 @@ bool CTaskManip::TransporterDeposeObject()
Math::Vector pos; Math::Vector 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);
if (slotNum == ENERGY_CELL_SLOT) {
assert(other->Implements(ObjectInterfaceType::Powered));
CObject* cargo = dynamic_cast<CPoweredObject*>(other)->GetPower(); CObject* cargo = dynamic_cast<CPoweredObject*>(other)->GetPower();
if (cargo != nullptr) return false; // the other already has a battery? if (cargo != nullptr) return false; // the other already has a battery?
} else {
assert(other->Implements(ObjectInterfaceType::Slotted));
CObject* cargo = dynamic_cast<CSlottedObject*>(other)->GetSlotContainedObject(slotNum);
if (cargo != nullptr) return false; // the other already has a battery?
}
cargo = m_object->GetCargo(); CObject *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); if (slotNum == ENERGY_CELL_SLOT)
dynamic_cast<CPoweredObject*>(other)->SetPower(cargo);
else
dynamic_cast<CSlottedObject*>(other)->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.
if (slotNum == ENERGY_CELL_SLOT)
cargo->SetPosition(dynamic_cast<CPoweredObject*>(other)->GetPowerPosition());
else
cargo->SetPosition(dynamic_cast<CSlottedObject*>(other)->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);

View File

@ -73,7 +73,7 @@ protected:
CObject* SearchTakeUnderObject(Math::Vector &pos, float dLimit); CObject* SearchTakeUnderObject(Math::Vector &pos, float dLimit);
CObject* SearchTakeFrontObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle); CObject* SearchTakeFrontObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle);
CObject* SearchTakeBackObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle); CObject* SearchTakeBackObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle);
CObject* SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, float &height); CObject* SearchOtherObject(bool bAdvance, Math::Vector &pos, float &distance, float &angle, float &height, int &slotNumOut);
bool TransporterTakeObject(); bool TransporterTakeObject();
bool TransporterDeposeObject(); bool TransporterDeposeObject();
bool IsFreeDeposeObject(Math::Vector pos); bool IsFreeDeposeObject(Math::Vector pos);

View File

@ -2725,7 +2725,8 @@ bool CPhysics::ExploOther(ObjectType iType,
oType == OBJECT_NUCLEAR || oType == OBJECT_NUCLEAR ||
oType == OBJECT_PARA || oType == OBJECT_PARA ||
oType == OBJECT_SAFE || oType == OBJECT_SAFE ||
oType == OBJECT_HUSTON ) // building? oType == OBJECT_HUSTON ||
oType == OBJECT_WATERPUMP) // building?
{ {
assert(pObj->Implements(ObjectInterfaceType::Damageable)); assert(pObj->Implements(ObjectInterfaceType::Damageable));
// TODO: implement "killer"? // TODO: implement "killer"?

View File

@ -115,6 +115,8 @@ const char* GetObjectName(ObjectType type)
if ( type == OBJECT_BEE ) return "AlienWasp"; if ( type == OBJECT_BEE ) return "AlienWasp";
if ( type == OBJECT_WORM ) return "AlienWorm"; if ( type == OBJECT_WORM ) return "AlienWorm";
if ( type == OBJECT_RUINmobilew1) return "Wreck"; if ( type == OBJECT_RUINmobilew1) return "Wreck";
if ( type == OBJECT_WATERPUMP ) return "WaterPump";
if ( type == OBJECT_BUCKET ) return "Bucket";
return ""; return "";
} }
@ -213,6 +215,7 @@ std::string GetHelpFilename(ObjectType type)
if ( type == OBJECT_BEE ) helpfile = "object/wasp"; if ( type == OBJECT_BEE ) helpfile = "object/wasp";
if ( type == OBJECT_WORM ) helpfile = "object/worm"; if ( type == OBJECT_WORM ) helpfile = "object/worm";
if ( type == OBJECT_RUINmobilew1) helpfile = "object/wreck"; if ( type == OBJECT_RUINmobilew1) helpfile = "object/wreck";
// XXX WaterPump
if (helpfile.empty()) if (helpfile.empty())
return ""; return "";

View File

@ -8,7 +8,7 @@
bl_info = { bl_info = {
"name": "Colobot Model Format (.txt)", "name": "Colobot Model Format (.txt)",
"author": "TerranovaTeam", "author": "TerranovaTeam",
"version": (0, 0, 2), "version": (0, 0, 3),
"blender": (2, 6, 4), "blender": (2, 6, 4),
"location": "File > Export > Colobot (.txt)", "location": "File > Export > Colobot (.txt)",
"description": "Export Colobot Model Format (.txt)", "description": "Export Colobot Model Format (.txt)",
@ -35,7 +35,7 @@ FUZZY_TOLERANCE = 1e-5
class ColobotError(Exception): class ColobotError(Exception):
"""Exception in I/O operations""" """Exception in I/O operations"""
def __init__(self, value): def __init__(self, value, errcode=None):
self.value = value self.value = value
def __str__(self): def __str__(self):
return repr(self.value) return repr(self.value)
@ -199,7 +199,8 @@ def write_colobot_model(filename, model):
file.write('tex1 ' + t.mat.tex1 + '\n') file.write('tex1 ' + t.mat.tex1 + '\n')
file.write('tex2 ' + t.mat.tex2 + '\n') file.write('tex2 ' + t.mat.tex2 + '\n')
file.write('var_tex2 ' + ( 'Y' if t.mat.var_tex2 else 'N' + '\n' ) ) file.write('var_tex2 ' + ( 'Y' if t.mat.var_tex2 else 'N' + '\n' ) )
file.write('lod_level ' + str(t.lod_level) + '\n') if model.version == 1:
file.write('lod_level ' + str(t.lod_level) + '\n')
file.write('state ' + str(t.mat.state) + '\n') file.write('state ' + str(t.mat.state) + '\n')
file.write('\n') file.write('\n')
@ -281,8 +282,8 @@ def read_colobot_model(filename):
if (tokens[0] != 'version'): if (tokens[0] != 'version'):
raise ColobotError("Invalid header", "version") raise ColobotError("Invalid header", "version")
model.version = int(tokens[1]) model.version = int(tokens[1])
if (model.version != 1): if (model.version != 1 and model.version != 2):
raise ColobotError("Unknown model file version") raise ColobotError("Unknown model file version "+str(model.version))
tokens, index = token_next_line(lines, index) tokens, index = token_next_line(lines, index)
if (tokens[0] != 'total_triangles'): if (tokens[0] != 'total_triangles'):
@ -329,10 +330,13 @@ def read_colobot_model(filename):
raise ColobotError("Invalid triangle", "var_tex2") raise ColobotError("Invalid triangle", "var_tex2")
t.mat.var_tex2 = tokens[1] == 'Y' t.mat.var_tex2 = tokens[1] == 'Y'
tokens, index = token_next_line(lines, index) if (model.version == 1):
if (tokens[0] != 'lod_level'): tokens, index = token_next_line(lines, index)
raise ColobotError("Invalid triangle", "lod_level") if (tokens[0] != 'lod_level'):
t.lod_level = int(tokens[1]) raise ColobotError("Invalid triangle", "lod_level")
t.lod_level = int(tokens[1])
else:
t.lod_level = 0 # constant
tokens, index = token_next_line(lines, index) tokens, index = token_next_line(lines, index)
if (tokens[0] != 'state'): if (tokens[0] != 'state'):
@ -384,9 +388,9 @@ def append_obj_to_colobot_model(obj, model, scene, defaults):
t.mat.specular[3] = mat.specular_alpha t.mat.specular[3] = mat.specular_alpha
if (mat.texture_slots[0] != None): if (mat.texture_slots[0] != None):
t.tex1 = bpy.path.basename(mat.texture_slots[0].texture.image.filepath) t.mat.tex1 = bpy.path.basename(mat.texture_slots[0].texture.image.filepath)
if (mat.texture_slots[1] != None): if (mat.texture_slots[1] != None):
t.tex2 = bpy.path.basename(mat.texture_slots[1].texture.image.filepath) t.mat.tex2 = bpy.path.basename(mat.texture_slots[1].texture.image.filepath)
t.var_tex2 = mat.get('var_tex2', defaults['var_tex2']) t.var_tex2 = mat.get('var_tex2', defaults['var_tex2'])
t.state = mat.get('state', defaults['state']) t.state = mat.get('state', defaults['state'])
@ -589,7 +593,7 @@ class ExportColobotDialog(bpy.types.Operator):
write_colobot_model(EXPORT_FILEPATH, model) write_colobot_model(EXPORT_FILEPATH, model)
except ColobotError as e: except ColobotError as e:
self.report({'ERROR'}, e.args.join(": ")) self.report({'ERROR'}, ": ".join(e.args))
return {'FINISHED'} return {'FINISHED'}
self.report({'INFO'}, 'Export OK') self.report({'INFO'}, 'Export OK')
@ -665,7 +669,7 @@ class ImportColobotDialog(bpy.types.Operator):
obj.layers = layers obj.layers = layers
except ColobotError as e: except ColobotError as e:
self.report({'ERROR'}, e.args.join(": ")) self.report({'ERROR'}, ": ".join(e.args))
return {'FINISHED'} return {'FINISHED'}
self.report({'INFO'}, 'Import OK') self.report({'INFO'}, 'Import OK')