home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1996 November / macformat-043.iso / mac / Shareware Plus / Developers / Sprite Animation Toolkit 2.3.8 / Add-ons / Sprite behavior / SATStrictGridToolbox.p < prev    next >
Encoding:
Text File  |  1995-09-10  |  12.8 KB  |  391 lines  |  [TEXT/PJMM]

  1. unit SATStrictGridToolbox;
  2.  
  3. interface
  4.     uses
  5. {$IFC UNDEFINED THINK_PASCAL}
  6.         Types, QuickDraw, Fonts, Events, Packages, Menus, Dialogs, Windows,{}
  7.         OSUtils, ToolUtils, Resources,
  8. {$ENDC}
  9.         SAT, SATToolbox, SATGridStubs, SATGridToolbox;
  10.  
  11.  
  12.     type
  13.         GridSpritePtr = ^GridSprite;
  14.         GridSprite = record
  15. { Variables that you should change as appropriate }
  16.                 kind: Integer; { Used for identification. >0: friend. <0 foe }
  17.                 position: Point;
  18.                 hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
  19.                                     {hotRect is set by you. hotRect2 is offset to the current position.}
  20.                 face: FacePtr;            { Pointer to the Face (appearance) to be used. }
  21.                 task: ProcPtr;            { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
  22.                 hitTask: ProcPtr;        { Callback in collisions. }
  23.                 destructTask: ProcPtr;    { Called when a sprite is disposed. (Usually nil.) }
  24.                 clip: RgnHandle;            {Clip region to be used when this sprite is drawn.}
  25. { SAT variables that you shouldn't change: }
  26.                 oldpos: Point;                {Used by RunSAT2}
  27.                 next, prev: SpritePtr;    {You may change them in your own sorting routine, but be careful if you do.}
  28.                 r, oldr: Rect;                {Rectangle telling where to draw. Avoid messing with it.}
  29.                 oldFace: FacePtr;            {Used by RunSAT2}
  30.                 dirty: Boolean;            {Used by RunSAT2}
  31. {Variables for internal use by the sprites. Use as you please. Edit as necessary - this is merely a default}
  32. {set, enough space for most cases - but if you change the size of the record, call SetSpriteSize immediately}
  33. {after initializing (before any sprites are created)!}
  34.                 layer: integer; {For layer-sorting. When not used for that, use freely.}
  35.                 gridPos: Point; { Position in grid coordinate. }
  36.                 mode: integer; { Usually used for different modes and/or to determine what image to show next. }
  37.                 whatever: Longint; {free for any use}
  38.                 direction, partMove: SignedByte;
  39.                 appInt: Integer; {free for any use.}
  40.             end;
  41.  
  42.     procedure InitStrictGrid;                                                                {Init tables}
  43.  
  44. {Movement and collisions}
  45.     function GridMoveSprite (theSprite: GridSpritePtr; howFar: Integer): Boolean;    {Returns true if the sprite can change direction}
  46.     procedure GridTurnAroundSprite (theSprite: GridSpritePtr);                        {Reverse direction}
  47.     procedure GridRectBounce (theSprite: GridSpritePtr; anotherSprite: GridSpritePtr; push: Integer);
  48.     function KeepInGrid (theSprite: GridSpritePtr): Boolean;
  49.  
  50. {Utilities for inspecting the grid}
  51.     function GridPtInGrid (p: Point): Boolean;                                                {Is a grid point blocked or out of bounds?}
  52.     function PossibleMove (sp: GridSpritePtr; direction: Integer): Boolean;            {Can a sprite move in a specified direction?}
  53.     function GetGridStep (fromP: Point; direction: Integer; distance: Integer): Point;    {Get a grid point in a specified direction ad distance}
  54.  
  55.     const
  56.         kUp = 0;
  57.         kLeft = 1;
  58.         kDown = 2;
  59.         kRight = 3;
  60.  
  61.         kSnapDist = 2;        {How far from a junction can a sprite freely change direction if pushed?}
  62.  
  63.     var
  64.         dirTable: array[0..3] of Point;    {One step in each direction}
  65.         sizeTable: array[0..3] of Integer;    {Size of grid spaces in each direction}
  66.  
  67. implementation
  68.  
  69.     function MakePoint (h, v: Integer): Point;
  70.     begin
  71.         MakePoint.h := h;
  72.         MakePoint.v := v;
  73.     end; {MakePoint}
  74.  
  75.     function GetGridStep (fromP: Point; direction: Integer; distance: Integer): Point;
  76.     begin
  77.         GetGridStep.h := fromP.h + dirTable[direction].h * distance;
  78.         GetGridStep.v := fromP.v + dirTable[direction].v * distance;
  79.     end; {GetGridStep}
  80.  
  81.  
  82.     procedure InitStrictGrid;
  83.     begin
  84.         dirTable[kRight] := MakePoint(1, 0);
  85.         dirTable[kUp] := MakePoint(0, -1);
  86.         dirTable[kLeft] := MakePoint(-1, 0);
  87.         dirTable[kDown] := MakePoint(0, 1);
  88.  
  89.         sizeTable[kRight] := kTileSizeH;
  90.         sizeTable[kLeft] := kTileSizeH;
  91.         sizeTable[kUp] := kTileSizeV;
  92.         sizeTable[kDown] := kTileSizeV;
  93.     end; {InitStrictGrid}
  94.  
  95. {Borde till stubs-funktion.}
  96. {Kan man fixa vridna, pseudo-3D-grids med denna?}
  97.     function Sprite2Point (theSprite: GridSpritePtr): Point;
  98.     begin
  99.         case theSprite^.direction of
  100.             kUp: 
  101.                 begin
  102.                     Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH;
  103.                     Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV - theSprite^.partMove;
  104.                 end;
  105.             kRight: 
  106.                 begin
  107.                     Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH + theSprite^.partMove;
  108.                     Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV;
  109.                 end;
  110.             kDown: 
  111.                 begin
  112.                     Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH;
  113.                     Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV + theSprite^.partMove;
  114.                 end;
  115.             kLeft: 
  116.                 begin
  117.                     Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH - theSprite^.partMove;
  118.                     Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV;
  119.                 end;
  120.         end; {case}
  121.     end; {Sprite2Point}
  122.  
  123.     function GridPtInGrid (p: Point): Boolean;
  124.     begin
  125.         GridPtInGrid := tileArray[p.h][p.v] > kFreeSpace;
  126.  
  127.         if p.h < 0 then
  128.             GridPtInGrid := true
  129.         else if p.v < 0 then
  130.             GridPtInGrid := true
  131.         else if p.h > kArraySizeH then
  132.             GridPtInGrid := true
  133.         else if p.v > kArraySizeV then
  134.             GridPtInGrid := true;
  135.     end; (*GridPtInGrid*)
  136.  
  137.     function PossibleMove (sp: GridSpritePtr; direction: Integer): Boolean;
  138.     begin
  139.         PossibleMove := not GridPtInGrid(GetGridStep(sp^.gridPos, direction, 1));
  140.     end;
  141.  
  142.  
  143.     function GridMoveSprite (theSprite: GridSpritePtr; howFar: Integer): Boolean;
  144.         var
  145.             spacing: Integer;
  146.             nextP: Point;
  147.     begin
  148.         GridMoveSprite := false;
  149.  
  150.         nextP.h := theSprite^.gridpos.h + dirTable[theSprite^.direction].h;
  151.         nextP.v := theSprite^.gridpos.v + dirTable[theSprite^.direction].v;
  152.         if GridPtInGrid(nextP) then
  153.             begin {blocked}
  154.                 theSprite^.partMove := 0;
  155.                 GridMoveSprite := true;
  156.             end
  157.         else
  158.             begin {not blocked}
  159.  
  160.                 spacing := sizeTable[theSprite^.direction];
  161.                 if false then
  162.                     case theSprite^.direction of
  163.                         kUp, kDown: 
  164.                             spacing := kTileSizeV;
  165.                         kLeft, kRight: 
  166.                             spacing := kTileSizeH;
  167.                     end; {case}
  168.  
  169.                 if howFar >= spacing - theSprite^.partMove then
  170.                     begin
  171.                         theSprite^.partMove := 0;
  172.                         theSprite^.gridpos.h := theSprite^.gridpos.h + dirTable[theSprite^.direction].h;
  173.                         theSprite^.gridpos.v := theSprite^.gridpos.v + dirTable[theSprite^.direction].v;
  174.                         GridMoveSprite := true;
  175.                     end
  176.                 else
  177.                     begin
  178.                         theSprite^.partMove := theSprite^.partMove + howFar;
  179.                     end;
  180.             end; {not blocked}
  181.  
  182.         theSprite^.position := Sprite2Point(theSprite);
  183.  
  184.     end; {GridMoveSprite}
  185.  
  186.     procedure GridTurnAroundSprite (theSprite: GridSpritePtr);                        {Revere direction}
  187.     begin
  188.         theSprite^.gridpos.h := theSprite^.gridpos.h + dirTable[theSprite^.direction].h;
  189.         theSprite^.gridpos.v := theSprite^.gridpos.v + dirTable[theSprite^.direction].v;
  190.         theSprite^.direction := BitXor(theSprite^.direction, 2);
  191.         theSprite^.partMove := sizeTable[theSprite^.direction] - theSprite^.partMove;
  192.     end; {GridTurnAroundSprite}
  193.  
  194.     procedure GridRectBounce (theSprite: GridSpritePtr; anotherSprite: GridSpritePtr; push: Integer);
  195.         type
  196.             ArrRect = array[0..3] of Integer; {Index-able Rect!}
  197.         var
  198.             bounds1, bounds2: ArrRect;
  199.             min, max1, max2: array[0..3] of Integer;
  200.             shortest, shortestDistance, i: Integer;
  201.             mov1, mov2: Integer;
  202.     begin
  203. {bounds1 := ArrRect(theSprite^.hotRect);}
  204. {OffsetRect(bounds1, theSprite^.position); {or use hotRect2?}
  205. {bounds2 := ArrRect(anotherSprite^.hotRect);}
  206. {OffsetRect(bounds2, anotherSprite^.position); {or use hotRect2?}
  207.  
  208.         bounds1 := ArrRect(theSprite^.hotRect2);
  209.         bounds2 := ArrRect(anotherSprite^.hotRect2);
  210.  
  211.         for i := 0 to 3 do
  212.             begin
  213.                 min[i] := Abs(bounds1[(i + 2) mod 4] - bounds2[i]);
  214.  
  215. {max for theSprite = max1}
  216.                 if push = kPushHim then
  217.                     max1[i] := 0
  218.                 else if BitAnd(i, 1) <> BitAnd(theSprite^.direction, 1) then                {Not the direction it is moving in!}
  219.                     if (theSprite^.partMove > kSnapDist) and (sizeTable[theSprite^.direction] - theSprite^.partMove > kSnapDist) then
  220.                         max1[i] := 0
  221.                     else
  222.                         begin                                                                    {wrong direction but allowed to push into this direction!}
  223.                             if GridPtInGrid(GetGridStep(theSprite^.gridPos, i, 1)) then
  224.                                 max1[i] := 0                                                        {blocked by the grid!}
  225.                             else
  226.                                 max1[i] := sizeTable[i];                                            {Go on, push me!}
  227.                         end
  228.                 else
  229.                     begin
  230.                         if i = theSprite^.direction then
  231.                             max1[i] := sizeTable[theSprite^.direction] - theSprite^.partMove {How far left ahead?}
  232.                         else
  233.                             max1[i] := theSprite^.partMove;                                    {How far back?}
  234.  
  235.                         if not GridPtInGrid(GetGridStep(theSprite^.gridPos, i, 1)) then
  236.                             max1[i] := max1[i] + sizeTable[theSprite^.direction];
  237.                     end;
  238.  
  239. {max for anotherSprite = max2}
  240.                 if push = kPushMe then
  241.                     max2[i] := 0
  242.                 else if BitAnd(i, 1) <> BitAnd(anotherSprite^.direction, 1) then                {Not the direction it is moving in!}
  243.                     if (anotherSprite^.partMove > kSnapDist) and (sizeTable[anotherSprite^.direction] - anotherSprite^.partMove > kSnapDist) then
  244.                         max2[i] := 0
  245.                     else
  246.                         begin                                                                    {wrong direction but allowed to push into this direction!}
  247.                             if GridPtInGrid(GetGridStep(anotherSprite^.gridPos, i, 1)) then
  248.                                 max2[i] := 0                                                        {blocked by the grid!}
  249.                             else
  250.                                 max2[i] := sizeTable[i];                                            {Go on, push me!}
  251.                         end
  252.                 else
  253.                     begin
  254.                         if i = anotherSprite^.direction then
  255.                             max2[i] := sizeTable[anotherSprite^.direction] - anotherSprite^.partMove {How far left ahead?}
  256.                         else
  257.                             max2[i] := anotherSprite^.partMove;                            {How far back?}
  258.  
  259.                         if not GridPtInGrid(GetGridStep(anotherSprite^.gridPos, i, 1)) then
  260.                             max2[i] := max2[i] + sizeTable[anotherSprite^.direction];
  261.                     end;
  262.  
  263.             end; {for}
  264.  
  265. (*Find the shortest distance*)
  266.         shortest := -1;
  267.         shortestDistance := 9999;
  268.         for i := 0 to 3 do
  269.             if min[i] < max1[i] + max2[i] then
  270.                 if min[i] < shortestDistance then
  271.                     begin
  272.                         shortest := i;
  273.                         shortestDistance := min[i];
  274.                     end;
  275.  
  276. {Do the push!}
  277.         if i <> -1 then
  278.             begin
  279. {Half the move for each!}
  280.                 mov1 := BSR(min[shortest], 1);
  281.                 mov2 := min[shortest] - mov1;
  282. {Is theSprite over its max?}
  283.                 if mov1 > max1[shortest] then
  284.                     begin
  285.                         mov2 := mov2 + (mov1 - max1[shortest]);
  286.                         mov1 := max1[shortest];
  287.                     end;
  288. {Is anotherSprite over its max?}
  289.                 if mov2 > max2[shortest] then
  290.                     begin
  291.                         mov1 := mov1 + (mov2 - max2[shortest]);
  292.                         mov2 := max2[shortest];
  293.                     end;
  294.  
  295. {Push theSprite}
  296.                 if mov1 > 0 then
  297.                     if shortest = theSprite^.direction then
  298.                         begin
  299.                             theSprite^.partMove := theSprite^.partMove + mov1;
  300.                         end
  301.                     else if BitAnd(shortest, 1) = BitAnd(theSprite^.direction, 1) then
  302.                         begin
  303.                             GridTurnAroundSprite(theSprite);
  304.                             theSprite^.partMove := theSprite^.partMove + mov1;
  305.                         end
  306.                     else
  307.                         begin
  308.                             theSprite^.direction := shortest;
  309.                             theSprite^.partMove := mov1;
  310.                         end;
  311.                 if theSprite^.partMove > sizeTable[shortest] then
  312.                     begin
  313.                         theSprite^.partMove := theSprite^.partMove - sizeTable[shortest];
  314.                         theSprite^.gridPos := GetGridStep(theSprite^.gridPos, shortest, 1);
  315.                     end;
  316.  
  317. {Reverse the direction "shortest" - to move anotherSprite in the other direction!}
  318.                 shortest := BitAnd(shortest + 2, 3);
  319.  
  320. {Push anotherSprite}
  321.                 if mov2 > 0 then
  322.                     if shortest = anotherSprite^.direction then
  323.                         begin
  324.                             anotherSprite^.partMove := anotherSprite^.partMove + mov2;
  325.                         end
  326.                     else if BitAnd(shortest, 1) = BitAnd(anotherSprite^.direction, 1) then
  327.                         begin
  328.                             GridTurnAroundSprite(anotherSprite);
  329.                             anotherSprite^.partMove := anotherSprite^.partMove + mov2;
  330.                         end
  331.                     else
  332.                         begin
  333.                             anotherSprite^.direction := shortest;
  334.                             anotherSprite^.partMove := mov2;
  335.                         end;
  336.                 if anotherSprite^.partMove > sizeTable[shortest] then
  337.                     begin
  338.                         anotherSprite^.partMove := anotherSprite^.partMove - sizeTable[shortest];
  339.                         anotherSprite^.gridPos := GetGridStep(anotherSprite^.gridPos, shortest, 1);
  340.                     end;
  341.  
  342.                 theSprite^.position := Sprite2Point(theSprite);
  343.                 anotherSprite^.position := Sprite2Point(anotherSprite);
  344.             end; {Push possible!}
  345.  
  346.     end; {GridRectBounce}
  347.  
  348. {KeepInGrid is unnecessary as long as there is a border with blocked tiles.}
  349.     function KeepInGrid (theSprite: GridSpritePtr): Boolean;
  350.         var
  351.             nextP: Point;
  352.     begin
  353.         KeepInGrid := false;
  354.         nextP := GetGridStep(theSprite^.gridPos, theSprite^.direction, 1);
  355.         if nextP.h < 0 then
  356.             begin
  357.                 theSprite^.partMove := 0;
  358.                 theSprite^.direction := kRight;
  359.                 KeepInGrid := true;
  360.             end;
  361.         if nextP.v < 0 then
  362.             begin
  363.                 theSprite^.partMove := 0;
  364.                 theSprite^.direction := kDown;
  365.                 KeepInGrid := true;
  366.             end;
  367.         if nextP.h > kArraySizeH then
  368.             begin
  369.                 theSprite^.partMove := 0;
  370.                 theSprite^.direction := kLeft;
  371.                 KeepInGrid := true;
  372.             end;
  373.         if nextP.v > kArraySizeV then
  374.             begin
  375.                 theSprite^.partMove := 0;
  376.                 theSprite^.direction := kUp;
  377.                 KeepInGrid := true;
  378.             end;
  379.  
  380. {The following shouldn't happen}
  381.         if theSprite^.gridPos.h < 0 then
  382.             theSprite^.gridPos.h := 0;
  383.         if theSprite^.gridPos.v < 0 then
  384.             theSprite^.gridPos.v := 0;
  385.         if theSprite^.gridPos.h > kArraySizeH then
  386.             theSprite^.gridPos.h := kArraySizeH;
  387.         if theSprite^.gridPos.v > kArraySizeV then
  388.             theSprite^.gridPos.v := kArraySizeV;
  389.     end; {KeepInGrid}
  390.  
  391. end.