home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / SAT 2.4.0 / SAT / Add-ons / Sprite behavior / SATToolbox.p < prev    next >
Encoding:
Text File  |  1997-03-12  |  23.3 KB  |  749 lines  |  [TEXT/PJMM]

  1. { SAT Toolbox}
  2. {This unit holds a set of utility routines that may simplify your sprite programming,}
  3. {but that are in no way mandatory.}
  4.  
  5.  
  6. unit SATToolbox;
  7.  
  8. interface
  9.  
  10.     uses
  11. {$ifc UNDEFINED THINK_PASCAL}
  12.         Types, QuickDraw, Events, Windows, Dialogs, Fonts, DiskInit, TextEdit, Traps,{}
  13.         Memory, SegLoad, Scrap, ToolUtils, OSUtils, Menus, Resources, StandardFile,{}
  14.         GestaltEqu, Files, Errors, Devices, QuickDrawText, 
  15. {$endc}
  16.         SAT;
  17.  
  18. {The following switch should be true if your SAT.p has a fixedPointPosition field.}
  19. {$setc _hasfixedpoint := TRUE}
  20.  
  21. {MoveSprite: Moves a sprite according to speed}
  22. {KeepOnScreen: Performs border checks for a sprite}
  23. {RectSeparate: Moves two sprites apart}
  24. {RegionHit: Checks if the regions of two sprites overlap}
  25. {SplitVector: A utility for splitting a vector in components parallel and perpendicular to another vector}
  26. {}
  27. {Sinus, Cosinus, SquareRoot: Useful look-up table based operations.}
  28.  
  29.  
  30.     type
  31.         FixSpritePtr = ^FixSprite;
  32.         FixSprite = record
  33. { Variables that you should change as appropriate }
  34.                 kind: Integer; { Used for identification. >0: friend. <0 foe }
  35.                 position: Point;
  36.                 hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
  37.                                     {hotRect is set by you. hotRect2 is offset to the current position.}
  38.                 face: FacePtr;            { Pointer to the Face (appearance) to be used. }
  39.                 task: ProcPtr;            { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
  40.                 hitTask: ProcPtr;        { Callback in collisions. }
  41.                 destructTask: ProcPtr;    { Called when a sprite is disposed. (Usually nil.) }
  42.                 clip: RgnHandle;            {Clip region to be used when this sprite is drawn.}
  43. { SAT variables that you shouldn't change: }
  44.                 oldpos: Point;                {Used by RunSAT2}
  45.                 next, prev: SpritePtr;    {You may change them in your own sorting routine, but be careful if you do.}
  46.                 r, oldr: Rect;                {Rectangle telling where to draw. Avoid messing with it.}
  47.                 oldFace: FacePtr;            {Used by RunSAT2}
  48.                 dirty: Boolean;            {Used by RunSAT2}
  49. {Variables for internal use by the sprites. Use as you please. Edit as necessary - this is merely a default}
  50. {set, enough space for most cases - but if you change the size of the record, call SetSpriteSize immediately}
  51. {after initializing (before any sprites are created)!}
  52.                 layer: integer; {For layer-sorting. When not used for that, use freely.}
  53.                 speed: Point; { Can be used for speed, but not necessarily. }
  54.                 mode: integer; { Usually used for different modes and/or to determine what image to show next. }
  55.                 fixedPointPosition: Point; {fixed point position}
  56.                 appLong: Longint; {Longint for free use by the application.}
  57.             end;
  58.  
  59. {Constants for separation/bounceoff between sprites}
  60.     const
  61.         kPushBoth = 0;
  62.         kPushMe = 1;
  63.         kPushHim = 2;
  64.  
  65. (*Sprite utilities*)
  66.     procedure MoveSprite (theSprite: SpritePtr);
  67.     function KeepOnScreen (theSprite: SpritePtr): Boolean;
  68. {$ifc _hasfixedpoint}
  69.     procedure MoveSpriteFixedPoint (theSprite: FixSpritePtr);
  70.     function KeepOnScreenFixed (theSprite: FixSpritePtr): Boolean;
  71. {$endc}
  72.     function KeepSpriteInRect (theSprite: SpritePtr; r: Rect): Boolean;
  73.     function RectSeparate (theSprite: SpritePtr; anotherSprite: SpritePtr; push: Integer): Integer;
  74.     procedure RectBounce (me, him: SpritePtr; push: Integer);
  75.     procedure RectBounceFixed (me, him: FixSpritePtr; push: Integer);
  76.     function PtInSpriteRgn (p: Point; theSprite: SpritePtr): Boolean;
  77.     function RegionHit (theSprite: SpritePtr; anotherSprite: SpritePtr): Boolean;
  78.     procedure SplitVector (v: Point; d: Point; var p: Point; var n: Point);
  79.  
  80.     function RegionBounce (s1, s2: FixSpritePtr; elasticity: Integer): Boolean;
  81.     function RegionBounce2 (s1, s2: FixSpritePtr; elasticity: Integer): Boolean;
  82.  
  83. {Look-up table based fixed-point math operations.}
  84.  
  85.     const
  86.         kFixedPointShift = 4; {Number of fixed-point positions}
  87.         kFixedOne = 16; {The "one" in the fixed-point system being used!}
  88.  
  89.     procedure InitTables; {Init not required}
  90.     function SquareRoot (arg: Longint): Longint;
  91.     function Sinus (arg: Longint): Longint;
  92.     function Cosinus (arg: Longint): Longint;
  93.     function VectorLength (vector: Point): Longint;
  94.     function FPVectorLength (vector: Point): Longint;
  95.     function ApproxVectorLength (vector: Point): Longint;
  96.  
  97. implementation
  98.  
  99. {Call MoveSprite or MoveSpriteFixedPoint from the sprite handling routine, to move it according to the speed.}
  100.  
  101.     procedure MoveSprite (theSprite: SpritePtr);
  102.     begin
  103.         theSprite^.position.h := theSprite^.position.h + theSprite^.speed.h;
  104.         theSprite^.position.v := theSprite^.position.v + theSprite^.speed.v;
  105.         if KeepOnScreen(theSprite) then
  106.             ;
  107.     end; (*MoveSprite*)
  108.  
  109. {$ifc _hasfixedpoint}
  110.     procedure MoveSpriteFixedPoint (theSprite: FixSpritePtr);
  111.     begin
  112.         theSprite^.fixedPointPosition.h := theSprite^.fixedPointPosition.h + theSprite^.speed.h;
  113.         theSprite^.fixedPointPosition.v := theSprite^.fixedPointPosition.v + theSprite^.speed.v;
  114.         theSprite^.position.h := BSR(theSprite^.fixedPointPosition.h, kFixedPointShift);
  115.         theSprite^.position.v := BSR(theSprite^.fixedPointPosition.v, kFixedPointShift);
  116.         if KeepOnScreenFixed(theSprite) then
  117.             ;
  118.     end; (*MoveSpriteFixedPoint*)
  119. {$ENDC}
  120.  
  121.  
  122. (* KeepOnScreen makes border checks to keep the sprite within the window.}
  123. {on a border hit, the speed is negated in order to make the sprite bounce.}
  124. {KeepOnScreen returns true if a border was hit. *)
  125.  
  126.     function KeepOnScreen (theSprite: SpritePtr): Boolean;
  127.         var
  128.             returnValue: Boolean;
  129.     begin
  130.         returnValue := false;
  131.         if theSprite^.position.h < 0 then
  132.             begin
  133.                 theSprite^.position.h := 0;
  134.                 theSprite^.speed.h := abs(theSprite^.speed.h);
  135.                 returnValue := true;
  136.             end;
  137.         if theSprite^.position.v < 0 then
  138.             begin
  139.                 theSprite^.position.v := 0;
  140.                 theSprite^.speed.v := abs(theSprite^.speed.v);
  141.                 returnValue := true;
  142.             end;
  143.         if theSprite^.position.h > gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right then
  144.             begin
  145.                 theSprite^.position.h := gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right;
  146.                 theSprite^.speed.h := -abs(theSprite^.speed.h);
  147.                 returnValue := true;
  148.             end;
  149.         if theSprite^.position.v > gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom then
  150.             begin
  151.                 theSprite^.position.v := gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom;
  152.                 theSprite^.speed.v := -abs(theSprite^.speed.v);
  153.                 returnValue := true;
  154.             end;
  155.  
  156.         KeepOnScreen := returnValue;
  157.     end; (*KeepOnScreen*)
  158.  
  159.  
  160. {$ifc _hasfixedpoint}
  161.  
  162. (* Same as above, but also modifies the fixedPointPosition field *)
  163.     function KeepOnScreenFixed (theSprite: FixSpritePtr): Boolean;
  164.         var
  165.             returnValue: Boolean;
  166.     begin
  167.         returnValue := false;
  168.         if theSprite^.fixedPointPosition.h < 0 then
  169.             begin
  170.                 theSprite^.position.h := 0;
  171.                 theSprite^.fixedPointPosition.h := 0;
  172.                 theSprite^.speed.h := abs(theSprite^.speed.h);
  173.                 returnValue := true;
  174.             end;
  175.         if theSprite^.fixedPointPosition.v < 0 then
  176.             begin
  177.                 theSprite^.position.v := 0;
  178.                 theSprite^.fixedPointPosition.v := 0;
  179.                 theSprite^.speed.v := abs(theSprite^.speed.v);
  180.                 returnValue := true;
  181.             end;
  182.         if theSprite^.position.h > gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right then
  183.             begin
  184.                 theSprite^.position.h := gSAT.offScreen.port^.portRect.right - theSprite^.hotRect.right;
  185.                 theSprite^.fixedPointPosition.h := BSL(theSprite^.position.h, kFixedPointShift);
  186.                 theSprite^.speed.h := -abs(theSprite^.speed.h);
  187.                 returnValue := true;
  188.             end;
  189.         if theSprite^.position.v > gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom then
  190.             begin
  191.                 theSprite^.position.v := gSAT.offScreen.port^.portRect.bottom - theSprite^.hotRect.bottom;
  192.                 theSprite^.fixedPointPosition.v := BSL(theSprite^.position.v, kFixedPointShift);
  193.                 theSprite^.speed.v := -abs(theSprite^.speed.v);
  194.                 returnValue := true;
  195.             end;
  196.  
  197.         KeepOnScreenFixed := returnValue;
  198.     end; (*KeepOnScreenFixed*)
  199. {$endc}
  200.  
  201.  
  202.     function KeepSpriteInRect (theSprite: SpritePtr; r: Rect): Boolean;
  203.         var
  204.             returnValue: Boolean;
  205.     begin
  206.         returnValue := false;
  207.         if theSprite^.position.h < r.left then
  208.             begin
  209.                 theSprite^.position.h := r.left;
  210.                 theSprite^.speed.h := abs(theSprite^.speed.h);
  211.                 returnValue := true;
  212.             end;
  213.         if theSprite^.position.v < r.top then
  214.             begin
  215.                 theSprite^.position.v := r.top;
  216.                 theSprite^.speed.v := abs(theSprite^.speed.v);
  217.                 returnValue := true;
  218.             end;
  219.         if theSprite^.position.h > r.right - theSprite^.hotRect.right then
  220.             begin
  221.                 theSprite^.position.h := r.right - theSprite^.hotRect.right;
  222.                 theSprite^.speed.h := -abs(theSprite^.speed.h);
  223.                 returnValue := true;
  224.             end;
  225.         if theSprite^.position.v > r.bottom - theSprite^.hotRect.bottom then
  226.             begin
  227.                 theSprite^.position.v := r.bottom - theSprite^.hotRect.bottom;
  228.                 theSprite^.speed.v := -abs(theSprite^.speed.v);
  229.                 returnValue := true;
  230.             end;
  231.  
  232.         KeepSpriteInRect := returnValue;
  233.     end; (*KeepSpriteInRect*)
  234.  
  235.  
  236. (* Moves two sprites apart, to separate them with respect to their bounding boxes. *)
  237.  
  238.     function RectSeparate (theSprite: SpritePtr; anotherSprite: SpritePtr; push: Integer): Integer;
  239.         var
  240.             distance: array[0..3] of Integer;
  241.             shortest, shortestDistance, i: Integer;
  242.             bounds1, bounds2: Rect;
  243.             pushMe, pushHim: Integer;
  244.     begin
  245.         bounds1 := theSprite^.hotRect;    {Duger inte hotRect2???}
  246.         OffsetRect(bounds1, theSprite^.position.h, theSprite^.position.v);
  247.  
  248.         bounds2 := anotherSprite^.hotRect;        {Duger inte hotRect2???}
  249.         OffsetRect(bounds2, anotherSprite^.position.h, anotherSprite^.position.v);
  250.  
  251. (*Calculate the distance to separate the sprites in every direction*)
  252.         distance[0] := bounds2.top - bounds1.bottom; {up}
  253.         distance[1] := bounds2.bottom - bounds1.top; {down}
  254.         distance[2] := bounds2.right - bounds1.left; {right}
  255.         distance[3] := bounds2.left - bounds1.right; {left}
  256.  
  257. (*Find the shortest distance*)
  258.         shortest := 0;
  259.         shortestDistance := abs(distance[0]);
  260.         for i := 1 to 3 do
  261.             if abs(distance[i]) < shortestDistance then
  262.                 begin
  263.                     shortest := i;
  264.                     shortestDistance := abs(distance[i]);
  265.                 end;
  266.  
  267. (*Move the sprite in the appropriate direction*)
  268.         case push of
  269.             kPushBoth: 
  270.                 begin
  271.                     pushMe := distance[shortest] div 2; {Bugfix 24/1-97}
  272.                     pushHim := distance[shortest] - pushMe;
  273.                     case shortest of
  274.                         0, 1: 
  275.                             begin
  276.                                 theSprite^.position.v := theSprite^.position.v + pushMe;
  277.                                 anotherSprite^.position.v := anotherSprite^.position.v - pushHim;
  278.                             end;
  279.                         2, 3: 
  280.                             begin
  281.                                 theSprite^.position.h := theSprite^.position.h + pushMe;
  282.                                 anotherSprite^.position.h := anotherSprite^.position.h - pushHim;
  283.                             end;
  284.                     end; {case}
  285.                 end;
  286.             kPushMe: 
  287.                 case shortest of
  288.                     0, 1: 
  289.                         theSprite^.position.v := theSprite^.position.v + distance[shortest];
  290.                     2, 3: 
  291.                         theSprite^.position.h := theSprite^.position.h + distance[shortest];
  292.                 end; {case}
  293.             kPushHim: 
  294.                 case shortest of
  295.                     0, 1: 
  296.                         anotherSprite^.position.v := anotherSprite^.position.v - distance[shortest];
  297.                     2, 3: 
  298.                         anotherSprite^.position.h := anotherSprite^.position.h - distance[shortest];
  299.                 end; {case}
  300.         end; {case}
  301.         RectSeparate := shortest;
  302.     end; (*RectSeparate*)
  303.  
  304.     procedure InternalDoBounce (me, him: SpritePtr; separateResult, push: Integer);
  305.         var
  306.             tempSpeed: integer;
  307.     begin
  308.         case push of
  309.             kPushBoth: 
  310.                 if separateResult >= 2 then { 2 or 3: horizontal, otherwise vertical}
  311.                     begin
  312.                         tempSpeed := me^.speed.h;
  313.                         me^.speed.h := him^.speed.h;
  314.                         him^.speed.h := tempSpeed;
  315.                     end
  316.                 else
  317.                     begin
  318.                         tempSpeed := me^.speed.v;
  319.                         me^.speed.v := him^.speed.v;
  320.                         him^.speed.v := tempSpeed;
  321.                     end;
  322.             kPushMe: 
  323.                 if separateResult >= 2 then
  324.                     begin
  325.                         if me^.position.h > him^.position.h then
  326.                             me^.speed.h := Abs(me^.speed.h)
  327.                         else
  328.                             me^.speed.h := -Abs(me^.speed.h);
  329.                     end
  330.                 else
  331.                     begin
  332.                         if me^.position.v > him^.position.v then
  333.                             me^.speed.v := Abs(me^.speed.v)
  334.                         else
  335.                             me^.speed.v := -Abs(me^.speed.v);
  336.                     end;
  337.             kPushHim: 
  338.                 if separateResult >= 2 then { 2 or 3: horizontal, otherwise vertical}
  339.                     begin
  340.                         if him^.position.h > me^.position.h then
  341.                             him^.speed.h := Abs(him^.speed.h)
  342.                         else
  343.                             him^.speed.h := -Abs(him^.speed.h);
  344.                     end
  345.                 else
  346.                     begin
  347.                         if him^.position.v > me^.position.v then
  348.                             him^.speed.v := Abs(him^.speed.v)
  349.                         else
  350.                             him^.speed.v := -Abs(him^.speed.v);
  351.                     end;
  352.         end; {case}
  353.     end; {InternalDoBounce}
  354.  
  355.     procedure RectBounce (me, him: SpritePtr; push: Integer);
  356.         var
  357.             result: Integer;
  358.     begin
  359.         result := RectSeparate(me, him, push);
  360.         InternalDoBounce(me, him, result, push);
  361.     end; {RectBounce}
  362.  
  363.     procedure RectBounceFixed (me, him: FixSpritePtr; push: Integer);
  364.         var
  365.             result: integer;
  366.     begin
  367.         result := RectSeparate(SpritePtr(me), SpritePtr(him), push);
  368.         InternalDoBounce(SpritePtr(me), SpritePtr(him), result, push);
  369.  
  370.         me^.fixedPointPosition.h := BSL(me^.position.h, kFixedPointShift);
  371.         me^.fixedPointPosition.v := BSL(me^.position.v, kFixedPointShift);
  372.         him^.fixedPointPosition.h := BSL(him^.position.h, kFixedPointShift);
  373.         him^.fixedPointPosition.v := BSL(him^.position.v, kFixedPointShift);
  374.     end; {RectBounceFixed}
  375.  
  376.  
  377. {Check if a point is within a sprite – useful for mouse hits in sprites}
  378.  
  379.     function PtInSpriteRgn (p: Point; theSprite: SpritePtr): Boolean;
  380.         var
  381.             faceRgn: RgnHandle;
  382.     begin
  383.         PtInSpriteRgn := false;
  384.         if theSprite = nil then
  385.             Exit(PtInSpriteRgn);
  386.         if theSprite^.face = nil then
  387.             Exit(PtInSpriteRgn);
  388.  
  389.         faceRgn := NewRgn;
  390.  
  391.         CopyRgn(theSprite^.face^.maskRgn, faceRgn);
  392.         OffsetRgn(faceRgn, theSprite^.position.h, theSprite^.position.v);
  393.  
  394.         PtInSpriteRgn := PtInRgn(p, faceRgn);
  395.         DisposeRgn(faceRgn);
  396.     end; {PtInSpriteRgn}
  397.  
  398.  
  399. (* Collision test using regions! *)
  400.  
  401.     function RegionHit (theSprite: SpritePtr; anotherSprite: SpritePtr): Boolean;
  402.         var
  403.             faceRegion1, faceRegion2: RgnHandle;
  404.             result: Boolean;
  405.     begin
  406.         faceRegion1 := NewRgn;
  407.         faceRegion2 := NewRgn;
  408.  
  409.         CopyRgn(theSprite^.face^.maskRgn, faceRegion1);
  410.         OffsetRgn(faceRegion1, theSprite^.position.h, theSprite^.position.v);
  411.  
  412.         CopyRgn(anotherSprite^.face^.maskRgn, faceRegion2);
  413.         OffsetRgn(faceRegion2, anotherSprite^.position.h, anotherSprite^.position.v);
  414.  
  415.         SectRgn(faceRegion1, faceRegion2, faceRegion1);
  416.         result := not EmptyRgn(faceRegion1);
  417.  
  418.         DisposeRgn(faceRegion1);
  419.         DisposeRgn(faceRegion2);
  420.  
  421.         RegionHit := result;
  422.     end; (*RegionHit*)
  423.  
  424.  
  425. (* Split a vector v into a component p parallel to another vector d,}
  426. {and a component n that is perpendicular to d. Useful for realistic}
  427. {collision handling! *)
  428.  
  429.     procedure SplitVector (v: Point; d: Point; var p: Point; var n: Point);
  430.         var
  431.             length2, dotProduct: LongInt;
  432.     begin
  433.         length2 := d.h * d.h + d.v * d.v;    (*Squared length of "d"*)
  434.  
  435.         dotProduct := v.h * d.h + v.v * d.v;    (*Scalar product*)
  436.  
  437.         p.h := d.h * dotProduct div length2;
  438.         p.v := d.v * dotProduct div length2;
  439.         n.h := v.h - p.h;
  440.         n.v := v.v - p.v;
  441.     end; (* SplitVector *)
  442.  
  443.  
  444.  
  445.  
  446.  
  447.  
  448.  
  449.  
  450.  
  451.  
  452.  
  453.  
  454.  
  455.     procedure RegionSeparate (i, j: FixSpritePtr; r1, r2, diff: RgnHandle);
  456.         var
  457.             internalDiff: RgnHandle;
  458.             initVector, nowVector: Point;
  459.             absH, absV: integer;
  460.             moveH, moveV: integer;
  461.             frac: integer;
  462. {Normal signum function (which I don't think is in the libs)}
  463.         function Sgn (x: integer): integer;
  464.         begin
  465.             if x > 0 then
  466.                 Sgn := 1
  467.             else if x < 0 then
  468.                 Sgn := -1
  469.             else
  470.                 Sgn := 0;
  471.         end;
  472.  
  473.     begin {RegionSeparate}
  474.         internalDiff := NewRgn;
  475.         frac := 0;
  476.         initVector.h := i^.position.h - j^.position.h;
  477.         initVector.v := i^.position.v - j^.position.v;
  478.  
  479.         initVector.h := -(diff^^.rgnBBox.right + diff^^.rgnBBox.left) div 2 + (r1^^.rgnBBox.right + r1^^.rgnBBox.left) div 2;
  480.         initVector.v := -(diff^^.rgnBBox.bottom + diff^^.rgnBBox.top) div 2 + (r1^^.rgnBBox.bottom + r1^^.rgnBBox.top) div 2;
  481.  
  482.         absH := abs(initVector.h);
  483.         absV := abs(initVector.v);
  484.         moveH := Sgn(initVector.h);
  485.         moveV := Sgn(initVector.v);
  486.         if moveH = 0 then
  487.             if moveV = 0 then
  488.                 moveV := 1;
  489.         repeat
  490.             if absH > absV then
  491.                 begin
  492.                     i^.position.h := i^.position.h + moveH;
  493.                     OffsetRgn(r1, moveH, 0);
  494. {j^.position.h := j^.position.h - moveH;}
  495.                     frac := frac + absV;
  496.                     if frac > absH then
  497.                         begin
  498.                             i^.position.v := i^.position.v + moveV;
  499.                             OffsetRgn(r1, 0, moveV);
  500. {j^.position.v := j^.position.v - moveV;}
  501.                             frac := frac - absH;
  502.                         end
  503.                 end
  504.             else
  505.                 begin
  506.                     i^.position.v := i^.position.v + moveV;
  507.                     OffsetRgn(r1, 0, moveV);
  508. {j^.position.v := j^.position.v - moveV;}
  509.                     frac := frac + absH;
  510.                     if frac > absV then
  511.                         begin
  512.                             i^.position.h := i^.position.h + moveH;
  513.                             OffsetRgn(r1, moveH, 0);
  514. {j^.position.h := j^.position.h - moveH;}
  515.                             frac := frac - absV;
  516.                         end
  517.                 end;
  518.             nowVector.h := i^.position.h - j^.position.h;
  519.             nowVector.v := i^.position.v - j^.position.v;
  520.  
  521.             SectRgn(r1, r2, internalDiff);
  522.         until EmptyRgn(internalDiff);
  523.  
  524. {until longint(nowVector.h) * nowVector.h + longint(nowVector.v) * nowVector.v > kBallDiameterSquared;}
  525.         i^.fixedPointPosition.h := BSL(i^.position.h, 4); {Make fixedPos by shifting in the 4 binary "decimals"}
  526.         i^.fixedPointPosition.v := BSL(i^.position.v, 4);
  527.  
  528. {j^.fixedPos.h := BSL(j^.position.h, 4); {Make fixedPos by shifting in the 4 binary "decimals"}
  529. {j^.fixedPos.v := BSL(j^.position.v, 4);}
  530.  
  531.         DisposeRgn(internalDiff);
  532.     end;{RegionSeparate}
  533.  
  534.  
  535.  
  536. {Variant av RegionHitTest som antar att s1 är en flyttbar sprite (boll) och den andra}
  537. {ett icke cirkulärt objekt.}
  538. {elasticity is percent of the speed that is preserved}
  539.  
  540.     function RegionBounce (s1, s2: FixSpritePtr; elasticity: Integer): Boolean;
  541.         var
  542.             r1, r2, diff: RgnHandle;
  543.             vector, parallel, normal: Point;
  544.             elast: Longint;
  545.     begin
  546.         elast := elasticity;    {Move to Longint}
  547.         RegionBounce := false;
  548.  
  549.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  550.             begin
  551.                 SATReportStr('Error: No mask region!');
  552.                 exit(RegionBounce);
  553.             end;
  554.  
  555. {Make copies of the mask regions and offset them to the proper places.}
  556.         r1 := NewRgn;
  557.         r2 := NewRgn;
  558.         diff := NewRgn;
  559.         CopyRgn(s1^.face^.maskRgn, r1);
  560.         CopyRgn(s2^.face^.maskRgn, r2);
  561.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  562.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  563.  
  564.         SectRgn(r1, r2, diff);                    {Is there any overlap?}
  565.  
  566. {If empty, no collision, otherwise, handle the collision!}
  567.         if not EmptyRgn(diff) then
  568.             begin
  569.                 vector.h := (diff^^.rgnBBox.right + diff^^.rgnBBox.left) div 2 - (r1^^.rgnBBox.right + r1^^.rgnBBox.left) div 2;
  570.                 vector.v := (diff^^.rgnBBox.bottom + diff^^.rgnBBox.top) div 2 - (r1^^.rgnBBox.bottom + r1^^.rgnBBox.top) div 2;
  571.  
  572.                 RegionSeparate(s1, s2, r1, r2, diff);
  573.                 RegionBounce := true;
  574.  
  575. {Avstuds som antar att vi skall ha elastisk studs utan störningar.}
  576. {Flipprar, bumpers och sånt måste ha diverse påslag och variationer.}
  577. {För att få plastisk studs - det vill man också ha ibland - så skall väl parallell-vektorn tas bort}
  578. {eller dämpas kraftigt? Detta görs med parametern "elasticity". Brus?}
  579.                 if Longint(vector) <> 0 then {Skydd mot div med 0.}
  580.                     begin
  581.                         SplitVector(s1^.speed, vector, parallel, normal);
  582.                         s1^.speed.h := Longint(-parallel.h) * elasticity div 100 + normal.h;
  583.                         s1^.speed.v := Longint(-parallel.v) * elasticity div 100 + normal.v;
  584.                     end;
  585.             end;
  586.  
  587.         DisposeRgn(r1);
  588.         DisposeRgn(r2);
  589.         DisposeRgn(diff);
  590.     end; {RegionBounce}
  591.  
  592.  
  593. {Like RegionBounce but assumes that both objects are movable.}
  594.     function RegionBounce2 (s1, s2: FixSpritePtr; elasticity: Integer): Boolean;
  595.         var
  596.             r1, r2, diff: RgnHandle;
  597.             vector, parallel, normal: Point;
  598.             parallel2, normal2: Point;
  599.             elast: Longint;
  600.     begin
  601.         elast := elasticity;    {Move to Longint}
  602.         RegionBounce2 := false;
  603.  
  604.         if (s1^.face^.maskRgn = nil) or (s2^.face^.maskRgn = nil) then
  605.             begin
  606.                 SATReportStr('Error: No mask region!');
  607.                 exit(RegionBounce2);
  608.             end;
  609.  
  610. {Make copies of the mask regions and offset them to the proper places.}
  611.         r1 := NewRgn;
  612.         r2 := NewRgn;
  613.         diff := NewRgn;
  614.         CopyRgn(s1^.face^.maskRgn, r1);
  615.         CopyRgn(s2^.face^.maskRgn, r2);
  616.         OffsetRgn(r1, s1^.position.h, s1^.position.v);
  617.         OffsetRgn(r2, s2^.position.h, s2^.position.v);
  618.  
  619.         SectRgn(r1, r2, diff);                    {Is there any overlap?}
  620.  
  621. {If empty, no collision, otherwise, handle the collision!}
  622.         if not EmptyRgn(diff) then
  623.             begin
  624.                 vector.h := (diff^^.rgnBBox.right + diff^^.rgnBBox.left) div 2 - (r1^^.rgnBBox.right + r1^^.rgnBBox.left) div 2;
  625.                 vector.v := (diff^^.rgnBBox.bottom + diff^^.rgnBBox.top) div 2 - (r1^^.rgnBBox.bottom + r1^^.rgnBBox.top) div 2;
  626.  
  627.                 RegionSeparate(s1, s2, r1, r2, diff);
  628.                 RegionBounce2 := true;
  629.  
  630. {Avstuds som antar att vi skall ha elastisk studs utan störningar.}
  631. {Flipprar, bumpers och sånt måste ha diverse påslag och variationer.}
  632. {För att få plastisk studs - det vill man också ha ibland - så skall väl parallell-vektorn tas bort}
  633. {eller dämpas kraftigt? Detta görs med parametern "elasticity". Brus?}
  634.                 if Longint(vector) <> 0 then {Skydd mot div med 0.}
  635.                     begin
  636.                         SplitVector(s1^.speed, vector, parallel, normal);
  637.                         SplitVector(s2^.speed, vector, parallel2, normal2);
  638.                         s1^.speed.h := Longint(parallel2.h) * elasticity div 100 + normal.h;
  639.                         s1^.speed.v := Longint(parallel2.v) * elasticity div 100 + normal.v;
  640.                         s2^.speed.h := Longint(parallel.h) * elasticity div 100 + normal2.h;
  641.                         s2^.speed.v := Longint(parallel.v) * elasticity div 100 + normal2.v;
  642.                     end;
  643.             end;
  644.  
  645.         DisposeRgn(r1);
  646.         DisposeRgn(r2);
  647.         DisposeRgn(diff);
  648.     end; {RegionBounce2}
  649.  
  650.  
  651.  
  652.  
  653. {Look-up table handlers:}
  654.  
  655.     const
  656.         kMaxAngle = 360;
  657.         kMaxSqrt = 1000;
  658.         kPi = 3.1416;
  659.     var
  660.         sinTable: array[0..kMaxAngle] of Longint;
  661.         sqrtTable: array[0..kMaxSqrt] of Longint;
  662.  
  663.     procedure InitTables;
  664.         var
  665.             i: Longint;
  666.     begin
  667.         for i := 0 to kMaxSqrt do
  668.             sqrtTable[i] := Trunc(sqrt(kFixedOne * i));
  669.         for i := 0 to kMaxAngle do
  670.             sinTable[i] := Trunc(kFixedOne * sin(i * 2 * kPi / kMaxAngle));
  671.     end; {InitTables}
  672.  
  673. {Take the fixed-point square root of a fixed-point number}
  674.     function SquareRoot (arg: Longint): Longint;
  675.         var
  676.             shift: Integer;
  677.     begin
  678.         if sqrtTable[kFixedOne] <> kFixedOne then
  679.             InitTables;
  680.         shift := 0;
  681.         if arg < 0 then
  682.             begin
  683.                 SquareRoot := 0;
  684.                 Exit(SquareRoot);
  685.             end;
  686.         while arg > kMaxSqrt do
  687.             begin
  688.                 shift := shift + 1;
  689.                 arg := BSR(arg, 2);
  690.             end;
  691.         SquareRoot := BSL(sqrtTable[arg], shift);
  692.     end; {SquareRoot}
  693.  
  694. {Get the fixed-point sinus of a fixed-point number}
  695.     function Sinus (arg: Longint): Longint;
  696.     begin
  697.         if sqrtTable[kFixedOne] <> kFixedOne then
  698.             InitTables;
  699.         while arg < 0 do
  700.             arg := arg + kMaxAngle;
  701.         while arg > kMaxAngle do
  702.             arg := arg - kMaxAngle;
  703.         Sinus := sinTable[arg];
  704.     end; {Sinus}
  705.  
  706. {Get the fixed-point cosinus of a fixed-point number}
  707.     function Cosinus (arg: Longint): Longint;
  708.     begin
  709.         if sqrtTable[kFixedOne] <> kFixedOne then
  710.             InitTables;
  711.         arg := arg + BSR(kMaxAngle, 2); {Displace sinus to get cosinus!}
  712.         while arg < 0 do
  713.             arg := arg + kMaxAngle;
  714.         while arg > kMaxAngle do
  715.             arg := arg - kMaxAngle;
  716.         Cosinus := sinTable[arg];
  717.     end; {Cosinus}
  718.  
  719. {Get the length of a vector (integer vector, fixed-point result!)}
  720.     function VectorLength (vector: Point): Longint;
  721.     begin
  722.         VectorLength := SquareRoot(BSL(vector.h * vector.h + vector.v * vector.v, kFixedPointShift));
  723.     end; {VectorLength}
  724.  
  725. {Get the length of a vector (fixed-point vector, fixed-point result!)}
  726.     function FPVectorLength (vector: Point): Longint;
  727.     begin
  728.         FPVectorLength := SquareRoot(vector.h * vector.h + vector.v * vector.v);
  729.     end; {VectorLength}
  730.  
  731. {ApproxVectorLength makes an approximation using only simple integer operations.}
  732.     function ApproxVectorLength (vector: Point): Longint;
  733.         var
  734.             maj, min: Integer;
  735.     begin
  736.         if abs(vector.h) > abs(vector.v) then
  737.             begin
  738.                 maj := abs(vector.h);
  739.                 min := abs(vector.v);
  740.             end
  741.         else
  742.             begin
  743.                 maj := abs(vector.v);
  744.                 min := abs(vector.h);
  745.             end;
  746.         ApproxVectorLength := Longint(maj) + BSR(min + BSR(min, 1), 2); {UNTESTED}
  747.     end; {ApproxVectorLength}
  748.  
  749. end.