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

400 lines
9.6 KiB
C

/**
Sawmill
Cuts trees or other objects into wood. Accepts only objects purely made from wood (like trees).
@authors Ringwaul, Clonkonaut
*/
#include Library_Structure
#include Library_Ownable
#include Library_PowerConsumer
local running = false;
local power = false;
public func Construction()
{
SetProperty("MeshTransformation", Trans_Rotate(-20, 0, 1, 0));
SetAction("Default");
return _inherited(...);
}
public func IsHammerBuildable() { return true; }
public func Initialize()
{
this.SpinAnimation = PlayAnimation("work", 10, Anim_Const(0));
AddTimer("CollectTrees", 4);
return _inherited(...);
}
/*-- Interaction --*/
// Sawmill acts as a container to be able to collect wooden objects.
public func IsContainer() { return true; }
// Do not show normal inventory menu. Instead we show the remaining wood in an extra menu.
public func RejectContentsMenu() { return true; }
// Sawmill can't be interacted with.
public func IsInteractable() { return false; }
/*-- Production --*/
private func Collection(object obj)
{
Sound("Objects::Clonk");
Saw(obj);
}
private func RejectCollect(id id_def, object collect)
{
// Don't collect wood
if (id_def == Wood)
return true;
if (CheckWoodObject(collect))
return false;
return true;
}
// Timer, check for objects to collect in the designated collection zone
public func CollectTrees()
{
if (GetCon() < 100)
return;
var tree = FindObject(Find_AtPoint(), Find_Func("IsTree"), Find_Not(Find_Func("IsStanding")), Find_Func("GetComponent", Wood));
// If there is no tree in front of the sawmill try to get one from the cable car network.
if (!tree)
RequestTree();
// Only take one tree at a time.
if (!ContentsCount(Wood) && tree)
Saw(tree);
return;
}
// Returns whether the object is made purely out of wood.
private func CheckWoodObject(object target)
{
if (target->GetComponent(nil, 0) != Wood)
return false;
if (target->GetComponent(nil, 1))
return false;
return true;
}
// Provides an own interaction menu.
public func HasInteractionMenu() { return true; }
// Show a helpful hint to the player. The hint is colored and titled the same as the production menu for more visual coherence.
public func GetInteractionMenus(object clonk)
{
var menus = _inherited(clonk, ...) ?? [];
var prod_menu =
{
title = "$Production$",
entries_callback = this.GetInfoMenuEntries,
callback = nil,
BackgroundColor = RGB(0, 0, 50),
Priority = 20
};
PushBack(menus, prod_menu);
return menus;
}
public func GetInfoMenuEntries()
{
var wood_count = ContentsCount(Wood);
var info_text =
{
Right = "100%", Bottom = "6em",
text = {Left = "2em", Text = "$AutoProduction$", Style = GUI_TextVCenter | GUI_TextHCenter},
image = {Right = "2em", Bottom = "2em", Symbol = Wood},
queue =
{
Top = "100% - 1.5em",
Style = GUI_TextRight,
Text = Format("$WoodInQueue$: %2d {{Wood}}", wood_count)
}
};
return [{symbol = Wood, custom = info_text}];
}
public func Saw(object target)
{
if (target->Contained() != this)
target->Enter(this);
target->Split2Components();
AddEffect("WoodProduction", this, 100, 3, this);
// Refresh interaction menus to show the wood count.
UpdateInteractionMenus(this.GetInfoMenuEntries);
return true;
}
private func ProductionTime(id product) { return _inherited(product, ...) ?? 100; }
private func PowerNeed() { return 20; }
private func FxWoodProductionStart(object t, proplist effect, int temp)
{
if (temp) return;
effect.runtime = 0;
effect.starttime = 0;
RegisterPowerRequest(PowerNeed());
}
private func FxWoodProductionTimer(object t, proplist effect, int time)
{
if (!power)
{
if (effect.starttime)
effect.starttime = 0;
return FX_OK;
}
if (!(time%20))
Smoke(10 * GetCalcDir(),10,10);
if (!running)
{
SpinOn(effect.starttime);
effect.starttime += 3;
return FX_OK;
}
effect.runtime += 3;
var dir = GetCalcDir();
CreateParticle("WoodChip", PV_Random(-7 * dir, -3 * dir), PV_Random(3, 6), PV_Random(-5 * dir, -11 * dir), PV_Random(-4, -2), PV_Random(36 * 3, 36 * 10), Particles_WoodChip(), 3);
if (effect.runtime >= ProductionTime())
{
EjectWood();
effect.runtime = 0;
}
else
return FX_OK;
if (!ContentsCount(Wood))
return FX_Execute_Kill;
}
private func FxWoodProductionStop(object t, proplist effect, int r, bool temp)
{
if (temp) return;
UnregisterPowerRequest();
SpinOff();
power = false;
}
public func OnEnoughPower()
{
if (power) return _inherited(...);
power = true;
return _inherited(...);
}
public func OnNotEnoughPower()
{
if (!power) return _inherited(...);
power = false;
SpinOff();
return _inherited(...);
}
public func EjectWood()
{
var wood = FindContents(Wood);
if (!wood) return;
wood->Exit(-25 * GetCalcDir(), -8, 30 - Random(59), -2 * GetCalcDir(), 1);
Sound("Structures::EjectionPop");
// Refresh interaction menus to show the wood count.
UpdateInteractionMenus(this.GetInfoMenuEntries);
}
/*-- Animation --*/
private func SpinOn(int diff)
{
var spin;
var rotate = 0;
if (diff == 0)
{
ClearScheduleCall(this, "SpinOff");
Sound("Sawmill::EngineStart");
SetMeshMaterial("Beltspin", 1);
}
if (diff > 20) rotate = Sin(70 * diff, 1) - 1;
if (Inside(diff, 0, 20)) spin = 100;
if (Inside(diff, 21, 40)) spin = 75;
if (Inside(diff, 41, 60)) spin = 50;
if (Inside(diff, 61, 80)) spin = 30;
if (diff > 80)
{
spin = 30;
rotate = 0;
SetMeshMaterial("SawmillBlade.Spin", 2);
running = true;
Sound("Structures::SawmillRipcut", {loop_count = +1});
Sound("Sawmill::EngineLoop", {loop_count = +1});
}
SetProperty("MeshTransformation", Trans_Mul(Trans_Rotate(-20, 0, 1, 0), Trans_Rotate(rotate, 1, 0, 0)));
SetAnimationPosition(this.SpinAnimation, Anim_Linear(GetAnimationPosition(this.SpinAnimation), 0, GetAnimationLength("work"), spin, ANIM_Loop));
}
private func SpinOff(int call)
{
var spin;
if (!call)
{
running = false;
spin = 50;
SetMeshMaterial("SawmillBlade", 2);
Sound("Structures::SawmillRipcut", {loop_count = -1});
SetProperty("MeshTransformation", Trans_Rotate(-20, 0, 1, 0));
}
if (call == 1) spin = 75;
if (call == 2)
{
spin = 100;
Sound("Sawmill::EngineLoop", {loop_count = -1});
Sound("Sawmill::EngineStop");
}
if (call == 3) spin = 150;
if (call == 4)
{
SetMeshMaterial("SawmillBelt", 1);
SetAnimationPosition(this.SpinAnimation, Anim_Const(GetAnimationPosition(this.SpinAnimation)));
return;
}
SetAnimationPosition(this.SpinAnimation, Anim_Linear(GetAnimationPosition(this.SpinAnimation), 0, GetAnimationLength("work"), spin, ANIM_Loop));
ScheduleCall(this, "SpinOff", this.SpinStep * 2, nil, call+1);
}
/*-- Cable Network --*/
local cable_station;
public func AcceptsCableStationConnection() { return true; }
public func IsNoCableStationConnected() { return !cable_station; }
public func ConnectCableStation(object station)
{
cable_station = station;
}
private func RequestTree()
{
if (!cable_station)
return;
// Find a collectible tree on the network.
var collectible_tree = cable_station->FindObjectOnNetworkCables(Find_And(Find_Func("IsTree"), Find_Not(Find_Property("is_being_cable_car_collected"))));
if (!collectible_tree)
return;
// Find an available hoist in the network.
var hoist = cable_station->FindCableCar(Find_And(Find_Not(Find_Func("GetAttachedVehicle")), Find_Not(Find_Property("is_busy_collecting_tree"))));
if (!hoist)
return;
hoist->CreateEffect(Sawmill.FxCableCarCollectTree, 100, 1, collectible_tree, cable_station);
return;
}
local FxCableCarCollectTree = new Effect
{
Construction = func(array collectible_tree, object sawmill_station)
{
this.tree = collectible_tree[0];
this.station_1 = collectible_tree[1];
this.station_2 = collectible_tree[2];
this.station_sawmill = sawmill_station;
this.tree.is_being_cable_car_collected = true;
Target.is_busy_collecting_tree = true;
Target->SetDestination(this.station_1);
return FX_OK;
},
Timer = func()
{
if (!this.station_1 || !this.station_2 || !this.station_sawmill)
return FX_Execute_Kill;
if (IsValueInArray(this.station_1->GetIdleCars(), Target))
Target->SetDestination(this.station_2);
if (IsValueInArray(this.station_2->GetIdleCars(), Target))
Target->SetDestination(this.station_sawmill);
var tree = Target->FindObject(Target->Find_AtPoint(), Find_InArray([this.tree]));
if (tree && !tree.has_been_picked_up)
{
this.tree.has_been_picked_up = true;
this.tree->CreateEffect(Sawmill.FxCableCarRotateTree, 100, 1);
Target->PickupVehicle(this.tree);
}
if (IsValueInArray(this.station_sawmill->GetIdleCars(), Target))
return FX_Execute_Kill;
return FX_OK;
},
Destruction = func()
{
//this.tree.is_being_cable_car_collected = false;
Target.is_busy_collecting_tree = false;
Target->DropVehicle();
}
};
local FxCableCarRotateTree = new Effect
{
Construction = func(int rotate_goal)
{
this.rotate_goal = 90;
if (Target->GetR() < 0)
this.rotate_goal = -90;
},
Timer = func()
{
var step = 3;
var dr = this.rotate_goal - Target->GetR();
if (Inside(dr, -step, step))
return FX_Execute_Kill;
Target->SetR(Target->GetR() + BoundBy(dr, -step, step));
return FX_OK;
}
};
/*-- Properties --*/
local ActMap = {
Default = {
Prototype = Action,
Name = "Default",
Procedure = DFA_NONE,
Directions = 2,
FlipDir = 1,
Length = 1,
Delay = 0,
FacetBase = 1,
NextAction = "Default",
},
};
func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Translate(2000, 0, 7000), Trans_Rotate(-20, 1, 0, 0),Trans_Rotate(30, 0, 1, 0)), def);
return _inherited(def, ...);
}
local Name = "$Name$";
local Description = "$Description$";
local SpinStep = 30;
local ContainBlast = true;
local BlastIncinerate = 100;
local FireproofContainer = true;
local HitPoints = 70;
local Components = {Rock = 4, Wood = 1};