2012-06-26 20:23:05 +00:00
|
|
|
// * This file is part of the COLOBOT source code
|
|
|
|
// * Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch
|
|
|
|
// *
|
|
|
|
// * 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://www.gnu.org/licenses/.
|
|
|
|
|
|
|
|
// taskbuild.cpp
|
|
|
|
|
|
|
|
#include "object/task/taskbuild.h"
|
|
|
|
|
|
|
|
#include "common/iman.h"
|
2012-09-11 21:11:34 +00:00
|
|
|
#include "graphics/core/color.h"
|
|
|
|
#include "graphics/core/light.h"
|
|
|
|
#include "graphics/engine/lightman.h"
|
|
|
|
#include "graphics/engine/terrain.h"
|
|
|
|
#include "graphics/engine/water.h"
|
2012-06-26 20:23:05 +00:00
|
|
|
#include "math/geometry.h"
|
|
|
|
#include "object/auto/auto.h"
|
|
|
|
#include "object/motion/motionhuman.h"
|
|
|
|
#include "object/robotmain.h"
|
|
|
|
#include "physics/physics.h"
|
|
|
|
#include "ui/displaytext.h"
|
|
|
|
|
2012-09-15 16:50:51 +00:00
|
|
|
#include <string.h>
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
// Object's constructor.
|
|
|
|
|
|
|
|
CTaskBuild::CTaskBuild(CInstanceManager* iMan, CObject* object)
|
|
|
|
: CTask(iMan, object)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
m_type = OBJECT_DERRICK;
|
|
|
|
m_time = 0.0f;
|
|
|
|
m_soundChannel = -1;
|
|
|
|
|
|
|
|
for ( i=0 ; i<TBMAXLIGHT ; i++ )
|
|
|
|
{
|
|
|
|
m_lightRank[i] = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Object's destructor.
|
|
|
|
|
|
|
|
CTaskBuild::~CTaskBuild()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ( m_soundChannel != -1 )
|
|
|
|
{
|
|
|
|
m_sound->FlushEnvelope(m_soundChannel);
|
|
|
|
m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP);
|
|
|
|
m_soundChannel = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i=0 ; i<TBMAXLIGHT ; i++ )
|
|
|
|
{
|
|
|
|
if ( m_lightRank[i] == -1 ) continue;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_lightMan->DeleteLight(m_lightRank[i]);
|
2012-06-26 20:23:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Creates a building.
|
|
|
|
|
|
|
|
bool CTaskBuild::CreateBuilding(Math::Vector pos, float angle)
|
|
|
|
{
|
|
|
|
m_building = new CObject(m_iMan);
|
|
|
|
if ( !m_building->CreateBuilding(pos, angle, 0.0f, m_type, 0.0f) )
|
|
|
|
{
|
|
|
|
delete m_building;
|
|
|
|
m_building = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_building->UpdateMapping();
|
|
|
|
m_building->SetLock(true); // not yet usable
|
|
|
|
|
|
|
|
if ( m_type == OBJECT_DERRICK ) m_buildingHeight = 35.0f;
|
|
|
|
if ( m_type == OBJECT_FACTORY ) m_buildingHeight = 28.0f;
|
|
|
|
if ( m_type == OBJECT_REPAIR ) m_buildingHeight = 30.0f;
|
|
|
|
if ( m_type == OBJECT_STATION ) m_buildingHeight = 13.0f;
|
|
|
|
if ( m_type == OBJECT_CONVERT ) m_buildingHeight = 20.0f;
|
|
|
|
if ( m_type == OBJECT_TOWER ) m_buildingHeight = 30.0f;
|
|
|
|
if ( m_type == OBJECT_RESEARCH ) m_buildingHeight = 22.0f;
|
|
|
|
if ( m_type == OBJECT_RADAR ) m_buildingHeight = 19.0f;
|
|
|
|
if ( m_type == OBJECT_ENERGY ) m_buildingHeight = 20.0f;
|
|
|
|
if ( m_type == OBJECT_LABO ) m_buildingHeight = 16.0f;
|
|
|
|
if ( m_type == OBJECT_NUCLEAR ) m_buildingHeight = 40.0f;
|
|
|
|
if ( m_type == OBJECT_PARA ) m_buildingHeight = 68.0f;
|
|
|
|
if ( m_type == OBJECT_INFO ) m_buildingHeight = 19.0f;
|
|
|
|
m_buildingHeight *= 0.25f;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
m_buildingPos = m_building->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
m_buildingPos.y -= m_buildingHeight;
|
|
|
|
m_building->SetPosition(0, m_buildingPos);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates lights for the effects.
|
|
|
|
|
|
|
|
void CTaskBuild::CreateLight()
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
Gfx::Light light;
|
|
|
|
Gfx::Color color;
|
|
|
|
Math::Vector center, pos, dir;
|
|
|
|
Math::Point c, p;
|
|
|
|
float angle;
|
|
|
|
int i;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( !m_engine->GetLightMode() ) return;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
center = m_metal->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
angle = 0;
|
|
|
|
for ( i=0 ; i<TBMAXLIGHT ; i++ )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
m_lightRank[i] = m_lightMan->CreateLight();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( m_lightRank[i] == -1 ) continue;
|
|
|
|
|
|
|
|
c.x = center.x;
|
|
|
|
c.y = center.z;
|
|
|
|
p.x = center.x+40.0f;
|
|
|
|
p.y = center.z;
|
|
|
|
p = Math::RotatePoint(c, angle, p);
|
|
|
|
pos.x = p.x;
|
|
|
|
pos.z = p.y;
|
|
|
|
pos.y = center.y+40.0f;
|
|
|
|
dir = center-pos;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
memset(&light, 0, sizeof(light));
|
|
|
|
light.type = Gfx::LIGHT_SPOT;
|
|
|
|
light.diffuse.r = 0.0f;
|
|
|
|
light.diffuse.g = 0.0f;
|
|
|
|
light.diffuse.b = 0.0f; // white (invisible)
|
|
|
|
light.position.x = pos.x;
|
|
|
|
light.position.y = pos.y;
|
|
|
|
light.position.z = pos.z;
|
|
|
|
light.direction.x = dir.x;
|
|
|
|
light.direction.y = dir.y;
|
|
|
|
light.direction.z = dir.z;
|
|
|
|
//TODO Is this value correct
|
|
|
|
light.spotIntensity = 128;
|
|
|
|
light.attenuation0 = 1.0f;
|
|
|
|
light.attenuation1 = 0.0f;
|
|
|
|
light.attenuation2 = 0.0f;
|
|
|
|
//TODO Is this value correct
|
|
|
|
light.spotAngle = 90;
|
|
|
|
m_lightMan->SetLight(m_lightRank[i], light);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
color.r = -1.0f;
|
|
|
|
color.g = -1.0f;
|
|
|
|
color.b = -0.5f; // violet
|
|
|
|
color.a = 0.0f;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_lightMan->SetLightColor(m_lightRank[i], color);
|
|
|
|
m_lightMan->SetLightColorSpeed(m_lightRank[i], 1.0f/((1.0f/m_speed)*0.25f));
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
angle += (Math::PI*2.0f)/TBMAXLIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_bBlack = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Switches the lights from black to white.
|
|
|
|
|
|
|
|
void CTaskBuild::BlackLight()
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
Gfx::Color color;
|
|
|
|
int i;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
for ( i=0 ; i<TBMAXLIGHT ; i++ )
|
|
|
|
{
|
|
|
|
if ( m_lightRank[i] == -1 ) continue;
|
|
|
|
|
|
|
|
color.r = 0.0f;
|
|
|
|
color.g = 0.0f;
|
|
|
|
color.b = 0.0f; // white (invisible)
|
|
|
|
color.a = 0.0f;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_lightMan->SetLightColor(m_lightRank[i], color);
|
|
|
|
m_lightMan->SetLightColorSpeed(m_lightRank[i], 1.0f/((1.0f/m_speed)*0.75f));
|
2012-06-26 20:23:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
m_bBlack = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Management of an event.
|
|
|
|
|
|
|
|
bool CTaskBuild::EventProcess(const Event &event)
|
|
|
|
{
|
|
|
|
Math::Matrix* mat;
|
|
|
|
Math::Vector pos, dir, speed;
|
|
|
|
Math::Point dim;
|
|
|
|
float a, g, cirSpeed, dist, linSpeed;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( m_engine->GetPause() ) return true;
|
|
|
|
if ( event.type != EVENT_FRAME ) return true;
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( m_bError ) return false;
|
|
|
|
|
|
|
|
m_time += event.rTime;
|
|
|
|
|
|
|
|
m_progress += event.rTime*m_speed; // other advance
|
|
|
|
|
|
|
|
if ( m_phase == TBP_TURN ) // preliminary rotation?
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
a = m_object->GetAngleY(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
g = m_angleY;
|
|
|
|
cirSpeed = Math::Direction(a, g)*1.0f;
|
|
|
|
if ( cirSpeed > 1.0f ) cirSpeed = 1.0f;
|
|
|
|
if ( cirSpeed < -1.0f ) cirSpeed = -1.0f;
|
|
|
|
|
|
|
|
m_physics->SetMotorSpeedZ(cirSpeed); // turns left/right
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_MOVE ) // preliminary forward/backward?
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
dist = Math::Distance(m_object->GetPosition(0), m_metal->GetPosition(0));
|
2012-06-26 20:23:05 +00:00
|
|
|
linSpeed = 0.0f;
|
|
|
|
if ( dist > 30.0f ) linSpeed = 1.0f;
|
|
|
|
if ( dist < 30.0f ) linSpeed = -1.0f;
|
|
|
|
m_physics->SetMotorSpeedX(linSpeed); // forward/backward
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_RECEDE ) // terminal back?
|
|
|
|
{
|
|
|
|
m_physics->SetMotorSpeedX(-1.0f); // back
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_TAKE ) // takes gun?
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_PREP ) // prepares?
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_TERM ) // ends?
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !m_bBuild ) // building to build?
|
|
|
|
{
|
|
|
|
m_bBuild = true;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
pos = m_metal->GetPosition(0);
|
|
|
|
a = m_object->GetAngleY(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( !CreateBuilding(pos, a+Math::PI) )
|
|
|
|
{
|
|
|
|
m_metal->SetLock(false); // usable again
|
|
|
|
m_motion->SetAction(-1);
|
|
|
|
m_object->SetObjectParent(14, 0);
|
|
|
|
m_object->SetPosition(14, Math::Vector(-1.5f, 0.3f, -1.35f));
|
|
|
|
m_object->SetAngleZ(14, Math::PI);
|
|
|
|
m_camera->FlushEffect();
|
|
|
|
Abort();
|
|
|
|
m_bError = true;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_displayText->DisplayError(ERR_TOOMANY, m_object->GetPosition(0));
|
2012-06-26 20:23:05 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
CreateLight();
|
|
|
|
}
|
|
|
|
|
|
|
|
pos = m_buildingPos;
|
|
|
|
pos.y += m_buildingHeight*m_progress;
|
|
|
|
m_building->SetPosition(0, pos); // the building rises
|
|
|
|
|
|
|
|
m_building->SetZoom(0, m_progress*0.75f+0.25f);
|
|
|
|
m_metal->SetZoom(0, 1.0f-m_progress);
|
|
|
|
|
|
|
|
a = (2.0f-2.0f*m_progress);
|
|
|
|
if ( a > 1.0f ) a = 1.0f;
|
|
|
|
dir.x = (Math::Rand()-0.5f)*a*0.1f;
|
|
|
|
dir.z = (Math::Rand()-0.5f)*a*0.1f;
|
|
|
|
dir.y = (Math::Rand()-0.5f)*a*0.1f;
|
|
|
|
m_building->SetCirVibration(dir);
|
|
|
|
|
|
|
|
if ( !m_bBlack && m_progress >= 0.25f )
|
|
|
|
{
|
|
|
|
BlackLight();
|
|
|
|
}
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( m_lastParticle+m_engine->ParticleAdapt(0.05f) <= m_time )
|
2012-06-26 20:23:05 +00:00
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
m_lastParticle = m_time;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
pos = m_metal->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
speed.x = (Math::Rand()-0.5f)*20.0f;
|
|
|
|
speed.z = (Math::Rand()-0.5f)*20.0f;
|
|
|
|
speed.y = Math::Rand()*10.0f;
|
|
|
|
dim.x = Math::Rand()*6.0f+4.0f;
|
|
|
|
dim.y = dim.x;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_particle->CreateParticle(pos, speed, dim, Gfx::PARTIFIRE);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
pos = Math::Vector(0.0f, 0.5f, 0.0f);
|
2012-09-11 21:11:34 +00:00
|
|
|
mat = m_object->GetWorldMatrix(14);
|
2012-06-26 20:23:05 +00:00
|
|
|
pos = Transform(*mat, pos);
|
2012-09-11 21:11:34 +00:00
|
|
|
speed = m_metal->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
speed.x += (Math::Rand()-0.5f)*5.0f;
|
|
|
|
speed.z += (Math::Rand()-0.5f)*5.0f;
|
|
|
|
speed -= pos;
|
|
|
|
dim.x = 2.0f;
|
|
|
|
dim.y = dim.x;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_particle->CreateParticle(pos, speed, dim, Gfx::PARTIFIREZ);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
if ( Math::Rand() < 0.3f )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
m_sound->Play(SOUND_BUILD, m_object->GetPosition(0), 0.5f, 1.0f*Math::Rand()*1.5f);
|
2012-06-26 20:23:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Assigns the goal was achieved.
|
|
|
|
|
|
|
|
Error CTaskBuild::Start(ObjectType type)
|
|
|
|
{
|
|
|
|
Math::Vector pos, speed, pv, pm;
|
|
|
|
Error err;
|
|
|
|
float iAngle, oAngle;
|
|
|
|
|
|
|
|
m_type = type;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_lastParticle = 0.0f;
|
2012-06-26 20:23:05 +00:00
|
|
|
m_progress = 0.0f;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
iAngle = m_object->GetAngleY(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
iAngle = Math::NormAngle(iAngle); // 0..2*Math::PI
|
|
|
|
oAngle = iAngle;
|
|
|
|
|
|
|
|
m_bError = true; // operation impossible
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
pos = m_object->GetPosition(0);
|
|
|
|
if ( pos.y < m_water->GetLevel() ) return ERR_BUILD_WATER;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( !m_physics->GetLand() ) return ERR_BUILD_FLY;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
speed = m_physics->GetMotorSpeed();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( speed.x != 0.0f ||
|
|
|
|
speed.z != 0.0f ) return ERR_BUILD_MOTOR;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( m_object->GetFret() != 0 ) return ERR_MANIP_BUSY;
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
m_metal = SearchMetalObject(oAngle, 2.0f, 100.0f, Math::PI*0.25f, err);
|
|
|
|
if ( err == ERR_BUILD_METALNEAR && m_metal != 0 )
|
|
|
|
{
|
|
|
|
err = FlatFloor();
|
|
|
|
if ( err != ERR_OK ) return err;
|
|
|
|
return ERR_BUILD_METALNEAR;
|
|
|
|
}
|
|
|
|
if ( err != ERR_OK ) return err;
|
|
|
|
|
|
|
|
err = FlatFloor();
|
|
|
|
if ( err != ERR_OK ) return err;
|
|
|
|
|
|
|
|
m_metal->SetLock(true); // not usable
|
|
|
|
m_camera->StartCentering(m_object, Math::PI*0.15f, 99.9f, 0.0f, 1.0f);
|
2012-06-26 21:01:17 +00:00
|
|
|
|
2012-06-26 20:23:05 +00:00
|
|
|
m_phase = TBP_TURN; // rotation necessary preliminary
|
|
|
|
m_angleY = oAngle; // angle was reached
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
pv = m_object->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
pv.y += 8.3f;
|
2012-09-11 21:11:34 +00:00
|
|
|
pm = m_metal->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
m_angleZ = Math::RotateAngle(Math::DistanceProjected(pv, pm), fabs(pv.y-pm.y));
|
|
|
|
|
|
|
|
m_physics->SetFreeze(true); // it does not move
|
|
|
|
|
|
|
|
m_bBuild = false; // not yet built
|
|
|
|
m_bError = false; // ok
|
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Indicates whether the action is finished.
|
|
|
|
|
|
|
|
Error CTaskBuild::IsEnded()
|
|
|
|
{
|
|
|
|
CAuto* automat;
|
|
|
|
float angle, dist, time;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( m_engine->GetPause() ) return ERR_CONTINUE;
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( m_bError ) return ERR_STOP;
|
|
|
|
|
|
|
|
if ( m_phase == TBP_TURN ) // preliminary rotation?
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
angle = m_object->GetAngleY(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
angle = Math::NormAngle(angle); // 0..2*Math::PI
|
|
|
|
|
|
|
|
if ( Math::TestAngle(angle, m_angleY-Math::PI*0.01f, m_angleY+Math::PI*0.01f) )
|
|
|
|
{
|
|
|
|
m_physics->SetMotorSpeedZ(0.0f);
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
dist = Math::Distance(m_object->GetPosition(0), m_metal->GetPosition(0));
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( dist > 30.0f )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
time = m_physics->GetLinTimeLength(dist-30.0f, 1.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
m_speed = 1.0f/time;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
time = m_physics->GetLinTimeLength(30.0f-dist, -1.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
m_speed = 1.0f/time;
|
|
|
|
}
|
|
|
|
m_phase = TBP_MOVE;
|
|
|
|
m_progress = 0.0f;
|
|
|
|
}
|
|
|
|
return ERR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_MOVE ) // preliminary forward/backward?
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
dist = Math::Distance(m_object->GetPosition(0), m_metal->GetPosition(0));
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
if ( dist >= 25.0f && dist <= 35.0f )
|
|
|
|
{
|
|
|
|
m_physics->SetMotorSpeedX(0.0f);
|
|
|
|
m_motion->SetAction(MHS_GUN); // takes gun
|
|
|
|
|
|
|
|
m_phase = TBP_TAKE;
|
|
|
|
m_speed = 1.0f/1.0f;
|
|
|
|
m_progress = 0.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( m_progress > 1.0f ) // timeout?
|
|
|
|
{
|
|
|
|
m_metal->SetLock(false); // usable again
|
|
|
|
if ( dist < 30.0f ) return ERR_BUILD_METALNEAR;
|
|
|
|
else return ERR_BUILD_METALAWAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ERR_CONTINUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_TAKE ) // takes gun
|
|
|
|
{
|
|
|
|
if ( m_progress < 1.0f ) return ERR_CONTINUE;
|
|
|
|
|
|
|
|
m_motion->SetAction(MHS_FIRE); // shooting position
|
|
|
|
m_object->SetObjectParent(14, 4);
|
|
|
|
m_object->SetPosition(14, Math::Vector(0.6f, 0.1f, 0.3f));
|
|
|
|
m_object->SetAngleZ(14, 0.0f);
|
|
|
|
|
|
|
|
m_phase = TBP_PREP;
|
|
|
|
m_speed = 1.0f/1.0f;
|
|
|
|
m_progress = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_PREP ) // prepares?
|
|
|
|
{
|
|
|
|
if ( m_progress < 1.0f ) return ERR_CONTINUE;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
m_soundChannel = m_sound->Play(SOUND_TREMBLE, m_object->GetPosition(0), 0.0f, 1.0f, true);
|
2012-06-26 20:23:05 +00:00
|
|
|
m_sound->AddEnvelope(m_soundChannel, 0.7f, 1.0f, 1.0f, SOPER_CONTINUE);
|
|
|
|
m_sound->AddEnvelope(m_soundChannel, 0.7f, 1.5f, 7.0f, SOPER_CONTINUE);
|
|
|
|
m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.5f, 2.0f, SOPER_STOP);
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
m_camera->StartEffect(Gfx::CAM_EFFECT_VIBRATION, m_metal->GetPosition(0), 1.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
m_phase = TBP_BUILD;
|
|
|
|
m_speed = 1.0f/10.f; // duration of 10s
|
|
|
|
m_progress = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_BUILD ) // construction?
|
|
|
|
{
|
|
|
|
if ( m_progress < 1.0f ) return ERR_CONTINUE;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
DeleteMark(m_metal->GetPosition(0), 20.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
|
|
|
m_metal->DeleteObject(); // removes the metal
|
|
|
|
delete m_metal;
|
|
|
|
m_metal = 0;
|
|
|
|
|
|
|
|
m_building->SetZoom(0, 1.0f);
|
|
|
|
m_building->SetCirVibration(Math::Vector(0.0f, 0.0f, 0.0f));
|
|
|
|
m_building->SetLock(false); // building usable
|
|
|
|
m_main->CreateShortcuts();
|
|
|
|
m_displayText->DisplayError(INFO_BUILD, m_buildingPos, 10.0f, 50.0f);
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
automat = m_building->GetAuto();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( automat != 0 )
|
|
|
|
{
|
|
|
|
automat->Init();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_motion->SetAction(MHS_GUN); // hands gun
|
|
|
|
m_phase = TBP_TERM;
|
|
|
|
m_speed = 1.0f/1.0f;
|
|
|
|
m_progress = 0.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_TERM ) // rotation terminale ?
|
|
|
|
{
|
|
|
|
if ( m_progress < 1.0f ) return ERR_CONTINUE;
|
|
|
|
|
|
|
|
m_motion->SetAction(-1);
|
|
|
|
m_object->SetObjectParent(14, 0);
|
|
|
|
m_object->SetPosition(14, Math::Vector(-1.5f, 0.3f, -1.35f));
|
|
|
|
m_object->SetAngleZ(14, Math::PI);
|
|
|
|
|
|
|
|
if ( m_type == OBJECT_FACTORY ||
|
|
|
|
m_type == OBJECT_RESEARCH ||
|
|
|
|
m_type == OBJECT_NUCLEAR )
|
|
|
|
{
|
|
|
|
|
|
|
|
m_phase = TBP_RECEDE;
|
|
|
|
m_speed = 1.0f/1.5f;
|
|
|
|
m_progress = 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_phase == TBP_RECEDE ) // back?
|
|
|
|
{
|
|
|
|
if ( m_progress < 1.0f ) return ERR_CONTINUE;
|
|
|
|
|
|
|
|
m_physics->SetMotorSpeedX(0.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
Abort();
|
|
|
|
return ERR_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Suddenly ends the current action.
|
|
|
|
|
|
|
|
bool CTaskBuild::Abort()
|
|
|
|
{
|
|
|
|
if ( m_soundChannel != -1 )
|
|
|
|
{
|
|
|
|
m_sound->FlushEnvelope(m_soundChannel);
|
|
|
|
m_sound->AddEnvelope(m_soundChannel, 0.0f, 1.0f, 1.0f, SOPER_STOP);
|
|
|
|
m_soundChannel = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_camera->StopCentering(m_object, 2.0f);
|
|
|
|
m_physics->SetFreeze(false); // is moving again
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Checks whether the terrain is fairly flat
|
|
|
|
// and if there is not too close to another object.
|
|
|
|
|
|
|
|
Error CTaskBuild::FlatFloor()
|
|
|
|
{
|
|
|
|
CObject *pObj;
|
|
|
|
ObjectType type;
|
|
|
|
Math::Vector center, pos, oPos, bPos;
|
|
|
|
Math::Point c, p;
|
|
|
|
float radius, max, oRadius, bRadius, angle, dist;
|
|
|
|
int i, j;
|
|
|
|
bool bLittleFlat, bBase;
|
|
|
|
|
|
|
|
radius = 0.0f;
|
|
|
|
if ( m_type == OBJECT_DERRICK ) radius = 5.0f;
|
|
|
|
if ( m_type == OBJECT_FACTORY ) radius = 15.0f;
|
|
|
|
if ( m_type == OBJECT_REPAIR ) radius = 12.0f;
|
|
|
|
if ( m_type == OBJECT_STATION ) radius = 12.0f;
|
|
|
|
if ( m_type == OBJECT_CONVERT ) radius = 12.0f;
|
|
|
|
if ( m_type == OBJECT_TOWER ) radius = 7.0f;
|
|
|
|
if ( m_type == OBJECT_RESEARCH ) radius = 10.0f;
|
|
|
|
if ( m_type == OBJECT_RADAR ) radius = 5.0f;
|
|
|
|
if ( m_type == OBJECT_ENERGY ) radius = 8.0f;
|
|
|
|
if ( m_type == OBJECT_LABO ) radius = 12.0f;
|
|
|
|
if ( m_type == OBJECT_NUCLEAR ) radius = 20.0f;
|
|
|
|
if ( m_type == OBJECT_PARA ) radius = 20.0f;
|
|
|
|
if ( m_type == OBJECT_INFO ) radius = 5.0f;
|
|
|
|
if ( radius == 0.0f ) return ERR_GENERIC;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
center = m_metal->GetPosition(0);
|
|
|
|
angle = m_terrain->GetFineSlope(center);
|
|
|
|
bLittleFlat = ( angle < Gfx::TERRAIN_FLATLIMIT);
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
max = m_terrain->GetFlatZoneRadius(center, radius);
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( max < radius ) // area too small?
|
|
|
|
{
|
|
|
|
if ( bLittleFlat )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
m_main->SetShowLimit(1, Gfx::PARTILIMIT3, m_metal, center, max, 10.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
}
|
|
|
|
return bLittleFlat?ERR_BUILD_FLATLIT:ERR_BUILD_FLAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
max = 100000.0f;
|
|
|
|
bBase = false;
|
|
|
|
for ( i=0 ; i<1000000 ; i++ )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
pObj = static_cast<CObject*>(m_iMan->SearchInstance(CLASS_OBJECT, i));
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( pObj == 0 ) break;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( !pObj->GetActif() ) continue; // inactive?
|
|
|
|
if ( pObj->GetTruck() != 0 ) continue; // object transported?
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( pObj == m_metal ) continue;
|
|
|
|
if ( pObj == m_object ) continue;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
type = pObj->GetType();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( type == OBJECT_BASE )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
oPos = pObj->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
dist = Math::Distance(center, oPos)-80.0f;
|
|
|
|
if ( dist < max )
|
|
|
|
{
|
|
|
|
max = dist;
|
|
|
|
bPos = oPos;
|
|
|
|
bRadius = oRadius;
|
|
|
|
bBase = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
j = 0;
|
|
|
|
while ( pObj->GetCrashSphere(j++, oPos, oRadius) )
|
|
|
|
{
|
|
|
|
dist = Math::Distance(center, oPos)-oRadius;
|
|
|
|
if ( dist < max )
|
|
|
|
{
|
|
|
|
max = dist;
|
|
|
|
bPos = oPos;
|
|
|
|
bRadius = oRadius;
|
|
|
|
bBase = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( max < radius )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
m_main->SetShowLimit(1, Gfx::PARTILIMIT2, m_metal, center, max, 10.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( bRadius < 2.0f ) bRadius = 2.0f;
|
2012-09-11 21:11:34 +00:00
|
|
|
m_main->SetShowLimit(2, Gfx::PARTILIMIT3, m_metal, bPos, bRadius, 10.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
return bBase?ERR_BUILD_BASE:ERR_BUILD_BUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
max = 100000.0f;
|
|
|
|
for ( i=0 ; i<1000000 ; i++ )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
pObj = static_cast<CObject*>(m_iMan->SearchInstance(CLASS_OBJECT, i));
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( pObj == 0 ) break;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( !pObj->GetActif() ) continue; // inactive?
|
|
|
|
if ( pObj->GetTruck() != 0 ) continue; // object transported?
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( pObj == m_metal ) continue;
|
|
|
|
if ( pObj == m_object ) continue;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
type = pObj->GetType();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( type == OBJECT_DERRICK ||
|
|
|
|
type == OBJECT_FACTORY ||
|
|
|
|
type == OBJECT_STATION ||
|
|
|
|
type == OBJECT_CONVERT ||
|
|
|
|
type == OBJECT_REPAIR ||
|
|
|
|
type == OBJECT_TOWER ||
|
|
|
|
type == OBJECT_RESEARCH ||
|
|
|
|
type == OBJECT_RADAR ||
|
|
|
|
type == OBJECT_ENERGY ||
|
|
|
|
type == OBJECT_LABO ||
|
|
|
|
type == OBJECT_NUCLEAR ||
|
|
|
|
type == OBJECT_START ||
|
|
|
|
type == OBJECT_END ||
|
|
|
|
type == OBJECT_INFO ||
|
|
|
|
type == OBJECT_PARA ||
|
|
|
|
type == OBJECT_SAFE ||
|
|
|
|
type == OBJECT_HUSTON ) // building?
|
|
|
|
{
|
|
|
|
j = 0;
|
|
|
|
while ( pObj->GetCrashSphere(j++, oPos, oRadius) )
|
|
|
|
{
|
|
|
|
dist = Math::Distance(center, oPos)-oRadius;
|
|
|
|
if ( dist < max )
|
|
|
|
{
|
|
|
|
max = dist;
|
|
|
|
bPos = oPos;
|
|
|
|
bRadius = oRadius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( max-BUILDMARGIN < radius )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
m_main->SetShowLimit(1, Gfx::PARTILIMIT2, m_metal, center, max-BUILDMARGIN, 10.0f);
|
|
|
|
m_main->SetShowLimit(2, Gfx::PARTILIMIT3, m_metal, bPos, bRadius+BUILDMARGIN, 10.0f);
|
2012-06-26 20:23:05 +00:00
|
|
|
return bBase?ERR_BUILD_BASE:ERR_BUILD_NARROW;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ERR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Seeks the nearest metal object.
|
|
|
|
|
|
|
|
CObject* CTaskBuild::SearchMetalObject(float &angle, float dMin, float dMax,
|
|
|
|
float aLimit, Error &err)
|
|
|
|
{
|
|
|
|
CObject *pObj, *pBest;
|
|
|
|
Math::Vector iPos, oPos;
|
|
|
|
ObjectType type;
|
|
|
|
float min, iAngle, a, aa, aBest, distance, magic;
|
|
|
|
int i;
|
|
|
|
bool bMetal;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
iPos = m_object->GetPosition(0);
|
|
|
|
iAngle = m_object->GetAngleY(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
iAngle = Math::NormAngle(iAngle); // 0..2*Math::PI
|
|
|
|
|
|
|
|
min = 1000000.0f;
|
|
|
|
pBest = 0;
|
|
|
|
bMetal = false;
|
|
|
|
for ( i=0 ; i<1000000 ; i++ )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
pObj = static_cast<CObject*>(m_iMan->SearchInstance(CLASS_OBJECT, i));
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( pObj == 0 ) break;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
if ( !pObj->GetActif() ) continue; // objet inactive?
|
|
|
|
if ( pObj->GetTruck() != 0 ) continue; // object transported?
|
2012-06-26 20:23:05 +00:00
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
type = pObj->GetType();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( type != OBJECT_METAL ) continue;
|
|
|
|
|
|
|
|
bMetal = true; // metal exists
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
oPos = pObj->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
distance = Math::Distance(oPos, iPos);
|
|
|
|
a = Math::RotateAngle(oPos.x-iPos.x, iPos.z-oPos.z); // CW!
|
|
|
|
|
|
|
|
if ( distance > dMax ) continue;
|
|
|
|
if ( !Math::TestAngle(a, iAngle-aLimit, iAngle+aLimit) ) continue;
|
|
|
|
|
|
|
|
if ( distance < dMin )
|
|
|
|
{
|
|
|
|
err = ERR_BUILD_METALNEAR; // too close
|
|
|
|
return pObj;
|
|
|
|
}
|
|
|
|
|
|
|
|
aa = fabs(a-iAngle);
|
|
|
|
if ( aa > Math::PI ) aa = Math::PI*2.0f-aa;
|
|
|
|
magic = distance*aa;
|
|
|
|
|
|
|
|
if ( magic < min )
|
|
|
|
{
|
|
|
|
min = magic;
|
|
|
|
aBest = a;
|
|
|
|
pBest = pObj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( pBest == 0 )
|
|
|
|
{
|
|
|
|
if ( bMetal ) err = ERR_BUILD_METALAWAY; // too far
|
|
|
|
else err = ERR_BUILD_METALINEX; // non-existent
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
angle = aBest;
|
|
|
|
err = ERR_OK;
|
|
|
|
}
|
|
|
|
return pBest;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroys all the close marks.
|
|
|
|
|
|
|
|
void CTaskBuild::DeleteMark(Math::Vector pos, float radius)
|
|
|
|
{
|
|
|
|
CObject* pObj;
|
|
|
|
Math::Vector oPos;
|
|
|
|
ObjectType type;
|
|
|
|
float distance;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for ( i=0 ; i<1000000 ; i++ )
|
|
|
|
{
|
2012-09-11 21:11:34 +00:00
|
|
|
pObj = static_cast<CObject*>(m_iMan->SearchInstance(CLASS_OBJECT, i));
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( pObj == 0 ) break;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
type = pObj->GetType();
|
2012-06-26 20:23:05 +00:00
|
|
|
if ( type != OBJECT_MARKSTONE &&
|
|
|
|
type != OBJECT_MARKURANIUM &&
|
|
|
|
type != OBJECT_MARKKEYa &&
|
|
|
|
type != OBJECT_MARKKEYb &&
|
|
|
|
type != OBJECT_MARKKEYc &&
|
|
|
|
type != OBJECT_MARKKEYd &&
|
|
|
|
type != OBJECT_MARKPOWER ) continue;
|
|
|
|
|
2012-09-11 21:11:34 +00:00
|
|
|
oPos = pObj->GetPosition(0);
|
2012-06-26 20:23:05 +00:00
|
|
|
distance = Math::Distance(oPos, pos);
|
|
|
|
if ( distance <= radius )
|
|
|
|
{
|
|
|
|
pObj->DeleteObject(); // removes the mark
|
|
|
|
delete pObj;
|
|
|
|
i --;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|