openclonk/planet/Objects.ocd/Items.ocd/Tools.ocd/Pipe.ocd/Script.c

332 lines
7.4 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";
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
var line = GetConnectedLine();
if (line) line->RemoveObject();
}
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 pipe = GetConnectedLine();
if (!pipe) return;
var percentage = 100 * pipe->GetPipeLength() / pipe.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) && (PipeState == other.PipeState);
}
/**
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 ---------- */
func IsNeutralPipe(){ return PipeState == PIPE_STATE_Neutral;}
func IsDrainPipe(){ return PipeState == PIPE_STATE_Drain;}
func IsSourcePipe(){ return PipeState == PIPE_STATE_Source;}
func SetNeutralPipe()
{
PipeState = PIPE_STATE_Neutral;
SetGraphics("", nil, GFX_Overlay, GFXOV_MODE_Picture);
Description = "$Description$";
Name = "$Name$";
var line = GetConnectedLine();
if (line)
{
line->SetNeutral();
}
}
func SetDrainPipe()
{
PipeState = 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();
}
}
func SetSourcePipe()
{
PipeState = 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();
}
}
/* ---------- Pipe Connection ---------- */
func ConnectPipeTo(object target, string specific_pipe_state)
{
if (!target || target->~QueryConnectPipe(this)) return false;
AddLineConnectionTo(target);
target->OnPipeConnect(this, specific_pipe_state);
Sound("Objects::Connect");
return true;
}
/* ---------- Line Connection ---------- */
/**
Finds a line that is connected to this pipe kit.
@return object the pipe, or nil if nothing was found.
*/
func GetConnectedLine()
{
return FindObject(Find_Func("IsConnectedTo", this));
}
/**
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
*/
func AddLineConnectionTo(object target)
{
var line = GetConnectedLine();
if (line)
{
if (line->IsConnectedTo(this, true))
{
line->SwitchConnection(this, target);
ScheduleCall(this, this.Enter, 1, nil, line); // delayed entrance, so that the message is still displayed above the clonk
return line;
}
else
{
FatalError("This line is connected to two objects already!");
}
}
else
{
return CreateLine(target);
}
}
/**
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
*/
func CutLineConnection(object target)
{
var line = GetConnectedLine();
if (!line) return;
// connected only to the kit and a structure
if (line->IsConnectedTo(this, true))
{
target->OnPipeDisconnect(this);
line->RemoveObject();
}
// 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());
line->SwitchConnection(target, this);
}
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)));
}
}
/**
Creates a new pipe line that is connected to this pipe kit.
@par target the target object.
@return object the line that was created
*/
func CreateLine(object target)
{
// Create and connect pipe line.
var line = CreateObject(PipeLine, 0, 0, NO_OWNER);
line->SetActionTargets(this, target);
line->SetPipeKit(this);
return 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);
}
return true;
}
/**
Displays a message at top-level container of this object.
@par message the message
*/
func Report(string message)
{
var reporter = this;
var next = Contained();
while(next)
{
reporter = next;
next = reporter->Contained();
}
reporter->Message(message, ...);
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 1;
local PipeState = nil;
local Components = {Metal = 1};