openclonk/planet/Objects.ocd/Structures.ocd/Pump.ocd/Script.c

393 lines
8.4 KiB
C
Raw Normal View History

/*--
Pump
Author: Maikel, ST-DDT, Sven2, Newton
Pumps liquids using drain and source pipes. Features include:
+ switch on and off
+ consume/produce a variable amount of power depending on the height of
source and drain
--*/
#include Library_Structure
#include Library_Ownable
#include Library_PowerConsumer
#include Library_PowerProducer
local Name = "$Name$";
local Description = "$Description$";
local BlastIncinerate = 50;
local HitPoints = 70;
/*
States
"Wait": turned off or source pipe not connected
"WaitForPower": turned on but no power (does consume power)
"WaitForLiquid": turned on but no liquid (does not consume power)
"Pump": currently working and consuming/producing power
*/
local ActMap = {
Pump = {
Prototype = Action,
Name = "Pump",
Length = 30,
Delay = 3,
NextAction = "Pump",
StartCall = "CheckState",
PhaseCall = "Pumping"
},
Wait = {
Prototype = Action,
Name = "Wait",
Delay = 90,
NextAction = "Wait",
EndCall = "CheckState"
},
WaitForPower = {
Prototype = Action,
Name = "WaitForPower",
Delay = 150,
NextAction = "WaitForPower",
EndCall = "CheckState"
},
WaitForLiquid = {
Prototype = Action,
Name = "WaitForLiquid",
Delay = 30,
NextAction = "WaitForLiquid",
EndCall = "CheckState"
}
};
local animation; // animation handle
local switched_on; // the pump can be switched on and off with interact
local powered; // whether the pump is powered (either has enough power as consumer or even produces power)
local stored_material_index; //contained liquid
local stored_material_amount;
local source_pipe;
local drain_pipe;
2013-05-25 15:58:19 +00:00
/** This object is a liquid pump, thus pipes can be connected. */
public func IsLiquidPump() { return true; }
func Definition(def)
{
// for title image
SetProperty("PictureTransformation",Trans_Rotate(50,0,1,0),def);
// for building preview
SetProperty("MeshTransformation",Trans_Rotate(50,0,1,0),def);
}
func Construction()
{
// Rotate at a 45 degree angle towards viewer and add a litte bit of Random
this.MeshTransformation = Trans_Rotate(50 + RandomX(-10,10),0,1,0);
}
func Initialize()
{
switched_on = true;
var start = 0;
var end = GetAnimationLength("pump");
animation = PlayAnimation("pump", 5, Anim_Linear(GetAnimationPosition(animation), start, end, 35, ANIM_Loop), Anim_Const(1000));
SetState("Wait");
}
/*-- Interaction --*/
public func IsInteractable() { return GetCon() >= 100; }
public func GetInteractionMetaInfo(object clonk)
{
if (switched_on)
return { Description = "$MsgTurnOff$", IconName = nil, IconID = Icon_Stop };
2013-05-25 15:58:19 +00:00
else
return { Description = "$MsgTurnOn$", IconName = nil, IconID = Icon_Play };
}
2013-05-25 15:58:19 +00:00
/** Turn on or off. */
public func Interact(object clonk)
{
switched_on = !switched_on;
2013-05-25 15:58:19 +00:00
CheckState();
return true;
}
/*-- Pipe connection --*/
public func GetSource() { return source_pipe; }
public func SetDrain(object pipe) { drain_pipe = pipe; }
public func GetDrain() { return drain_pipe; }
public func SetSource(object pipe)
{
Log("source pipe connected");
source_pipe = pipe;
2013-05-25 15:58:19 +00:00
CheckState();
}
2013-05-25 15:58:19 +00:00
/*-- Power stuff --*/
func QueryWaivePowerRequest()
{
// has less priority than other objects, but not too low
return 10;
}
func OnNotEnoughPower()
{
Log("not enough power");
powered = false;
2013-05-25 15:58:19 +00:00
CheckState();
return _inherited(...);
}
func OnEnoughPower()
{
Log("enough power");
powered = true;
2013-05-25 15:58:19 +00:00
CheckState();
return _inherited(...);
}
2013-05-25 15:58:19 +00:00
/** Returns object to which the liquid is pumped */
private func GetDrainObject()
{
if (drain_pipe) return drain_pipe->GetConnectedObject(this) ?? this;
return this;
}
2011-10-11 22:32:46 +00:00
2013-05-25 15:58:19 +00:00
/** Returns object to which the liquid is pumped */
private func GetSourceObject()
{
if (source_pipe) return source_pipe->GetConnectedObject(this) ?? this;
return this;
}
/** Returns amount of pixels to pump per 30 frames */
public func GetPumpSpeed()
{
return 50;
}
2013-05-25 15:58:19 +00:00
/** PhaseCall of Pump: Pump the liquid from the source to the drain pipe */
protected func Pumping()
{
// at this point we can assert that we have power
// something went wrong in the meantime?
// let the central function handle that on next check
if (!source_pipe) return;
var pump_ok = true;
// is empty? -> try to get liquid
if (!stored_material_amount)
2011-10-11 22:32:46 +00:00
{
// get new materials
var aMat = GetSourceObject()->ExtractLiquidAmount(0,0, GetPumpSpeed()/10);
// no material to pump?
if (aMat)
{
stored_material_index = aMat[0];
stored_material_amount = aMat[1];
}
else
{
pump_ok = false;
}
2011-10-11 22:32:46 +00:00
}
if (pump_ok)
{
var i = stored_material_amount;
while (i > 0)
{
if (GetDrainObject()->InsertMaterial(stored_material_index))
{
i--;
}
// Drain is stuck.
else
{
pump_ok = false;
break;
}
}
stored_material_amount = i;
if (stored_material_amount <= 0)
stored_material_index = nil;
if(!pump_ok) {Log("cant drain liquid");}
}
else { Log("cant get liquid"); }
if(!pump_ok)
2011-10-11 22:32:46 +00:00
{
SetState("WaitForLiquid");
2011-10-11 22:32:46 +00:00
}
}
/** Re check state and change the state if needed */
2013-05-25 15:58:19 +00:00
func CheckState()
{
var is_fullcon = GetCon() >= 100;
var can_pump = source_pipe && is_fullcon && switched_on;
Message("@0");
// can't pump at all -> wait
if (!can_pump)
{
SetState("Wait");
}
else
{
// can pump but has no liquid -> wait for liquid
if (!HasLiquidToPump())
2013-05-25 15:58:19 +00:00
{
SetState("WaitForLiquid");
2013-05-25 15:58:19 +00:00
}
else
2013-05-25 15:58:19 +00:00
{
// can pump, has liquid but has no power -> wait for power
if (!powered)
2013-05-25 15:58:19 +00:00
{
SetState("WaitForPower");
2013-05-25 15:58:19 +00:00
}
// otherwise, pump! :-)
else
{
SetState("Pump");
}
// regularly update the power usage while pumping or waiting for power
UpdatePowerUsage();
2013-05-25 15:58:19 +00:00
}
}
}
2013-05-25 15:58:19 +00:00
/** Get current height the pump has to push liquids upwards (input.y - output.y) */
private func GetPumpHeight()
{
// compare each the surfaces of the bodies of liquid pumped
2013-05-25 15:58:19 +00:00
// find Y position of surface of liquid that is pumped to target
var source_y = 0;
if (GetSourceObject()->GBackLiquid())
2013-05-25 15:58:19 +00:00
{
var src_mat = GetSourceObject()->GetMaterial();
while (src_mat == GetSourceObject()->GetMaterial(0, source_y-1))
--source_y;
2013-05-25 15:58:19 +00:00
}
// same for target
var target_y = 0;
if (GetDrainObject()->GBackLiquid())
{
var src_mat = GetDrainObject()->GetMaterial();
while (src_mat == GetDrainObject()->GetMaterial(0, target_y-1))
--target_y;
}
return (GetSourceObject()->GetY() + source_y) - (GetDrainObject()->GetY() + target_y);
}
2013-05-25 15:58:19 +00:00
/** Recheck power usage/production for current pump height
and make the pump a producer / consumer for the power system */
private func UpdatePowerUsage()
{
2013-05-25 15:58:19 +00:00
var new_power;
if(IsUsingPower())
new_power = PumpHeight2Power(GetPumpHeight());
else
new_power = 0;
// and update energy system
if (new_power > 0)
{
2013-05-25 15:58:19 +00:00
UnmakePowerProducer();
MakePowerConsumer(new_power);
}
2013-05-25 15:58:19 +00:00
else if (new_power <= 0)
{
UnmakePowerConsumer();
2013-05-25 15:58:19 +00:00
MakePowerProducer(-new_power);
}
Message("@%d",new_power);
}
2013-05-25 15:58:19 +00:00
/** Return whether the pump should be using power in the current state */
private func IsUsingPower()
{
2013-05-25 15:58:19 +00:00
// does also not consume power if waiting (for source pipe)
return switched_on && GetAction() != "Wait" && GetAction() != "WaitForLiquid";
}
2013-05-25 15:58:19 +00:00
/** Transform pump height (input.y - output.y) to required power */
private func PumpHeight2Power(int pump_height)
{
2013-05-25 15:58:19 +00:00
// pumping downwards will only produce energy after an offset
var power_offset = 35;
// max power consumed/produced
var max_power = 150;
return BoundBy((pump_height + power_offset)/15*5, -max_power,max_power);
}
2013-05-25 15:58:19 +00:00
/** Returns whether there is liquid at the source pipe to pump */
private func HasLiquidToPump()
{
if (!source_pipe)
return false;
2013-05-25 15:58:19 +00:00
// source
if(!GetSourceObject()->GBackLiquid())
return false;
// target (test with the very popular liquid "water"
if(!GetDrainObject()->CanInsertMaterial(Material("Water"),0,0))
return false;
return true;
}
/** Set the state of the pump, retaining the animation position and updating the power usage */
func SetState(string act)
{
if(act == GetAction()) return;
Log("setting state %s",act);
var start = 0;
var end = GetAnimationLength("pump");
if (act == "Pump")
{
SetAnimationPosition(animation, Anim_Linear(GetAnimationPosition(animation), start, end, 35, ANIM_Loop));
2011-10-11 22:32:46 +00:00
}
else if(act == "WaitForLiquid")
{
SetAnimationPosition(animation, Anim_Linear(GetAnimationPosition(animation), start, end, 350, ANIM_Loop));
}
else
{
SetAnimationPosition(animation, Anim_Const(GetAnimationPosition(animation)));
}
// deactivate power usage on wait
if (act == "Wait")
{
UnmakePowerProducer();
UnmakePowerConsumer();
Message("@OFF");
}
// finally, set the action
SetAction(act);
}