|
|
|
@ -3,18 +3,49 @@
|
|
|
|
|
#include Library_Structure
|
|
|
|
|
#include Library_PowerConsumer
|
|
|
|
|
|
|
|
|
|
local elevator;
|
|
|
|
|
local slave, partner, no_connection;
|
|
|
|
|
static const ElevatorCase_move_speed = 20;
|
|
|
|
|
static const ElevatorCase_up = -1;
|
|
|
|
|
static const ElevatorCase_down = 1;
|
|
|
|
|
|
|
|
|
|
// if you change the vertices in the defcore make sure to adjust this
|
|
|
|
|
static const ElevatorCase_additional_vertex_index_begin = 4;
|
|
|
|
|
static const ElevatorCase_normal_vertex_index_begin = 0;
|
|
|
|
|
static const ElevatorCase_additional_vertex_count = 4;
|
|
|
|
|
|
|
|
|
|
// Meshes
|
|
|
|
|
local front, back;
|
|
|
|
|
|
|
|
|
|
local elevator;
|
|
|
|
|
local partner, partner_was_synced, is_master;
|
|
|
|
|
|
|
|
|
|
// can be changed from outside
|
|
|
|
|
// standard: ElevatorCase_move_speed, 2 * ElevatorCase_move_speed
|
|
|
|
|
local case_speed; // when user controls case
|
|
|
|
|
local case_speed_automatic; // when case goes to user
|
|
|
|
|
|
|
|
|
|
/* Callbacks */
|
|
|
|
|
|
|
|
|
|
func GetDrillSpeed()
|
|
|
|
|
{
|
|
|
|
|
return ElevatorCase_move_speed / 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Initialize()
|
|
|
|
|
{
|
|
|
|
|
AddTimer("Movement", 1);
|
|
|
|
|
AddEffect("CheckAutoMoveTo", this, 1, 30, this);
|
|
|
|
|
AddEffect("ElevatorUpperLimitCheck", this, 1, 1, this);
|
|
|
|
|
AddEffect("FetchVehicles", this, 1, 10, this);
|
|
|
|
|
|
|
|
|
|
case_speed = ElevatorCase_move_speed;
|
|
|
|
|
case_speed_automatic = 2 * case_speed;
|
|
|
|
|
|
|
|
|
|
partner_was_synced = false;
|
|
|
|
|
|
|
|
|
|
front = CreateObject(Elevator_Case_Front, 0,13, GetOwner());
|
|
|
|
|
back = CreateObject(Elevator_Case_Back, 0,13, GetOwner());
|
|
|
|
|
|
|
|
|
|
front->SetAction("Attach", this);
|
|
|
|
|
back->SetAction("Attach", this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called by the elevator
|
|
|
|
@ -22,225 +53,135 @@ func Connect(object connect_to)
|
|
|
|
|
{
|
|
|
|
|
elevator = connect_to;
|
|
|
|
|
SetComDir(COMD_None);
|
|
|
|
|
SetAction("Drive");
|
|
|
|
|
SetAction("DriveIdle");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// request power
|
|
|
|
|
MakePowerConsumer(50);
|
|
|
|
|
func Destruction()
|
|
|
|
|
{
|
|
|
|
|
if(partner)
|
|
|
|
|
partner->LoseConnection();
|
|
|
|
|
if(elevator)
|
|
|
|
|
elevator->LostCase();
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < 3; ++i)
|
|
|
|
|
{
|
|
|
|
|
var wood = CreateObject(Wood, 0, 0, NO_OWNER);
|
|
|
|
|
wood->Incinerate();
|
|
|
|
|
wood->SetXDir(RandomX(-10, 10));
|
|
|
|
|
wood->SetYDir(RandomX(-2, 0));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func LostElevator()
|
|
|
|
|
{
|
|
|
|
|
// die x.x
|
|
|
|
|
RemoveObject();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called by the elevator in case a partner elevator was constructed
|
|
|
|
|
func StartConnection(object elevator)
|
|
|
|
|
func StartConnection(object case)
|
|
|
|
|
{
|
|
|
|
|
partner = elevator.case;
|
|
|
|
|
partner.partner = this;
|
|
|
|
|
// We are not yet connected
|
|
|
|
|
no_connection = true;
|
|
|
|
|
partner.no_connection = true;
|
|
|
|
|
partner = case;
|
|
|
|
|
partner_was_synced = false;
|
|
|
|
|
if(case.partner != this)
|
|
|
|
|
{
|
|
|
|
|
case->StartConnection(this);
|
|
|
|
|
is_master = true;
|
|
|
|
|
AddEffect("TryToSync", this, 1, 1, this);
|
|
|
|
|
}
|
|
|
|
|
else // is slave
|
|
|
|
|
{
|
|
|
|
|
is_master = false;
|
|
|
|
|
MoveTo(nil, 0, case);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called when the other elevator is destroyed or moved
|
|
|
|
|
func LoseConnection()
|
|
|
|
|
{
|
|
|
|
|
partner = nil;
|
|
|
|
|
no_connection = false;
|
|
|
|
|
slave = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Energy */
|
|
|
|
|
|
|
|
|
|
// for the position
|
|
|
|
|
func GetActualPowerConsumer()
|
|
|
|
|
{
|
|
|
|
|
return elevator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the lift may not need power when not used
|
|
|
|
|
func QueryWaivePowerRequest()
|
|
|
|
|
{
|
|
|
|
|
if(!movement && !drill)
|
|
|
|
|
return 10;
|
|
|
|
|
else return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func OnNotEnoughPower()
|
|
|
|
|
{
|
|
|
|
|
// Stop movement if moving
|
|
|
|
|
is_master = nil;
|
|
|
|
|
partner_was_synced = false;
|
|
|
|
|
if(GetEffect("TryToSync", this))
|
|
|
|
|
RemoveEffect("TryToSync", this);
|
|
|
|
|
SetPartnerVertices(0, 0);
|
|
|
|
|
SetActionData(0);
|
|
|
|
|
|
|
|
|
|
Halt();
|
|
|
|
|
movement = 0;
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
if (drill)
|
|
|
|
|
{
|
|
|
|
|
SetAction("Drive");
|
|
|
|
|
Sound("ElevatorDrilling", nil, nil, nil, -1);
|
|
|
|
|
drill = false;
|
|
|
|
|
}
|
|
|
|
|
return _inherited(...);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func OnEnoughPower()
|
|
|
|
|
// slave loses attach to master?
|
|
|
|
|
func AttachTargetLost()
|
|
|
|
|
{
|
|
|
|
|
return _inherited(...);
|
|
|
|
|
LoseConnection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Movement behaviour */
|
|
|
|
|
|
|
|
|
|
local move_to, // Y-coordinate to move to on its own
|
|
|
|
|
move_to_delay, // Delay before moving
|
|
|
|
|
move_to_target; // Target to move to
|
|
|
|
|
|
|
|
|
|
func Movement()
|
|
|
|
|
func ExecuteSync()
|
|
|
|
|
{
|
|
|
|
|
// Move back and front
|
|
|
|
|
front->SetPosition(GetX(), GetY()-3);
|
|
|
|
|
back->SetPosition(GetX(), GetY()-5);
|
|
|
|
|
if(!is_master) FatalError("ExecuteSync() called on slave elevator case!");
|
|
|
|
|
partner_was_synced = true;
|
|
|
|
|
partner.partner_was_synced = true;
|
|
|
|
|
ForceSync();
|
|
|
|
|
|
|
|
|
|
SetPartnerVertices(partner->GetX() - GetX(), partner->GetY() - GetY());
|
|
|
|
|
|
|
|
|
|
// reset power usage
|
|
|
|
|
UnmakePowerConsumer();
|
|
|
|
|
partner->UnmakePowerConsumer();
|
|
|
|
|
|
|
|
|
|
// can now attach partner on one of the new vertices
|
|
|
|
|
partner->SetAction("Attach", this);
|
|
|
|
|
var vertex_data = (ElevatorCase_normal_vertex_index_begin << 8) + ElevatorCase_additional_vertex_index_begin;
|
|
|
|
|
partner->SetActionData(vertex_data);
|
|
|
|
|
|
|
|
|
|
Sound("Click");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No elevator?!
|
|
|
|
|
if (!elevator)
|
|
|
|
|
// sets additional vertices to partner's position
|
|
|
|
|
func SetPartnerVertices(int off_x, int off_y)
|
|
|
|
|
{
|
|
|
|
|
var update_mode = 2; // force immediate update and store information
|
|
|
|
|
|
|
|
|
|
for(var i = 0; i < ElevatorCase_additional_vertex_count; ++i)
|
|
|
|
|
{
|
|
|
|
|
// Elevator crash, oh the horror!
|
|
|
|
|
if (!ActIdle()) SetAction("Idle");
|
|
|
|
|
return;
|
|
|
|
|
SetVertex(ElevatorCase_additional_vertex_index_begin + i, VTX_X, GetVertex(ElevatorCase_normal_vertex_index_begin + i, VTX_X) + off_x, update_mode);
|
|
|
|
|
SetVertex(ElevatorCase_additional_vertex_index_begin + i, VTX_Y, GetVertex(ElevatorCase_normal_vertex_index_begin + i, VTX_Y) + off_y, update_mode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch vehicles
|
|
|
|
|
for (var vehicle in FindObjects(Find_InRect(-5, -5, 10, 10), Find_Category(C4D_Vehicle), Find_NoContainer(), Find_Func("FitsInElevator")))
|
|
|
|
|
func IsMaster() { return partner && is_master && partner_was_synced; }
|
|
|
|
|
func IsSlave() { return partner && !is_master && partner_was_synced; }
|
|
|
|
|
|
|
|
|
|
func FxTryToSyncTimer(object target, effect, int time)
|
|
|
|
|
{
|
|
|
|
|
var diff = Abs(partner->GetY() - GetY());
|
|
|
|
|
if(diff > 5) return 1;
|
|
|
|
|
ExecuteSync();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FxCheckAutoMoveToTimer(object target, effect, int time)
|
|
|
|
|
{
|
|
|
|
|
if(!elevator) return -1;
|
|
|
|
|
if(IsSlave()) return 1;
|
|
|
|
|
if(!CheckIdle()) return 1;
|
|
|
|
|
if(GetEffect("MoveTo", this)) return 1;
|
|
|
|
|
|
|
|
|
|
// look for Clonks at shaft
|
|
|
|
|
var additional = 20;
|
|
|
|
|
var x = GetX() - additional;
|
|
|
|
|
var w = GetX() + additional;
|
|
|
|
|
var y = elevator->GetY();
|
|
|
|
|
var h = LandscapeHeight();
|
|
|
|
|
|
|
|
|
|
if(IsMaster())
|
|
|
|
|
{
|
|
|
|
|
if (GetEffect("ElevatorControl", vehicle)) continue;
|
|
|
|
|
|
|
|
|
|
vehicle->SetPosition(GetX(), GetY() + 12 - vehicle->GetObjHeight()/2 );
|
|
|
|
|
vehicle->SetSpeed();
|
|
|
|
|
vehicle->SetR();
|
|
|
|
|
AddEffect("ElevatorControl", vehicle, 30, 5, vehicle, nil, this);
|
|
|
|
|
x = Min(x, partner->GetX() - additional);
|
|
|
|
|
w = Max(w, partner->GetX() + additional);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// I'm a slave
|
|
|
|
|
if (slave) return elevator->CheckSlavery();
|
|
|
|
|
|
|
|
|
|
// Check for partner connection
|
|
|
|
|
if (partner && no_connection)
|
|
|
|
|
if (Inside(partner->GetY(), GetY()-1, GetY()+1))
|
|
|
|
|
{
|
|
|
|
|
no_connection = false;
|
|
|
|
|
partner.no_connection = false;
|
|
|
|
|
partner->SetPosition(partner->GetX(), GetY());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Slave moved away?
|
|
|
|
|
if (HasReadyPartner())
|
|
|
|
|
if (partner->GetY() != GetY() && !drill) // This does crazy stuff when drilling
|
|
|
|
|
partner->SetPosition(partner->GetX(), GetY());
|
|
|
|
|
|
|
|
|
|
// no power?
|
|
|
|
|
if (!CurrentlyHasPower())
|
|
|
|
|
return;
|
|
|
|
|
// Slave has no power?
|
|
|
|
|
if (HasReadyPartner())
|
|
|
|
|
if (!partner->CurrentlyHasPower())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Start or stop drilling
|
|
|
|
|
if (drill && GetAction() == "Drive")
|
|
|
|
|
{
|
|
|
|
|
SetAction("Drill");
|
|
|
|
|
Sound("ElevatorDrilling", nil, nil, nil, 1);
|
|
|
|
|
if (HasReadyPartner())
|
|
|
|
|
partner->SetAction("Drill");
|
|
|
|
|
}
|
|
|
|
|
if (!drill && GetAction() == "Drill")
|
|
|
|
|
{
|
|
|
|
|
SetAction("Drive");
|
|
|
|
|
Sound("ElevatorDrilling", nil, nil, nil, -1);
|
|
|
|
|
if (HasReadyPartner())
|
|
|
|
|
partner->SetAction("Drive");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stop if at upmost position
|
|
|
|
|
if (GetY() - 20 <= elevator->GetY() && movement < 0)
|
|
|
|
|
{
|
|
|
|
|
if (GetYDir() < 0) Halt();
|
|
|
|
|
movement = 0;
|
|
|
|
|
SetPosition(GetX(), elevator->GetY() + 20);
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
if (HasReadyPartner()) partner->SetPosition(partner->GetX(), GetY());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move or stop
|
|
|
|
|
if (movement && GetYDir() != this.Speed * movement)
|
|
|
|
|
{
|
|
|
|
|
if (movement < 0 && GetContact(-1) & CNAT_Top)
|
|
|
|
|
{
|
|
|
|
|
movement = 0;
|
|
|
|
|
} else if (movement > 0 && GetContact(-1) & CNAT_Bottom)
|
|
|
|
|
{
|
|
|
|
|
movement = 0;
|
|
|
|
|
} else
|
|
|
|
|
{
|
|
|
|
|
if (Abs(GetYDir()) == 1) elevator->StartEngine();
|
|
|
|
|
SetYDir(GetYDir() + movement);
|
|
|
|
|
if (HasReadyPartner()) partner->SetYDir(GetYDir());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!movement && !move_to && GetYDir())
|
|
|
|
|
{
|
|
|
|
|
Halt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Idle?
|
|
|
|
|
if (!CheckIdle()) return;
|
|
|
|
|
|
|
|
|
|
// Move-to job?
|
|
|
|
|
if (move_to)
|
|
|
|
|
{
|
|
|
|
|
if (move_to_delay)
|
|
|
|
|
{
|
|
|
|
|
move_to_delay--;
|
|
|
|
|
if (move_to_target)
|
|
|
|
|
{
|
|
|
|
|
if (HasReadyPartner())
|
|
|
|
|
{
|
|
|
|
|
if (move_to_target->GetComDir() != COMD_Stop)
|
|
|
|
|
return ClearMoveTo();
|
|
|
|
|
if (AbsX(GetX() - move_to_target->GetX()) > 20)
|
|
|
|
|
if (AbsX(partner->GetX() - move_to_target->GetX()) > 20)
|
|
|
|
|
return ClearMoveTo();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (move_to_target->GetComDir() != COMD_Stop || AbsX(GetX() - move_to_target->GetX()) > 20)
|
|
|
|
|
return ClearMoveTo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!move_to_delay) move_to_target = nil;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (Inside(move_to, GetY()-2, GetY()+2))
|
|
|
|
|
{
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
Halt();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (move_to < GetY() && GetYDir() > -this.RushSpeed)
|
|
|
|
|
SetYDir(GetYDir() - 1);
|
|
|
|
|
if (move_to > GetY() && GetYDir() < this.RushSpeed)
|
|
|
|
|
SetYDir(GetYDir() + 1);
|
|
|
|
|
if (Abs(GetYDir()) == 1) elevator->StartEngine();
|
|
|
|
|
if (HasReadyPartner()) partner->SetYDir(GetYDir());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Search for waiting clonks
|
|
|
|
|
var clonk, best;
|
|
|
|
|
var in_rect = Find_InRect(-20, AbsY(elevator->GetY()), 40, LandscapeHeight() - elevator->GetY());
|
|
|
|
|
if (HasReadyPartner())
|
|
|
|
|
{
|
|
|
|
|
if (partner->GetX() < GetX())
|
|
|
|
|
in_rect = Find_InRect(-44, AbsY(elevator->GetY()), 56, LandscapeHeight() - elevator->GetY());
|
|
|
|
|
else
|
|
|
|
|
in_rect = Find_InRect(-20, AbsY(elevator->GetY()), 56, LandscapeHeight() - elevator->GetY());
|
|
|
|
|
}
|
|
|
|
|
for (clonk in FindObjects(in_rect, Find_OCF(OCF_CrewMember), Find_OCF(OCF_Alive), Find_NoContainer(), Find_Allied(GetOwner()), Sort_Distance(), Sort_Reverse()))
|
|
|
|
|
|
|
|
|
|
for (clonk in FindObjects(Find_InRect(x - GetX(), y - GetY(), w - x, h - y), Find_OCF(OCF_CrewMember), Find_OCF(OCF_Alive), Find_NoContainer(), Find_Allied(GetOwner()), Sort_Distance(), Sort_Reverse()))
|
|
|
|
|
{
|
|
|
|
|
var proc = clonk.Action.Procedure;
|
|
|
|
|
if (clonk->GetComDir() != COMD_Stop && !((proc == "SWIM") && Inside(clonk->GetXDir(), -5, 5)))
|
|
|
|
@ -252,7 +193,11 @@ func Movement()
|
|
|
|
|
if (clonk->GetY() > GetY() + 7)
|
|
|
|
|
if (!PathFree(GetX(), GetY() + 16, GetX(), clonk->GetY()))
|
|
|
|
|
continue;
|
|
|
|
|
if (clonk->GetY() > GetY() && GetContact(-1) & CNAT_Bottom) continue;
|
|
|
|
|
if ((clonk->GetY() > GetY()) && GetContact(-1, CNAT_Bottom)) continue;
|
|
|
|
|
|
|
|
|
|
// do not move to very close Clonks
|
|
|
|
|
if(Abs(GetY() - clonk->GetY()) < 5) continue;
|
|
|
|
|
|
|
|
|
|
// Priority rules: Cursor is better than no cursor, nearer is better than farer (Sort_Distance() & Sort_Reverse() do this)
|
|
|
|
|
// So unlike in CR's elevator, no distance check has to be done because later cycles are always nearer clonks
|
|
|
|
|
if (!best) best = clonk;
|
|
|
|
@ -261,31 +206,275 @@ func Movement()
|
|
|
|
|
else if (GetCursor(best->GetController()) != best)
|
|
|
|
|
best = clonk;
|
|
|
|
|
}
|
|
|
|
|
if (best) return MoveTo(best->GetY(), 35, best);
|
|
|
|
|
|
|
|
|
|
// Stop, why do you move?
|
|
|
|
|
Halt();
|
|
|
|
|
if (best)
|
|
|
|
|
MoveTo(best->GetY(), 10, best);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Halt(bool no_partner_call)
|
|
|
|
|
func FxElevatorUpperLimitCheckTimer(target, effect, time)
|
|
|
|
|
{
|
|
|
|
|
if (!elevator) return;
|
|
|
|
|
if (GetYDir() && !slave) elevator->StopEngine();
|
|
|
|
|
if(!elevator) return -1;
|
|
|
|
|
if(IsSlave()) return -1;
|
|
|
|
|
|
|
|
|
|
var d = GetY() - (elevator->GetY() + 20);
|
|
|
|
|
|
|
|
|
|
// HOW COULD THIS HAPPEN :C
|
|
|
|
|
if(d <= 0)
|
|
|
|
|
{
|
|
|
|
|
if(GetYDir() < 0)
|
|
|
|
|
{
|
|
|
|
|
SetPosition(GetX(), GetY() - d);
|
|
|
|
|
ForceSync();
|
|
|
|
|
ContactTop();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
if(GetYDir() == 0)
|
|
|
|
|
SetPosition(GetX(), GetY() - d);
|
|
|
|
|
|
|
|
|
|
effect.Interval = 1;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// everything okay, adjust timer accordingly
|
|
|
|
|
// check less often if far away from elevator
|
|
|
|
|
// note: d > 0
|
|
|
|
|
var t = BoundBy(d / 3, 1, 20);
|
|
|
|
|
effect.Interval = t;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for vehicle control
|
|
|
|
|
func OutOfRange(object vehicle)
|
|
|
|
|
{
|
|
|
|
|
if(Abs(GetY() - vehicle->GetY()) > 10) return true;
|
|
|
|
|
|
|
|
|
|
var min_x = GetX() - 12;
|
|
|
|
|
var max_x = GetX() + 12;
|
|
|
|
|
if(IsMaster())
|
|
|
|
|
{
|
|
|
|
|
min_x = Min(min_x, partner->GetX() - 12);
|
|
|
|
|
max_x = Max(max_x, partner->GetX() + 12);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(vehicle->GetX() < min_x) return true;
|
|
|
|
|
if(vehicle->GetX() > max_x) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FxFetchVehiclesTimer(target, effect, time)
|
|
|
|
|
{
|
|
|
|
|
if(!elevator) return -1;
|
|
|
|
|
if(IsSlave()) return 1;
|
|
|
|
|
|
|
|
|
|
// look for vehicles
|
|
|
|
|
var additional = -5;
|
|
|
|
|
var x = GetX() - 12 - additional;
|
|
|
|
|
var w = GetX() + 12 + additional;
|
|
|
|
|
var y = GetY() - 12;
|
|
|
|
|
var h = GetY() + 15;
|
|
|
|
|
|
|
|
|
|
if(IsMaster())
|
|
|
|
|
{
|
|
|
|
|
x = Min(x, partner->GetX() - 12 - additional);
|
|
|
|
|
w = Max(w, partner->GetX() + 12 + additional);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch vehicles
|
|
|
|
|
for (var vehicle in FindObjects(Find_InRect(x - GetX(), y - GetY(), w - x, h - y), Find_Category(C4D_Vehicle), Find_NoContainer(), Find_Func("FitsInElevator")))
|
|
|
|
|
{
|
|
|
|
|
if (GetEffect("ElevatorControl", vehicle)) continue;
|
|
|
|
|
vehicle->SetPosition(vehicle->GetX(), GetY() + 12 - vehicle->GetObjHeight()/2);
|
|
|
|
|
vehicle->SetSpeed();
|
|
|
|
|
vehicle->SetR();
|
|
|
|
|
AddEffect("ElevatorControl", vehicle, 30, 5, vehicle, nil, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Energy */
|
|
|
|
|
|
|
|
|
|
func GetNeededPower()
|
|
|
|
|
{
|
|
|
|
|
var p = Elevator_needed_power;
|
|
|
|
|
if(partner_was_synced) p = 2 * p;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// for the position
|
|
|
|
|
func GetActualPowerConsumer()
|
|
|
|
|
{
|
|
|
|
|
return elevator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// the lift may not need power when not used
|
|
|
|
|
func QueryWaivePowerRequest()
|
|
|
|
|
{
|
|
|
|
|
// no clonk on elevator? must be automatic
|
|
|
|
|
if(CheckIdle()) return 20;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func OnNotEnoughPower()
|
|
|
|
|
{
|
|
|
|
|
_inherited(...); // on purpose before the rest
|
|
|
|
|
|
|
|
|
|
if(GetYDir())
|
|
|
|
|
StoreMovementData();
|
|
|
|
|
else; // already has data stored
|
|
|
|
|
|
|
|
|
|
if(GetAction() != "DriveIdle")
|
|
|
|
|
Halt(false, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func OnEnoughPower()
|
|
|
|
|
{
|
|
|
|
|
_inherited(...); // on purpose before the rest
|
|
|
|
|
RestoreMovementData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StoreMovementData(int y_dir, string action, bool user_requested)
|
|
|
|
|
{
|
|
|
|
|
y_dir = y_dir ?? GetYDir();
|
|
|
|
|
action = action ?? GetAction();
|
|
|
|
|
user_requested = user_requested ?? !CheckIdle();
|
|
|
|
|
var e = GetEffect("StoredMovementData", this);
|
|
|
|
|
if(!e) e = AddEffect("StoredMovementData", this, 1, 0, this);
|
|
|
|
|
e.y_dir = y_dir;
|
|
|
|
|
e.action = action;
|
|
|
|
|
e.user_requested = user_requested;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RestoreMovementData()
|
|
|
|
|
{
|
|
|
|
|
var e = GetEffect("StoredMovementData", this);
|
|
|
|
|
if(!e) return;
|
|
|
|
|
var drill = false;
|
|
|
|
|
if(e.action == "Drill")
|
|
|
|
|
drill = true;
|
|
|
|
|
SetMoveDirection(BoundBy(e.y_dir, -1, 1), e.user_requested, drill);
|
|
|
|
|
RemoveEffect(nil, this, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func SetMoveDirection(int dir, bool user_requested, bool drill)
|
|
|
|
|
{
|
|
|
|
|
if(IsSlave())
|
|
|
|
|
return partner->SetMoveSpeed(dir, user_requested, drill);
|
|
|
|
|
|
|
|
|
|
if(user_requested) StopAutomaticMovement();
|
|
|
|
|
|
|
|
|
|
var e;
|
|
|
|
|
if(e = GetEffect("StopPowerConsumption", this))
|
|
|
|
|
RemoveEffect(nil, this, e);
|
|
|
|
|
|
|
|
|
|
// no change?
|
|
|
|
|
if((dir < 0) && (GetYDir() < 0)) return;
|
|
|
|
|
if((dir > 0) && (GetYDir() > 0)) return;
|
|
|
|
|
|
|
|
|
|
// already reached top/bottom?
|
|
|
|
|
if(GetContact(-1, CNAT_Bottom) && (dir > 0) && !drill)
|
|
|
|
|
return;
|
|
|
|
|
if(GetContact(-1, CNAT_Top) && (dir < 0))
|
|
|
|
|
return;
|
|
|
|
|
if(dir == 0) return Halt();
|
|
|
|
|
|
|
|
|
|
var speed = case_speed;
|
|
|
|
|
// note: can not move down with full speed because of solidmask problem
|
|
|
|
|
// todo..
|
|
|
|
|
if(!user_requested && dir < 0) speed = case_speed_automatic;
|
|
|
|
|
|
|
|
|
|
var action = "Drive";
|
|
|
|
|
if(drill)
|
|
|
|
|
{
|
|
|
|
|
action = "Drill";
|
|
|
|
|
speed = GetDrillSpeed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(CurrentlyHasPower())
|
|
|
|
|
{
|
|
|
|
|
SetYDir(dir * speed);
|
|
|
|
|
SetAction(action);
|
|
|
|
|
ForceSync();
|
|
|
|
|
|
|
|
|
|
Sound("ElevatorStart");
|
|
|
|
|
elevator->StartEngine();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
StoreMovementData(dir * speed, action);
|
|
|
|
|
MakePowerConsumer(GetNeededPower());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Halt(bool user_requested, bool power_out)
|
|
|
|
|
{
|
|
|
|
|
if(IsSlave()) return;
|
|
|
|
|
|
|
|
|
|
StopAutomaticMovement();
|
|
|
|
|
|
|
|
|
|
if(GetYDir())
|
|
|
|
|
{
|
|
|
|
|
if(elevator)
|
|
|
|
|
elevator->StopEngine();
|
|
|
|
|
Sound("ElevatorStop");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// clear speed
|
|
|
|
|
SetAction("DriveIdle");
|
|
|
|
|
SetYDir();
|
|
|
|
|
if (no_partner_call && HasReadyPartner()) return SetPosition(GetX(), partner->GetY());
|
|
|
|
|
if (HasReadyPartner()) partner->Halt(true);
|
|
|
|
|
ForceSync();
|
|
|
|
|
|
|
|
|
|
if(user_requested)
|
|
|
|
|
{
|
|
|
|
|
UnmakePowerConsumer();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// if not stopped because of lack of power, stop consuming power
|
|
|
|
|
if(!power_out)
|
|
|
|
|
AddEffect("StopPowerConsumption", this, 1, 40, this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func FxStopPowerConsumptionTimer(object target, effect, int time)
|
|
|
|
|
{
|
|
|
|
|
UnmakePowerConsumer();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ForceSync()
|
|
|
|
|
{
|
|
|
|
|
if(!IsMaster()) return;
|
|
|
|
|
// clear rounding errors
|
|
|
|
|
SetPosition(GetX(), GetY());
|
|
|
|
|
// adjust partner
|
|
|
|
|
partner->SetPosition(partner->GetX(), GetY());
|
|
|
|
|
partner->SetYDir(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ContactTop()
|
|
|
|
|
{
|
|
|
|
|
Halt();
|
|
|
|
|
Sound("WoodHit*");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ContactBottom()
|
|
|
|
|
{
|
|
|
|
|
// try to dig free
|
|
|
|
|
if(GetAction() == "Drill")
|
|
|
|
|
{
|
|
|
|
|
Drilling();
|
|
|
|
|
|
|
|
|
|
// wee!
|
|
|
|
|
if(!GetContact(-1, CNAT_Bottom))
|
|
|
|
|
{
|
|
|
|
|
SetYDir(GetDrillSpeed());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Halt();
|
|
|
|
|
movement = 0;
|
|
|
|
|
drill = false;
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
Sound("WoodHit*");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Checks whether the elevator should not move because someone's holding it
|
|
|
|
@ -293,10 +482,10 @@ func ContactBottom()
|
|
|
|
|
func CheckIdle()
|
|
|
|
|
{
|
|
|
|
|
// I have no mind of my own
|
|
|
|
|
if (slave) return true;
|
|
|
|
|
if(IsSlave()) return;
|
|
|
|
|
|
|
|
|
|
var in_rect = Find_InRect(-13, -13, 26, 26);
|
|
|
|
|
if (partner && !no_connection)
|
|
|
|
|
if (IsMaster())
|
|
|
|
|
{
|
|
|
|
|
if (partner->GetX() < GetX())
|
|
|
|
|
in_rect = Find_InRect(-39, -13, 52, 26);
|
|
|
|
@ -307,7 +496,8 @@ func CheckIdle()
|
|
|
|
|
{
|
|
|
|
|
if (pusher->GetActionTarget() == this) return false;
|
|
|
|
|
if (GetEffect("ElevatorControl", pusher->GetActionTarget()) && GetEffect("ElevatorControl", pusher->GetActionTarget()).case == this) return false;
|
|
|
|
|
if (partner && !no_connection)
|
|
|
|
|
|
|
|
|
|
if (IsMaster())
|
|
|
|
|
{
|
|
|
|
|
if (pusher->GetActionTarget() == partner) return false;
|
|
|
|
|
if (GetEffect("ElevatorControl", pusher->GetActionTarget()) && GetEffect("ElevatorControl", pusher->GetActionTarget()).case == partner) return false;
|
|
|
|
@ -316,88 +506,148 @@ func CheckIdle()
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StopAutomaticMovement()
|
|
|
|
|
{
|
|
|
|
|
var done = false;
|
|
|
|
|
if(GetEffect("MoveTo", this))
|
|
|
|
|
{
|
|
|
|
|
RemoveEffect("MoveTo", this);
|
|
|
|
|
done = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// todo: check if sensible
|
|
|
|
|
if(done) Halt();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Moves the case to the specific y-coordinate
|
|
|
|
|
// delay in frames, so the elevator does not freak out
|
|
|
|
|
// target will be checked again for COMD_Stop and distance after delay run out
|
|
|
|
|
func MoveTo(int y, int delay, object target)
|
|
|
|
|
func MoveTo(int y, int delay, object target, bool user_requested)
|
|
|
|
|
{
|
|
|
|
|
// Not idle?
|
|
|
|
|
if (!CheckIdle()) return false;
|
|
|
|
|
if (!CheckIdle() && !user_requested) return false;
|
|
|
|
|
Halt();
|
|
|
|
|
move_to = BoundBy(y, elevator->GetY() + 20, LandscapeHeight());
|
|
|
|
|
move_to_delay = delay;
|
|
|
|
|
move_to_target = target;
|
|
|
|
|
var e = AddEffect("MoveTo", this, 1, 2, this);
|
|
|
|
|
e.delay = delay;
|
|
|
|
|
e.move_to_y = y;
|
|
|
|
|
e.target = target;
|
|
|
|
|
e.user_requested = user_requested;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ClearMoveTo()
|
|
|
|
|
func FxMoveToTimer(target, effect, time)
|
|
|
|
|
{
|
|
|
|
|
move_to = nil;
|
|
|
|
|
move_to_target = nil;
|
|
|
|
|
move_to_delay = nil;
|
|
|
|
|
if(time < effect.delay) return 1;
|
|
|
|
|
// what would take more than 10 seconds?
|
|
|
|
|
if((time - effect.delay) / 36 > 10) return -1;
|
|
|
|
|
|
|
|
|
|
var y = effect.move_to_y;
|
|
|
|
|
if(effect.target) y = effect.target->GetY();
|
|
|
|
|
|
|
|
|
|
// target dead?
|
|
|
|
|
if(y == nil)
|
|
|
|
|
{
|
|
|
|
|
Halt();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// target moves away from elevator shaft, finish movement but stop following
|
|
|
|
|
if(effect.target)
|
|
|
|
|
if(Abs(GetX() - effect.target->GetX()) > 100)
|
|
|
|
|
{
|
|
|
|
|
effect.move_to_y = effect.target->GetY();
|
|
|
|
|
effect.target = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// destination reached
|
|
|
|
|
if(Abs(GetY() - y) < 5)
|
|
|
|
|
{
|
|
|
|
|
Halt();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
var dir = ElevatorCase_up;
|
|
|
|
|
if(y > GetY()) dir = ElevatorCase_down;
|
|
|
|
|
SetMoveDirection(dir, effect.user_requested, false);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HasReadyPartner()
|
|
|
|
|
func StartDrilling()
|
|
|
|
|
{
|
|
|
|
|
return partner && !no_connection;
|
|
|
|
|
SetAction("Drill");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func StopDrilling()
|
|
|
|
|
{
|
|
|
|
|
SetAction("Drive");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Drilling()
|
|
|
|
|
{
|
|
|
|
|
var additional_y = 1;
|
|
|
|
|
var rect = Rectangle(GetX() - 12, GetY() - 13 - additional_y, GetX() + 12, GetY() + 13 + additional_y);
|
|
|
|
|
if(IsMaster())
|
|
|
|
|
{
|
|
|
|
|
rect.x = Min(rect.x, partner->GetX() - 12);
|
|
|
|
|
rect.y = Min(rect.y, partner->GetY() - 13 - additional_y);
|
|
|
|
|
rect.w = Max(rect.w, partner->GetX() + 12);
|
|
|
|
|
rect.h = Max(rect.h, partner->GetY() + 13 + additional_y);
|
|
|
|
|
}
|
|
|
|
|
DigFreeRect(rect.x, rect.y, rect.w - rect.x, rect.h - rect.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Controls */
|
|
|
|
|
|
|
|
|
|
local movement, drill;
|
|
|
|
|
|
|
|
|
|
func ControlUseStart(object clonk) // Drilling
|
|
|
|
|
func ControlUseStart(object clonk, int x, int y) // send elevator to position
|
|
|
|
|
{
|
|
|
|
|
if (slave) return Control2Master("ControlUseStart", clonk);
|
|
|
|
|
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
drill = true;
|
|
|
|
|
movement = 1;
|
|
|
|
|
return true;
|
|
|
|
|
if (IsSlave()) return Control2Master("ControlUseStart", clonk, x, y);
|
|
|
|
|
MoveTo(GetY() + y, 60, nil, true);
|
|
|
|
|
Sound("Click", nil, nil, clonk->GetOwner());
|
|
|
|
|
// does not want UseStop-callback
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
func ControlUseStop(object clonk)
|
|
|
|
|
{
|
|
|
|
|
if (slave) return Control2Master("ControlUseStop", clonk);
|
|
|
|
|
|
|
|
|
|
drill = false;
|
|
|
|
|
movement = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ControlDown(object clonk)
|
|
|
|
|
{
|
|
|
|
|
if (slave) return Control2Master("ControlDown", clonk);
|
|
|
|
|
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
if (!drill)
|
|
|
|
|
movement = 1;
|
|
|
|
|
if (IsSlave()) return Control2Master("ControlDown", clonk);
|
|
|
|
|
|
|
|
|
|
// pressing down when already on ground results in drilling
|
|
|
|
|
var drill = !!GetContact(-1, CNAT_Bottom);
|
|
|
|
|
|
|
|
|
|
SetMoveDirection(ElevatorCase_down, true, drill);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
func ControlUp(object clonk)
|
|
|
|
|
{
|
|
|
|
|
if (slave) return Control2Master("ControlUp", clonk);
|
|
|
|
|
|
|
|
|
|
ClearMoveTo();
|
|
|
|
|
if (!drill)
|
|
|
|
|
movement = -1;
|
|
|
|
|
if (IsSlave()) return Control2Master("ControlUp", clonk);
|
|
|
|
|
|
|
|
|
|
// what is that player even doing
|
|
|
|
|
if(GetY() <= elevator->GetY() + 20)
|
|
|
|
|
{
|
|
|
|
|
Sound("Click", nil, nil, clonk->GetOwner());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetMoveDirection(ElevatorCase_up, true, false);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ControlStop(object clonk, int control)
|
|
|
|
|
{
|
|
|
|
|
if (slave) return Control2Master("ControlStop", clonk, control);
|
|
|
|
|
|
|
|
|
|
if (control == CON_Up || control == CON_Down)
|
|
|
|
|
{
|
|
|
|
|
if (!drill)
|
|
|
|
|
movement = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (IsSlave()) return Control2Master("ControlStop", clonk, control);
|
|
|
|
|
|
|
|
|
|
if(control == CON_Up && GetYDir() <= 0)
|
|
|
|
|
Halt(true);
|
|
|
|
|
else if(control == CON_Down && GetYDir() >= 0)
|
|
|
|
|
Halt(true);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Control2Master(string call, object clonk, int control)
|
|
|
|
|
func Control2Master(string call, object clonk)
|
|
|
|
|
{
|
|
|
|
|
if (no_connection) return false;
|
|
|
|
|
|
|
|
|
|
return partner->Call(call, clonk, control);
|
|
|
|
|
if (!IsSlave()) return false;
|
|
|
|
|
return partner->Call(call, clonk, ...);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local ActMap = {
|
|
|
|
@ -410,7 +660,19 @@ local ActMap = {
|
|
|
|
|
Y = 0,
|
|
|
|
|
Wdt = 24,
|
|
|
|
|
Hgt = 26,
|
|
|
|
|
NextAction = "Drive"
|
|
|
|
|
NextAction = "Drive",
|
|
|
|
|
Sound = "ElevatorMoving"
|
|
|
|
|
},
|
|
|
|
|
DriveIdle = {
|
|
|
|
|
Prototype = Action,
|
|
|
|
|
Name = "DriveIdle",
|
|
|
|
|
Procedure = DFA_FLOAT,
|
|
|
|
|
Directions = 1,
|
|
|
|
|
X = 0,
|
|
|
|
|
Y = 0,
|
|
|
|
|
Wdt = 24,
|
|
|
|
|
Hgt = 26,
|
|
|
|
|
NextAction = "DriveIdle",
|
|
|
|
|
},
|
|
|
|
|
Drill = {
|
|
|
|
|
Prototype = Action,
|
|
|
|
@ -421,14 +683,27 @@ local ActMap = {
|
|
|
|
|
Y = 0,
|
|
|
|
|
Wdt = 24,
|
|
|
|
|
Hgt = 26,
|
|
|
|
|
Delay = 1,
|
|
|
|
|
Length = 1,
|
|
|
|
|
PhaseCall = "Drilling",
|
|
|
|
|
NextAction = "Drill",
|
|
|
|
|
Sound = "ElevatorDrilling",
|
|
|
|
|
DigFree = 1
|
|
|
|
|
},
|
|
|
|
|
Attach = {
|
|
|
|
|
Prototype = Action,
|
|
|
|
|
Name = "Attach",
|
|
|
|
|
Procedure = DFA_ATTACH,
|
|
|
|
|
Directions = 1,
|
|
|
|
|
X = 0,
|
|
|
|
|
Y = 0,
|
|
|
|
|
Wdt = 24,
|
|
|
|
|
Hgt = 26,
|
|
|
|
|
NextAction = "Attach"
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
local Name = "$Name$";
|
|
|
|
|
local Description = "$Description$";
|
|
|
|
|
local Touchable = 2;
|
|
|
|
|
local Speed = 15;
|
|
|
|
|
local RushSpeed = 20; // When moving on its own
|
|
|
|
|
local HitPoints = 50;
|