forked from Mirrors/openclonk
393 lines
10 KiB
C
393 lines
10 KiB
C
/**
|
|
Cable Car
|
|
Library object for the cable car.
|
|
|
|
@author Randrian, Clonkonaut, Maikel
|
|
|
|
Cable cars must set up a movement speed using SetCableSpeed(x);
|
|
Cable cars handle movement on their own. In order to move along a rail, cable cars must call DoMovement() regularly.
|
|
E.g. using AddTimer("DoMovement", 1);
|
|
*/
|
|
|
|
// The speed with which the car travels along rails
|
|
local lib_ccar_speed;
|
|
// The rail (cable or crossing) that is currently traveled along or stayed at
|
|
local lib_ccar_rail;
|
|
// The direction (0 or 1) the rail is travelled along, translates into the cable's action targets (of which there should be two!)
|
|
local lib_ccar_direction;
|
|
// The travel progress on the current rail
|
|
local lib_ccar_progress;
|
|
// The length of the rail
|
|
local lib_ccar_max_progress;
|
|
// This target point for pathfinding
|
|
local lib_ccar_destination;
|
|
// Current delivery the car is on, array: [starting station, target station, requested objects, amount]
|
|
local lib_ccar_delivery;
|
|
|
|
/*--- Overloads ---*/
|
|
|
|
// Overload these functions as you feel fit
|
|
|
|
// Called after the car is attached to a rail
|
|
func Engaged() {}
|
|
|
|
// Called after the car is detached from the rail
|
|
func Disengaged() {}
|
|
|
|
// To offset the position on the cable from the object's center
|
|
// position is a 2-value-array [x,y]
|
|
// prec is nil or a value to multiply your calculations with
|
|
func GetCableOffset(array position, int prec) {}
|
|
|
|
// To add custom interaction menu entries after the regular cable car entries
|
|
// custom_entry is a prototype for proper spacing of buttons
|
|
// Use priorities > 2000 just to be sure
|
|
func GetCableCarExtraMenuEntries(array menu_entries, proplist custom_entry, object clonk) {}
|
|
|
|
// Whenever movement is about to begin
|
|
// Movement data like lib_ccar_direction is still nil at this moment
|
|
func OnStart() {}
|
|
|
|
// Whenever the car stops its movement
|
|
// failed is true if the movement to a destination was cancelled (usually because the path broke in the meantime)
|
|
func OnStop(bool failed) {}
|
|
|
|
/*--- Interface ---*/
|
|
|
|
// Sets the speed of the cable car
|
|
public func SetCableSpeed(int value)
|
|
{
|
|
lib_ccar_speed = value;
|
|
}
|
|
|
|
// Positioning of the car along the cable
|
|
// This should be called regularly, see header comment!
|
|
public func DoMovement()
|
|
{
|
|
if (!GetRailTarget()) return;
|
|
if (lib_ccar_destination == nil) return;
|
|
if (lib_ccar_direction == nil) return;
|
|
|
|
var start = 1;
|
|
var end = 0;
|
|
if (lib_ccar_direction == 1)
|
|
{
|
|
start = 0;
|
|
end = 1;
|
|
}
|
|
|
|
lib_ccar_progress += lib_ccar_speed;
|
|
var position = CreateArray(2);
|
|
if (lib_ccar_progress >= lib_ccar_max_progress)
|
|
{
|
|
lib_ccar_rail->~Deactivation(1);
|
|
lib_ccar_rail = lib_ccar_rail->GetActionTarget(end);
|
|
lib_ccar_rail->GetCablePosition(position);
|
|
GetCableOffset(position);
|
|
SetPosition(position[0], position[1]);
|
|
lib_ccar_direction = nil;
|
|
CrossingReached();
|
|
return;
|
|
}
|
|
|
|
var prec = 100;
|
|
var origin = CreateArray(2), ending = CreateArray(2);
|
|
lib_ccar_rail->GetActionTarget(start)->GetCablePosition(origin, prec);
|
|
lib_ccar_rail->GetActionTarget(end)->GetCablePosition(ending, prec);
|
|
position[0] = origin[0] + (ending[0] - origin[0]) * lib_ccar_progress/lib_ccar_max_progress;
|
|
position[1] = origin[1] + (ending[1] - origin[1]) * lib_ccar_progress/lib_ccar_max_progress;
|
|
GetCableOffset(position, prec);
|
|
SetPosition(position[0], position[1], 1, prec);
|
|
}
|
|
|
|
/*--- Status ---*/
|
|
|
|
public func IsCableCar() { return true; }
|
|
|
|
public func GetRailTarget() { return lib_ccar_rail; }
|
|
|
|
public func IsTravelling() { return lib_ccar_destination; }
|
|
|
|
/* Interaction */
|
|
|
|
// Provides an own interaction menu.
|
|
public func HasInteractionMenu() { return true; }
|
|
|
|
// Show settins in interaction menu
|
|
public func GetInteractionMenus(object clonk)
|
|
{
|
|
var menus = _inherited(clonk, ...) ?? [];
|
|
var cablecar_menu =
|
|
{
|
|
title = "$CableCarOptions$",
|
|
entries_callback = this.GetCableCarMenuEntries,
|
|
callback = nil,
|
|
callback_hover = "OnCableCarHover",
|
|
callback_target = this,
|
|
BackgroundColor = RGB(0, 0, 50),
|
|
Priority = 20
|
|
};
|
|
PushBack(menus, cablecar_menu);
|
|
|
|
return menus;
|
|
}
|
|
|
|
public func GetCableCarMenuEntries(object clonk)
|
|
{
|
|
var control_prototype =
|
|
{
|
|
BackgroundColor = { Std = 0, Selected = RGB(100, 30, 30) },
|
|
OnMouseIn = GuiAction_SetTag("Selected"),
|
|
OnMouseOut = GuiAction_SetTag("Std"),
|
|
Right = "2em"
|
|
};
|
|
|
|
var custom_entry =
|
|
{
|
|
Right = "3em", Bottom = "2em",
|
|
image = { Prototype = control_prototype },
|
|
icon = { Left = "2em" }
|
|
};
|
|
|
|
var menu_entries = [];
|
|
|
|
// Clickable buttons
|
|
|
|
if (!GetRailTarget())
|
|
{
|
|
// Engaging onto a rail
|
|
var stations = FindObjects(Find_AtPoint(), Find_Func("IsCableStation"));
|
|
var i = 0;
|
|
for (var station in stations)
|
|
{
|
|
var engage = new custom_entry {
|
|
Priority = 1000 + i,
|
|
Tooltip = "$TooltipEngage$",
|
|
OnClick = GuiAction_Call(this, "EngageRail", station),
|
|
image = { Prototype = custom_entry.image, Symbol = station },
|
|
icon = { Prototype = custom_entry.icon, Symbol = Icon_LibraryCableCar, GraphicsName = "Engage" }
|
|
};
|
|
PushBack(menu_entries, { symbol = station, extra_data = "Engage", custom = engage });
|
|
i++;
|
|
}
|
|
// No station present
|
|
if (i == 0)
|
|
{
|
|
var search = {
|
|
Priority = 1000,
|
|
Right = "100%", Bottom = "2em",
|
|
text = { Text = "$NoStation$", Style = GUI_TextVCenter | GUI_TextHCenter}
|
|
};
|
|
PushBack(menu_entries, { symbol = this, extra_data = "NoStation", custom = search });
|
|
}
|
|
} else {
|
|
// Start the trip
|
|
if (!IsTravelling())
|
|
{
|
|
var go = new custom_entry {
|
|
Priority = 1000,
|
|
Tooltip = "$TooltipGo$",
|
|
OnClick = GuiAction_Call(this, "OpenDestinationSelection", clonk),
|
|
image = { Prototype = custom_entry.image, Symbol = Icon_Play }
|
|
};
|
|
PushBack(menu_entries, { symbol = this, extra_data = "Go", custom = go });
|
|
|
|
var disengage = new custom_entry {
|
|
Priority = 1001,
|
|
Tooltip = "$TooltipDisengage$",
|
|
OnClick = GuiAction_Call(this, "DisengageRail"),
|
|
image = { Prototype = custom_entry.image, Symbol = GetRailTarget() },
|
|
icon = { Prototype = custom_entry.icon, Symbol = Icon_LibraryCableCar, GraphicsName = "Disengage" }
|
|
};
|
|
PushBack(menu_entries, { symbol = GetRailTarget(), extra_data = "Disengage", custom = disengage });
|
|
}
|
|
}
|
|
// Add custom entries
|
|
GetCableCarExtraMenuEntries(menu_entries, custom_entry, clonk);
|
|
|
|
return menu_entries;
|
|
}
|
|
|
|
public func OnCableCarHover(symbol, extra_data, desc_menu_target, menu_id)
|
|
{
|
|
if (symbol == nil) return;
|
|
|
|
var text = "";
|
|
if (extra_data == "Engage")
|
|
text = Format("$DescEngage$", GetName(), symbol->GetName());
|
|
if (extra_data == "Go")
|
|
text = "$DescGo$";
|
|
if (extra_data == "Disengage")
|
|
text = Format("$DescDisengage$", GetName(), symbol->GetName());
|
|
GuiUpdate({ Text = text }, menu_id, 1, desc_menu_target);
|
|
}
|
|
|
|
/*--- Travelling ---*/
|
|
|
|
// Attach the car onto a crossing
|
|
public func EngageRail(object crossing, bool silent)
|
|
{
|
|
if (! crossing->~IsCableCrossing()) return false;
|
|
|
|
var position = CreateArray(2);
|
|
crossing->GetCablePosition(position);
|
|
GetCableOffset(position);
|
|
SetPosition(position[0], position[1]);
|
|
SetSpeed(0,0);
|
|
SetR(0);
|
|
SetRDir(0);
|
|
SetComDir(COMD_None);
|
|
lib_ccar_rail = crossing;
|
|
lib_ccar_direction = nil;
|
|
if (!silent) Sound("Objects::Connect");
|
|
UpdateInteractionMenus(this.GetCableCarMenuEntries);
|
|
|
|
Engaged();
|
|
lib_ccar_rail->OnCableCarEngaged(this);
|
|
}
|
|
|
|
// Detach the car from its current holding point (cable or crossing, does not matter)
|
|
public func DisengageRail()
|
|
{
|
|
lib_ccar_rail = nil;
|
|
lib_ccar_direction = nil;
|
|
lib_ccar_progress = 0;
|
|
lib_ccar_max_progress = 0;
|
|
lib_ccar_destination = nil;
|
|
UpdateInteractionMenus(this.GetCableCarMenuEntries);
|
|
|
|
Disengaged();
|
|
if (lib_ccar_rail) lib_ccar_rail->OnCableCarDisengaged(this);
|
|
}
|
|
|
|
// Sets a target point for travelling and starts the movement process
|
|
public func SetDestination(dest)
|
|
{
|
|
if(GetType(dest) == C4V_Int)
|
|
{
|
|
dest = FindObjects(Find_Func("IsCableCrossing"))[dest];
|
|
if (!dest) return;
|
|
}
|
|
|
|
lib_ccar_destination = dest;
|
|
|
|
if(lib_ccar_direction == nil)
|
|
{
|
|
OnStart();
|
|
CrossingReached();
|
|
if (lib_ccar_rail)
|
|
lib_ccar_rail->~OnCableCarDeparture(this);
|
|
}
|
|
}
|
|
|
|
// Whenever a crossing is reached it must be queried for the next crossing to go to
|
|
func CrossingReached()
|
|
{
|
|
var target;
|
|
if(lib_ccar_destination != lib_ccar_rail)
|
|
{
|
|
if(target = lib_ccar_rail->GetNextWaypoint(lib_ccar_destination))
|
|
MoveTo(target);
|
|
else
|
|
DestinationFailed();
|
|
}
|
|
// Destination reached
|
|
else {
|
|
DestinationReached();
|
|
}
|
|
}
|
|
|
|
// When the current destination is reached
|
|
func DestinationReached()
|
|
{
|
|
lib_ccar_destination = nil;
|
|
lib_ccar_direction = nil;
|
|
lib_ccar_progress = 0;
|
|
lib_ccar_max_progress = 0;
|
|
|
|
OnStop(false);
|
|
|
|
if (lib_ccar_rail)
|
|
{
|
|
if (lib_ccar_delivery)
|
|
FinishedRequest(lib_ccar_rail);
|
|
lib_ccar_rail->OnCableCarArrival(this);
|
|
}
|
|
}
|
|
|
|
// When the way to the current destination has vanished somehow
|
|
func DestinationFailed()
|
|
{
|
|
lib_ccar_destination = nil;
|
|
lib_ccar_direction = nil;
|
|
lib_ccar_progress = 0;
|
|
lib_ccar_max_progress = 0;
|
|
|
|
OnStop(true);
|
|
}
|
|
|
|
// Setup movement process
|
|
func MoveToIndex(int dest)
|
|
{
|
|
var dest_obj = FindObjects(Find_Func("IsCableCrossing"))[dest];
|
|
if (dest_obj) return MoveTo(dest_obj);
|
|
}
|
|
|
|
public func MoveTo(object dest)
|
|
{
|
|
var rail = 0;
|
|
for(var test_rail in FindObjects(Find_Func("IsConnectedTo", lib_ccar_rail)))
|
|
{
|
|
if(test_rail->IsConnectedTo(dest))
|
|
{
|
|
rail = test_rail;
|
|
break;
|
|
}
|
|
}
|
|
if(!rail)
|
|
return DestinationFailed(); // Shouldn't happen
|
|
|
|
// Target the first or second action target?
|
|
if(rail->GetActionTarget(0) == dest)
|
|
lib_ccar_direction = 0;
|
|
else
|
|
lib_ccar_direction = 1;
|
|
lib_ccar_progress = 0;
|
|
var origin = CreateArray(2), ending = CreateArray(2);
|
|
rail->GetActionTarget(0)->GetCablePosition(origin);
|
|
rail->GetActionTarget(1)->GetCablePosition(ending);
|
|
rail->~Activation(1);
|
|
lib_ccar_max_progress = Distance(origin[0], origin[1], ending[0], ending[1]);
|
|
lib_ccar_rail = rail;
|
|
}
|
|
|
|
/* Destination selection */
|
|
|
|
public func OpenDestinationSelection(object clonk)
|
|
{
|
|
if (!clonk) return;
|
|
if (!GetRailTarget()) return;
|
|
|
|
var plr = clonk->GetOwner();
|
|
// Close interaction menu
|
|
if (clonk->GetMenu())
|
|
if (!clonk->TryCancelMenu())
|
|
return;
|
|
|
|
GUI_DestinationSelectionMenu->CreateFor(clonk, this, GetRailTarget());
|
|
}
|
|
|
|
/*-- Delivery --*/
|
|
|
|
public func AddRequest(proplist requested, int amount, proplist target, proplist source)
|
|
{
|
|
lib_ccar_delivery = [source, target, requested, amount];
|
|
SetDestination(target);
|
|
}
|
|
|
|
func FinishedRequest(object station)
|
|
{
|
|
if (station && lib_ccar_delivery)
|
|
station->RequestArrived(this, lib_ccar_delivery[2], lib_ccar_delivery[3]);
|
|
lib_ccar_delivery = nil;
|
|
} |