cable cars: improve network deletion and recreation + clean up

This is a rather chaotic commit and does not solve all problems of network creation yet, but is an improvement over the old system where deletion of a cable line would fail the network. However, we probably need a cleaner method to construct a network properly.
master
Maikel de Vries 2018-01-28 12:47:36 +01:00
parent 49d03e52c6
commit d8736a56ac
11 changed files with 535 additions and 252 deletions

View File

@ -1,6 +1,10 @@
/*-- Cable line --*/
/**
Cable line
func Initialize()
@author Clonkonaut
*/
public func Initialize()
{
SetAction("Connect");
SetVertexXY(0, GetX(), GetY());
@ -37,28 +41,44 @@ public func GetConnectedObject(object obj)
return GetActionTarget(0);
}
/* Breaking */
func LineBreak(bool no_msg)
/*-- Breaking --*/
public func OnLineBreak(bool no_msg)
{
Sound("Objects::Connect");
if (GetActionTarget(0)) GetActionTarget(0)->~CableDeactivation(activations);
if (GetActionTarget(1)) GetActionTarget(1)->~CableDeactivation(activations);
var act1 = GetActionTarget(0);
var act2 = GetActionTarget(1);
SetAction("Idle");
if (act1)
{
act1->~CableDeactivation(activations);
act1->~RemoveCableConnection(this);
}
if (act2)
{
act2->~CableDeactivation(activations);
act2->~RemoveCableConnection(this);
}
if (!no_msg)
BreakMessage();
}
func BreakMessage()
public func BreakMessage()
{
var line_end = GetActionTarget(0);
if (line_end->GetID() != CableLorryReel)
if (!line_end || line_end->GetID() != CableLorryReel)
line_end = GetActionTarget(1);
if (line_end->Contained()) line_end = line_end->Contained();
line_end->Message("$TxtLinebroke$");
if (line_end && line_end->Contained())
line_end = line_end->Contained();
if (line_end)
line_end->Message("$TxtLinebroke$");
return;
}
/* Activation */
/*-- Activation --*/
local activations = 0;
@ -67,7 +87,8 @@ local activations = 0;
public func Activation(int count)
{
// Count must be > 0
if (count < 1) return FatalError("Cable Line: Activation() was called with count < 1.");
if (count < 1)
return FatalError("Cable Line: Activation() was called with count < 1.");
activations += count;
if (GetActionTarget(0)) GetActionTarget(0)->~CableActivation(count);
if (GetActionTarget(1)) GetActionTarget(1)->~CableActivation(count);
@ -78,22 +99,29 @@ public func Activation(int count)
public func Deactivation(int count)
{
// Count must be > 0
if (count < 1) return FatalError("Cable Line: Deactivation() was called with count < 1.");
if (count < 1)
return FatalError("Cable Line: Deactivation() was called with count < 1.");
activations -= count;
if (GetActionTarget(0)) GetActionTarget(0)->~CableDeactivation(count);
if (GetActionTarget(1)) GetActionTarget(1)->~CableDeactivation(count);
}
/* Saving */
/*-- Saving --*/
public func SaveScenarioObject(props)
{
if (!inherited(props, ...)) return false;
if (!inherited(props, ...))
return false;
SaveScenarioObjectAction(props);
if (IsCableLine()) props->AddCall("Connection", this, "SetConnectedObjects", GetActionTarget(0), GetActionTarget(1));
if (IsCableLine())
props->AddCall("Connection", this, "SetConnectedObjects", GetActionTarget(0), GetActionTarget(1));
return true;
}
/*-- Properties --*/
local ActMap = {
Connect = {
Prototype = Action,
@ -103,4 +131,4 @@ local ActMap = {
}
};
local Name = "$Name$";
local Name = "$Name$";

View File

@ -1,4 +1,9 @@
/*-- Cable reel --*/
/**
Cable reel
Connects cable stations.
@author Clonkonaut
*/
protected func Hit()
{
@ -7,6 +12,7 @@ protected func Hit()
public func IsToolProduct() { return true; }
/*-- Line connection --*/
protected func ControlUse(object clonk, int x, int y)
@ -32,7 +38,7 @@ protected func ControlUse(object clonk, int x, int y)
else
{
// Connect existing power line to obj.
if(line->GetActionTarget(0) == this)
if (line->GetActionTarget(0) == this)
line->SetActionTargets(obj, line->GetActionTarget(1));
else if(line->GetActionTarget(1) == this)
line->SetActionTargets(line->GetActionTarget(0), obj);
@ -63,6 +69,9 @@ private func Find_CableLine(object obj)
return [C4FO_Func, "IsConnectedTo", obj];
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 1;

View File

@ -24,7 +24,8 @@ local lib_ccar_destination;
// Current delivery the car is on, array: [starting station, target station, requested objects, amount]
local lib_ccar_delivery;
/*--- Overloads ---*/
/*-- Overloads --*/
// Overload these functions as you feel fit
@ -52,7 +53,8 @@ func OnStart() {}
// failed is true if the movement to a destination was cancelled (usually because the path broke in the meantime)
func OnStop(bool failed) {}
/*--- Interface ---*/
/*-- Interface --*/
// Sets the speed of the cable car
public func SetCableSpeed(int value)
@ -97,13 +99,14 @@ public func DoMovement()
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;
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 ---*/
/*-- Status --*/
public func IsCableCar() { return true; }
@ -113,7 +116,8 @@ public func GetRailTarget() { return lib_ccar_rail; }
public func IsTravelling() { return lib_ccar_destination; }
/* Interaction */
/*-- Interaction --*/
// Provides an own interaction menu.
public func HasInteractionMenu() { return true; }
@ -185,7 +189,9 @@ public func GetCableCarMenuEntries(object clonk)
};
PushBack(menu_entries, { symbol = this, extra_data = "NoStation", custom = search });
}
} else {
}
else
{
// Start the trip
if (!IsTravelling())
{
@ -227,12 +233,22 @@ public func OnCableCarHover(symbol, extra_data, desc_menu_target, menu_id)
GuiUpdate({ Text = text }, menu_id, 1, desc_menu_target);
}
/*--- Travelling ---*/
/*-- Travelling --*/
// Called when the network is updated.
public func OnRailNetworkUpdate()
{
// The car may have been stuck on a request, continue it now.
ContinueRequest();
return;
}
// Attach the car onto a crossing
public func EngageRail(object crossing, bool silent)
{
if (! crossing->~IsCableCrossing()) return false;
if (!crossing->~IsCableCrossing())
return false;
var position = CreateArray(2);
crossing->GetCablePosition(position);
@ -244,7 +260,8 @@ public func EngageRail(object crossing, bool silent)
SetComDir(COMD_None);
lib_ccar_rail = crossing;
lib_ccar_direction = nil;
if (!silent) Sound("Objects::Connect");
if (!silent)
Sound("Objects::Connect");
UpdateInteractionMenus(this.GetCableCarMenuEntries);
Engaged();
@ -262,21 +279,23 @@ public func DisengageRail()
UpdateInteractionMenus(this.GetCableCarMenuEntries);
Disengaged();
if (lib_ccar_rail) lib_ccar_rail->OnCableCarDisengaged(this);
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)
if (GetType(dest) == C4V_Int)
{
dest = FindObjects(Find_Func("IsCableCrossing"))[dest];
if (!dest) return;
if (!dest)
return;
}
lib_ccar_destination = dest;
if(lib_ccar_direction == nil)
if (lib_ccar_direction == nil)
{
OnStart();
CrossingReached();
@ -286,24 +305,25 @@ public func SetDestination(dest)
}
// Whenever a crossing is reached it must be queried for the next crossing to go to
func CrossingReached()
public func CrossingReached()
{
var target;
if(lib_ccar_destination != lib_ccar_rail)
if (lib_ccar_destination != lib_ccar_rail)
{
if(target = lib_ccar_rail->GetNextWaypoint(lib_ccar_destination))
if (target = lib_ccar_rail->GetNextWaypoint(lib_ccar_destination))
MoveTo(target);
else
DestinationFailed();
}
// Destination reached
else {
else
{
DestinationReached();
}
}
// When the current destination is reached
func DestinationReached()
public func DestinationReached()
{
lib_ccar_destination = nil;
lib_ccar_direction = nil;
@ -321,8 +341,11 @@ func DestinationReached()
}
// When the way to the current destination has vanished somehow
func DestinationFailed()
public func DestinationFailed()
{
if (lib_ccar_rail)
lib_ccar_rail->OnCableCarStopped(this);
lib_ccar_destination = nil;
lib_ccar_direction = nil;
lib_ccar_progress = 0;
@ -331,8 +354,14 @@ func DestinationFailed()
OnStop(true);
}
public func Destruction()
{
if (lib_ccar_rail)
lib_ccar_rail->OnCableCarDestruction(this);
}
// Setup movement process
func MoveToIndex(int dest)
public func MoveToIndex(int dest)
{
var dest_obj = FindObjects(Find_Func("IsCableCrossing"))[dest];
if (dest_obj) return MoveTo(dest_obj);
@ -341,19 +370,19 @@ func MoveToIndex(int dest)
public func MoveTo(object dest)
{
var rail = 0;
for(var test_rail in FindObjects(Find_Func("IsConnectedTo", lib_ccar_rail)))
for (var test_rail in FindObjects(Find_Func("IsConnectedTo", lib_ccar_rail)))
{
if(test_rail->IsConnectedTo(dest))
if (test_rail->IsConnectedTo(dest))
{
rail = test_rail;
break;
}
}
if(!rail)
if (!rail)
return DestinationFailed(); // Shouldn't happen
// Target the first or second action target?
if(rail->GetActionTarget(0) == dest)
if (rail->GetActionTarget(0) == dest)
lib_ccar_direction = 0;
else
lib_ccar_direction = 1;
@ -366,14 +395,14 @@ public func MoveTo(object dest)
lib_ccar_rail = rail;
}
/* Destination selection */
/*-- 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())
@ -382,6 +411,7 @@ public func OpenDestinationSelection(object clonk)
GUI_DestinationSelectionMenu->CreateFor(clonk, this, GetRailTarget());
}
/*-- Delivery --*/
public func AddRequest(proplist requested, int amount, proplist target, proplist source)
@ -390,9 +420,17 @@ public func AddRequest(proplist requested, int amount, proplist target, proplist
SetDestination(target);
}
func FinishedRequest(object station)
public func ContinueRequest()
{
if (!lib_ccar_delivery)
return;
var target = lib_ccar_delivery[1];
SetDestination(target);
}
public func FinishedRequest(object station)
{
if (station && lib_ccar_delivery)
station->RequestArrived(this, lib_ccar_delivery[2], lib_ccar_delivery[3]);
lib_ccar_delivery = nil;
}
}

View File

@ -26,6 +26,9 @@ public func CableDeactivation(int count) { }
// Called by arriving cable cars if this station is the final stop
public func OnCableCarArrival(object car) { }
// Called by cable cars if it stopped at this station (usually because of a problem)
public func OnCableCarStopped(object car) { }
// Called by departing cable cars if it just starts a new journey
public func OnCableCarDeparture(object car) { }
@ -35,6 +38,9 @@ public func OnCableCarEngaged(object car) { }
// Called by a cable car that has been taken off the rail at this station
public func OnCableCarDisengaged(object car) { }
// Called by a cable car that has been destroyed at this station
public func OnCableCarDestruction(object car) { }
// Called when a cable car with a requested delivery arrives
public func OnCableCarDelivery(object car, id requested, int amount) { }
@ -56,26 +62,10 @@ public func Construction()
return _inherited(...);
}
/* Removes this crossing from the network
It first clears every waypoint from the network and then renews the whole information.
Optimisation welcome!
*/
public func Destruction()
{
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (!connection->~IsCableLine()) continue;
var other_crossing = connection->~GetConnectedObject(this);
if (!other_crossing || !other_crossing->~IsCableCrossing()) continue;
other_crossing->ClearConnections(this);
}
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (!connection->~IsCableLine()) continue;
var other_crossing = connection->~GetConnectedObject(this);
if (!other_crossing || !other_crossing->~IsCableCrossing()) continue;
other_crossing->RenewConnections(this);
}
// The connection with other stations is broken via the cable and the network updating is handled there.
// So there is updating to be performed here.
return _inherited(...);
}
@ -106,7 +96,8 @@ public func SetCableStation(bool station)
// Returns the cable hookup position for proper positioning of a car along the line.
public func GetCablePosition(array coordinates, int prec)
{
if (!prec) prec = 1;
if (!prec)
prec = 1;
coordinates[0] = GetX(prec);
coordinates[1] = GetY(prec);
if (this.LineAttach)
@ -153,19 +144,23 @@ public func GetDestinationList(object middle)
AddCableConnection(object cable)
AddCableDestinations(array new_list, object crossing)
AddCableDestination(object new_destination, object crossing, int distance_add)
ClearConnections(object crossing)
RenewConnections(object crossing)
ClearConnections()
RenewConnections()
*/
/** Returns the destination array so it can be used by other crossings.
*/
public func GetDestinations()
{
return destination_list[:];
// This is a nested array, so ensure a proper deep copy is made.
var deep_copy = [];
for (var dest in destination_list)
PushBack(deep_copy, dest[:]);
return deep_copy;
}
// Stores the next crossing (waypoint) to take when advancing to a certain final point
// Scheme (2D array): [Desired final point, Next waypoint to take, Distance (not airline!) until final point]
// Stores the next crossing (waypoint) to take when advancing to a certain final point. The list may not contain the crossing itself.
// Scheme (2D array): [Desired final point, Next waypoint to take, Distance (not airline!) until final point].
local destination_list;
// Constants for easier script reading
@ -181,16 +176,25 @@ local const_distance = 2;
public func AddCableConnection(object cable)
{
// Failsafe
if (!cable || ! cable->~IsCableLine())
if (!cable || !cable->~IsCableLine())
return false;
// Line setup finished?
var other_crossing = cable->~GetConnectedObject(this);
if (! other_crossing->~IsCableCrossing())
if (!other_crossing || !other_crossing->~IsCableCrossing())
return false;
// Acquire destinations of the other crossing, all these are now in reach
AddCableDestinations(other_crossing->GetDestinations(), other_crossing);
// Send own destinations, now in reach for the other one
other_crossing->AddCableDestinations(destination_list[:], this);
other_crossing->AddCableDestinations(GetDestinations(), this);
// Awesome, more power to the network!
DestinationsUpdated();
return true;
}
public func RemoveCableConnection(object cable)
{
// It is easiest to just update all connections.
UpdateConnections();
// Awesome, more power to the network!
DestinationsUpdated();
return true;
@ -200,28 +204,28 @@ public func AddCableConnection(object cable)
* @param new_list The new destination list, formated like a crossing's normal destination list
* @param crossing The crossing where this list comes from
*/
public func AddCableDestinations(array new_list, object crossing)
public func AddCableDestinations(array new_list, object crossing, bool has_reversed)
{
// Append crossing to the list
if (crossing)
{
new_list[GetLength(new_list)] = [crossing, crossing];
// This value is to be added to every distance
var distance_add = ObjectDistance(crossing);
}
else
return false; // Does not compute
if (!crossing)
return false;
// Append crossing itself to the list with zero distance.
new_list[GetLength(new_list)] = [crossing, crossing, 0];
// This value is to be added to every distance
var distance_add = ObjectDistance(crossing);
// Check every new destination
for (var list_item in new_list)
{
if (!list_item) continue;
// Destination is this
if (list_item[const_finaldestination] == this) continue;
// Check whether the destination is already in reach
if (!list_item)
continue;
// Destination may not be this crossing.
if (list_item[const_finaldestination] == this)
continue;
// Check whether the destination is already in already in the list.
var handled = false;
for (var i = 0, destination = false; i < GetLength(destination_list); i++)
for (var i = 0, destination = nil; i < GetLength(destination_list); i++)
{
if (!destination_list[i]) continue;
if (!destination_list[i])
continue;
destination = destination_list[i];
if (destination[const_finaldestination] == list_item[const_finaldestination])
{
@ -231,18 +235,55 @@ public func AddCableDestinations(array new_list, object crossing)
{
// It is shorter, replace, route through crossing
destination_list[i] = [list_item[const_finaldestination], crossing, list_item[const_distance] + distance_add];
// Inform the destination
// Inform the destination.
list_item[const_finaldestination]->UpdateCableDestination(this, crossing, distance_add);
}
}
}
// Destination is replaced or to be dismissed (because the new path would be longer), do nothing
if (handled) continue;
// Destination is a new one, add to the list
destination_list[GetLength(destination_list)] = [list_item[const_finaldestination], crossing, list_item[const_distance] + distance_add];
// Add me to the new destination (the way to me is the same than to crossing)
if (list_item[const_finaldestination] != crossing)
list_item[const_finaldestination]->AddCableDestination(this, crossing, distance_add);
// Destination is replaced or to be dismissed (because the new path would be longer), do nothing.
if (handled)
continue;
// Destination is a new one, add to the list.
AddCableDestination(list_item[const_finaldestination], crossing, list_item[const_distance] + distance_add);
// Add me to the new destination (the way to me is the same than to crossing).
if (list_item[const_finaldestination] != crossing && !has_reversed)
{
// This new crossing may have a bunch of other connections that need to be explored and potentially added.
// The new crossing is connected to this crossing via the crossing specified as the parameter to this function.
// So for the new destinations we want to add, we need to add the distance to the crossing.
var add_destinations = list_item[const_finaldestination]->GetDestinations();
var add_dest_distance = 0;
for (var dest in crossing->GetDestinations())
if (dest[const_finaldestination] == list_item[const_finaldestination])
add_dest_distance = dest[const_distance];
for (var dest in add_destinations)
dest[const_distance] += add_dest_distance;
this->AddCableDestinations(add_destinations, crossing, has_reversed);
// However, this crossing and its destinations must also be added in reverse to the new crossing found.
var reverse_destinations = this->GetDestinations();
PushBack(reverse_destinations, [this, crossing, 0]);
var reverse_crossing = nil;
for (var dest in list_item[const_finaldestination]->GetDestinations())
if (dest[const_finaldestination] == crossing)
reverse_crossing = dest[const_nextwaypoint];
if (reverse_crossing == nil)
return FatalError("AddCableDestinations(): reverse_crossing not found.");
var add_reverse_distance = distance_add;
if (reverse_crossing != crossing)
{
for (var dest in reverse_crossing->GetDestinations())
if (dest[const_finaldestination] == crossing)
add_reverse_distance += dest[const_distance];
if (add_reverse_distance == distance_add)
return FatalError("AddCableDestinations(): reverse_distance not correct.");
}
for (var dest in reverse_destinations)
dest[const_distance] += add_reverse_distance;
list_item[const_finaldestination]->AddCableDestinations(reverse_destinations, reverse_crossing, true);
}
}
DestinationsUpdated();
return true;
@ -253,28 +294,19 @@ public func AddCableDestinations(array new_list, object crossing)
* @param crossing The crossing which links to the new destination
* @param distance_add The distance between crossing and new_destination
*/
public func AddCableDestination(object new_destination, object crossing, int distance_add)
public func AddCableDestination(object new_destination, object crossing, int distance)
{
// Failsafe
if (!new_destination || !crossing) return false;
if (new_destination == this) return false;
// Find the entry of crossing to get the next waypoint and the distance
var crossing_item;
for (var list_item in destination_list)
{
if (!list_item) continue;
if (list_item[const_finaldestination] == crossing)
{
crossing_item = list_item;
break;
}
}
// Failsafe
if (!crossing_item) return false;
// Save the new destination
destination_list[GetLength(destination_list)] = [new_destination, crossing_item[const_nextwaypoint], crossing_item[const_distance] + distance_add];
// Failsafes.
if (!new_destination || !crossing)
return;
if (new_destination == this)
return;
if (!IsDirectlyConnectToCrossing(crossing))
return FatalError(Format("destination to %v for %v is not connected to crossing link %v.", new_destination, this, crossing));
// Add to destination list.
PushBack(destination_list, [new_destination, crossing, distance]);
DestinationsUpdated();
return true;
return;
}
/** Updates the path to \a known_destination via \a crossing (e.g. because the path is shorter through \a crossing)
@ -291,7 +323,8 @@ public func UpdateCableDestination(object known_destination, object crossing, in
var crossing_item, destination_item, i = 0;
for (var list_item in destination_list)
{
if (!list_item) continue;
if (!list_item)
continue;
if (list_item[const_finaldestination] == crossing)
crossing_item = list_item;
if (list_item[const_finaldestination] == known_destination)
@ -299,7 +332,8 @@ public func UpdateCableDestination(object known_destination, object crossing, in
i++;
}
// Failsafe
if (!crossing_item || !destination_item) return false;
if (!crossing_item || !destination_item)
return false;
// Save the updated path
destination_list[destination_item][const_nextwaypoint] = crossing_item[const_nextwaypoint];
destination_list[destination_item][const_distance] = crossing_item[const_distance] + distance_add;
@ -309,42 +343,68 @@ public func UpdateCableDestination(object known_destination, object crossing, in
local clearing;
/* Called automatically by Destruction, see description there
* @param crossing The calling crossing
*/
public func ClearConnections(object crossing)
// Updates the network for this crossing, this is called when a cable to this crossing has changed.
// It first clears every waypoint from the network and then renews the whole information.
public func UpdateConnections()
{
if (clearing) return;
// Clear this crossing and then other connected crossings.
ClearConnections();
RenewConnections();
return;
}
// Called automatically by UpdateConnections, see description there.
public func ClearConnections()
{
if (clearing)
return;
clearing = true;
destination_list = [];
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (!connection->~IsCableLine()) continue;
if (!connection->~IsCableLine())
continue;
var other_crossing = connection->~GetConnectedObject(this);
if (!other_crossing || !other_crossing->~IsCableCrossing()) continue;
if (!other_crossing || !other_crossing->~IsCableCrossing())
continue;
other_crossing->ClearConnections();
}
}
/* Called automatically by Destruction, see description there
* @param crossing The calling crossing
*/
public func RenewConnections(object crossing)
// Called automatically by UpdateConnections, see description there.
public func RenewConnections()
{
if (!clearing) return;
if (!clearing)
return;
clearing = false;
for(var connection in FindObjects(Find_Func("IsConnectedTo", this)))
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (!connection->~IsCableLine()) continue;
if (!connection->~IsCableLine())
continue;
var other_crossing = connection->~GetConnectedObject(this);
if (!other_crossing || !other_crossing->~IsCableCrossing()) continue;
if (other_crossing == crossing) continue;
destination_list[GetLength(destination_list)] = [other_crossing, other_crossing, ObjectDistance(other_crossing)];
if (!other_crossing || !other_crossing->~IsCableCrossing())
continue;
PushBack(destination_list, [other_crossing, other_crossing, ObjectDistance(other_crossing)]);
AddCableDestinations(other_crossing->GetDestinations(), other_crossing);
other_crossing->RenewConnections();
}
}
/*--- Pathfinding ---*/
// Returns whether there is a direct conenctions between this and the other crossing.
public func IsDirectlyConnectToCrossing(object other_crossing)
{
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (!connection->~IsCableLine())
continue;
if (other_crossing == connection->~GetConnectedObject(this))
return true;
}
return false;
}
/*-- Pathfinding --*/
/* Functions:
GetNextWaypoint(object end)
@ -356,10 +416,12 @@ public func RenewConnections(object crossing)
*/
public func GetNextWaypoint(object end)
{
if (!destination_list) return nil;
if (!destination_list)
return nil;
for (var item in destination_list)
{
if (!item) continue;
if (!item)
continue;
if (item[const_finaldestination] == end)
return item[const_nextwaypoint];
}
@ -372,16 +434,19 @@ public func GetNextWaypoint(object end)
*/
public func GetLengthToTarget(object end)
{
if (!destination_list) return nil;
if (!destination_list)
return nil;
for (var item in destination_list)
{
if (!item) continue;
if (!item)
continue;
if (item[const_finaldestination] == end)
return item[const_distance];
}
return nil;
}
/*-- Auto production --*/
local request_queue;
@ -410,12 +475,11 @@ public func AddRequest(proplist requested_id, int amount)
// Great. Start working immediately
PushBack(request_queue, [requested_id, amount]);
car->AddRequest(requested_id, amount, this, source);
return true;
}
// Check all connected stations for available objects
func CheckAvailability(proplist requested, int amount)
public func CheckAvailability(proplist requested, int amount)
{
var nearest_station;
var length_to;
@ -494,4 +558,4 @@ public func RemoveRequest(id requested, int amount)
return false;
RemoveArrayIndex(request_queue, i, true);
return true;
}
}

View File

@ -1,11 +1,11 @@
/*--
/**
Cable Crossing
The standard crossing for the cable network.
The crossing will automatically be a station if it is at the end of the cable line (i.e. only one cable connected).
But the crossing can also manually be set to function as a station.
Author: Clonkonaut
--*/
@author Clonkonaut
*/
#include Library_CableStation
@ -32,7 +32,8 @@ func Initialize()
// Prevents the automatic change of the station status when manually set to station mode
local manual_setting = false;
/* Library functions: Cable Station */
/*-- Library functions: Cable Station --*/
public func DestinationsUpdated()
{
@ -44,6 +45,11 @@ public func DestinationsUpdated()
else
SetCableStation(false);
CheckStationStatus();
// Inform all cars at station about the update.
for (var car in arrived_cars)
if (car)
car->~OnRailNetworkUpdate();
}
public func IsAvailable(proplist requested, int amount)
@ -107,7 +113,8 @@ public func OnCableCarDelivery(object car, id requested, int amount)
}
}
/* Construction */
/*-- Construction --*/
public func IsHammerBuildable() { return true; }
@ -124,7 +131,7 @@ public func ConstructionCombineOffset(proplist other)
return;
// Make sure the station preview is on the same ground level than the other building
var ret = [0,0];
var ret = [0, 0];
ret[1] = other->GetObjHeight()/2 - this->GetDefHeight()/2;
return ret;
}
@ -238,7 +245,9 @@ public func ToggleDropOff(bool silent)
if (!IsCableStation())
ToggleStation(true);
setting_dropoff = true;
} else {
}
else
{
setting_dropoff = false;
}
if (!silent)
@ -246,7 +255,8 @@ public func ToggleDropOff(bool silent)
CheckStationStatus();
}
/* Cable Car Management */
/*-- Cable Car Management --*/
public func OnCableCarArrival(object car)
{
@ -259,6 +269,11 @@ public func OnCableCarArrival(object car)
PushBack(arrived_cars, car);
}
public func OnCableCarStopped(object car)
{
PushBack(arrived_cars, car);
}
public func OnCableCarDeparture(object car)
{
RemoveArrayValue(arrived_cars, car, true);
@ -274,19 +289,23 @@ public func OnCableCarDisengaged(object car)
RemoveArrayValue(arrived_cars, car, true);
}
/* Visuals */
public func OnCableCarDestruction(object car)
{
RemoveArrayValue(arrived_cars, car, true);
}
func CheckStationStatus()
/*-- Visuals --*/
public func CheckStationStatus()
{
if (IsCableStation())
{
// In order of priority
if (setting_dropoff)
{
SetMeshMaterial("CableCarStation_SignDropOff", 1);
} else {
else
SetMeshMaterial("CableCarStation_SignStation", 1);
}
}
else
SetMeshMaterial("CableCarStation_Sign", 1);
@ -294,14 +313,14 @@ func CheckStationStatus()
local activations = 0;
func CableActivation(int count)
public func CableActivation(int count)
{
if (activations <= 0)
SetAnimationPosition(turn_anim, Anim_Linear(GetAnimationPosition(turn_anim), 0, GetAnimationLength("Engine"), 175, ANIM_Loop));
activations += count;
}
func CableDeactivation(int count)
public func CableDeactivation(int count)
{
activations -= count;
if (activations <= 0)
@ -310,9 +329,10 @@ func CableDeactivation(int count)
public func NoConstructionFlip() { return true; }
/* Saving */
func SaveScenarioObject(props)
/*-- Saving --*/
public func SaveScenarioObject(props)
{
if (!inherited(props, ...)) return false;
if (IsCableStation() && manual_setting)
@ -327,7 +347,8 @@ func SaveScenarioObject(props)
public func SetManual() { manual_setting = true; return true; }
/* Properties */
/*-- Properties --*/
local Name = "$Name$";
local BlastIncinerate = 50;

View File

@ -3,5 +3,7 @@ Name=Seilbahnkreuzung
StationSettings=Seilbahneinstellungen
TooltipToggleStation=Bahnhofs-Status umstellen.
TooltipToggleDropOff=Toggle item dropping status
DescToggleStation=Diese Kreuzung von Hand zu einem Bahnhof oder wieder zu einer Kreuzung umschalten. Nur Bahnhöfe sind als Ziel wählbar und nur am Bahnhöfen können Fahrzeuge aufgesetzt werden.
DescToggleStation=Diese Kreuzung von Hand zu einem Bahnhof oder wieder zu einer Kreuzung umschalten. Nur Bahnhöfe sind als Ziel wählbar und nur am Bahnhöfen können Fahrzeuge aufgesetzt werden.
DescToggleDropOff=All arriving vehicle are emptied upon arrival at this station (giving it is the final stop). Will also be set as a cable station.

View File

@ -1,20 +1,26 @@
/*-- Cable Hoist --*/
/**
Cable Hoist
@author Clonkonaut
*/
#include Library_CableCar
local pickup;
/* Creation */
func Construction()
/*-- Creation --*/
public func Construction()
{
SetProperty("MeshTransformation",Trans_Rotate(13,0,1,0));
this.MeshTransformation = Trans_Rotate(13, 0, 1, 0);
SetCableSpeed(1);
}
/* Engine Callbacks */
func Damage(int change, int cause)
/*-- Engine Callbacks --*/
public func Damage(int change, int cause)
{
if (cause == FX_Call_DmgBlast)
{
@ -26,25 +32,28 @@ func Damage(int change, int cause)
}
}
/* Status */
/*-- Status --*/
public func IsToolProduct() { return true; }
/* Cable Car Library */
func GetCableOffset(array position, int prec)
/*-- Cable Car Library --*/
public func GetCableOffset(array position, int prec)
{
if (!prec) prec = 1;
if (!prec)
prec = 1;
position[1] += 5 * prec;
}
func Engaged()
public func Engaged()
{
this.Touchable = 0;
SetAction("OnRail");
}
func Disengaged()
public func Disengaged()
{
this.Touchable = 1;
SetAction("Idle");
@ -52,9 +61,10 @@ func Disengaged()
DropVehicle();
}
func GetCableCarExtraMenuEntries(array menu_entries, proplist custom_entry, object clonk)
public func GetCableCarExtraMenuEntries(array menu_entries, proplist custom_entry, object clonk)
{
if (IsTravelling()) return;
if (IsTravelling())
return;
if (!pickup && GetRailTarget())
{
@ -103,7 +113,8 @@ public func OnCableCarHover(symbol, extra_data, desc_menu_target, menu_id)
return _inherited(symbol, extra_data, desc_menu_target, menu_id, ...);
}
/* Picking up vehicles */
/*-- Picking up vehicles --*/
public func PickupVehicle(object vehicle)
{
@ -155,9 +166,10 @@ local FxCableHoistPickup = new Effect
}
};
/* Actions */
func OnRail()
/*-- Actions --*/
public func OnRail()
{
DoMovement();
}
@ -180,7 +192,8 @@ local ActMap = {
},
};
/* Callbacks */
/*-- Callbacks --*/
public func GetAttachedVehicle()
{
@ -230,15 +243,15 @@ public func OverridePriority(proplist requested, int amount, proplist requesting
// Check if the connected vehicle holds the requested objects and if yes, override the selection
if (pickup && pickup->ContentsCount(requested) >= amount)
return true;
return false;
}
/* Definition */
func Definition(def)
/*-- Definition --*/
public func Definition(def)
{
SetProperty("PictureTransformation",Trans_Mul(Trans_Rotate(-25,1,0,0),Trans_Rotate(40,0,1,0)),def);
def.PictureTransformation = Trans_Mul(Trans_Rotate(-25, 1, 0, 0), Trans_Rotate(40, 0, 1, 0));
}
local Name = "$Name$";

View File

@ -12,23 +12,23 @@ local empty_anim;
/*-- Engine Callbacks --*/
func Construction()
public func Construction()
{
SetProperty("MeshTransformation", Trans_Rotate(13, 0, 1, 0));
}
func Initialize()
public func Initialize()
{
drive_anim = PlayAnimation("Drive", 1, Anim_X(0,0, GetAnimationLength("Drive"), 30), Anim_Const(1000));
empty_anim = PlayAnimation("Empty", 2, Anim_Const(0), Anim_Const(0));
}
func Hit3()
public func Hit3()
{
Sound("Hits::Materials::Metal::DullMetalHit?");
}
func RejectCollect(id object_id, object obj)
public func RejectCollect(id object_id, object obj)
{
// Collection maybe blocked if this object was just dumped.
if (!obj->Contained() && GetEffect("BlockCollectionByLorry", obj))
@ -59,7 +59,7 @@ func RejectCollect(id object_id, object obj)
}
// Automatic unloading in buildings.
func Entrance(object container)
public func Entrance(object container)
{
// Only in buildings
if (container->GetCategory() & (C4D_StaticBack | C4D_Structure))
@ -69,19 +69,19 @@ func Entrance(object container)
container->GrabContents(this);
}
func ContactLeft()
public func ContactLeft()
{
if (Stuck() && !Random(5))
SetRDir(RandomX(-7, +7));
}
func ContactRight()
public func ContactRight()
{
if (Stuck() && !Random(5))
SetRDir(RandomX(-7, +7));
}
func Damage(int change, int cause, int by_player)
public func Damage(int change, int cause, int by_player)
{
// Only explode the lorry on blast damage.
if (cause != FX_Call_DmgBlast)
@ -144,7 +144,7 @@ public func DropContents(proplist station)
ScheduleCall(this, "Empty", 35);
}
func Empty()
public func Empty()
{
// Exit everything at once (as opposed to manual dumping below)
while (Contents())

View File

@ -144,6 +144,7 @@ func InitializeObjects()
CableLine019->SetConnectedObjects(CableCrossing019, CableCrossing003);
var ToolsWorkshop001 = CreateObjectAbove(ToolsWorkshop, 76, 388);
CableCrossing019->CombineWith(ToolsWorkshop001);
var CableHoist001 = CreateObjectAbove(CableHoist, 560, 384);
CableHoist001->SetComDir(COMD_None);

View File

@ -150,6 +150,10 @@ protected func InitializePlayer(int plr)
// No FOW here.
//SetFoW(false, plr);
JoinPlayer(plr);
// Give all knowledge.
var index = 0, def;
while (def = GetDefinition(index++))
SetPlrKnowledge(plr, def);
return;
}

View File

@ -56,10 +56,10 @@ protected func InitializePlayer(int plr)
SetPlrKnowledge(plr, def);
// Add test control effect.
var effect = AddEffect("IntTestControl", nil, 100, 2);
effect.testnr = 1;
effect.launched = false;
effect.plr = plr;
var fx = AddEffect("IntTestControl", nil, 100, 2);
fx.testnr = 7;
fx.launched = false;
fx.plr = plr;
return;
}
@ -191,7 +191,7 @@ global func Test1_OnStart(int plr)
workshop->AddToQueue(Shovel, 2);
// Log what the test is about.
Log("A workshop needs material to produce 2 shovels, lorry in system has materials.");
Log("A workshop needs material to produce shovels, lorry in system has materials.");
return true;
}
@ -288,11 +288,13 @@ global func Test3_OnStart(int plr)
var workshop = CreateObjectAbove(ToolsWorkshop, 40, 160, plr);
crossing1->CombineWith(workshop);
workshop->AddToQueue(Shovel, 2);
workshop->AddToQueue(Shovel, 1);
ScheduleCall(line_to_break, "OnLineBreak", 36, 0, true);
ScheduleCall(line_to_break, "RemoveObject", 37, 0, true);
ScheduleCall(nil, "PrintCableCarNetwork", 38, 0);
ScheduleCall(nil, "CreateCableCrossingsConnection", 240, 0, crossing2, crossing3);
ScheduleCall(nil, "PrintCableCarNetwork", 241, 0);
// Log what the test is about.
Log("Check if a delivery is continued when a cable breaks and is repaired.");
@ -301,7 +303,7 @@ global func Test3_OnStart(int plr)
global func Test3_Completed()
{
if (ObjectCount(Find_ID(Shovel)) >= 2)
if (ObjectCount(Find_ID(Shovel)) >= 1)
return true;
return false;
}
@ -319,26 +321,30 @@ global func Test4_OnStart(int plr)
var crossing1 = CreateObjectAbove(CableCrossing, 70, 160, plr);
var crossing2 = CreateObjectAbove(CableCrossing, 216, 64, plr);
var crossing3 = CreateObjectAbove(CableCrossing, 272, 64, plr);
var crossing4 = CreateObjectAbove(CableCrossing, 450, 104, plr);
var crossing3 = CreateObjectAbove(CableCrossing, 244, 64, plr);
var crossing4 = CreateObjectAbove(CableCrossing, 272, 64, plr);
var crossing5 = CreateObjectAbove(CableCrossing, 450, 104, plr);
CreateCableCrossingsConnection(crossing1, crossing2);
CreateCableCrossingsConnection(crossing2, crossing3);
CreateCableCrossingsConnection(crossing3, crossing4);
CreateCableCrossingsConnection(crossing4, crossing5);
var hoist = crossing4->CreateObject(CableHoist);
hoist->EngageRail(crossing4);
var lorry = crossing4->CreateObject(CableLorry);
var hoist = crossing5->CreateObject(CableHoist);
hoist->EngageRail(crossing5);
var lorry = crossing5->CreateObject(CableLorry);
hoist->PickupVehicle(lorry);
lorry->CreateContents(Metal, 2);
lorry->CreateContents(Wood, 2);
var workshop = CreateObjectAbove(ToolsWorkshop, 40, 160, plr);
crossing1->CombineWith(workshop);
workshop->AddToQueue(Shovel, 2);
workshop->AddToQueue(Shovel, 1);
ScheduleCall(crossing2, "RemoveObject", 36, 0, true);
ScheduleCall(nil, "CreateCableCrossingsConnection", 240, 0, crossing1, crossing3);
ScheduleCall(crossing3, "RemoveObject", 36, 0, true);
ScheduleCall(nil, "PrintCableCarNetwork", 37, 0);
ScheduleCall(nil, "CreateCableCrossingsConnection", 240, 0, crossing2, crossing4);
ScheduleCall(nil, "PrintCableCarNetwork", 241, 0);
// Log what the test is about.
Log("Check if a delivery is continued when a station is destroyed.");
@ -347,7 +353,7 @@ global func Test4_OnStart(int plr)
global func Test4_Completed()
{
if (ObjectCount(Find_ID(Shovel)) >= 2)
if (ObjectCount(Find_ID(Shovel)) >= 1)
return true;
return false;
}
@ -358,7 +364,6 @@ global func Test4_OnFinished()
return;
}
global func Test5_OnStart(int plr)
{
SetWindFixed(50);
@ -464,7 +469,7 @@ global func Test6_OnStart(int plr)
var drain = CreateObjectAbove(Pipe, 80, 300, plr);
drain->ConnectPipeTo(pump, PIPE_STATE_Drain);
Schedule(nil, "CreateObject(Dynamite, 482, 266)->Fuse()", 180, 10**6);
Schedule(nil, "CreateObject(Rock, 480, 274)", 36, 10**6);
// Log what the test is about.
Log("Test automated concrete production line.");
@ -539,6 +544,141 @@ global func Test7_OnFinished()
return;
}
global func Test8_OnStart(int plr)
{
ClearFreeRect(0, 0, LandscapeWidth(), LandscapeHeight());
var nr_crossings = RandomX(6, 12);
var connect_chance = 20; // In percent.
var start_time = GetTime();
var crossings = [];
for (var cnt = 0; cnt < nr_crossings; cnt++)
PushBack(crossings, CreateObjectAbove(CableCrossing, RandomX(10, LandscapeWidth() - 10), RandomX(20, LandscapeHeight() - 20), plr));
var nr_connections = 0;
for (var c1 in crossings)
{
for (var c2 in crossings)
{
if (c1 != c2 && Random(100) < connect_chance)
{
CreateCableCrossingsConnection(c1, c2);
nr_connections++;
}
}
}
// Log what the test is about.
Log("Test a random network for symmetric distance measures.");
var time = GetTime() - start_time;
Log("It took %d ms to create %d stations with %d connections.", time, nr_crossings, nr_connections);
return true;
}
global func Test8_Completed()
{
if (IsSymmetricCableCarNetwork())
return true;
return false;
}
global func Test8_OnFinished()
{
RemoveTestObjects();
return;
}
/*-- Cable Network Functions --*/
global func CreateCableCrossingsConnection(object c1, object c2)
{
var cable = c1->CreateObject(CableLine);
cable->SetConnectedObjects(c1, c2);
// Log the distance the cable covers of the cable.
var dummy = CreateObject(Dummy, (c1->GetX() + c2->GetX()) / 2, (c1->GetY() + c2->GetY()) / 2);
dummy->SetCategory(C4D_StaticBack);
dummy.Visibility = VIS_All;
dummy->Message("@<c 141432>%d</c>", ObjectDistance(c1, c2));
cable->CreateEffect(FxRemoveWith, 1, 0, dummy);
return cable;
}
static const FxRemoveWith = new Effect
{
Construction = func(object to_remove)
{
this.to_remove = to_remove;
},
Destruction = func()
{
this.to_remove->RemoveObject();
}
};
global func CreateObjectAbove(id obj, ...)
{
var res = _inherited(obj, ...);
if (obj == CableCrossing)
res->Message("@<c aa0000>%d</c>", res->ObjectNumber());
return res;
}
global func PrintCableCarNetwork()
{
Log("Distances between all of the cable crossings:");
var cable_crossings = FindObjects(Find_Func("IsCableCrossing"));
var header = "Obj# |";
var line = "------";
for (var crossing in cable_crossings)
{
//Log("%v: %v", crossing, crossing->GetDestinations());
header = Format("%s %04d", header, crossing->ObjectNumber());
line = Format("%s-----", line);
}
Log(header);
Log(line);
for (var crossing1 in cable_crossings)
{
var msg = Format("%04d |", crossing1->ObjectNumber());
for (var crossing2 in cable_crossings)
{
var len = crossing1->GetLengthToTarget(crossing2);
msg = Format("%s %04d", msg, len);
}
Log(msg);
}
Log(line);
if (!IsSymmetricCableCarNetwork())
Log("WARNING: distance table is not symmetric.");
return;
}
global func IsSymmetricCableCarNetwork()
{
var cable_crossings = FindObjects(Find_Func("IsCableCrossing"));
var dist_table = [];
var index1 = 0;
for (var crossing1 in cable_crossings)
{
var index2 = 0;
dist_table[index1] = [];
for (var crossing2 in cable_crossings)
{
var len = crossing1->GetLengthToTarget(crossing2);
dist_table[index1][index2] = len;
index2++;
}
index1++;
}
var is_symmetric = true;
for (var index1 = 0; index1 < GetLength(dist_table); index1++)
for (var index2 = index1 + 1; index2 < GetLength(dist_table); index2++)
if (dist_table[index1][index2] != dist_table[index2][index1])
is_symmetric = false;
return is_symmetric;
}
/*-- Helper Functions --*/
@ -608,40 +748,3 @@ global func RemoveTestObjects()
));
return;
}
global func CreateCableCrossingsConnection(object c1, object c2)
{
var cable = c1->CreateObject(CableLine);
cable->SetConnectedObjects(c1, c2);
return cable;
}
global func PrintCableCarNetwork()
{
Log("Distances between all of the cable crossings:");
var cable_crossings = FindObjects(Find_Func("IsCableCrossing"));
var header = "Obj# |";
var line = "------";
for (var crossing in cable_crossings)
{
header = Format("%s %04d", header, crossing->ObjectNumber());
line = Format("%s-----", line);
}
Log(Format("%s tot", header));
Log(Format("%s-----", line));
for (var crossing1 in cable_crossings)
{
var msg = Format("%04d |", crossing1->ObjectNumber());
var sum = 0;
for (var crossing2 in cable_crossings)
{
var len = crossing1->GetLengthToTarget(crossing2);
sum += len;
msg = Format("%s %04d", msg, len);
}
msg = Format("%s %04d", msg, sum);
Log(msg);
}
return;
}