summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Items.c4d/Carrier.c4d/ActMap.txt4
-rw-r--r--Items.c4d/Carrier.c4d/DefCore.txt10
-rw-r--r--Items.c4d/Carrier.c4d/DescDE.txt1
-rw-r--r--Items.c4d/Carrier.c4d/DescUS.txt1
-rw-r--r--Items.c4d/Carrier.c4d/Graphics.pngbin0 -> 247 bytes
-rw-r--r--Items.c4d/Carrier.c4d/Names.txt2
-rw-r--r--Items.c4d/Carrier.c4d/Script.c2
-rw-r--r--Items.c4d/Distributor.c4d/ActMap.txt4
-rw-r--r--Items.c4d/Distributor.c4d/DefCore.txt21
-rw-r--r--Items.c4d/Distributor.c4d/Graphics.pngbin0 -> 245 bytes
-rw-r--r--Items.c4d/Distributor.c4d/Names.txt2
-rw-r--r--Items.c4d/Distributor.c4d/Script.c107
-rw-r--r--Items.c4d/Extractor.c4d/ActMap.txt5
-rw-r--r--Items.c4d/Extractor.c4d/DefCore.txt22
-rw-r--r--Items.c4d/Extractor.c4d/Graphics.pngbin0 -> 234 bytes
-rw-r--r--Items.c4d/Extractor.c4d/Names.txt2
-rw-r--r--Items.c4d/Extractor.c4d/Script.c323
-rw-r--r--Items.c4d/Extractor.c4d/StringTblDE.txt7
-rw-r--r--Items.c4d/Extractor.c4d/StringTblUS.txt7
-rw-r--r--Items.c4d/Filler.c4d/ActMap.txt10
-rw-r--r--Items.c4d/Filler.c4d/DefCore.txt22
-rw-r--r--Items.c4d/Filler.c4d/Graphics.pngbin0 -> 2317 bytes
-rw-r--r--Items.c4d/Filler.c4d/Names.txt2
-rw-r--r--Items.c4d/Filler.c4d/Script.c95
-rw-r--r--Items.c4d/Filler.c4d/StringTblDE.txt2
-rw-r--r--Items.c4d/Filler.c4d/StringTblUS.txt2
-rw-r--r--Items.c4d/Sender.c4d/ActMap.txt4
-rw-r--r--Items.c4d/Sender.c4d/DefCore.txt22
-rw-r--r--Items.c4d/Sender.c4d/Graphics.pngbin0 -> 2832 bytes
-rw-r--r--Items.c4d/Sender.c4d/Names.txt2
-rw-r--r--Items.c4d/Sender.c4d/Script.c19
-rw-r--r--Items.c4d/Sender.c4d/StringTblDE.txt1
-rw-r--r--Items.c4d/Sender.c4d/StringTblUS.txt1
-rw-r--r--Items.c4d/Tubekit.c4d/DefCore.txt19
-rw-r--r--Items.c4d/Tubekit.c4d/Graphics.pngbin0 -> 4993 bytes
-rw-r--r--Items.c4d/Tubekit.c4d/Names.txt2
-rw-r--r--Items.c4d/Tubekit.c4d/Script.c85
-rw-r--r--Items.c4d/Tubekit.c4d/StringTblDE.txt7
-rw-r--r--Items.c4d/Tubekit.c4d/StringTblUS.txt7
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/ActMap.txt4
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/DefCore.txt13
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/DescDE.txt1
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/DescUS.txt1
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/Graphics.pngbin0 -> 1217 bytes
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/Names.txt2
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/Script.c305
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/StringTblDE.txt1
-rw-r--r--Items.c4d/Tubekit.c4d/Tube.c4d/StringTblUS.txt1
-rw-r--r--Items.c4d/Utility.c4d/DefCore.txt4
-rw-r--r--Items.c4d/Utility.c4d/Graphics.pngbin0 -> 126 bytes
-rw-r--r--Items.c4d/Utility.c4d/Names.txt2
-rw-r--r--Items.c4d/Utility.c4d/Script.c421
-rw-r--r--Items.c4d/Utility.c4d/StringTblDE.txt5
-rw-r--r--Items.c4d/Utility.c4d/StringTblUS.txt5
-rw-r--r--System.c4g/ClonkAutoBuy.c31
-rw-r--r--System.c4g/IDs.c10
-rw-r--r--System.c4g/TestTargets.disabled7
-rw-r--r--System.c4g/Utility.c62
58 files changed, 1697 insertions, 0 deletions
diff --git a/Items.c4d/Carrier.c4d/ActMap.txt b/Items.c4d/Carrier.c4d/ActMap.txt
new file mode 100644
index 0000000..2a84bf1
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/ActMap.txt
@@ -0,0 +1,4 @@
+[Action]
+Name=Connect
+Procedure=CONNECT
+FacetBase=1
diff --git a/Items.c4d/Carrier.c4d/DefCore.txt b/Items.c4d/Carrier.c4d/DefCore.txt
new file mode 100644
index 0000000..d8fc616
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/DefCore.txt
@@ -0,0 +1,10 @@
+[DefCore]
+id=TC7I
+Version=4,9,8
+Name=Tube carrier
+Category=C4D_StaticBack
+Width=20
+Height=7
+Offset=-10,-3
+Vertices=1
+Rotate=1
diff --git a/Items.c4d/Carrier.c4d/DescDE.txt b/Items.c4d/Carrier.c4d/DescDE.txt
new file mode 100644
index 0000000..9c1fb62
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/DescDE.txt
@@ -0,0 +1 @@
+Abfluß für Pumpen.
diff --git a/Items.c4d/Carrier.c4d/DescUS.txt b/Items.c4d/Carrier.c4d/DescUS.txt
new file mode 100644
index 0000000..75008ce
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/DescUS.txt
@@ -0,0 +1 @@
+Drain for pumps.
diff --git a/Items.c4d/Carrier.c4d/Graphics.png b/Items.c4d/Carrier.c4d/Graphics.png
new file mode 100644
index 0000000..1b5895f
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Carrier.c4d/Names.txt b/Items.c4d/Carrier.c4d/Names.txt
new file mode 100644
index 0000000..5290aa1
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Rohrpostbüchse
+US:Tube mail carrier
diff --git a/Items.c4d/Carrier.c4d/Script.c b/Items.c4d/Carrier.c4d/Script.c
new file mode 100644
index 0000000..773fb96
--- /dev/null
+++ b/Items.c4d/Carrier.c4d/Script.c
@@ -0,0 +1,2 @@
+#strict 2
+
diff --git a/Items.c4d/Distributor.c4d/ActMap.txt b/Items.c4d/Distributor.c4d/ActMap.txt
new file mode 100644
index 0000000..54a4a81
--- /dev/null
+++ b/Items.c4d/Distributor.c4d/ActMap.txt
@@ -0,0 +1,4 @@
+[Action]
+Name=Float
+Procedure=FLOAT
+FacetBase=1
diff --git a/Items.c4d/Distributor.c4d/DefCore.txt b/Items.c4d/Distributor.c4d/DefCore.txt
new file mode 100644
index 0000000..8e9155e
--- /dev/null
+++ b/Items.c4d/Distributor.c4d/DefCore.txt
@@ -0,0 +1,21 @@
+[DefCore]
+id=DS7I
+Name=Distributor
+Version=4,9,5
+Category=C4D_Object|C4D_SelectMaterial|C4D_SelectKnowledge|C4D_SelectHomebase
+Width=11
+Height=11
+Offset=-5,-4
+Mass=10
+Value=5
+Components=METL=1;
+; Picture=10,0,64,64
+Vertices=4
+VertexX=0,-5,5
+VertexY=4,-4,-4
+VertexFriction=100,100,100
+Grab=2
+Collectible=1
+MaxUserSelect=10
+Rebuy=1
+NoPushEnter=1
diff --git a/Items.c4d/Distributor.c4d/Graphics.png b/Items.c4d/Distributor.c4d/Graphics.png
new file mode 100644
index 0000000..23114f2
--- /dev/null
+++ b/Items.c4d/Distributor.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Distributor.c4d/Names.txt b/Items.c4d/Distributor.c4d/Names.txt
new file mode 100644
index 0000000..47a29e4
--- /dev/null
+++ b/Items.c4d/Distributor.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Verteiler
+US:Distributor
diff --git a/Items.c4d/Distributor.c4d/Script.c b/Items.c4d/Distributor.c4d/Script.c
new file mode 100644
index 0000000..156c30d
--- /dev/null
+++ b/Items.c4d/Distributor.c4d/Script.c
@@ -0,0 +1,107 @@
+#strict 2
+#include UT7I
+
+func Floating()
+{
+ return GetProcedure() == "FLOAT";
+}
+
+func ControlDigDouble(object caller)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ return SetAction("Idle");
+}
+
+func Activate(object caller)
+{
+ caller->Exit(this);
+ SetAction("Float");
+ return true;
+}
+
+func AcceptsObject(object obj, id id, object requester)
+{
+ return FindObject2(Find_Tube(), ExcludeOrigin(requester), Find_Func("CanSendObject", this, obj));
+}
+
+func RetrieveObject(object requester, id type, arriveCallback, object excludeOrigin, failCallback, object realTarget)
+{
+ var line = FindObject2(Find_Tube(), ExcludeOrigin(requester), ExcludeOrigin(excludeOrigin), Find_Func("CanRetrieveObject", this, type, false)) || FindObject2(Find_Tube(), ExcludeOrigin(requester), ExcludeOrigin(excludeOrigin), Find_Func("CanRetrieveObject", this, type, true), Sort_Func("FactoryLoad"));
+ if(line)
+ {
+ return line->RetrieveObject(this, type, BindCallback(Callback("ForwardObject"), [0, 1, 2, Bind(requester), Bind(arriveCallback)]), 0, failCallback, realTarget || requester->GetLineEnd(this));
+ }
+}
+
+func ForwardObject(object obj, object line, object receiver, object requester, arriveCallback)
+{
+ if(requester)
+ {
+ return requester->SendObject(this, obj, arriveCallback);
+ }
+}
+
+func ExcludeOrigin(object originLine)
+{
+ var origin = originLine;
+ if(!originLine)
+ {
+ return Find_And();
+ }
+ if(origin->~IsTube())
+ {
+ origin = origin->ObjectOrigin(this);
+ }
+ else
+ {
+ originLine = 0;
+ }
+ return Find_And(Find_Exclude(originLine), Find_Not(Find_Func("HasObjectOrigin", origin)));
+}
+
+func ReceiveObject(object obj, bool noReturn, array arriveCallback, object senderLine)
+{
+ if(arriveCallback)
+ {
+ if(CallA(arriveCallback, [obj, senderLine, this], true))
+ {
+ return TU7I_NoCallback;
+ }
+ }
+ else
+ {
+ var line = FindObject2(Find_Tube(), ExcludeOrigin(senderLine), Find_Func("CanSendObject", this, obj));
+ return line && line->SendObject(this, obj, noReturn);
+ }
+}
+
+func HasObject(id type, bool allowProduction, object receiverLine, object excludeOrigin)
+{
+ return FindObject2(Find_Tube(), ExcludeOrigin(receiverLine), Find_Func("CanRetrieveObject", this, type, allowProduction), ExcludeOrigin(excludeOrigin));
+}
+
+func GetRetrievableObjects(object requester, bool produce)
+{
+ var lines = FindObjects(Find_Tube(), ExcludeOrigin(requester));
+ var IDs = [], produceIDs = [], part;
+ for(var line in lines)
+ {
+ part = line->~GetRetrievableObjects(this, produce) || [];
+ for(var id in part)
+ {
+ if(GetIndexOf(id, IDs) == -1)
+ {
+ IDs[GetLength(IDs)] = id;
+ }
+ }
+ }
+ return IDs;
+}
+
+func RejectEntrance(object obj)
+{
+ return Floating();
+}
diff --git a/Items.c4d/Extractor.c4d/ActMap.txt b/Items.c4d/Extractor.c4d/ActMap.txt
new file mode 100644
index 0000000..48318ab
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/ActMap.txt
@@ -0,0 +1,5 @@
+[Action]
+Name=Attach
+Procedure=ATTACH
+FacetBase=1
+AbortCall=Detached
diff --git a/Items.c4d/Extractor.c4d/DefCore.txt b/Items.c4d/Extractor.c4d/DefCore.txt
new file mode 100644
index 0000000..b2db749
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/DefCore.txt
@@ -0,0 +1,22 @@
+[DefCore]
+id=EX7I
+Name=Extractor
+Version=4,9,5
+Category=C4D_Object|C4D_SelectMaterial|C4D_SelectKnowledge|C4D_SelectHomebase
+Width=11
+Height=11
+Offset=-5,-4
+Mass=10
+Value=5
+Components=METL=1;
+; Picture=10,0,64,64
+Vertices=4
+VertexX=0,-5,5
+VertexY=4,-4,-4
+VertexFriction=100,100,100
+Grab=2
+Collectible=1
+MaxUserSelect=10
+Rebuy=1
+NoPushEnter=1
+; GrabPutGet=C4D_GrabGet
diff --git a/Items.c4d/Extractor.c4d/Graphics.png b/Items.c4d/Extractor.c4d/Graphics.png
new file mode 100644
index 0000000..22fc4fb
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Extractor.c4d/Names.txt b/Items.c4d/Extractor.c4d/Names.txt
new file mode 100644
index 0000000..8032740
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Extrahierer
+US:Extractor
diff --git a/Items.c4d/Extractor.c4d/Script.c b/Items.c4d/Extractor.c4d/Script.c
new file mode 100644
index 0000000..05a1fc6
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/Script.c
@@ -0,0 +1,323 @@
+#strict 2
+#include UT7I
+
+local worker;
+local produceQueue;
+
+func ControlThrow(object caller)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ SendObjectThroughTube();
+ return ClearLastPlrCom(GetController(caller)) || true;
+}
+
+func ControlDigDouble(object caller)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ return Detach();
+}
+
+func ControlUp(object caller, id selectID)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ return SendMenu(caller);
+}
+
+func Activate(object caller)
+{
+ return AttachMenu(caller);
+}
+
+func ContextSetWorker(object caller)
+{
+ [$SetWorker$|Image=CXCN|Condition=Attached]
+ if(CheckWorker() && caller == worker)
+ {
+ worker = 0;
+ PlayerMessage(GetController(caller), "$WorkerUnset$", this);
+ }
+ else
+ {
+ worker = caller;
+ PlayerMessage(GetController(caller), "$WorkerSet$", this, GetName(caller));
+ }
+ return true;
+}
+
+func HasObject(id type, bool allowProduction)
+{
+ var ret = FindSendableObject(type);
+ if(!ret && allowProduction)
+ {
+ var attached = Attached();
+ if(attached && GetDefCollectible(type))
+ {
+ if(attached->GetBase() != NO_OWNER)
+ {
+ var plr = attached->GetBase();
+ return GetHomebaseMaterial(plr, type) && GetWealth(plr) >= GetValue(0, type, attached, plr);
+ }
+ else if(CheckWorker() && attached->~IsProducerOf(worker, type))
+ {
+ return true;
+ }
+ }
+ }
+ return ret;
+}
+
+func RetrieveSendObject(object line, id type)
+{
+ return SendObjectThroughTube(type, line, ...);
+}
+
+func RetrieveProduceObject(object line, id type)
+{
+ BuyOrProduceSendObject(type, line, ...);
+ return true;
+}
+
+func BuyOrProduceSendObject(id type, object line, arriveCallback, object excludeOrigin, failCallback, object realTarget) // TODO: Sort factories by "load", use very high "load" for buy-only
+{
+ if(GetDefCollectible(type))
+ {
+ var attached = Attached();
+ if(attached)
+ {
+ if(attached->GetBase() != NO_OWNER)
+ {
+ var plr = attached->GetBase();
+ var obj = Buy(type, plr, plr, attached, true);
+ if(obj)
+ {
+ line = line || FindObject2(Find_Tube(), Find_Func("CanSendObject", this, obj));
+ return line && line->SendObject(this, obj, arriveCallback, false, realTarget, ...);
+ }
+ }
+ else if(CheckWorker() && attached->~IsProducerOf(worker, type))
+ {
+ AddQueue([type, line, arriveCallback, failCallback, realTarget]);
+ //SetWorkerCommands(worker, [type, line, arriveCallback, failCallback], true);
+ }
+ }
+ }
+}
+
+func AddQueue(array args)
+{
+ if(!produceQueue)
+ {
+ produceQueue = [args];
+ }
+ else
+ {
+ produceQueue[GetLength(produceQueue)] = args;
+ }
+
+ if(GetLength(produceQueue) == 1 || !GetCommand(worker))
+ {
+ ContinueQueue();
+ }
+ ShowQueue();
+}
+
+func ContinueQueue()
+{
+ if(GetLength(produceQueue) > 0)
+ {
+ SetWorkerCommands(worker, produceQueue[0], true);
+ }
+}
+
+func RemoveQueued()
+{
+ ArrayErase(produceQueue, 0);
+ ContinueQueue();
+ ShowQueue();
+}
+
+func ShowQueue()
+{
+ if(GetLength(produceQueue) > 0)
+ {
+ var msgStr = "", showCount = 0, cnt = 1, lastID = produceQueue[i][0];
+ for(var i = 1; i <= GetLength(produceQueue); ++i)
+ {
+ var id = produceQueue[i] && produceQueue[i][0];
+
+ if(lastID == id)
+ {
+ ++cnt;
+ }
+ else
+ {
+ if(cnt > 1)
+ {
+ msgStr = Format("|{{%i}} x%d%s", lastID, cnt, msgStr);
+ }
+ else
+ {
+ msgStr = Format("|{{%i}}%s", lastID, msgStr);
+ }
+ if(++showCount >= 10)
+ {
+ break;
+ }
+ cnt = 0;
+ }
+
+ lastID = id;
+ }
+ if(i < GetLength(produceQueue))
+ {
+ msgStr = Format("...%s", msgStr);
+ }
+ Message("@%s", this, msgStr);
+ }
+ else
+ {
+ Message("", this);
+ }
+}
+
+func FactoryLoad() // TODO: TEST and FIXME
+{
+ var attached = Attached();
+ if(attached && attached->GetBase() != NO_OWNER)
+ {
+ return 10; // maybe adjust this
+ }
+ else
+ {
+ return produceQueue && GetLength(produceQueue);
+ }
+}
+
+func SetWorkerCommands(object worker, array args, bool checkIdle)
+{
+ if(!checkIdle || !GetCommand(worker))
+ {
+ var attached = Attached();
+ if(attached)
+ {
+ AddCommand(worker, "Call", this, args, 0, 0, 0, "ProductionFinished");
+ AddCommand(worker, "Put", attached, 0, 0, 0, 0, args[0]);
+ AddCommand(worker, "Acquire", this, 0, 0, 0, 0, args[0]); // don't Acquire through tubes this time
+ attached->~HowToProduce(worker, args[0]);
+ }
+ }
+ else
+ {
+ AppendCommand(worker, "Call", this, args, 0, 0, 0, "SetWorkerCommands");
+ }
+}
+
+func ProductionFinishedFailed(object worker, array args)
+{
+ RemoveQueued();
+ Call(args[3], worker);
+}
+
+func ProductionFinished(object worker, array args)
+{
+ RemoveQueued();
+ return args[0] && SendObjectThroughTube(args[0], args[1], args[2], false, args[4]);
+}
+
+func GetRetrievableObjects(object line, bool production)
+{
+ return GetSendableIDs(production);
+}
+
+func ProductionTitle()
+{
+ var attached = Attached();
+ if(attached)
+ {
+ if(attached->GetBase() != NO_OWNER)
+ {
+ return "$Buy$";
+ }
+ }
+ return _inherited(...);
+}
+
+func GetProducableIDs()
+{
+ var ret = [];
+ var attached = Attached();
+ if(attached)
+ {
+ if(attached->GetBase() != NO_OWNER)
+ {
+ var plr = attached->GetBase();
+ for(var i = 0, type; type = GetHomebaseMaterial(plr, 0, i, C4D_StaticBack | C4D_Structure | C4D_Vehicle | C4D_Living | C4D_Object); ++i)
+ {
+ if(GetDefCollectible(type) && GetHomebaseMaterial(plr, type) && GetWealth(plr) >= GetValue(0, type, attached, plr))
+ {
+ ret[GetLength(ret)] = type;
+ }
+ }
+ }
+ else if(CheckWorker())
+ {
+ for(var i = 0, type; type = GetDefinition(i); ++i)
+ {
+ if(GetDefCollectible(type))
+ {
+ if(attached->~IsProducerOf(worker, type))
+ {
+ ret[GetLength(ret)] = type;
+ }
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+func AttachObjectCondition(object caller)
+{
+ return _inherited(caller, Find_GrabPutGet(C4D_GrabGet), ...);
+}
+
+func AttachContextSend(object caller)
+{
+ [$SendObject$|Image=EX7I]
+ return SendMenu(caller);
+}
+
+func SensorCallbacks()
+{
+ var ret = [["$SendObject$", BindCallback("SendObjectThroughTube", [1, 2])]];
+ var attached = Attached();
+ if(attached)
+ {
+ for(var part in [GetSendableIDs(), GetSendableIDs(true)])
+ {
+ for(var id in part)
+ {
+ ret[GetLength(ret)] = [Format("$SendObjectType$", GetName(0, id)), BindCallback("SendObjectThroughTube", [Bind(id), 2]), id];
+ }
+ }
+ }
+ return ret;
+}
+
+func CheckWorker()
+{
+ if(worker && !worker->GetAlive())
+ {
+ worker = 0;
+ }
+ return worker;
+}
diff --git a/Items.c4d/Extractor.c4d/StringTblDE.txt b/Items.c4d/Extractor.c4d/StringTblDE.txt
new file mode 100644
index 0000000..8d03da9
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/StringTblDE.txt
@@ -0,0 +1,7 @@
+SelectTarget=Behälter wählen
+SendObject=Objekt senden
+SendObjectType=%s senden
+Buy=Kaufen
+SetWorker=Als Arbeiterclonk setzen
+WorkerSet=%s als Arbeiter gesetzt.
+WorkerUnset=Arbeiter entfernt.
diff --git a/Items.c4d/Extractor.c4d/StringTblUS.txt b/Items.c4d/Extractor.c4d/StringTblUS.txt
new file mode 100644
index 0000000..6f14e9d
--- /dev/null
+++ b/Items.c4d/Extractor.c4d/StringTblUS.txt
@@ -0,0 +1,7 @@
+SelectTarget=Select container
+SendObject=Send object
+SendObject=Send %s
+Buy=Buy
+SetWorker=Set als worker clonk
+WorkerSet=%s was set as worker clonk.
+WorkerUnset=Worker clonk was unset.
diff --git a/Items.c4d/Filler.c4d/ActMap.txt b/Items.c4d/Filler.c4d/ActMap.txt
new file mode 100644
index 0000000..010034a
--- /dev/null
+++ b/Items.c4d/Filler.c4d/ActMap.txt
@@ -0,0 +1,10 @@
+[Action]
+Name=Attach
+Procedure=ATTACH
+FacetBase=1
+AbortCall=Detached
+
+[Action]
+Name=Float
+Procedure=FLOAT
+FacetBase=1
diff --git a/Items.c4d/Filler.c4d/DefCore.txt b/Items.c4d/Filler.c4d/DefCore.txt
new file mode 100644
index 0000000..eb42525
--- /dev/null
+++ b/Items.c4d/Filler.c4d/DefCore.txt
@@ -0,0 +1,22 @@
+[DefCore]
+id=FI7I
+Name=Filler
+Version=4,9,5
+Category=C4D_Object|C4D_SelectMaterial|C4D_SelectKnowledge|C4D_SelectHomebase
+Width=11
+Height=11
+Offset=-5,-4
+Mass=10
+Value=5
+Components=METL=1;
+; Picture=10,0,64,64
+Vertices=4
+VertexX=0,-5,5
+VertexY=4,-4,-4
+VertexFriction=100,100,100
+Grab=2
+Collectible=1
+MaxUserSelect=10
+Rebuy=1
+NoPushEnter=1
+GrabPutGet=C4D_GrabPut
diff --git a/Items.c4d/Filler.c4d/Graphics.png b/Items.c4d/Filler.c4d/Graphics.png
new file mode 100644
index 0000000..2474ac2
--- /dev/null
+++ b/Items.c4d/Filler.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Filler.c4d/Names.txt b/Items.c4d/Filler.c4d/Names.txt
new file mode 100644
index 0000000..47abeb3
--- /dev/null
+++ b/Items.c4d/Filler.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Einfüllstutzen
+US:Filler
diff --git a/Items.c4d/Filler.c4d/Script.c b/Items.c4d/Filler.c4d/Script.c
new file mode 100644
index 0000000..7459a08
--- /dev/null
+++ b/Items.c4d/Filler.c4d/Script.c
@@ -0,0 +1,95 @@
+#strict 2
+#include UT7I
+
+func ControlDigDouble(object caller)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ return Detach();
+}
+
+func ControlThrow(object caller)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ if(!Contents(0, caller))
+ {
+ RetrieveObjectThroughTube();
+ return ClearLastPlrCom(GetController(caller)) || true;
+ }
+ return false;
+}
+
+func ControlUp(object caller)
+{
+ if(DenyContainedDirectCom())
+ {
+ return false;
+ }
+ return RetrieveMenu(caller);
+}
+
+func AttachObjectCondition(object caller)
+{
+ return _inherited(caller, Find_GrabPutGet(C4D_GrabPut), ...);
+}
+
+func Activate(object caller)
+{
+ return AttachMenu(caller, true);
+}
+
+func AcceptsObject(object obj, id id)
+{
+ var attached = Attached() || Contained();
+ return !attached || CanCollect(attached, obj, id);
+}
+
+func RejectCollect(id id, object obj)
+{
+ return !AcceptsObject(obj, id);
+}
+
+func Collection2(object obj)
+{
+ return TryPut(obj) || Exit(obj);
+}
+
+func SensorCallbacks()
+{
+ var ret = [["$RetrieveObject$", BindCallback("RetrieveObjectThroughTube", [1, 2])]];
+ for(var id in GetRetrievableIDs())
+ {
+ ret[GetLength(ret)] = [Format("$RetrieveObjectType$", GetName(0, id)), BindCallback("RetrieveObjectThroughTube", [Bind(id), 2]), id];
+ }
+ for(var part in GetRetrievableIDs(true))
+ {
+ for(var id in part[1])
+ {
+ ret[GetLength(ret)] = [Format("$RetrieveObjectType$", GetName(0, id)), BindCallback("RetrieveObjectThroughTube", [Bind(id), 2]), id];
+ }
+ }
+ return ret;
+}
+
+func AttachContextRetrieve(object caller)
+{
+ [$RetrieveObject$|Image=FI7I]
+ return RetrieveMenu(caller);
+}
+
+func IsProducerOf(object caller, id def)
+{
+ return CanRetrieveObject(def);
+}
+
+func HowToProduce(object caller, id def, object excludeOrigin)
+{
+ return SetRetrieveCommands(def, caller, true, excludeOrigin) || true;
+}
+
+func IsRetriever() { return true; }
diff --git a/Items.c4d/Filler.c4d/StringTblDE.txt b/Items.c4d/Filler.c4d/StringTblDE.txt
new file mode 100644
index 0000000..08d8e59
--- /dev/null
+++ b/Items.c4d/Filler.c4d/StringTblDE.txt
@@ -0,0 +1,2 @@
+RetrieveObject=Objekt anfordern
+RetrieveObjectType=%s anfordern
diff --git a/Items.c4d/Filler.c4d/StringTblUS.txt b/Items.c4d/Filler.c4d/StringTblUS.txt
new file mode 100644
index 0000000..4232fe2
--- /dev/null
+++ b/Items.c4d/Filler.c4d/StringTblUS.txt
@@ -0,0 +1,2 @@
+RetrieveObject=Request object
+RetrieveObjectType=Request %s
diff --git a/Items.c4d/Sender.c4d/ActMap.txt b/Items.c4d/Sender.c4d/ActMap.txt
new file mode 100644
index 0000000..498c7db
--- /dev/null
+++ b/Items.c4d/Sender.c4d/ActMap.txt
@@ -0,0 +1,4 @@
+[Action]
+Name=Attach
+Procedure=ATTACH
+FacetBase=1
diff --git a/Items.c4d/Sender.c4d/DefCore.txt b/Items.c4d/Sender.c4d/DefCore.txt
new file mode 100644
index 0000000..54dc7cd
--- /dev/null
+++ b/Items.c4d/Sender.c4d/DefCore.txt
@@ -0,0 +1,22 @@
+[DefCore]
+id=TX7I
+Name=Sender
+Version=4,9,5
+Category=C4D_Vehicle|C4D_SelectMaterial|C4D_SelectKnowledge|C4D_SelectHomebase
+Width=10
+Height=10
+Offset=-5,-5
+Mass=10
+Value=5
+Components=METL=1;
+Picture=10,0,64,64
+Vertices=1
+VertexFriction=10
+VertexY=5
+Grab=1
+Construction=1
+; Collectible=1
+MaxUserSelect=10
+Rebuy=1
+; NoPushEnter=1
+GrabPutGet=C4D_GrabPut
diff --git a/Items.c4d/Sender.c4d/Graphics.png b/Items.c4d/Sender.c4d/Graphics.png
new file mode 100644
index 0000000..554a751
--- /dev/null
+++ b/Items.c4d/Sender.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Sender.c4d/Names.txt b/Items.c4d/Sender.c4d/Names.txt
new file mode 100644
index 0000000..a88c76f
--- /dev/null
+++ b/Items.c4d/Sender.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Objektsender
+US:Object sender
diff --git a/Items.c4d/Sender.c4d/Script.c b/Items.c4d/Sender.c4d/Script.c
new file mode 100644
index 0000000..ef4ddaa
--- /dev/null
+++ b/Items.c4d/Sender.c4d/Script.c
@@ -0,0 +1,19 @@
+#strict 2
+
+func IsTubeTarget() { return true; }
+
+func Collection2(object obj)
+{
+ var line = FindObject2(Find_Tube(), Find_Func("CanSendObject", this, obj));
+ if(!line || !line->SendObject(this, obj))
+ {
+ Exit(obj);
+ }
+}
+
+func RejectCollect(id id, object obj)
+{
+ return obj && !FindObject2(Find_Tube(), Find_Func("CanSendObject", this, obj));
+}
+
+func IsAnvilProduct() { return true; }
diff --git a/Items.c4d/Sender.c4d/StringTblDE.txt b/Items.c4d/Sender.c4d/StringTblDE.txt
new file mode 100644
index 0000000..793a24a
--- /dev/null
+++ b/Items.c4d/Sender.c4d/StringTblDE.txt
@@ -0,0 +1 @@
+Press=Drücken
diff --git a/Items.c4d/Sender.c4d/StringTblUS.txt b/Items.c4d/Sender.c4d/StringTblUS.txt
new file mode 100644
index 0000000..690c11f
--- /dev/null
+++ b/Items.c4d/Sender.c4d/StringTblUS.txt
@@ -0,0 +1 @@
+Press=Press
diff --git a/Items.c4d/Tubekit.c4d/DefCore.txt b/Items.c4d/Tubekit.c4d/DefCore.txt
new file mode 100644
index 0000000..b65a2b3
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/DefCore.txt
@@ -0,0 +1,19 @@
+[DefCore]
+id=TK7I
+Version=4,9,8
+Name=Tubekit
+Category=C4D_Object|C4D_SelectMaterial|C4D_SelectKnowledge|C4D_SelectHomebase
+MaxUserSelect=10
+Width=8
+Height=6
+Offset=-4,-3
+Vertices=5
+VertexX=0,-4,-4,4,4
+VertexY=1,-3,2,-3,2
+VertexFriction=50,50,50,50,50
+Value=10
+Mass=15
+Components=METL=1
+Picture=8,0,80,60
+Rebuy=1
+Collectible=1
diff --git a/Items.c4d/Tubekit.c4d/Graphics.png b/Items.c4d/Tubekit.c4d/Graphics.png
new file mode 100644
index 0000000..b24ab47
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Tubekit.c4d/Names.txt b/Items.c4d/Tubekit.c4d/Names.txt
new file mode 100644
index 0000000..7dd4557
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Postrohrbausatz
+US:Tubekit
diff --git a/Items.c4d/Tubekit.c4d/Script.c b/Items.c4d/Tubekit.c4d/Script.c
new file mode 100644
index 0000000..25df4db
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Script.c
@@ -0,0 +1,85 @@
+#strict 2
+
+static const TK7I_Tube = TU7I;
+
+func Find_TubeTarget()
+{
+ return [C4FO_Func, "IsTubeTarget"];
+}
+
+func Activate(object caller)
+{
+ var line = FindObject2(Find_Tube(this)), lineEnd;
+ if(line)
+ {
+ lineEnd = line->GetLineEnd(this);
+ }
+ var targets = FindObjects(Find_AtRect(0, -9, 1, 18), Find_TubeTarget());
+ if(GetLength(targets) > 1)
+ {
+ caller->~CreateSelectionMenu("ConnectTarget", targets, GetID(), "$SelectTarget$", this);
+ }
+ else if(GetLength(targets) == 1)
+ {
+ ConnectTarget(0, targets[0]);
+ }
+ else
+ {
+ PlayerMessage(GetController(caller), "$TxtNoNewLine$", this);
+ }
+ return true;
+}
+
+func ConnectTarget(id id, object target)
+{
+ var line = FindObject2(Find_Line(TK7I_Tube, this)), lineEnd;
+ if(line)
+ {
+ lineEnd = line->GetLineEnd(this);
+ if(lineEnd == target)
+ {
+ PlayerMessage(GetController(Contained()), "$TxtLineRemoval$", Contained(), GetName(line));
+ RemoveObject(line);
+ return true;
+ }
+ }
+ else
+ {
+ line = CreateObject(TK7I_Tube);
+ CreateObject(TK7I_Tube)->Complement(line);
+ }
+
+ if(line->GetActionTarget(0) == lineEnd)
+ {
+ line->SetActionTargets(lineEnd || this, target);
+ }
+ else
+ {
+ line->SetActionTargets(target, lineEnd || this);
+ }
+
+ PlayerMessage(GetController(Contained()), "$TxtConnect$", Contained(), GetName(line), GetName(target));
+ if(lineEnd)
+ {
+ RemoveObject();
+ }
+ return true;
+}
+
+func AcceptsObject(object obj)
+{
+ return true;
+}
+
+func ReceiveObject(object obj, bool noReturn)
+{
+ obj->Enter(this);
+ Exit(obj);
+ if(Contained())
+ {
+ Put(Contained(), obj);
+ }
+ return true;
+}
+
+func IsAnvilProduct() { return true; }
diff --git a/Items.c4d/Tubekit.c4d/StringTblDE.txt b/Items.c4d/Tubekit.c4d/StringTblDE.txt
new file mode 100644
index 0000000..d700dc6
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/StringTblDE.txt
@@ -0,0 +1,7 @@
+TxtConnectline=Rohr anschließen
+TxtNoNewLine=Neues Rohr hier|nicht möglich.
+TxtLineRemoval=%s abgenommen.
+TxtNewLine=Konstruktion|%s.
+TxtNoConnectType=%s kann nicht an|%s angeschlossen werden.
+TxtConnect=%s an|%s angeschlossen
+SelectTarget=Rohrziel wählen
diff --git a/Items.c4d/Tubekit.c4d/StringTblUS.txt b/Items.c4d/Tubekit.c4d/StringTblUS.txt
new file mode 100644
index 0000000..3258152
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/StringTblUS.txt
@@ -0,0 +1,7 @@
+TxtConnectline=Connect tube
+TxtNoNewLine=Cannot create a new tube here.
+TxtLineRemoval=%s disconnected.
+TxtNewLine=New|%s
+TxtNoConnectType=%s cannot be connected|to %s.
+TxtConnect=%s connected|to %s
+SelectTarget=Select tube target
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/ActMap.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/ActMap.txt
new file mode 100644
index 0000000..2a84bf1
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/ActMap.txt
@@ -0,0 +1,4 @@
+[Action]
+Name=Connect
+Procedure=CONNECT
+FacetBase=1
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/DefCore.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/DefCore.txt
new file mode 100644
index 0000000..a312dcf
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/DefCore.txt
@@ -0,0 +1,13 @@
+[DefCore]
+id=TU7I
+Version=4,9,8
+Name=Tube
+Category=C4D_StaticBack
+Width=1
+Height=1
+Vertices=2
+Value=10
+Mass=15
+Components=METL=3
+Picture=0,0,36,32
+Line=C4D_LineColored
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/DescDE.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/DescDE.txt
new file mode 100644
index 0000000..9c1fb62
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/DescDE.txt
@@ -0,0 +1 @@
+Abfluß für Pumpen.
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/DescUS.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/DescUS.txt
new file mode 100644
index 0000000..75008ce
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/DescUS.txt
@@ -0,0 +1 @@
+Drain for pumps.
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/Graphics.png b/Items.c4d/Tubekit.c4d/Tube.c4d/Graphics.png
new file mode 100644
index 0000000..8b4683b
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/Names.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/Names.txt
new file mode 100644
index 0000000..cab7bf6
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Postrohr
+US:Mail tube
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/Script.c b/Items.c4d/Tubekit.c4d/Tube.c4d/Script.c
new file mode 100644
index 0000000..02d3796
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/Script.c
@@ -0,0 +1,305 @@
+#strict 2
+
+static TU7I_Speed;
+static const TU7I_NoCallback = -1;
+
+func Initialize()
+{
+ SetAction("Connect");
+
+ SetVertex(0, 0, GetX());
+ SetVertex(0, 1, GetY());
+
+ SetVertex(1, 0, GetX());
+ SetVertex(1, 1, GetY());
+
+ SetPosition(0, 0, this);
+
+ Local(0) = 2;
+ Local(1) = 2;
+
+ SetClrModulation(RGBa(255, 255, 255, 64));
+}
+
+func Complement(object line)
+{
+ if(line)
+ {
+ SetAction("Idle");
+ SetObjectOrder(this, line, true);
+ return AddEffect("ComplementLine", line, 1, 1, this);
+ }
+}
+
+func LineBreak(bool noMsg)
+{
+ if(!noMsg)
+ {
+ var source = GetActionTarget(0);
+ var target = GetActionTarget(1);
+
+ if(source && GetID(source) == TK7I)
+ {
+ Message("$TxtLinebroke$", source);
+ }
+ else
+ {
+ Message("$TxtLinebroke$", target);
+ }
+ }
+}
+
+func KitID()
+{
+ return TK7I;
+}
+
+func CanSendObject(object sender, object obj)
+{
+ var lineEnd = GetLineEnd(sender);
+ return lineEnd && lineEnd->~AcceptsObject(obj, 0, this);
+}
+
+func CanRetrieveObject(object receiver, id type, bool allowProduction, object excludeOrigin, object sender)
+{
+ if(ObjectOrigin(receiver) == excludeOrigin)
+ {
+ return false;
+ }
+ var lineEnd = sender || GetLineEnd(receiver);
+ return lineEnd && lineEnd->~HasObject(type, allowProduction, this, excludeOrigin);
+}
+
+func SendObject(object sender, object obj, arriveCallback, bool noReturn, object realTarget)
+{
+ var lineEnd = GetLineEnd(sender);
+ if(obj != lineEnd && (noReturn || (realTarget || lineEnd)->~AcceptsObject(obj, 0, this)))
+ {
+ var box = CreateObject(TC7I, 0, 0, GetOwner(obj));
+ SetObjectOrder(this, box, true);
+ if(!obj->Enter(box))
+ {
+ return false;
+ }
+ AddEffect("SendObject", box, 1, 1, this, 0, sender, noReturn, arriveCallback);
+ return true;
+ }
+ return false;
+}
+
+func RetrieveObject(object receiver, id type, arriveCallback, object excludeOrigin, failCallback, object realTarget)
+{
+ var lineEnd = GetLineEnd(receiver);
+ return lineEnd && CanRetrieveObject(receiver, type, true, excludeOrigin, lineEnd) && lineEnd->~RetrieveObject(this, type, arriveCallback, excludeOrigin, failCallback, realTarget);
+}
+
+func GetRetrievableObjects(object receiver)
+{
+ var lineEnd = GetLineEnd(receiver);
+ return lineEnd && lineEnd->~GetRetrievableObjects(this, ...);
+}
+
+func GetLineDistancePoint(int distance, int startVertex, int dir, int& x, int& y, int& r)
+{
+ if(!dir)
+ {
+ return;
+ }
+ var max = GetVertexNum() - 1, vertexDistance, vXC, vYC, vXN, vYN;
+ var rOff = 3 + (dir < 0);
+ for(var vertex = startVertex; Inside(vertex + dir, 0, max); vertex += dir)
+ {
+ vXC = GetVertex(vertex, VTX_X);
+ vYC = GetVertex(vertex, VTX_Y);
+ vXN = GetVertex(vertex + dir, VTX_X);
+ vYN = GetVertex(vertex + dir, VTX_Y);
+ var dist = Distance(vXC, vYC, vXN, vYN);
+ if(dist > distance)
+ {
+ var angle = Angle(vXC, vYC, vXN, vYN);
+ x = vXC + ((vXN - vXC) * distance + dist / 2) / dist - Cos(angle, rOff) * dir;
+ y = vYC + ((vYN - vYC) * distance + dist / 2) / dist - Sin(angle, rOff) * dir;
+ r = angle - 90;
+ return false;
+ }
+ else
+ {
+ distance -= dist;
+ }
+ }
+ x = vXN;
+ y = vYN;
+ return true;
+}
+
+func FxSendObjectStart(object target, int effectNumber, int temp, object sender, bool noReturn, arriveCallback)
+{
+ if(!temp)
+ {
+ EffectVar(0, target, effectNumber) = (GetActionTarget(1) == sender);
+ EffectVar(1, target, effectNumber) = noReturn;
+
+ EffectCall(target, effectNumber, "Timer");
+ EffectVar(2, target, effectNumber) = AddEffect("SendObjectHelper", this, 1, 0, this, 0, target);
+ EffectVar(3, target, effectNumber) = arriveCallback;
+ }
+}
+
+func FxSendObjectHelperStart(object target, int effectNumber, int temp, object box)
+{
+ if(!temp)
+ {
+ EffectVar(0, target, effectNumber) = box;
+ }
+}
+
+func FxSendObjectHelperStop(object target, int effectNumber, int reason, bool temp)
+{
+ if(!temp)
+ {
+ var box = EffectVar(0, target, effectNumber);
+ if(box)
+ {
+ RemoveObject(box, true);
+ }
+ }
+}
+
+func FxSendObjectTimer(object target, int effectNumber, int effectTime)
+{
+ var startTarget = EffectVar(0, target, effectNumber);
+ var dir = -(startTarget * 2 - 1);
+ var startVertex = 0;
+ if(startTarget)
+ {
+ startVertex = GetVertexNum() - 1;
+ }
+ var x, y, r, end;
+ r = target->GetR();
+ end = GetLineDistancePoint(effectTime * (TU7I_Speed || 20), startVertex, dir, x, y, r);
+
+ target->SetPosition(x, y);
+ target->SetR(r);
+ target->SetRDir(0);
+
+ if(end)
+ {
+ var lineEnd = GetActionTarget(!startTarget);
+ var noReturn = EffectVar(1, target, effectNumber);
+ var arriveCallback = EffectVar(3, target, effectNumber);
+ var cnt = target->ContentsCount();
+ for(var i = cnt - 1; i >= 0; --i)
+ {
+ var obj = target->Contents(i);
+ var ret;
+ if(!(ret = lineEnd->~ReceiveObject(obj, noReturn, arriveCallback, this)) && !noReturn)
+ {
+ SendObject(lineEnd, obj, 0, true);
+ }
+ else
+ {
+ if(arriveCallback && ret != TU7I_NoCallback)
+ {
+ lineEnd->CallA(arriveCallback, [obj, this, lineEnd], true);
+ }
+ }
+ }
+ return FX_Execute_Kill;
+ }
+}
+
+func FxSendObjectStop(object target, int effectNumber, int reason, bool temp)
+{
+ if(!temp)
+ {
+ RemoveObject(target, true);
+ RemoveEffect(0, this, EffectVar(2, target, effectNumber));
+ }
+}
+
+func FxComplementLineStart(object target, int effectNumber, int temp)
+{
+ if(!temp)
+ {
+ return EffectCall(target, effectNumber, "Timer");
+ }
+}
+
+func FxComplementLineTimer(object target, int effectNumber)
+{
+ var i;
+ var angle, prevAngle, midAngle;
+ var cnt = target->GetVertexNum();
+ for(i = 0; i < cnt; ++i)
+ {
+ var x = target->GetVertex(i, VTX_X);
+ var y = target->GetVertex(i, VTX_Y);
+
+ if(i < cnt - 1)
+ {
+ var nX = target->GetVertex(i + 1, VTX_X);
+ var nY = target->GetVertex(i + 1, VTX_Y);
+ angle = Angle(x, y, nX, nY);
+ }
+
+ if(i == 0)
+ {
+ midAngle = angle;
+ }
+ else if(i == cnt - 1)
+ {
+ midAngle = prevAngle;
+ }
+ else
+ {
+ midAngle = (angle + prevAngle) / 2;
+ if(Abs(angle - prevAngle) > 180)
+ {
+ midAngle += 180;
+ }
+ }
+
+ if(GetVertexNum() <= i)
+ {
+ AddVertex(x, y);
+ }
+ else
+ {
+ SetVertex(i, VTX_X, x - Cos(midAngle, 7));
+ SetVertex(i, VTX_Y, y - Sin(midAngle, 7));
+ }
+ prevAngle = angle;
+ }
+ for(var j = GetVertexNum() - 1; j >= i; --j)
+ {
+ RemoveVertex(j);
+ }
+}
+
+func FxComplementLineStop(object target, int effectNumber, int reason, bool temp)
+{
+ if(!temp)
+ {
+ RemoveObject();
+ }
+}
+
+func ObjectOrigin(object other)
+{
+ return GetLineEnd(other)->~ObjectOrigin();
+}
+
+func HasObjectOrigin(object origin)
+{
+ return GetLineEnd()->~ObjectOrigin() == origin || GetLineEnd(0, true)->~ObjectOrigin() == origin;
+}
+
+func IsTube()
+{
+ return true;
+}
+
+func FactoryLoad(object other)
+{
+ return GetLineEnd(other)->~FactoryLoad();
+}
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/StringTblDE.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/StringTblDE.txt
new file mode 100644
index 0000000..107e9e9
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/StringTblDE.txt
@@ -0,0 +1 @@
+TxtLinebroke=Rohr gebrochen
diff --git a/Items.c4d/Tubekit.c4d/Tube.c4d/StringTblUS.txt b/Items.c4d/Tubekit.c4d/Tube.c4d/StringTblUS.txt
new file mode 100644
index 0000000..cf1be94
--- /dev/null
+++ b/Items.c4d/Tubekit.c4d/Tube.c4d/StringTblUS.txt
@@ -0,0 +1 @@
+TxtLinebroke=Tube broke
diff --git a/Items.c4d/Utility.c4d/DefCore.txt b/Items.c4d/Utility.c4d/DefCore.txt
new file mode 100644
index 0000000..50f3078
--- /dev/null
+++ b/Items.c4d/Utility.c4d/DefCore.txt
@@ -0,0 +1,4 @@
+[DefCore]
+Name=Tube mail participants utility
+id=UT7I
+Category=C4D_StaticBack
diff --git a/Items.c4d/Utility.c4d/Graphics.png b/Items.c4d/Utility.c4d/Graphics.png
new file mode 100644
index 0000000..2326ee6
--- /dev/null
+++ b/Items.c4d/Utility.c4d/Graphics.png
Binary files differ
diff --git a/Items.c4d/Utility.c4d/Names.txt b/Items.c4d/Utility.c4d/Names.txt
new file mode 100644
index 0000000..ebf5848
--- /dev/null
+++ b/Items.c4d/Utility.c4d/Names.txt
@@ -0,0 +1,2 @@
+DE:Rohrpostteilnehmer-Hilfsfunktionen
+EN:Tube mail participants utility
diff --git a/Items.c4d/Utility.c4d/Script.c b/Items.c4d/Utility.c4d/Script.c
new file mode 100644
index 0000000..48cdc7c
--- /dev/null
+++ b/Items.c4d/Utility.c4d/Script.c
@@ -0,0 +1,421 @@
+#strict 2
+
+func AttachVertex() { return 3; }
+
+func Detach()
+{
+ if(Attached() || Floating())
+ {
+ SetAction("Idle");
+ return true;
+ }
+ return false;
+}
+
+func AttachObjectCondition(object caller)
+{
+ return Find_And(Find_Or(Find_OCF(OCF_Entrance | OCF_Collection), ...), Find_Not(Find_Category(C4D_Living)));
+}
+
+func AttachMenu(object caller, bool allowFloating)
+{
+ var targets = FindObjects(Find_AtPoint(), Find_Exclude(caller), Find_Exclude(this), AttachObjectCondition(caller));
+ if(allowFloating)
+ {
+ targets[GetLength(targets)] = [0, "$AttachWall$", GetID()];
+ }
+ return caller->~CreateSelectionMenu("AttachEntry", targets, GetID(), "$SelectTarget$", this);
+}
+
+func AttachEntry(id id, object target)
+{
+ if(target)
+ {
+ return Attach(target);
+ }
+ else
+ {
+ if(Contained())
+ {
+ Contained()->Exit(this); // TODO: maybe use scripted Float instead
+ }
+ return SetAction("Float");
+ }
+}
+
+func Attach(object target) // TODO: maybe use scripted Attach instead; check if target shape is still at own position; At best implement FilterObject(s) using Find_*-Filter at first und use that
+{ // WARNING: needs an action with the name "Attach", the Procedure "ATTACH" and an AbortCall of "Detached"
+ SetVertex(AttachVertex(), VTX_X, GetX(target) - GetX() + GetVertex(0, VTX_X, target), this, VTX_SetPermanentUpd);
+ SetVertex(AttachVertex(), VTX_Y, GetY(target) - GetY() + 5 + GetVertex(0, VTX_Y, target), this, VTX_SetPermanentUpd);
+
+ SetAction("Attach", target);
+ SetActionData(AttachVertex() << 8);
+}
+
+func UpdateTransferZone()
+{
+ if(Attached())
+ {
+ SetActionData(AttachVertex() << 8);
+ }
+}
+
+func IsTubeTarget() { return true; }
+
+func IsAnvilProduct() { return true; }
+
+func Attached(bool containerFallback) { return (GetAction() == "Attach" && GetActionTarget()) || (containerFallback && Contained()); }
+
+func Floating() { return GetAction() == "Float"; }
+
+func Detached()
+{
+ SetVertex(AttachVertex(), VTX_X, 0, this, VTX_SetPermanentUpd);
+ SetVertex(AttachVertex(), VTX_Y, 0, this, VTX_SetPermanentUpd);
+}
+
+func RejectEntrance(object container) { return Attached() && Contained(Attached()) != container || Floating(); }
+
+func CanRetrieveObject(id type, bool allowProduction, object excludeOrigin)
+{
+ return FindObject2(Find_Tube(), Find_Func("CanRetrieveObject", this, type, allowProduction, excludeOrigin), excludeOrigin && Find_Not(Find_Func("HasOrigin", excludeOrigin)));
+}
+
+func RetrieveObject() { return this->~RetrieveSendObject(...) || this->~RetrieveProduceObject(...); }
+
+func GetRetrievableIDs(bool produce)
+{
+ var lines = FindObjects(Find_Tube()), IDs = [], produceIDs = [];
+ for(var line in lines)
+ {
+ var lineIDs = line->GetRetrievableObjects(this);
+ if(lineIDs)
+ {
+ for(var id in lineIDs)
+ {
+ if(GetIndexOf(id, IDs) == -1)
+ {
+ IDs[GetLength(IDs)] = id;
+ }
+ }
+ }
+
+ if(produce)
+ {
+ produceIDs[GetLength(produceIDs)] = [line->GetLineEnd(this), line->GetRetrievableObjects(this, true)];
+ }
+ }
+ if(produce)
+ {
+ if(GetLength(produceIDs) == 0)
+ {
+ return [];
+ }
+ var ret = [], parts = [], part;
+ for(var i = 0; i < GetLength(produceIDs); ++i)
+ {
+ part = [];
+ if(produceIDs[i][1])
+ {
+ for(var id in produceIDs[i][1])
+ {
+ if(GetIndexOf(id, parts) == -1 && GetIndexOf(id, IDs) == -1)
+ {
+ part[GetLength(part)] = id;
+ parts[GetLength(parts)] = id;
+ }
+ }
+ if(GetLength(part) > 0)
+ {
+ ret[GetLength(ret)] = [produceIDs[i][0], part];
+ }
+ }
+ }
+ return ret;
+ }
+ return IDs;
+}
+
+func ProductionTitle() { return "$Production$"; }
+func ProductionSymbol() { return GetID(Attached() || this); }
+
+func RetrieveMenu(object caller, id selectID)
+{
+ var IDs = GetRetrievableIDs();
+ if(caller && CreateMenu(GetID(), caller, this, C4MN_Extra_None, "$SelectRetrieveType$", 0, C4MN_Style_Context))
+ {
+ if(!selectID)
+ {
+ SetCommand(caller, "None");
+ }
+ var i = 0;
+ for(var id in IDs)
+ {
+ AddMenuItem(GetName(0, id), "RetrieveMenuEntry", id, caller, 0, caller, 0, C4MN_Add_ForceNoDesc);
+ if(id == selectID)
+ {
+ SelectMenuItem(i, caller);
+ }
+ ++i;
+ }
+ IDs = GetRetrievableIDs(true);
+ if(GetLength(IDs) > 0)
+ {
+ for(var producer in IDs)
+ {
+ AddMenuItem(producer[0]->ProductionTitle(), 0, producer[0]->ProductionSymbol(), caller);
+ ++i;
+ for(var id in producer[1])
+ {
+ AddMenuItem(Format(" %s", GetName(0, id)), "RetrieveMenuEntry", id, caller, 0, caller, 0, C4MN_Add_ForceNoDesc);
+ if(id == selectID)
+ {
+ SelectMenuItem(i, caller);
+ }
+ ++i;
+ }
+ }
+ }
+ return true;
+ }
+}
+
+func RetrieveMenuEntry(id type, object caller)
+{
+ SetRetrieveCommands(type, caller, false);
+ RetrieveMenu(caller, type);
+}
+
+func SetRetrieveCommands(id type, object caller, bool indirect, object excludeOrigin)
+{
+ if((!indirect && ((!Attached() && !Floating()) || (GetProcedure(caller) == "PUSH" && GetActionTarget(0, caller) == this))) || !CommandRequireContainerOrPush(caller, Attached() || (Floating() && this), BindCallback((indirect && "RetrieveObjectForProduction") || "RetrieveMenuEntry", [Bind(type), 0, Bind(excludeOrigin)]), 0, indirect))
+ {
+ RetrieveObjectThroughTube(type);
+ }
+}
+
+func RetrieveObjectForProduction(id type, object caller, object excludeOrigin)
+{
+ RetrieveObjectThroughTube(type, BindCallback("ObjectReceived", [Bind(caller), 0]), excludeOrigin, BindCallback("ObjectReceivingFailed", [Bind(caller)]));
+ AddCommand(caller, "Wait", 0, 0, 0, 0, 0, -1);
+}
+
+func ObjectReceivingFailed(object caller)
+{
+ FinishCommand(caller, false);
+}
+
+func ObjectReceived(object caller, object obj)
+{
+ FinishCommand(caller, true);
+ FinishCommand(caller, true);
+ FinishCommand(caller, true);
+ AddCommand(caller, "Get", obj);
+}
+
+func RetrieveObjectThroughTube(id type, arriveCallback, object excludeOrigin, failCallback)
+{
+ var line = FindObject2(Find_Tube(), Find_Func("CanRetrieveObject", this, type, false, excludeOrigin)) || FindObject2(Find_Tube(), Find_Func("CanRetrieveObject", this, type, true, excludeOrigin), Sort_Func("FactoryLoad", this));
+ return line && line->RetrieveObject(this, type, arriveCallback, excludeOrigin, failCallback);
+}
+
+func SendableObjectConditions(object container)
+{
+ return Find_And(Find_OCF(OCF_Collectible), Find_OCF(OCF_Fullcon), ...);
+}
+
+func SendableConditions(object container)
+{
+ container = container || Attached();
+ if(container)
+ {
+ return Find_And(Find_Container(container), SendableObjectConditions(container));
+ }
+ else
+ {
+ return Find_Or();
+ }
+}
+
+func GetSendableIDs(bool production)
+{
+ var IDs = [];
+ var attached = Attached();
+ if(attached)
+ {
+ for(var obj in FindObjects(SendableConditions(attached)))
+ {
+ if(GetIndexOf(GetID(obj), IDs) == -1)
+ {
+ IDs[GetLength(IDs)] = GetID(obj);
+ }
+ }
+ }
+ if(production)
+ {
+ var produceIDs = this->~GetProducableIDs();
+ var ret = [];
+ if(produceIDs)
+ {
+ for(var id in produceIDs)
+ {
+ if(GetIndexOf(id, IDs) == -1)
+ {
+ ret[GetLength(ret)] = id;
+ }
+ }
+ }
+ return ret;
+ }
+ return IDs;
+}
+
+func FindSendableObject(id type)
+{
+ return FindObject2(SendableConditions(), type && Find_ID(type));
+}
+
+func SendMenu(object caller, id selectID)
+{
+ if(caller && CreateMenu(GetID(), caller, this, C4MN_Extra_None, "$SelectSendType$", 0, C4MN_Style_Context))
+ {
+ if(!selectID)
+ {
+ SetCommand(caller, "None");
+ }
+ var i = 0;
+ for(var id in GetSendableIDs())
+ {
+ AddMenuItem(GetName(0, id), "SendMenuEntry", id, caller, 0, caller, 0, C4MN_Add_ForceNoDesc);
+ if(id == selectID)
+ {
+ SelectMenuItem(i, caller);
+ }
+ ++i;
+ }
+ var IDs = GetSendableIDs(true);
+ if(GetLength(IDs) > 0)
+ {
+ AddMenuItem(ProductionTitle(), 0, ProductionSymbol(), caller);
+ ++i;
+ for(var id in IDs)
+ {
+ AddMenuItem(Format(" %s", GetName(0, id)), "SendMenuEntry", id, caller, 0, caller, 0, C4MN_Add_ForceNoDesc);
+ if(id == selectID)
+ {
+ SelectMenuItem(i, caller);
+ }
+ ++i;
+ }
+ }
+ return true;
+ }
+}
+
+func SendMenuEntry(id type, object caller)
+{
+ if(!Attached() || (GetProcedure(caller) == "PUSH" && GetActionTarget(0, caller) == this) || !CommandRequireContainerOrPush(caller, Attached(), BindCallback("SendMenuEntry", [Bind(type), 0])))
+ {
+ SendObjectThroughTube(type);
+ }
+ SendMenu(caller, type);
+}
+
+func SendObjectThroughTube(id type, object line, arriveCallback, object excludeOrigin, failCallback, object realTarget)
+{
+ var attached = Attached();
+ if(attached)
+ {
+ var objs = FindObjects(SendableConditions(attached), type && Find_ID(type));
+ if(GetLength(objs) > 0)
+ {
+ for(var obj in objs)
+ {
+ line = line || FindObject2(Find_Tube(), Find_Func("CanSendObject", this, obj));
+ if(line)
+ {
+ line->SendObject(this, obj, arriveCallback, false, realTarget);
+ break;
+ }
+ }
+ return true;
+ }
+ else if(type)
+ {
+ this->~RetrieveProduceObject(line, type, arriveCallback, excludeOrigin, failCallback, realTarget);
+ return true;
+ }
+ }
+}
+
+func DenyContainedDirectCom()
+{
+ return Contained();
+}
+
+func TryPut(object obj, object container)
+{
+ var attached = container || Attached() || Contained();
+ if(attached)
+ {
+ return Put(attached, obj);
+ }
+ else
+ {
+ return false;
+ }
+}
+
+func ReceiveObject(object obj, bool noReturn)
+{
+ Exit(obj);
+ SetPosition(GetX(), GetY(), obj);
+ return TryPut(obj) || (!Attached() && !Contained());
+}
+
+func IsRemoteControllable() { return false; }
+
+func CommandRequireContainerOrPush(object cmdTarget, object target, callback, object callbackTarget, bool add)
+{
+ if(add || (Contained(cmdTarget) != target && (GetProcedure(cmdTarget) != "PUSH" || GetActionTarget(0, cmdTarget) != target)))
+ {
+ if(add)
+ {
+ AddCommand(cmdTarget, "Call", this, callback, 0, callbackTarget || this, 0, "ClonkEnteredCallback");
+ AddCommand(cmdTarget, "Call", this, target, 0, 0, 0, "EnterOrGrabIfNotAlready");
+ }
+ else
+ {
+ AppendCommand(cmdTarget, "Call", this, target, 0, 0, 0, "EnterOrGrabIfNotAlready");
+ AppendCommand(cmdTarget, "Call", this, callback, 0, callbackTarget || this, 0, "ClonkEnteredCallback");
+ }
+ return true;
+ }
+ return false;
+}
+
+func EnterOrGrabIfNotAlready(object cmdTarget, object target)
+{
+ if(target && Contained(cmdTarget) != target && (GetProcedure(cmdTarget) != "PUSH" || GetActionTarget(0, cmdTarget) != target))
+ {
+ if(GetDefGrab(GetID(target)))
+ {
+ AddCommand(cmdTarget, "Grab", target);
+ }
+ else
+ {
+ AddCommand(cmdTarget, "Enter", target);
+ }
+ return true;
+ }
+}
+
+func ClonkEnteredCallback(object cmdTarget, callback, null, object callbackTarget)
+{
+ return callbackTarget && callbackTarget->Call(callback, cmdTarget);
+}
+
+func ObjectOrigin()
+{
+ return Attached(true) || this;
+}
diff --git a/Items.c4d/Utility.c4d/StringTblDE.txt b/Items.c4d/Utility.c4d/StringTblDE.txt
new file mode 100644
index 0000000..f3cf199
--- /dev/null
+++ b/Items.c4d/Utility.c4d/StringTblDE.txt
@@ -0,0 +1,5 @@
+SelectTarget=Behälter wählen
+SelectSendType=Zu sendendes Objekt wählen
+SelectRetrieveType=Anzuforderndes Objekt wählen
+Production=Produktion
+AttachWall=An Wand anbringen
diff --git a/Items.c4d/Utility.c4d/StringTblUS.txt b/Items.c4d/Utility.c4d/StringTblUS.txt
new file mode 100644
index 0000000..9c857cc
--- /dev/null
+++ b/Items.c4d/Utility.c4d/StringTblUS.txt
@@ -0,0 +1,5 @@
+SelectTarget=Select container
+SelectSendType=Choose object to send
+SelectRetrieveType=Select object to request
+Production=Production
+AttachWall=Attach to wall
diff --git a/System.c4g/ClonkAutoBuy.c b/System.c4g/ClonkAutoBuy.c
new file mode 100644
index 0000000..70a020a
--- /dev/null
+++ b/System.c4g/ClonkAutoBuy.c
@@ -0,0 +1,31 @@
+#strict 2
+#appendto CLNK
+
+func ControlCommandAcquire(object target, Tx, int Ty, object target2, id def)
+{ // TODO: decide based on distance to AvailableObject/Homebase and Retriever which one to use
+ if(!target) // TODO: avoid "stealing"
+ {
+ var retriever = FindObject2(Find_InRect(-500, -250, 1000, 500), Find_Func("IsRetriever"), Find_Func("CanRetrieveObject", def, false, target2), Find_Exclude(target), Sort_Distance());
+ if(retriever)
+ {
+ FinishCommand(this, true);
+ if(retriever->~HowToProduce(this, def, target2))
+ {
+ return true;
+ }
+ }
+ if(!GetAvailableObject (def, target2))
+ {
+ var produceRetriever = FindObject2(Find_InRect(-500, -250, 1000, 500), Find_Func("IsRetriever"), Find_Func("CanRetrieveObject", def, true, target2), Find_Exclude(target), Sort_Distance());
+ if(produceRetriever)
+ {
+ FinishCommand(this, true);
+ if(produceRetriever->~HowToProduce(this, def, target2))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return _inherited(target, Tx, Ty, target2, def);
+}
diff --git a/System.c4g/IDs.c b/System.c4g/IDs.c
new file mode 100644
index 0000000..3ddc963
--- /dev/null
+++ b/System.c4g/IDs.c
@@ -0,0 +1,10 @@
+#strict 2
+
+static const DT_TubeMail_Carrier = TC7I;
+static const DT_TubeMail_Distributor = DS7I;
+static const DT_TubeMail_Extractor = EX7I;
+static const DT_TubeMail_Filler = FI7I;
+static const DT_TubeMail_Transmitter = TX7I;
+static const DT_TubeMail_TubeKit = TK7I;
+static const DT_TubeMail_Tube = TU7I;
+static const DT_TubeMail_Utility = UT7I;
diff --git a/System.c4g/TestTargets.disabled b/System.c4g/TestTargets.disabled
new file mode 100644
index 0000000..7e987a7
--- /dev/null
+++ b/System.c4g/TestTargets.disabled
@@ -0,0 +1,7 @@
+#strict 2
+#appendto HUT1
+#appendto HUT2
+#appendto HUT3
+#appendto WRKS
+
+func IsTubeTarget() { return true; }
diff --git a/System.c4g/Utility.c b/System.c4g/Utility.c
new file mode 100644
index 0000000..1d58f8d
--- /dev/null
+++ b/System.c4g/Utility.c
@@ -0,0 +1,62 @@
+#strict 2
+
+global func GetLineEnd(object otherEnd, bool second, object line)
+{
+ line = line || this;
+ var ret = GetActionTarget(!!second, line);
+ if(otherEnd && otherEnd == ret)
+ {
+ ret = GetActionTarget(!second, line);
+ }
+ return ret;
+}
+
+global func Find_Tube(object target)
+{
+ return Find_Line(TK7I_Tube, target);
+}
+
+global func Find_Line(id type, object target)
+{
+ target = target || this;
+ return [C4FO_Func, "Find_LineCheck", type, target];
+}
+
+global func Find_LineCheck(id type, object target)
+{
+ return (!type || type == GetID(this)) && GetProcedure() == "CONNECT" && (GetActionTarget(0) == target || GetActionTarget(1) == target);
+}
+
+global func Put(object container, object obj)
+{
+ obj = obj || this;
+ if(container->GetOCF() & OCF_Collection)
+ {
+ return container->Collect(obj);
+ }
+ else
+ {
+ return CanCollect(container, obj, GetID(obj)) && obj->Enter(container) && (container->~Collection(obj, !(GetOCF(container) & OCF_Entrance)) || true);
+ }
+}
+
+global func CanCollect(object container, object obj, id id)
+{
+ id = id || GetID(obj);
+ var collectionLimit = GetDefCoreVal("CollectionLimit", "DefCore", GetID(container), 0);
+ if(!collectionLimit || container->ContentsCount() < collectionLimit)
+ {
+ return !container->~RejectCollect(id, obj) && (!obj || !obj->~RejectEntrance(container));
+ }
+ return false;
+}
+
+global func Find_GrabPutGet(int type)
+{
+ return [C4FO_Func, "Find_GrabPutGetCheck", type];
+}
+
+global func Find_GrabPutGetCheck(int type)
+{
+ return GetDefGrabPutGet(GetID()) & type;
+}