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