home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-09-10 | 12.8 KB | 391 lines | [TEXT/PJMM] |
- unit SATStrictGridToolbox;
-
- interface
- uses
- {$IFC UNDEFINED THINK_PASCAL}
- Types, QuickDraw, Fonts, Events, Packages, Menus, Dialogs, Windows,{}
- OSUtils, ToolUtils, Resources,
- {$ENDC}
- SAT, SATToolbox, SATGridStubs, SATGridToolbox;
-
-
- type
- GridSpritePtr = ^GridSprite;
- GridSprite = record
- { Variables that you should change as appropriate }
- kind: Integer; { Used for identification. >0: friend. <0 foe }
- position: Point;
- hotRect, hotRect2: Rect; { Tells how large the sprite is; hotRect is centered around origo }
- {hotRect is set by you. hotRect2 is offset to the current position.}
- face: FacePtr; { Pointer to the Face (appearance) to be used. }
- task: ProcPtr; { Callback-routine, called once per frame. If task=nil, the sprite is removed. }
- hitTask: ProcPtr; { Callback in collisions. }
- destructTask: ProcPtr; { Called when a sprite is disposed. (Usually nil.) }
- clip: RgnHandle; {Clip region to be used when this sprite is drawn.}
- { SAT variables that you shouldn't change: }
- oldpos: Point; {Used by RunSAT2}
- next, prev: SpritePtr; {You may change them in your own sorting routine, but be careful if you do.}
- r, oldr: Rect; {Rectangle telling where to draw. Avoid messing with it.}
- oldFace: FacePtr; {Used by RunSAT2}
- dirty: Boolean; {Used by RunSAT2}
- {Variables for internal use by the sprites. Use as you please. Edit as necessary - this is merely a default}
- {set, enough space for most cases - but if you change the size of the record, call SetSpriteSize immediately}
- {after initializing (before any sprites are created)!}
- layer: integer; {For layer-sorting. When not used for that, use freely.}
- gridPos: Point; { Position in grid coordinate. }
- mode: integer; { Usually used for different modes and/or to determine what image to show next. }
- whatever: Longint; {free for any use}
- direction, partMove: SignedByte;
- appInt: Integer; {free for any use.}
- end;
-
- procedure InitStrictGrid; {Init tables}
-
- {Movement and collisions}
- function GridMoveSprite (theSprite: GridSpritePtr; howFar: Integer): Boolean; {Returns true if the sprite can change direction}
- procedure GridTurnAroundSprite (theSprite: GridSpritePtr); {Reverse direction}
- procedure GridRectBounce (theSprite: GridSpritePtr; anotherSprite: GridSpritePtr; push: Integer);
- function KeepInGrid (theSprite: GridSpritePtr): Boolean;
-
- {Utilities for inspecting the grid}
- function GridPtInGrid (p: Point): Boolean; {Is a grid point blocked or out of bounds?}
- function PossibleMove (sp: GridSpritePtr; direction: Integer): Boolean; {Can a sprite move in a specified direction?}
- function GetGridStep (fromP: Point; direction: Integer; distance: Integer): Point; {Get a grid point in a specified direction ad distance}
-
- const
- kUp = 0;
- kLeft = 1;
- kDown = 2;
- kRight = 3;
-
- kSnapDist = 2; {How far from a junction can a sprite freely change direction if pushed?}
-
- var
- dirTable: array[0..3] of Point; {One step in each direction}
- sizeTable: array[0..3] of Integer; {Size of grid spaces in each direction}
-
- implementation
-
- function MakePoint (h, v: Integer): Point;
- begin
- MakePoint.h := h;
- MakePoint.v := v;
- end; {MakePoint}
-
- function GetGridStep (fromP: Point; direction: Integer; distance: Integer): Point;
- begin
- GetGridStep.h := fromP.h + dirTable[direction].h * distance;
- GetGridStep.v := fromP.v + dirTable[direction].v * distance;
- end; {GetGridStep}
-
-
- procedure InitStrictGrid;
- begin
- dirTable[kRight] := MakePoint(1, 0);
- dirTable[kUp] := MakePoint(0, -1);
- dirTable[kLeft] := MakePoint(-1, 0);
- dirTable[kDown] := MakePoint(0, 1);
-
- sizeTable[kRight] := kTileSizeH;
- sizeTable[kLeft] := kTileSizeH;
- sizeTable[kUp] := kTileSizeV;
- sizeTable[kDown] := kTileSizeV;
- end; {InitStrictGrid}
-
- {Borde till stubs-funktion.}
- {Kan man fixa vridna, pseudo-3D-grids med denna?}
- function Sprite2Point (theSprite: GridSpritePtr): Point;
- begin
- case theSprite^.direction of
- kUp:
- begin
- Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH;
- Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV - theSprite^.partMove;
- end;
- kRight:
- begin
- Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH + theSprite^.partMove;
- Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV;
- end;
- kDown:
- begin
- Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH;
- Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV + theSprite^.partMove;
- end;
- kLeft:
- begin
- Sprite2Point.h := theSprite^.gridPos.h * kTileSizeH - theSprite^.partMove;
- Sprite2Point.v := theSprite^.gridPos.v * kTileSizeV;
- end;
- end; {case}
- end; {Sprite2Point}
-
- function GridPtInGrid (p: Point): Boolean;
- begin
- GridPtInGrid := tileArray[p.h][p.v] > kFreeSpace;
-
- if p.h < 0 then
- GridPtInGrid := true
- else if p.v < 0 then
- GridPtInGrid := true
- else if p.h > kArraySizeH then
- GridPtInGrid := true
- else if p.v > kArraySizeV then
- GridPtInGrid := true;
- end; (*GridPtInGrid*)
-
- function PossibleMove (sp: GridSpritePtr; direction: Integer): Boolean;
- begin
- PossibleMove := not GridPtInGrid(GetGridStep(sp^.gridPos, direction, 1));
- end;
-
-
- function GridMoveSprite (theSprite: GridSpritePtr; howFar: Integer): Boolean;
- var
- spacing: Integer;
- nextP: Point;
- begin
- GridMoveSprite := false;
-
- nextP.h := theSprite^.gridpos.h + dirTable[theSprite^.direction].h;
- nextP.v := theSprite^.gridpos.v + dirTable[theSprite^.direction].v;
- if GridPtInGrid(nextP) then
- begin {blocked}
- theSprite^.partMove := 0;
- GridMoveSprite := true;
- end
- else
- begin {not blocked}
-
- spacing := sizeTable[theSprite^.direction];
- if false then
- case theSprite^.direction of
- kUp, kDown:
- spacing := kTileSizeV;
- kLeft, kRight:
- spacing := kTileSizeH;
- end; {case}
-
- if howFar >= spacing - theSprite^.partMove then
- begin
- theSprite^.partMove := 0;
- theSprite^.gridpos.h := theSprite^.gridpos.h + dirTable[theSprite^.direction].h;
- theSprite^.gridpos.v := theSprite^.gridpos.v + dirTable[theSprite^.direction].v;
- GridMoveSprite := true;
- end
- else
- begin
- theSprite^.partMove := theSprite^.partMove + howFar;
- end;
- end; {not blocked}
-
- theSprite^.position := Sprite2Point(theSprite);
-
- end; {GridMoveSprite}
-
- procedure GridTurnAroundSprite (theSprite: GridSpritePtr); {Revere direction}
- begin
- theSprite^.gridpos.h := theSprite^.gridpos.h + dirTable[theSprite^.direction].h;
- theSprite^.gridpos.v := theSprite^.gridpos.v + dirTable[theSprite^.direction].v;
- theSprite^.direction := BitXor(theSprite^.direction, 2);
- theSprite^.partMove := sizeTable[theSprite^.direction] - theSprite^.partMove;
- end; {GridTurnAroundSprite}
-
- procedure GridRectBounce (theSprite: GridSpritePtr; anotherSprite: GridSpritePtr; push: Integer);
- type
- ArrRect = array[0..3] of Integer; {Index-able Rect!}
- var
- bounds1, bounds2: ArrRect;
- min, max1, max2: array[0..3] of Integer;
- shortest, shortestDistance, i: Integer;
- mov1, mov2: Integer;
- begin
- {bounds1 := ArrRect(theSprite^.hotRect);}
- {OffsetRect(bounds1, theSprite^.position); {or use hotRect2?}
- {bounds2 := ArrRect(anotherSprite^.hotRect);}
- {OffsetRect(bounds2, anotherSprite^.position); {or use hotRect2?}
-
- bounds1 := ArrRect(theSprite^.hotRect2);
- bounds2 := ArrRect(anotherSprite^.hotRect2);
-
- for i := 0 to 3 do
- begin
- min[i] := Abs(bounds1[(i + 2) mod 4] - bounds2[i]);
-
- {max for theSprite = max1}
- if push = kPushHim then
- max1[i] := 0
- else if BitAnd(i, 1) <> BitAnd(theSprite^.direction, 1) then {Not the direction it is moving in!}
- if (theSprite^.partMove > kSnapDist) and (sizeTable[theSprite^.direction] - theSprite^.partMove > kSnapDist) then
- max1[i] := 0
- else
- begin {wrong direction but allowed to push into this direction!}
- if GridPtInGrid(GetGridStep(theSprite^.gridPos, i, 1)) then
- max1[i] := 0 {blocked by the grid!}
- else
- max1[i] := sizeTable[i]; {Go on, push me!}
- end
- else
- begin
- if i = theSprite^.direction then
- max1[i] := sizeTable[theSprite^.direction] - theSprite^.partMove {How far left ahead?}
- else
- max1[i] := theSprite^.partMove; {How far back?}
-
- if not GridPtInGrid(GetGridStep(theSprite^.gridPos, i, 1)) then
- max1[i] := max1[i] + sizeTable[theSprite^.direction];
- end;
-
- {max for anotherSprite = max2}
- if push = kPushMe then
- max2[i] := 0
- else if BitAnd(i, 1) <> BitAnd(anotherSprite^.direction, 1) then {Not the direction it is moving in!}
- if (anotherSprite^.partMove > kSnapDist) and (sizeTable[anotherSprite^.direction] - anotherSprite^.partMove > kSnapDist) then
- max2[i] := 0
- else
- begin {wrong direction but allowed to push into this direction!}
- if GridPtInGrid(GetGridStep(anotherSprite^.gridPos, i, 1)) then
- max2[i] := 0 {blocked by the grid!}
- else
- max2[i] := sizeTable[i]; {Go on, push me!}
- end
- else
- begin
- if i = anotherSprite^.direction then
- max2[i] := sizeTable[anotherSprite^.direction] - anotherSprite^.partMove {How far left ahead?}
- else
- max2[i] := anotherSprite^.partMove; {How far back?}
-
- if not GridPtInGrid(GetGridStep(anotherSprite^.gridPos, i, 1)) then
- max2[i] := max2[i] + sizeTable[anotherSprite^.direction];
- end;
-
- end; {for}
-
- (*Find the shortest distance*)
- shortest := -1;
- shortestDistance := 9999;
- for i := 0 to 3 do
- if min[i] < max1[i] + max2[i] then
- if min[i] < shortestDistance then
- begin
- shortest := i;
- shortestDistance := min[i];
- end;
-
- {Do the push!}
- if i <> -1 then
- begin
- {Half the move for each!}
- mov1 := BSR(min[shortest], 1);
- mov2 := min[shortest] - mov1;
- {Is theSprite over its max?}
- if mov1 > max1[shortest] then
- begin
- mov2 := mov2 + (mov1 - max1[shortest]);
- mov1 := max1[shortest];
- end;
- {Is anotherSprite over its max?}
- if mov2 > max2[shortest] then
- begin
- mov1 := mov1 + (mov2 - max2[shortest]);
- mov2 := max2[shortest];
- end;
-
- {Push theSprite}
- if mov1 > 0 then
- if shortest = theSprite^.direction then
- begin
- theSprite^.partMove := theSprite^.partMove + mov1;
- end
- else if BitAnd(shortest, 1) = BitAnd(theSprite^.direction, 1) then
- begin
- GridTurnAroundSprite(theSprite);
- theSprite^.partMove := theSprite^.partMove + mov1;
- end
- else
- begin
- theSprite^.direction := shortest;
- theSprite^.partMove := mov1;
- end;
- if theSprite^.partMove > sizeTable[shortest] then
- begin
- theSprite^.partMove := theSprite^.partMove - sizeTable[shortest];
- theSprite^.gridPos := GetGridStep(theSprite^.gridPos, shortest, 1);
- end;
-
- {Reverse the direction "shortest" - to move anotherSprite in the other direction!}
- shortest := BitAnd(shortest + 2, 3);
-
- {Push anotherSprite}
- if mov2 > 0 then
- if shortest = anotherSprite^.direction then
- begin
- anotherSprite^.partMove := anotherSprite^.partMove + mov2;
- end
- else if BitAnd(shortest, 1) = BitAnd(anotherSprite^.direction, 1) then
- begin
- GridTurnAroundSprite(anotherSprite);
- anotherSprite^.partMove := anotherSprite^.partMove + mov2;
- end
- else
- begin
- anotherSprite^.direction := shortest;
- anotherSprite^.partMove := mov2;
- end;
- if anotherSprite^.partMove > sizeTable[shortest] then
- begin
- anotherSprite^.partMove := anotherSprite^.partMove - sizeTable[shortest];
- anotherSprite^.gridPos := GetGridStep(anotherSprite^.gridPos, shortest, 1);
- end;
-
- theSprite^.position := Sprite2Point(theSprite);
- anotherSprite^.position := Sprite2Point(anotherSprite);
- end; {Push possible!}
-
- end; {GridRectBounce}
-
- {KeepInGrid is unnecessary as long as there is a border with blocked tiles.}
- function KeepInGrid (theSprite: GridSpritePtr): Boolean;
- var
- nextP: Point;
- begin
- KeepInGrid := false;
- nextP := GetGridStep(theSprite^.gridPos, theSprite^.direction, 1);
- if nextP.h < 0 then
- begin
- theSprite^.partMove := 0;
- theSprite^.direction := kRight;
- KeepInGrid := true;
- end;
- if nextP.v < 0 then
- begin
- theSprite^.partMove := 0;
- theSprite^.direction := kDown;
- KeepInGrid := true;
- end;
- if nextP.h > kArraySizeH then
- begin
- theSprite^.partMove := 0;
- theSprite^.direction := kLeft;
- KeepInGrid := true;
- end;
- if nextP.v > kArraySizeV then
- begin
- theSprite^.partMove := 0;
- theSprite^.direction := kUp;
- KeepInGrid := true;
- end;
-
- {The following shouldn't happen}
- if theSprite^.gridPos.h < 0 then
- theSprite^.gridPos.h := 0;
- if theSprite^.gridPos.v < 0 then
- theSprite^.gridPos.v := 0;
- if theSprite^.gridPos.h > kArraySizeH then
- theSprite^.gridPos.h := kArraySizeH;
- if theSprite^.gridPos.v > kArraySizeV then
- theSprite^.gridPos.v := kArraySizeV;
- end; {KeepInGrid}
-
- end.