OpenClonk
C4ObjectAction.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 /* Logic for C4Object: Actions */
19 
20 #include "C4Include.h"
22 #include "object/C4Object.h"
23 
24 #include "game/C4Physics.h"
26 #include "landscape/C4PXS.h"
27 #include "object/C4Command.h"
28 #include "object/C4Def.h"
29 #include "object/C4MeshAnimation.h"
30 #include "object/C4ObjectCom.h"
31 #include "platform/C4SoundSystem.h"
32 
33 
34 void GrabLost(C4Object *cObj, C4Object *prev_target)
35 {
36  // Grab lost script call on target (quite hacky stuff...)
37  if (prev_target && prev_target->Status) prev_target->Call(PSF_GrabLost);
38  // Clear commands down to first PushTo (if any) in command stack
39  for (C4Command *pCom=cObj->Command; pCom; pCom=pCom->Next)
40  if (pCom->Next && pCom->Next->Command==C4CMD_PushTo)
41  {
42  cObj->ClearCommand(pCom);
43  break;
44  }
45 }
46 
47 static void DoGravity(C4Object *cobj)
48 {
49  // Floatation in liquids
50  if (cobj->InLiquid && cobj->Def->Float)
51  {
52  cobj->ydir-=GravAccel * C4REAL100(80);
53  if (cobj->ydir<C4REAL100(-160)) cobj->ydir=C4REAL100(-160);
54  if (cobj->xdir<-FloatFriction) cobj->xdir+=FloatFriction;
55  if (cobj->xdir>+FloatFriction) cobj->xdir-=FloatFriction;
56  if (cobj->rdir<-FloatFriction) cobj->rdir+=FloatFriction;
57  if (cobj->rdir>+FloatFriction) cobj->rdir-=FloatFriction;
58  // Reduce upwards speed when about to leave liquid to prevent eternal moving up and down.
59  // Check both for no liquid one and two pixels above the surface, because the object could
60  // skip one pixel as its speed can be more than 100.
61  int32_t y_float = cobj->GetY() - 1 + cobj->Def->Float * cobj->GetCon() / FullCon;
62  if (!GBackLiquid(cobj->GetX(), y_float - 1) || !GBackLiquid(cobj->GetX(), y_float - 2))
63  if (cobj->ydir < 0)
64  {
65  // Reduce the upwards speed and set to zero for small values to prevent fluctuations.
66  cobj->ydir /= 2;
67  if (Abs(cobj->ydir) < C4REAL100(10))
68  cobj->ydir = 0;
69  }
70  }
71  // Free fall gravity
72  else if (~cobj->Category & C4D_StaticBack)
73  cobj->ydir+=GravAccel;
74 }
75 
76 bool ReduceLineSegments(C4Shape &rShape, bool fAlternate)
77 {
78  // try if line could go by a path directly when skipping on evertex. If fAlternate is true, try by skipping two vertices
79  for (int32_t cnt=0; cnt+2+fAlternate<rShape.VtxNum; cnt++)
80  if (PathFree(rShape.VtxX[cnt],rShape.VtxY[cnt],
81  rShape.VtxX[cnt+2+fAlternate],rShape.VtxY[cnt+2+fAlternate]))
82  {
83  if (fAlternate) rShape.RemoveVertex(cnt+2);
84  rShape.RemoveVertex(cnt+1);
85  return true;
86  }
87  return false;
88 }
89 
91 {
92  ObjectComStop(cobj);
93  cobj->AddCommand(C4CMD_Wait,nullptr,0,0,50);
94 }
95 
96 void Towards(C4Real &val, C4Real target, C4Real step)
97 {
98  if (val==target) return;
99  if (Abs(val-target)<=step) { val=target; return; }
100  if (val<target) val+=step; else val-=step;
101 }
102 
103 void C4Object::UpdateFace(bool bUpdateShape, bool fTemp)
104 {
105 
106  // Update shape - NOT for temp call, because temnp calls are done in drawing routine
107  // must not change sync relevant data here (although the shape and pos *should* be updated at that time anyway,
108  // because a runtime join would desync otherwise)
109  if (!fTemp) { if (bUpdateShape) UpdateShape(); else UpdatePos(); }
110 
111  // SolidMask
112  if (!fTemp) UpdateSolidMask(false);
113 
114  // Null defaults
115  TopFace.Default();
116 
117  // newgfx: TopFace only
118  if (Con>=FullCon || Def->GrowthType)
119  if (!Def->Rotateable || (fix_r == Fix0))
120  if (Def->TopFace.Wdt>0) // Fullcon & no rotation
121  TopFace.Set(GetGraphics()->GetBitmap(Color),
122  Def->TopFace.x,Def->TopFace.y,
124 
125  // Active face
127 }
128 
130 {
131  int32_t iFlipDir;
132  // We're active
133  C4PropList* pActionDef = GetAction();
134  if (pActionDef)
135  // Get flipdir value from action
136  if ((iFlipDir = pActionDef->GetPropertyInt(P_FlipDir)))
137  // Action dir is in flipdir range
138  if (Action.Dir >= iFlipDir)
139  {
140  // Calculate flipped drawing dir (from the flipdir direction going backwards)
141  Action.DrawDir = (iFlipDir - 1 - (Action.Dir - iFlipDir));
142  // Set draw transform, creating one if necessary
143  if (pDrawTransform)
145  else
147  // Done setting flipdir
148  return;
149  }
150  // No flipdir necessary
152  // Draw transform present?
153  if (pDrawTransform)
154  {
155  // reset flip dir
157  // if it's identity now, remove the matrix
158  if (pDrawTransform->IsIdentity())
159  {
160  delete pDrawTransform;
161  pDrawTransform=nullptr;
162  }
163  }
164 }
165 
167 {
168  C4Value value;
169  GetProperty(P_Action, &value);
170  return value.getPropList();
171 }
172 
173 bool C4Object::SetAction(C4PropList * Act, C4Object *pTarget, C4Object *pTarget2, int32_t iCalls, bool fForce)
174 {
175  C4Value vLastAction;
176  GetProperty(P_Action, &vLastAction);
177  C4PropList * LastAction = vLastAction.getPropList();
178  int32_t iLastPhase=Action.Phase;
179  C4Object *pLastTarget = Action.Target;
180  C4Object *pLastTarget2 = Action.Target2;
181  // No other action
182  if (LastAction)
183  if (LastAction->GetPropertyInt(P_NoOtherAction) && !fForce)
184  if (Act != LastAction)
185  return false;
186  // Set animation on instance. Abort if the mesh does not have
187  // such an animation.
188  if (pMeshInstance)
189  {
191  Action.Animation = nullptr;
192 
193  C4String* Animation = Act ? Act->GetPropertyStr(P_Animation) : nullptr;
194  if (Animation)
195  {
196  // note that weight is ignored
197  Action.Animation = pMeshInstance->PlayAnimation(Animation->GetData(), 0, nullptr, new C4ValueProviderAction(this), new C4ValueProviderConst(itofix(1)), true);
198  }
199  }
200  // Stop previous act sound
201  if (LastAction)
202  if (Act != LastAction)
203  if (LastAction->GetPropertyStr(P_Sound))
204  StopSoundEffect(LastAction->GetPropertyStr(P_Sound)->GetCStr(),this);
205  // Unfullcon objects no action
206  if (Con<FullCon)
207  if (!Def->IncompleteActivity)
208  Act = nullptr;
209  // Reset action time on change
210  if (Act!=LastAction)
211  {
212  Action.Time=0;
213  // reset action data and targets if procedure is changed
214  if ((Act ? Act->GetPropertyP(P_Procedure) : -1)
215  != (LastAction ? LastAction->GetPropertyP(P_Procedure) : -1))
216  {
217  Action.Data = 0;
218  Action.Target = nullptr;
219  Action.Target2 = nullptr;
220  }
221  }
222  // Set new action
225  // Set target if specified
226  if (pTarget) Action.Target=pTarget;
227  if (pTarget2) Action.Target2=pTarget2;
228  // Set Action Facet
230  // update flipdir
231  if ((LastAction ? LastAction->GetPropertyInt(P_FlipDir) : 0)
232  != (Act ? Act->GetPropertyInt(P_FlipDir) : 0)) UpdateFlipDir();
233  // Start act sound
234  if (Act)
235  if (Act != LastAction)
236  if (Act->GetPropertyStr(P_Sound))
237  StartSoundEffect(Act->GetPropertyStr(P_Sound)->GetCStr(),+1,100,this);
238  // Reset OCF
239  SetOCF();
240  // issue calls
241  // Execute EndCall for last action
242  if (iCalls & SAC_EndCall && !fForce)
243  if (LastAction)
244  {
245  if (LastAction->GetPropertyStr(P_EndCall))
246  {
247  C4Def *pOldDef = Def;
248  Call(LastAction->GetPropertyStr(P_EndCall)->GetCStr());
249  // abort exeution if def changed
250  if (Def != pOldDef || !Status) return true;
251  }
252  }
253  // Execute AbortCall for last action
254  if (iCalls & SAC_AbortCall && !fForce)
255  if (LastAction)
256  {
257  if (LastAction->GetPropertyStr(P_AbortCall))
258  {
259  C4Def *pOldDef = Def;
260  if (pLastTarget && !pLastTarget->Status) pLastTarget = nullptr;
261  if (pLastTarget2 && !pLastTarget2->Status) pLastTarget2 = nullptr;
262  Call(LastAction->GetPropertyStr(P_AbortCall)->GetCStr(), &C4AulParSet(iLastPhase, pLastTarget, pLastTarget2));
263  // abort exeution if def changed
264  if (Def != pOldDef || !Status) return true;
265  }
266  }
267  // Execute StartCall for new action
268  if (iCalls & SAC_StartCall)
269  if (Act)
270  {
271  if (Act->GetPropertyStr(P_StartCall))
272  {
273  C4Def *pOldDef = Def;
275  // abort exeution if def changed
276  if (Def != pOldDef || !Status) return true;
277  }
278  }
279 
280  C4Def *pOldDef = Def;
281  Call(PSF_OnActionChanged, &C4AulParSet(LastAction ? LastAction->GetName() : "Idle"));
282  if (Def != pOldDef || !Status) return true;
283 
284  return true;
285 }
286 
288 {
289  // Default: no action face
290  Action.Facet.Default();
291  // Active: get action facet from action definition
292  C4PropList* pActionDef = GetAction();
293  if (pActionDef)
294  {
295  if (pActionDef->GetPropertyInt(P_Wdt)>0)
296  {
297  Action.Facet.Set(GetGraphics()->GetBitmap(Color),
298  pActionDef->GetPropertyInt(P_X),pActionDef->GetPropertyInt(P_Y),
299  pActionDef->GetPropertyInt(P_Wdt),pActionDef->GetPropertyInt(P_Hgt));
300  Action.FacetX=pActionDef->GetPropertyInt(P_OffX);
301  Action.FacetY=pActionDef->GetPropertyInt(P_OffY);
302  }
303  }
304 }
305 
307  C4Object *pTarget, C4Object *pTarget2,
308  int32_t iCalls, bool fForce)
309 {
310  assert(ActName);
311  // If we get the null string or ActIdle by name, set ActIdle
312  if (!ActName || ActName == &Strings.P[P_Idle])
313  return SetAction(nullptr,nullptr,nullptr,iCalls,fForce);
314  C4Value ActMap; GetProperty(P_ActMap, &ActMap);
315  if (!ActMap.getPropList()) return false;
316  C4Value Action; ActMap.getPropList()->GetPropertyByS(ActName, &Action);
317  if (!Action.getPropList()) return false;
318  return SetAction(Action.getPropList(),pTarget,pTarget2,iCalls,fForce);
319 }
320 
321 bool C4Object::SetActionByName(const char * szActName,
322  C4Object *pTarget, C4Object *pTarget2,
323  int32_t iCalls, bool fForce)
324 {
325  C4String * ActName = Strings.RegString(szActName);
326  ActName->IncRef();
327  bool r = SetActionByName(ActName, pTarget, pTarget2, iCalls, fForce);
328  ActName->DecRef();
329  return r;
330 }
331 
332 void C4Object::SetDir(int32_t iDir)
333 {
334  // Not active
335  C4PropList* pActionDef = GetAction();
336  if (!pActionDef) return;
337  // Invalid direction
338  if (!Inside<int32_t>(iDir,0,pActionDef->GetPropertyInt(P_Directions)-1)) return;
339  // Execute turn action
340  if (iDir != Action.Dir)
341  if (pActionDef->GetPropertyStr(P_TurnAction))
342  { SetActionByName(pActionDef->GetPropertyStr(P_TurnAction)); }
343  // Set dir
344  Action.Dir=iDir;
345  // update by flipdir?
346  if (pActionDef->GetPropertyInt(P_FlipDir))
347  UpdateFlipDir();
348  else
349  Action.DrawDir=iDir;
350 }
351 
352 bool C4Object::SetPhase(int32_t iPhase)
353 {
354  C4PropList* pActionDef = GetAction();
355  if (!pActionDef) return false;
356  const int32_t length = pActionDef->GetPropertyInt(P_Length);
357  Action.Phase=Clamp<int32_t>(iPhase,0,length);
358  Action.PhaseDelay = 0;
359  return true;
360 }
361 
362 int32_t C4Object::GetProcedure() const
363 {
364  C4PropList* pActionDef = GetAction();
365  if (!pActionDef) return -1;
366  return pActionDef->GetPropertyP(P_Procedure);
367 }
368 
370 {
371  // Active objects
372  if (GetAction())
373  {
374  int32_t iProcedure = GetProcedure();
375  C4Object *prev_target = Action.Target;
376  // Scaling upwards: corner scale
377  if (iProcedure == DFA_SCALE && Action.ComDir != COMD_Stop && ComDirLike(Action.ComDir, COMD_Up))
378  if (ObjectActionCornerScale(this)) return;
379  if (iProcedure == DFA_SCALE && Action.ComDir == COMD_Left && Action.Dir == DIR_Left)
380  if (ObjectActionCornerScale(this)) return;
381  if (iProcedure == DFA_SCALE && Action.ComDir == COMD_Right && Action.Dir == DIR_Right)
382  if (ObjectActionCornerScale(this)) return;
383  // Scaling and stopped: fall off to side (avoid zuppel)
384  if ((iProcedure == DFA_SCALE) && (Action.ComDir == COMD_Stop))
385  {
386  if (Action.Dir == DIR_Left)
387  { if (ObjectActionJump(this,itofix(1),Fix0,false)) return; }
388  else
389  { if (ObjectActionJump(this,itofix(-1),Fix0,false)) return; }
390  }
391  // Pushing: grab loss
392  if (iProcedure==DFA_PUSH) GrabLost(this, prev_target);
393  // Else jump
394  ObjectActionJump(this,xdir,ydir,false);
395  }
396  // Inactive objects, simple mobile natural gravity
397  else
398  {
399  DoGravity(this);
400  Mobile=true;
401  }
402 }
403 
405 {
406  // Take certain action on contact. Evaluate t_contact-CNAT and Procedure.
407 
408  // Determine Procedure
409  C4PropList* pActionDef = GetAction();
410  if (!pActionDef) return;
411  int32_t iProcedure=pActionDef->GetPropertyP(P_Procedure);
412  int32_t fDisabled=pActionDef->GetPropertyInt(P_ObjectDisabled);
413 
414  //------------------------------- Hit Bottom ---------------------------------------------
415  if (t_contact & CNAT_Bottom)
416  switch (iProcedure)
417  {
418  case DFA_FLIGHT:
419  if (ydir < 0) return;
420  // Jump: FlatHit / HardHit / Walk
421  if ((OCF & OCF_HitSpeed4) || fDisabled)
422  if (ObjectActionFlat(this,Action.Dir)) return;
423  if (OCF & OCF_HitSpeed3)
424  if (ObjectActionKneel(this)) return;
425  ObjectActionWalk(this);
426  ydir = 0;
427  return;
428  case DFA_SCALE:
429  // Scale down: stand
431  {
432  ObjectActionStand(this);
433  return;
434  }
435  break;
436  case DFA_DIG:
437  // no special action
438  break;
439  case DFA_SWIM:
440  // Try corner scale out
441  if (!GBackSemiSolid(GetX(),GetY()-1+Def->Float*Con/FullCon-1))
442  if (ObjectActionCornerScale(this)) return;
443  break;
444  }
445 
446  //------------------------------- Hit Ceiling -----------------------------------------
447  if (t_contact & CNAT_Top)
448  switch (iProcedure)
449  {
450  case DFA_WALK:
451  // Walk: Stop
452  ObjectActionStand(this); return;
453  case DFA_SCALE:
454  // Scale: Try hangle, else stop if going upward
456  {
457  if (ObjectActionHangle(this))
458  {
460  return;
461  }
463  }
464  break;
465  case DFA_FLIGHT:
466  // Jump: Try hangle, else bounce off
467  // High Speed Flight: Tumble
468  if ((OCF & OCF_HitSpeed3) || fDisabled)
469  { ObjectActionTumble(this, Action.Dir, xdir, ydir); break; }
470  if (ObjectActionHangle(this)) return;
471  break;
472  case DFA_DIG:
473  // No action
474  break;
475  case DFA_HANGLE:
477  break;
478  }
479 
480  //---------------------------- Hit Left Wall ----------------------------------------
481  if (t_contact & CNAT_Left)
482  {
483  switch (iProcedure)
484  {
485  case DFA_FLIGHT:
486  // High Speed Flight: Tumble
487  if ((OCF & OCF_HitSpeed3) || fDisabled)
488  { ObjectActionTumble(this, DIR_Left, xdir, ydir); break; }
489  // Else
490  else if (!ComDirLike(Action.ComDir, COMD_Right) && ObjectActionScale(this,DIR_Left)) return;
491  break;
492  case DFA_WALK:
493  // Walk: Try scale
495  {
496  if (ObjectActionScale(this,DIR_Left))
497  {
498  ydir = C4REAL100(-1);
499  return;
500  }
501  }
502  // Heading away from solid
504  {
505  // Slide off
506  ObjectActionJump(this,xdir/2,ydir,false);
507  }
508  return;
509  case DFA_SWIM:
510  // Only scale if swimming at the surface
511  if (!GBackSemiSolid(GetX(),GetY()-1+Def->Float*Con/FullCon-1))
512  {
513  // Try scale, only if swimming at the surface.
515  if (ObjectActionScale(this,DIR_Left)) return;
516  // Try corner scale out
517  if (ObjectActionCornerScale(this)) return;
518  }
519  return;
520  case DFA_HANGLE:
521  // Hangle: Try scale
522  if (ObjectActionScale(this,DIR_Left))
523  {
524  ydir = C4REAL100(1);
525  return;
526  }
527  return;
528  case DFA_DIG:
529  // Dig: no action
530  break;
531  }
532  }
533 
534  //------------------------------ Hit Right Wall --------------------------------------
535  if (t_contact & CNAT_Right)
536  {
537  switch (iProcedure)
538  {
539  case DFA_FLIGHT:
540  // High Speed Flight: Tumble
541  if ((OCF & OCF_HitSpeed3) || fDisabled)
542  { ObjectActionTumble(this, DIR_Right, xdir, ydir); break; }
543  // Else Scale
544  else if (!ComDirLike(Action.ComDir, COMD_Left) && ObjectActionScale(this,DIR_Right)) return;
545  break;
546  case DFA_WALK:
547  // Walk: Try scale
549  {
550  if (ObjectActionScale(this,DIR_Right))
551  {
552  ydir = C4REAL100(-1);
553  return;
554  }
555  }
556  // Heading away from solid
558  {
559  // Slide off
560  ObjectActionJump(this,xdir/2,ydir,false);
561  }
562  return;
563  case DFA_SWIM:
564  // Only scale if swimming at the surface
565  if (!GBackSemiSolid(GetX(),GetY()-1+Def->Float*Con/FullCon-1))
566  {
567  // Try scale
569  if (ObjectActionScale(this,DIR_Right)) return;
570  // Try corner scale out
571  if (ObjectActionCornerScale(this)) return;
572  }
573  return;
574  case DFA_HANGLE:
575  // Hangle: Try scale
576  if (ObjectActionScale(this,DIR_Right))
577  {
578  ydir = C4REAL100(1);
579  return;
580  }
581  return;
582  case DFA_DIG:
583  // Dig: no action
584  break;
585  }
586  }
587 
588  //---------------------------- Unresolved Cases ---------------------------------------
589 
590  // Flight stuck
591  if (iProcedure==DFA_FLIGHT)
592  {
593  // Enforce slide free (might slide through tiny holes this way)
594  if (!ydir)
595  {
596  int fAllowDown = !(t_contact & CNAT_Bottom);
597  if (t_contact & CNAT_Right)
598  {
599  ForcePosition(fix_x - 1, fix_y + fAllowDown);
600  xdir=ydir=0;
601  }
602  if (t_contact & CNAT_Left)
603  {
604  ForcePosition(fix_x + 1, fix_y + fAllowDown);
605  xdir=ydir=0;
606  }
607  }
608  if (!xdir)
609  {
610  if (t_contact & CNAT_Top)
611  {
612  ForcePosition(fix_x, fix_y + 1);
613  xdir=ydir=0;
614  }
615  }
616  }
617 }
618 
620 {
621  C4Real iTXDir;
622  C4Real lftspeed,tydir;
623  int32_t iTargetX;
624  int32_t iPushRange,iPushDistance;
625 
626  // Standard phase advance
627  int32_t iPhaseAdvance=1;
628 
629  // Upright attachment check
630  if (!Mobile)
631  if (Def->UprightAttach)
632  if (Inside<int32_t>(GetR(),-StableRange,+StableRange))
633  {
635  Mobile=true;
636  }
637 
638  C4PropList* pActionDef = GetAction();
639  // No IncompleteActivity? Reset action if there was one
640  if (!(OCF & OCF_FullCon) && !Def->IncompleteActivity && pActionDef)
641  {
642  SetAction(nullptr);
643  pActionDef = nullptr;
644  }
645 
646  // InLiquidAction check
647  if (InLiquid)
648  if (pActionDef && pActionDef->GetPropertyStr(P_InLiquidAction))
649  {
651  pActionDef = GetAction();
652  }
653 
654  // Idle objects do natural gravity only
655  if (!pActionDef)
656  {
658  if (Mobile) DoGravity(this);
659  return;
660  }
661 
662  C4Real fWalk,fMove;
663 
664  // Action time advance
665  Action.Time++;
666 
667  C4Value Attach;
668  pActionDef->GetProperty(P_Attach, &Attach);
669  if (Attach.GetType() != C4V_Nil)
670  {
671  Action.t_attach = Attach.getInt();
672  }
673  else switch (pActionDef->GetPropertyP(P_Procedure))
674  {
675  case DFA_SCALE:
678  break;
679  case DFA_HANGLE:
681  break;
682  case DFA_WALK:
683  case DFA_KNEEL:
684  case DFA_THROW:
685  case DFA_PUSH:
686  case DFA_PULL:
687  case DFA_DIG:
689  break;
690  default:
692  }
693 
694  // if an object is in controllable state, so it can be assumed that if it dies later because of NO_OWNER's cause,
695  // it has been its own fault and not the fault of the last one who threw a flint on it
696  // do not reset for burning objects to make sure the killer is set correctly if they fall out of the map while burning
697  if (!pActionDef->GetPropertyInt(P_ObjectDisabled) && pActionDef->GetPropertyP(P_Procedure) != DFA_FLIGHT && !OnFire)
699 
700  // Handle Default Action Procedure: evaluates Procedure and Action.ComDir
701  // Update xdir,ydir,Action.Dir,attachment,iPhaseAdvance
702  int32_t dir = Action.Dir;
703  C4Real accel = C4REAL100(pActionDef->GetPropertyInt(P_Accel));
704  C4Real decel = accel;
705  {
706  C4Value decel_val;
707  pActionDef->GetProperty(P_Decel, &decel_val);
708  if (decel_val.GetType() != C4V_Nil)
709  decel = C4REAL100(decel_val.getInt());
710  }
711  C4Real limit = C4REAL100(pActionDef->GetPropertyInt(P_Speed));
712 
713  switch (pActionDef->GetPropertyP(P_Procedure))
714  {
715  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
716  case DFA_WALK:
717  switch (Action.ComDir)
718  {
719  case COMD_Left: case COMD_UpLeft: case COMD_DownLeft:
720  // breaaak!!!
721  if (dir == DIR_Right)
722  xdir-=decel;
723  else
724  xdir-=accel;
725  if (xdir<-limit) xdir=-limit;
726  break;
727  case COMD_Right: case COMD_UpRight: case COMD_DownRight:
728  if (dir == DIR_Left)
729  xdir+=decel;
730  else
731  xdir+=accel;
732  if (xdir>+limit) xdir=+limit;
733  break;
734  case COMD_Stop: case COMD_Up: case COMD_Down:
735  if (xdir<0) xdir+=decel;
736  if (xdir>0) xdir-=decel;
737  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
738  break;
739  }
740  iPhaseAdvance=0;
741  if (xdir<0)
742  {
743  if (dir != DIR_Left) { SetDir(DIR_Left); xdir = -1; }
744  iPhaseAdvance=-fixtoi(xdir*10);
745  }
746  if (xdir>0)
747  {
748  if (dir != DIR_Right) { SetDir(DIR_Right); xdir = 1; }
749  iPhaseAdvance=+fixtoi(xdir*10);
750  }
751 
752  Mobile=true;
753  // object is rotateable? adjust to ground, if in horizontal movement or not attached to the center vertex
755  AdjustWalkRotation(20, 20, 100);
756  else
757  rdir=0;
758  break;
759  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
760  case DFA_KNEEL:
761  ydir=0;
762  Mobile=true;
763  break;
764  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
765  case DFA_SCALE:
766  {
767  int ComDir = Action.ComDir;
768  if (Shape.CheckScaleToWalk(GetX(), GetY()))
769  {
770  ObjectActionWalk(this);
771  return;
772  }
773  if ((Action.Dir == DIR_Left && ComDir == COMD_Left) || (Action.Dir == DIR_Right && ComDir == COMD_Right))
774  {
775  ComDir = COMD_Up;
776  }
777  switch (ComDir)
778  {
779  case COMD_Up: case COMD_UpRight: case COMD_UpLeft:
780  if (ydir > 0) ydir -= decel;
781  else ydir -= accel;
782  if (ydir < -limit) ydir = -limit;
783  break;
784  case COMD_Down: case COMD_DownRight: case COMD_DownLeft:
785  if (ydir < 0) ydir += decel;
786  else ydir += accel;
787  if (ydir > +limit) ydir = +limit;
788  break;
789  case COMD_Left: case COMD_Right: case COMD_Stop:
790  if (ydir < 0) ydir += decel;
791  if (ydir > 0) ydir -= decel;
792  if ((ydir > -decel) && (ydir < +decel)) ydir = 0;
793  break;
794  }
795  iPhaseAdvance=0;
796  if (ydir<0) iPhaseAdvance=-fixtoi(ydir*14);
797  if (ydir>0) iPhaseAdvance=+fixtoi(ydir*14);
798  xdir=0;
799  Mobile=true;
800  break;
801  }
802  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
803  case DFA_HANGLE:
804  switch (Action.ComDir)
805  {
806  case COMD_Left: case COMD_UpLeft: case COMD_DownLeft:
807  if (xdir > 0) xdir -= decel;
808  else xdir -= accel;
809  if (xdir < -limit) xdir = -limit;
810  break;
811  case COMD_Right: case COMD_UpRight: case COMD_DownRight:
812  if (xdir < 0) xdir += decel;
813  else xdir += accel;
814  if (xdir > +limit) xdir = +limit;
815  break;
816  case COMD_Up:
817  if (Action.Dir == DIR_Left)
818  if (xdir > 0) xdir -= decel;
819  else xdir -= accel;
820  else
821  if (xdir < 0) xdir += decel;
822  else xdir += accel;
823  if (xdir < -limit) xdir = -limit;
824  if (xdir > +limit) xdir = +limit;
825  break;
826  case COMD_Stop: case COMD_Down:
827  if (xdir < 0) xdir += decel;
828  if (xdir > 0) xdir -= decel;
829  if ((xdir > -decel) && (xdir < +decel)) xdir = 0;
830  break;
831  }
832  iPhaseAdvance=0;
833  if (xdir<0) { iPhaseAdvance=-fixtoi(xdir*10); SetDir(DIR_Left); }
834  if (xdir>0) { iPhaseAdvance=+fixtoi(xdir*10); SetDir(DIR_Right); }
835  ydir=0;
836  Mobile=true;
837  break;
838  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
839  case DFA_FLIGHT:
840  // Contained: fall out (one try only)
841  if (!::Game.iTick10)
842  if (Contained)
843  {
846  }
847 
848  switch (Action.ComDir)
849  {
850  case COMD_Left: case COMD_UpLeft: case COMD_DownLeft:
851  xdir -= std::max(std::min(limit + xdir, xdir > 0 ? decel : accel), itofix(0));
852  break;
853  case COMD_Right: case COMD_UpRight: case COMD_DownRight:
854  xdir += std::max(std::min(limit - xdir, xdir < 0 ? decel : accel), itofix(0));
855  break;
856  }
857 
858  // Gravity/mobile
859  DoGravity(this);
860  Mobile=true;
861  break;
862  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
863  case DFA_DIG:
864  {
865  int32_t smpx = GetX(), smpy = GetY();
866  bool fAttachOK = false;
867  if (Action.t_attach & CNAT_Bottom && Shape.Attach(smpx,smpy,CNAT_Bottom)) fAttachOK = true;
868  else if (Action.t_attach & CNAT_Left && Shape.Attach(smpx,smpy,CNAT_Left)) { fAttachOK = true; }
869  else if (Action.t_attach & CNAT_Right && Shape.Attach(smpx,smpy,CNAT_Right)) { fAttachOK = true; }
870  else if (Action.t_attach & CNAT_Top && Shape.Attach(smpx,smpy,CNAT_Top)) fAttachOK = true;
871  if (!fAttachOK)
872  { ObjectComStopDig(this); return; }
873  iPhaseAdvance=fixtoi(itofix(40)*limit);
874 
875  if (xdir < 0) SetDir(DIR_Left); else if (xdir > 0) SetDir(DIR_Right);
877  Mobile=true;
878  break;
879  }
880  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
881  case DFA_SWIM:
882  // ComDir changes xdir/ydir
883  switch (Action.ComDir)
884  {
885  case COMD_Up: ydir-=accel; break;
886  case COMD_UpRight: ydir-=accel; xdir+=accel; break;
887  case COMD_Right: xdir+=accel; break;
888  case COMD_DownRight:ydir+=accel; xdir+=accel; break;
889  case COMD_Down: ydir+=accel; break;
890  case COMD_DownLeft: ydir+=accel; xdir-=accel; break;
891  case COMD_Left: xdir-=accel; break;
892  case COMD_UpLeft: ydir-=accel; xdir-=accel; break;
893  case COMD_Stop:
894  if (xdir<0) xdir+=decel;
895  if (xdir>0) xdir-=decel;
896  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
897  if (ydir<0) ydir+=decel;
898  if (ydir>0) ydir-=decel;
899  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
900  break;
901  }
902 
903  // Out of liquid check
904  if (!InLiquid)
905  {
906  // Just above liquid: move down
907  if (GBackLiquid(GetX(),GetY()+1+Def->Float*Con/FullCon-1)) ydir=+accel;
908  // Free fall: walk
909  else { ObjectActionWalk(this); return; }
910  }
911 
912  // xdir/ydir bounds, don't apply if COMD_None
913  if (Action.ComDir != COMD_None)
914  {
915  ydir = Clamp(ydir, -limit, limit);
916  xdir = Clamp(xdir, -limit, limit);
917  }
918  // Surface dir bound
919  if (!GBackLiquid(GetX(),GetY()-1+Def->Float*Con/FullCon-1)) if (ydir<0) ydir=0;
920  // Dir, Phase, Attach
921  if (xdir<0) SetDir(DIR_Left);
922  if (xdir>0) SetDir(DIR_Right);
923  iPhaseAdvance=fixtoi(limit*10);
924  Mobile=true;
925 
926  break;
927  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
928  case DFA_THROW:
929  Mobile=true;
930  break;
931  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
932  case DFA_PUSH:
933  // No target
934  if (!Action.Target) { StopActionDelayCommand(this); return; }
935  // Inside target
936  if (Contained==Action.Target) { StopActionDelayCommand(this); return; }
937  // Target pushing force
938  bool fStraighten;
939  iTXDir=0; fStraighten=false;
940  switch (Action.ComDir)
941  {
942  case COMD_Left: case COMD_DownLeft: iTXDir=-limit; break;
943  case COMD_UpLeft: fStraighten=true; iTXDir=-limit; break;
944  case COMD_Right: case COMD_DownRight: iTXDir=+limit; break;
945  case COMD_UpRight: fStraighten=true; iTXDir=+limit; break;
946  case COMD_Up: fStraighten=true; break;
947  case COMD_Stop: case COMD_Down: iTXDir=0; break;
948  }
949  // Push object
950  if (!Action.Target->Push(iTXDir,accel,fStraighten))
951  { StopActionDelayCommand(this); return; }
952  // Set target controller
954  // ObjectAction got hold check
955  iPushDistance = std::max(Shape.Wdt/2-8,0);
956  iPushRange = iPushDistance + 10;
957  int32_t sax,say,sawdt,sahgt;
958  Action.Target->GetArea(sax,say,sawdt,sahgt);
959  // Object lost
960  if (!Inside(GetX()-sax,-iPushRange,sawdt-1+iPushRange)
961  || !Inside(GetY()-say,-iPushRange,sahgt-1+iPushRange))
962  {
963  C4Object *prev_target = Action.Target;
964  // Wait command (why, anyway?)
966  // Grab lost action
967  GrabLost(this, prev_target);
968  // Done
969  return;
970  }
971  // Follow object (full xdir reset)
972  // Vertical follow: If object moves out at top, assume it's being pushed upwards and the Clonk must run after it
973  if (GetY()-iPushDistance > say+sahgt && iTXDir) { if (iTXDir>0) sax+=sawdt/2; sawdt/=2; }
974  // Horizontal follow
975  iTargetX=Clamp(GetX(),sax-iPushDistance,sax+sawdt-1+iPushDistance);
976  if (GetX()==iTargetX)
977  {
978  xdir=0;
979  }
980  else
981  {
982  if (GetX()<iTargetX)
983  xdir=+limit;
984  else if (GetX()>iTargetX)
985  xdir=-limit;
986  }
987  // Phase by XDir
988  iPhaseAdvance=0;
989  if (xdir<0) { iPhaseAdvance=-fixtoi(xdir*10); SetDir(DIR_Left); }
990  if (xdir>0) { iPhaseAdvance=+fixtoi(xdir*10); SetDir(DIR_Right); }
991  // No YDir
992  ydir=0;
993  Mobile=true;
994  break;
995  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
996  case DFA_PULL:
997  // No target
998  if (!Action.Target) { StopActionDelayCommand(this); return; }
999  // Inside target
1000  if (Contained==Action.Target) { StopActionDelayCommand(this); return; }
1001  // Target contained
1002  if (Action.Target->Contained) { StopActionDelayCommand(this); return; }
1003 
1004  int32_t iPullDistance;
1005  int32_t iPullX;
1006 
1007  iPullDistance = Action.Target->Shape.Wdt/2 + Shape.Wdt/2;
1008 
1009  iTargetX=GetX();
1010  if (Action.ComDir==COMD_Right) iTargetX = Action.Target->GetX()+iPullDistance;
1011  if (Action.ComDir==COMD_Left) iTargetX = Action.Target->GetX()-iPullDistance;
1012 
1013  iPullX=Action.Target->GetX();
1014  if (Action.ComDir==COMD_Right) iPullX = GetX()-iPullDistance;
1015  if (Action.ComDir==COMD_Left) iPullX = GetX()+iPullDistance;
1016 
1017  fWalk = limit;
1018 
1019  fMove = 0;
1020  if (Action.ComDir==COMD_Right) fMove = +fWalk;
1021  if (Action.ComDir==COMD_Left) fMove = -fWalk;
1022 
1023  iTXDir = fMove + fWalk * Clamp<int32_t>(iPullX-Action.Target->GetX(),-10,+10) / 10;
1024 
1025  // Push object
1026  if (!Action.Target->Push(iTXDir,accel,false))
1027  { StopActionDelayCommand(this); return; }
1028  // Set target controller
1030 
1031  // Train pulling: com dir transfer
1032  if ( (Action.Target->GetProcedure()==DFA_WALK)
1033  || (Action.Target->GetProcedure()==DFA_PULL) )
1034  {
1036  if (iTXDir<0) Action.Target->Action.ComDir=COMD_Left;
1037  if (iTXDir>0) Action.Target->Action.ComDir=COMD_Right;
1038  }
1039 
1040  // Pulling range
1041  iPushDistance = std::max(Shape.Wdt/2-8,0);
1042  iPushRange = iPushDistance + 20;
1043  Action.Target->GetArea(sax,say,sawdt,sahgt);
1044  // Object lost
1045  if (!Inside(GetX()-sax,-iPushRange,sawdt-1+iPushRange)
1046  || !Inside(GetY()-say,-iPushRange,sahgt-1+iPushRange))
1047  {
1048  // Remember target. Will be lost on changing action.
1049  C4Object *prev_target = Action.Target;
1050  // Wait command (why, anyway?)
1051  StopActionDelayCommand(this);
1052  // Grab lost action
1053  GrabLost(this, prev_target);
1054  // Lose target
1055  Action.Target=nullptr;
1056  // Done
1057  return;
1058  }
1059 
1060  // Move to pulling position
1061  xdir = fMove + fWalk * Clamp<int32_t>(iTargetX-GetX(),-10,+10) / 10;
1062 
1063  // Phase by XDir
1064  iPhaseAdvance=0;
1065  if (xdir<0) { iPhaseAdvance=-fixtoi(xdir*10); SetDir(DIR_Left); }
1066  if (xdir>0) { iPhaseAdvance=+fixtoi(xdir*10); SetDir(DIR_Right); }
1067  // No YDir
1068  ydir=0;
1069  Mobile=true;
1070 
1071  break;
1072  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1073  case DFA_LIFT:
1074  // Valid check
1075  if (!Action.Target) { SetAction(nullptr); return; }
1076  // Target lifting force
1077  lftspeed=itofix(2); tydir=0;
1078  switch (Action.ComDir)
1079  {
1080  case COMD_Up: tydir=-lftspeed; break;
1081  case COMD_Stop: tydir=-GravAccel; break;
1082  case COMD_Down: tydir=+lftspeed; break;
1083  }
1084  // Lift object
1085  if (!Action.Target->Lift(tydir,C4REAL100(50)))
1086  { SetAction(nullptr); return; }
1087  // Check LiftTop
1088  if (Def->LiftTop)
1089  if (Action.Target->GetY()<=(GetY()+Def->LiftTop))
1090  if (Action.ComDir==COMD_Up)
1091  Call(PSF_LiftTop);
1092  // General
1093  DoGravity(this);
1094  break;
1095  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1096  case DFA_FLOAT:
1097  // ComDir changes xdir/ydir
1098  switch (Action.ComDir)
1099  {
1100  case COMD_Up:
1101  ydir-=accel;
1102  if (xdir<0) xdir+=decel;
1103  if (xdir>0) xdir-=decel;
1104  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
1105  break;
1106  case COMD_UpRight:
1107  ydir-=accel; xdir+=accel; break;
1108  case COMD_Right:
1109  xdir+=accel;
1110  if (ydir<0) ydir+=decel;
1111  if (ydir>0) ydir-=decel;
1112  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
1113  break;
1114  case COMD_DownRight:
1115  ydir+=accel; xdir+=accel; break;
1116  case COMD_Down:
1117  ydir+=accel;
1118  if (xdir<0) xdir+=decel;
1119  if (xdir>0) xdir-=decel;
1120  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
1121  break;
1122  case COMD_DownLeft:
1123  ydir+=accel; xdir-=accel; break;
1124  case COMD_Left:
1125  xdir-=accel;
1126  if (ydir<0) ydir+=decel;
1127  if (ydir>0) ydir-=decel;
1128  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
1129  break;
1130  case COMD_UpLeft:
1131  ydir-=accel; xdir-=accel; break;
1132  case COMD_Stop:
1133  if (xdir<0) xdir+=decel;
1134  if (xdir>0) xdir-=decel;
1135  if ((xdir>-decel) && (xdir<+decel)) xdir=0;
1136  if (ydir<0) ydir+=decel;
1137  if (ydir>0) ydir-=decel;
1138  if ((ydir>-decel) && (ydir<+decel)) ydir=0;
1139  break;
1140  }
1141  // xdir/ydir bounds, don't apply if COMD_None
1142  if (Action.ComDir != COMD_None)
1143  {
1144  ydir = Clamp(ydir, -limit, limit);
1145  xdir = Clamp(xdir, -limit, limit);
1146  }
1147 
1148  Mobile=true;
1149  break;
1150  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1151  // ATTACH: Force position to target object
1152  // own vertex index is determined by high-order byte of action data
1153  // target vertex index is determined by low-order byte of action data
1154  case DFA_ATTACH:
1155 
1156  // No target
1157  if (!Action.Target)
1158  {
1159  if (Status)
1160  {
1161  SetAction(nullptr);
1163  }
1164  return;
1165  }
1166 
1167  // Target incomplete and no incomplete activity
1168  if (!(Action.Target->OCF & OCF_FullCon))
1170  { SetAction(nullptr); return; }
1171 
1172  // Force containment
1174  {
1175  if (Action.Target->Contained)
1177  else
1178  Exit(GetX(),GetY(),GetR());
1179  }
1180 
1181  // Object might have detached in Enter/Exit call
1182  if (!Action.Target) break;
1183 
1184  // Move position (so objects on solidmask move)
1186  -Shape.VtxX[Action.Data>>8] - fix_x,
1188  -Shape.VtxY[Action.Data>>8] - fix_y);
1189  // must zero motion...
1190  xdir=ydir=0;
1191 
1192  break;
1193  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1194  case DFA_CONNECT:
1195  {
1196  bool fBroke=false;
1197  bool fLineChange = false;
1198 
1199  // Line destruction check: Target missing or incomplete
1200  if (!Action.Target || (Action.Target->Con<FullCon)) fBroke=true;
1201  if (!Action.Target2 || (Action.Target2->Con<FullCon)) fBroke=true;
1202  if (fBroke)
1203  {
1205  AssignRemoval();
1206  return;
1207  }
1208 
1209  // Movement by Target
1210  // Connect to attach vertex
1211  C4Value lineAttachV; C4ValueArray *lineAttach;
1212  Action.Target->GetProperty(P_LineAttach, &lineAttachV);
1213  lineAttach = lineAttachV.getArray();
1214  int32_t iConnectX1, iConnectY1;
1215  iConnectX1 = Action.Target->GetX();
1216  iConnectY1 = Action.Target->GetY();
1217  if (lineAttach)
1218  {
1219  iConnectX1 += lineAttach->GetItem(0).getInt();
1220  iConnectY1 += lineAttach->GetItem(1).getInt();
1221  }
1222  if ((iConnectX1!=Shape.VtxX[0]) || (iConnectY1!=Shape.VtxY[0]))
1223  {
1224  // Regular wrapping line
1225  if (Def->LineIntersect == 0)
1226  if (!Shape.LineConnect(iConnectX1,iConnectY1,0,+1,
1227  Shape.VtxX[0],Shape.VtxY[0])) fBroke=true;
1228  // No-intersection line
1229  if (Def->LineIntersect == 1)
1230  { Shape.VtxX[0]=iConnectX1; Shape.VtxY[0]=iConnectY1; }
1231 
1232  fLineChange = true;
1233  }
1234 
1235  // Movement by Target2
1236  // Connect to attach vertex
1237  Action.Target2->GetProperty(P_LineAttach, &lineAttachV);
1238  lineAttach = lineAttachV.getArray();
1239  int32_t iConnectX2, iConnectY2;
1240  iConnectX2 = Action.Target2->GetX();
1241  iConnectY2 = Action.Target2->GetY();
1242  if (lineAttach)
1243  {
1244  iConnectX2 += lineAttach->GetItem(0).getInt();
1245  iConnectY2 += lineAttach->GetItem(1).getInt();
1246  }
1247  if ((iConnectX2!=Shape.VtxX[Shape.VtxNum-1]) || (iConnectY2!=Shape.VtxY[Shape.VtxNum-1]))
1248  {
1249  // Regular wrapping line
1250  if (Def->LineIntersect == 0)
1251  if (!Shape.LineConnect(iConnectX2,iConnectY2,Shape.VtxNum-1,-1,
1252  Shape.VtxX[Shape.VtxNum-1],Shape.VtxY[Shape.VtxNum-1])) fBroke=true;
1253  // No-intersection line
1254  if (Def->LineIntersect == 1)
1255  { Shape.VtxX[Shape.VtxNum-1]=iConnectX2; Shape.VtxY[Shape.VtxNum-1]=iConnectY2; }
1256 
1257  fLineChange = true;
1258  }
1259 
1260  // Line fBroke
1261  if (fBroke)
1262  {
1263  Call(PSF_OnLineBreak,nullptr);
1264  AssignRemoval();
1265  return;
1266  }
1267 
1268  // Reduce line segments
1269  if (!::Game.iTick35)
1271  fLineChange = true;
1272 
1273  // Line change callback
1274  if (fLineChange)
1276  }
1277  break;
1278  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1279  default:
1280  // Attach
1281  if (Action.t_attach)
1282  {
1283  xdir = ydir = 0;
1284  Mobile = true;
1285  }
1286  // Free gravity
1287  else
1288  DoGravity(this);
1289  break;
1290  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1291  }
1292 
1293  // Phase Advance (zero delay means no phase advance)
1294  if (pActionDef->GetPropertyInt(P_Delay))
1295  {
1296  Action.PhaseDelay+=iPhaseAdvance;
1297  if (Action.PhaseDelay >= pActionDef->GetPropertyInt(P_Delay))
1298  {
1299  // Advance Phase
1300  Action.PhaseDelay=0;
1301  Action.Phase += pActionDef->GetPropertyInt(P_Step);
1302  // Phase call
1303  if (pActionDef->GetPropertyStr(P_PhaseCall))
1304  {
1305  Call(pActionDef->GetPropertyStr(P_PhaseCall)->GetCStr());
1306  }
1307  // Phase end
1308  if (Action.Phase>=pActionDef->GetPropertyInt(P_Length))
1309  {
1310  C4String *next_action = pActionDef->GetPropertyStr(P_NextAction);
1311  // Keep current action if there is no NextAction
1312  if (!next_action)
1313  Action.Phase = 0;
1314  // set new action if it's not Hold
1315  else if (next_action == &Strings.P[P_Hold])
1316  {
1317  Action.Phase = pActionDef->GetPropertyInt(P_Length)-1;
1318  Action.PhaseDelay = pActionDef->GetPropertyInt(P_Delay)-1;
1319  }
1320  else
1321  {
1322  // Set new action
1323  SetActionByName(next_action, nullptr, nullptr, SAC_StartCall | SAC_EndCall);
1324  }
1325  }
1326  }
1327  }
1328 
1329  return;
1330 }
@ C4CMD_Wait
Definition: C4Command.h:37
@ C4CMD_PushTo
Definition: C4Command.h:43
@ C4CMD_Exit
Definition: C4Command.h:32
const int32_t FullCon
Definition: C4Constants.h:181
const BYTE CNAT_Bottom
Definition: C4Constants.h:112
const int32_t MNone
Definition: C4Constants.h:177
const uint32_t OCF_HitSpeed4
Definition: C4Constants.h:95
const uint32_t OCF_HitSpeed3
Definition: C4Constants.h:92
const BYTE CNAT_Right
Definition: C4Constants.h:110
const BYTE CNAT_Top
Definition: C4Constants.h:111
const uint32_t OCF_FullCon
Definition: C4Constants.h:85
const BYTE CNAT_None
Definition: C4Constants.h:108
const int NO_OWNER
Definition: C4Constants.h:137
const BYTE CNAT_Left
Definition: C4Constants.h:109
const int32_t C4D_StaticBack
Definition: C4Def.h:40
#define PSF_OnActionChanged
Definition: C4GameScript.h:152
#define PSF_OnLineBreak
Definition: C4GameScript.h:76
#define PSF_OnLineChange
Definition: C4GameScript.h:77
#define PSF_LiftTop
Definition: C4GameScript.h:68
#define PSF_AttachTargetLost
Definition: C4GameScript.h:85
#define PSF_GrabLost
Definition: C4GameScript.h:75
C4Game Game
Definition: C4Globals.cpp:52
C4StringTable Strings
Definition: C4Globals.cpp:42
bool PathFree(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
bool GBackLiquid(int32_t x, int32_t y)
Definition: C4Landscape.h:239
bool GBackSemiSolid(int32_t x, int32_t y)
Definition: C4Landscape.h:234
#define COMD_UpLeft
Definition: C4Object.h:58
#define COMD_DownRight
Definition: C4Object.h:54
#define COMD_Down
Definition: C4Object.h:55
#define DIR_Right
Definition: C4Object.h:42
#define COMD_DownLeft
Definition: C4Object.h:56
#define DIR_Left
Definition: C4Object.h:41
#define COMD_UpRight
Definition: C4Object.h:52
#define COMD_Right
Definition: C4Object.h:53
#define COMD_Left
Definition: C4Object.h:57
#define COMD_Stop
Definition: C4Object.h:50
#define COMD_Up
Definition: C4Object.h:51
#define COMD_None
Definition: C4Object.h:49
void GrabLost(C4Object *cObj, C4Object *prev_target)
void Towards(C4Real &val, C4Real target, C4Real step)
bool ReduceLineSegments(C4Shape &rShape, bool fAlternate)
void StopActionDelayCommand(C4Object *cobj)
void ObjectComStopDig(C4Object *cObj)
bool ObjectActionScale(C4Object *cObj, int32_t dir)
bool ObjectActionWalk(C4Object *cObj)
Definition: C4ObjectCom.cpp:39
bool ObjectActionFlat(C4Object *cObj, int32_t dir)
bool ObjectActionJump(C4Object *cObj, C4Real xdir, C4Real ydir, bool fByCom)
Definition: C4ObjectCom.cpp:50
bool ObjectActionHangle(C4Object *cObj)
bool ObjectActionKneel(C4Object *cObj)
bool ObjectActionTumble(C4Object *cObj, int32_t dir, C4Real xdir, C4Real ydir)
Definition: C4ObjectCom.cpp:88
bool ComDirLike(int32_t iComDir, int32_t iSample)
bool ObjectComStop(C4Object *cObj)
bool ObjectActionStand(C4Object *cObj)
Definition: C4ObjectCom.cpp:44
bool ObjectActionCornerScale(C4Object *cObj)
const C4Real FloatFriction
Definition: C4Movement.cpp:35
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
void StopSoundEffect(const char *szSndName, C4Object *pObj)
C4SoundInstance * StartSoundEffect(const char *szSndName, bool fLoop, int32_t iVolume, C4Object *pObj, int32_t iCustomFalloffDistance, int32_t iPitch, C4SoundModifier *modifier)
@ DFA_SWIM
@ P_Directions
@ DFA_FLOAT
@ P_Accel
@ P_Delay
@ P_Step
@ P_Wdt
@ P_Attach
@ DFA_HANGLE
@ DFA_WALK
@ P_NextAction
@ DFA_DIG
@ P_Speed
@ DFA_PULL
@ P_Idle
@ P_TurnAction
@ P_EndCall
@ P_NoOtherAction
@ P_Y
@ P_Hold
@ P_Action
@ P_FlipDir
@ P_Length
@ P_AbortCall
@ DFA_KNEEL
@ P_StartCall
@ P_Hgt
@ P_OffX
@ P_PhaseCall
@ DFA_FLIGHT
@ DFA_LIFT
@ P_OffY
@ DFA_THROW
@ DFA_ATTACH
@ P_LineAttach
@ P_X
@ DFA_PUSH
@ P_Animation
@ P_Procedure
@ DFA_SCALE
@ P_ObjectDisabled
@ P_Decel
@ DFA_CONNECT
@ P_InLiquidAction
@ P_Sound
@ P_ActMap
@ C4V_Nil
Definition: C4Value.h:25
C4Value C4VPropList(C4PropList *p)
Definition: C4Value.h:242
T Abs(T val)
Definition: Standard.h:42
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
C4Facet Facet
Definition: C4Object.h:88
int32_t Data
Definition: C4Object.h:84
int32_t t_attach
Definition: C4Object.h:86
int32_t Time
Definition: C4Object.h:83
int32_t PhaseDelay
Definition: C4Object.h:85
int32_t Phase
Definition: C4Object.h:85
C4ObjectPtr Target2
Definition: C4Object.h:87
int32_t ComDir
Definition: C4Object.h:82
int32_t Dir
Definition: C4Object.h:80
int32_t FacetY
Definition: C4Object.h:89
int32_t DrawDir
Definition: C4Object.h:81
int32_t FacetX
Definition: C4Object.h:89
StdMeshInstanceAnimationNode * Animation
Definition: C4Object.h:90
C4ObjectPtr Target
Definition: C4Object.h:87
C4Command * Next
Definition: C4Command.h:90
Definition: C4Def.h:99
int32_t IncompleteActivity
Definition: C4Def.h:128
int32_t Float
Definition: C4Def.h:120
int32_t UprightAttach
Definition: C4Def.h:125
int32_t Rotateable
Definition: C4Def.h:119
int32_t GrowthType
Definition: C4Def.h:111
C4Shape Shape
Definition: C4Def.h:104
int32_t LineIntersect
Definition: C4Def.h:127
int32_t LiftTop
Definition: C4Def.h:123
C4TargetRect TopFace
Definition: C4Def.h:109
bool IsIdentity() const
Definition: C4Facet.h:90
void SetFlipDir(int32_t iNewFlipDir)
Definition: C4Facet.h:82
void Set(C4Surface &rSfc)
Definition: C4Facet.cpp:459
void Default()
Definition: C4Facet.cpp:31
Definition: C4Real.h:59
int32_t iTick2
Definition: C4Game.h:130
int32_t iTick35
Definition: C4Game.h:130
int32_t iTick10
Definition: C4Game.h:130
void UpdateSolidMask(bool fRestoreAttachedObjects)
bool Enter(C4Object *pTarget, bool fCalls=true, bool fCopyMotion=true, bool *pfRejectCollect=nullptr)
bool OnFire
Definition: C4Object.h:171
void ClearCommand(C4Command *pUntil)
BYTE GetArea(int32_t &aX, int32_t &aY, int32_t &aWdt, int32_t &aHgt) const
Definition: C4Object.cpp:700
C4Real ydir
Definition: C4Object.h:124
C4Real fix_y
Definition: C4Object.h:123
void ContactAction()
C4PropList * GetAction() const
bool AddCommand(int32_t iCommand, C4Object *pTarget, C4Value iTx, int32_t iTy=0, int32_t iUpdateInterval=0, C4Object *pTarget2=nullptr, bool fInitEvaluation=true, C4Value iData=C4VNull, bool fAppend=false, int32_t iRetries=0, C4String *szText=nullptr, int32_t iBaseMode=0)
C4Real xdir
Definition: C4Object.h:124
void NoAttachAction()
void UpdateFace(bool bUpdateShape, bool fTemp=false)
void SetCommand(int32_t iCommand, C4Object *pTarget, C4Value iTx, int32_t iTy=0, C4Object *pTarget2=nullptr, bool fControl=false, C4Value iData=C4VNull, int32_t iRetries=0, C4String *szText=nullptr)
bool AdjustWalkRotation(int32_t iRangeX, int32_t iRangeY, int32_t iSpeed)
C4Real fix_x
Definition: C4Object.h:123
uint32_t t_contact
Definition: C4Object.h:131
bool SetActionByName(C4String *ActName, C4Object *pTarget=nullptr, C4Object *pTarget2=nullptr, int32_t iCalls=SAC_StartCall|SAC_AbortCall, bool fForce=false)
int32_t GetProcedure() const
C4DrawTransform * pDrawTransform
Definition: C4Object.h:135
C4Command * Command
Definition: C4Object.h:165
int32_t GetX() const
Definition: C4Object.h:285
int32_t Category
Definition: C4Object.h:111
int32_t Controller
Definition: C4Object.h:109
void SetDir(int32_t tdir)
uint32_t OCF
Definition: C4Object.h:132
bool Push(C4Real txdir, C4Real dforce, bool fStraighten)
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 ExecAction()
int32_t GetY() const
Definition: C4Object.h:286
StdMeshInstance * pMeshInstance
Definition: C4Object.h:154
void UpdateFlipDir()
bool Lift(C4Real tydir, C4Real dforce)
int32_t LastEnergyLossCausePlayer
Definition: C4Object.h:110
void AssignRemoval(bool exit_contents=false)
Definition: C4Object.cpp:215
void MovePosition(int32_t dx, int32_t dy)
Definition: C4Movement.cpp:675
int32_t GetR() const
Definition: C4Object.h:287
int32_t GetCon() const
Definition: C4Object.h:271
C4ObjectPtr Contained
Definition: C4Object.h:142
void UpdateActionFace()
C4Real rdir
Definition: C4Object.h:124
bool Exit(int32_t iX=0, int32_t iY=0, int32_t iR=0, C4Real iXDir=Fix0, C4Real iYDir=Fix0, C4Real iRDir=Fix0, bool fCalls=true)
C4Action Action
Definition: C4Object.h:145
void SetOCF()
Definition: C4ObjectOCF.cpp:30
bool InLiquid
Definition: C4Object.h:129
@ SAC_StartCall
Definition: C4Object.h:250
@ SAC_EndCall
Definition: C4Object.h:250
@ SAC_AbortCall
Definition: C4Object.h:250
C4Def * Def
Definition: C4Object.h:141
void UpdatePos()
uint32_t Color
Definition: C4Object.h:118
void UpdateShape(bool bUpdateVertices=true)
Definition: C4Object.cpp:327
bool SetPhase(int32_t iPhase)
C4Facet TopFace
Definition: C4Object.h:140
bool SetAction(C4PropList *Act, C4Object *pTarget=nullptr, C4Object *pTarget2=nullptr, int32_t iCalls=SAC_StartCall|SAC_AbortCall, bool fForce=false)
C4Shape Shape
Definition: C4Object.h:146
C4DefGraphics * GetGraphics() const
Definition: C4Object.h:339
int32_t GetPropertyInt(C4PropertyName k, int32_t default_val=0) const
Definition: C4PropList.cpp:855
virtual const char * GetName() const
Definition: C4PropList.cpp:618
int32_t Status
Definition: C4PropList.h:173
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const
Definition: C4PropList.cpp:726
C4PropertyName GetPropertyP(C4PropertyName k) const
Definition: C4PropList.cpp:824
C4String * GetPropertyStr(C4PropertyName k) const
Definition: C4PropList.cpp:744
C4Value Call(C4PropertyName k, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4PropList.h:114
bool GetProperty(C4PropertyName k, C4Value *pResult) const
Definition: C4PropList.h:105
void SetProperty(C4PropertyName k, const C4Value &to)
Definition: C4PropList.h:124
int32_t y
Definition: C4Rect.h:30
int32_t Hgt
Definition: C4Rect.h:30
int32_t Wdt
Definition: C4Rect.h:30
int32_t x
Definition: C4Rect.h:30
void DecRef()
Definition: C4StringTable.h:28
void IncRef()
Definition: C4StringTable.h:27
int32_t AttachMat
Definition: C4Shape.h:50
int32_t VtxNum
Definition: C4Shape.h:42
int32_t iAttachVtx
Definition: C4Shape.h:53
bool RemoveVertex(int32_t iPos)
Definition: C4Shape.cpp:398
bool CheckScaleToWalk(int x, int y)
Definition: C4Shape.cpp:482
int32_t VtxY[C4D_MaxVertex]
Definition: C4Shape.h:44
bool Attach(int32_t &cx, int32_t &cy, BYTE cnat_pos)
Definition: C4Shape.cpp:215
int32_t VtxX[C4D_MaxVertex]
Definition: C4Shape.h:43
bool LineConnect(int32_t tx, int32_t ty, int32_t cvtx, int32_t ld, int32_t oldx, int32_t oldy)
Definition: C4Shape.cpp:295
StdStrBuf GetData() const
Definition: C4StringTable.h:50
const char * GetCStr() const
Definition: C4StringTable.h:49
C4String P[P_LAST]
C4String * RegString(StdStrBuf String)
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
C4ValueArray * getArray() const
Definition: C4Value.h:118
int32_t getInt() const
Definition: C4Value.h:112
C4V_Type GetType() const
Definition: C4Value.h:161
C4PropList * getPropList() const
Definition: C4Value.h:116
AnimationNode * PlayAnimation(const StdStrBuf &animation_name, int slot, AnimationNode *sibling, ValueProvider *position, ValueProvider *weight, bool stop_previous_animation)
Definition: StdMesh.cpp:1167
void StopAnimation(AnimationNode *node)
Definition: StdMesh.cpp:1190