forked from Mirrors/openclonk
339 lines
7.4 KiB
C
339 lines
7.4 KiB
C
/**
|
|
Wooden Barrel
|
|
The barrel is used to transport liquids
|
|
|
|
@author: Ringwaul, ST-DDT
|
|
*/
|
|
|
|
#include Library_CarryHeavy
|
|
#include Library_LiquidContainer
|
|
#include Library_HasExtraSlot
|
|
#include Library_Flammable
|
|
|
|
/*-- Engine Callbacks --*/
|
|
|
|
func Initialize()
|
|
{
|
|
AddTimer("Check", 5);
|
|
UpdateLiquidContainer();
|
|
}
|
|
|
|
func RejectCollect(id def, object new_contents)
|
|
{
|
|
// The barrel can only contain liquids.
|
|
if (RejectStack(new_contents)) return true;
|
|
return _inherited(def, new_contents, ...);
|
|
}
|
|
|
|
func Hit()
|
|
{
|
|
this->PlayBarrelHitSound();
|
|
if (Contents())
|
|
{
|
|
if (GBackLiquid(0, this.BarrelIntakeY)
|
|
&& GetMaterial(0, this.BarrelIntakeY) != Contents()->GetLiquidType())
|
|
return;
|
|
|
|
EmptyBarrel(GetR());
|
|
Sound("Liquids::Splash1");
|
|
}
|
|
}
|
|
|
|
func PlayBarrelHitSound()
|
|
{
|
|
Sound("Hits::Materials::Wood::DullWoodHit?");
|
|
}
|
|
|
|
public func Collection2(object item)
|
|
{
|
|
UpdateLiquidContainer();
|
|
return _inherited(item, ...);
|
|
}
|
|
|
|
public func Ejection(object item)
|
|
{
|
|
UpdateLiquidContainer();
|
|
return _inherited(item, ...);
|
|
}
|
|
|
|
public func ContentsDestruction(object item)
|
|
{
|
|
ScheduleCall(this, "UpdateLiquidContainer", 1);
|
|
return _inherited(item, ...);
|
|
}
|
|
|
|
public func RemoveLiquid(liquid_name, int amount, object destination)
|
|
{
|
|
var res = _inherited(liquid_name, amount, destination, ...);
|
|
UpdateLiquidContainer();
|
|
return res;
|
|
}
|
|
|
|
public func PutLiquid(liquid_name, int amount, object source)
|
|
{
|
|
var res = _inherited(liquid_name, amount, source, ...);
|
|
UpdateLiquidContainer();
|
|
return res;
|
|
}
|
|
|
|
/*-- Callbacks --*/
|
|
|
|
public func CollectFromStack(object item)
|
|
{
|
|
// Callback from stackable object: Try grabbing partial objects from this stack, if the stack is too large
|
|
if (item->GetStackCount() > GetLiquidAmountRemaining() && !this->RejectStack(item))
|
|
{
|
|
// Get one sample object and try to insert it into the barrel
|
|
var candidate = item->TakeObject();
|
|
candidate->Enter(this);
|
|
|
|
// Put it back if it was not collected
|
|
if (candidate && !(candidate->Contained()))
|
|
{
|
|
item->TryAddToStack(candidate);
|
|
}
|
|
}
|
|
}
|
|
|
|
public func RejectStack(object item)
|
|
{
|
|
// Callback from stackable object: When should a stack entrance be rejected, if the object was not merged into the existing stacks?
|
|
if (Contents())
|
|
{
|
|
// The barrel can hold only one type of liquid
|
|
return true;
|
|
}
|
|
if (item->~IsLiquid() && this->~IsLiquidContainerForMaterial(item->~GetLiquidType()))
|
|
{
|
|
// The liquid is suitable, collect it!
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// Reject anything else
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public func GetLiquidContainerMaxFillLevel(liquid_name)
|
|
{
|
|
return 300;
|
|
}
|
|
|
|
public func IsBarrel()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public func IsLiquidContainerForMaterial(string liquid_name)
|
|
{
|
|
return !!WildcardMatch("Water", liquid_name) || !!WildcardMatch("Oil", liquid_name) || !!WildcardMatch("Concrete", liquid_name);
|
|
}
|
|
|
|
public func CanBeStackedWith(object other)
|
|
{
|
|
// Does not take into account the fill level for now.
|
|
var liquid = other->Contents();
|
|
var my_liquid = this->Contents();
|
|
var both_filled = (my_liquid != nil) && (liquid != nil);
|
|
var both_empty = !my_liquid && !liquid;
|
|
|
|
if (both_filled) both_filled = (liquid->~GetLiquidType() == Contents()->~GetLiquidType());
|
|
|
|
return _inherited(other, ...) && (both_empty || both_filled);
|
|
}
|
|
|
|
// Sells the contents only, leaving an empty barrel.
|
|
// Empty barrels can then be sold separately.
|
|
public func QueryOnSell(int for_player, object in_base)
|
|
{
|
|
if (Contents() && in_base)
|
|
{
|
|
// Sell contents first
|
|
for(var contents in FindObjects(Find_Container(this)))
|
|
{
|
|
in_base->~DoSell(contents, for_player);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*-- Usage --*/
|
|
|
|
public func ControlUse(object clonk, int iX, int iY)
|
|
{
|
|
var AimAngle = Angle(0, 0, iX, iY);
|
|
if (Contents())
|
|
{
|
|
EmptyBarrel(AimAngle, 50, clonk);
|
|
if (iX > 1)
|
|
Contained()->SetDir(1);
|
|
if (iX < -1)
|
|
Contained()->SetDir(0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
func Check()
|
|
{
|
|
//Fills Barrel with specified liquid from if submerged
|
|
FillWithLiquid();
|
|
|
|
//Message("Volume:|%d|Liquid:|%s", iVolume, szLiquid);
|
|
}
|
|
|
|
func FillWithLiquid()
|
|
{
|
|
var intake = this.BarrelIntakeY;
|
|
if (!GBackLiquid(0, intake)) return;
|
|
if (GetLiquidAmount() >= GetLiquidContainerMaxFillLevel()) return;
|
|
|
|
var mat = GetMaterial(0, intake);
|
|
var mat_name = MaterialName(mat);
|
|
if (!IsLiquidContainerForMaterial(mat_name)) return;
|
|
|
|
var remaining_volume = GetLiquidContainerMaxFillLevel() - GetLiquidAmount();
|
|
var extracted = 0;
|
|
while (extracted < remaining_volume && GetMaterial(0, intake) == mat)
|
|
{
|
|
extracted += 1;
|
|
ExtractLiquid(0, intake);
|
|
}
|
|
|
|
var inserted = 0;
|
|
if (extracted > 0) inserted = PutLiquid(mat_name, extracted);
|
|
|
|
if (inserted < extracted)
|
|
{
|
|
CastPXS(mat_name, extracted - inserted, 1, 0, intake);
|
|
}
|
|
}
|
|
|
|
func EmptyBarrel(int angle, int strength, object clonk)
|
|
{
|
|
if (Contents())
|
|
{
|
|
var material = Contents()->~GetLiquidType();
|
|
var volume = Contents()->~GetLiquidAmount();
|
|
|
|
Contents()->~Disperse(angle, strength);
|
|
|
|
var spray = {};
|
|
spray.Liquid = material;
|
|
spray.Volume = volume;
|
|
spray.Strength = strength;
|
|
spray.Angle = angle;
|
|
spray.Clonk = clonk;
|
|
AddEffect("ExtinguishingSpray", clonk, 100, 1, this, nil, spray);
|
|
|
|
UpdateLiquidContainer();
|
|
}
|
|
}
|
|
|
|
func UpdateLiquidContainer()
|
|
{
|
|
if (Contents())
|
|
{
|
|
var color;
|
|
var material = Material(Contents()->GetLiquidType());
|
|
if (material >= 0)
|
|
{
|
|
var tex = GetMaterialVal("TextureOverlay", "Material", material);
|
|
color = GetAverageTextureColor(tex);
|
|
}
|
|
else
|
|
{
|
|
color = RGB(0, 0, 0);
|
|
}
|
|
SetColor(color);
|
|
}
|
|
else
|
|
{
|
|
SetColor(RGB(0, 0, 0));
|
|
}
|
|
|
|
this.Name = GetNameForBarrel();
|
|
return;
|
|
}
|
|
|
|
func FxExtinguishingSprayStart(object target, proplist effect, int temp, proplist spray)
|
|
{
|
|
if (temp)
|
|
return FX_OK;
|
|
// Only for extinguishing materials.
|
|
if (!GetMaterialVal("Extinguisher", "Material", Material(spray.Liquid)))
|
|
return FX_Start_Deny;
|
|
// If used by an object also extinguish that.
|
|
if (spray.Clonk)
|
|
spray.Clonk->Extinguish(Min(100, spray.Volume/3));
|
|
// Store spray parameters.
|
|
effect.Volume = spray.Volume;
|
|
effect.Strength = spray.Strength;
|
|
effect.Angle = spray.Angle;
|
|
return FX_OK;
|
|
}
|
|
|
|
func FxExtinguishingSprayTimer(object target, proplist effect, int time)
|
|
{
|
|
// Move three lines from the barrel outwards along the defined angle.
|
|
// And extinguish all objects on these lines.
|
|
if (time > 20)
|
|
return FX_Execute_Kill;
|
|
var d = effect.Strength * time / 25;
|
|
for (var dev = -10; dev <= 10; dev+= 10)
|
|
{
|
|
var x = Sin(effect.Angle + dev, d);
|
|
var y = -Cos(effect.Angle + dev, d);
|
|
if (PathFree(GetX(), GetY(), GetX() + x, GetY() + y))
|
|
for (var obj in FindObjects(Find_AtPoint(x, y), Find_OCF(OCF_OnFire)))
|
|
obj->Extinguish(Max(0, effect.Volume/3 - 2 * d));
|
|
}
|
|
return FX_OK;
|
|
}
|
|
|
|
/*-- Production --*/
|
|
|
|
public func IsToolProduct() { return true; }
|
|
|
|
/*-- Display --*/
|
|
|
|
public func GetCarryTransform(clonk)
|
|
{
|
|
if(GetCarrySpecial(clonk))
|
|
return Trans_Translate(1000, 6500, 0);
|
|
|
|
return Trans_Translate(1500, 0, -1500);
|
|
}
|
|
|
|
public func GetCarryPhase()
|
|
{
|
|
return 900;
|
|
}
|
|
|
|
public func Definition(proplist def)
|
|
{
|
|
SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(0, 1000, 0), Trans_Rotate(-40, 1, 0, 0), Trans_Rotate(20, 0, 0, 1)), def);
|
|
}
|
|
|
|
/*-- Properties --*/
|
|
|
|
func GetNameForBarrel()
|
|
{
|
|
if (Contents())
|
|
{
|
|
var name = Format("%s $NameWith$ %s", this.Prototype.Name, Contents().Prototype.Name);
|
|
return name;
|
|
}
|
|
else
|
|
{
|
|
return this.Prototype.Name;
|
|
}
|
|
}
|
|
|
|
local Name = "$Name$";
|
|
local Description = "$Description$";
|
|
local Collectible = true;
|
|
local ContactIncinerate = 2;
|
|
local BarrelIntakeY = 3;
|
|
local Components = {Wood = 2, Metal = 1}; |