home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / SAT 2.3.8 / Libraries & Documentation / Add-ons / Sprite behavior / SATToolbox.p < prev   
Encoding:
Text File  |  1996-05-28  |  19.8 KB  |  650 lines  |  [TEXT/PJMM]

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