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
31 const C4Real FRedirect=C4REAL100(50);
32 const C4Real FFriction=C4REAL100(30);
33 const C4Real FixFullCircle=itofix(360),FixHalfCircle=FixFullCircle/2;
34 const C4Real FloatFriction=C4REAL100(2);
35 const C4Real RotateAccel=C4REAL100(20);
36 const C4Real HitSpeed1=C4REAL100(150); // Hit Event
37 const C4Real HitSpeed2=itofix(2); // Cross Check Hit
38 const C4Real HitSpeed3=itofix(6); // Scale disable, kneel
39 const C4Real HitSpeed4=itofix(8); // Flat
40 const C4Real DefaultGravAccel=C4REAL100(20);
41
42 /* Some helper functions */
43
RedirectForce(C4Real & from,C4Real & to,int32_t tdir)44 void RedirectForce(C4Real &from, C4Real &to, int32_t tdir)
45 {
46 C4Real fred;
47 fred=std::min(Abs(from), FRedirect);
48 from-=fred*Sign(from);
49 to+=fred*tdir;
50 }
51
ApplyFriction(C4Real & tval,int32_t percent)52 void ApplyFriction(C4Real &tval, int32_t percent)
53 {
54 C4Real ffric=FFriction*percent/100;
55 if (tval>+ffric) { tval-=ffric; return; }
56 if (tval<-ffric) { tval+=ffric; return; }
57 tval=0;
58 }
59
60 // Compares all Shape.VtxContactCNAT[] CNAT flags to search flag.
61 // Returns true if CNAT match has been found.
62
ContactVtxCNAT(C4Object * cobj,BYTE cnat_dir)63 bool ContactVtxCNAT(C4Object *cobj, BYTE cnat_dir)
64 {
65 int32_t cnt;
66 bool fcontact=false;
67 for (cnt=0; cnt<cobj->Shape.VtxNum; cnt++)
68 if (cobj->Shape.VtxContactCNAT[cnt] & cnat_dir)
69 fcontact=true;
70 return fcontact;
71 }
72
73 // Finds first vertex with contact flag set.
74 // Returns -1/0/+1 for relation on vertex to object center.
75
ContactVtxWeight(C4Object * cobj)76 int32_t ContactVtxWeight(C4Object *cobj)
77 {
78 int32_t cnt;
79 for (cnt=0; cnt<cobj->Shape.VtxNum; cnt++)
80 if (cobj->Shape.VtxContactCNAT[cnt])
81 {
82 if (cobj->Shape.VtxX[cnt]<0) return -1;
83 if (cobj->Shape.VtxX[cnt]>0) return +1;
84 }
85 return 0;
86 }
87
88 // ContactVtxFriction: Returns 0-100 friction value of first
89 // contacted vertex;
90
ContactVtxFriction(C4Object * cobj)91 int32_t ContactVtxFriction(C4Object *cobj)
92 {
93 int32_t cnt;
94 for (cnt=0; cnt<cobj->Shape.VtxNum; cnt++)
95 if (cobj->Shape.VtxContactCNAT[cnt])
96 return cobj->Shape.VtxFriction[cnt];
97 return 0;
98 }
99
CNATName(int32_t cnat)100 const char *CNATName(int32_t cnat)
101 {
102 switch (cnat)
103 {
104 case CNAT_None: return "None";
105 case CNAT_Left: return "Left";
106 case CNAT_Right: return "Right";
107 case CNAT_Top: return "Top";
108 case CNAT_Bottom: return "Bottom";
109 case CNAT_Center: return "Center";
110 }
111 return "Undefined";
112 }
113
Contact(int32_t iCNAT)114 bool C4Object::Contact(int32_t iCNAT)
115 {
116 if (GetPropertyInt(P_ContactCalls))
117 {
118 return !! Call(FormatString(PSF_Contact, CNATName(iCNAT)).getData());
119 }
120 return false;
121 }
122
DoMotion(int32_t mx,int32_t my)123 void C4Object::DoMotion(int32_t mx, int32_t my)
124 {
125 if (pSolidMaskData) pSolidMaskData->Remove(true);
126 fix_x += mx; fix_y += my;
127 }
128
StopAndContact(C4Real & ctco,C4Real limit,C4Real & speed,int32_t cnat)129 void C4Object::StopAndContact(C4Real & ctco, C4Real limit, C4Real & speed, int32_t cnat)
130 {
131 ctco = limit;
132 speed = 0;
133 Contact(cnat);
134 }
135
ContactCheck(int32_t iAtX,int32_t iAtY,uint32_t * border_hack_contacts,bool collide_halfvehic)136 int32_t C4Object::ContactCheck(int32_t iAtX, int32_t iAtY, uint32_t *border_hack_contacts, bool collide_halfvehic)
137 {
138 // Check shape contact at given position
139 Shape.ContactCheck(iAtX,iAtY,border_hack_contacts,collide_halfvehic);
140
141 // Store shape contact values in object t_contact
142 t_contact=Shape.ContactCNAT;
143
144 // Contact script call for the first contacted cnat
145 if (Shape.ContactCNAT)
146 for (int32_t ccnat=0; ccnat<4; ccnat++) // Left, right, top bottom
147 if (Shape.ContactCNAT & (1<<ccnat))
148 if (Contact(1<<ccnat))
149 break; // Will stop on first positive return contact call!
150
151 // Return shape contact count
152 return Shape.ContactCount;
153 }
154
155 // Stop the object and do contact calls if it collides with the border
SideBounds(C4Real & ctcox)156 void C4Object::SideBounds(C4Real &ctcox)
157 {
158 // layer bounds
159 if (Layer && Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer)
160 {
161 C4PropList* pActionDef = GetAction();
162 if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
163 {
164 C4Real lbound = itofix(Layer->GetX() + Layer->Shape.GetX() - Shape.GetX()),
165 rbound = itofix(Layer->GetX() + Layer->Shape.GetX() + Layer->Shape.Wdt - (Shape.GetX() + Shape.Wdt));
166 if (ctcox < lbound) StopAndContact(ctcox, lbound, xdir, CNAT_Left);
167 if (ctcox > rbound) StopAndContact(ctcox, rbound, xdir, CNAT_Right);
168 }
169 }
170 // landscape bounds
171 C4Real lbound = itofix(0 - Shape.GetX()),
172 rbound = itofix(::Landscape.GetWidth() - (Shape.GetX() + Shape.Wdt));
173 if (ctcox < lbound && GetPropertyInt(P_BorderBound) & C4D_Border_Sides)
174 StopAndContact(ctcox, lbound, xdir, CNAT_Left);
175 if (ctcox > rbound && GetPropertyInt(P_BorderBound) & C4D_Border_Sides)
176 StopAndContact(ctcox, rbound, xdir, CNAT_Right);
177 }
178
VerticalBounds(C4Real & ctcoy)179 void C4Object::VerticalBounds(C4Real &ctcoy)
180 {
181 // layer bounds
182 if (Layer && Layer->GetPropertyInt(P_BorderBound) & C4D_Border_Layer)
183 {
184 C4PropList* pActionDef = GetAction();
185 if (!pActionDef || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
186 {
187 C4Real tbound = itofix(Layer->GetY() + Layer->Shape.GetY() - Shape.GetY()),
188 bbound = itofix(Layer->GetY() + Layer->Shape.GetY() + Layer->Shape.Hgt - (Shape.GetY() + Shape.Hgt));
189 if (ctcoy < tbound) StopAndContact(ctcoy, tbound, ydir, CNAT_Top);
190 if (ctcoy > bbound) StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom);
191 }
192 }
193 // landscape bounds
194 C4Real tbound = itofix(0 - Shape.GetY()),
195 bbound = itofix(::Landscape.GetHeight() - (Shape.GetY() + Shape.Hgt));
196 if (ctcoy < tbound && GetPropertyInt(P_BorderBound) & C4D_Border_Top)
197 StopAndContact(ctcoy, tbound, ydir, CNAT_Top);
198 if (ctcoy > bbound && GetPropertyInt(P_BorderBound) & C4D_Border_Bottom)
199 StopAndContact(ctcoy, bbound, ydir, CNAT_Bottom);
200 }
201
DoMovement()202 void C4Object::DoMovement()
203 {
204 int32_t iContact=0;
205 bool fAnyContact=false; int iContacts = 0;
206 BYTE fTurned=0,fRedirectYR=0,fNoAttach=0;
207 // Restrictions
208 if (Def->NoHorizontalMove) xdir=0;
209 // Dig free target area
210 C4PropList* pActionDef = GetAction();
211 if (pActionDef)
212 if (pActionDef->GetPropertyInt(P_DigFree))
213 {
214 int ctcox, ctcoy;
215 // Shape size square
216 if (pActionDef->GetPropertyInt(P_DigFree)==1)
217 {
218 ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir);
219 ::Landscape.DigFreeRect(ctcox+Shape.GetX(),ctcoy+Shape.GetY(),Shape.Wdt,Shape.Hgt,this);
220 }
221 // Free size round (variable size)
222 else
223 {
224 ctcox=fixtoi(fix_x+xdir); ctcoy=fixtoi(fix_y+ydir);
225 int32_t rad = pActionDef->GetPropertyInt(P_DigFree);
226 if (Con<FullCon) rad = rad*6*Con/5/FullCon;
227 ::Landscape.DigFree(ctcox,ctcoy-1,rad,this);
228 }
229 }
230
231 // store previous movement and ocf
232 C4Real oldxdir(xdir), oldydir(ydir);
233 uint32_t old_ocf = OCF;
234
235 bool fMoved = false;
236 C4Real new_x = fix_x + xdir;
237 C4Real new_y = fix_y + ydir;
238 SideBounds(new_x);
239
240 if (!Action.t_attach) // Unattached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
241 {
242 // Horizontal movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
243 // Move to target
244 while (fixtoi(new_x) != fixtoi(fix_x))
245 {
246 // Next step
247 int step = Sign(new_x - fix_x);
248 uint32_t border_hack_contacts = 0;
249 iContact=ContactCheck(GetX() + step, GetY(), &border_hack_contacts);
250 if (iContact || border_hack_contacts)
251 {
252 fAnyContact=true; iContacts |= t_contact | border_hack_contacts;
253 }
254 if (iContact)
255 {
256 // Abort horizontal movement
257 new_x = fix_x;
258 // Vertical redirection (always)
259 RedirectForce(xdir,ydir,-1);
260 ApplyFriction(ydir,ContactVtxFriction(this));
261 }
262 else // Free horizontal movement
263 {
264 DoMotion(step, 0);
265 fMoved = true;
266 }
267 }
268 // Vertical movement - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
269 // Movement target
270 new_y = fix_y + ydir;
271 // Movement bounds (vertical)
272 VerticalBounds(new_y);
273 // Move to target
274 while (fixtoi(new_y) != fixtoi(fix_y))
275 {
276 // Next step
277 int step = Sign(new_y - fix_y);
278 if ((iContact=ContactCheck(GetX(), GetY() + step, nullptr, ydir > 0)))
279 {
280 fAnyContact=true; iContacts |= t_contact;
281 new_y = fix_y;
282 // Vertical contact horizontal friction
283 ApplyFriction(xdir,ContactVtxFriction(this));
284 // Redirection slide or rotate
285 if (!ContactVtxCNAT(this,CNAT_Left))
286 RedirectForce(ydir,xdir,-1);
287 else if (!ContactVtxCNAT(this,CNAT_Right))
288 RedirectForce(ydir,xdir,+1);
289 else
290 {
291 // living things are always capable of keeping their rotation
292 if (OCF & OCF_Rotate) if (iContact==1) if (!Alive)
293 {
294 RedirectForce(ydir,rdir,-ContactVtxWeight(this));
295 fRedirectYR=1;
296 }
297 ydir=0;
298 }
299 }
300 else // Free vertical movement
301 {
302 DoMotion(0,step);
303 fMoved = true;
304 }
305 }
306 }
307 if (Action.t_attach) // Attached movement = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
308 {
309 VerticalBounds(new_y);
310 // Move to target
311 do
312 {
313 // Set next step target
314 int step_x = 0, step_y = 0;
315 if (fixtoi(new_x) != GetX())
316 step_x = Sign(fixtoi(new_x) - GetX());
317 else if (fixtoi(new_y) != GetY())
318 step_y = Sign(fixtoi(new_y) - GetY());
319 int32_t ctx = GetX() + step_x;
320 int32_t cty = GetY() + step_y;
321 // Attachment check
322 if (!Shape.Attach(ctx,cty,Action.t_attach))
323 fNoAttach=1;
324 else
325 {
326 // Attachment change to ctx/cty overrides target
327 if (ctx != GetX() + step_x)
328 {
329 xdir = Fix0; new_x = itofix(ctx);
330 }
331 if (cty != GetY() + step_y)
332 {
333 ydir = Fix0; new_y = itofix(cty);
334 }
335 }
336 // Contact check & evaluation
337 uint32_t border_hack_contacts = 0;
338 iContact=ContactCheck(ctx,cty,&border_hack_contacts);
339 if (iContact || border_hack_contacts)
340 {
341 fAnyContact=true; iContacts |= border_hack_contacts | t_contact;
342 }
343 if (iContact)
344 {
345 // Abort movement
346 if (ctx != GetX())
347 {
348 ctx = GetX(); new_x = fix_x;
349 }
350 if (cty != GetY())
351 {
352 cty = GetY(); new_y = fix_y;
353 }
354 }
355 DoMotion(ctx - GetX(), cty - GetY());
356 fMoved = true;
357 }
358 while (fixtoi(new_x) != GetX() || fixtoi(new_y) != GetY());
359 }
360
361 if(fix_x != new_x || fix_y != new_y)
362 {
363 fMoved = true;
364 if (pSolidMaskData) pSolidMaskData->Remove(true);
365 fix_x = new_x;
366 fix_y = new_y;
367 }
368 // Rotation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
369 if (OCF & OCF_Rotate && !!rdir)
370 {
371 C4Real target_r = fix_r + rdir * 5;
372 // Rotation limit
373 if (Def->Rotateable>1)
374 {
375 if (target_r > itofix(Def->Rotateable))
376 { target_r = itofix(Def->Rotateable); rdir=0; }
377 if (target_r < itofix(-Def->Rotateable))
378 { target_r = itofix(-Def->Rotateable); rdir=0; }
379 }
380 int32_t ctx=GetX(); int32_t cty=GetY();
381 // Move to target
382 while (fixtoi(fix_r) != fixtoi(target_r))
383 {
384 // Save step undos
385 C4Real lcobjr = fix_r; C4Shape lshape=Shape;
386 // Try next step
387 fix_r += Sign(target_r - fix_r);
388 UpdateShape();
389 // attached rotation: rotate around attachment pos
390 if (Action.t_attach && !fNoAttach)
391 {
392 // more accurately, attachment should be evaluated by a rotation around the attachment vertex
393 // however, as long as this code is only used for some surfaces adjustment for large vehicles,
394 // it's enough to assume rotation around the center
395 ctx=GetX(); cty=GetY();
396 // evaluate attachment, but do not bother about attachment loss
397 // that will then be done in next execution cycle
398 Shape.Attach(ctx,cty,Action.t_attach);
399 }
400 // check for contact
401 if ((iContact=ContactCheck(ctx,cty))) // Contact
402 {
403 fAnyContact=true; iContacts |= t_contact;
404 // Undo step and abort movement
405 Shape=lshape;
406 target_r = fix_r = lcobjr;
407 // last UpdateShape-call might have changed sector lists!
408 UpdatePos();
409 // Redirect to GetY()
410 if (iContact==1) if (!fRedirectYR)
411 RedirectForce(rdir,ydir,-1);
412 // Stop rotation
413 rdir=0;
414 }
415 else
416 {
417 fTurned=1;
418 if (ctx != GetX() || cty != GetY())
419 {
420 fix_x = itofix(ctx); fix_y = itofix(cty);
421 }
422 }
423 }
424 // Circle bounds
425 if (target_r < -FixHalfCircle) { target_r += FixFullCircle; }
426 if (target_r > +FixHalfCircle) { target_r -= FixFullCircle; }
427 fix_r = target_r;
428 }
429 // Reput solid mask if moved by motion
430 if (fMoved || fTurned) UpdateSolidMask(true);
431 // Misc checks ===========================================================================================
432 // InLiquid check
433 // this equals C4Object::UpdateLiquid, but the "fNoAttach=false;"-line
434 if (IsInLiquidCheck()) // In Liquid
435 {
436 if (!InLiquid) // Enter liquid
437 {
438 if (OCF & OCF_HitSpeed2)
439 if (Mass>3) Splash();
440 fNoAttach=false;
441 InLiquid=true;
442 }
443 }
444 else // Out of liquid
445 {
446 if (InLiquid) // Leave liquid
447 InLiquid=false;
448 }
449 // Contact Action
450 if (fAnyContact)
451 {
452 t_contact = iContacts;
453 ContactAction();
454 }
455 // Attachment Loss Action
456 if (fNoAttach)
457 NoAttachAction();
458 // Movement Script Execution
459 if (fAnyContact)
460 {
461 C4AulParSet pars(fixtoi(oldxdir, 100), fixtoi(oldydir, 100));
462 if (old_ocf & OCF_HitSpeed1) Call(PSF_Hit, &pars);
463 if (old_ocf & OCF_HitSpeed2) Call(PSF_Hit2, &pars);
464 if (old_ocf & OCF_HitSpeed3) Call(PSF_Hit3, &pars);
465 }
466 // Rotation gfx
467 if (fTurned)
468 UpdateFace(true);
469 else
470 // pos changed?
471 if (fMoved) UpdatePos();
472 }
473
Stabilize()474 void C4Object::Stabilize()
475 {
476 // def allows stabilization?
477 if (Def->NoStabilize) return;
478 // normalize angle
479 C4Real nr = fix_r;
480 while (nr < itofix(-180)) nr += 360;
481 while (nr > itofix(180)) nr -= 360;
482 if (nr != Fix0)
483 if (Inside<C4Real>(nr,itofix(-StableRange),itofix(+StableRange)))
484 {
485 // Save step undos
486 C4Real lcobjr=fix_r;
487 C4Shape lshape=Shape;
488 // Try rotation
489 fix_r=Fix0;
490 UpdateShape();
491 if (ContactCheck(GetX(),GetY()))
492 { // Undo rotation
493 Shape=lshape;
494 fix_r=lcobjr;
495 }
496 else
497 { // Stabilization okay
498 UpdateFace(true);
499 }
500 }
501 }
502
CopyMotion(C4Object * from)503 void C4Object::CopyMotion(C4Object *from)
504 {
505 // Designed for contained objects, no static
506 if (fix_x != from->fix_x || fix_y != from->fix_y)
507 {
508 fix_x=from->fix_x; fix_y=from->fix_y;
509 // Resort into sectors
510 UpdatePos();
511 }
512 xdir=from->xdir; ydir=from->ydir;
513 }
514
ForcePosition(C4Real tx,C4Real ty)515 void C4Object::ForcePosition(C4Real tx, C4Real ty)
516 {
517 fix_x=tx; fix_y=ty;
518 UpdatePos();
519 UpdateSolidMask(false);
520 }
521
MovePosition(int32_t dx,int32_t dy)522 void C4Object::MovePosition(int32_t dx, int32_t dy)
523 {
524 MovePosition(itofix(dx), itofix(dy));
525 }
526
MovePosition(C4Real dx,C4Real dy)527 void C4Object::MovePosition(C4Real dx, C4Real dy)
528 {
529 // move object position; repositions SolidMask
530 if (pSolidMaskData) pSolidMaskData->Remove(true);
531 fix_x+=dx;
532 fix_y+=dy;
533 UpdatePos();
534 UpdateSolidMask(true);
535 }
536
537
ExecMovement()538 bool C4Object::ExecMovement() // Every Tick1 by Execute
539 {
540 // update in which material this object is
541 UpdateInMat();
542
543 // Containment check
544 if (Contained)
545 {
546 CopyMotion(Contained);
547
548 return true;
549 }
550
551 // General mobility check
552 if (Category & C4D_StaticBack) return false;
553
554 // Movement execution
555 if (Mobile) // Object is moving
556 {
557 // Move object
558 DoMovement();
559 // Demobilization check
560 if ((xdir==0) && (ydir==0) && (rdir==0)) Mobile=false;
561 // Check for stabilization
562 if (rdir==0) Stabilize();
563 }
564 else // Object is static
565 {
566 // Check for stabilization
567 Stabilize();
568 // Check for mobilization
569 if (!::Game.iTick10)
570 {
571 // Gravity mobilization
572 xdir=ydir=rdir=0;
573 Mobile=true;
574 }
575 }
576
577 // Enforce zero rotation
578 if (!Def->Rotateable) fix_r=Fix0;
579
580 // Out of bounds check
581 if ((!Inside<int32_t>(GetX() + Shape.GetX(), -Shape.Wdt, ::Landscape.GetWidth()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Sides))
582 || ((GetY() + Shape.GetY() > ::Landscape.GetHeight()) && !(GetPropertyInt(P_BorderBound) & C4D_Border_Bottom)))
583 {
584 C4PropList* pActionDef = GetAction();
585 // Never remove attached objects: If they are truly outside landscape, their target will be removed,
586 // and the attached objects follow one frame later
587 if (!pActionDef || !Action.Target || pActionDef->GetPropertyP(P_Procedure) != DFA_ATTACH)
588 {
589 bool fRemove = true;
590 // never remove HUD objects
591 if (Category & C4D_Parallax)
592 {
593 int parX, parY;
594 GetParallaxity(&parX, &parY);
595 fRemove = false;
596 if (GetX()>::Landscape.GetWidth() || GetY()>::Landscape.GetHeight()) fRemove = true; // except if they are really out of the viewport to the right...
597 else if (GetX()<0 && !!parX) fRemove = true; // ...or it's not HUD horizontally and it's out to the left
598 else if (!parX && GetX()<-::Landscape.GetWidth()) fRemove = true; // ...or it's HUD horizontally and it's out to the left
599 }
600 if (fRemove)
601 {
602 AssignDeath(true);
603 AssignRemoval();
604 }
605 }
606 }
607 return true;
608 }
609
SimFlight(C4Real & x,C4Real & y,C4Real & xdir,C4Real & ydir,int32_t iDensityMin,int32_t iDensityMax,int32_t & iIter)610 bool SimFlight(C4Real &x, C4Real &y, C4Real &xdir, C4Real &ydir, int32_t iDensityMin, int32_t iDensityMax, int32_t &iIter)
611 {
612 bool hitOnTime = true;
613 bool fBreak = false;
614 int32_t ctcox,ctcoy,cx,cy,i;
615 cx = fixtoi(x); cy = fixtoi(y);
616 i = iIter;
617 do
618 {
619 if (!--i) {hitOnTime = false; break;}
620 // If the object isn't moving and there is no gravity either, abort
621 if (xdir == 0 && ydir == 0 && GravAccel == 0)
622 return false;
623 // If the object is above the landscape flying upwards in no/negative gravity, abort
624 if (ydir <= 0 && GravAccel <= 0 && cy < 0)
625 return false;
626 // Set target position by momentum
627 x+=xdir; y+=ydir;
628 // Movement to target
629 ctcox=fixtoi(x); ctcoy=fixtoi(y);
630 // Bounds
631 if (!Inside<int32_t>(ctcox,0,::Landscape.GetWidth()) || (ctcoy>=::Landscape.GetHeight()))
632 return false;
633 // Move to target
634 do
635 {
636 // Set next step target
637 cx+=Sign(ctcox-cx); cy+=Sign(ctcoy-cy);
638 // Contact check
639 if (Inside(GBackDensity(cx,cy), iDensityMin, iDensityMax))
640 { fBreak = true; break; }
641 }
642 while ((cx!=ctcox) || (cy!=ctcoy));
643 // Adjust GravAccel once per frame
644 ydir+=GravAccel;
645 }
646 while (!fBreak);
647 // write position back
648 x = itofix(cx); y = itofix(cy);
649
650 // how many steps did it take to get here?
651 iIter -= i;
652
653 return hitOnTime;
654 }
655
SimFlightHitsLiquid(C4Real fcx,C4Real fcy,C4Real xdir,C4Real ydir)656 bool SimFlightHitsLiquid(C4Real fcx, C4Real fcy, C4Real xdir, C4Real ydir)
657 {
658 // Start in water?
659 int temp;
660 if (DensityLiquid(GBackDensity(fixtoi(fcx), fixtoi(fcy))))
661 if (!SimFlight(fcx, fcy, xdir, ydir, 0, C4M_Liquid - 1, temp=10))
662 return false;
663 // Hits liquid?
664 if (!SimFlight(fcx, fcy, xdir, ydir, C4M_Liquid, 100, temp=-1))
665 return false;
666 // liquid & deep enough?
667 return GBackLiquid(fixtoi(fcx), fixtoi(fcy)) && GBackLiquid(fixtoi(fcx), fixtoi(fcy) + 9);
668 }
669
670