1 /**
2 Library_Wearable
3 Library for all clothing and other things worn on the clonk.
4 The library will make sure that not two objects are worn at the same
5 position at the same time.
6
7 @author: Clonkonaut
8 */
9
10 /* Bone names of attachment on the clonk and also identifiers */
11
12 // Headwear like helmets, caps or similar
13 static const WEARABLE_Head = "skeleton_head";
14
15 local wear_effect;
16
17 local display_disabled = false;
18
19 /* Overloads */
20
21 // These functions must exist in order for this library to work
22
GetWearPlace()23 public func GetWearPlace()
24 {
25 return; // must return one of the WEARABLE_* constants
26 }
27
28 /* Other functions that can be present in the object:
29
30 func GetWearBone: return the bone with which it is attached to the clonk (default: "main")
31 func GetWearTransform(clonk): transformation added when worn
32
33 func StayAfterDeath: return true if the item should remain on the clonk after its death (default is that it does not stay)
34
35 func OnPutOn(clonk): callback after the item was put on
36 func OnTakenOff(clonk): callback after the item was taken off
37
38 func OnDamage(damage_amount, cause, by_player): Callback whenever the wearer is damaged, parameters are passed on from the effect Damage callback
39 Return value is returned (useful for protective clothing)
40
41 func GetCarryMode: must(!) return CARRY_None whenever display_disabled is true, otherwise display error will likely occur
42
43 */
44
45 /* Engine Callbacks */
46
47 // It is assumed that a wearable must be contained in the clonk to be worn.
Departure()48 public func Departure()
49 {
50 if (IsWorn())
51 TakeOff();
52 _inherited(...);
53 }
54
Destruction()55 public func Destruction()
56 {
57 if (IsWorn())
58 TakeOff();
59 _inherited(...);
60 }
61
62 /* Interface */
63
64 // The clonk will put on the item and take off any other item currently worn
65 // in the same place, unless no_force is true in which case false will be returned
66 // when something else is worn.
PutOn(object clonk,bool no_force)67 public func PutOn(object clonk, bool no_force)
68 {
69 // ???
70 if (!clonk->~IsClonk()) return false;
71
72 // Remove all other things before putting on
73 if (!no_force)
74 {
75 var fx;
76 for (var i = GetEffectCount("FxWearing", clonk); fx = GetEffect("FxWearing", clonk, i - 1); i--)
77 if (fx.identifier == GetWearPlace())
78 fx->Remove();
79 }
80
81 // It is not impossible that the item is currently held in the hand of the clonk.
82 // If so, temporarily disable display because the same mesh cannot be attached twice.
83 // Any item must adhere this variable in GetCarryMode!
84 display_disabled =true;
85 clonk->~UpdateAttach();
86
87 wear_effect = clonk->CreateEffect(FxWearing, 2, nil, GetWearPlace(), this);
88
89 if (wear_effect == -1) // got rejected
90 wear_effect = nil;
91
92 display_disabled = false;
93 clonk->~UpdateAttach();
94
95 if (wear_effect)
96 {
97 // Callback to do whatever
98 this->~OnPutOn(clonk);
99 return true;
100 }
101
102 return false;
103 }
104
IsWorn()105 public func IsWorn()
106 {
107 return wear_effect;
108 }
109
TakeOff()110 public func TakeOff()
111 {
112 if (wear_effect)
113 wear_effect->Remove();
114 return;
115 }
116
TakenOff()117 public func TakenOff()
118 {
119 wear_effect = nil;
120 if (Contained())
121 Contained()->~UpdateAttach();
122 // Callback to do whatever; note that at this point the item isn't necessary contained.
123 this->~OnTakenOff();
124 }
125
126 // Returns whether the clonk has a free place for this wearable.
HasFreeWearPlace(object clonk)127 public func HasFreeWearPlace(object clonk)
128 {
129 var fx;
130 for (var i = GetEffectCount("FxWearing", clonk); fx = GetEffect("FxWearing", clonk, i - 1); i--)
131 if (fx.identifier == GetWearPlace())
132 return false;
133 return true;
134 }
135
IsWearable()136 public func IsWearable() { return true; }
137
138 /* Wearing effect */
139
140 local FxWearing = new Effect {
func(string wearing_identifier,object worn_item)141 Construction = func(string wearing_identifier, object worn_item)
142 {
143 // Save where this thing is worn
144 this.identifier = wearing_identifier;
145 // Save what is worn
146 this.item = worn_item;
147 },
148
149 Start = func()
150 {
151 // Check if parameters are properly set
152 if (this.identifier == nil) return -1;
153 if (this.item == nil) return -1;
154
155 var attachment_bone = this.item->~GetWearBone() ?? "main";
156 var attachment_transform = this.item->~GetWearTransform(this.Target);
157 var attachment_flags = this.item->~GetWearFlags(); // does not need a default value
158
159 this.attach = Target->AttachMesh(this.item, this.identifier, attachment_bone, attachment_transform, attachment_flags);
160 },
161
162 Damage = func(int damage, int cause, int by_player)
163 {
164 if (!this.item) return damage;
165
166 var ret = this.item->~OnDamage(damage, cause, by_player);
167 if (ret == nil)
168 ret = damage;
169 return ret;
170 },
171
172 Effect = func(string new_name, var1)
173 {
174 // Reject wearing effects if in the same place
175 if (new_name == "FxWearing")
176 if (var1 == this.identifier)
177 return -1;
178 },
179
180 Stop = func(int reason)
181 {
182 // Items can prevent being removed from the clonk on death
183 if (reason == FX_Call_RemoveDeath)
184 if (this.item && this.item->~StayAfterDeath(this.Target))
185 return -1;
186
187 if (this.Target) this.Target->DetachMesh(this.attach);
188 this.attach = nil;
189 },
190
191 Destruction = func()
192 {
193 if (this.attach != nil && this.Target)
194 this.Target->DetachMesh(this.attach);
195 if (this.item)
196 this.item->TakenOff();
197 }
198 };
199