/* * This file is part of the Colobot: Gold Edition source code * Copyright (C) 2001-2014, Daniel Roux, EPSITEC SA & TerranovaTeam * http://epsiteс.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 "CBot/CBotDll.h" #include "app/app.h" #include "common/global.h" #include "common/restext.h" #include "graphics/engine/lightman.h" #include "graphics/engine/lightning.h" #include "graphics/engine/modelmanager.h" #include "graphics/engine/particle.h" #include "graphics/engine/pyro_manager.h" #include "graphics/engine/terrain.h" #include "math/geometry.h" #include "object/auto/auto.h" #include "object/auto/autojostle.h" #include "object/brain.h" #include "object/motion/motion.h" #include "object/motion/motionvehicle.h" #include "object/robotmain.h" #include "object/object_manager.h" #include "object/level/parserline.h" #include "object/level/parserparam.h" #include "object/level/parserexceptions.h" #include "physics/physics.h" #include "script/cbottoken.h" #include "script/cmdtoken.h" #include #define ADJUST_ONBOARD 0 // 1 -> adjusts the camera ONBOARD #define ADJUST_ARM 0 // 1 -> adjusts the manipulator arm const float VIRUS_DELAY = 60.0f; // duration of virus infection const float LOSS_SHIELD = 0.24f; // loss of the shield by shot const float LOSS_SHIELD_H = 0.10f; // loss of the shield for humans const float LOSS_SHIELD_M = 0.02f; // loss of the shield for the laying #if ADJUST_ONBOARD static float debug_x = 0.0f; static float debug_y = 0.0f; static float debug_z = 0.0f; #endif #if ADJUST_ARM static float debug_arm1 = 0.0f; static float debug_arm2 = 0.0f; static float debug_arm3 = 0.0f; #endif // Updates the class Object. void uObject(CBotVar* botThis, void* user) { CObject* object = static_cast(user); CObject* power; CObject* cargo; CPhysics* physics; CBotVar *pVar, *pSub; ObjectType type; Math::Vector pos; float value; if ( object == 0 ) return; physics = object->GetPhysics(); // Updates the object's type. pVar = botThis->GetItemList(); // "category" type = object->GetType(); pVar->SetValInt(type, object->GetName()); // Updates the position of the object. pVar = pVar->GetNext(); // "position" if ( object->GetTransporter() == 0 ) { pos = object->GetPosition(0); float waterLevel = Gfx::CEngine::GetInstancePointer()->GetWater()->GetLevel(); pos.y -= waterLevel; // relative to sea level! pSub = pVar->GetItemList(); // "x" pSub->SetValFloat(pos.x/g_unit); pSub = pSub->GetNext(); // "y" pSub->SetValFloat(pos.z/g_unit); pSub = pSub->GetNext(); // "z" pSub->SetValFloat(pos.y/g_unit); } else // object transported? { pSub = pVar->GetItemList(); // "x" pSub->SetInit(IS_NAN); pSub = pSub->GetNext(); // "y" pSub->SetInit(IS_NAN); pSub = pSub->GetNext(); // "z" pSub->SetInit(IS_NAN); } // Updates the angle. pos = object->GetAngle(0); pos += object->GetTilt(); pVar = pVar->GetNext(); // "orientation" pVar->SetValFloat(360.0f-Math::Mod(pos.y*180.0f/Math::PI, 360.0f)); pVar = pVar->GetNext(); // "pitch" pVar->SetValFloat(pos.z*180.0f/Math::PI); pVar = pVar->GetNext(); // "roll" pVar->SetValFloat(pos.x*180.0f/Math::PI); // Updates the energy level of the object. pVar = pVar->GetNext(); // "energyLevel" value = object->GetEnergy(); pVar->SetValFloat(value); // Updates the shield level of the object. pVar = pVar->GetNext(); // "shieldLevel" value = object->GetShield(); pVar->SetValFloat(value); // Updates the temperature of the reactor. pVar = pVar->GetNext(); // "temperature" if ( physics == 0 ) value = 0.0f; else value = 1.0f-physics->GetReactorRange(); pVar->SetValFloat(value); // Updates the height above the ground. pVar = pVar->GetNext(); // "altitude" if ( physics == 0 ) value = 0.0f; else value = physics->GetFloorHeight(); pVar->SetValFloat(value/g_unit); // Updates the lifetime of the object. pVar = pVar->GetNext(); // "lifeTime" value = object->GetAbsTime(); pVar->SetValFloat(value); // Updates the type of battery. pVar = pVar->GetNext(); // "energyCell" power = object->GetPower(); if ( power == 0 ) pVar->SetPointer(0); else pVar->SetPointer(power->GetBotVar()); // Updates the transported object's type. pVar = pVar->GetNext(); // "load" cargo = object->GetCargo(); if ( cargo == 0 ) pVar->SetPointer(0); else pVar->SetPointer(cargo->GetBotVar()); pVar = pVar->GetNext(); // "id" value = object->GetID(); pVar->SetValInt(value); } // Object's constructor. CObject::CObject(int id) : m_id(id) { m_sound = CApplication::GetInstancePointer()->GetSound(); m_engine = Gfx::CEngine::GetInstancePointer(); m_lightMan = m_engine->GetLightManager(); m_particle = m_engine->GetParticle(); m_main = CRobotMain::GetInstancePointer(); m_terrain = m_main->GetTerrain(); m_camera = m_main->GetCamera(); m_runScript = nullptr; m_type = OBJECT_FIX; m_option = 0; m_name = ""; m_shadowLight = -1; m_effectLight = -1; m_linVibration = Math::Vector(0.0f, 0.0f, 0.0f); m_cirVibration = Math::Vector(0.0f, 0.0f, 0.0f); m_tilt = Math::Vector(0.0f, 0.0f, 0.0f); m_power = 0; m_cargo = 0; m_transporter = 0; m_transporterLink = 0; m_energy = 1.0f; m_capacity = 1.0f; m_shield = 1.0f; m_range = 0.0f; m_transparency = 0.0f; m_lastEnergy = 999.9f; m_bSelect = false; m_bSelectable = true; m_bCheckToken = true; m_bVisible = true; m_bEnable = true; m_bProxyActivate = false; m_bTrainer = false; m_bToy = false; m_bManual = false; m_bFixed = false; m_bClip = true; m_bShowLimit = false; m_showLimitRadius = 0.0f; m_aTime = 0.0f; m_shotTime = 0.0f; m_bVirusMode = false; m_virusTime = 0.0f; m_lastVirusParticle = 0.0f; m_totalDesectList = 0; m_bLock = false; m_bIgnoreBuildCheck = false; m_bExplo = false; m_bCargo = false; m_bBurn = false; m_bDead = false; m_bFlat = false; m_gunGoalV = 0.0f; m_gunGoalH = 0.0f; m_shieldRadius = 0.0f; m_defRank = -1; m_magnifyDamage = 1.0f; m_proxyDistance = 60.0f; m_param = 0.0f; m_infoReturn = NAN; m_team = 0; memset(&m_character, 0, sizeof(m_character)); m_character.wheelFront = 1.0f; m_character.wheelBack = 1.0f; m_character.wheelLeft = 1.0f; m_character.wheelRight = 1.0f; m_resetCap = RESET_NONE; m_bResetBusy = false; m_resetPosition = Math::Vector(0.0f, 0.0f, 0.0f); m_resetAngle = Math::Vector(0.0f, 0.0f, 0.0f); m_resetRun = nullptr; m_cameraType = Gfx::CAM_TYPE_BACK; m_cameraDist = 50.0f; m_bCameraLock = false; for (int i=0 ; iAddUpdateFunc(uObject); } m_botVar = CBotVar::Create("", CBotTypResult(CBotTypClass, "object")); m_botVar->SetUserPtr(this); m_botVar->SetIdent(m_id); } // Object's destructor. CObject::~CObject() { if ( m_botVar != nullptr ) { m_botVar->SetUserPtr(OBJECTDELETED); delete m_botVar; m_botVar = nullptr; } } // Removes an object. // If bAll = true, it does not help, // because all objects in the scene are quickly destroyed! void CObject::DeleteObject(bool bAll) { if ( m_botVar != 0 ) { m_botVar->SetUserPtr(OBJECTDELETED); } if ( m_camera->GetControllingObject() == this ) { m_camera->SetControllingObject(0); } for (CObject* obj : CObjectManager::GetInstancePointer()->GetAllObjects()) { obj->DeleteDeselList(this); } if ( !bAll ) { #if 0 type = m_camera->GetType(); if ( (type == Gfx::CAM_TYPE_BACK || type == Gfx::CAM_TYPE_FIX || type == Gfx::CAM_TYPE_EXPLO || type == Gfx::CAM_TYPE_ONBOARD) && m_camera->GetControllingObject() == this ) { obj = m_main->SearchNearest(GetPosition(0), this); if ( obj == 0 ) { m_camera->SetControllingObject(0); m_camera->SetType(Gfx::CAM_TYPE_FREE); } else { m_camera->SetControllingObject(obj); m_camera->SetType(Gfx::CAM_TYPE_BACK); } } #endif m_engine->GetPyroManager()->CutObjectLink(this); if ( m_bSelect ) { SetSelect(false); } if ( m_type == OBJECT_BASE || m_type == OBJECT_FACTORY || m_type == OBJECT_REPAIR || m_type == OBJECT_DESTROYER|| m_type == OBJECT_DERRICK || m_type == OBJECT_STATION || m_type == OBJECT_CONVERT || m_type == OBJECT_TOWER || m_type == OBJECT_RESEARCH || m_type == OBJECT_RADAR || m_type == OBJECT_INFO || m_type == OBJECT_ENERGY || m_type == OBJECT_LABO || m_type == OBJECT_NUCLEAR || m_type == OBJECT_PARA || m_type == OBJECT_SAFE || m_type == OBJECT_HUSTON || m_type == OBJECT_START || m_type == OBJECT_END ) // building? { m_terrain->DeleteBuildingLevel(GetPosition(0)); // flattens the field } } m_type = OBJECT_NULL; // invalid object until complete destruction if ( m_shadowLight != -1 ) { m_lightMan->DeleteLight(m_shadowLight); m_shadowLight = -1; } if ( m_effectLight != -1 ) { m_lightMan->DeleteLight(m_effectLight); m_effectLight = -1; } if ( m_physics != nullptr ) { m_physics->DeleteObject(bAll); } if ( m_brain != nullptr ) { m_brain->DeleteObject(bAll); } if ( m_motion != nullptr ) { m_motion->DeleteObject(bAll); } if ( m_auto != nullptr ) { m_auto->DeleteObject(bAll); } for (int i=0 ; iDeleteObject(m_objectPart[i].object); if ( m_objectPart[i].masterParti != -1 ) { m_particle->DeleteParticle(m_objectPart[i].masterParti); m_objectPart[i].masterParti = -1; } } } if ( m_bShowLimit ) { m_main->FlushShowLimit(0); m_bShowLimit = false; } if ( !bAll ) m_main->CreateShortcuts(); } // Simplifies a object (he was the brain, among others). void CObject::Simplify() { if ( m_brain != nullptr ) { m_brain->StopProgram(); } m_main->SaveOneScript(this); if ( m_physics != nullptr ) { m_physics->DeleteObject(); m_physics.reset(); } if ( m_brain != nullptr ) { m_brain->DeleteObject(); m_brain.reset(); } if ( m_motion != nullptr ) { m_motion->DeleteObject(); m_motion.reset(); } if ( m_auto != nullptr ) { m_auto->DeleteObject(); m_auto.reset(); } m_main->CreateShortcuts(); } // Detonates an object, when struck by a shot. // If false is returned, the object is still screwed. // If true is returned, the object is destroyed. bool CObject::ExplodeObject(ExplosionType type, float force, float decay) { Gfx::PyroType pyroType; float loss, shield; if ( type == ExplosionType::Bang ) { if ( m_type == OBJECT_MOBILEtg || m_type == OBJECT_TEEN28 || // cylinder? m_type == OBJECT_METAL || m_type == OBJECT_POWER || m_type == OBJECT_ATOMIC || m_type == OBJECT_TNT || m_type == OBJECT_SCRAP1 || m_type == OBJECT_SCRAP2 || m_type == OBJECT_SCRAP3 || m_type == OBJECT_SCRAP4 || m_type == OBJECT_SCRAP5 || m_type == OBJECT_BULLET || m_type == OBJECT_EGG ) // object that isn't burning? { type = ExplosionType::Bang; force = 1.0f; decay = 1.0f; } } if ( type == ExplosionType::Bang ) { if ( m_shotTime < 0.5f ) return false; m_shotTime = 0.0f; } if ( m_type == OBJECT_HUMAN && m_bDead ) return false; // Calculate the power lost by the explosion. if ( force == 0.0f ) { if ( m_type == OBJECT_HUMAN ) { loss = LOSS_SHIELD_H; } else if ( m_type == OBJECT_MOTHER ) { loss = LOSS_SHIELD_M; } else { loss = LOSS_SHIELD; } } else { loss = force; } loss *= m_magnifyDamage; loss *= decay; // Decreases the power of the shield. shield = GetShield(); shield -= loss; if ( shield < 0.0f ) shield = 0.0f; SetShield(shield); if ( shield > 0.0f ) // not dead yet? { if ( type == ExplosionType::Water ) { if ( m_type == OBJECT_HUMAN ) { pyroType = Gfx::PT_SHOTH; } else { pyroType = Gfx::PT_SHOTW; } } else { if ( m_type == OBJECT_HUMAN ) { pyroType = Gfx::PT_SHOTH; } else if ( m_type == OBJECT_MOTHER ) { pyroType = Gfx::PT_SHOTM; } else { pyroType = Gfx::PT_SHOTT; } } } else // completely dead? { if ( type == ExplosionType::Burn ) // burning? { if ( m_type == OBJECT_MOTHER || m_type == OBJECT_ANT || m_type == OBJECT_SPIDER || m_type == OBJECT_BEE || m_type == OBJECT_WORM || m_type == OBJECT_BULLET ) { pyroType = Gfx::PT_BURNO; SetBurn(true); } else if ( m_type == OBJECT_HUMAN ) { pyroType = Gfx::PT_DEADG; } else { pyroType = Gfx::PT_BURNT; SetBurn(true); } SetVirusMode(false); } else if ( type == ExplosionType::Water ) { if ( m_type == OBJECT_HUMAN ) { pyroType = Gfx::PT_DEADW; } else { pyroType = Gfx::PT_FRAGW; } } else // explosion? { if ( m_type == OBJECT_ANT || m_type == OBJECT_SPIDER || m_type == OBJECT_BEE || m_type == OBJECT_WORM ) { pyroType = Gfx::PT_EXPLOO; } else if ( m_type == OBJECT_MOTHER || m_type == OBJECT_NEST || m_type == OBJECT_BULLET ) { pyroType = Gfx::PT_FRAGO; } else if ( m_type == OBJECT_HUMAN ) { pyroType = Gfx::PT_DEADG; } else if ( m_type == OBJECT_BASE || m_type == OBJECT_DERRICK || m_type == OBJECT_FACTORY || m_type == OBJECT_STATION || m_type == OBJECT_CONVERT || m_type == OBJECT_REPAIR || m_type == OBJECT_DESTROYER|| m_type == OBJECT_TOWER || m_type == OBJECT_NEST || m_type == OBJECT_RESEARCH || m_type == OBJECT_RADAR || m_type == OBJECT_INFO || m_type == OBJECT_ENERGY || m_type == OBJECT_LABO || m_type == OBJECT_NUCLEAR || m_type == OBJECT_PARA || m_type == OBJECT_SAFE || m_type == OBJECT_HUSTON || m_type == OBJECT_START || m_type == OBJECT_END ) // building? { pyroType = Gfx::PT_FRAGT; } else if ( m_type == OBJECT_MOBILEtg || m_type == OBJECT_TEEN28 || // cylinder? m_type == OBJECT_TEEN31 ) // basket? { pyroType = Gfx::PT_FRAGT; } else { pyroType = Gfx::PT_EXPLOT; } } loss = 1.0f; } m_engine->GetPyroManager()->Create(pyroType, this, loss); if ( shield == 0.0f ) // dead? { if ( m_brain != nullptr ) { m_brain->StopProgram(); } m_main->SaveOneScript(this); } if ( shield > 0.0f ) return false; // not dead yet if ( GetSelect() ) { SetSelect(false); // deselects the object m_camera->SetType(Gfx::CAM_TYPE_EXPLO); m_main->DeselectAll(); } DeleteDeselList(this); if ( m_botVar != 0 ) { if ( m_type == OBJECT_STONE || m_type == OBJECT_URANIUM || m_type == OBJECT_METAL || m_type == OBJECT_POWER || m_type == OBJECT_ATOMIC || m_type == OBJECT_BULLET || m_type == OBJECT_BBOX || m_type == OBJECT_TNT || m_type == OBJECT_SCRAP1 || m_type == OBJECT_SCRAP2 || m_type == OBJECT_SCRAP3 || m_type == OBJECT_SCRAP4 || m_type == OBJECT_SCRAP5 ) // (*) { m_botVar->SetUserPtr(OBJECTDELETED); } } return true; } // (*) If a robot or cosmonaut dies, the subject must continue to exist, // so that programs of the ants continue to operate as usual. // Initializes a new part. void CObject::InitPart(int part) { m_objectPart[part].bUsed = true; m_objectPart[part].object = -1; m_objectPart[part].parentPart = -1; m_objectPart[part].position = Math::Vector(0.0f, 0.0f, 0.0f); m_objectPart[part].angle.y = 0.0f; m_objectPart[part].angle.x = 0.0f; m_objectPart[part].angle.z = 0.0f; m_objectPart[part].zoom = Math::Vector(1.0f, 1.0f, 1.0f); m_objectPart[part].bTranslate = true; m_objectPart[part].bRotate = true; m_objectPart[part].bZoom = false; m_objectPart[part].matTranslate.LoadIdentity(); m_objectPart[part].matRotate.LoadIdentity(); m_objectPart[part].matTransform.LoadIdentity(); m_objectPart[part].matWorld.LoadIdentity();; m_objectPart[part].masterParti = -1; } // Removes part. void CObject::DeletePart(int part) { if ( !m_objectPart[part].bUsed ) return; if ( m_objectPart[part].masterParti != -1 ) { m_particle->DeleteParticle(m_objectPart[part].masterParti); m_objectPart[part].masterParti = -1; } m_objectPart[part].bUsed = false; m_engine->DeleteObject(m_objectPart[part].object); UpdateTotalPart(); } void CObject::UpdateTotalPart() { int i; m_totalPart = 0; for ( i=0 ; iAddParam("camera", CLevelParserParamUPtr{new CLevelParserParam(GetCameraType())}); if ( GetCameraLock() ) line->AddParam("cameraLock", CLevelParserParamUPtr{new CLevelParserParam(GetCameraLock())}); if ( GetEnergy() != 0.0f ) line->AddParam("energy", CLevelParserParamUPtr{new CLevelParserParam(GetEnergy())}); if ( GetCapacity() != 1.0f ) line->AddParam("capacity", CLevelParserParamUPtr{new CLevelParserParam(GetCapacity())}); if ( GetShield() != 1.0f ) line->AddParam("shield", CLevelParserParamUPtr{new CLevelParserParam(GetShield())}); if ( GetRange() != 1.0f ) line->AddParam("range", CLevelParserParamUPtr{new CLevelParserParam(GetRange())}); if ( !GetSelectable() ) line->AddParam("selectable", CLevelParserParamUPtr{new CLevelParserParam(GetSelectable())}); if ( !GetEnable() ) line->AddParam("enable", CLevelParserParamUPtr{new CLevelParserParam(GetEnable())}); if ( GetFixed() ) line->AddParam("fixed", CLevelParserParamUPtr{new CLevelParserParam(GetFixed())}); if ( !GetClip() ) line->AddParam("clip", CLevelParserParamUPtr{new CLevelParserParam(GetClip())}); if ( GetLock() ) line->AddParam("lock", CLevelParserParamUPtr{new CLevelParserParam(GetLock())}); if ( GetProxyActivate() ) { line->AddParam("proxyActivate", CLevelParserParamUPtr{new CLevelParserParam(GetProxyActivate())}); line->AddParam("proxyDistance", CLevelParserParamUPtr{new CLevelParserParam(GetProxyDistance()/g_unit)}); } if ( GetMagnifyDamage() != 1.0f ) line->AddParam("magnifyDamage", CLevelParserParamUPtr{new CLevelParserParam(GetMagnifyDamage())}); if ( GetTeam() != 0 ) line->AddParam("team", CLevelParserParamUPtr{new CLevelParserParam(GetTeam())}); if ( GetGunGoalV() != 0.0f ) line->AddParam("aimV", CLevelParserParamUPtr{new CLevelParserParam(GetGunGoalV())}); if ( GetGunGoalH() != 0.0f ) line->AddParam("aimH", CLevelParserParamUPtr{new CLevelParserParam(GetGunGoalH())}); if ( GetResetCap() != 0 ) { line->AddParam("resetCap", CLevelParserParamUPtr{new CLevelParserParam(static_cast(GetResetCap()))}); line->AddParam("resetPos", CLevelParserParamUPtr{new CLevelParserParam(GetResetPosition()/g_unit)}); line->AddParam("resetAngle", CLevelParserParamUPtr{new CLevelParserParam(GetResetAngle()/(Math::PI/180.0f))}); line->AddParam("resetRun", CLevelParserParamUPtr{new CLevelParserParam(m_brain->GetProgramIndex(GetResetRun()))}); } if ( m_bVirusMode ) line->AddParam("virusMode", CLevelParserParamUPtr{new CLevelParserParam(m_bVirusMode)}); if ( m_virusTime != 0.0f ) line->AddParam("virusTime", CLevelParserParamUPtr{new CLevelParserParam(m_virusTime)}); // Sets the parameters of the command line. CLevelParserParamVec cmdline; for ( i=0 ; i 0) line->AddParam("cmdline", CLevelParserParamUPtr{new CLevelParserParam(std::move(cmdline))}); if ( m_motion != nullptr ) { m_motion->Write(line); } if ( m_brain != nullptr ) { m_brain->Write(line); } if ( m_physics != nullptr ) { m_physics->Write(line); } if ( m_auto != nullptr ) { m_auto->Write(line); } } // Returns all parameters of the object. void CObject::Read(CLevelParserLine* line) { Math::Vector pos, dir; Gfx::CameraType cType = line->GetParam("camera")->AsCameraType(Gfx::CAM_TYPE_NULL); if ( cType != Gfx::CAM_TYPE_NULL ) { SetCameraType(cType); } SetCameraLock(line->GetParam("cameraLock")->AsBool(false)); SetEnergy(line->GetParam("energy")->AsFloat(0.0f)); SetCapacity(line->GetParam("capacity")->AsFloat(1.0f)); SetShield(line->GetParam("shield")->AsFloat(1.0f)); SetRange(line->GetParam("range")->AsFloat(1.0f)); SetSelectable(line->GetParam("selectable")->AsBool(true)); SetEnable(line->GetParam("enable")->AsBool(true)); SetFixed(line->GetParam("fixed")->AsBool(false)); SetClip(line->GetParam("clip")->AsBool(true)); SetLock(line->GetParam("lock")->AsBool(false)); SetProxyActivate(line->GetParam("proxyActivate")->AsBool(false)); SetProxyDistance(line->GetParam("proxyDistance")->AsFloat(15.0f)*g_unit); SetRange(line->GetParam("range")->AsFloat(30.0f)); SetMagnifyDamage(line->GetParam("magnifyDamage")->AsFloat(1.0f)); SetTeam(line->GetParam("team")->AsInt(0)); SetGunGoalV(line->GetParam("aimV")->AsFloat(0.0f)); SetGunGoalH(line->GetParam("aimH")->AsFloat(0.0f)); SetResetCap(static_cast(line->GetParam("resetCap")->AsInt(0))); SetResetPosition(line->GetParam("resetPos")->AsPoint(Math::Vector())*g_unit); SetResetAngle(line->GetParam("resetAngle")->AsPoint(Math::Vector())*(Math::PI/180.0f)); SetResetRun(m_brain->GetProgram(line->GetParam("resetRun")->AsInt(-1))); m_bBurn = line->GetParam("burnMode")->AsBool(false); m_bVirusMode = line->GetParam("virusMode")->AsBool(false); m_virusTime = line->GetParam("virusTime")->AsFloat(0.0f); // Sets the parameters of the command line. if (line->GetParam("cmdline")->IsDefined()) { int i = 0; for (auto& p : line->GetParam("cmdline")->AsArray()) { if (i >= OBJECTMAXCMDLINE) break; SetCmdLine(i, p->AsFloat()); i++; } } if ( m_motion != nullptr ) { m_motion->Read(line); } if ( m_brain != nullptr ) { m_brain->Read(line); } if ( m_physics != nullptr ) { m_physics->Read(line); } if ( m_auto != nullptr ) { m_auto->Read(line); } } // Seeking the nth son of a father. int CObject::SearchDescendant(int parent, int n) { int i; for ( i=0 ; i= MAXCRASHSPHERE ) return -1; zoom = GetZoomX(0); m_crashSpherePos[m_crashSphereUsed] = pos; m_crashSphereRadius[m_crashSphereUsed] = radius*zoom; m_crashSphereHardness[m_crashSphereUsed] = hardness; m_crashSphereSound[m_crashSphereUsed] = sound; return m_crashSphereUsed++; } // Returns the number of spheres. int CObject::GetCrashSphereTotal() { return m_crashSphereUsed; } // Returns a sphere for collisions. // The position is absolute in the world. bool CObject::GetCrashSphere(int rank, Math::Vector &pos, float &radius) { if ( rank < 0 || rank >= m_crashSphereUsed ) { pos = m_objectPart[0].position; radius = 0.0f; return false; } // Returns to the sphere collisions, // which ignores the inclination of the vehicle. // This is necessary to collisions with vehicles, // so as not to reflect SetTilt, for example. // The sphere must necessarily have a center (0, y, 0). if ( rank == 0 && m_crashSphereUsed == 1 && m_crashSpherePos[0].x == 0.0f && m_crashSpherePos[0].z == 0.0f ) { pos = m_objectPart[0].position + m_crashSpherePos[0]; radius = m_crashSphereRadius[0]; return true; } if ( m_objectPart[0].bTranslate || m_objectPart[0].bRotate ) { UpdateTransformObject(); } pos = Math::Transform(m_objectPart[0].matWorld, m_crashSpherePos[rank]); radius = m_crashSphereRadius[rank]; return true; } // Returns the hardness of a sphere. Sound CObject::GetCrashSphereSound(int rank) { return m_crashSphereSound[rank]; } // Returns the hardness of a sphere. float CObject::GetCrashSphereHardness(int rank) { return m_crashSphereHardness[rank]; } // Specifies the global sphere, relative to the object. void CObject::SetGlobalSphere(Math::Vector pos, float radius) { float zoom; zoom = GetZoomX(0); m_globalSpherePos = pos; m_globalSphereRadius = radius*zoom; } // Returns the global sphere, in the world. void CObject::GetGlobalSphere(Math::Vector &pos, float &radius) { pos = Math::Transform(m_objectPart[0].matWorld, m_globalSpherePos); radius = m_globalSphereRadius; } // Specifies the sphere of jostling, relative to the object. void CObject::SetJostlingSphere(Math::Vector pos, float radius) { m_jostlingSpherePos = pos; m_jostlingSphereRadius = radius; } // Specifies the sphere of jostling, in the world. void CObject::GetJostlingSphere(Math::Vector &pos, float &radius) { pos = Math::Transform(m_objectPart[0].matWorld, m_jostlingSpherePos); radius = m_jostlingSphereRadius; } // Specifies the radius of the shield. void CObject::SetShieldRadius(float radius) { m_shieldRadius = radius; } // Returns the radius of the shield. float CObject::GetShieldRadius() { return m_shieldRadius; } // Positioning an object on a certain height, above the ground. void CObject::SetFloorHeight(float height) { Math::Vector pos; pos = m_objectPart[0].position; m_terrain->AdjustToFloor(pos); if ( m_physics != nullptr ) { m_physics->SetLand(height == 0.0f); m_physics->SetMotor(height != 0.0f); } m_objectPart[0].position.y = pos.y+height+m_character.height; m_objectPart[0].bTranslate = true; // it will recalculate the matrices } // Adjust the inclination of an object laying on the ground. void CObject::FloorAdjust() { Math::Vector pos, n; Math::Point nn; float a; pos = GetPosition(0); if ( m_terrain->GetNormal(n, pos) ) { #if 0 SetAngleX(0, sinf(n.z)); SetAngleZ(0, -sinf(n.x)); SetAngleY(0, 0.0f); #else a = GetAngleY(0); nn = Math::RotatePoint(-a, Math::Point(n.z, n.x)); SetAngleX(0, sinf(nn.x)); SetAngleZ(0, -sinf(nn.y)); #endif } } // Getes the linear vibration. void CObject::SetLinVibration(Math::Vector dir) { if ( m_linVibration.x != dir.x || m_linVibration.y != dir.y || m_linVibration.z != dir.z ) { m_linVibration = dir; m_objectPart[0].bTranslate = true; } } Math::Vector CObject::GetLinVibration() { return m_linVibration; } // Getes the circular vibration. void CObject::SetCirVibration(Math::Vector dir) { if ( m_cirVibration.x != dir.x || m_cirVibration.y != dir.y || m_cirVibration.z != dir.z ) { m_cirVibration = dir; m_objectPart[0].bRotate = true; } } Math::Vector CObject::GetCirVibration() { return m_cirVibration; } // Getes the inclination. void CObject::SetTilt(Math::Vector dir) { if ( m_tilt.x != dir.x || m_tilt.y != dir.y || m_tilt.z != dir.z ) { m_tilt = dir; m_objectPart[0].bRotate = true; } } Math::Vector CObject::GetTilt() { return m_tilt; } // Getes the position of center of the object. void CObject::SetPosition(int part, const Math::Vector &pos) { Math::Vector shPos, n[20], norm; float height, radius; int rank, i, j; m_objectPart[part].position = pos; m_objectPart[part].bTranslate = true; // it will recalculate the matrices if ( part == 0 && !m_bFlat ) // main part? { rank = m_objectPart[0].object; shPos = pos; m_terrain->AdjustToFloor(shPos, true); m_engine->SetObjectShadowPos(rank, shPos); if ( m_physics != nullptr && m_physics->GetType() == TYPE_FLYING ) { height = pos.y-shPos.y; } else { height = 0.0f; } m_engine->SetObjectShadowHeight(rank, height); // Calculating the normal to the ground in nine strategic locations, // then perform a weighted average (the dots in the center are more important). radius = m_engine->GetObjectShadowRadius(rank); i = 0; m_terrain->GetNormal(norm, pos); n[i++] = norm; n[i++] = norm; n[i++] = norm; shPos = pos; shPos.x += radius*0.6f; shPos.z += radius*0.6f; m_terrain->GetNormal(norm, shPos); n[i++] = norm; n[i++] = norm; shPos = pos; shPos.x -= radius*0.6f; shPos.z += radius*0.6f; m_terrain->GetNormal(norm, shPos); n[i++] = norm; n[i++] = norm; shPos = pos; shPos.x += radius*0.6f; shPos.z -= radius*0.6f; m_terrain->GetNormal(norm, shPos); n[i++] = norm; n[i++] = norm; shPos = pos; shPos.x -= radius*0.6f; shPos.z -= radius*0.6f; m_terrain->GetNormal(norm, shPos); n[i++] = norm; n[i++] = norm; shPos = pos; shPos.x += radius; shPos.z += radius; m_terrain->GetNormal(norm, shPos); n[i++] = norm; shPos = pos; shPos.x -= radius; shPos.z += radius; m_terrain->GetNormal(norm, shPos); n[i++] = norm; shPos = pos; shPos.x += radius; shPos.z -= radius; m_terrain->GetNormal(norm, shPos); n[i++] = norm; shPos = pos; shPos.x -= radius; shPos.z -= radius; m_terrain->GetNormal(norm, shPos); n[i++] = norm; norm.LoadZero(); for ( j=0 ; j(i); // average vector m_engine->SetObjectShadowNormal(rank, norm); if ( m_shadowLight != -1 ) { shPos = pos; shPos.y += m_shadowHeight; m_lightMan->SetLightPos(m_shadowLight, shPos); } if ( m_effectLight != -1 ) { shPos = pos; shPos.y += m_effectHeight; m_lightMan->SetLightPos(m_effectLight, shPos); } if ( m_bShowLimit ) { m_main->AdjustShowLimit(0, pos); } } } Math::Vector CObject::GetPosition(int part) { return m_objectPart[part].position; } // Getes the rotation around three axis. void CObject::SetAngle(int part, const Math::Vector &angle) { m_objectPart[part].angle = angle; m_objectPart[part].bRotate = true; // it will recalculate the matrices if ( part == 0 && !m_bFlat ) // main part? { m_engine->SetObjectShadowAngle(m_objectPart[0].object, m_objectPart[0].angle.y); } } Math::Vector CObject::GetAngle(int part) { return m_objectPart[part].angle; } // Getes the rotation about the axis Y. void CObject::SetAngleY(int part, float angle) { m_objectPart[part].angle.y = angle; m_objectPart[part].bRotate = true; // it will recalculate the matrices if ( part == 0 && !m_bFlat ) // main part? { m_engine->SetObjectShadowAngle(m_objectPart[0].object, m_objectPart[0].angle.y); } } // Getes the rotation about the axis X. void CObject::SetAngleX(int part, float angle) { m_objectPart[part].angle.x = angle; m_objectPart[part].bRotate = true; // it will recalculate the matrices } // Getes the rotation about the axis Z. void CObject::SetAngleZ(int part, float angle) { m_objectPart[part].angle.z = angle; m_objectPart[part].bRotate = true; //it will recalculate the matrices } float CObject::GetAngleY(int part) { return m_objectPart[part].angle.y; } float CObject::GetAngleX(int part) { return m_objectPart[part].angle.x; } float CObject::GetAngleZ(int part) { return m_objectPart[part].angle.z; } // Getes the global zoom. void CObject::SetZoom(int part, float zoom) { m_objectPart[part].bTranslate = true; // it will recalculate the matrices m_objectPart[part].zoom.x = zoom; m_objectPart[part].zoom.y = zoom; m_objectPart[part].zoom.z = zoom; m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || m_objectPart[part].zoom.y != 1.0f || m_objectPart[part].zoom.z != 1.0f ); } void CObject::SetZoom(int part, Math::Vector zoom) { m_objectPart[part].bTranslate = true; // it will recalculate the matrices m_objectPart[part].zoom = zoom; m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || m_objectPart[part].zoom.y != 1.0f || m_objectPart[part].zoom.z != 1.0f ); } Math::Vector CObject::GetZoom(int part) { return m_objectPart[part].zoom; } void CObject::SetZoomX(int part, float zoom) { m_objectPart[part].bTranslate = true; // it will recalculate the matrices m_objectPart[part].zoom.x = zoom; m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || m_objectPart[part].zoom.y != 1.0f || m_objectPart[part].zoom.z != 1.0f ); } void CObject::SetZoomY(int part, float zoom) { m_objectPart[part].bTranslate = true; // it will recalculate the matrices m_objectPart[part].zoom.y = zoom; m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || m_objectPart[part].zoom.y != 1.0f || m_objectPart[part].zoom.z != 1.0f ); } void CObject::SetZoomZ(int part, float zoom) { m_objectPart[part].bTranslate = true; // it will recalculate the matrices m_objectPart[part].zoom.z = zoom; m_objectPart[part].bZoom = ( m_objectPart[part].zoom.x != 1.0f || m_objectPart[part].zoom.y != 1.0f || m_objectPart[part].zoom.z != 1.0f ); } float CObject::GetZoomX(int part) { return m_objectPart[part].zoom.x; } float CObject::GetZoomY(int part) { return m_objectPart[part].zoom.y; } float CObject::GetZoomZ(int part) { return m_objectPart[part].zoom.z; } void CObject::SetTrainer(bool bEnable) { m_bTrainer = bEnable; if ( m_bTrainer ) // training? { m_cameraType = Gfx::CAM_TYPE_FIX; } } bool CObject::GetTrainer() { return m_bTrainer; } void CObject::SetToy(bool bEnable) { m_bToy = bEnable; } bool CObject::GetToy() { return m_bToy; } void CObject::SetManual(bool bManual) { m_bManual = bManual; } bool CObject::GetManual() { return m_bManual; } void CObject::SetResetCap(ResetCap cap) { m_resetCap = cap; } ResetCap CObject::GetResetCap() { return m_resetCap; } void CObject::SetResetBusy(bool bBusy) { m_bResetBusy = bBusy; } bool CObject::GetResetBusy() { return m_bResetBusy; } void CObject::SetResetPosition(const Math::Vector &pos) { m_resetPosition = pos; } Math::Vector CObject::GetResetPosition() { return m_resetPosition; } void CObject::SetResetAngle(const Math::Vector &angle) { m_resetAngle = angle; } Math::Vector CObject::GetResetAngle() { return m_resetAngle; } Program* CObject::GetResetRun() { return m_resetRun; } void CObject::SetResetRun(Program* run) { m_resetRun = run; } // Management of the particle master. void CObject::SetMasterParticle(int part, int parti) { m_objectPart[part].masterParti = parti; } int CObject::GetMasterParticle(int part) { return m_objectPart[part].masterParti; } // Management of the stack transport. void CObject::SetPower(CObject* power) { m_power = power; } CObject* CObject::GetPower() { return m_power; } // Management of the object transport. void CObject::SetCargo(CObject* cargo) { m_cargo = cargo; } CObject* CObject::GetCargo() { return m_cargo; } // Management of the object "transporter" that transports it. void CObject::SetTransporter(CObject* transporter) { m_transporter = transporter; // Invisible shadow if the object is transported. m_engine->SetObjectShadowHide(m_objectPart[0].object, (m_transporter != 0)); } CObject* CObject::GetTransporter() { return m_transporter; } // Management of the conveying portion. void CObject::SetTransporterPart(int part) { m_transporterLink = part; } void CObject::SetInfoReturn(float value) { m_infoReturn = value; } float CObject::GetInfoReturn() { return m_infoReturn; } bool CObject::SetCmdLine(int rank, float value) { if ( rank < 0 || rank >= OBJECTMAXCMDLINE ) return false; m_cmdLine[rank] = value; return true; } float CObject::GetCmdLine(int rank) { if ( rank < 0 || rank >= OBJECTMAXCMDLINE ) return 0.0f; return m_cmdLine[rank]; } // Returns matrices of an object portion. Math::Matrix* CObject::GetRotateMatrix(int part) { return &m_objectPart[part].matRotate; } Math::Matrix* CObject::GetWorldMatrix(int part) { if ( m_objectPart[0].bTranslate || m_objectPart[0].bRotate ) { UpdateTransformObject(); } return &m_objectPart[part].matWorld; } // Indicates whether the object should be drawn below the interface. void CObject::SetDrawWorld(bool bDraw) { int i; for ( i=0 ; iSetObjectDrawWorld(m_objectPart[i].object, bDraw); } } } // Indicates whether the object should be drawn over the interface. void CObject::SetDrawFront(bool bDraw) { int i; for ( i=0 ; iSetObjectDrawFront(m_objectPart[i].object, bDraw); } } } // Creates shade under a vehicle as a negative light. bool CObject::CreateShadowLight(float height, Gfx::Color color) { if ( !m_engine->GetLightMode() ) return true; Math::Vector pos = GetPosition(0); m_shadowHeight = height; Gfx::Light light; light.type = Gfx::LIGHT_SPOT; light.diffuse = color; light.ambient = color * 0.1f; light.position = Math::Vector(pos.x, pos.y+height, pos.z); light.direction = Math::Vector(0.0f, -1.0f, 0.0f); // against the bottom light.spotIntensity = 128; light.attenuation0 = 1.0f; light.attenuation1 = 0.0f; light.attenuation2 = 0.0f; light.spotAngle = 90.0f*Math::PI/180.0f; m_shadowLight = m_lightMan->CreateLight(); if ( m_shadowLight == -1 ) return false; m_lightMan->SetLight(m_shadowLight, light); // Only illuminates the objects on the ground. m_lightMan->SetLightIncludeType(m_shadowLight, Gfx::ENG_OBJTYPE_TERRAIN); return true; } // Returns the number of negative light shade. int CObject::GetShadowLight() { return m_shadowLight; } // Creates light for the effects of a vehicle. bool CObject::CreateEffectLight(float height, Gfx::Color color) { if ( !m_engine->GetLightMode() ) return true; m_effectHeight = height; Gfx::Light light; light.type = Gfx::LIGHT_SPOT; light.diffuse = color; light.position = Math::Vector(0.0f, height, 0.0f); light.direction = Math::Vector(0.0f, -1.0f, 0.0f); // against the bottom light.spotIntensity = 0.0f; light.attenuation0 = 1.0f; light.attenuation1 = 0.0f; light.attenuation2 = 0.0f; light.spotAngle = 90.0f*Math::PI/180.0f; m_effectLight = m_lightMan->CreateLight(); if ( m_effectLight == -1 ) return false; m_lightMan->SetLight(m_effectLight, light); m_lightMan->SetLightIntensity(m_effectLight, 0.0f); return true; } // Returns the number of light effects. int CObject::GetEffectLight() { return m_effectLight; } // Creates the circular shadow underneath a vehicle. bool CObject::CreateShadowCircle(float radius, float intensity, Gfx::EngineShadowType type) { float zoom; if ( intensity == 0.0f ) return true; zoom = GetZoomX(0); m_engine->CreateShadow(m_objectPart[0].object); m_engine->SetObjectShadowRadius(m_objectPart[0].object, radius*zoom); m_engine->SetObjectShadowIntensity(m_objectPart[0].object, intensity); m_engine->SetObjectShadowHeight(m_objectPart[0].object, 0.0f); m_engine->SetObjectShadowAngle(m_objectPart[0].object, m_objectPart[0].angle.y); m_engine->SetObjectShadowType(m_objectPart[0].object, type); return true; } // Reads a program. bool CObject::ReadProgram(Program* program, const char* filename) { if ( m_brain != nullptr ) { return m_brain->ReadProgram(program, filename); } return false; } // Writes a program. bool CObject::WriteProgram(Program* program, const char* filename) { if ( m_brain != nullptr ) { return m_brain->WriteProgram(program, filename); } return false; } // Calculates the matrix for transforming the object. // Returns true if the matrix has changed. // The rotations occur in the order Y, Z and X. bool CObject::UpdateTransformObject(int part, bool bForceUpdate) { Math::Vector position, angle, eye; bool bModif = false; int parent; if ( m_transporter != 0 ) // transported by transporter? { m_objectPart[part].bTranslate = true; m_objectPart[part].bRotate = true; } if ( !bForceUpdate && !m_objectPart[part].bTranslate && !m_objectPart[part].bRotate ) return false; position = m_objectPart[part].position; angle = m_objectPart[part].angle; if ( part == 0 ) // main part? { position += m_linVibration; angle += m_cirVibration+m_tilt; } if ( m_objectPart[part].bTranslate || m_objectPart[part].bRotate ) { if ( m_objectPart[part].bTranslate ) { m_objectPart[part].matTranslate.LoadIdentity(); m_objectPart[part].matTranslate.Set(1, 4, position.x); m_objectPart[part].matTranslate.Set(2, 4, position.y); m_objectPart[part].matTranslate.Set(3, 4, position.z); } if ( m_objectPart[part].bRotate ) { Math::LoadRotationZXYMatrix(m_objectPart[part].matRotate, angle); } if ( m_objectPart[part].bZoom ) { Math::Matrix mz; mz.LoadIdentity(); mz.Set(1, 1, m_objectPart[part].zoom.x); mz.Set(2, 2, m_objectPart[part].zoom.y); mz.Set(3, 3, m_objectPart[part].zoom.z); m_objectPart[part].matTransform = Math::MultiplyMatrices(m_objectPart[part].matTranslate, Math::MultiplyMatrices(m_objectPart[part].matRotate, mz)); } else { m_objectPart[part].matTransform = Math::MultiplyMatrices(m_objectPart[part].matTranslate, m_objectPart[part].matRotate); } bModif = true; } if ( bForceUpdate || m_objectPart[part].bTranslate || m_objectPart[part].bRotate ) { parent = m_objectPart[part].parentPart; if ( part == 0 && m_transporter != 0 ) // transported by a transporter? { Math::Matrix* matWorldTransporter; matWorldTransporter = m_transporter->GetWorldMatrix(m_transporterLink); m_objectPart[part].matWorld = Math::MultiplyMatrices(*matWorldTransporter, m_objectPart[part].matTransform); } else { if ( parent == -1 ) // no parent? { m_objectPart[part].matWorld = m_objectPart[part].matTransform; } else { m_objectPart[part].matWorld = Math::MultiplyMatrices(m_objectPart[parent].matWorld, m_objectPart[part].matTransform); } } bModif = true; } if ( bModif ) { m_engine->SetObjectTransform(m_objectPart[part].object, m_objectPart[part].matWorld); } m_objectPart[part].bTranslate = false; m_objectPart[part].bRotate = false; return bModif; } // Updates all matrices to transform the object father and all his sons. // Assume a maximum of 4 degrees of freedom. // Appropriate, for example, to a body, an arm, forearm, hand and fingers. bool CObject::UpdateTransformObject() { bool bUpdate1, bUpdate2, bUpdate3, bUpdate4; int level1, level2, level3, level4, rank; int parent1, parent2, parent3, parent4; if ( m_bFlat ) { for ( level1=0 ; level1ChangeTextureMapping(m_objectPart[0].object, mat, Gfx::ENG_RSTATE_PART3, "objects/lemt.png", "", lodLevels[j], Gfx::ENG_TEX_MAPPING_1Y, au, bu, 1.0f, 0.0f); } } // Manual action. bool CObject::EventProcess(const Event &event) { if ( event.type == EVENT_KEY_DOWN ) { #if ADJUST_ONBOARD if ( m_bSelect ) { if ( event.param == 'E' ) debug_x += 0.1f; if ( event.param == 'D' ) debug_x -= 0.1f; if ( event.param == 'R' ) debug_y += 0.1f; if ( event.param == 'F' ) debug_y -= 0.1f; if ( event.param == 'T' ) debug_z += 0.1f; if ( event.param == 'G' ) debug_z -= 0.1f; } #endif #if ADJUST_ARM if ( m_bSelect ) { if ( event.param == 'X' ) debug_arm1 += 5.0f*Math::PI/180.0f; if ( event.param == 'C' ) debug_arm1 -= 5.0f*Math::PI/180.0f; if ( event.param == 'V' ) debug_arm2 += 5.0f*Math::PI/180.0f; if ( event.param == 'B' ) debug_arm2 -= 5.0f*Math::PI/180.0f; if ( event.param == 'N' ) debug_arm3 += 5.0f*Math::PI/180.0f; if ( event.param == 'M' ) debug_arm3 -= 5.0f*Math::PI/180.0f; if ( event.param == 'X' || event.param == 'C' || event.param == 'V' || event.param == 'B' || event.param == 'N' || event.param == 'M' ) { SetAngleZ(1, debug_arm1); SetAngleZ(2, debug_arm2); SetAngleZ(3, debug_arm3); char s[100]; sprintf(s, "a=%.2f b=%.2f c=%.2f", debug_arm1*180.0f/Math::PI, debug_arm2*180.0f/Math::PI, debug_arm3*180.0f/Math::PI); m_engine->SetInfoText(5, s); } } #endif } if ( m_physics != nullptr ) { if ( !m_physics->EventProcess(event) ) // object destroyed? { if ( GetSelect() && m_type != OBJECT_ANT && m_type != OBJECT_SPIDER && m_type != OBJECT_BEE ) { if ( !m_bDead ) m_camera->SetType(Gfx::CAM_TYPE_EXPLO); m_main->DeselectAll(); } return false; } } if ( m_auto != nullptr ) { m_auto->EventProcess(event); if ( event.type == EVENT_FRAME && m_auto->IsEnded() != ERR_CONTINUE ) { m_auto->DeleteObject(); m_auto.reset(); } } if ( m_motion != nullptr ) { m_motion->EventProcess(event); } if ( event.type == EVENT_FRAME ) { return EventFrame(event); } return true; } // Animates the object. bool CObject::EventFrame(const Event &event) { if ( m_type == OBJECT_HUMAN && m_main->GetMainMovie() == MM_SATCOMopen ) { UpdateTransformObject(); return true; } if ( m_type != OBJECT_SHOW && m_engine->GetPause() ) return true; m_aTime += event.rTime; m_shotTime += event.rTime; VirusFrame(event.rTime); PartiFrame(event.rTime); UpdateMapping(); UpdateTransformObject(); UpdateSelectParticle(); if ( m_bProxyActivate ) // active if it is near? { Math::Vector eye = m_engine->GetLookatPt(); float dist = Math::Distance(eye, GetPosition(0)); if ( dist < m_proxyDistance ) { m_bProxyActivate = false; m_main->CreateShortcuts(); m_sound->Play(SOUND_FINDING); m_engine->GetPyroManager()->Create(Gfx::PT_FINDING, this, 0.0f); m_main->DisplayError(INFO_FINDING, this); } } return true; } // Updates the mapping of the object. void CObject::UpdateMapping() { if ( m_type == OBJECT_POWER || m_type == OBJECT_ATOMIC || m_type == OBJECT_STATION || m_type == OBJECT_ENERGY ) { UpdateEnergyMapping(); } } // Management of viruses. void CObject::VirusFrame(float rTime) { Gfx::ParticleType type; Math::Vector pos, speed; Math::Point dim; int r; if ( !m_bVirusMode ) return; // healthy object? m_virusTime += rTime; if ( m_virusTime >= VIRUS_DELAY ) { m_bVirusMode = false; // the virus is no longer active } if ( m_lastVirusParticle+m_engine->ParticleAdapt(0.2f) <= m_aTime ) { m_lastVirusParticle = m_aTime; r = rand()%10; if ( r == 0 ) type = Gfx::PARTIVIRUS1; if ( r == 1 ) type = Gfx::PARTIVIRUS2; if ( r == 2 ) type = Gfx::PARTIVIRUS3; if ( r == 3 ) type = Gfx::PARTIVIRUS4; if ( r == 4 ) type = Gfx::PARTIVIRUS5; if ( r == 5 ) type = Gfx::PARTIVIRUS6; if ( r == 6 ) type = Gfx::PARTIVIRUS7; if ( r == 7 ) type = Gfx::PARTIVIRUS8; if ( r == 8 ) type = Gfx::PARTIVIRUS9; if ( r == 9 ) type = Gfx::PARTIVIRUS10; pos = GetPosition(0); pos.x += (Math::Rand()-0.5f)*10.0f; pos.z += (Math::Rand()-0.5f)*10.0f; speed.x = (Math::Rand()-0.5f)*2.0f; speed.z = (Math::Rand()-0.5f)*2.0f; speed.y = Math::Rand()*4.0f+4.0f; dim.x = Math::Rand()*0.3f+0.3f; dim.y = dim.x; m_particle->CreateParticle(pos, speed, dim, type, 3.0f); } } // Management particles mistresses. void CObject::PartiFrame(float rTime) { Math::Vector pos, angle, factor; int i, channel; for ( i=0 ; iGetPosition(channel, pos) ) { m_objectPart[i].masterParti = -1; // particle no longer exists! continue; } SetPosition(i, pos); // Each song spins differently. switch( i%5 ) { case 0: factor = Math::Vector( 0.5f, 0.3f, 0.6f); break; case 1: factor = Math::Vector(-0.3f, 0.4f,-0.2f); break; case 2: factor = Math::Vector( 0.4f,-0.6f,-0.3f); break; case 3: factor = Math::Vector(-0.6f,-0.2f, 0.0f); break; case 4: factor = Math::Vector( 0.4f, 0.1f,-0.7f); break; } angle = GetAngle(i); angle += rTime*Math::PI*factor; SetAngle(i, angle); } } // Changes the perspective to view if it was like in the vehicle, // or behind the vehicle. void CObject::SetViewFromHere(Math::Vector &eye, float &dirH, float &dirV, Math::Vector &lookat, Math::Vector &upVec, Gfx::CameraType type) { float speed; int part; UpdateTransformObject(); part = 0; if ( m_type == OBJECT_HUMAN || m_type == OBJECT_TECH ) { eye.x = -0.2f; eye.y = 3.3f; eye.z = 0.0f; //? eye.x = 1.0f; //? eye.y = 3.3f; //? eye.z = 0.0f; } else if ( m_type == OBJECT_MOBILErt || m_type == OBJECT_MOBILErr || m_type == OBJECT_MOBILErs ) { eye.x = -1.1f; // on the cap eye.y = 7.9f; eye.z = 0.0f; } else if ( m_type == OBJECT_MOBILEwc || m_type == OBJECT_MOBILEtc || m_type == OBJECT_MOBILEfc || m_type == OBJECT_MOBILEic ) // fireball? { //? eye.x = -0.9f; // on the cannon //? eye.y = 3.0f; //? eye.z = 0.0f; //? part = 1; eye.x = -0.9f; // on the cannon eye.y = 8.3f; eye.z = 0.0f; } else if ( m_type == OBJECT_MOBILEwi || m_type == OBJECT_MOBILEti || m_type == OBJECT_MOBILEfi || m_type == OBJECT_MOBILEii ) // orgaball ? { //? eye.x = -3.5f; // on the cannon //? eye.y = 5.1f; //? eye.z = 0.0f; //? part = 1; eye.x = -2.5f; // on the cannon eye.y = 10.4f; eye.z = 0.0f; } else if ( m_type == OBJECT_MOBILErc ) { //? eye.x = 2.0f; // in the cannon //? eye.y = 0.0f; //? eye.z = 0.0f; //? part = 2; eye.x = 4.0f; // on the cannon eye.y = 11.0f; eye.z = 0.0f; } else if ( m_type == OBJECT_MOBILEsa ) { eye.x = 3.0f; eye.y = 4.5f; eye.z = 0.0f; } else if ( m_type == OBJECT_MOBILEdr ) { eye.x = 1.0f; eye.y = 6.5f; eye.z = 0.0f; } else if ( m_type == OBJECT_APOLLO2 ) { eye.x = -3.0f; eye.y = 6.0f; eye.z = -2.0f; } else { eye.x = 0.7f; // between the brackets eye.y = 4.8f; eye.z = 0.0f; } #if ADJUST_ONBOARD eye.x += debug_x; eye.y += debug_y; eye.z += debug_z; char s[100]; sprintf(s, "x=%.2f y=%.2f z=%.2f", eye.x, eye.y, eye.z); m_engine->SetInfoText(4, s); #endif if ( type == Gfx::CAM_TYPE_BACK ) { eye.x -= 20.0f; eye.y += 1.0f; } lookat.x = eye.x+1.0f; lookat.y = eye.y+0.0f; lookat.z = eye.z+0.0f; eye = Math::Transform(m_objectPart[part].matWorld, eye); lookat = Math::Transform(m_objectPart[part].matWorld, lookat); // Camera tilts when turning. upVec = Math::Vector(0.0f, 1.0f, 0.0f); if ( m_physics != nullptr ) { if ( m_physics->GetLand() ) // on ground? { speed = m_physics->GetLinMotionX(MO_REASPEED); lookat.y -= speed*0.002f; speed = m_physics->GetCirMotionY(MO_REASPEED); upVec.z -= speed*0.04f; } else // in flight? { speed = m_physics->GetLinMotionX(MO_REASPEED); lookat.y += speed*0.002f; speed = m_physics->GetCirMotionY(MO_REASPEED); upVec.z += speed*0.08f; } } upVec = Math::Transform(m_objectPart[0].matRotate, upVec); dirH = -(m_objectPart[part].angle.y+Math::PI/2.0f); dirV = 0.0f; } // Management of features. void CObject::GetCharacter(Character* character) { memcpy(character, &m_character, sizeof(Character)); } Character* CObject::GetCharacter() { return &m_character; } // Returns the absolute time. float CObject::GetAbsTime() { return m_aTime; } // Management of energy contained in a battery. // Single subject possesses the battery energy, but not the vehicle that carries the battery! void CObject::SetEnergy(float level) { if ( level < 0.0f ) level = 0.0f; if ( level > 1.0f ) level = 1.0f; m_energy = level; } float CObject::GetEnergy() { if ( m_type != OBJECT_POWER && m_type != OBJECT_ATOMIC && m_type != OBJECT_STATION && m_type != OBJECT_ENERGY ) return 0.0f; return m_energy; } // Management of the capacity of a battery. // Single subject possesses a battery capacity, // but not the vehicle that carries the battery! void CObject::SetCapacity(float capacity) { m_capacity = capacity; } float CObject::GetCapacity() { return m_capacity; } // Management of the shield. void CObject::SetShield(float level) { m_shield = level; } float CObject::GetShield() { if ( m_type == OBJECT_FRET || m_type == OBJECT_STONE || m_type == OBJECT_URANIUM || m_type == OBJECT_BULLET || m_type == OBJECT_METAL || m_type == OBJECT_BBOX || m_type == OBJECT_KEYa || m_type == OBJECT_KEYb || m_type == OBJECT_KEYc || m_type == OBJECT_KEYd || m_type == OBJECT_TNT || m_type == OBJECT_TEEN31 || // basket? m_type == OBJECT_SCRAP1 || m_type == OBJECT_SCRAP2 || m_type == OBJECT_SCRAP3 || m_type == OBJECT_SCRAP4 || m_type == OBJECT_SCRAP5 || m_type == OBJECT_BOMB || m_type == OBJECT_WAYPOINT || m_type == OBJECT_FLAGb || m_type == OBJECT_FLAGr || m_type == OBJECT_FLAGg || m_type == OBJECT_FLAGy || m_type == OBJECT_FLAGv || m_type == OBJECT_POWER || m_type == OBJECT_ATOMIC || m_type == OBJECT_ANT || m_type == OBJECT_SPIDER || m_type == OBJECT_BEE || m_type == OBJECT_WORM ) return 0.0f; return m_shield; } // Management of flight range (zero = infinity). void CObject::SetRange(float delay) { m_range = delay; } float CObject::GetRange() { return m_range; } // Management of transparency of the object. void CObject::SetTransparency(float value) { int i; m_transparency = value; for ( i=0 ; iSetObjectTransparency(m_objectPart[i].object, value); } } } // Indicates whether an object is stationary (ant on the back). void CObject::SetFixed(bool bFixed) { m_bFixed = bFixed; } bool CObject::GetFixed() { return m_bFixed; } // Indicates whether an object is subjected to clipping (obstacles). void CObject::SetClip(bool bClip) { m_bClip = bClip; } bool CObject::GetClip() { return m_bClip; } // Controls object team void CObject::SetTeam(int team) { m_team = team; } int CObject::GetTeam() { return m_team; } // Pushes an object. bool CObject::JostleObject(float force) { if ( m_type == OBJECT_FLAGb || m_type == OBJECT_FLAGr || m_type == OBJECT_FLAGg || m_type == OBJECT_FLAGy || m_type == OBJECT_FLAGv ) // flag? { if ( m_auto == nullptr ) return false; m_auto->Start(1); } else { if ( m_auto != nullptr ) return false; std::unique_ptr autoJostle{new CAutoJostle(this)}; autoJostle->Start(0, force); m_auto = std::move(autoJostle); } return true; } // Beginning of the effect when the instruction "detect" is used. void CObject::StartDetectEffect(CObject *target, bool bFound) { Math::Matrix* mat; Math::Vector pos, goal; Math::Point dim; mat = GetWorldMatrix(0); pos = Math::Transform(*mat, Math::Vector(2.0f, 3.0f, 0.0f)); if ( target == 0 ) { goal = Math::Transform(*mat, Math::Vector(50.0f, 3.0f, 0.0f)); } else { goal = target->GetPosition(0); goal.y += 3.0f; goal = Math::SegmentPoint(pos, goal, Math::Distance(pos, goal)-3.0f); } dim.x = 3.0f; dim.y = dim.x; m_particle->CreateRay(pos, goal, Gfx::PARTIRAY2, dim, 0.2f); if ( target != 0 ) { goal = target->GetPosition(0); goal.y += 3.0f; goal = Math::SegmentPoint(pos, goal, Math::Distance(pos, goal)-1.0f); dim.x = 6.0f; dim.y = dim.x; m_particle->CreateParticle(goal, Math::Vector(0.0f, 0.0f, 0.0f), dim, bFound?Gfx::PARTIGLINT:Gfx::PARTIGLINTr, 0.5f); } m_sound->Play(bFound?SOUND_BUILD:SOUND_RECOVER); } // Management of time from which a virus is active. void CObject::SetVirusMode(bool bEnable) { m_bVirusMode = bEnable; m_virusTime = 0.0f; if ( m_bVirusMode && m_brain != nullptr ) { if ( !m_brain->IntroduceVirus() ) // tries to infect { m_bVirusMode = false; // program was not contaminated! } } } bool CObject::GetVirusMode() { return m_bVirusMode; } float CObject::GetVirusTime() { return m_virusTime; } // Management mode of the camera. void CObject::SetCameraType(Gfx::CameraType type) { m_cameraType = type; } Gfx::CameraType CObject::GetCameraType() { return m_cameraType; } void CObject::SetCameraDist(float dist) { m_cameraDist = dist; } float CObject::GetCameraDist() { return m_cameraDist; } void CObject::SetCameraLock(bool bLock) { m_bCameraLock = bLock; } bool CObject::GetCameraLock() { return m_bCameraLock; } // Management of the demonstration of the object. void CObject::SetHighlight(bool mode) { if (mode) { int list[OBJECTMAXPART+1]; int j = 0; for (int i = 0; i < m_totalPart; i++) { if ( m_objectPart[i].bUsed ) { list[j++] = m_objectPart[i].object; } } list[j] = -1; // terminate m_engine->SetHighlightRank(list); // gives the list of selected parts } } // Indicates whether the object is selected or not. void CObject::SetSelect(bool bMode, bool bDisplayError) { Error err; m_bSelect = bMode; if ( m_physics != nullptr ) { m_physics->CreateInterface(m_bSelect); } if ( m_auto != nullptr ) { m_auto->CreateInterface(m_bSelect); } CreateSelectParticle(); // creates / removes particles if ( !m_bSelect ) { //SetGunGoalH(0.0f); // puts the cannon right return; // selects if not finished } err = ERR_OK; if ( m_physics != nullptr ) { err = m_physics->GetError(); } if ( m_auto != nullptr ) { err = m_auto->GetError(); } if ( err != ERR_OK && bDisplayError ) { m_main->DisplayError(err, this); } } // Indicates whether the object is selected or not. bool CObject::GetSelect(bool bReal) { if ( !bReal && m_main->GetFixScene() ) return false; return m_bSelect; } // Indicates whether the object is selectable or not. void CObject::SetSelectable(bool bMode) { m_bSelectable = bMode; } // Indicates whether the object is selecionnable or not. bool CObject::GetSelectable() { return m_bSelectable; } // Management of the activities of an object. void CObject::SetActivity(bool bMode) { if ( m_brain != nullptr ) { m_brain->SetActivity(bMode); } } bool CObject::GetActivity() { if ( m_brain != nullptr ) { return m_brain->GetActivity(); } return false; } // Indicates if necessary to check the tokens of the object. void CObject::SetCheckToken(bool bMode) { m_bCheckToken = bMode; } // Indicates if necessary to check the tokens of the object. bool CObject::GetCheckToken() { return m_bCheckToken; } // Management of the visibility of an object. // The object is not hidden or visually disabled, but ignores detections! // For example: underground worm. void CObject::SetVisible(bool bVisible) { m_bVisible = bVisible; } // Management mode of operation of an object. // An inactive object is an object destroyed, nonexistent. // This mode is used for objects "resetables" // during training to simulate destruction. void CObject::SetEnable(bool bEnable) { m_bEnable = bEnable; } bool CObject::GetEnable() { return m_bEnable; } // Management mode or an object is only active when you're close. void CObject::SetProxyActivate(bool bActivate) { m_bProxyActivate = bActivate; } bool CObject::GetProxyActivate() { return m_bProxyActivate; } void CObject::SetProxyDistance(float distance) { m_proxyDistance = distance; } float CObject::GetProxyDistance() { return m_proxyDistance; } // Management of the method of increasing damage. void CObject::SetMagnifyDamage(float factor) { m_magnifyDamage = factor; } float CObject::GetMagnifyDamage() { return m_magnifyDamage; } // Management of free parameter. void CObject::SetParam(float value) { m_param = value; } float CObject::GetParam() { return m_param; } // Management of the mode "blocked" of an object. // For example, a cube of titanium is blocked while it is used to make something, // or a vehicle is blocked as its construction is not finished. void CObject::SetLock(bool bLock) { m_bLock = bLock; } bool CObject::GetLock() { return m_bLock; } // Ignore checks in build() function void CObject::SetIgnoreBuildCheck(bool bIgnoreBuildCheck) { m_bIgnoreBuildCheck = bIgnoreBuildCheck; } bool CObject::GetIgnoreBuildCheck() { return m_bIgnoreBuildCheck; } // Management of the mode "current explosion" of an object. // An object in this mode is not saving. void CObject::SetExploding(bool bExplo) { m_bExplo = bExplo; } bool CObject::IsExploding() { return m_bExplo; } // Mode management "cargo ship" during movies. void CObject::SetSpaceshipCargo(bool bCargo) { m_bCargo = bCargo; } bool CObject::IsSpaceshipCargo() { return m_bCargo; } // Management of the HS mode of an object. void CObject::SetBurn(bool bBurn) { m_bBurn = bBurn; //? if ( m_botVar != 0 ) //? { //? if ( m_bBurn ) m_botVar->SetUserPtr(OBJECTDELETED); //? else m_botVar->SetUserPtr(this); //? } } bool CObject::GetBurn() { return m_bBurn; } void CObject::SetDead(bool bDead) { m_bDead = bDead; if ( bDead && m_brain != nullptr ) { m_brain->StopProgram(); // stops the current task } //? if ( m_botVar != 0 ) //? { //? if ( m_bDead ) m_botVar->SetUserPtr(OBJECTDELETED); //? else m_botVar->SetUserPtr(this); //? } } bool CObject::GetDead() { return m_bDead; } bool CObject::GetRuin() { return m_bBurn|m_bFlat; } bool CObject::GetActive() { return !m_bLock && !m_bBurn && !m_bFlat && m_bVisible && m_bEnable; } // Management of the point of aim. void CObject::SetGunGoalV(float gunGoal) { if ( m_type == OBJECT_MOBILEfc || m_type == OBJECT_MOBILEtc || m_type == OBJECT_MOBILEwc || m_type == OBJECT_MOBILEic ) // fireball? { if ( gunGoal > 10.0f*Math::PI/180.0f ) gunGoal = 10.0f*Math::PI/180.0f; if ( gunGoal < -20.0f*Math::PI/180.0f ) gunGoal = -20.0f*Math::PI/180.0f; SetAngleZ(1, gunGoal); } else if ( m_type == OBJECT_MOBILEfi || m_type == OBJECT_MOBILEti || m_type == OBJECT_MOBILEwi || m_type == OBJECT_MOBILEii ) // orgaball? { if ( gunGoal > 20.0f*Math::PI/180.0f ) gunGoal = 20.0f*Math::PI/180.0f; if ( gunGoal < -20.0f*Math::PI/180.0f ) gunGoal = -20.0f*Math::PI/180.0f; SetAngleZ(1, gunGoal); } else if ( m_type == OBJECT_MOBILErc ) // phazer? { if ( gunGoal > 45.0f*Math::PI/180.0f ) gunGoal = 45.0f*Math::PI/180.0f; if ( gunGoal < -20.0f*Math::PI/180.0f ) gunGoal = -20.0f*Math::PI/180.0f; SetAngleZ(2, gunGoal); } else { gunGoal = 0.0f; } m_gunGoalV = gunGoal; } void CObject::SetGunGoalH(float gunGoal) { if ( m_type == OBJECT_MOBILEfc || m_type == OBJECT_MOBILEtc || m_type == OBJECT_MOBILEwc || m_type == OBJECT_MOBILEic ) // fireball? { if ( gunGoal > 40.0f*Math::PI/180.0f ) gunGoal = 40.0f*Math::PI/180.0f; if ( gunGoal < -40.0f*Math::PI/180.0f ) gunGoal = -40.0f*Math::PI/180.0f; SetAngleY(1, gunGoal); } else if ( m_type == OBJECT_MOBILEfi || m_type == OBJECT_MOBILEti || m_type == OBJECT_MOBILEwi || m_type == OBJECT_MOBILEii ) // orgaball? { if ( gunGoal > 40.0f*Math::PI/180.0f ) gunGoal = 40.0f*Math::PI/180.0f; if ( gunGoal < -40.0f*Math::PI/180.0f ) gunGoal = -40.0f*Math::PI/180.0f; SetAngleY(1, gunGoal); } else if ( m_type == OBJECT_MOBILErc ) // phazer? { if ( gunGoal > 40.0f*Math::PI/180.0f ) gunGoal = 40.0f*Math::PI/180.0f; if ( gunGoal < -40.0f*Math::PI/180.0f ) gunGoal = -40.0f*Math::PI/180.0f; SetAngleY(2, gunGoal); } else { gunGoal = 0.0f; } m_gunGoalH = gunGoal; } float CObject::GetGunGoalV() { return m_gunGoalV; } float CObject::GetGunGoalH() { return m_gunGoalH; } // Shows the limits of the object. bool CObject::StartShowLimit() { if ( m_showLimitRadius == 0.0f ) return false; m_main->SetShowLimit(0, Gfx::PARTILIMIT1, this, GetPosition(0), m_showLimitRadius); m_bShowLimit = true; return true; } void CObject::StopShowLimit() { m_bShowLimit = false; } void CObject::SetShowLimitRadius(float radius) { m_showLimitRadius = radius; } // Indicates whether a program is under execution. bool CObject::IsProgram() { if ( m_brain == nullptr ) return false; return m_brain->IsProgram(); } // Creates or removes particles associated to the object. void CObject::CreateSelectParticle() { Math::Vector pos, speed; Math::Point dim; int i; // Removes particles preceding. for ( i=0 ; i<4 ; i++ ) { if ( m_partiSel[i] != -1 ) { m_particle->DeleteParticle(m_partiSel[i]); m_partiSel[i] = -1; } } if ( m_bSelect || IsProgram() || m_main->GetMissionType() == MISSION_RETRO ) { // Creates particles lens for the headlights. if ( m_type == OBJECT_MOBILEfa || m_type == OBJECT_MOBILEta || m_type == OBJECT_MOBILEwa || m_type == OBJECT_MOBILEia || m_type == OBJECT_MOBILEfc || m_type == OBJECT_MOBILEtc || m_type == OBJECT_MOBILEwc || m_type == OBJECT_MOBILEic || m_type == OBJECT_MOBILEfi || m_type == OBJECT_MOBILEti || m_type == OBJECT_MOBILEwi || m_type == OBJECT_MOBILEii || m_type == OBJECT_MOBILEfs || m_type == OBJECT_MOBILEts || m_type == OBJECT_MOBILEws || m_type == OBJECT_MOBILEis || m_type == OBJECT_MOBILErt || m_type == OBJECT_MOBILErc || m_type == OBJECT_MOBILErr || m_type == OBJECT_MOBILErs || m_type == OBJECT_MOBILEsa || m_type == OBJECT_MOBILEtg || m_type == OBJECT_MOBILEft || m_type == OBJECT_MOBILEtt || m_type == OBJECT_MOBILEwt || m_type == OBJECT_MOBILEit || m_type == OBJECT_MOBILEdr ) // vehicle? { pos = Math::Vector(0.0f, 0.0f, 0.0f); speed = Math::Vector(0.0f, 0.0f, 0.0f); dim.x = 0.0f; dim.y = 0.0f; m_partiSel[0] = m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISELY, 1.0f, 0.0f, 0.0f); m_partiSel[1] = m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISELY, 1.0f, 0.0f, 0.0f); m_partiSel[2] = m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISELR, 1.0f, 0.0f, 0.0f); m_partiSel[3] = m_particle->CreateParticle(pos, speed, dim, Gfx::PARTISELR, 1.0f, 0.0f, 0.0f); UpdateSelectParticle(); } } } // Updates the particles associated to the object. void CObject::UpdateSelectParticle() { Math::Vector pos[4]; Math::Point dim[4]; float zoom[4]; float angle; int i; if ( !m_bSelect && !IsProgram() && m_main->GetMissionType() != MISSION_RETRO ) return; dim[0].x = 1.0f; dim[1].x = 1.0f; dim[2].x = 1.2f; dim[3].x = 1.2f; // Lens front yellow. if ( m_type == OBJECT_MOBILErt || m_type == OBJECT_MOBILErc || m_type == OBJECT_MOBILErr || m_type == OBJECT_MOBILErs ) // large caterpillars? { pos[0] = Math::Vector(4.2f, 2.8f, 1.5f); pos[1] = Math::Vector(4.2f, 2.8f, -1.5f); dim[0].x = 1.5f; dim[1].x = 1.5f; } else if ( m_type == OBJECT_MOBILEwt || m_type == OBJECT_MOBILEtt || m_type == OBJECT_MOBILEft || m_type == OBJECT_MOBILEit ) // trainer ? { pos[0] = Math::Vector(4.2f, 2.5f, 1.2f); pos[1] = Math::Vector(4.2f, 2.5f, -1.2f); dim[0].x = 1.5f; dim[1].x = 1.5f; } else if ( m_type == OBJECT_MOBILEsa ) // submarine? { pos[0] = Math::Vector(3.6f, 4.0f, 2.0f); pos[1] = Math::Vector(3.6f, 4.0f, -2.0f); } else if ( m_type == OBJECT_MOBILEtg ) // target? { pos[0] = Math::Vector(3.4f, 6.5f, 2.0f); pos[1] = Math::Vector(3.4f, 6.5f, -2.0f); } else if ( m_type == OBJECT_MOBILEdr ) // designer? { pos[0] = Math::Vector(4.9f, 3.5f, 2.5f); pos[1] = Math::Vector(4.9f, 3.5f, -2.5f); } else { pos[0] = Math::Vector(4.2f, 2.5f, 1.5f); pos[1] = Math::Vector(4.2f, 2.5f, -1.5f); } // Red back lens if ( m_type == OBJECT_MOBILEfa || m_type == OBJECT_MOBILEfc || m_type == OBJECT_MOBILEfi || m_type == OBJECT_MOBILEfs || m_type == OBJECT_MOBILEft ) // flying? { pos[2] = Math::Vector(-4.0f, 3.1f, 4.5f); pos[3] = Math::Vector(-4.0f, 3.1f, -4.5f); dim[2].x = 0.6f; dim[3].x = 0.6f; } if ( m_type == OBJECT_MOBILEwa || m_type == OBJECT_MOBILEwc || m_type == OBJECT_MOBILEwi || m_type == OBJECT_MOBILEws ) // wheels? { pos[2] = Math::Vector(-4.5f, 2.7f, 2.8f); pos[3] = Math::Vector(-4.5f, 2.7f, -2.8f); } if ( m_type == OBJECT_MOBILEwt ) // wheels? { pos[2] = Math::Vector(-4.0f, 2.5f, 2.2f); pos[3] = Math::Vector(-4.0f, 2.5f, -2.2f); } if ( m_type == OBJECT_MOBILEia || m_type == OBJECT_MOBILEic || m_type == OBJECT_MOBILEii || m_type == OBJECT_MOBILEis || m_type == OBJECT_MOBILEit ) // legs? { pos[2] = Math::Vector(-4.5f, 2.7f, 2.8f); pos[3] = Math::Vector(-4.5f, 2.7f, -2.8f); } if ( m_type == OBJECT_MOBILEta || m_type == OBJECT_MOBILEtc || m_type == OBJECT_MOBILEti || m_type == OBJECT_MOBILEts || m_type == OBJECT_MOBILEtt ) // caterpillars? { pos[2] = Math::Vector(-3.6f, 4.2f, 3.0f); pos[3] = Math::Vector(-3.6f, 4.2f, -3.0f); } if ( m_type == OBJECT_MOBILErt || m_type == OBJECT_MOBILErc || m_type == OBJECT_MOBILErr || m_type == OBJECT_MOBILErs ) // large caterpillars? { pos[2] = Math::Vector(-5.0f, 5.2f, 2.5f); pos[3] = Math::Vector(-5.0f, 5.2f, -2.5f); } if ( m_type == OBJECT_MOBILEsa ) // submarine? { pos[2] = Math::Vector(-3.6f, 4.0f, 2.0f); pos[3] = Math::Vector(-3.6f, 4.0f, -2.0f); } if ( m_type == OBJECT_MOBILEtg ) // target? { pos[2] = Math::Vector(-2.4f, 6.5f, 2.0f); pos[3] = Math::Vector(-2.4f, 6.5f, -2.0f); } if ( m_type == OBJECT_MOBILEdr ) // designer? { pos[2] = Math::Vector(-5.3f, 2.7f, 1.8f); pos[3] = Math::Vector(-5.3f, 2.7f, -1.8f); } angle = GetAngleY(0)/Math::PI; zoom[0] = 1.0f; zoom[1] = 1.0f; zoom[2] = 1.0f; zoom[3] = 1.0f; if ( ( IsProgram() || // current program? m_main->GetMissionType() == MISSION_RETRO ) && // Retro mode? Math::Mod(m_aTime, 0.7f) < 0.3f ) { zoom[0] = 0.0f; // blinks zoom[1] = 0.0f; zoom[2] = 0.0f; zoom[3] = 0.0f; } // Updates lens. for ( i=0 ; i<4 ; i++ ) { pos[i] = Math::Transform(m_objectPart[0].matWorld, pos[i]); dim[i].y = dim[i].x; m_particle->SetParam(m_partiSel[i], pos[i], dim[i], zoom[i], angle, 1.0f); } } // Getes the pointer to the current script execution. void CObject::SetRunScript(CScript* script) { m_runScript = script; } CScript* CObject::GetRunScript() { return m_runScript; } // Returns the variables of "this" for CBOT. CBotVar* CObject::GetBotVar() { return m_botVar; } // Returns the physics associated to the object. CPhysics* CObject::GetPhysics() { return m_physics.get(); } void CObject::SetPhysics(std::unique_ptr physics) { m_physics = std::move(physics); } // Returns the brain associated to the object. CBrain* CObject::GetBrain() { return m_brain.get(); } void CObject::SetBrain(std::unique_ptr brain) { m_brain = std::move(brain); } // Returns the movement associated to the object. CMotion* CObject::GetMotion() { return m_motion.get(); } void CObject::SetMotion(std::unique_ptr motion) { m_motion = std::move(motion); } // Returns the controller associated to the object. CAuto* CObject::GetAuto() { return m_auto.get(); } void CObject::SetAuto(std::unique_ptr automat) { m_auto = std::move(automat); } // Management of the position in the file definition. void CObject::SetDefRank(int rank) { m_defRank = rank; } int CObject::GetDefRank() { return m_defRank; } // Getes the object name for the tooltip. bool CObject::GetTooltipName(std::string& name) { GetResource(RES_OBJECT, m_type, name); if(GetTeam() != 0) { name += " [team "+boost::lexical_cast(GetTeam())+"]"; //TODO: better way to display this } return !name.empty(); } // Adds the object previously selected in the list. void CObject::AddDeselList(CObject* pObj) { int i; if ( m_totalDesectList >= OBJECTMAXDESELLIST ) { for ( i=0 ; i