OpenClonk
C4Movement.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Object motion, collision, friction */
19 
20 #include "C4Include.h"
21 
22 #include "game/C4Physics.h"
23 #include "landscape/C4Landscape.h"
24 #include "landscape/C4SolidMask.h"
25 #include "object/C4Def.h"
26 #include "object/C4Object.h"
27 #include "script/C4Effect.h"
28 
29 /* Some physical constants */
30 
37 const C4Real HitSpeed1 = C4REAL100(150); // Hit Event
38 const C4Real HitSpeed2 = itofix(2); // Cross Check Hit
39 const C4Real HitSpeed3 = itofix(6); // Scale disable, kneel
40 const C4Real HitSpeed4 = itofix(8); // Flat
42 
43 /* Some helper functions */
44 
45 void RedirectForce(C4Real &from, C4Real &to, int32_t tdir)
46 {
47  C4Real force_redirected = std::min(Abs(from), FRedirect);
48  from -= force_redirected * Sign(from);
49  to += force_redirected * tdir;
50 }
51 
52 void ApplyFriction(C4Real &tval, int32_t percent)
53 {
54  C4Real ffric = FFriction * percent / 100;
55  if (tval > +ffric)
56  {
57  tval -= ffric;
58  }
59  else if (tval < -ffric)
60  {
61  tval += ffric;
62  }
63  else
64  {
65  tval = 0;
66  }
67 }
68 
69 // Compares all Shape.VtxContactCNAT[] CNAT flags to search flag.
70 // Returns true if CNAT match has been found.
71 
72 bool ContactVtxCNAT(C4Object *object, BYTE cnat_dir)
73 {
74  for (int32_t index = 0; index < object->Shape.VtxNum; index++)
75  {
76  if (object->Shape.VtxContactCNAT[index] & cnat_dir)
77  {
78  return true;
79  }
80  }
81  return false;
82 }
83 
84 // Finds first vertex with contact flag set.
85 // Returns -1/0/+1 for relation on vertex to object center.
86 
87 int32_t ContactVtxWeight(C4Object *object)
88 {
89  for (int32_t index = 0; index < object->Shape.VtxNum; index++)
90  {
91  if (object->Shape.VtxContactCNAT[index])
92  {
93  if (object->Shape.VtxX[index] < 0)
94  {
95  return -1;
96  }
97  if (object->Shape.VtxX[index] > 0)
98  {
99  return +1;
100  }
101  }
102  }
103  return 0;
104 }
105 
106 // ContactVtxFriction: Returns 0-100 friction value of first
107 // contacted vertex;
108 
109 int32_t ContactVtxFriction(C4Object *object)
110 {
111  for (int32_t index = 0; index < object->Shape.VtxNum; index++)
112  {
113  if (object->Shape.VtxContactCNAT[index])
114  {
115  return object->Shape.VtxFriction[index];
116  }
117  }
118  return 0;
119 }
120 
121 const char *CNATName(int32_t cnat)
122 {
123  switch (cnat)
124  {
125  case CNAT_None: return "None";
126  case CNAT_Left: return "Left";
127  case CNAT_Right: return "Right";
128  case CNAT_Top: return "Top";
129  case CNAT_Bottom: return "Bottom";
130  case CNAT_Center: return "Center";
131  }
132  return "Undefined";
133 }
134 
135 bool C4Object::Contact(int32_t iCNAT)
136 {
138  {
139  return !! Call(FormatString(PSF_Contact, CNATName(iCNAT)).getData());
140  }
141  return false;
142 }
143 
144 void C4Object::DoMotion(int32_t distance_x, int32_t distance_y)
145 {
146  RemoveSolidMask(true);
147  fix_x += distance_x;
148  fix_y += distance_y;
149 }
150 
151 void C4Object::StopAndContact(C4Real &contact_coordinate, C4Real limit, C4Real &speed, int32_t cnat)
152 {
153  contact_coordinate = limit;
154  speed = 0;
155  Contact(cnat);
156 }
157 
158 int32_t C4Object::ContactCheck(int32_t at_x, int32_t at_y, uint32_t *border_hack_contacts, bool collide_halfvehic)
159 {
160  // Check shape contact at given position
161  Shape.ContactCheck(at_x, at_y, border_hack_contacts, collide_halfvehic);
162 
163  // Store shape contact values in object t_contact
165 
166  // Contact script call for the first contacted cnat
167  if (Shape.ContactCNAT)
168  {
169  for (int32_t ccnat = 0; ccnat < 4; ccnat++) // Left, right, top bottom
170  {
171  // Will stop on first positive return contact call!
172  if ((Shape.ContactCNAT & (1 << ccnat)) && Contact(1 << ccnat))
173  {
174  break;
175  }
176  }
177  }
178 
179  // Return shape contact count
180  return Shape.ContactCount;
181 }
182 
183 // Stop the object and do contact calls if it collides with the border
185 {
186  // Layer bounds
188  {
189  C4PropList* pActionDef = GetAction();
190  if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
191  {
192  C4Real lbound = itofix(Layer->GetX() + Layer->Shape.GetX() - Shape.GetX());
193  C4Real rbound = itofix(Layer->GetX() + Layer->Shape.GetX() + Layer->Shape.Wdt - (Shape.GetX() + Shape.Wdt));
194  if (target_x < lbound)
195  {
196  StopAndContact(target_x, lbound, xdir, CNAT_Left);
197  }
198  if (target_x > rbound)
199  {
200  StopAndContact(target_x, rbound, xdir, CNAT_Right);
201  }
202  }
203  }
204  // Landscape bounds
205  C4Real lbound = itofix(0 - Shape.GetX());
206  C4Real rbound = itofix(::Landscape.GetWidth() - (Shape.GetX() + Shape.Wdt));
207  if (target_x < lbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
208  {
209  StopAndContact(target_x, lbound, xdir, CNAT_Left);
210  }
211  if (target_x > rbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
212  {
213  StopAndContact(target_x, rbound, xdir, CNAT_Right);
214  }
215 }
216 
218 {
219  // Layer bounds
221  {
222  C4PropList* pActionDef = GetAction();
223  if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
224  {
225  C4Real tbound = itofix(Layer->GetY() + Layer->Shape.GetY() - Shape.GetY());
226  C4Real bbound = itofix(Layer->GetY() + Layer->Shape.GetY() + Layer->Shape.Hgt - (Shape.GetY() + Shape.Hgt));
227  if (target_y < tbound)
228  {
229  StopAndContact(target_y, tbound, ydir, CNAT_Top);
230  }
231  if (target_y > bbound)
232  {
233  StopAndContact(target_y, bbound, ydir, CNAT_Bottom);
234  }
235  }
236  }
237  // Landscape bounds
238  C4Real tbound = itofix(0 - Shape.GetY());
239  C4Real bbound = itofix(::Landscape.GetHeight() - (Shape.GetY() + Shape.Hgt));
240  if (target_y < tbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Top))
241  {
242  StopAndContact(target_y, tbound, ydir, CNAT_Top);
243  }
244  if (target_y > bbound && (GetPropertyInt(P_BorderBound) & C4D_Border_Bottom))
245  {
246  StopAndContact(target_y, bbound, ydir, CNAT_Bottom);
247  }
248 }
249 
251 {
252  int contact_bits = 0;
253  bool has_moved = false;
254  bool has_contact = false;
255  bool has_turned = false;
256  bool lost_attachment = false;
257  bool redirected_force_from_ydir_to_rdir = false;
258  // Restrictions
259  if (Def->NoHorizontalMove)
260  {
261  xdir = 0;
262  }
263 
264  // Dig free target area
265  MovementDigFreeTargetArea();
266 
267  // Store previous movement and ocf
268  C4Real old_xdir(xdir);
269  C4Real old_ydir(ydir);
270  uint32_t old_ocf = OCF;
271 
272  // Store new target x and y
273  C4Real new_x = fix_x + xdir;
274  C4Real new_y = fix_y + ydir;
275 
276  // Apply bounds
277  SideBounds(new_x);
278  VerticalBounds(new_y);
279 
280  // Create a heuristic for moving diagonally, according to the initial speed.
281  // Motivation: At high velocities it will make a difference if you move
282  // horizontally at first exclusively (one might argue, that if you move e.g.
283  // 50 pixels horizontally first and then down one pixel it you not make
284  // much of a difference, but it does make a difference...).
285  // Idea: You have a major velocity component, which is the one
286  // with greater absolute value (after taking the boundaries into account).
287  // Instead of moving horizontally first and then vertically, you do
288  // a movement step in the minor velocity direction every n-th step,
289  // and step into major velocity direction the rest of the time.
290  int steps_x = Abs(fixtoi(new_x - fix_x));
291  int steps_y = Abs(fixtoi(new_y - fix_y));
292  int steps_total = steps_x + steps_y;
293  // Divide total steps by lower amount of steps, to find
294  // out when to do a step in the minor velocity component.
295  // Minimum value is 2, which means alternating steps
296  int step_mod;
297  bool prefer_vertical_movement = steps_y > steps_x;
298  if (prefer_vertical_movement)
299  {
300  step_mod = std::max(2, (1 + steps_total) / std::max(1, steps_x));
301  }
302  else
303  {
304  step_mod = std::max(2, (1 + steps_total) / std::max(1, steps_y));
305  }
306 
307 
308  // Move to target
309  int step_counter = 0;
310  do
311  {
312  step_counter = (step_counter + 1) % step_mod;
313 
314  // Set next step target
315  int step_x = 0;
316  int step_y = 0;
317 
318  // Do a step in the major velocity component direction
319  if (step_counter > 0)
320  {
321  if (prefer_vertical_movement)
322  {
323  step_y = Sign(fixtoi(new_y) - GetY());
324  }
325  else
326  {
327  step_x = Sign(fixtoi(new_x) - GetX());
328  }
329  }
330  else // Do a step in the minor velocity component direction
331  {
332  if (prefer_vertical_movement)
333  {
334  step_x = Sign(fixtoi(new_x) - GetX());
335  }
336  else
337  {
338  step_y = Sign(fixtoi(new_y) - GetY());
339  }
340  }
341 
342  if (Action.t_attach) // Attached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
343  {
344  int32_t step_target_x = GetX() + step_x;
345  int32_t step_target_y = GetY() + step_y;
346  // Attachment check
347  if (!Shape.Attach(step_target_x, step_target_y, Action.t_attach))
348  {
349  lost_attachment = true;
350  }
351  else
352  {
353  // Attachment change to step_target_x/step_target_y overrides target
354  if (step_target_x != GetX() + step_x)
355  {
356  xdir = Fix0;
357  new_x = itofix(step_target_x);
358  }
359  if (step_target_y != GetY() + step_y)
360  {
361  ydir = Fix0;
362  new_y = itofix(step_target_y);
363  }
364  }
365  // Contact check & evaluation
366  uint32_t border_hack_contacts = 0;
367  int32_t current_contacts = ContactCheck(step_target_x, step_target_y, &border_hack_contacts);
368  if (current_contacts || border_hack_contacts)
369  {
370  has_contact = true;
371  contact_bits |= border_hack_contacts | t_contact;
372  }
373  if (current_contacts)
374  {
375  // Abort movement
376  if (step_target_x != GetX())
377  {
378  step_target_x = GetX();
379  new_x = fix_x;
380  }
381  if (step_target_y != GetY())
382  {
383  step_target_y = GetY();
384  new_y = fix_y;
385  }
386  }
387  DoMotion(step_target_x - GetX(), step_target_y - GetY());
388  }
389  else // Unattached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
390  {
391  if (step_x != 0)
392  {
393  uint32_t border_hack_contacts = 0;
394  int32_t current_contacts = ContactCheck(GetX() + step_x, GetY(), &border_hack_contacts, false);
395  if (current_contacts || border_hack_contacts)
396  {
397  has_contact = true;
398  contact_bits |= t_contact | border_hack_contacts;
399  }
400  if (current_contacts)
401  {
402  // Abort horizontal movement
403  new_x = fix_x;
404  // Vertical redirection (always), this is important for pushing vehicles over bumps in the landscape
405  RedirectForce(xdir, ydir, -1);
407  // Update direction
408  new_y = fix_y + ydir;
409  VerticalBounds(new_y);
410  }
411  else // Free horizontal movement
412  {
413  DoMotion(step_x, 0);
414  has_moved = true;
415  }
416  }
417  if (step_y != 0)
418  {
419  int32_t current_contacts = ContactCheck(GetX(), GetY() + step_y, nullptr, ydir > 0);
420  if (current_contacts)
421  {
422  has_contact = true;
423  contact_bits |= t_contact;
424  new_y = fix_y;
425  // Vertical contact horizontal friction
427  // Redirection slide or rotate
428  if (!ContactVtxCNAT(this, CNAT_Left))
429  {
430  RedirectForce(ydir, xdir, -1);
431  }
432  else if (!ContactVtxCNAT(this, CNAT_Right))
433  {
434  RedirectForce(ydir, xdir, +1);
435  }
436  else
437  {
438  // Living things are always capable of keeping their rotation
439  if ((OCF & OCF_Rotate) && current_contacts == 1 && !Alive)
440  {
442  redirected_force_from_ydir_to_rdir = true;
443  }
444  ydir = 0;
445  }
446  }
447  else // Free vertical movement
448  {
449  DoMotion(0, step_y);
450  has_moved = true;
451  }
452  }
453  }
454  has_moved = true;
455  }
456  while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY());
457 
458  if (fix_x != new_x || fix_y != new_y)
459  {
460  has_moved = true;
461  RemoveSolidMask(true);
462  fix_x = new_x;
463  fix_y = new_y;
464  }
465  // Rotation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
466  if ((OCF & OCF_Rotate) && !!rdir)
467  {
468  C4Real target_r = fix_r + rdir * 5;
469  // Rotation limit
470  if (Def->Rotateable > 1)
471  {
472  if (target_r > itofix(Def->Rotateable))
473  {
474  target_r = itofix(Def->Rotateable);
475  rdir = 0;
476  }
477  if (target_r < itofix(-Def->Rotateable))
478  {
479  target_r = itofix(-Def->Rotateable);
480  rdir = 0;
481  }
482  }
483  int32_t current_x = GetX();
484  int32_t current_y = GetY();
485  // Move to target
486  while (fixtoi(fix_r) != fixtoi(target_r))
487  {
488  // Save step undos
489  C4Real old_rotation = fix_r;
490  C4Shape old_shape = Shape;
491  // Try next step
492  fix_r += Sign(target_r - fix_r);
493  UpdateShape();
494  // Attached rotation: rotate around attachment pos
495  if (Action.t_attach && !lost_attachment)
496  {
497  // More accurately, attachment should be evaluated by a rotation around the attachment vertex
498  // however, as long as this code is only used for some surfaces adjustment for large vehicles,
499  // it's enough to assume rotation around the center
500  current_x = GetX();
501  current_y = GetY();
502  // Evaluate attachment, but do not bother about attachment loss
503  // that will then be done in next execution cycle
504  Shape.Attach(current_x, current_y, Action.t_attach);
505  }
506  // Check for contact
507  int32_t current_contacts = ContactCheck(current_x, current_y);
508  if (current_contacts) // Contact
509  {
510  has_contact = true;
511  contact_bits |= t_contact;
512  // Undo step and abort movement
513  Shape = old_shape;
514  target_r = fix_r = old_rotation;
515  // Last UpdateShape-call might have changed sector lists!
516  UpdatePos();
517  // Redirect to GetY()
518  if (current_contacts == 1 && !redirected_force_from_ydir_to_rdir)
519  {
520  RedirectForce(rdir, ydir, -1);
521  }
522  // Stop rotation
523  rdir = 0;
524  }
525  else
526  {
527  has_turned = true;
528  if (current_x != GetX() || current_y != GetY())
529  {
530  fix_x = itofix(current_x);
531  fix_y = itofix(current_y);
532  }
533  }
534  }
535  // Circle bounds
536  if (target_r < -FixHalfCircle)
537  {
538  target_r += FixFullCircle;
539  }
540  if (target_r > +FixHalfCircle)
541  {
542  target_r -= FixFullCircle;
543  }
544  fix_r = target_r;
545  }
546  // Reput solid mask if moved by motion
547  if (has_moved || has_turned)
548  {
549  UpdateSolidMask(true);
550  }
551  // Misc checks ===========================================================================================
552  // InLiquid check
553  // this equals C4Object::UpdateLiquid, but the "fNoAttach = false;"-line
554  if (IsInLiquidCheck()) // In Liquid
555  {
556  if (!InLiquid) // Enter liquid
557  {
558  if ((OCF & OCF_HitSpeed2) && (Mass > 3))
559  {
560  Splash();
561  }
562  lost_attachment = false;
563  InLiquid = true;
564  }
565  }
566  else // Out of liquid
567  {
568  if (InLiquid) // Leave liquid
569  {
570  InLiquid = false;
571  }
572  }
573  // Contact Action
574  if (has_contact)
575  {
576  t_contact = contact_bits;
577  ContactAction();
578  }
579  // Attachment Loss Action
580  if (lost_attachment)
581  {
582  NoAttachAction();
583  }
584  // Movement Script Execution
585  if (has_contact)
586  {
587  C4AulParSet pars(fixtoi(old_xdir, 100), fixtoi(old_ydir, 100));
588  if (old_ocf & OCF_HitSpeed1)
589  {
590  Call(PSF_Hit, &pars);
591  }
592  if (old_ocf & OCF_HitSpeed2)
593  {
594  Call(PSF_Hit2, &pars);
595  }
596  if (old_ocf & OCF_HitSpeed3)
597  {
598  Call(PSF_Hit3, &pars);
599  }
600  }
601  // Update graphics to rotation
602  if (has_turned)
603  {
604  UpdateFace(true);
605  }
606  else
607  {
608  // Position has changed?
609  if (has_moved)
610  {
611  UpdatePos();
612  }
613  }
614 }
615 
617 {
618  // Definition allows stabilization?
619  if (Def->NoStabilize)
620  {
621  return;
622  }
623  // Normalize angle
624  C4Real normalized_rotation = fix_r;
625  while (normalized_rotation < itofix(-180))
626  {
627  normalized_rotation += 360;
628  }
629  while (normalized_rotation > itofix(180))
630  {
631  normalized_rotation -= 360;
632  }
633  if ((normalized_rotation != Fix0) && Inside<C4Real>(normalized_rotation, itofix(-StableRange), itofix(+StableRange)))
634  {
635  // Save step undos
636  C4Real old_rotation = fix_r;
637  C4Shape old_shape = Shape;
638  // Try rotation
639  fix_r = Fix0;
640  UpdateShape();
641  if (ContactCheck(GetX(), GetY()))
642  { // Undo rotation
643  Shape = old_shape;
644  fix_r = old_rotation;
645  }
646  else
647  { // Stabilization okay
648  UpdateFace(true);
649  }
650  }
651 }
652 
653 void C4Object::CopyMotion(C4Object *from_object)
654 {
655  // Designed for contained objects, no static
656  if (fix_x != from_object->fix_x || fix_y != from_object->fix_y)
657  {
658  fix_x = from_object->fix_x;
659  fix_y = from_object->fix_y;
660  // Resort into sectors
661  UpdatePos();
662  }
663  xdir = from_object->xdir;
664  ydir = from_object->ydir;
665 }
666 
667 void C4Object::ForcePosition(C4Real target_x, C4Real target_y)
668 {
669  fix_x = target_x;
670  fix_y = target_y;
671  UpdatePos();
672  UpdateSolidMask(false);
673 }
674 
675 void C4Object::MovePosition(int32_t distance_x, int32_t distance_y)
676 {
677  MovePosition(itofix(distance_x), itofix(distance_y));
678 }
679 
680 void C4Object::MovePosition(C4Real distance_x, C4Real distance_y)
681 {
682  // Move object position; repositions SolidMask
683  RemoveSolidMask(true);
684  fix_x += distance_x;
685  fix_y += distance_y;
686  UpdatePos();
687  UpdateSolidMask(true);
688 }
689 
690 
691 bool C4Object::ExecMovement() // Every Tick1 by Execute
692 {
693  // Update in which material this object is
694  UpdateInMat();
695 
696  // Containment check
697  if (Contained)
698  {
700 
701  return true;
702  }
703 
704  // General mobility check
705  if (Category & C4D_StaticBack)
706  {
707  return false;
708  }
709 
710  // Movement execution
711  if (Mobile) // Object is moving
712  {
713  // Move object
714  DoMovement();
715  // Demobilization check
716  if ((xdir == 0) && (ydir == 0) && (rdir == 0))
717  {
718  Mobile = false;
719  }
720  // Check for stabilization
721  if (rdir == 0)
722  {
723  Stabilize();
724  }
725  }
726  else // Object is static
727  {
728  // Check for stabilization
729  Stabilize();
730  // Check for mobilization
731  if (!::Game.iTick10)
732  {
733  // Gravity mobilization
734  xdir = ydir = rdir = 0;
735  Mobile = true;
736  }
737  }
738 
739  // Enforce zero rotation
740  if (!Def->Rotateable)
741  {
742  fix_r = Fix0;
743  }
744 
745  // Out of bounds check
746  if ((!Inside<int32_t>(GetX() + Shape.GetX(), -Shape.Wdt, ::Landscape.GetWidth()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
748  {
749  C4PropList* current_action = GetAction();
750  // Never remove attached objects: If they are truly outside landscape, their target will be removed,
751  // and the attached objects follow one frame later
752  if (!current_action || !Action.Target || current_action->GetPropertyP(P_Procedure) != DFA_ATTACH)
753  {
754  bool should_remove = true;
755  // Never remove HUD objects
756  if (Category & C4D_Parallax)
757  {
758  int parallaxity_x, parallaxity_y;
759  GetParallaxity(&parallaxity_x, &parallaxity_y);
760 
761  should_remove = false;
762  if (GetX() > ::Landscape.GetWidth() || GetY() > ::Landscape.GetHeight())
763  {
764  should_remove = true; // except if they are really out of the viewport to the right...
765  }
766  else if (GetX() < 0 && !!parallaxity_x)
767  {
768  should_remove = true; // ...or it's not HUD horizontally and it's out to the left
769  }
770  else if (!parallaxity_x && GetX() < -::Landscape.GetWidth())
771  {
772  should_remove = true; // ...or it's HUD horizontally and it's out to the left
773  }
774  }
775  if (should_remove)
776  {
777  AssignDeath(true);
778  AssignRemoval();
779  }
780  }
781  }
782  return true;
783 }
784 
785 void C4Object::MovementDigFreeTargetArea()
786 {
787  C4PropList* current_action = GetAction();
788  if (current_action && (current_action->GetPropertyInt(P_DigFree)))
789  {
790  int target_x = fixtoi(fix_x + xdir);
791  int target_y = fixtoi(fix_y + ydir);
792  // Shape size square
793  if (current_action->GetPropertyInt(P_DigFree) == 1)
794  {
795  ::Landscape.DigFreeRect(target_x + Shape.GetX(), target_y + Shape.GetY(), Shape.Wdt, Shape.Hgt, this);
796  }
797  // Free size round (variable size)
798  else
799  {
800  int32_t rad = current_action->GetPropertyInt(P_DigFree);
801  if (Con < FullCon)
802  {
803  rad = rad * 6 * Con / 5 / FullCon;
804  }
805  ::Landscape.DigFree(target_x, target_y - 1, rad, this);
806  }
807  }
808 }
809 
810 bool SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t min_density, int32_t max_density, int32_t &iterations)
811 {
812  bool hit_on_time = true;
813  bool break_main_loop = false;
814  int32_t target_x, target_y;
815  int32_t current_x = fixtoi(x);
816  int32_t current_y = fixtoi(y);
817  int32_t index = iterations;
818  do
819  {
820  if (!--index)
821  {
822  hit_on_time = false;
823  break;
824  }
825  // If the object isn't moving and there is no gravity either, abort
826  if (xdir == 0 && ydir == 0 && GravAccel == 0)
827  {
828  return false;
829  }
830  // If the object is above the landscape flying upwards in no/negative gravity, abort
831  if (ydir <= 0 && GravAccel <= 0 && current_y < 0)
832  {
833  return false;
834  }
835  // Set target position by momentum
836  x += xdir;
837  y += ydir;
838  // Set target position, then iterate towards this position, checking for contact
839  target_x = fixtoi(x);
840  target_y = fixtoi(y);
841  // Is the target position outside of the landscape?
842  if (!Inside<int32_t>(target_x, 0, ::Landscape.GetWidth()) || (target_y >= ::Landscape.GetHeight()))
843  {
844  return false;
845  }
846  // Check the way towards the target position, by doing minimal movement steps
847  do
848  {
849  // Set next step target
850  current_x += Sign(target_x - current_x);
851  current_y += Sign(target_y - current_y);
852  // Contact check
853  if (Inside(GBackDensity(current_x, current_y), min_density, max_density))
854  {
855  break_main_loop = true;
856  break;
857  }
858  }
859  while ((current_x != target_x) || (current_y != target_y));
860  // Adjust GravAccel once per frame
861  ydir += GravAccel;
862  }
863  while (!break_main_loop);
864  // Write position back
865  x = itofix(current_x);
866  y = itofix(current_y);
867 
868  // How many steps did it take to get here?
869  iterations -= index;
870 
871  return hit_on_time;
872 }
873 
874 bool SimFlightHitsLiquid(C4Real start_x, C4Real start_y, C4Real xdir, C4Real ydir)
875 {
876  // Start in water?
877  if (DensityLiquid(GBackDensity(fixtoi(start_x), fixtoi(start_y))))
878  {
879  int temp = 10;
880  if (!SimFlight(start_x, start_y, xdir, ydir, 0, C4M_Liquid - 1, temp))
881  {
882  return false;
883  }
884  }
885  // Hits liquid?
886  int temp = -1;
887  if (!SimFlight(start_x, start_y, xdir, ydir, C4M_Liquid, C4M_Vehicle, temp))
888  {
889  return false;
890  }
891  // Liquid & deep enough?
892  return GBackLiquid(fixtoi(start_x), fixtoi(start_y)) && GBackLiquid(fixtoi(start_x), fixtoi(start_y) + 9);
893 }
894 
const int32_t FullCon
Definition: C4Constants.h:181
const BYTE CNAT_Bottom
Definition: C4Constants.h:112
const uint32_t OCF_HitSpeed1
Definition: C4Constants.h:84
const int32_t C4M_Vehicle
Definition: C4Constants.h:171
const uint32_t OCF_Rotate
Definition: C4Constants.h:88
const uint32_t OCF_HitSpeed3
Definition: C4Constants.h:92
const BYTE CNAT_Center
Definition: C4Constants.h:113
const int32_t C4M_Liquid
Definition: C4Constants.h:174
const BYTE CNAT_Right
Definition: C4Constants.h:110
const BYTE CNAT_Top
Definition: C4Constants.h:111
const uint32_t OCF_HitSpeed2
Definition: C4Constants.h:91
const BYTE CNAT_None
Definition: C4Constants.h:108
const BYTE CNAT_Left
Definition: C4Constants.h:109
const int32_t C4D_Border_Bottom
Definition: C4Def.h:67
const int32_t C4D_StaticBack
Definition: C4Def.h:40
const int32_t C4D_Border_Layer
Definition: C4Def.h:68
const int32_t C4D_Border_Top
Definition: C4Def.h:66
const int32_t C4D_Parallax
Definition: C4Def.h:51
const int32_t C4D_Border_Sides
Definition: C4Def.h:65
#define PSF_Contact
Definition: C4GameScript.h:69
#define PSF_Hit
Definition: C4GameScript.h:49
#define PSF_Hit2
Definition: C4GameScript.h:50
#define PSF_Hit3
Definition: C4GameScript.h:51
C4Game Game
Definition: C4Globals.cpp:52
C4Landscape Landscape
bool GBackLiquid(int32_t x, int32_t y)
Definition: C4Landscape.h:239
bool DensityLiquid(int32_t dens)
Definition: C4Landscape.h:206
int32_t GBackDensity(int32_t x, int32_t y)
Definition: C4Landscape.h:224
const C4Real HitSpeed3
Definition: C4Movement.cpp:39
const C4Real FloatFriction
Definition: C4Movement.cpp:35
bool SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t min_density, int32_t max_density, int32_t &iterations)
Definition: C4Movement.cpp:810
const C4Real HitSpeed1
Definition: C4Movement.cpp:37
const C4Real HitSpeed2
Definition: C4Movement.cpp:38
const C4Real FFriction
Definition: C4Movement.cpp:32
bool SimFlightHitsLiquid(C4Real start_x, C4Real start_y, C4Real xdir, C4Real ydir)
Definition: C4Movement.cpp:874
bool ContactVtxCNAT(C4Object *object, BYTE cnat_dir)
Definition: C4Movement.cpp:72
const C4Real DefaultGravAccel
Definition: C4Movement.cpp:41
const C4Real RotateAccel
Definition: C4Movement.cpp:36
void RedirectForce(C4Real &from, C4Real &to, int32_t tdir)
Definition: C4Movement.cpp:45
const C4Real FixHalfCircle
Definition: C4Movement.cpp:34
const C4Real HitSpeed4
Definition: C4Movement.cpp:40
void ApplyFriction(C4Real &tval, int32_t percent)
Definition: C4Movement.cpp:52
int32_t ContactVtxWeight(C4Object *object)
Definition: C4Movement.cpp:87
const char * CNATName(int32_t cnat)
Definition: C4Movement.cpp:121
const C4Real FixFullCircle
Definition: C4Movement.cpp:33
const C4Real FRedirect
Definition: C4Movement.cpp:31
int32_t ContactVtxFriction(C4Object *object)
Definition: C4Movement.cpp:109
const int StableRange
Definition: C4Physics.h:23
#define GravAccel
Definition: C4Physics.h:27
C4Fixed itofix(int32_t x)
Definition: C4Real.h:261
int fixtoi(const C4Fixed &x)
Definition: C4Real.h:259
const C4Real Fix0
Definition: C4Real.h:312
C4Real C4REAL100(int x)
Definition: C4Real.h:267
@ P_DigFree
@ P_ContactCalls
@ P_BorderBound
@ DFA_ATTACH
@ P_Procedure
uint8_t BYTE
int Sign(T val)
Definition: Standard.h:45
T Abs(T val)
Definition: Standard.h:42
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
int32_t t_attach
Definition: C4Object.h:86
C4ObjectPtr Target
Definition: C4Object.h:87
int32_t NoStabilize
Definition: C4Def.h:137
int32_t Rotateable
Definition: C4Def.h:119
int32_t NoHorizontalMove
Definition: C4Def.h:122
Definition: C4Real.h:59
int32_t iTick10
Definition: C4Game.h:130
int32_t GetWidth() const
int32_t DigFreeRect(int32_t tx, int32_t ty, int32_t wdt, int32_t hgt, C4Object *by_object=nullptr, bool no_dig2objects=false, bool no_instability_check=false)
int32_t GetHeight() const
int32_t DigFree(int32_t tx, int32_t ty, int32_t rad, C4Object *by_object=nullptr, bool no_dig2objects=false, bool no_instability_check=false)
void UpdateSolidMask(bool fRestoreAttachedObjects)
C4Real ydir
Definition: C4Object.h:124
int32_t ContactCheck(int32_t at_x, int32_t at_y, uint32_t *border_hack_contacts=nullptr, bool collide_halfvehic=false)
Definition: C4Movement.cpp:158
C4Real fix_y
Definition: C4Object.h:123
void ContactAction()
C4PropList * GetAction() const
C4Real xdir
Definition: C4Object.h:124
void CopyMotion(C4Object *from)
Definition: C4Movement.cpp:653
void NoAttachAction()
void UpdateFace(bool bUpdateShape, bool fTemp=false)
bool ExecMovement()
Definition: C4Movement.cpp:691
C4Real fix_x
Definition: C4Object.h:123
uint32_t t_contact
Definition: C4Object.h:131
int32_t GetX() const
Definition: C4Object.h:285
int32_t Category
Definition: C4Object.h:111
void GetParallaxity(int32_t *parX, int32_t *parY) const
bool Contact(int32_t cnat)
Definition: C4Movement.cpp:135
bool IsInLiquidCheck() const
uint32_t OCF
Definition: C4Object.h:132
void StopAndContact(C4Real &ctco, C4Real limit, C4Real &speed, int32_t cnat)
Definition: C4Movement.cpp:151
C4Real fix_r
Definition: C4Object.h:123
void ForcePosition(C4Real target_x, C4Real target_y)
Definition: C4Movement.cpp:667
bool Mobile
Definition: C4Object.h:126
int32_t Con
Definition: C4Object.h:172
void DoMovement()
Definition: C4Movement.cpp:250
C4ObjectPtr Layer
Definition: C4Object.h:134
int32_t GetY() const
Definition: C4Object.h:286
void AssignDeath(bool fForced)
Definition: C4Object.cpp:467
bool Alive
Definition: C4Object.h:174
void AssignRemoval(bool exit_contents=false)
Definition: C4Object.cpp:215
void MovePosition(int32_t dx, int32_t dy)
Definition: C4Movement.cpp:675
void Stabilize()
Definition: C4Movement.cpp:616
C4ObjectPtr Contained
Definition: C4Object.h:142
C4Real rdir
Definition: C4Object.h:124
C4Action Action
Definition: C4Object.h:145
int32_t Mass
Definition: C4Object.h:113
bool InLiquid
Definition: C4Object.h:129
void VerticalBounds(C4Real &target_y)
Definition: C4Movement.cpp:217
C4Def * Def
Definition: C4Object.h:141
void UpdatePos()
void UpdateShape(bool bUpdateVertices=true)
Definition: C4Object.cpp:327
void SideBounds(C4Real &target_x)
Definition: C4Movement.cpp:184
void DoMotion(int32_t mx, int32_t my)
Definition: C4Movement.cpp:144
C4Shape Shape
Definition: C4Object.h:146
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val=0) const
Definition: C4PropList.cpp:855
C4PropertyName GetPropertyP(C4PropertyName k) const
Definition: C4PropList.cpp:824
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:114
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
int32_t GetX() const
Definition: C4Shape.h:62
bool ContactCheck(int32_t cx, int32_t cy, uint32_t *border_hack_contacts=nullptr, bool collide_halfvehic=false)
Definition: C4Shape.cpp:432
int32_t VtxContactCNAT[C4D_MaxVertex]
Definition: C4Shape.h:51
int32_t ContactCNAT
Definition: C4Shape.h:48
int32_t GetY() const
Definition: C4Shape.h:63
int32_t ContactCount
Definition: C4Shape.h:49
bool Attach(int32_t &cx, int32_t &cy, BYTE cnat_pos)
Definition: C4Shape.cpp:215
int32_t VtxX[C4D_MaxVertex]
Definition: C4Shape.h:43