forked from Mirrors/openclonk
403 lines
10 KiB
C
403 lines
10 KiB
C
/**
|
|
Pipe
|
|
|
|
Author: ST-DDT, Marky
|
|
|
|
The pipe has several states:
|
|
- neutral
|
|
- source (green line)
|
|
- drain (red line)
|
|
|
|
By default a pipe is neutral, and stays neutral if you connect it to a liquid tank.
|
|
- Connected neutral pipes cannot be connected to another (neutral) liquid tank
|
|
(this would not make any sense, or maybe it does if you want to have a liquid
|
|
tank array, but this would lead to all kinds of complications and special
|
|
cases eventually).
|
|
|
|
However, a pipe can be connected to a liquid transferring structure (pump), too.
|
|
- Neutral pipes can be connected to a pump. They then become a drain or source pipe.
|
|
- Connected neutral pipes can be connected to a pump. See above.
|
|
- Source pipes can be connected to liquid tanks only.
|
|
- Drain pipes can be connected to liquid tanks only.
|
|
|
|
Logic for object use from inventory and interaction menu:
|
|
- Both know the using Clonk (interaction menu: the container of the pipe)
|
|
- The user may want to connect a drain pipe before connecting a source pipe
|
|
- The user may want to connect a neutral pipe
|
|
=> separate functions are necessary
|
|
*/
|
|
|
|
static const PIPE_STATE_Neutral = nil;
|
|
static const PIPE_STATE_Source = "Source";
|
|
static const PIPE_STATE_Drain = "Drain";
|
|
static const PIPE_STATE_Air = "Air";
|
|
|
|
local pipe_state = nil;
|
|
|
|
local pipe_line;
|
|
|
|
local ApertureOffsetX = 0;
|
|
local ApertureOffsetY = 3;
|
|
|
|
/* ---------- Callbacks ---------- */
|
|
|
|
protected func Hit()
|
|
{
|
|
Sound("Hits::GeneralHit?");
|
|
}
|
|
|
|
private func Destruction()
|
|
{
|
|
// Remove the line first, so that it does not provoke errors on destruction.
|
|
// Actually there is an ill-defined state where line contains the pipe and is
|
|
// removed. Then line = GetConnectedLine() causes an error, instead use the
|
|
// slower find object variant.
|
|
var line = FindObject(Find_Func("IsConnectedTo", this));
|
|
if (line)
|
|
line->RemoveObject();
|
|
return;
|
|
}
|
|
|
|
|
|
public func IsToolProduct() { return true;}
|
|
|
|
public func OnPipeLineRemoval()
|
|
{
|
|
SetNeutralPipe();
|
|
OnPipeLengthChange();
|
|
return;
|
|
}
|
|
|
|
public func OnPipeLengthChange()
|
|
{
|
|
// Update usage bar for a possible carrier (the clonk).
|
|
var carrier = Contained();
|
|
if (carrier)
|
|
carrier->~OnInventoryChange();
|
|
return;
|
|
}
|
|
|
|
// Display the line length bar over the pipe icon.
|
|
public func GetInventoryIconOverlay()
|
|
{
|
|
var line = GetConnectedLine();
|
|
if (!line) return;
|
|
|
|
var percentage = 100 * line->GetPipeLength() / line.PipeMaxLength;
|
|
var red = percentage * 255 / 100;
|
|
var green = 255 - red;
|
|
// Overlay a usage bar.
|
|
var overlay =
|
|
{
|
|
Bottom = "0.75em",
|
|
Margin = ["0.1em", "0.25em"],
|
|
BackgroundColor = RGB(0, 0, 0),
|
|
margin =
|
|
{
|
|
Margin = "0.05em",
|
|
bar =
|
|
{
|
|
BackgroundColor = RGB(red, green, 0),
|
|
Right = Format("%d%%", percentage),
|
|
}
|
|
}
|
|
};
|
|
return overlay;
|
|
}
|
|
|
|
public func CanBeStackedWith(object other)
|
|
{
|
|
// Do not stack source/drain/unused pipes
|
|
return _inherited(other) && (pipe_state == other.pipe_state);
|
|
}
|
|
|
|
|
|
/**
|
|
The pump calls this function to prevent clogging of the intake.
|
|
Cycles through several aperture offset indices.
|
|
*/
|
|
public func HasAperture() { return true; }
|
|
|
|
public func CycleApertureOffset()
|
|
{
|
|
// Cycle in three steps of three px each through X and Y
|
|
// covering a 3x3 grid on points -3,0,+3
|
|
ApertureOffsetX = (ApertureOffsetX + 6) % 9 - 3;
|
|
if (!ApertureOffsetX) ApertureOffsetY = (ApertureOffsetY + 6) % 9 - 3;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Container dies: Drop connected pipes so they don't
|
|
draw huge lines over the landscape
|
|
*/
|
|
func IsDroppedOnDeath(object clonk)
|
|
{
|
|
return !!GetConnectedLine();
|
|
}
|
|
|
|
|
|
/* ---------- Pipe States ---------- */
|
|
|
|
|
|
public func IsNeutralPipe() { return pipe_state == PIPE_STATE_Neutral; }
|
|
public func IsDrainPipe() { return pipe_state == PIPE_STATE_Drain; }
|
|
public func IsSourcePipe() { return pipe_state == PIPE_STATE_Source; }
|
|
public func IsAirPipe() { return pipe_state == PIPE_STATE_Air; }
|
|
|
|
public func GetPipeState() { return pipe_state; }
|
|
|
|
public func SetNeutralPipe()
|
|
{
|
|
pipe_state = PIPE_STATE_Neutral;
|
|
|
|
SetGraphics("", nil, GFX_Overlay, GFXOV_MODE_Picture);
|
|
Description = "$Description$";
|
|
Name = "$Name$";
|
|
|
|
var line = GetConnectedLine();
|
|
if (line)
|
|
line->SetNeutral();
|
|
}
|
|
|
|
public func SetDrainPipe()
|
|
{
|
|
pipe_state = PIPE_STATE_Drain;
|
|
|
|
SetGraphics("Drain", Pipe, GFX_Overlay, GFXOV_MODE_Picture);
|
|
SetObjDrawTransform(1000, 0, 0, 0, 1000, 10000, GFX_Overlay);
|
|
Description = "$DescriptionDrain$";
|
|
Name = "$NameDrain$";
|
|
|
|
var line = GetConnectedLine();
|
|
if (line)
|
|
line->SetDrain();
|
|
}
|
|
|
|
public func SetSourcePipe()
|
|
{
|
|
pipe_state = PIPE_STATE_Source;
|
|
|
|
SetGraphics("Source", Pipe, GFX_Overlay, GFXOV_MODE_Picture);
|
|
SetObjDrawTransform(1000, 0, 0, 0, 1000, 10000, GFX_Overlay);
|
|
Description = "$DescriptionSource$";
|
|
Name = "$NameSource$";
|
|
|
|
var line = GetConnectedLine();
|
|
if (line)
|
|
line->SetSource();
|
|
}
|
|
|
|
public func SetAirPipe()
|
|
{
|
|
pipe_state = PIPE_STATE_Air;
|
|
|
|
SetGraphics("Air", Pipe, GFX_Overlay, GFXOV_MODE_Picture);
|
|
SetObjDrawTransform(1000, 0, 0, 0, 1000, 10000, GFX_Overlay);
|
|
Description = "$DescriptionAir$";
|
|
Name = "$NameAir$";
|
|
|
|
var line = GetConnectedLine();
|
|
if (line)
|
|
line->SetAir();
|
|
}
|
|
|
|
/* ---------- Pipe Connection ---------- */
|
|
|
|
|
|
public func ConnectPipeTo(object target, string specific_pipe_state, bool block_cutting)
|
|
{
|
|
if (!target || target->~QueryConnectPipe(this, true))
|
|
return false;
|
|
AddLineConnectionTo(target, block_cutting);
|
|
target->OnPipeConnect(this, specific_pipe_state);
|
|
Sound("Objects::Connect");
|
|
return true;
|
|
}
|
|
|
|
/* ---------- Line Connection ---------- */
|
|
|
|
|
|
public func SetPipeLine(to_line)
|
|
{
|
|
pipe_line = to_line;
|
|
}
|
|
|
|
/**
|
|
Finds a line that is connected to this pipe kit.
|
|
@return object the pipe, or nil if nothing was found.
|
|
*/
|
|
public func GetConnectedLine()
|
|
{
|
|
return pipe_line;
|
|
}
|
|
|
|
|
|
/**
|
|
Connects a line to an object.
|
|
|
|
The pipe kit will connect the line to the target object and itself first.
|
|
Otherwise, if the pipe kit already has a line, it connects that line to the target.
|
|
|
|
Note: Reports a fatal error if the line would be connected to more than two targets
|
|
at the same time.
|
|
|
|
@par target the target object
|
|
*/
|
|
public func AddLineConnectionTo(object target, bool block_cutting)
|
|
{
|
|
var line = GetConnectedLine();
|
|
if (line)
|
|
{
|
|
if (line->IsConnectedTo(this, true))
|
|
{
|
|
line->SwitchConnection(this, target);
|
|
SetPipeLine(line);
|
|
line.BlockPipeCutting = block_cutting;
|
|
// Delayed entrance, so that the message is still displayed above the clonk.
|
|
ScheduleCall(this, this.Enter, 1, nil, line);
|
|
return line;
|
|
}
|
|
else
|
|
{
|
|
FatalError("This line is connected to two objects already!");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return CreateLine(target, block_cutting);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Cuts the connection between the line and an object.
|
|
|
|
Note: Reports a fatal error if the target was not
|
|
connected to the line.
|
|
|
|
@par target the target object
|
|
@par possible_container a container that should try to collect the pipe.
|
|
*/
|
|
public func CutLineConnection(object target, object possible_container)
|
|
{
|
|
var line = GetConnectedLine();
|
|
if (!line) return;
|
|
var allow_pickup = false;
|
|
|
|
// Connected only to the kit and a structure
|
|
if (line->IsConnectedTo(this, true))
|
|
{
|
|
target->OnPipeDisconnect(this);
|
|
line->RemoveObject();
|
|
// Collection by container, should be near enough or else you'd be able to
|
|
// retrieve a line where the line kit is down a well, but you simply detach it from the pump...
|
|
// Radius is the same as picking up objects via the surrounding
|
|
allow_pickup = ObjectDistance(possible_container, this) <= Helper_Surrounding.Radius;
|
|
}
|
|
// Connected to the target and another structure
|
|
else if (line->IsConnectedTo(target, true))
|
|
{
|
|
target->OnPipeDisconnect(this);
|
|
Exit(); // The kit was inside the line at this point.
|
|
SetPosition(target->GetX(), target->GetY() + target->GetBottom() - this->GetBottom());
|
|
line->SwitchConnection(target, this);
|
|
SetPipeLine(line);
|
|
// Can be picked up always
|
|
allow_pickup = true;
|
|
}
|
|
else
|
|
{
|
|
FatalError(Format("An object %v is trying to cut the pipe connection, but only objects %v and %v may request a disconnect", target, line->GetActionTarget(0), line->GetActionTarget(1)));
|
|
}
|
|
// Pick it up
|
|
if (possible_container && allow_pickup)
|
|
{
|
|
possible_container->Collect(this);
|
|
}
|
|
}
|
|
|
|
// Returns whether the cutting pipe is blocked.
|
|
public func QueryCutLineConnection(object target)
|
|
{
|
|
var line = GetConnectedLine();
|
|
if (!line)
|
|
return false;
|
|
return line.BlockPipeCutting;
|
|
}
|
|
|
|
/**
|
|
Creates a new pipe line that is connected to this pipe kit.
|
|
@par target the target object.
|
|
@return object the line that was created
|
|
*/
|
|
public func CreateLine(object target, bool block_cutting)
|
|
{
|
|
// Create and connect pipe line.
|
|
pipe_line = CreateObject(PipeLine, 0, 0, NO_OWNER);
|
|
pipe_line->SetActionTargets(this, target);
|
|
pipe_line->SetPipeKit(this);
|
|
pipe_line.BlockPipeCutting = block_cutting;
|
|
return pipe_line;
|
|
}
|
|
|
|
|
|
/** Will connect liquid line to building at the clonk's position. */
|
|
protected func ControlUse(object clonk, int x, int y)
|
|
{
|
|
var target = FindObject(Find_AtPoint(), Find_Func("CanConnectPipe"));
|
|
if (target)
|
|
{
|
|
ConnectPipeTo(target, GetPipeState());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Returns to which object the given object is connected through this pipe.
|
|
public func GetConnectedObject(object obj)
|
|
{
|
|
if (!pipe_line)
|
|
return;
|
|
return pipe_line->GetConnectedObject(obj);
|
|
}
|
|
|
|
/**
|
|
Displays a message at top-level container of this object.
|
|
@par message the message
|
|
*/
|
|
public func Report(string message)
|
|
{
|
|
var reporter = this;
|
|
var next = Contained();
|
|
|
|
while (next)
|
|
{
|
|
reporter = next;
|
|
next = reporter->Contained();
|
|
}
|
|
|
|
reporter->Message(message, ...);
|
|
}
|
|
|
|
|
|
/*-- Saving --*/
|
|
|
|
public func SaveScenarioObject(props)
|
|
{
|
|
if (!inherited(props, ...)) return false;
|
|
if (pipe_line) props->AddCall("PipeLine", this, "SetPipeLine", pipe_line);
|
|
if (IsNeutralPipe()) props->AddCall("PipeStateNeutral", this, "SetNeutralPipe");
|
|
else if (IsDrainPipe()) props->AddCall("PipeStateDrain", this, "SetDrainPipe");
|
|
else if (IsSourcePipe()) props->AddCall("PipeStateSource", this, "SetSourcePipe");
|
|
else if (IsAirPipe()) props->AddCall("PipeStateAir", this, "SetAirPipe");
|
|
return true;
|
|
}
|
|
|
|
|
|
/*-- Properties --*/
|
|
|
|
local Name = "$Name$";
|
|
local Description = "$Description$";
|
|
local Collectible = 1;
|
|
local Components = {Metal = 1};
|