1 /**
2 Objects.c
3 Functions generally applicable to objects; not enough to be worth distinct scripts though.
4
5 @author Maikel, boni, Ringwaul, Sven2, flgr, Clonkonaut, Günther, Randrian
6 */
7
8 // Does not set the speed of an object. But you can set two components of the velocity vector with this function.
9 // documented in /docs/sdk/script/fn
SetSpeed(int x_dir,int y_dir,int prec)10 global func SetSpeed(int x_dir, int y_dir, int prec)
11 {
12 SetXDir(x_dir, prec);
13 SetYDir(y_dir, prec);
14 return;
15 }
16
17 // Returns the speed of an object.
GetSpeed(int prec)18 global func GetSpeed(int prec)
19 {
20 return Sqrt(GetXDir(prec)**2 + GetYDir(prec)**2);
21 }
22
23 // You can add to the two components of the velocity vector individually with this function.
AddSpeed(int x_dir,int y_dir,int prec)24 global func AddSpeed(int x_dir, int y_dir, int prec)
25 {
26 SetXDir(GetXDir(prec) + x_dir, prec);
27 SetYDir(GetYDir(prec) + y_dir, prec);
28 }
29
30 // Sets an objects's speed and its direction, doesn't it?
31 // Can set either speed or angle of velocity, or both
SetVelocity(int angle,int speed,int precAng,int precSpd)32 global func SetVelocity(int angle, int speed, int precAng, int precSpd)
33 {
34 if (!precSpd) precSpd = 10;
35 if (!precAng) precAng = 1;
36 speed = speed ?? Distance(0, 0, GetXDir(precSpd), GetYDir(precSpd));
37 angle = angle ?? Angle(0, 0, GetXDir(precSpd), GetYDir(precSpd), precAng);
38
39 var x_dir = Sin(angle, speed, precAng);
40 var y_dir = -Cos(angle, speed, precAng);
41
42 SetXDir(x_dir, precSpd);
43 SetYDir(y_dir, precSpd);
44 return;
45 }
46
47 // Adds to an objects's speed and its direction:
48 // Can set either speed or angle of velocity, or both
AddVelocity(int angle,int speed,int precision_angle,int precision_speed)49 global func AddVelocity(int angle, int speed, int precision_angle, int precision_speed)
50 {
51 precision_speed = precision_speed ?? 10;
52 precision_angle = precision_angle ?? 1;
53 speed = speed ?? 0;
54 angle = angle ?? 0;
55
56 var current_x_dir = GetXDir(precision_speed);
57 var current_y_dir = GetYDir(precision_speed);
58
59 var x_dir = +Sin(angle, speed, precision_angle);
60 var y_dir = -Cos(angle, speed, precision_angle);
61
62 SetXDir(current_x_dir + x_dir, precision_speed);
63 SetYDir(current_y_dir + y_dir, precision_speed);
64 return;
65 }
66
67
68 // Sets the completion of this to new_con.
69 // documented in /docs/sdk/script/fn
SetCon(int new_con,int precision,bool grow_from_center)70 global func SetCon(int new_con, int precision, bool grow_from_center)
71 {
72 return DoCon(new_con - GetCon(), precision, grow_from_center);
73 }
74
GetObjAlpha()75 global func GetObjAlpha()
76 {
77 return (GetClrModulation() >> 24) & 0xFF;
78 }
79
80 // Sets the object's transparency.
SetObjAlpha(int by_alpha)81 global func SetObjAlpha(int by_alpha)
82 {
83 var clr_mod = GetClrModulation();
84
85 if (!clr_mod)
86 clr_mod = by_alpha << 24;
87 else
88 clr_mod = clr_mod & 16777215 | by_alpha << 24;
89 return SetClrModulation(clr_mod);
90 }
91
MakeInvincible(bool allow_fire)92 global func MakeInvincible(bool allow_fire)
93 {
94 if (!this) return false;
95 var fx = GetEffect("IntInvincible", this);
96 if (!fx) fx = AddEffect("IntInvincible", this, 300, 0);
97 if (!fx) return false;
98 fx.allow_fire = allow_fire;
99 if (!allow_fire && this->OnFire()) this->Extinguish();
100 fx.OnShockwaveHit = this.OnShockwaveHit;
101 fx.RejectWindbagForce = this.RejectWindbagForce;
102 fx.QueryCatchBlow = this.QueryCatchBlow;
103 this.OnShockwaveHit = Global.Invincibility_OnShockwaveHit;
104 this.RejectWindbagForce = Global.Invincibility_RejectWindbagForce;
105 this.QueryCatchBlow = Global.Invincibility_QueryCatchBlow;
106 return true;
107 }
108
IsInvincible()109 global func IsInvincible()
110 {
111 return !!GetEffect("IntInvincible", this);
112 }
113
SetInvincibility(bool to_val)114 global func SetInvincibility(bool to_val)
115 {
116 // Turn invincibility on or off
117 if (to_val)
118 return MakeInvincible(false);
119 else
120 return ClearInvincible();
121 }
122
FxIntInvincibleDamage(target)123 global func FxIntInvincibleDamage(target)
124 {
125 // avert all damage
126 return 0;
127 }
128
FxIntInvincibleEffect(string new_name,object target,proplist fx)129 global func FxIntInvincibleEffect(string new_name, object target, proplist fx)
130 {
131 // Block fire effects.
132 if (WildcardMatch(new_name, "*Fire*") && !fx.allow_fire)
133 return FX_Effect_Deny;
134 // All other effects are okay.
135 return FX_OK;
136 }
137
FxIntInvincibleSaveScen(object obj,proplist fx,proplist props)138 global func FxIntInvincibleSaveScen(object obj, proplist fx, proplist props)
139 {
140 // this is invincible. Save to scenario.
141 props->AddCall("Invincible", obj, "MakeInvincible", fx.allow_fire);
142 return true;
143 }
144
145 // Removes invincibility from object
ClearInvincible()146 global func ClearInvincible()
147 {
148 if (!this) return nil;
149 var fx = GetEffect("IntInvincible", this);
150 if (fx)
151 {
152 this.OnShockwaveHit = fx.OnShockwaveHit;
153 this.RejectWindbagForce = fx.RejectWindbagForce;
154 this.QueryCatchBlow = fx.QueryCatchBlow;
155 } else { // just to be sure
156 this.OnShockwaveHit = this->GetID().OnShockwaveHit;
157 this.RejectWindbagForce = this->GetID().RejectWindbagForce;
158 this.QueryCatchBlow = this->GetID().QueryCatchBlow;
159 }
160 return RemoveEffect("IntInvincible", this);
161 }
162
Invincibility_OnShockwaveHit()163 global func Invincibility_OnShockwaveHit()
164 {
165 return true;
166 }
167
Invincibility_RejectWindbagForce()168 global func Invincibility_RejectWindbagForce()
169 {
170 return true;
171 }
172
Invincibility_QueryCatchBlow()173 global func Invincibility_QueryCatchBlow()
174 {
175 return true;
176 }
177
178 // Move an object by the given parameters relative to its position.
MovePosition(int x,int y,int prec)179 global func MovePosition(int x, int y, int prec)
180 {
181 SetPosition(GetX(prec) + x, GetY(prec) + y, nil, prec);
182 }
183
184 // Returns the position as an array
GetPosition(int prec)185 global func GetPosition(int prec)
186 {
187 return [GetX(prec), GetY(prec)];
188 }
189
190 // Speed the calling object into the given direction (angle)
LaunchProjectile(int angle,int dist,int speed,int x,int y,int precAng,int precSpd,bool rel_x)191 global func LaunchProjectile(int angle, int dist, int speed, int x, int y, int precAng, int precSpd, bool rel_x)
192 {
193 // dist: Distance object travels on angle. Offset from calling object.
194 // x: X offset from container's center
195 // y: Y offset from container's center
196 // rel_x: if true, makes the X offset relative to container direction. (x=+30 will become x=-30 when Clonk turns left. This way offset always stays in front of a Clonk.)
197
198 var x_offset = x ?? Sin(angle, dist, precAng);
199 var y_offset = y ?? -Cos(angle, dist, precAng);
200
201 if(!precAng) precAng = 1;
202 if(!precSpd) precSpd = 10;
203
204 if (Contained() != nil && rel_x == true)
205 if (Contained()->GetDir() == 0)
206 x = -x;
207
208 if (Contained() != nil)
209 {
210 Exit(x_offset, y_offset, angle / precAng);
211 SetVelocity(angle, speed, precAng, precSpd);
212 return true;
213 }
214
215 if (Contained() == nil)
216 {
217 SetPosition(GetX() + x_offset, GetY() + y_offset);
218 SetR(angle/precAng);
219 SetVelocity(angle, speed, precAng, precSpd);
220 return true;
221 }
222 return false;
223 }
224
225 // Sets the MaxEnergy value of an object and does the necessary callbacks.
SetMaxEnergy(int value)226 global func SetMaxEnergy(int value)
227 {
228 if (!this)
229 return;
230 value *= 1000;
231 var old_maxenergy = this.MaxEnergy;
232 this.MaxEnergy = value;
233 // Change current energy percentage wise and implicit callback.
234 DoEnergy(GetEnergy() * (value - old_maxenergy) / old_maxenergy);
235 return;
236 }
237
238 // Returns the MaxEnergy value of an object.
GetMaxEnergy()239 global func GetMaxEnergy()
240 {
241 if (!this)
242 return;
243 return this.MaxEnergy / 1000;
244 }
245
246 // Returns whether an object is at full energy, that is if its energy is >= its MaxEnergy
HasMaxEnergy()247 global func HasMaxEnergy()
248 {
249 if (!this)
250 return false;
251 return GetEnergy() >= GetMaxEnergy();
252 }
253
254 // Sets the MaxBreath value of an object and does the necessary callbacks.
SetMaxBreath(int value)255 global func SetMaxBreath(int value)
256 {
257 if (!this)
258 return;
259 var old_maxbreath = this.MaxBreath;
260 this.MaxBreath = value;
261 // Change current breath percentage wise and implicit callback.
262 DoBreath(GetBreath() * (value - old_maxbreath) / old_maxbreath);
263 return;
264 }
265
266 // Returns the MaxBreath value of an object.
GetMaxBreath()267 global func GetMaxBreath()
268 {
269 if (!this)
270 return;
271 return this.MaxBreath;
272 }
273
274 // Makes an object gain Con until it is FullCon.
275 // value: the object grows approx. every second, in tenths of percent.
276 // max_size = the maximum object size in tenths of percent.
StartGrowth(int value,int max_size)277 global func StartGrowth(int value, int max_size)
278 {
279 if (value <= 0) return nil;
280 // Ensure max size is set and does not conflict with Oversize.
281 max_size = max_size ?? 1000;
282 if (!GetDefCoreVal("Oversize", "DefCore"))
283 max_size = Min(max_size, 1000);
284 var fx = AddEffect("IntGrowth", this, 1, 35, this, nil, value, max_size);
285 fx.Time = Random(35);
286 return fx;
287 }
288
StopGrowth()289 global func StopGrowth()
290 {
291 return RemoveEffect("IntGrowth", this);
292 }
293
GetGrowthValue()294 global func GetGrowthValue()
295 {
296 var fx = GetEffect("IntGrowth", this);
297 if (!fx)
298 return 0;
299 return fx.growth;
300 }
301
GetGrowthMaxSize()302 global func GetGrowthMaxSize()
303 {
304 var fx = GetEffect("IntGrowth", this);
305 if (!fx)
306 return 0;
307 return fx.max_size;
308 }
309
FxIntGrowthStart(object obj,effect fx,int temporary,int value,int max_size)310 global func FxIntGrowthStart(object obj, effect fx, int temporary, int value, int max_size)
311 {
312 if (!temporary)
313 {
314 fx.growth = value;
315 fx.max_size = max_size;
316 }
317 return FX_OK;
318 }
319
FxIntGrowthTimer(object obj,effect fx)320 global func FxIntGrowthTimer(object obj, effect fx)
321 {
322 if (obj->OnFire())
323 return FX_OK;
324 obj->DoCon(fx.growth, 1000);
325 // Negative growth might have removed the object.
326 if (!obj)
327 return FX_Execute_Kill;
328 var done = obj->GetCon(1000) >= fx.max_size;
329 return -done;
330 }
331
332 // Plays hit sounds for an average object made of stone or stone-like material.
333 // x and y need to be the parameters passed to Hit() from the engine.
StonyObjectHit(int x,int y)334 global func StonyObjectHit(int x, int y)
335 {
336 // Failsafe
337 if (!this) return false;
338 var xdir = GetXDir(), ydir = GetYDir();
339 if (x) x = x / Abs(x);
340 if (y) y = y / Abs(y);
341 // Check for solid in hit direction
342 var i = 0;
343 var average_obj_size = Distance(0,0, GetObjWidth(), GetObjHeight()) / 2 + 2;
344 while (!GBackSolid(x * i, y * i) && i < average_obj_size)
345 i++;
346 // To catch some high speed cases: if no solid found, check directly beneath
347 if (!GBackSolid(x * i, y * i))
348 while (!GBackSolid(x * i, y * i) && i < average_obj_size)
349 i++;
350 // Check if digfree
351 if (!GetMaterialVal("DigFree", "Material", GetMaterial(x*i, y*i)) && GBackSolid(x*i, y*i))
352 return Sound("Hits::Materials::Rock::RockHit?");
353 // Else play standard sound
354 if (Distance(0,0,xdir,ydir) > 10)
355 return Sound("Hits::SoftTouch?");
356 else
357 return Sound("Hits::SoftHit?");
358 }
359
360 // Removes all objects of the given type.
361 // documented in /docs/sdk/script/fn
RemoveAll(p,...)362 global func RemoveAll(p, ...)
363 {
364 var cnt;
365 if (GetType(p) == C4V_PropList) p = Find_ID(p); // RemoveAll(ID) shortcut
366 for (var obj in FindObjects(p, ...))
367 {
368 if (obj)
369 {
370 obj->RemoveObject();
371 cnt++;
372 }
373 }
374 return cnt;
375 }
376
377 // Pulls an object above ground if it was buried (e.g. by PlaceVegetation), mainly used by plants.
378 // The object must have 'Bottom' and 'Center' CNAT to use this.
379 // (bottom is the point which should be buried, center the lowest point that must not be buried)
RootSurface(int max_movement)380 global func RootSurface(int max_movement)
381 {
382 max_movement = max_movement ?? GetObjHeight() / 2;
383
384 if (HasCNAT(CNAT_Center))
385 {
386 var i = 0;
387 // Move up if too far underground.
388 while (GetContact(-1) & CNAT_Center && i < max_movement)
389 {
390 SetPosition(GetX(), GetY() - 1);
391 i++;
392 }
393 }
394 if (HasCNAT(CNAT_Bottom))
395 {
396 var i = 0;
397 // Move down if in midair.
398 while (!(GetContact(-1) & CNAT_Bottom) && i < max_movement)
399 {
400 SetPosition(GetX(), GetY() + 1);
401 i++;
402 }
403 // Try to make the plant stuck.
404 if (!Stuck())
405 SetPosition(GetX(), GetY() + 1);
406 }
407 }
408
409 // Buys an object. Returns the object if it could be bought.
410 // documented in /docs/sdk/script/fn
Buy(id buy_def,int for_plr,int pay_plr,object from_vendor,bool show_errors)411 global func Buy(id buy_def, int for_plr, int pay_plr, object from_vendor, bool show_errors)
412 {
413 // if no vendor is given try this
414 if (!from_vendor)
415 from_vendor = this;
416 // not a vendor?
417 if (!from_vendor->~IsVendor())
418 return nil;
419 return from_vendor->DoBuy(buy_def, for_plr, pay_plr, nil, false, show_errors);
420 }
421
422 // Sells an object. Returns true if it could be sold.
423 // documented in /docs/sdk/script/fn
Sell(int plr,object obj,object to_vendor)424 global func Sell(int plr, object obj, object to_vendor)
425 {
426 // if no vendor is given try this
427 if (!to_vendor)
428 to_vendor = this;
429 // not a vendor?
430 if (!to_vendor->~IsVendor())
431 return false;
432 return to_vendor->DoSell(obj, plr);
433 }
434
435 // GetXEdge returns the position of the objects top/bottom/left/right edge.
GetLeftEdge()436 global func GetLeftEdge()
437 {
438 return GetX() - GetObjWidth() / 2;
439 }
440
GetRightEdge()441 global func GetRightEdge()
442 {
443 return GetX() + GetObjWidth() / 2;
444 }
445
GetTopEdge()446 global func GetTopEdge()
447 {
448 return GetY() - GetObjHeight() / 2;
449 }
450
GetBottomEdge()451 global func GetBottomEdge()
452 {
453 return GetY() + GetObjHeight() / 2;
454 }
455
456 // Returns if the object is standing in front of the back-object
InFrontOf(object back)457 global func InFrontOf(object back)
458 {
459 var front = this;
460 if (!front)
461 return;
462 return front->FindObject(front->Find_AtPoint(), Find_Not(Find_Exclude(back))) != nil;
463 }
464
465 // Returns the current left of the object relative to its current position.
GetLeft()466 global func GetLeft()
467 {
468 var offset_x = GetObjectVal("Offset", nil, 0);
469 if (offset_x == nil)
470 offset_x = GetDefCoreVal("Offset", nil, 0);
471 return offset_x;
472 }
473
474 // Returns the current right of the object relative to its current position.
GetRight()475 global func GetRight()
476 {
477 var offset_x = GetObjectVal("Offset", nil, 0);
478 if (offset_x == nil)
479 offset_x = GetDefCoreVal("Offset", nil, 0);
480 var width = GetObjectVal("Width");
481 return offset_x + width;
482 }
483
484 // Returns the current top of the object relative to its current position.
GetTop()485 global func GetTop()
486 {
487 var offset_y = GetObjectVal("Offset", nil, 1);
488 if (offset_y == nil)
489 offset_y = GetDefCoreVal("Offset", nil, 1);
490 return offset_y;
491 }
492
493 // Returns the current bottom of the object relative to its current position.
GetBottom()494 global func GetBottom()
495 {
496 var offset_y = GetObjectVal("Offset", nil, 1);
497 if (offset_y == nil)
498 offset_y = GetDefCoreVal("Offset", nil, 1);
499 var height = GetObjectVal("Height");
500 return offset_y + height;
501 }
502
503 // Returns the current shape as an array [offset_x, offset_y, width, height].
GetShape()504 global func GetShape()
505 {
506 var offset_x = GetObjectVal("Offset", nil, 0);
507 if (offset_x == nil)
508 offset_x= GetDefCoreVal("Offset", nil, 0);
509 var offset_y = GetObjectVal("Offset", nil, 1);
510 if (offset_y == nil)
511 offset_y = GetDefCoreVal("Offset", nil, 1);
512 var width = GetObjectVal("Width");
513 var height = GetObjectVal("Height");
514 return [offset_x, offset_y, width, height];
515 }
516
GetEntranceRectangle()517 global func GetEntranceRectangle()
518 {
519 var entrance_x = GetDefCoreVal("Entrance", "DefCore", 0);
520 var entrance_y = GetDefCoreVal("Entrance", "DefCore", 1);
521 var entrance_wdt = GetDefCoreVal("Entrance", "DefCore", 2);
522 var entrance_hgt = GetDefCoreVal("Entrance", "DefCore", 3);
523 return [entrance_x, entrance_y, entrance_wdt, entrance_hgt];
524 }
525