From 865b46699503d210c9265810aeba3dbf774b38fc Mon Sep 17 00:00:00 2001 From: gered Date: Sat, 2 Nov 2019 13:17:24 -0400 Subject: [PATCH] initial commit sources taken from book CD: ack_lib -> /ACK/WIN/ACK_LIB fdemo -> /ACK/DOS/FDEMO/SOURCE (and /ACK/DOS/BORLAND as needed) mall -> /ACK/DOS/MALL/SOURCE (and /ACK/DOS/BORLAND as needed) some source files were missing for the demo projects and needed to be copied from /ACK/DOS/BORLAND (as indicated above) --- .gitignore | 5 + ack_lib/ACK3D.H | 436 ++++++++ ack_lib/ACKBKGD.C | 42 + ack_lib/ACKDATA.C | 150 +++ ack_lib/ACKDISP.C | 34 + ack_lib/ACKDOOR.C | 391 ++++++++ ack_lib/ACKENG.H | 66 ++ ack_lib/ACKEXT.H | 119 +++ ack_lib/ACKFLOOR.C | 1239 +++++++++++++++++++++++ ack_lib/ACKGIF.C | 30 + ack_lib/ACKIFF.C | 231 +++++ ack_lib/ACKINIT.C | 542 ++++++++++ ack_lib/ACKLDBMP.C | 327 ++++++ ack_lib/ACKOBJ.C | 470 +++++++++ ack_lib/ACKOVER.C | 73 ++ ack_lib/ACKPCX.C | 162 +++ ack_lib/ACKPOV.C | 671 +++++++++++++ ack_lib/ACKRAY.C | 630 ++++++++++++ ack_lib/ACKRTN.ASM | 989 ++++++++++++++++++ ack_lib/ACKRTN.INC | 130 +++ ack_lib/ACKRTN1.ASM | 571 +++++++++++ ack_lib/ACKRTN2.ASM | 805 +++++++++++++++ ack_lib/ACKRTN3.ASM | 830 ++++++++++++++++ ack_lib/ACKRTN4.ASM | 412 ++++++++ ack_lib/ACKRTN5.ASM | 552 +++++++++++ ack_lib/ACKSND.C | 348 +++++++ ack_lib/ACKSND.H | 35 + ack_lib/ACKUTIL.C | 296 ++++++ ack_lib/ACKVIEW.C | 851 ++++++++++++++++ ack_lib/ACKWRAP.C | 79 ++ ack_lib/IFF.H | 98 ++ ack_lib/KIT.H | 140 +++ ack_lib/WING.H | 78 ++ ack_lib/WINGDLL.H | 60 ++ fdemo/FDEMO.C | 2310 +++++++++++++++++++++++++++++++++++++++++++ fdemo/KIT.H | 140 +++ fdemo/KIT.OVL | Bin 0 -> 62924 bytes fdemo/MODPLAY.ASM | 1166 ++++++++++++++++++++++ fdemo/MODPLAY.H | 162 +++ fdemo/MOUSE.C | 181 ++++ fdemo/PICS.DTF | Bin 0 -> 1250885 bytes fdemo/SAHARA.MOD | Bin 0 -> 74052 bytes mall/KIT.H | 140 +++ mall/KIT.OVL | Bin 0 -> 62924 bytes mall/MALL.C | 2303 ++++++++++++++++++++++++++++++++++++++++++ mall/MODPLAY.ASM | 1166 ++++++++++++++++++++++ mall/MODPLAY.H | 162 +++ mall/MOUSE.C | 181 ++++ mall/PICS.DTF | Bin 0 -> 580372 bytes mall/WALK.MOD | Bin 0 -> 188062 bytes 50 files changed, 19803 insertions(+) create mode 100644 .gitignore create mode 100644 ack_lib/ACK3D.H create mode 100644 ack_lib/ACKBKGD.C create mode 100644 ack_lib/ACKDATA.C create mode 100644 ack_lib/ACKDISP.C create mode 100644 ack_lib/ACKDOOR.C create mode 100644 ack_lib/ACKENG.H create mode 100644 ack_lib/ACKEXT.H create mode 100644 ack_lib/ACKFLOOR.C create mode 100644 ack_lib/ACKGIF.C create mode 100644 ack_lib/ACKIFF.C create mode 100644 ack_lib/ACKINIT.C create mode 100644 ack_lib/ACKLDBMP.C create mode 100644 ack_lib/ACKOBJ.C create mode 100644 ack_lib/ACKOVER.C create mode 100644 ack_lib/ACKPCX.C create mode 100644 ack_lib/ACKPOV.C create mode 100644 ack_lib/ACKRAY.C create mode 100644 ack_lib/ACKRTN.ASM create mode 100644 ack_lib/ACKRTN.INC create mode 100644 ack_lib/ACKRTN1.ASM create mode 100644 ack_lib/ACKRTN2.ASM create mode 100644 ack_lib/ACKRTN3.ASM create mode 100644 ack_lib/ACKRTN4.ASM create mode 100644 ack_lib/ACKRTN5.ASM create mode 100644 ack_lib/ACKSND.C create mode 100644 ack_lib/ACKSND.H create mode 100644 ack_lib/ACKUTIL.C create mode 100644 ack_lib/ACKVIEW.C create mode 100644 ack_lib/ACKWRAP.C create mode 100644 ack_lib/IFF.H create mode 100644 ack_lib/KIT.H create mode 100644 ack_lib/WING.H create mode 100644 ack_lib/WINGDLL.H create mode 100644 fdemo/FDEMO.C create mode 100644 fdemo/KIT.H create mode 100644 fdemo/KIT.OVL create mode 100644 fdemo/MODPLAY.ASM create mode 100644 fdemo/MODPLAY.H create mode 100644 fdemo/MOUSE.C create mode 100644 fdemo/PICS.DTF create mode 100644 fdemo/SAHARA.MOD create mode 100644 mall/KIT.H create mode 100644 mall/KIT.OVL create mode 100644 mall/MALL.C create mode 100644 mall/MODPLAY.ASM create mode 100644 mall/MODPLAY.H create mode 100644 mall/MOUSE.C create mode 100644 mall/PICS.DTF create mode 100644 mall/WALK.MOD diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02827c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.obj +*.exe +*.lib +*.lbc +*.lnk diff --git a/ack_lib/ACK3D.H b/ack_lib/ACK3D.H new file mode 100644 index 0000000..090f069 --- /dev/null +++ b/ack_lib/ACK3D.H @@ -0,0 +1,436 @@ +// ACK-3D.H header file for the engine interface. +// This file contains the main data structures and definitions +// required to support the ACK-3D engine. The data structures defined are +// the ones you use to set up the communication links between your application +// and the ACK-3D library. The four main data structures set up in this header file +// include: ACKENG, DOORS, NEWOBJECT, and OBJSEQ. + +// USED TO RID OURSELVES OF THE MANY CASTING PROBLEMS +#define CAST(t,f) (t)(f) + +// Internal definitions used to simplify field declarations. +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char UCHAR; + +// Error codes returned from ACK-3D functions. You can use these error codes in your +// applications to debug the function calls. +#define ERR_BADFILE 100 // File not found, usually bad filename +#define ERR_BADCOMMAND 101 // Bad command found in the .INF file +#define ERR_BADOBJNUMBER 102 // Object number out of range +#define ERR_BADSYNTAX 103 // Invalid syntax in the .INF file +#define ERR_LOADINGBITMAP 104 // Error while reading a Bitmap file +#define ERR_BADDIRECTION 105 // Obsolete, was error in direction field of objects +#define ERR_BADSTARTX 106 // Error in initial X position of player +#define ERR_BADSTARTY 107 // Error in initial Y position of player +#define ERR_BADANGLE 108 // Error in initial player angle +#define ERR_BADMAPFILE 109 // Error in map file header +#define ERR_READINGMAP 110 // Error during read of map file +#define ERR_BADPICNAME 111 // Error in bitmap filename +#define ERR_INVALIDFORM 112 // Error in .BBM or .LBM format +#define ERR_NOPBM 113 // Error in .BBM format +#define ERR_BADPICFILE 114 // Error reading a bitmap file +#define ERR_NOMEMORY 115 // Out of memory error +#define ERR_BADPALFILE 116 // Error reading raw palette file +#define ERR_BADWINDOWSIZE 117 // Invalid window size for engine +#define ERR_TOMANYVIEWS 118 // Out of range object views +#define ERR_BADOBJECTNUM 119 // Invalid object index +#define ERR_BADOBJTYPE 120 // Invalid object type + +// Defines for angle sizes used with the ACK-3D engine. +// These values are used as table look up indexes. The range is from 0 to 1800 +// where each value represents and angle increment of 0.5 degrees; +// 0 = 0 degrees and 1800 = 360 degrees. +#define INT_ANGLE_1 5 +#define INT_ANGLE_2 10 +#define INT_ANGLE_4 20 +#define INT_ANGLE_6 30 +#define INT_ANGLE_32 160 +#define INT_ANGLE_45 225 +#define INT_ANGLE_90 450 +#define INT_ANGLE_135 675 +#define INT_ANGLE_180 900 +#define INT_ANGLE_225 1125 +#define INT_ANGLE_270 1350 +#define INT_ANGLE_315 1575 +#define INT_ANGLE_360 1800 + +// These values are returned by the AckMovePOV() and AckMoveObjectPOV() functions. +#define POV_NOTHING 0 // Nothing was hit +#define POV_XWALL 1 // An X wall was hit +#define POV_YWALL 2 // A Y wall was hit +#define POV_OBJECT 3 // An object was hit +#define POV_PLAYER 4 // The player was hit by object +#define POV_SLIDEX 5 // Sliding along an X wall +#define POV_SLIDEY 6 // Sliding along a Y wall +#define POV_NODOOR 0 // No door was opened +#define POV_XDOOR 1 // An X door was opened +#define POV_YDOOR 2 // A Y door was opened +#define POV_XSECRETDOOR 3 // An X secret door was opened +#define POV_YSECRETDOOR 4 // A Y secret door was opened +#define POV_DOORLOCKED 0x80 // Bit is on if door is locked + +// Defines required to support bitmaps. +// Each bitmap used in ACK-3D is 64x64 pixels in size. +#define BITMAP_WIDTH 64 +#define BITMAP_HEIGHT 64 +#define BITMAP_SHIFT 6 // Bits to shift for bitmap width +#define BITMAP_SIZE (BITMAP_WIDTH * BITMAP_HEIGHT) + +// Defines used to set up map grid. The grid is used with the ACKENG structure. +// Each grid component is 64x64 units in size. +#define GRID_MASK 0xFFC0 // Use FF80 for 128 and FFC0 for 64 +#define GRID_SIZE 64 // The size of an individual grid unit +#define GRID_WIDTH 64 +#define GRID_HEIGHT 64 +#define GRID_MAX GRID_WIDTH * GRID_HEIGHT +#define GRID_XMAX BITMAP_WIDTH * GRID_WIDTH +#define GRID_YMAX BITMAP_WIDTH * GRID_HEIGHT +#define GRID_XMAXLONG (GRID_XMAX * FP_MULT) +#define GRID_YMAXLONG (GRID_YMAX * FP_MULT) +// The main grid array used with the ACKENG structure +#define GRID_ARRAY (GRID_WIDTH+2) * (GRID_HEIGHT+2) + +// Defines to specify maximum sizes for the various components used with +// ACK-3D, including wall bitmaps, light zones, shading palette, views, multi- +// height walls, doors, object bitmaps, and objects. +#define MAX_WALLBMPS 256 // Total wall bitmaps allowed +#define MAX_ZONES 8 // Number of light zones +#define PAL_SIZE 4096 // Shading palette ranges +#define MAX_VIEWS 47 // Total sides to an object +#define MAX_MULTI 3 // Max height for multi-height walls +#define MAX_DOORS 10 // Max number of doors that can be opened or +// // closed at one time +#define MAX_OBJBMPS 256 // Total object bitmaps allowed +#define MAX_OBJECTS 254 // Total objects allowed in map + +// Defines to set up different wall types. +#define WALL_TYPE_TRANS 0x0800 // Transparent wall +#define WALL_TYPE_MULTI 0x0400 // Wall is 1.5 times high +#define WALL_TYPE_UPPER 0x0200 // Wall is above floor level +#define WALL_TYPE_PASS 0x0100 // Wall can be walked through + +#define TYPE_WALL 0 +#define TYPE_OBJECT 1 + +#define RES_LOW 1 // Resolution is low for walls, floor, and ceiling +#define RES_MEDIUM 2 // Resolution is low only for floor and ceiling + +//*************************************************************** +// Definitions and data structures used to represent and process objects. + +// These defines specify the two settings for the Active field in the NEWOBJECT structure. +#define OBJECT_ACTIVE 1 // Active and may be moveable +#define OBJECT_INACTIVE 0 // Won't be considered moveable + +// These defines specify values for the Flags field in the NEWOBJECT structure. +// They are used to indicate the type of animation available for an object. +#define OF_PASSABLE 0x80 // Object can be walked thru +#define OF_ANIMATE 0x40 // Object bitmaps are animated +#define OF_MOVEABLE 0x20 // Object will move X,Y +#define OF_MULTIVIEW 0x10 // Object has multiple sides +#define OF_ANIMONCE 0x08 // Animate once then stop +#define OF_ANIMDONE 0x04 // One shot animation is completed + +// These defines specify values for the CurrentType field in the NEWOBJECT structure. +// They are used to indicate the current status of the object. +// The names given are arbitrary and can be used for a variety of purposes. +#define NO_CREATE 1 +#define NO_DESTROY 2 +#define NO_WALK 3 +#define NO_ATTACK 4 +#define NO_INTERACT 5 + +// The Object Sequence structure. This structure is used by the NEWOBJECT structure. +#define MAX_OBJ_BITMAPS 32 // Max bitmaps per sequence type +typedef struct { + UCHAR flags; // Flags for this sequence + UCHAR bitmaps[MAX_OBJ_BITMAPS]; // Bitmap numbers in this sequence + short bmSides; // Number of views in sequence + short bmBitmapsPerView; // Number of bitmaps in each view + short AngleFactor; // Used internally to support the object sequence + UCHAR MaxBitmaps; // Max bitmaps in this sequence + } OBJSEQ; + +// The defininition of the main object structure--NEWOBJECT. +typedef struct { + char Active; // Determines object status: 0=Inactive, 1=Active + UCHAR Flags; // Misc flags for this object + char Speed; // Speed of obj (used by application) + short Dir; // Direction of obj (used by application) + short x; // Current x,y location in grid + short y; + short mPos; // Current map location in grid + UCHAR id; // Object id + short CurrentType; // Create, Destroy, etc. is current + UCHAR *CurrentBitmaps; // Current bitmap list + short Sides; // Number of views + short aFactor; // Angle factor + short BitmapsPerView; // Number of bitmaps in each view + UCHAR CurrentBm; // Current bitmap number + UCHAR Maxbm; // Max bitmap number for this view + OBJSEQ Create; // Stores structures for the 5 object sequences + OBJSEQ Destroy; + OBJSEQ Walk; + OBJSEQ Attack; + OBJSEQ Interact; + } NEWOBJECT; + +//************************************************************************** +// The definitions and data structure that are used to represent doors. + +// The default value for the Speed field in the DOORS structure. +#define DEFAULT_DOOR_SPEED 2 +// These defines are used to set the Type field. +// Obsolete door codes since any wall can be a door +#define DOOR_XCODE 60 // Map codes for the various doors +#define DOOR_SIDECODE 61 +#define DOOR_YCODE 62 + +// These two defines are used to set the Flags field in the DOORS structure. +#define DOOR_OPENING 0x80 // Set if door is currently opening +#define DOOR_CLOSING 0x40 // Set if door is currently closing +// Other attributes that can be assigned to the Flags field. +#define DOOR_TYPE_SECRET 0x8000 // Secret door +#define DOOR_LOCKED 0x4000 // Locked door +#define DOOR_TYPE_SLIDE 0x2000 // Sliding door +#define DOOR_TYPE_SPLIT 0x1000 // Split door + +// The main DOORS structure. +typedef struct { + short mPos; // Stores position info for a door + short mPos1; + short mCode; // Bitmap ID of the door + short mCode1; + UCHAR ColOffset; // Column offset for the door + char Speed; // Speed setting for opening and closing the door + char Type; // Code for the door type + UCHAR Flags; // Door attribute settings +} DOORS; + +//*********************************************************************** +// The defines and data structure for the main interface structure--ACKENG. + +// These defines are used for the LightFlag field in the ACKENG structure. +#define SHADING_ON 1 // Set if distance shading is on +#define SHADING_OFF 0 + +// These defines are used for the SysFlags field in the ACKENG structure. +#define SYS_SOLID_BACK 0x8000 // On if solid color bkgd vs picture +#define SYS_SOLID_FLOOR 0x4000 // On if solid vs texture floor +#define SYS_SOLID_CEIL 0x2000 // On if solid vs texture ceiling +#define SYS_NO_WALLS 0x1000 // On if walls are NOT to display +#define SYS_SINGLE_BMP 0x0800 // On if 1 bitmap for floor & ceiling + +// These defines indicate how bitmaps will be loaded. they are used with the bmLoadType field. +#define BMLOAD_BBM 0 // Bitmaps will be loaded using BBM format +#define BMLOAD_GIF 1 // Bitmaps will be loaded using GIF format +#define BMLOAD_PCX 2 // Bitmaps will be loaded using PCX format + +// The main interface structure used between the application and the ACK-3D engine. +// This structure MUST be allocated or defined before any ACK-3D calls are made. +typedef struct { + USHORT xGrid[GRID_ARRAY]; // Map for X walls + USHORT yGrid[GRID_ARRAY]; // Map for Y walls + UCHAR *mxGrid[GRID_ARRAY]; // Wall data for multi-height X walls + UCHAR *myGrid[GRID_ARRAY]; // Wall data for multi-height Y walls + UCHAR *bMaps[MAX_WALLBMPS]; // Pointers to wall bitmaps + UCHAR *oMaps[MAX_OBJBMPS]; // Pointers to object bitmaps + + UCHAR *ScreenBuffer; // 64k buffer for screen + UCHAR *OverlayBuffer; // Buffer for compiled overlay + UCHAR *BkgdBuffer; // Buffer for ceiling, floor + + short xPlayer; // X value from 0 to 4095--stores current position + short yPlayer; // Y value from 0 to 4095--stores current position + short PlayerAngle; // Angle value from 0 to 1799 + + short DoorSpeed; // Door open/close speed + short NonSecretCode; // Wall code for secret door + + UCHAR TopColor; // Base color of ceiling + UCHAR BottomColor; // Base color of floor + UCHAR FloorBitmap; // Bitmap number for single floor + UCHAR CeilBitmap; // Bitmap number for single ceiling + + UCHAR LightFlag; // 0 = no light shading, 1 = ON + UCHAR PalTable[PAL_SIZE]; // 16 zones of 256 colors each + + short WinStartX; // Value of left side of viewport + short WinStartY; // Value of top side of viewport + short WinEndX; // Value of right side + short WinEndY; // Value of bottom side + short CenterRow; // Value of (WinEndY-WinStartY)/2 + short CenterOffset; // Center row times bytes per row + short WinWidth; // Value of WinEndX - WinStartX + short WinHeight; // Value of WinEndY - WinStartY + USHORT WinLength; // Number of dwords in window + USHORT WinStartOffset; // Value of WinStartY * 320 + USHORT SysFlags; // General system flags--determines display attributes + UCHAR bmLoadType; // Bitmap load flags (BBM, GIF, PCX, etc) + short MaxObjects; // Total number of objects in map + NEWOBJECT *ObjList[MAX_OBJECTS+1]; // Current objects in map + DOORS Door[MAX_DOORS]; // Doors moving at one time +} ACKENG; + +// Structure used to build the palette ranges for light shading +// There are 16 color ranges (or zones) each containing 256 colors. +typedef struct { + unsigned char start; // Starting color for this range + unsigned char length; // Length of range +} ColorRange; + +//************************************************************************* +// Function prototypes for the user callable ACK-3D functions. +//************************************************************************* +// The user callable functions defined in ACKINIT.C. +//************************************************************************* +// Reads trig files, builds wall and object maps, and performs the general +// initialization tasks. This function also sets up the distance table used by ACK-3D. +short AckInitialize(ACKENG *ae); + +// Opens a resource file and prepares for reading. +short AckOpenResource(char *ResFileName); + +// Closes the currently open resource file. +void AckCloseResource(void); + +// Reads the map file and sets up the map grids. +short AckReadMapFile(ACKENG *ae,char *MapFileName); + +//************************************************************************* +// The user callable functions defined in ACKLDBMP.C. +//************************************************************************* +// Loads a bitmap and places it in either a wall bitmap array or an object bitmap array. +short AckLoadBitmap(ACKENG *ae,short BitmapNumber,short BitmapType,char *bmFileName); + +// Loads a wall bitmap and places it into the wall array. +short AckLoadWall(ACKENG *ae,short WallNumber,char *bmFileName); + +// Loads an object bitmap and places it into the object array. +short AckLoadObject(ACKENG *ae,short BmpNumber,char *bmFileName); + +// Fills in an ObjList structure with the information passed in. +short AckCreateObject(ACKENG *ae,short ObjNumber); + +// Sets up an object into one of the pre-defined sequences (CREATE, DESTRY, and so on). +short AckSetObjectType(ACKENG *ae,short oNum,short oType); + +// Fills in an object structure with a communication structure passed by the application. +short AckSetupObject(ACKENG *ae,short oNum,short oType,OBJSEQ *os); + +//************************************************************************* +// The user callable functions defined in ACKIFF.C, ACKGIF.C, and ACKPCX.C +//************************************************************************* +// Reads in a .LBM or .BBM file and returns a buffer. +UCHAR *AckReadiff(char *FileName); + +// Reads a 256 color GIF file and returns a buffer. +UCHAR *AckReadgif(char *FileName); + +// Reads in a 256 color PCX file and returns a buffer. +UCHAR *AckReadPCX(char *filename); + +//************************************************************************* +// The user callable functions defined in ACKVIEW.C. +//************************************************************************* +// Assigns the current engine structure to various global variables. +// This function MUST be called before AckBuildView() is called. +void AckRegisterStructure(ACKENG *ae); + +// Draws the current view into ScreenBuffer. +void AckBuildView(void); + +// Returns the angle (0-1919) between two objects. +short AckGetObjectAngle(long DeltaX,long DeltaY); + +// Checks if a collision occurs. Used by AckCheckDoorOpen routine. +short AckCheckHit(short xPlayer,short yPlayer,short ViewAngle); + + +//************************************************************************* +// The user callable functions defined in ACKPOV.C. +//************************************************************************* +// Used by AckMovePOV() and AckMoveObjectPOV() to check collision with objects. +short AckCheckObjPosn(short xPlayer,short yPlayer,short oIndex); + +// Runs the list of objects to check movement. +void AckCheckObjectMovement(void); + +// Moves the POV by the specified amount at the specified angle. +short AckMovePOV(short Angle,short Amount); + +// Moves the object POV by the specified amount at the specified angle. +short AckMoveObjectPOV(short ObjIndex,short Angle,short Amount); + +//************************************************************************* +// The user callable function defined in ACKOVER.C. +//************************************************************************* +// Reads overlay file and compiles it into the OverlayBuffer. +short AckCreateOverlay(ACKENG *ae, UCHAR *OverlayScreen); + +//************************************************************************* +// The user callable function defined in ACKBKGD.C. +//************************************************************************* +// Builds background buffer from TopColor, BottomColor, and LightFlag. +short AckBuildBackground(ACKENG *ae); + +//************************************************************************* +// The user callable function defined in ACKDOOR.C. +//************************************************************************* +// Check if a door in front to open +short AckCheckDoorOpen(short xPlayer,short yPlayer,short PlayerAngle); + +//************************************************************************* +// The user callable function defined in ACKWRAP.C. +//************************************************************************* +// Frees memory buffers. +short AckWrapUp(ACKENG *ae); + +//************************************************************************* +// The user callable functions defined in ACKUTIL.C. +//************************************************************************* +// Internal memory allocation for development purposes +void *AckMalloc(size_t mSize); + +// All memory allocated with AckMalloc() must use AckFree() to free memory. +void AckFree(void *m); + +// Reads in a palette file and sets the screen palette. +short AckLoadAndSetPalette(char *FileName); + +// Fills in the shading palette table. +void AckSetupPalRanges(ACKENG *ae,ColorRange *ranges); + +// Returns the object index number of the last object hit. +short AckGetObjectHit(void); + +// Returns the map location of the last wall hit. +short AckGetWallHit(void); + +// Removes the specified object from the map. +short AckDeleteObject(ACKENG *ae,short ObjectIndex); + +// Sets a new bitmap or changes image in the bitmap array. +short AckSetNewBitmapPtr(short BitmapNumber,UCHAR **Maps,UCHAR *NewBitmap); + +// Frees up the memory used by the bitmap. +short AckFreeBitmap(UCHAR *bmType); + +//************************************************************************* +// The user callable functions defined in ACKRTN.ASM. +//************************************************************************* +// Sets a previously loaded palette. +void AckSetPalette(UCHAR *PalBuffer); + +// Places video in standard 320x200 mode 13h. +void AckSetVGAmode(void); + +// Places video in 80x25 color text mode 3. +void AckSetTextmode(void); + +// Displays the contents of ScreenBuffer and OverlayBuffer if desired. +short AckDisplayScreen(void); + + + \ No newline at end of file diff --git a/ack_lib/ACKBKGD.C b/ack_lib/ACKBKGD.C new file mode 100644 index 0000000..f8f7589 --- /dev/null +++ b/ack_lib/ACKBKGD.C @@ -0,0 +1,42 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//typedef unsigned short USHORT; + +#include "ack3d.h" +#include "ackeng.h" + + +void AckBuildCeilingFloor (UCHAR *, short, short, short, short, short, short); + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Generates a solid floor and ceiling +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckBuildBackground (ACKENG * ae) +{ + +/* Let the assembly routine do all the hard work */ + +#if FLOOR_ACTIVE +#else + AckBuildCeilingFloor (ae->BkgdBuffer, + ae->LightFlag, + ae->TopColor, + ae->BottomColor, + ae->WinStartY, + ae->WinEndY, + ae->CenterRow); +#endif + + return (0); +} + \ No newline at end of file diff --git a/ack_lib/ACKDATA.C b/ack_lib/ACKDATA.C new file mode 100644 index 0000000..21a4c00 --- /dev/null +++ b/ack_lib/ACKDATA.C @@ -0,0 +1,150 @@ +#include +#include + +#include "ack3d.h" +#include "ackeng.h" + +long scPtr; +UCHAR *bmWall; + +long bmDistance; +long BackDropRows; +USHORT ScreenOffset; + +long xPglobal; +long yPglobal; +long xBegGlobal; +long yBegGlobal; + +ACKENG *aeGlobal; +USHORT *xGridGlobal; +USHORT *yGridGlobal; +long xPglobalHI; +long yPglobalHI; +ULONG *rbaTable; +short rsHandle; + +long LastX1; +long LastY1; +long iLastX; +long iLastY; + +long x_xPos; +long x_yPos; +long x_xNext; +long x_yNext; +long y_xPos; +long y_yPos; +long y_xNext; +long y_yNext; + +short MaxDistance; +short LightFlag; +short ErrorCode; + +long xMapPosn; +long yMapPosn; + +USHORT *Grid; +USHORT *ObjGrid; +SLICE Slice[VIEW_WIDTH]; +SLICE *sPtr; + +short TotalSpecial; + +short DistanceTable[MAX_DISTANCE + 1]; +long *AdjustTable; + +short xSecretmPos; +short xSecretmPos1; +short xSecretColumn; + +short ySecretmPos; +short ySecretmPos1; +short ySecretColumn; + +short TotalSecret; +short ViewColumn; + +long *SinTable; +long *CosTable; + +long *LongTanTable; +long *LongInvTanTable; +long InvCosTable[INT_ANGLE_360]; +long InvSinTable[INT_ANGLE_360]; +long *LongCosTable; +long *ViewCosTable; + +long *xNextTable; +long *yNextTable; + +short LastFloorAngle = -1; +short LastFloorX; +short LastFloorY; +short LastMapPosn; +short LastObjectHit; +short TotalObjects; +short FoundObjectCount; +UCHAR ObjectsSeen[MAX_OBJECTS + 1]; +short MoveObjectCount; +UCHAR MoveObjectList[MAX_OBJECTS + 1]; +UCHAR ObjNumber[MAX_OBJECTS + 1]; +USHORT ObjRelDist[MAX_OBJECTS + 1]; +short ObjColumn[MAX_OBJECTS + 1]; +short ObjAngle[MAX_OBJECTS + 1]; +short DirAngle[] = +{INT_ANGLE_270, INT_ANGLE_315, 0, + INT_ANGLE_45, INT_ANGLE_90, + INT_ANGLE_135, INT_ANGLE_180, + INT_ANGLE_225}; + +UCHAR WorkPalette[768]; +UCHAR *BackArray[INT_ANGLE_360]; +short Resolution; + +long Flooru; +long Floorv; +long Floordu; +long Floordv; +long Floorkx; +long Floorky; +long Floorku; +long Floorkv; +long Floorkdu; +long Floorkdv; +UCHAR *Floorbm; +UCHAR *Floorscr; +UCHAR *FloorscrTop; +UCHAR *Floorptr2; +UCHAR *Floors1; +UCHAR *Floors2; +long Floorht; +long Floorwt; +short Floorvht; +short Flooreht; +long FloorLastbNum; +long FloorLastbm; + +short ViewHeight = 31; +short CeilingHeight = 31; +short LastWallHeight; +short PlayerAngle; +short ViewAngle; +USHORT SysFlags; +UCHAR **WallbMaps; +UCHAR *VidTop; +UCHAR *VidBottom; +short BotRowTable[320]; +USHORT FloorMap[4096]; +USHORT CeilMap[4096]; +UCHAR HitMap[4096]; + +UCHAR *VidSeg; +char *scantables[96]; +UCHAR AckKeys[128]; // Buffer for keystrokes +long AckTimerCounter; + +// **** End of Data **** + + \ No newline at end of file diff --git a/ack_lib/ACKDISP.C b/ack_lib/ACKDISP.C new file mode 100644 index 0000000..9293fea --- /dev/null +++ b/ack_lib/ACKDISP.C @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//typedef unsigned short USHORT; + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + + +void AckDrawPage (void); + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// This function has been replaced by AckDisplayScreen in assembler +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short xxxAckDisplayScreen (void) +{ + +/* Let the assembly routine do the hard work */ + AckDrawPage (); + + return (0); +} + + + \ No newline at end of file diff --git a/ack_lib/ACKDOOR.C b/ack_lib/ACKDOOR.C new file mode 100644 index 0000000..3b42c0d --- /dev/null +++ b/ack_lib/ACKDOOR.C @@ -0,0 +1,391 @@ +// This source file contains the functions needed to process doors. +// (c) 1995 ACK Software (Lary Myers) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +extern DOORS *gDoor; + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Check all the active doors to see what current state they are in. If a +// door's column offset is non-zero, it is either opening or closing, +// so the speed of the door is added in and the door is checked to +// see if it is fully opened or fully closed. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void CheckDoors (void) +{ + short i, MapPosn, mPos, mPos1; + short mx, my, DeltaGrid; + short xPlayer, yPlayer; + DOORS *dPtr; + +xPlayer = xPglobal; +yPlayer = yPglobal; +dPtr = &gDoor[0]; + +for (i = 0; i < MAX_DOORS; i++) + { + if (dPtr->ColOffset) // Door is not closed + { // Add speed to door column offset + dPtr->ColOffset += dPtr->Speed; + mPos = dPtr->mPos; // Get grid position of door + mPos1 = dPtr->mPos1; + + // Door is closing and is visible. Put old codes back to make non-passable + if (dPtr->Speed < 1 && dPtr->ColOffset < 65) + { // Get corner of grid + MapPosn = (yPlayer & 0xFFC0) + (xPlayer >> BITMAP_SHIFT); + // Is player in same grid square that the door is in? + if (MapPosn == mPos || MapPosn == mPos1) + { + dPtr->ColOffset -= dPtr->Speed; // Reduce column offset + continue; + } + // Is the door along an x wall + if (dPtr->Type == DOOR_XCODE) + { // Store bitmap ids for doors + xGridGlobal[mPos] = dPtr->mCode; + xGridGlobal[mPos1] = dPtr->mCode1; + } + else + { // Door is along a y wall + yGridGlobal[mPos] = dPtr->mCode; + yGridGlobal[mPos1] = dPtr->mCode1; + } + + // Door is close enough to fully closed to get rid + // of the door from the array + if (dPtr->ColOffset < 3) + { + dPtr->ColOffset = 0; // Close door + dPtr->mPos = -1; + dPtr->mPos1 = -1; + dPtr->Flags = 0; + } + } + // Door is fully opened--time to start closing it + if (dPtr->ColOffset > 0xA0) + { + dPtr->Speed = -dPtr->Speed; // Start to reduce speed + dPtr->Flags &= ~DOOR_OPENING; // Set flag to indicate door closing + dPtr->Flags |= DOOR_CLOSING; + } + } + dPtr++; // Advance to check next door + } + +//============================================================================= +// Now check for any action occuring in a secret door. This is currently +// setup to handle only one door at a time in the X and Y directions, but +// it should be fairly straightforward to use a list of doors, similiar +// to normal doors, to handle more than one. +//============================================================================= +if (xSecretColumn) + { + if (xSecretColumn > 0) // See if the door is to the right of us + { + mPos = xSecretmPos1; + DeltaGrid = -1; + xSecretColumn += aeGlobal->DoorSpeed; + } + else + { + mPos = xSecretmPos; + DeltaGrid = 0; + xSecretColumn -= aeGlobal->DoorSpeed; + } + + my = mPos & 0xFFC0; + mx = (mPos - my) << 6; + + if (abs (xSecretColumn) > BITMAP_WIDTH) // Beyond one grid square + { + mx += xSecretColumn; + my = my + (mx >> BITMAP_SHIFT); + if (xGridGlobal[my]) // No further, an obstruction + { + xGridGlobal[xSecretmPos] = 0; + xGridGlobal[xSecretmPos1] = 0; + my += DeltaGrid; + xGridGlobal[my] = aeGlobal->NonSecretCode; + xGridGlobal[my + 1] = aeGlobal->NonSecretCode; + yGridGlobal[my] = aeGlobal->NonSecretCode; + yGridGlobal[my + GRID_WIDTH] = aeGlobal->NonSecretCode; + xSecretColumn = 0; + } + else + { + if (my != mPos) + { + xGridGlobal[xSecretmPos] = 0; + xGridGlobal[xSecretmPos1] = 0; + if (xSecretColumn > 0) + { + xSecretColumn -= (BITMAP_WIDTH - 1); + xGridGlobal[my] = DOOR_TYPE_SECRET + 1; + xSecretmPos1 = my; + my--; + xGridGlobal[my] = DOOR_TYPE_SECRET + 1; + xSecretmPos = my; + } + else + { + xSecretColumn += (BITMAP_WIDTH - 1); + xGridGlobal[my] = DOOR_TYPE_SECRET + 1; + xGridGlobal[my + 1] = DOOR_TYPE_SECRET + 1; + xSecretmPos = my; + xSecretmPos = my + 1; + } + } + } // End if (xGrid[my]) ... else ... + } // End if (abs(xSecretColumn) > GRID_SIZE) + } // End if (xSecretColumn) + +//============================================================================= +// Perform same process on a secret door that may be moving in the Y +// direction. The same door can move either way, depending on which +// angle the player struck it at. +//============================================================================= +if (ySecretColumn) + { + if (ySecretColumn > 0) + { + mPos = ySecretmPos1; + DeltaGrid = -GRID_WIDTH; + ySecretColumn += aeGlobal->DoorSpeed; + } + else + { + mPos = ySecretmPos; + DeltaGrid = 0; + ySecretColumn -= aeGlobal->DoorSpeed; + } + + my = mPos & 0xFFC0; + mx = (mPos - my) << 6; + + if (abs (ySecretColumn) > BITMAP_WIDTH) + { + my += ySecretColumn; + my = (my & 0xFFC0) + (mx >> 6); + if (yGridGlobal[my]) + { + yGridGlobal[ySecretmPos] = 0; + yGridGlobal[ySecretmPos1] = 0; + my += DeltaGrid; + xGridGlobal[my] = aeGlobal->NonSecretCode; + xGridGlobal[my + 1] = aeGlobal->NonSecretCode; + yGridGlobal[my] = aeGlobal->NonSecretCode; + yGridGlobal[my + GRID_WIDTH] = aeGlobal->NonSecretCode; + ySecretColumn = 0; + } + else + { + if (my != mPos) + { + yGridGlobal[ySecretmPos] = 0; + yGridGlobal[ySecretmPos1] = 0; + if (ySecretColumn > 0) + { + ySecretColumn -= (BITMAP_WIDTH - 1); + yGridGlobal[my] = DOOR_TYPE_SECRET + 1; + ySecretmPos1 = my; + my -= GRID_WIDTH; + yGridGlobal[my] = DOOR_TYPE_SECRET + 1; + ySecretmPos = my; + } + else + { + ySecretColumn += (BITMAP_WIDTH - 1); + yGridGlobal[my] = DOOR_TYPE_SECRET + 1; + yGridGlobal[my + GRID_WIDTH] = DOOR_TYPE_SECRET + 1; + ySecretmPos = my; + ySecretmPos = my + GRID_WIDTH; + } + } + } + } + } + +} + + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal function called by FindDoorSlot(). This function +// locates a door from its map coordinate and return the index. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short FindDoor (short MapPosn) +{ + short index; + +for (index = 0; index < MAX_DOORS; index++) + { + if (MapPosn == gDoor[index].mPos || + MapPosn == gDoor[index].mPos1) + { + return (index); + } + } +return (-1); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal routine called by AckCheckDoorOpen() This function +// finds an empty slot for a door. If the door already occupies a slot +// and it is in a non-closed state, an error is returned. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short FindDoorSlot (short MapPosn) +{ + short index; + +index = FindDoor (MapPosn); +if (index >= 0 && gDoor[index].ColOffset) + return (-1); + +for (index = 0; index < MAX_DOORS; index++) + { + if (gDoor[index].mPos == -1) + return (index); + } +return (-1); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Checks directly in front of the POV to see if a door is there. If so, +// and the door is not locked, the door is set to begin opening. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckCheckDoorOpen (short xPlayer, short yPlayer, short PlayerAngle) +{ + short i,j,DoorCode; + +DoorCode = POV_NODOOR; +// Check to see if player is close to a wall +i = AckCheckHit (xPlayer, yPlayer, PlayerAngle); +// A secret door is found along an x wall +if (i == 1 && xGridGlobal[xMapPosn] & DOOR_TYPE_SECRET) + { + if (xSecretColumn == 0) // Player is on the right side of door + { + DoorCode = POV_XSECRETDOOR; + // Secret door is locked; can't open + if (xGridGlobal[xMapPosn] & DOOR_LOCKED) + return (DoorCode | POV_DOORLOCKED); + // Get grid map position that corresponds with the x wall + // position of where the door starts + xSecretmPos = xMapPosn; + if (iLastX > xPlayer) // Door is to the right of player + { + xSecretmPos1 = xMapPosn + 1; + LastMapPosn = xMapPosn; + xSecretColumn = 1; // Set to indicate player is on right side + yGridGlobal[xMapPosn] = yGridGlobal[xMapPosn - GRID_WIDTH]; + } + else + { // Door is to the left of the player + LastMapPosn = xSecretmPos1 = xMapPosn - 1; + xSecretColumn = -1; + yGridGlobal[xSecretmPos1] = yGridGlobal[xSecretmPos1 - GRID_WIDTH]; + } + } + } + +// A secret door is found along a y wall +if (i == 2 && yGridGlobal[yMapPosn] & DOOR_TYPE_SECRET) + { + if (ySecretColumn == 0) + { + DoorCode = POV_YSECRETDOOR; + + if (yGridGlobal[yMapPosn] & DOOR_LOCKED) + return (DoorCode | POV_DOORLOCKED); + + ySecretmPos = yMapPosn; + if (iLastY > yPlayer) + { + LastMapPosn = yMapPosn; + ySecretmPos1 = yMapPosn + GRID_WIDTH; + xGridGlobal[yMapPosn] = xGridGlobal[yMapPosn - 1]; + ySecretColumn = 1; + } + else + { + LastMapPosn = ySecretmPos1 = yMapPosn - GRID_WIDTH; + xGridGlobal[ySecretmPos1] = xGridGlobal[ySecretmPos1 - 1]; + ySecretColumn = -1; + } + } + } + +// A sliding or split door is found along an x wall +if (i == 1 && (xGridGlobal[xMapPosn] & (DOOR_TYPE_SLIDE + DOOR_TYPE_SPLIT))) + { + j = FindDoorSlot (xMapPosn); + if (j >= 0) + { + DoorCode = POV_XDOOR; + + LastMapPosn = gDoor[j].mPos = xMapPosn; + if ((short) iLastX > xPlayer) + i = xMapPosn + 1; + else + LastMapPosn = i = xMapPosn - 1; + if (xGridGlobal[xMapPosn] & DOOR_LOCKED) + { + gDoor[j].mPos = -1; + return (DoorCode | POV_DOORLOCKED); + } + // Update door data structure + gDoor[j].mCode = xGridGlobal[xMapPosn]; + gDoor[j].mCode1 = xGridGlobal[i]; + gDoor[j].mPos1 = i; + gDoor[j].ColOffset = 1; + gDoor[j].Speed = aeGlobal->DoorSpeed; + gDoor[j].Type = DOOR_XCODE; + gDoor[j].Flags = DOOR_OPENING; + } + } + +// A sliding or split door is found along a y wall +if (i == 2 && (yGridGlobal[yMapPosn] & (DOOR_TYPE_SLIDE + DOOR_TYPE_SPLIT))) + { + j = FindDoorSlot (yMapPosn); + if (j >= 0) + { + DoorCode = POV_YDOOR; + LastMapPosn = gDoor[j].mPos = yMapPosn; + if ((short) iLastY > yPlayer) + i = yMapPosn + GRID_WIDTH; + else + LastMapPosn = i = yMapPosn - GRID_WIDTH; + + if (yGridGlobal[yMapPosn] & DOOR_LOCKED) + { + gDoor[j].mPos = -1; + return (DoorCode | POV_DOORLOCKED); + } + + gDoor[j].mCode = yGridGlobal[yMapPosn]; + gDoor[j].mCode1 = yGridGlobal[i]; + gDoor[j].mPos1 = i; + gDoor[j].ColOffset = 1; + gDoor[j].Speed = aeGlobal->DoorSpeed; + gDoor[j].Type = DOOR_YCODE; + gDoor[j].Flags = DOOR_OPENING; + } + } +return (DoorCode); +} +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKENG.H b/ack_lib/ACKENG.H new file mode 100644 index 0000000..c035221 --- /dev/null +++ b/ack_lib/ACKENG.H @@ -0,0 +1,66 @@ +// ACKENG.H header file for supporting raycasting routines. +// This file contains the main data structure named SLICE and definitions +// required to support the raycasting routines. The data structures defined +// in this file are not to be used directly by the programmer who is using +// the functions in the ACK-3D library. To locate data structures for the +// ACK-3D interface, see the file ACK3D.H. + +#define TRANS_WALLS 0 +#define FLOOR_ACTIVE 1 +#define USE_XMS 0 // Set to 0 if XMS not desired + +#define MAX_RBA 500 // Number of rba's in resource header + +// Fixed point constants used to perform fixed point calculations. +#define FP_SHIFT 16 +#define FP_MULT 65536 +#define FP_HALF 32768 + +#define VIEW_WIDTH 320 // The number of columns in a view (screen) +#define MAX_DISTANCE 2048 // The max distance from the POV to a wall slice + +#define TYPE_WALL 0 +#define TYPE_OBJECT 1 +#define TYPE_PALETTE 2 + +#define MAX_HEIGHT 960 // Maximum height of a wall +#define MIN_HEIGHT 8 // Minimum height of a wall + +#define MAX_UPDOWN 30 // Max up or down spots for each level + +#define MAP_STARTCODE 0xFC // Force player to this square +#define MAP_UPCODE 0xFD // Go up to previous level +#define MAP_DOWNCODE 0xFE // Go down to next level +#define MAP_GOALCODE 0xFF // Finish line! + +#define ST_WALL 1 +#define ST_OBJECT 2 + +#define COLS_PER_BYTE 1 // Use 1 for normal mode 13h, 4 for modeX +#define BYTES_PER_ROW 320 // Use 320 for normal mode 13h, 80 for modeX +#define DWORDS_PER_ROW (BYTES_PER_ROW / 4) +#define SCREEN_SIZE 64000 + +// Holds information for the current wall section found during the raycasting process. +// During the raycasting process, ACK-3D casts out rays, looks for a wall at a +// given screen column position, and if a wall (slice) is found, information about +// the slice is stored in this structure. + +typedef struct _slicer { + UCHAR **bMap; // Pointer to wall bitmap found while ray casting + UCHAR *mPtr; // Grid pointer to reference multi-height wall data + short bNumber; // Bitmap number of the wall found + unsigned short bColumn; // Screen column location of the found slice + short Distance; // Distance from the POV to the slice + short mPos; // Position of the slice in the associated map + unsigned char Type; // Indicates if the slice is a wall or object + void (*Fnc)(void); // Pointer to a function to draw wall or object + unsigned char Active; // Indicates last slice in listif a wall or object is displayable or not + // The next two pointers are used if the current slice + // is part of a transparent wall + struct _slicer *Prev; // References the wall slice in front of current slice + struct _slicer *Next; // References the wall slice behind the current slice + } SLICE; + + + \ No newline at end of file diff --git a/ack_lib/ACKEXT.H b/ack_lib/ACKEXT.H new file mode 100644 index 0000000..7337360 --- /dev/null +++ b/ack_lib/ACKEXT.H @@ -0,0 +1,119 @@ +/* ACK-3D ( Animation Construction Kit 3D ) */ + +extern UCHAR *BackArray[]; +extern long xPglobal; +extern long yPglobal; +extern long xBegGlobal; +extern long yBegGlobal; +extern long BackDropRows; +extern ACKENG *aeGlobal; +extern USHORT *xGridGlobal; +extern USHORT *yGridGlobal; +extern long xPglobalHI; +extern long yPglobalHI; +extern ULONG *rbaTable; + +extern long bmDistance; + + +extern short rsHandle; +extern long LastX1; +extern long LastY1; +extern long iLastX; +extern long iLastY; +extern short MaxDistance; +extern short ErrorCode; +extern short LightFlag; + +extern long xMapPosn; +extern long yMapPosn; + +extern short DefZone[]; +extern short AckLightZones[]; +extern UCHAR *HtTable[]; +extern USHORT *Grid; +extern USHORT *ObjGrid; +extern UCHAR HitMap[]; +extern UCHAR *BitmapXferPtr; +extern short TotalSpecial; +extern short DistanceTable[]; +extern long *AdjustTable; +extern short xSecretmPos; +extern short xSecretmPos1; +extern short xSecretColumn; +extern short ySecretmPos; +extern short ySecretmPos1; +extern short ySecretColumn; +extern short TotalSecret; +extern short ViewColumn; +extern long *SinTable; +extern long *CosTable; +extern long *LongTanTable; +extern long *LongInvTanTable; +extern long InvCosTable[]; +extern long InvSinTable[]; +extern long *LongCosTable; +extern long *ViewCosTable; +extern long *xNextTable; +extern long *yNextTable; +extern UCHAR ObjectsSeen[]; +extern UCHAR MoveObjectList[]; +extern short TotalObjects; +extern short FoundObjectCount; +extern short MoveObjectCount; +extern short LastObjectHit; +extern short LastMapPosn; +extern UCHAR ObjNumber[]; +extern USHORT ObjRelDist[]; +extern short ObjColumn[]; +extern short ObjAngle[]; +extern short DirAngle[]; +extern UCHAR LightMap[]; + +extern USHORT FloorMap[]; +extern USHORT CeilMap[]; +extern SLICE Slice[]; +extern USHORT ScreenOffset; +extern short LastFloorAngle; +extern short LastFloorX; +extern short LastFloorY; +extern long Flooru; +extern long Floorv; +extern long Floordu; +extern long Floordv; +extern long Floorkx; +extern long Floorky; +extern long Floorku; +extern long Floorkv; +extern long Floorkdu; +extern long Floorkdv; +extern UCHAR *Floorbm; +extern UCHAR *Floorscr; +extern UCHAR *FloorscrTop; +extern UCHAR *Floorptr2; +extern UCHAR *Floors1; +extern UCHAR *Floors2; +extern long Floorht; +extern long Floorwt; +extern short Floorvht; +extern short Flooreht; +extern short ViewAngle; +extern short ViewHeight; +extern short CeilingHeight; +extern short Resolution; +extern short LastWallHeight; +extern short PlayerAngle; +extern short ViewAngle; +extern USHORT SysFlags; +extern SLICE Slice[]; +extern SLICE *sPtr; +extern UCHAR **WallbMaps; +extern UCHAR *VidTop; +extern UCHAR *VidBottom; +extern short BotRowTable[]; +extern USHORT FloorMap[]; +extern USHORT CeilMap[]; +extern char *scantables[]; + + + \ No newline at end of file diff --git a/ack_lib/ACKFLOOR.C b/ack_lib/ACKFLOOR.C new file mode 100644 index 0000000..e77ebfd --- /dev/null +++ b/ack_lib/ACKFLOOR.C @@ -0,0 +1,1239 @@ +// This source file contains the functions needed to process floors. +// (c) 1995 ACK Software (Lary Myers) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +#define MAX_F_VIEWHALFHEIGHT 50 + +extern long FloorCosTable[]; +extern short gWinStartX; +extern short gWinStartY; +extern short gWinEndX; +extern short gWinHalfHeight; +extern UCHAR *gScrnBufferCenter; +extern long WallDistTable[]; + + long zdTable[VIEW_WIDTH][50]; + long mFactor; + long dFactor; + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal function called during the initialize process to setup the +// floor and light shading arrays. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void SetupFloors(ACKENG *ae) +{ + short i,a; + int ht,scanline,ht1; + int Scale_Fac; + long scan1,scan2,f; + long x,y,dist; + long Lastx,Lasty; + +for ( i=0; i<12;i++ ) scantables[i] = ae->PalTable + (7*256); +for ( i=12;i<24;i++ ) scantables[i] = ae->PalTable + (6*256); +for ( i=24;i<36;i++ ) scantables[i] = ae->PalTable + (5*256); +for ( i=36;i<48;i++ ) scantables[i] = ae->PalTable + (4*256); +for ( i=48;i<60;i++ ) scantables[i] = ae->PalTable + (3*256); +for ( i=60;i<72;i++ ) scantables[i] = ae->PalTable + (2*256); +for ( i=72;i<84;i++ ) scantables[i] = ae->PalTable + (1*256); +for ( i=84;i<96;i++ ) scantables[i] = ae->PalTable; + +Scale_Fac = (89 - ViewHeight) * 5; + +ht = 89 - ViewHeight; + +ht *= Scale_Fac; + +for (i = 0; i < VIEW_WIDTH; i++) + { + f = FloorCosTable[i]; + zdTable[i][0] = 0; + for (scanline = 1; scanline < MAX_F_VIEWHALFHEIGHT; scanline++) + { + scan2 = ht / scanline; + zdTable[i][scanline] = (f * scan2) >> 15; + if (zdTable[i][scanline-1] < zdTable[i][scanline]) + zdTable[i][scanline-1] = zdTable[i][scanline]; + + } + } + +// Some debugging values for internal use +mFactor = 10368; +dFactor = 160; + +} + +// Optimization attempts were made with drawing the rotating background +// as the ceiling was being drawn and drawing the entire background before +// drawing any ceiling tiles or walls. The former seemed to be faster +// in most cases, so the define below was used to toggle between the two +// methods. +#define DRAW_BACK 1 + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Draws the floor and ceiling horizontally. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawFloorHz(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + short va,va1; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + +#if !(DRAW_BACK) +DrawBackDrop(); // Draw entire background +#endif + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Get the starting column for the background, there are 640 +// columns possible, so the remainder when we divide is where +// we want to begin displaying +bcol = va % 640; +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 5 video rows above the center row +scrCeil = scr - 1600; +// Start 6 video rows below the center row +scr += 1920; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current floor and ceiling columns + fscr = scr; + cscr = scrCeil; +// Advance the columns for the next pass + scr += 2; + scrCeil += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +#if DRAW_BACK +// Pick up the pointer to the background image for the current column + ba = BackArray[bcol++]; +// Check for wrap-around in our 640 column image + if (bcol > 639) bcol = 0; +// Pick up the next column as well since the floor and ceiling are +// always done in low resolution + ba1 = BackArray[bcol++]; + if (bcol > 639) bcol = 0; + ba += ht; + ba1 += ht; +#endif + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + + for (row = 6; row <= ht; row++) + { + + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + mPos = (y & 0xFC0) + (x >> 6); // Calc the Map Posn + if (mPos < 0L || mPos > 4095L) + continue; + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + +// Get the bitmap number for our floor + bCode = FloorMap[mPos]; +// And use it to pull the actual bitmap from our array of wall bitmaps + bmp = WallbMaps[bCode]; +// Make sure it's really a bitmap and then get the actual pixel to display + if (bmp != NULL) + ch = bmp[bPos]; + +// Place the pixel in this column as well as the next. Since we have a +// scattering effect we can display in low resolution without too much +// loss in detail. This really speeds up the drawing of the floor and +// ceiling. + *fscr = ch; // Put it into two locations + fscr[1] = ch; // for low resolution + +// Only draw a ceiling pixel if there is a ceiling tile placed in the map +// This gives the effect of seeing the background through holes in the ceiling + if ((bCode = CeilMap[mPos]) != 0) + { + bmp = WallbMaps[bCode]; + if (bmp != NULL) + ch = bmp[bPos]; + *cscr = ch; + cscr[1] = ch; + } +#if DRAW_BACK +// If no ceiling is drawn, then we need to draw the background image + else + { + *cscr = *ba; + cscr[1] = *ba1; + } +#endif + } + + fscr += 320; // Advance our screen position for the floor + cscr -= 320; // and the ceiling + +#if DRAW_BACK +// Advance the pointers to the background image for the next pass + ba--; + ba1--; +#endif + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Draws the floor and ceiling horizontally. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawCeilingOnlyNS(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + short va,va1; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + +#if !(DRAW_BACK) +DrawBackDrop(); // Draw entire background +#endif + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Get the starting column for the background, there are 640 +// columns possible, so the remainder when we divide is where +// we want to begin displaying +bcol = va % 640; +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 5 video rows above the center row +scrCeil = scr - 1600; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current ceiling columns + cscr = scrCeil; +// Advance the columns for the next pass + scrCeil += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +#if DRAW_BACK +// Pick up the pointer to the background image for the current column + ba = BackArray[bcol++]; +// Check for wrap-around in our 640 column image + if (bcol > 639) bcol = 0; +// Pick up the next column as well since the floor and ceiling are +// always done in low resolution + ba1 = BackArray[bcol++]; + if (bcol > 639) bcol = 0; + ba += ht; + ba1 += ht; +#endif + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + + for (row = 6; row <= ht; row++) + { + + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + mPos = (y & 0xFC0) + (x >> 6); // Calc the Map Posn + if (mPos < 0L || mPos > 4095L) + continue; + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + +// Only draw a ceiling pixel if there is a ceiling tile placed in the map +// This gives the effect of seeing the background through holes in the ceiling + if ((bCode = CeilMap[mPos]) != 0) + { + bmp = WallbMaps[bCode]; + if (bmp != NULL) + ch = bmp[bPos]; + *cscr = ch; + cscr[1] = ch; + } +#if DRAW_BACK +// If no ceiling is drawn, then we need to draw the background image + else + { + *cscr = *ba; + cscr[1] = *ba1; + } +#endif + } + + cscr -= 320; // and the ceiling + +#if DRAW_BACK +// Advance the pointers to the background image for the next pass + ba--; + ba1--; +#endif + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Draws the floor and ceiling horizontally. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawFloorOnlyNS(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + short va,va1; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + +#if !(DRAW_BACK) +DrawBackDrop(); // Draw entire background +#endif + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Get the starting column for the background, there are 640 +// columns possible, so the remainder when we divide is where +// we want to begin displaying +bcol = va % 640; +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 6 video rows below the center row +scr += 1920; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current floor and ceiling columns + fscr = scr; +// Advance the columns for the next pass + scr += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +#if DRAW_BACK +// Pick up the pointer to the background image for the current column + ba = BackArray[bcol++]; +// Check for wrap-around in our 640 column image + if (bcol > 639) bcol = 0; +// Pick up the next column as well since the floor and ceiling are +// always done in low resolution + ba1 = BackArray[bcol++]; + if (bcol > 639) bcol = 0; + ba += ht; + ba1 += ht; +#endif + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + + for (row = 6; row <= ht; row++) + { + + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + mPos = (y & 0xFC0) + (x >> 6); // Calc the Map Posn + if (mPos < 0L || mPos > 4095L) + continue; + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + +// Get the bitmap number for our floor + bCode = FloorMap[mPos]; +// And use it to pull the actual bitmap from our array of wall bitmaps + bmp = WallbMaps[bCode]; +// Make sure it's really a bitmap and then get the actual pixel to display + if (bmp != NULL) + ch = bmp[bPos]; + +// Place the pixel in this column as well as the next. Since we have a +// scattering effect we can display in low resolution without too much +// loss in detail. This really speeds up the drawing of the floor and +// ceiling. + *fscr = ch; // Put it into two locations + fscr[1] = ch; // for low resolution + } + + fscr += 320; // Advance our screen position for the floor + +#if DRAW_BACK +// Advance the pointers to the background image for the next pass + ba--; + ba1--; +#endif + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// The function performs the same process as AckDrawFloorHz except here +// we include light shading with distance. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawFloor(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + char *stPtr; + short va,va1,LineNum; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + +#if !(DRAW_BACK) +DrawBackDrop(); // Draw entire background +#endif + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Get the starting column for the background, there are 640 +// columns possible, so the remainder when we divide is where +// we want to begin displaying +bcol = va % 640; +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 5 video rows above the center row +scrCeil = scr - 1600; +// Start 6 video rows below the center row +scr += 1920; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current floor and ceiling columns + fscr = scr; + cscr = scrCeil; +// Advance the columns for the next pass + scr += 2; + scrCeil += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +#if DRAW_BACK +// Pick up the pointer to the background image for the current column + ba = BackArray[bcol++]; +// Check for wrap-around in our 640 column image + if (bcol > 639) bcol = 0; +// Pick up the next column as well since the floor and ceiling are +// always done in low resolution + ba1 = BackArray[bcol++]; + if (bcol > 639) bcol = 0; + ba += ht; + ba1 += ht; +#endif + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + LineNum = 0; + + for (row = 6; row <= ht; row++) + { + stPtr = scantables[LineNum++]; + + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + mPos = (y & 0xFC0) + (x >> 6); // Calc the Map Posn + if (mPos < 0L || mPos > 4095L) + continue; + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + +// Get the bitmap number for our floor + bCode = FloorMap[mPos]; +// And use it to pull the actual bitmap from our array of wall bitmaps + bmp = WallbMaps[bCode]; +// Make sure it's really a bitmap and then get the actual pixel to display + if (bmp != NULL) + ch = bmp[bPos]; + + ch = stPtr[ch]; // Get shaded value for pixel + +// Place the pixel in this column as well as the next. Since we have a +// scattering effect we can display in low resolution without too much +// loss in detail. This really speeds up the drawing of the floor and +// ceiling. + *fscr = ch; // Put it into two locations + fscr[1] = ch; // for low resolution + +// Only draw a ceiling pixel if there is a ceiling tile placed in the map +// This gives the effect of seeing the background through holes in the ceiling + if ((bCode = CeilMap[mPos]) != 0) + { + bmp = WallbMaps[bCode]; + if (bmp != NULL) + ch = bmp[bPos]; + ch = stPtr[ch]; // Get shaded value for pixel + *cscr = ch; + cscr[1] = ch; + } +#if DRAW_BACK +// If no ceiling is drawn, then we need to draw the background image + else + { + *cscr = *ba; + cscr[1] = *ba1; + } +#endif + } + + fscr += 320; // Advance our screen position for the floor + cscr -= 320; // and the ceiling + +#if DRAW_BACK +// Advance the pointers to the background image for the next pass + ba--; + ba1--; +#endif + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Draw ceiling texture bitmaps only. Floor is assumed to be a solid color. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawCeilingOnly(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + short va,va1,LineNum; + char *stPtr; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + +#if !(DRAW_BACK) +DrawBackDrop(); // Draw entire background +#endif + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Get the starting column for the background, there are 640 +// columns possible, so the remainder when we divide is where +// we want to begin displaying +bcol = va % 640; +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 5 video rows above the center row +scrCeil = scr - 1600; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current ceiling columns + cscr = scrCeil; +// Advance the columns for the next pass + scrCeil += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +#if DRAW_BACK +// Pick up the pointer to the background image for the current column + ba = BackArray[bcol++]; +// Check for wrap-around in our 640 column image + if (bcol > 639) bcol = 0; +// Pick up the next column as well since the floor and ceiling are +// always done in low resolution + ba1 = BackArray[bcol++]; + if (bcol > 639) bcol = 0; + ba += ht; + ba1 += ht; +#endif + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + LineNum = 0; + + for (row = 6; row <= ht; row++) + { + stPtr = scantables[LineNum++]; + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + mPos = (y & 0xFC0) + (x >> 6); // Calc the Map Posn + if (mPos < 0L || mPos > 4095L) + continue; + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + +// Only draw a ceiling pixel if there is a ceiling tile placed in the map +// This gives the effect of seeing the background through holes in the ceiling + if ((bCode = CeilMap[mPos]) != 0) + { + bmp = WallbMaps[bCode]; + if (bmp != NULL) + ch = bmp[bPos]; + + ch = stPtr[ch]; // Get shaded pixel + *cscr = ch; + cscr[1] = ch; + } +#if DRAW_BACK +// If no ceiling is drawn, then we need to draw the background image + else + { + *cscr = *ba; + cscr[1] = *ba1; + } +#endif + } + + cscr -= 320; // and the ceiling + +#if DRAW_BACK +// Advance the pointers to the background image for the next pass + ba--; + ba1--; +#endif + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Draw floor textured bitmaps only. Ceiling is a solid color. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawFloorOnly(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + short va,va1,LineNum; + char *stPtr; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + +#if !(DRAW_BACK) +DrawBackDrop(); // Draw entire background +#endif + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Get the starting column for the background, there are 640 +// columns possible, so the remainder when we divide is where +// we want to begin displaying +bcol = va % 640; +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 6 video rows below the center row +scr += 1920; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current floor and ceiling columns + fscr = scr; +// Advance the columns for the next pass + scr += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +#if DRAW_BACK +// Pick up the pointer to the background image for the current column + ba = BackArray[bcol++]; +// Check for wrap-around in our 640 column image + if (bcol > 639) bcol = 0; +// Pick up the next column as well since the floor and ceiling are +// always done in low resolution + ba1 = BackArray[bcol++]; + if (bcol > 639) bcol = 0; + ba += ht; + ba1 += ht; +#endif + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + LineNum = 0; + + for (row = 6; row <= ht; row++) + { + stPtr = scantables[LineNum++]; + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + mPos = (y & 0xFC0) + (x >> 6); // Calc the Map Posn + if (mPos < 0L || mPos > 4095L) + continue; + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + +// Get the bitmap number for our floor + bCode = FloorMap[mPos]; +// And use it to pull the actual bitmap from our array of wall bitmaps + bmp = WallbMaps[bCode]; +// Make sure it's really a bitmap and then get the actual pixel to display + if (bmp != NULL) + ch = bmp[bPos]; + + ch = stPtr[ch]; // Get shaded pixel + +// Place the pixel in this column as well as the next. Since we have a +// scattering effect we can display in low resolution without too much +// loss in detail. This really speeds up the drawing of the floor and +// ceiling. + *fscr = ch; // Put it into two locations + fscr[1] = ch; // for low resolution + } + + fscr += 320; // Advance our screen position for the floor + +#if DRAW_BACK +// Advance the pointers to the background image for the next pass + ba--; + ba1--; +#endif + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + + + + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// This function performs the same process as AckDrawFloorHz except +// we don't need to check for a bitmap index for every pixel since only +// one bitmap will be used for the floor and one bitmap for the ceiling. +// Draws a floor that contains only one type of bitmap. This is a much +// faster process and may be useful in some applications. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckDrawOneFloor(void) +{ + int i,col,row,ht,Rcol,EndCol,BegCol; + int Scale_Fac,ScaleHt; + UCHAR *scr,*fscr,*bmp,*cbmp,*scrCeil,*cscr; + UCHAR *Rscr,*Rfscr,*RscrCeil,*Rcscr; + UCHAR *ba,*ba1,*Rba,*Rba1; + short va,va1; + long LastDist,scan2; + long cv,sv,dist,x,y,bx,by,mPos,bPos,wdist,bcol; + long Rbcol,Rwdist,xp,yp; + long *zd,*wPtr,*RwPtr,*xyPtr; + long fcv; + UCHAR ch; + USHORT bCode; + + +BegCol = gWinStartX; +EndCol = gWinEndX; + +// Get the starting offset of the center row of the view +scr = gScrnBufferCenter + BegCol; +// Get the left side of the POV view +va = PlayerAngle - INT_ANGLE_32; +// Check for wrap-around +if (va < 0) va += INT_ANGLE_360; + +// Adjust the viewing angle based on the starting view column +va += BegCol; +// and check for wrap-around +if (va >= INT_ANGLE_360) + va -= INT_ANGLE_360; + +// Temporarily hold onto the view height / 2 +ht = gWinHalfHeight; +// Since our horizon should always have some form of wall or object, +// no matter how far away it is, we can optimize alittle here by not +// drawing the first few horizon rows (it does actually save time!). +// Start 5 video rows above the center row +scrCeil = scr - 1600; +// Start 6 video rows below the center row +scr += 1920; +// Initial right side of view +Rcol = 319; +wPtr = &WallDistTable[BegCol]; // Get pointers to avoid indexing +RwPtr = &WallDistTable[Rcol]; + +// The ScaleHt is used to determine the distance from the player for each +// row of the floor and ceiling. The value 89 was arrived at based on a +// reasonable height above the floor that the player is standing. By +// experimenting with this value, the effect of jumping up and down can +// be achieved, (as long as the walls and objects are made to jump by the +// same amount). +Scale_Fac = (89 - ViewHeight) * 5; +ScaleHt = 89 - ViewHeight; +ScaleHt *= Scale_Fac; + +bmp = aeGlobal->bMaps[aeGlobal->FloorBitmap]; +cbmp = aeGlobal->bMaps[aeGlobal->CeilBitmap]; + +// Here we loop through each column of the viewing window +for (col = BegCol; col < EndCol; col += 2) + { +// Pick up the cosine and sine values for the current angle + cv = CosTable[va]; + sv = SinTable[va]; +// Point to the left side of the current floor and ceiling columns + fscr = scr; + cscr = scrCeil; +// Advance the columns for the next pass + scr += 2; + scrCeil += 2; +// Pick up the current distance to the wall that was found for the +// current column + wdist = *wPtr; +// Advance the wall distance pointer for the next pass + wPtr += 2; + +// Initialize a last distance variable + LastDist = -1; +// Our floor cosine is used to counteract a fisheye effect + fcv = FloorCosTable[col]; + + for (row = 6; row <= ht; row++) + { + scan2 = ScaleHt / row; +// Get the distance for the current column and row position + dist = (fcv * scan2) >> 15; + +// If we're still closer than any wall + if (dist < wdist) + { +// Do some extra calculations if it's a new distance from last time +// (Sometimes our distance works out to be the same so we can avoid +// the next few lines) + if (dist != LastDist) + { + x = xPglobal + ((cv * dist) >> 16); + y = yPglobal + ((sv * dist) >> 16); + LastDist = dist; + } + + bPos = (y & 63) + ((x & 63)<<6); // Calc the Bitmap Pixel + ch = bmp[bPos]; + +// Place the pixel in this column as well as the next. Since we have a +// scattering effect we can display in low resolution without too much +// loss in detail. This really speeds up the drawing of the floor and +// ceiling. + *fscr = ch; // Put it into two locations + fscr[1] = ch; // for low resolution + + ch = cbmp[bPos]; + *cscr = ch; + cscr[1] = ch; + } + + fscr += 320; // Advance our screen position for the floor + cscr -= 320; // and the ceiling + } + +// Advance our current viewing angle by 2 since we are in low resolution + va += 2; +// and check for wrap-around + if (va >= INT_ANGLE_360) va -= INT_ANGLE_360; + } + +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKGIF.C b/ack_lib/ACKGIF.C new file mode 100644 index 0000000..66059b0 --- /dev/null +++ b/ack_lib/ACKGIF.C @@ -0,0 +1,30 @@ +// This source file contains the functions needed to read in GIF files. +// (c) 1995 ACK Software (Lary Myers) +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Read a GIF format file and return a buffer containing the uncompressed +// image. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +#pragma argsused +unsigned char *AckReadgif (char *picname) +{ +// This is a stub routine used only as a place holder for the actual +// GIF read routine. It was omitted based on current patent issues. +return(NULL); +} +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKIFF.C b/ack_lib/ACKIFF.C new file mode 100644 index 0000000..3ff064d --- /dev/null +++ b/ack_lib/ACKIFF.C @@ -0,0 +1,231 @@ +// This source file contains the functions needed to read in BBM files. +// (c) 1995 ACK Software (Lary Myers) +//============================================================================= +// This function will return a pointer to a buffer that holds the raw image. +// just free the pointer to delete this buffer. After returning, the array +// colordat will hold the adjusted palette of this pic. +// +// Also, this has been modified to only read in form PBM brushes. form ILBM's +// (the "old" type) are not supported. use the "new" deluxe paint .lbm type +// and do not choose "old". +//============================================================================= +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" +#include "iff.h" + +extern int errno; + +extern char rsName[]; + +unsigned char colordat[768]; // maximum it can be...256 colors + +unsigned char cplanes[8][80]; // setting max at 640 pixels width + // thats 8 pixels per byte per plane +unsigned char *pplanes= &cplanes[0][0]; // for a form pbm + +#define MAX_BUF_POS 4096 + + int rdbufpos; + int rdSize; + UCHAR rdBuffer[MAX_BUF_POS+2]; + +//============================================================================= +// +//============================================================================= +void CloseFile(FILE *fp) +{ + +fclose(fp); +if (rsHandle) + { + rsHandle = _lopen(rsName,OF_READ); + if (rsHandle < 1) + rsHandle = 0; + } + +} + +unsigned char *AckReadiff(char *picname) + { + FILE *pic; + short handle; + form_chunk fchunk; + ChunkHeader chunk; + BitMapHeader bmhd; + long length,fpos; + char value; // must remain signed, no matter what. ignore any warnings. + short sofar; + short width,height,planes; + short pixw; + unsigned char *destx, *savedestx; + + rdbufpos = MAX_BUF_POS + 1; + rdSize = MAX_BUF_POS; + + if (!rsHandle) + handle = _lopen(picname,OF_READ); + else + { + handle = rsHandle; + _llseek(rsHandle,rbaTable[(ULONG)picname],SEEK_SET); + } + + _lread(handle,&fchunk,sizeof(form_chunk)); + + if (fchunk.type != FORM) + { + if (!rsHandle) + _lclose(handle); + + ErrorCode = ERR_INVALIDFORM; + return(0L); + } + + if (fchunk.subtype != ID_PBM) + { + if (!rsHandle) + _lclose(handle); + ErrorCode = ERR_NOPBM; + return(0L); + } + // now lets loop...Because the Chunks can be in any order! + while(1) + { + _lread(handle,&chunk,sizeof(ChunkHeader)); + chunk.ckSize = ByteFlipLong(chunk.ckSize); + if (chunk.ckSize & 1) chunk.ckSize ++; // must be word aligned + if(chunk.ckID == ID_BMHD) + { + _lread(handle,&bmhd,sizeof(BitMapHeader)); + bmhd.w=iffswab(bmhd.w); // the only things we need. + bmhd.h=iffswab(bmhd.h); + destx = (unsigned char *)AckMalloc((bmhd.w * bmhd.h)+4); + if ( !destx ) + { + if (!rsHandle) + _lclose(handle); + ErrorCode = ERR_NOMEMORY; + return(0L); + } + + savedestx = destx; + + destx[0] = bmhd.w%256; + destx[1] = bmhd.w/256; + destx[2] = bmhd.h%256; + destx[3] = bmhd.h/256; + destx += 4; + continue; + } + if(chunk.ckID == ID_CMAP) + { + short i; + unsigned char r,g; + + _lread(handle,colordat,chunk.ckSize); + for (i=0;i<768;i++) + { + r = colordat[i]; // r,g do not stand for red and green + g = r >> 2; + colordat[i] = g; + } + continue; + } + if(chunk.ckID == ID_BODY) + { + for(height = 0; height 0) + { + short len; + len = value +1; + sofar -= len; + if (!(_lread(handle,dest,len))) + { + if (!rsHandle) + _lclose(handle); + ErrorCode = ERR_BADPICFILE; + AckFree(savedestx); + return(0L); + } + dest +=len; + } + else + { + short count; + count = -value; // get amount to dup + count ++; + sofar -= count; + value = 0; + _lread(handle,&value,1); + while (--count >= 0) *dest++ = value; + } + } + else + { + _lread(handle,dest,sofar); + sofar = 0; + } + } + if (sofar < 0) + { + if (!rsHandle) + _lclose(handle); + } + _fmemcpy(destx,pplanes,bmhd.w); + destx += bmhd.w; + } + break; // leave if we've unpacked the BODY + } + + _llseek(handle,chunk.ckSize,SEEK_CUR); + } + + if (!rsHandle) + _lclose(handle); + return((char *)savedestx); + } + + +long ByteFlipLong(long NUMBER) + { + long Y, T; + short I; + + T = NUMBER; + Y=0;for (I=0;I<4;I++){Y = Y | (T & 0xFF);if (I<3) {Y = Y << 8;T = T >> 8;}} + return(Y); + } + +short iffswab(unsigned short number) + { + unsigned short xx1,xx2; + unsigned short result; + + xx1 = number <<8; xx2 = number >>8; result = xx1|xx2; + return(result); + } + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKINIT.C b/ack_lib/ACKINIT.C new file mode 100644 index 0000000..afe1e03 --- /dev/null +++ b/ack_lib/ACKINIT.C @@ -0,0 +1,542 @@ +// This file contains the key initialization functions for the ACK-3D engine. +// The main function AckInitialize() must be called first before any of the +// other ACK-3D functions are called. The internal functions defined in this file +// perform all of the set up work of loading tables and resource files. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +extern char AckKeyboardSetup; +extern char AckTimerSetup; + +short *LowerTable[2048]; +short tmpLowerValue[400]; +short LowerLen[2048]; +short OurDataSeg; + +char rsName[128]; + +long FloorCosTable[VIEW_WIDTH+1]; + +short AckBuildTables(ACKENG *ae); +void AckBuildHeightTables(ACKENG *ae); +void AckBuildGrid(ACKENG *ae); +void SetupFloors(ACKENG *ae); + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Internal function called by AckInitialize(). This function sets up the +// internal variables that are required to support the off-screen buffer +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +void AckSetupWindow(ACKENG *ae) +{ +// Access the center row of the viewport +ae->CenterRow = ae->WinStartY + ((ae->WinEndY - ae->WinStartY) / 2); +// Access a memory location for the center row +ae->CenterOffset = ae->CenterRow * BYTES_PER_ROW; +// Access the starting memory location of the viewport +ae->WinStartOffset = ae->WinStartY * BYTES_PER_ROW; +// Calculate the window length in double words +ae->WinLength = ((ae->WinEndY - ae->WinStartY)+1) * DWORDS_PER_ROW; +// Calculates the viewport window width and height +ae->WinWidth = (ae->WinEndX - ae->WinStartX) + 1; +ae->WinHeight = (ae->WinEndY - ae->WinStartY) + 1; +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Initializes the ACK interface structure and reads in the TRIG tables +// from either the stand alone file TRIG.DAT or from a resource file that +// was opened previous to this call. +// This function MUST be called before AckBuildView() and AckDisplayScreen() +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +short AckInitialize(ACKENG *ae) +{ + short i,result = 0; + short j; + UCHAR topcolor; + +#ifdef __BORLANDC__ // Conditional for Borland C++ +OurDataSeg = _DS; +#endif + +AckKeyboardSetup = 0; // Indicates keyboard interrupt has not been set up +AckTimerSetup = 0; // Indicates timer has not been set up + +// Check to see if viewport coordinates are set up properly +if (!ae->WinEndY || !ae->WinEndX || + (ae->WinEndY - ae->WinStartY) < 10 || // Height is less than 10 pixels + (ae->WinEndX - ae->WinStartX) < 10) // Width is less than 10 pixels +{ + return(ERR_BADWINDOWSIZE); // Return error code for invalid viewport +} + +result = AckBuildTables(ae); // Read in TRIG.DAT and allocate tables +if (result) + return(result); + +AckSetupWindow(ae); // Set up the internal coordinates for the viewport +SetupFloors(ae); // Set up the floors +AckBuildHeightTables(ae); // Build height and adjustment tables +topcolor = ae->TopColor; +BackDropRows = 100; + +for (i = 0; i < 640; i++) + { + BackArray[i] = AckMalloc(BackDropRows+1); + if (BackArray[i] == NULL) + return(ERR_NOMEMORY); + memset(BackArray[i],topcolor,BackDropRows); + } +return(result); +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Opens a resource file for use by any ACK routine that requires a +// filename. Only one resource file can be opened at a time. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +short AckOpenResource(char *fName) +{ + ULONG hLen; + +if (rsHandle) // Is a resource file currently opened? + _lclose(rsHandle); // Close it before opening a new one + +rsHandle = _lopen(fName,OF_READ); // Open new resource file +if (rsHandle < 1) // Check to see if file is opened properly + { + rsHandle = 0; // Reset file handle + return(ERR_BADFILE); // Return error code for faliure + } + +hLen = MAX_RBA * sizeof(long); // Get size of file +if (rbaTable == NULL) + rbaTable = (ULONG *)AckMalloc(hLen); // Allocate buffer for file +if (rbaTable == NULL) // Was memory available? + { + _lclose(rsHandle); // Close file + rsHandle = 0; // Reset file handle + return(ERR_NOMEMORY); // Return error code + } + +// Read in the file and check for byte count error +if (_lread(rsHandle,(ULONG *)rbaTable,hLen) != hLen) + { + _lclose(rsHandle); // Close file + rsHandle = 0; // Reset file handle + AckFree(rbaTable); // Free up buffer + return(ERR_BADFILE); // Return file error code + } + +strcpy(rsName,fName); // Store resource filename +return(0); +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Closes a resource file if one is opened. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +void AckCloseResource(void) +{ +if (rsHandle) // Check to make sure resource file is opened + _lclose(rsHandle); // Close the resource + +if (rbaTable != NULL) // Do we need to free the memory for the file buffer? + { + AckFree(rbaTable); // Free up the file buffer + rbaTable = NULL; + } + +rsHandle = 0; // Reset the file handle +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Internal function used to pre-define height tables for the wall +// drawing code. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +void BuildWallDstTables(void) +{ + short i,j,dst,row,HiValue; + long ldst,value,LowValue,len; + short *lp; + +for (ldst = 10;ldst < 2048; ldst++) + { + HiValue = value = 0; + row = 0; + + while (HiValue < 64 && row < 100) + { + HiValue = (value >> 8) & 0xFF; + tmpLowerValue[row] = HiValue; + row++; + value += ldst; + } + + LowerLen[ldst] = row; + len = row * 2; + j = 1; + if (row == LowerLen[ldst-1]) + { + j = 0; + lp = LowerTable[ldst-1]; + for (i = 0; i < row; i++) + { + if (tmpLowerValue[i] != lp[i]) + { + j = 1; + break; + } + } + } + if (j) + { + lp = AckMalloc(len); + if (lp == NULL) + { + return; + } + LowerTable[ldst] = lp; + for (i = 0; i < row; i++) + lp[i] = tmpLowerValue[i]; + } + else + { + LowerTable[ldst] = LowerTable[ldst-1]; + } + } +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Internal function called from AckInitialize() to read in the trig tables +// and allocate memory for the various buffers. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +short AckBuildTables(ACKENG *ae) +{ + short handle,len,ca,na; + int c,s,ang; + long fAng,tu,tv; + SLICE *sa,*saNext; + +BuildWallDstTables(); // Create the distance tables + +if (!rsHandle) // Check to make sure resource file is not opened + { + handle = _lopen("trig.dat",OF_READ); // Open trig data file + if (handle < 1) + return(ERR_BADFILE); // File can't be opened; return error code + } +else + { + handle = rsHandle; // Get handle for resource file + _llseek(handle,rbaTable[0],SEEK_SET); + } + +// Allocate memory for trig and coordinate tables +LongTanTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +LongInvTanTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +CosTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +SinTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +LongCosTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +xNextTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +yNextTable = (long *)AckMalloc(sizeof(long) * INT_ANGLE_360); +ViewCosTable = (long *)AckMalloc(sizeof(long) * VIEW_WIDTH); + +// Allocate memory for map grid and object grid +Grid = (unsigned short *)AckMalloc((GRID_MAX * 2)+1); +ObjGrid = (unsigned short *)AckMalloc((GRID_MAX * 2)+1); + +// Allocate memory for height adjustment table +AdjustTable = (long *)AckMalloc((MAX_DISTANCE+1) * sizeof(long)); +// Allocate memory for screen buffers +ae->ScreenBuffer = (UCHAR *)AckMalloc(SCREEN_SIZE+640); +ae->BkgdBuffer = (UCHAR *)AckMalloc(SCREEN_SIZE+640); + +if (LongTanTable == NULL || // Make sure memory is allocated for tables + LongInvTanTable == NULL || + CosTable == NULL || + SinTable == NULL || + LongCosTable == NULL || + xNextTable == NULL || + yNextTable == NULL || + Grid == NULL || + ObjGrid == NULL || + AdjustTable == NULL || + ae->ScreenBuffer == NULL || + ae->BkgdBuffer == NULL || + ViewCosTable == NULL) + { + if (!rsHandle) + _lclose(handle); + return(ERR_NOMEMORY); // Return memory allocation error code + } + +len = sizeof(long) * INT_ANGLE_360; // Calculate size for each trig table +_lread(handle,SinTable,len); // Read in trig data and place in appropriate tables +_lread(handle,CosTable,len); +_lread(handle,LongTanTable,len); +_lread(handle,LongInvTanTable,len); +_lread(handle,InvCosTable,len); +_lread(handle,InvSinTable,len); +_lread(handle,LongCosTable,len); + +if (!rsHandle) + _lclose(handle); // Done reading, close trig.dat + +ca = INT_ANGLE_32; +na = -1; + +// Set up viewing tables for 32 to -32 angle sweep +for (len = 0; len < VIEW_WIDTH; len++) + { + ViewCosTable[len] = LongCosTable[ca]; + FloorCosTable[len] = InvCosTable[ca] >> 6; + ca += na; + if (ca <= 0) // Index is less than 0 so switch + { + ca = -ca; + na = -na; + } + } + +// Adjust tables for 90, 180, and 270 degree angles +LongTanTable[INT_ANGLE_90] = LongTanTable[INT_ANGLE_90+1]; +LongInvTanTable[INT_ANGLE_90] = LongInvTanTable[INT_ANGLE_90+1]; +LongTanTable[INT_ANGLE_180] = LongTanTable[INT_ANGLE_180+1]; +LongInvTanTable[INT_ANGLE_180] = LongInvTanTable[INT_ANGLE_180+1]; +LongTanTable[INT_ANGLE_270] = LongTanTable[INT_ANGLE_270+1]; +LongInvTanTable[INT_ANGLE_270] = LongInvTanTable[INT_ANGLE_270+1]; + +for (len = 0; len < INT_ANGLE_360; len++) + { + yNextTable[len] = (long)BITMAP_WIDTH * LongTanTable[len]; // Calculate y intercept increments + xNextTable[len] = (long)BITMAP_WIDTH * LongInvTanTable[len]; // Calculate x intercept increments + InvCosTable[len] = InvCosTable[len] >> 4; // Scale inverse tables + InvSinTable[len] = InvSinTable[len] >> 6; + } + +// Set up the array od slice structures to represent the full width of the view +// Each slice structure is initialized by setting its data fields to 0s +// Each slice in the array is also linked to a second slice to reference a slice that +// could be visually behind the current slice +for (len = 0; len < VIEW_WIDTH; len++) + { + sa = &Slice[len]; // Initialize array of slice structures + memset(sa,0,sizeof(SLICE)); // Set all data to 0 + for (ca = 0; ca < 8; ca++) + { + saNext = AckMalloc(sizeof(SLICE)); // Create a slice structure to link in + if (saNext == NULL) + return(ERR_NOMEMORY); // Check for memory allocation + memset(saNext,0,sizeof(SLICE)); // Initialize all data to 0 + sa->Next = saNext; // Link in slice + saNext->Prev = sa; + sa = saNext; + } + } +return(0); +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Reads a map file and processes any multi-height walls. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +short AckReadMapFile(ACKENG *ae,char *fName) +{ + short len,handle,rdlen,count,i,pos; + int mLen,aLen; + UCHAR buf[MAX_MULTI+2]; + UCHAR *mPtr; + +if (!rsHandle) // Check to see if resource file is open already + { // No resource file so open new one + handle = _lopen(fName,OF_READ); // Open the specified resource + if (handle < 1) + return(ERR_BADMAPFILE); // File was not opened; return error code + } +else + { + handle = rsHandle; // Get handle to open resource + _llseek(handle,rbaTable[(ULONG)fName],SEEK_SET); // Access opened resource file + } + +aLen = GRID_ARRAY * 2; +mLen = GRID_MAX * 2; + +if (_lread(handle,Grid,mLen) != mLen) // Read in grid map data + { + if (!rsHandle) + _lclose(handle); + return(ERR_READINGMAP); // Return file read error code + } + +if (_lread(handle,ObjGrid,mLen) != mLen) // Read in object map data + { + if (!rsHandle) + _lclose(handle); + return(ERR_READINGMAP); + } + +if (_lread(handle,ae->xGrid,aLen) != aLen) // Read in x grid data + { + if (!rsHandle) + _lclose(handle); + return(ERR_READINGMAP); + } + +if (_lread(handle,ae->yGrid,aLen) != aLen) // Read in y grid data + { + if (!rsHandle) + _lclose(handle); + return(ERR_READINGMAP); + } + +if (_lread(handle,FloorMap,mLen) != mLen) // Read in floor map data + { + if (!rsHandle) + _lclose(handle); + return(ERR_READINGMAP); + } + +if (_lread(handle,CeilMap,mLen) != mLen) // Read in ceiling map data + { + if (!rsHandle) + _lclose(handle); + return(ERR_READINGMAP); + } + +_lread(handle,&count,2); // Check counter for multi-height walls +if (count) + { + for (i = 0; i < count;i++) // Read in multi-height wall data + { + _lread(handle,&pos,2); // Get grid position for this multi-height wall + mPtr = (UCHAR *)AckMalloc(MAX_MULTI+1); // Allocate memory for multi-height wall data + if (mPtr == NULL) + { + if (!rsHandle) + _lclose(handle); + return(ERR_NOMEMORY); + } + + ae->mxGrid[pos] = mPtr; // Store pointer to multi-height wall + ae->myGrid[pos] = mPtr; + ae->mxGrid[pos+1] = mPtr; + ae->myGrid[pos+GRID_WIDTH] = mPtr; + _lread(handle,buf,MAX_MULTI); + buf[MAX_MULTI] = '\0'; + len = strlen(buf); + if (len > MAX_MULTI) len = MAX_MULTI; + *mPtr = len; + if (len) + memmove(&mPtr[1],buf,len); + } + } + +if (!rsHandle) // Close handle + _lclose(handle); + +AckBuildGrid(ae); // Build object lists +return(0); +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Internal function to create height and distance tables for objects. In +// the DistanceTable[] each entry represents the distance from the player +// to a wall. The value stored in the array is the hight of the wall at +// the corresponding distance. For example, DistanceTable[100] indicates +// that the distance to the wall is 100 units. The value stored at this +// location is 81--the pixel height f the wall. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +void AckBuildHeightTables(ACKENG *ae) +{ + short i,x; + short result; + long height; + +height = BITMAP_WIDTH * 128L; // Calculate distance to height conversion factor + +DistanceTable[0] = MAX_HEIGHT; // First entry = max. height (960) + +//************ 64 * 65536 ******** +AdjustTable[0] = 4194304L / height; + +for (i = 1; i < MAX_DISTANCE; i++) // Loop to calculate each entry for the arrays + { + DistanceTable[i] = height / i; + if (height - (DistanceTable[i] * i) > (i / 2)) + DistanceTable[i]++; // Add 1 to height value + if (DistanceTable[i] < MIN_HEIGHT) // Adjust for min. height (8) + DistanceTable[i] = MIN_HEIGHT; + if (DistanceTable[i] > MAX_HEIGHT) // Adjust for maax. height (960) + DistanceTable[i] = MAX_HEIGHT; + AdjustTable[i] = 2097152L / DistanceTable[i]; // Calculate entry for + } +} + +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +// Internal function called by AckReadMapFile() to process the objects +// in the map. Moveable vs stationary objects are processed here. +//ññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ +void AckBuildGrid(ACKENG *ae) +{ +short i,j,CurIndex,pos,x1,y1; +USHORT MapCode,MapHiCode; + +// Initialize doors +for (i = 0; i < MAX_DOORS; i++) + { + ae->Door[i].ColOffset = 0; + ae->Door[i].mPos = ae->Door[i].mPos1 = -1; + } + +ae->SysFlags |= SYS_NO_WALLS; // Assume no floating walls + +CurIndex = 1; +TotalSpecial = 0; +TotalSecret = 0; +for (i = 0; i < GRID_HEIGHT; i++) // Loop until entire grid has been checked + { + for (j = 0; j < GRID_WIDTH; j++) + { + pos = (i * GRID_WIDTH) + j; + MapCode = ObjGrid[pos]; // Check object at current grid position + if (MapCode) // Is there an object here? + { + CurIndex = MapCode & 0xFF; + if (CurIndex < MAX_OBJECTS) // Get the index of the object into + { + if (ae->ObjList[CurIndex] == NULL) // No object allocated yet + { + // Allocate memory for object + ae->ObjList[CurIndex] = (NEWOBJECT *)AckMalloc(sizeof(NEWOBJECT)); + if (ae->ObjList[CurIndex] != NULL) + memset(ae->ObjList[CurIndex],0,sizeof(NEWOBJECT)); + } + // If memory has been allocated calculate coordinates for object + if (ae->ObjList[CurIndex] != NULL) + { + x1 = (j * BITMAP_WIDTH) + (BITMAP_WIDTH/2); + y1 = (i * BITMAP_WIDTH) + (BITMAP_WIDTH/2); + ae->ObjList[CurIndex]->x = x1; // Store x,y position + ae->ObjList[CurIndex]->y = y1; + ae->ObjList[CurIndex]->mPos = pos; // Store map position + ae->ObjList[CurIndex]->Active = 1; // Indicates object is active + } + } + } + } + } + +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKLDBMP.C b/ack_lib/ACKLDBMP.C new file mode 100644 index 0000000..d0cd911 --- /dev/null +++ b/ack_lib/ACKLDBMP.C @@ -0,0 +1,327 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +char *GetExtent(char *s); +UCHAR *AckReadiff(char *s); +UCHAR *AckReadPCX(char *s); +short BlankSlice(short,UCHAR *); + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Determines if the column of the bitmap contains all transparent colors +// or not. If so then it is marked to be skipped during the draw phase. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short BlankSlice(short col,UCHAR *bmp) +{ + short i,pos; + +pos = col * 64; +for (i = 0; i < 64; i++) + { + if (bmp[pos++]) + return(1); + } + +return(0); +} + + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Loads a bitmap of different formats based on the setting of bmLoadType +// in the ACKENG interface structure. The bitmap loaded is placed into +// either the wall bitmap array or the object array based on the value +// of BitmapType passed to this function. +// BitmapName can be either a filename or an index into the currently +// open resource file. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckLoadBitmap(ACKENG *ae,short BitmapNumber,short BitmapType,char *BitmapName) +{ + short handle,bFlag; + short x,y,bLen; + short sPos,dPos; + UCHAR ch; + UCHAR *buf; + UCHAR *bmp; + UCHAR *bmpFlags; + +bFlag = 0; + +bLen = BITMAP_SIZE + BITMAP_WIDTH; +buf = NULL; + +if (ae->bmLoadType == BMLOAD_BBM) + buf = AckReadiff(BitmapName); + +if (ae->bmLoadType == BMLOAD_GIF) + buf = AckReadgif(BitmapName); + +if (ae->bmLoadType == BMLOAD_PCX) + buf = AckReadPCX(BitmapName); + +if (buf == NULL) + return(ERR_LOADINGBITMAP); + +x = (*(short *)buf); +y = (*(short *)&buf[2]); +if ((x*y) != BITMAP_SIZE) + { + AckFree(buf); + return(ERR_INVALIDFORM); + } + +memmove(buf,&buf[4],BITMAP_SIZE); +bFlag = 1; + +bmp = AckMalloc(bLen); +if (bmp == NULL) + { + AckFree(buf); + return(ERR_NOMEMORY); + } + +if (BitmapType == TYPE_WALL) + { + ae->bMaps[BitmapNumber] = bmp; + } + +if (BitmapType == TYPE_OBJECT) + { + ae->oMaps[BitmapNumber] = bmp; + } + +if (!bFlag) + { + handle = _lopen(BitmapName,OF_READ); + if (handle < 1) + { + AckFree(buf); + AckFree(bmp); + return(ERR_BADFILE); + } + + read(handle,buf,4); // Skip width and height for now + read(handle,buf,BITMAP_SIZE); + _lclose(handle); + } + +for (y = 0; y < BITMAP_HEIGHT; y++) + { + sPos = y; + dPos = y * BITMAP_WIDTH; + for (x = 0; x < BITMAP_WIDTH; x++) + { + ch = buf[sPos]; + bmp[dPos++] = ch; + sPos += BITMAP_WIDTH; + } + } + + +bmpFlags = &bmp[BITMAP_SIZE]; +memset(bmpFlags,0,BITMAP_WIDTH); + +for (x = 0; x < BITMAP_WIDTH; x++) + { + if (!BlankSlice(x,bmp)) + bmpFlags[x] = 1; + + } + +AckFree(buf); +return(0); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Returns a pointer to the file extent +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +char *GetExtent(char *s) +{ + char *e; + +e = strchr(s,'.'); +if (e == NULL) + return(s); +e++; + +return(e); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Calls AckLoadBitmap with the TYPE_WALL flag set so the bitmap is placed +// in the wall array. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckLoadWall(ACKENG *ae,short WallNumber,char *bmFileName) +{ +return( AckLoadBitmap(ae,WallNumber,TYPE_WALL,bmFileName) ); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Calls AckLoadBitmap with the TYPE_OBJECT flag set so the bitmap is +// placed in the object array. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckLoadObject(ACKENG *ae,short BmpNumber,char *bmFileName) +{ +return( AckLoadBitmap(ae,BmpNumber,TYPE_OBJECT,bmFileName) ); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Creates an object structure. This function MUST be called before the +// object data can be initialized in the NEWOBJECT structure. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckCreateObject(ACKENG *ae,short ObjNumber) +{ + +if (ae->ObjList[ObjNumber] == NULL) + { + ae->ObjList[ObjNumber] = (NEWOBJECT *)AckMalloc(sizeof(NEWOBJECT)); + + if (ae->ObjList[ObjNumber] == NULL) + return(ERR_NOMEMORY); + + memset(ae->ObjList[ObjNumber],0,sizeof(NEWOBJECT)); + } + +if (ObjNumber >= ae->MaxObjects) + ae->MaxObjects = ObjNumber + 1; + +return(0); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Sets an object up into one of the predefined phase types (CREATE,DESTROY, +// etc.). Moveable objects are placed into a special list that is used +// later in the drawing phase. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckSetObjectType(ACKENG *ae,short oNum,short oType) +{ + short i,j,result = 0; + OBJSEQ *os; + + +switch (oType) + { + case NO_CREATE: + os = &ae->ObjList[oNum]->Create; + break; + + case NO_DESTROY: + os = &ae->ObjList[oNum]->Destroy; + break; + + case NO_WALK: + os = &ae->ObjList[oNum]->Walk; + break; + + case NO_ATTACK: + os = &ae->ObjList[oNum]->Attack; + break; + + case NO_INTERACT: + os = &ae->ObjList[oNum]->Interact; + break; + + default: + result = ERR_BADOBJTYPE; + break; + } + +if (!result) + { + ae->ObjList[oNum]->CurrentBitmaps = (UCHAR *)&os->bitmaps; + ae->ObjList[oNum]->Flags = os->flags; + ae->ObjList[oNum]->Sides = os->bmSides; + ae->ObjList[oNum]->BitmapsPerView = os->bmBitmapsPerView; + ae->ObjList[oNum]->CurrentBm = 0; + ae->ObjList[oNum]->Maxbm = os->MaxBitmaps; + ae->ObjList[oNum]->CurrentType = oType; + ae->ObjList[oNum]->aFactor = os->AngleFactor; + } + +if (ae->ObjList[oNum]->Flags & OF_MOVEABLE) + { + j = 0; + for (i = 0; i < MoveObjectCount; i++) + { + if (MoveObjectList[i] == oNum) + { + j = 1; + break; + } + } + + if (!j) + MoveObjectList[MoveObjectCount++] = oNum; + + i = (ae->ObjList[oNum]->y & 0xFFC0) + (ae->ObjList[oNum]->x >> 6); + ObjGrid[i] = 0; + } + +return(result); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Fills in the object structure with a communication structure passed +// by the application. This allows the application to setup the fields +// such as number of sides to an object, what bitmaps are displayed for +// each side, etc. The object structures are defined in ACK3D.H +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckSetupObject(ACKENG *ae,short oNum,short oType,OBJSEQ *os) +{ + short result = 0; + +if (ae->ObjList[oNum] == NULL) + return(ERR_BADOBJECTNUM); + +if (os->flags & OF_MULTIVIEW) + { + os->AngleFactor = INT_ANGLE_360 / os->bmSides; + } + +switch (oType) + { + case NO_CREATE: + memmove(&ae->ObjList[oNum]->Create,os,sizeof(OBJSEQ)); + break; + + case NO_DESTROY: + memmove(&ae->ObjList[oNum]->Destroy,os,sizeof(OBJSEQ)); + break; + + case NO_WALK: + memmove(&ae->ObjList[oNum]->Walk,os,sizeof(OBJSEQ)); + break; + + case NO_ATTACK: + memmove(&ae->ObjList[oNum]->Attack,os,sizeof(OBJSEQ)); + break; + + case NO_INTERACT: + memmove(&ae->ObjList[oNum]->Interact,os,sizeof(OBJSEQ)); + break; + + default: + result = ERR_BADOBJTYPE; + break; + } + +if (!result && ae->ObjList[oNum]->CurrentBitmaps == NULL) + result = AckSetObjectType(ae,oNum,oType); + +return(result); +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKOBJ.C b/ack_lib/ACKOBJ.C new file mode 100644 index 0000000..17b6048 --- /dev/null +++ b/ack_lib/ACKOBJ.C @@ -0,0 +1,470 @@ +// This source file contains the internal functions needed to add objects +// to the slice structures as a view is being built. +// (c) 1995 ACK Software (Lary Myers) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" // Main ACK-3D internal and interface data structures +#include "ackeng.h" // Internal data structures and constants +#include "ackext.h" // Defines external (global) variables + +extern short gWinStartX; // Global variables to define the left and +extern short gWinEndX; // right edge of the viewport +// A function pointer to refernce the actual routine used to build a wall slice +extern void (*WallMaskRtn)(void); + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal function called by FindObject(). Your programs may call this +// function if they need to calculate the angle between two points. dx and +// dy represent the deltas between the two points. (i.e. dx = x1 - x and +// dy = y1 - y) +// +// Quadrants +// 2 | 3 If the object is in quadrants 0 or 2, we need +// ---+--- to add the resulting angle to the quad value less +// 1 | 0 than the resulting angle. If the object is in +// quadrants 1 or 3, we need to subtract the +// resulting angle from the next higher quadrant +// value. This is because quads 1 and 3 are negative +// values returned from arctan, while 0 and 2 are +// positive. +// +// The angle between the two points is determined by using the formula: +// tan (angle) = dy/dx. The look-up table LongTanTable[] is used to +// access tangent values of angles. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckGetObjectAngle(long dx,long dy) +{ + short i,quadrant,objAngle; + short Beg; + long avalue; + +if (dx == 0 || dy == 0) // Test to see if angle is 0, 90, 180, or 270 + { + if (dx == 0) // Distance is directly up or down + { + if (dy < 0) // Distance is straight up + return(INT_ANGLE_270); + return(INT_ANGLE_90); + } + if (dx < 0) // dy = 0; distance is directly left or right + return(INT_ANGLE_180); + return(0); + } + +// Need to determine which quadrant is involved +quadrant = 0; // Set to quad 0 as default +if (dx < 0 && dy > 0) // We're in quad 1 + quadrant = INT_ANGLE_180; +else + { + if (dx < 0 && dy < 0) // We're in quad 2 + quadrant = INT_ANGLE_270; + else + { + if (dx > 0 && dy < 0) // We're in quad 3 + quadrant = INT_ANGLE_360; + } + } + +// Get the absolute values to use for our ratio +if (dy < 0) + dy = -dy; +if (dx < 0) + dx = -dx; + +//======================================================================= +// Next we need to convert dy into the same fixed point representation +// as used in our tangent table. Then, we divide dy by dx (rise/run) +// to get the ratio so we can determine the tangent of the angle between +// the two pints. We use the ratio to search the tangent table +// and the index that is returned tells us what the actual angle is. +// We only need to check angles from 0 to 90 degrees. Later, the angle +// will be adjusted to take into account which quadrant we are in. +//======================================================================= +dy = dy << FP_SHIFT; // Make the dividend the same fixed point as the table +avalue = dy / dx; // Get our ratio to search for + // This ratio tells us the tangent of the angle +if (LongTanTable[INT_ANGLE_90-1] <= avalue) // Angle is 89 degrees + return(INT_ANGLE_90-1); +objAngle = 0; // Initialize angle to 0 + +//============================================================================= +// Now we use a binary lookup trick to speed up the search. This invloves +// a test to see if the angle is between o and 45 degrees or between 45 and +// 90 degrees. Then, we search the list sequentially to find the first value +// higher than our ratio. +//============================================================================= +Beg = 0; // Assume midpoint between 0 and 45 degrees +if (LongTanTable[INT_ANGLE_45] < avalue) + { + if (LongTanTable[360] < avalue) + Beg = 360; // Use angle of 360 + else + Beg = INT_ANGLE_45; // Midpoint between 45 and 90 degrees + } + +// Loop to check the tan table and find the correct angle +for (i = Beg; i < INT_ANGLE_90; i++) + { + if (LongTanTable[i] > avalue) // We've passed by the angle + { + objAngle = i - 1; // Get the correct angle + break; + } + } + +if (objAngle < 0) // Adjust for angle=0 + objAngle = 0; + +//============================================================================ +// Now we adjust the resulting angle based on the quadrant. If we are in +// quad 0 we do nothing. If we're in quads 1 and 3 we subtract the angle from +// the next higher quad angle. If we're in quad 2 we add the angle to the next +// lower quad angle to get the actual angle (0-1800) between the points. +//============================================================================ +if (quadrant) + { + if (quadrant != INT_ANGLE_270) + objAngle = quadrant - objAngle; + else + objAngle += INT_ANGLE_180; + } + +// Returns the angle between the two points. This value is mainly used for +// determining the angle between the POV and an object, but it could +// be used for generic purposes as well. +return(objAngle); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal function that returns the square root of a long value. +// This function is called by Find)bject(). +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short long_sqrt(long v) +{ + short i; + unsigned short result,tmp; + unsigned long low,high; + +if (v <= 1L) return((unsigned)v); // Value is less than 1; return value +low = v; // Initialize starting variables +high = 0L; +result = 0; + +for (i = 0; i < 16; i++) + { + result += result; + high = (high << 2) | ((low >>30) & 0x3); + low <<= 2; // Shift left by 2 + tmp = result + result + 1; + if (high >= tmp) + { + result++; + high -= tmp; + } + } +return(result); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal function called by AckBuildView() which checks the list of +// objects found during the ray cast process and places the object slices +// into the wall slices. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void FindObject(void) +{ + short i,j,StartX,EndX; + short oCount; + short minAngle,maxAngle,objAngle; + short objColumn; + USHORT distance; + long dx,dy; + short count,SaveCenter; + short ObjNum,oQuad,pQuad,numsides,afact; + short NewX,NewY,LightFlag; + short MaxOpp,Column,ColBeg,ColEnd; + short wt,ObjIndex; + short vidwt,vidht,hoff; + short MaxObjs; + short SliceLen; + USHORT BmpColumn; + long xp,yp; + short wht; + UCHAR *wall,*ScreenBuffer; + UCHAR *pTable; + UCHAR **omaps; + SLICE *sa,*sa2,*saNext; + UCHAR *bmpFlags; + NEWOBJECT **oList; + NEWOBJECT *oPtr; + +if (FoundObjectCount) // Make sure objects were found during ray casting + { + oList = &aeGlobal->ObjList[0]; // Get pointer to the array of objects + StartX = gWinStartX; // Starting column of view + EndX = gWinEndX; // Ending column of view + + minAngle = PlayerAngle - (INT_ANGLE_32 + 10); // Starting angle of view + if (minAngle < 0) // Check for wrap-around at angle 0 + minAngle += INT_ANGLE_360; + maxAngle = PlayerAngle + (INT_ANGLE_32 + 10); // Ending angle of view + if (maxAngle >= INT_ANGLE_360) // Check for wrap-around at angle 360 + maxAngle -= INT_ANGLE_360; + + TotalObjects = 0; // Stores nmber of objects in view + SliceLen = sizeof(SLICE) - 9; // Amount of slice we'll move later + +// Loop and process each object in the view. This invloves setting up +// a few arrays to store the object number, the distance from the player +// to the object, the viewing angle, and the view column where the object +// will be displayed. + + for (oCount = 0; oCount < FoundObjectCount; oCount++) + { + i = ObjectsSeen[oCount]; // Get index to possible object + oPtr = oList[i]; // Pointer to object structure + if (oPtr == NULL) // Make sure it's a valid object + continue; + if (!oPtr->Active) // Make sure it's visible + continue; + + dx = oPtr->x - xPglobal; // Get the delta x,y between the + dy = oPtr->y - yPglobal; // object and the player + + // Calculate the angle the object is relative to the player + if ((objAngle = AckGetObjectAngle(dx,dy)) < 0) + continue; // Negative angle means it can't be seen + + // Here we determine if the POV is looking toward the right or + // the left and the actual column of the view (from 0 to view width) + // where the object would be seen. + if (minAngle > maxAngle) // If looking towards the right + { + if (objAngle >= minAngle) // Cal. view column of object + objColumn = objAngle - minAngle; + else + objColumn = (objAngle+INT_ANGLE_360) - minAngle; + } + else + { + objColumn = objAngle - minAngle; // Calc. view column of object + } + + // Get the distance to the object + distance = long_sqrt((dx * dx) + (dy * dy)); + // No need to check further if it's too far away + if (distance >= MaxDistance) + continue; + + // Place the objects in the correct order so further ones are behind + j = TotalObjects; // Current number of objects we've found + if (j) + { + // Sort the objects found by distance so that further ones + // are drawn BEFORE closer ones. + for (count = 0; count < TotalObjects; count++) + { + if (distance <= ObjRelDist[count]) + { + for (j = TotalObjects; j > count; j--) + { + ObjRelDist[j] = ObjRelDist[j-1]; + ObjNumber[j] = ObjNumber[j-1]; + ObjColumn[j] = ObjColumn[j-1]; + ObjAngle[j] = ObjAngle[j-1]; + } + j = count; + count = TotalObjects; + } + } + } + + // Hold onto relavant data for the object found + ObjNumber[j] = i; // Store the object number + ObjRelDist[j] = distance; // Store the distance to the object + ObjColumn[j] = objColumn; // Store view column where object resides + ObjAngle[j] = objAngle; // Store the viewing angle + TotalObjects++; // Bump the count of objects in view + ObjRelDist[TotalObjects] = 0L; // Set to relative dist. in next object to 0 + } + + // Didn't find any objects on the above pass, so we're done + if (!TotalObjects) + return; + + omaps = &aeGlobal->oMaps[0]; // Bitmaps used for objects + pQuad = PlayerAngle / INT_ANGLE_45; // Quadrant POV is facing + +// Check each object in the list to be displayed and get the object's +// bitmap. Also, calulate the width and height of the object. +// This loop also checks to see if an object has multiple sides +// and it determines which bitmap should be used to display the object. + for (i = 0; i < TotalObjects; i++) + { + ObjIndex = ObjNumber[i]; // Actual object found + oPtr = oList[ObjIndex]; // Pointer to object structure + if (oPtr == NULL) // Again check for a null object + continue; + // Current bitmap for the object (this number can change if the + // object is animated) + ObjNum = oPtr->CurrentBitmaps[oPtr->CurrentBm]; + distance = ObjRelDist[i]; // Get relative distance to object + // Make sure distance is within a reasonable entry in our + // pre-calculated table + if (distance >= (MAX_DISTANCE - 10)) + distance = MAX_DISTANCE-11; + + // Get the width of the object + wt = DistanceTable[distance]; // Adjust the width using the distance + // Keep the width of the object reasonable + if (wt > 300) // The object is too wide + continue; // Skip over + if (wt < 6) wt = 6; // Adjust if too small + + // Get the scale factor which was pre-calculated based on + // distance in AckInitialize() function + yp = AdjustTable[distance]; + xp = 0; // First col of the object to display + + NewX = ObjColumn[i]; // View column where object resides + + // Check if object has multiple sides. If so we need to determine + // the correct bitmap to display based on the angle between the + // POV and the object. We'll perform a trick here by breaking down + // the problem into quadrants and then use the quadrant to determine + // which side we're facing. The object itself is facing a certain + // angle (stored in the Dir field of the object structure) so this + // needs to be taken into account as well. + if (oPtr->Flags & OF_MULTIVIEW) + { + afact = oPtr->aFactor; // Get the angles per side of object + numsides = oPtr->Sides; // Get total sides for this object + pQuad = ObjAngle[i] / afact; // Get the quadrant from POV to object + oQuad = oPtr->Dir / afact; // Get the quadrant it wants to face + + // The difference between the POV-Object angle and the angle the + // object is facing determines the actual side of the object that + // can currently be seen + j = (pQuad - oQuad) + (numsides >> 1); + + // Check for wrap-around and keep within range + if (j >= numsides) + j -= numsides; + // Check wrap-around in both directions + if (j < 0) + j += numsides; + + // Calculate which bitmap set we should use (each side could + // have multiple bitmaps for animation) + j *= oPtr->BitmapsPerView; + j += oPtr->CurrentBm; + // Get the actual bitmap for this side and animation + ObjNum = oPtr->CurrentBitmaps[j]; + } + + // Done processing multiple sides. Next, find the + // ending column based on the starting column plus the scaled + // width of the object. + ColEnd = NewX + wt; + // Finally get the pointer to the actual bitmap + wall = omaps[ObjNum]; + // Pick up the transparent flags at end of bitmap + bmpFlags = &wall[BITMAP_SIZE]; + j = distance; + + // Loop from starting column to ending column and fold in the + // object into the appropriate slice structure. + for (Column = NewX - wt; Column < ColEnd; Column++) + { + // Make sure column is within view width + if (Column >= StartX && Column <= EndX) + { + // Scale bitmap column down from fixed point + BmpColumn = xp >> FP_SHIFT; + if (bmpFlags[BmpColumn]) // If transparent column + goto keepgoing; // Ouch! But it works + + j = distance; + // Account for fisheye effect + dy = ViewCosTable[Column] >> 2; + dx = distance * dy; + // Now we strip off somemore decimal points and check round-up + dy = dx >> 12; + if (dx - (dy << 12) >= 4096) + dy++; + if (dy > 32L) + j = dy; + + // Now we pick up the slice for this column and insert sort + // the object slice based on the distance. This allows objects + // to appear between walls at various distances, behind + // transparent walls, and so on. + sa = &Slice[Column]; // Access the corresponding slice + if (sa->Active) // Multiple slices for this column? + { + while (sa != NULL) + { + if (j <= sa->Distance) + { + sa2 = sa; + while (sa2->Next != NULL) // Go to end of slices + sa2 = sa2->Next; + saNext = sa2->Prev; + while (sa2 != sa) // Move slice down to create + { // a space for new slice + memcpy(sa2,saNext,sizeof(SLICE)-9); + sa2->Active = saNext->Active; + sa2 = sa2->Prev; + saNext = saNext->Prev; + } + // Fill in the slice structure with the + // info about the object + sa->Distance = distance; + sa->bNumber = ObjNum; + sa->bColumn = BmpColumn; + sa->bMap = omaps; + sa->Active = 1; + sa->Type = ST_OBJECT; + sa->Fnc = WallMaskRtn; + break; + } + if (!sa->Active) + break; + sa = sa->Next; + } + } + else // Only one slice is used for this column (typical) + { + if (j <= sa->Distance) // Only put it in if object is + { // closer than current slice + sa->Active = 1; + saNext = sa->Next; + memcpy(saNext,sa,sizeof(SLICE)-9); + sa->Distance = distance; + sa->bColumn = BmpColumn; + sa->bNumber = ObjNum; + sa->bMap = omaps; + sa->Type = ST_OBJECT; + sa->Fnc = WallMaskRtn; + saNext->Active = 0; + } + } + } + keepgoing: + xp += yp; // Advance the next column to display (scaling) + } + } + } +} +// **** End of Source **** diff --git a/ack_lib/ACKOVER.C b/ack_lib/ACKOVER.C new file mode 100644 index 0000000..38bbab8 --- /dev/null +++ b/ack_lib/ACKOVER.C @@ -0,0 +1,73 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Creates an overlay buffer that contains non-transparent information +// about the image. Position and length of the non-transparent areas is +// stored for later processing after the drawing phase. Theoretically the +// amount of information stored in the overlay buffer could exceed the +// actual size of the image. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckCreateOverlay(ACKENG *ae,UCHAR *sBuf) +{ + USHORT bPos,vPos,vLen; + USHORT len,sPos,sPos1; + +vLen = (ae->WinEndY - ae->WinStartY) * BYTES_PER_ROW; +vPos = ae->WinStartY * BYTES_PER_ROW; +bPos = 0; +sPos = vPos; + +while (vLen > 0) + { + if (sBuf[sPos]) + { + sPos1 = sPos; + while (vLen > 0 && sBuf[sPos1++]) + vLen--; + + len = (sPos1 - sPos) - 1; + (*(short *)&ae->ScreenBuffer[bPos]) = len; + bPos += 2; + (*(short *)&ae->ScreenBuffer[bPos]) = sPos; + bPos += 2; + memmove(&ae->ScreenBuffer[bPos],&sBuf[sPos],len); + bPos += len; + sPos = sPos1; + } + else + { + sPos++; + vLen--; + } + } + +(*(short *)&ae->ScreenBuffer[bPos]) = 0; +bPos += 2; + +ae->OverlayBuffer = AckMalloc(bPos); + +if (ae->OverlayBuffer != NULL) + { + memmove(ae->OverlayBuffer,ae->ScreenBuffer,bPos); + return(0); + } + +return(ERR_NOMEMORY); +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKPCX.C b/ack_lib/ACKPCX.C new file mode 100644 index 0000000..6a9dabd --- /dev/null +++ b/ack_lib/ACKPCX.C @@ -0,0 +1,162 @@ +// This source file contains the functions needed to read in PCX files. +// (c) 1995 ACK Software (Lary Myers) +#include +#include +#include +#include +#include +#include + +//typedef unsigned short USHORT; + +#include "ack3d.h" // Main ACK-3D internal and interface data structures +#include "ackeng.h" // Internal data structures and constants +#include "ackext.h" // Defines external (global) variables + +typedef struct +{ + char manufacturer; // Always set to 0 + char version; // Always 5 for 256-color files + char encoding; // Always set to 1 + char bits_per_pixel; // Should be 8 for 256-color files + short xmin,ymin; // Coordinates for top left corner + short xmax,ymax; // Width and height of image + short hres; // Horizontal resolution of image + short vres; // Vertical resolution of image + char palette16[48]; // EGA palette; not used for 256-color files + char reserved; // Reserved for future use + char color_planes; // Color planes + short bytes_per_line; // Number of bytes in 1 line of pixels + short palette_type; // Should be 2 for color palette + char filler[58]; // Reserved +} PcxHeader; + +typedef struct +{ + PcxHeader hdr; // Header information + UCHAR *bitmap; // The bitmap data + UCHAR pal[768]; // Color palette for the bitmap data + unsigned short imagebytes,width,height; // Size of the bitmap +} PcxFile; + +#define PCX_MAX_SIZE 64000L +enum {PCX_OK,PCX_NOMEM,PCX_TOOBIG,PCX_NOFILE}; + +enum {NORMAL,RLE}; +//enum {FALSE,TRUE}; + + PcxFile pcxGlobal; // data structure for reading PCX files + +extern unsigned char colordat[]; + +//============================================================================= +// This routine loads a 256 color PCX file. The file can be a standalone +// PCX file or it can be combined with a resource. If the data is part +// of a resource, the rshandle flag will be set. The bitmap data is read +// into a buffer that is the size of the bitmap + 4 bytes. The first 4 +// bytes in the buffer contain the width and height of the bitmap. +//============================================================================= +unsigned char *AckReadPCX(char *filename) +{ + long i; + int mode=NORMAL,nbytes; + char abyte,*p; + short handle; + PcxFile *pcx; + +pcx = &pcxGlobal; +// Open the file since no resource is open. +if (!rsHandle) + { + handle = _lopen(filename,OF_READ); // Open the file for reading + if (handle == HFILE_ERROR) // Make sure file is opened + { + ErrorCode = ERR_BADFILE; + return NULL; + } + } +else // Use the resource instead + { + handle = rsHandle; // Use the handle to the resource file + // Move to the location in the resource where the data is stored + _llseek(handle,rbaTable[(ULONG)filename],SEEK_SET); + } + + +_lread(handle,&pcx->hdr,sizeof(PcxHeader)); // Read in the header data +pcx->width=1+pcx->hdr.xmax-pcx->hdr.xmin; // Store width and height +pcx->height=1+pcx->hdr.ymax-pcx->hdr.ymin; +// Store number of bytes used for image +pcx->imagebytes=(unsigned int)(pcx->width*pcx->height); + +// Make sure bitmap is correct size +if (pcx->imagebytes > PCX_MAX_SIZE) + { + if (!rsHandle) + _lclose(handle); + ErrorCode = ERR_INVALIDFORM; + return(NULL); + } + +// Allocate size for bitmap. 4 extra bytes are included to give +// room to store bitmap width and height info. +pcx->bitmap=(char*)AckMalloc(pcx->imagebytes+4); + +if (pcx->bitmap == NULL) // Make sure memory is allocated + { + if (!rsHandle) + _lclose(handle); + ErrorCode = ERR_NOMEMORY; + return(NULL); + } +p=&pcx->bitmap[4]; // Get address of data area + +// Loop and read in pixel data for bitmap +// Uses RLE decompression +for (i=0;iimagebytes;i++) + { + if (mode == NORMAL) // Normal color read mode + { + _lread(handle,&abyte,1); // Read in pixel value from file + if ((unsigned char)abyte > 0xbf) // Value read > 191 + { + nbytes=abyte & 0x3f; // Get the RLE counter + _lread(handle,&abyte,1); + if (--nbytes > 0) // Is counter greater than 1? + mode=RLE; // Yes, we're in RLE mode + } + } + else if (--nbytes == 0) // When counter down to 0 + mode=NORMAL; // return to color read mode + *p++=abyte; // Store pixel value + } + +// Get palette from PCX file, 256 color palette store 768 bytes from +// end of file. For a resource file we need to find the position where +// the next file starts and then backup 768 bytes +if (rsHandle) + _llseek(handle,rbaTable[(ULONG)(filename + 1)]-768L,SEEK_CUR); +else + _llseek(handle,-768L,SEEK_END); + +// Store the palette data in our global colordat array +_lread(handle,colordat,768); +p=colordat; +for (i=0;i<768;i++) // bit shift palette + *p++ = *p >> 2; + +if (!rsHandle) // Close pcx file if not using a resource + _lclose(handle); + +// Add in bitmap width and height to first 4 bytes of buffer +p = pcx->bitmap; +(*(short *)p) = pcx->width; +p += sizeof(short); +(*(short *)p) = pcx->height; + +return(pcx->bitmap); // return bitmap buffer +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKPOV.C b/ack_lib/ACKPOV.C new file mode 100644 index 0000000..3d9ceee --- /dev/null +++ b/ack_lib/ACKPOV.C @@ -0,0 +1,671 @@ +// Source file ACKPOV.C - Player and Object Movement routines +// (c) 1995 ACK Software (Lary Myers) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +//************************************************************************ +// Internal function called by AckMovePOV(). Checks the passed X and Y +// coordinates of the player against the object coordinates to see if the player will +// encouner an object. +//************************************************************************ +short AckCheckObjPosn(short xPlayer,short yPlayer, short oIndex) +{ + short i,result,maxObj; + short MapPosn; + NEWOBJECT **oList; + NEWOBJECT *oPtr; + +result = POV_NOTHING; // Initialize to nothing found +MapPosn = (yPlayer & 0xFFC0) + (xPlayer >> 6); // Calculate grid square the player will be in +maxObj = aeGlobal->MaxObjects; // Total number of objects used +oList = &aeGlobal->ObjList[0]; // Reference the list of objects + +for (i = 0; i < maxObj; i++) // Loop and check each object in the list + { + oPtr = oList[i]; // Point to current object + if (oPtr == NULL) // No object here; skip to next object in list + continue; + + if (!oPtr->Active || oPtr->Flags & OF_PASSABLE) // Object is not active or is passable + continue; // Skip to next object in list + + if (MapPosn == oPtr->mPos && i != oIndex) // Object is found in the player's grid position + { + LastObjectHit = i; // Store the number of the object found + return(POV_OBJECT); // Return flag to indicate an object is found + } + } +return(result); +} + +//************************************************************************ +// Internal function called by AckMovePOV() to see if a wall or object is located in +// the player's current grid square. This function checks for walls or objects in the +// x-plane using the xGrid array. The bitmap code for the wall or object is returned. +// A value returned of 0 indicates that no wall or object is present or that the wall +// is passable. +//************************************************************************ +USHORT GetWallX(short mPos) +{ + USHORT mCode; + +mCode = xGridGlobal[mPos]; // Get bitmap code at specified map position +if (mCode & WALL_TYPE_PASS) // Passable walls can be walked through + mCode = 0; +return(mCode); +} + +//************************************************************************ +// Internal function called by AckMovePOV() to see if a wall or object is located in +// the player's current grid square. This function checks for walls or objects in the +// y-plane using the yGrid array. The bitmap code for the wall or object is returned. +// A value returned of 0 indicates that no wall or object is present or that the wall +// is passable. +//************************************************************************ +USHORT GetWallY(short mPos) +{ + USHORT mCode; + +mCode = yGridGlobal[mPos]; // Get bitmap code at specified map position +if (mCode & WALL_TYPE_PASS) // Passable walls can be walked through + mCode = 0; +return(mCode); +} + +//************************************************************************ +// Moves the POV based on Angle for Amount. After moving but prior to +// returning the position of the POV is check for collisions. +//************************************************************************ +short AckMovePOV(short Angle,short Amount) +{ + short x1,y1,HitResult; // New coordinate position + short xp,yp; // Starting player coordinates + short xLeft,xRight,yTop,yBottom; // Coordinates for grid square + short mPos; // Map position for xGrid[], yGrid[] + USHORT mCodeX,mCodeY; // Return codes for x,y wall arrays + +HitResult = POV_NOTHING; // We haven't hit anything yet +xp = aeGlobal->xPlayer; // Get the current x,y player coordinates +yp = aeGlobal->yPlayer; + +xLeft = xp & 0xFFC0; // Determine coordinates of the boundaries +yTop = yp & 0xFFC0; // of the grid square we're in. +xRight = xLeft + GRID_SIZE; +yBottom = yTop + GRID_SIZE; + +// Calculate the x,y distance of movement using the angle and distance +// x1,y1 = the new coordinate position of the player. +x1 = xp + (long)((CosTable[Angle] * Amount) >> FP_SHIFT); +y1 = yp + (long)((SinTable[Angle] * Amount) >> FP_SHIFT); +// Calculate current map position for the xGrid[] and yGrid[] arrays +mPos = yTop + (xp >> 6); // Current Map Posn + +// It's time to see what happens when we move +if (x1 < xp) // Are we moving left? + { + if (GetWallX(mPos)) // Wall found in current square (left edge) + { + if (x1 < xLeft || abs(x1-xLeft) < 28) // We crossed the wall or we're too close + { + x1 = xp; // Use the previous x position + HitResult = POV_SLIDEX; // We're possibly sliding along the left x wall + } + } + } + +if (x1 > xp) // Are we moving right? + { + if (GetWallX(mPos+1)) // Wall found in current square (right edge) + { + if (x1 > xRight || abs(xRight-x1) < 28) // We crossed the wall or we're too close + { + x1 = xp; // Use the previous x position + HitResult = POV_SLIDEX; // We're possibly sliding along the right x wall + } + } + } + +if (y1 < yp) // Are we moving up? + { + if (GetWallY(mPos)) // Wall found in current square (top edge) + { + if (y1 < yTop || abs(y1-yTop) < 28) // We crossed the wall or we're too close + { + y1 = yp; // Use the previous y position + HitResult = POV_SLIDEY; // We're possibly sliding along the top wall + } + } + } + +if (y1 > yp) // Are we moving down? + { + if (GetWallY(mPos+GRID_WIDTH)) // Wall found in current square (bottom edge) + { + if (y1 > yBottom || abs(yBottom-y1) < 28) // We crossed the wall or we're too close + { + y1 = yp; // Use the previous y position + HitResult = POV_SLIDEY; // We're sliding along the bottom wall + } + } + } + +// A wall or object hasn't been hit yet--we must look further. +// The current grid sqaure will be divided into four regions: +// A = top left; B = top right; C = bottom left; D = bottom right +// Each of these regions will be checked to see if the player's new position (x1,y1) +// is close to a wall or object that borders one of these regions. +// Each grid square is 64x64 units, so each region to check is 32x32 units. +if (!HitResult) + { // Check region A--top left area of grid + if (y1 < (yTop+32)) // New y position falls in top half + { + if (x1 < (xLeft+32)) // New x position falls in left half + { + mCodeX = GetWallX(mPos-GRID_WIDTH); // Check adjacent x wall (to left) + mCodeY = GetWallY(mPos-1); // Check adjacent y wall (above) + + if (mCodeX && y1 < (yTop+28)) // Adjacent x wall found and new y coord + { // is within 28 units + if (x1 < (xLeft+28)) // New x coord. is within 28 units of edge + { + if (xp > (xLeft+27)) // Previous x position was outside range + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 < (xLeft+28)) // Adjacent y wall found and new x coord. + { // is within 28 units + if (y1 < (yTop+28)) // New y coord. is within 28 units of edge + { + if (yp > (yTop+27)) // Previous y position was outside range + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + } + } + } + // Check region B--top right area + if (x1 > (xRight-32) && !HitResult)// New x is at top right + { + mCodeX = GetWallX(mPos+1-GRID_WIDTH); // Check adjacent x wall (to right) + mCodeY = GetWallY(mPos+1); // Check adjacent y wall (above) + + if (mCodeX && y1 < (yTop+28)) // Adjacent x wall found + { + if (x1 > (xRight-28)) + { + if (xp < (xRight-27)) + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 > (xRight-28)) // Adjacent y wall found + { + if (y1 < (yTop+28)) + { + if (yp > (yTop+27)) + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + } + } + } + } + // Check region C--bottom left area + if (y1 > (yTop+32) && !HitResult) // We are below upper half of square + { + if (x1 < (xLeft+32)) // and on the left half of square + { + mCodeX = GetWallX(mPos+GRID_WIDTH); // Check adjacent x wall (to left) + mCodeY = GetWallY(mPos-1+GRID_WIDTH); // Check adjacent y wall (below) + + if (mCodeX && y1 > (yBottom-28)) // Adjacent x wall found + { + if (x1 < (xLeft+28)) + { + if (xp > (xLeft+27)) + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 < (xLeft+28)) // Adjacent y wall found + { + if (y1 > (yBottom-28)) + { + if (yp < (yBottom-27)) + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + } + } + } + // Check region D--bottom right area + if (x1 > (xRight-32) && !HitResult) // Check right side of square + { + mCodeX = GetWallX(mPos+1+GRID_WIDTH); // Check adjacent x wall (to right) + mCodeY = GetWallY(mPos+1+GRID_WIDTH); // Check adjacent y wall (below) + + if (mCodeX && y1 > (yBottom-28)) // Adjacent x wall found + { + if (x1 > (xRight-28)) + { + if (xp < (xRight-27)) + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 > (xRight-28)) // Adjacent y wall found + { + if (y1 > (yBottom-28)) + { + if (yp < (yBottom-27)) + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + } + } + } + } + } + +if (AckCheckObjPosn(x1,y1,0)) // We've hit an object--not a wall + return(POV_OBJECT); + +if (HitResult == POV_SLIDEX && y1 == yp) // We've hit an x wall and we're not sliding + HitResult = POV_XWALL; + +if (HitResult == POV_SLIDEY && x1 == xp) // We've hit a y wall and we're not sliding + HitResult = POV_YWALL; + +aeGlobal->xPlayer = x1; // Update player's new x,y position +aeGlobal->yPlayer = y1; + +return(HitResult); +} + + +//************************************************************************ +// Moves an object based on Angle and Amount then checks for collision +// with other objects AND the POV. +//************************************************************************ +short AckMoveObjectPOV(short ObjIndex,short Angle,short Amount) +{ + short xp,yp,x1,y1,HitResult,oNum; + USHORT mCodeX,mCodeY; + short xLeft,xRight,yTop,yBottom,mPos; + short MapPosn,PlayerPosn; + NEWOBJECT **oList; + NEWOBJECT *oPtr; + +oList = &aeGlobal->ObjList[0]; // Reference the start of the object list +oPtr = oList[ObjIndex]; // Set a pointer to the object being moved + +if (oPtr == NULL) // no object is available to move; we're done + return(0); + +xp = oPtr->x; // Get the current x,y coordinate of the object +yp = oPtr->y; +// Calculate the new x,y, cordinates of the object (after moving) +x1 = xp + (short)((CosTable[Angle] * Amount) >> FP_SHIFT); +y1 = yp + (short)((SinTable[Angle] * Amount) >> FP_SHIFT); + +xLeft = xp & 0xFFC0; // Determine the coordinates of the grid square the +xRight = xLeft + GRID_SIZE - 1; // object is currently in +yTop = yp & 0xFFC0; +yBottom = yTop + GRID_SIZE - 1; +mPos = yTop + (xp >> 6); // Calculate the map position of the grid square the object is in +MapPosn = (y1 & 0xFFC0) + (x1 >> 6); // Calculate the map position of the grid square the + // object is moving to + +// Check to see if the object will encouner another object while moving +oNum = AckCheckObjPosn(x1,y1,ObjIndex); +if (oNum > 0) // Yes, return falg to indicate object found + return(POV_OBJECT); + +HitResult = POV_NOTHING; // Nothing found yet, initialize flag +if (x1 < xp) // Are we moving left? + { + if (GetWallX(mPos)) // Wall found in current square (left edge) + { + if (x1 < xLeft || abs(x1-xLeft) < 28) // We crossed the wall or we're too close + { + x1 = xp; // Use the previous x position + HitResult = POV_SLIDEX; // We're possibly sliding along the left x wall + } + } + } + +if (x1 > xp) // Are we moving right? + { + if (GetWallX(mPos+1)) // Wall found in current square (right edge) + { + if (x1 > xRight || abs(xRight-x1) < 28) // We crossed the wall or we're too close + { + x1 = xp; // Use the previous x position + HitResult = POV_SLIDEX; // We're possibly sliding along the right x wall + } + } + } + +if (y1 < yp) // Are we moving up? + { + if (GetWallY(mPos)) // Wall found in current square (top edge) + { + if (y1 < yTop || abs(y1-yTop) < 28) // We crossed the wall or we're too close + { + y1 = yp; // Use the previous y position + HitResult = POV_SLIDEY; // We're possibly sliding along the top wall + } + } + } + +if (y1 > yp) // Are we moving down? + { + if (GetWallY(mPos+GRID_WIDTH)) // Wall found in current square (bottom edge) + { + if (y1 > yBottom || abs(yBottom-y1) < 28) // We crossed the wall or we're too close + { + y1 = yp; // Use the previous y position + HitResult = POV_SLIDEY; // We're sliding along the bottom wall + } + } + } + +if (!HitResult) // Nothing hit yet, look further + { + if (y1 < (yTop+32)) // We are above upper half of square + { + if (x1 < (xLeft+32)) // and on the left half of square + { + mCodeX = GetWallX(mPos-GRID_WIDTH); // Check adjacent x wall (to left) + mCodeY = GetWallY(mPos-1); // Check adjacent y wall (above) + + if (mCodeX && y1 < (yTop+28)) // Adjacent x wall found and new y coord + { // is within 28 units + if (x1 < (xLeft+28)) // New x coord. is within 28 units of edge + { + if (xp > (xLeft+27)) // Previous x position was outside range + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 < (xLeft+28)) // Adjacent y wall found and new x coord. + { // is within 28 units + if (y1 < (yTop+28)) // New y coord. is within 28 units of edge + { + if (yp > (yTop+27)) // Previous y position was outside range + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + } + } + } + + if (x1 > (xRight-32) && !HitResult) // New x is at top right + { + mCodeX = GetWallX(mPos+1-GRID_WIDTH); // Check adjacent x wall (to right) + mCodeY = GetWallY(mPos+1); // Check adjacent y wall (above) + + if (mCodeX && y1 < (yTop+28)) // Adjacent x wall found + { + if (x1 > (xRight-28)) + { + if (xp < (xRight-27)) + { + x1 = xp; // Use previous x position + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 > (xRight-28)) // Adjacent y wall found + { + if (y1 < (yTop+28)) + { + if (yp > (yTop+27)) + { + y1 = yp; // Use previous y position + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; + HitResult = POV_SLIDEX; + } + } + } + } + } + + if (y1 > (yTop+32) && !HitResult) // We are below upper half of square + { + if (x1 < (xLeft+32)) // and on the left half of square + { + mCodeX = GetWallX(mPos+GRID_WIDTH); + mCodeY = GetWallY(mPos-1+GRID_WIDTH); + + if (mCodeX && y1 > (yBottom-28)) + { + if (x1 < (xLeft+28)) + { + if (xp > (xLeft+27)) + { + x1 = xp; + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 < (xLeft+28)) + { + if (y1 > (yBottom-28)) + { + if (yp < (yBottom-27)) + { + y1 = yp; + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; + HitResult = POV_SLIDEX; + } + } + } + } + + if (x1 > (xRight-32) && !HitResult) // on right side of square + { + mCodeX = GetWallX(mPos+1+GRID_WIDTH); + mCodeY = GetWallY(mPos+1+GRID_WIDTH); + + if (mCodeX && y1 > (yBottom-28)) + { + if (x1 > (xRight-28)) + { + if (xp < (xRight-27)) + { + x1 = xp; + HitResult = POV_SLIDEX; + } + else + { + y1 = yp; + HitResult = POV_SLIDEY; + } + } + } + + if (mCodeY && x1 > (xRight-28)) + { + if (y1 > (yBottom-28)) + { + if (yp < (yBottom-27)) + { + y1 = yp; + HitResult = POV_SLIDEY; + } + else + { + x1 = xp; + HitResult = POV_SLIDEX; + } + } + } + } + } + } + +oPtr->x = x1; // Update the new x,y coordinates for the object +oPtr->y = y1; +oPtr->mPos = MapPosn; // Update the grid map position for the object + +PlayerPosn = (aeGlobal->yPlayer & 0xFFC0) + (aeGlobal->xPlayer >> 6); +if (MapPosn == PlayerPosn) + return(POV_PLAYER); + +return(HitResult); +} + +//************************************************************************ +// Checks the list of objects used in an application and sets up the current bitmap for +// each object that can be animated. Object animation is performed by displaying +// different bitmaps for an object in sequence. +//************************************************************************ +void AckCheckObjectMovement(void) +{ + short i,maxObj; + short dx; + NEWOBJECT **oList; + NEWOBJECT *oPtr; + +maxObj = aeGlobal->MaxObjects; // Get the number of objects used +oList = &aeGlobal->ObjList[0]; // Reference the list of objects + +for (i = 1; i < maxObj; i++) // Loop to check each object in the list + { + oPtr = oList[i]; // Access current object in list + if (oPtr == NULL) // No object here; skip + continue; + + if (!oPtr->Active) // Object is not active; skip + continue; + + if (!oPtr->Speed) // Object has no speed setting; skip + continue; + + if (!(oPtr->Flags & OF_ANIMATE)) // Object is not set up for animation + continue; + + dx = oPtr->CurrentBm + 1; // Use the next bitmap + if (dx >= oPtr->Maxbm) // We're at the end of the list of bitmaps + { + if (oPtr->Flags & OF_ANIMONCE) // Object should only be animated once + { + oPtr->Flags &= ~OF_ANIMATE; // Reset flags to indicate that we're done + oPtr->Flags |= OF_ANIMDONE; // animating the object + dx = oPtr->CurrentBm; // Keep current bitmap number + } + else + dx = 0; // Start at the beginning of the set of bitmaps + } + oPtr->CurrentBm = dx; // Store the next bitmap as the current one + } +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKRAY.C b/ack_lib/ACKRAY.C new file mode 100644 index 0000000..e1f216f --- /dev/null +++ b/ack_lib/ACKRAY.C @@ -0,0 +1,630 @@ +//****************** ( Animation Construction Kit 3D ) ********************** +// Ray Casting Routines +// CopyRight (c) 1993 Author: Lary Myers +//*************************************************************************** + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +extern short ViewAngle; + +//************************************************************************** +// +//************************************************************************** +short ObjectExist(UCHAR onum) +{ + short i; + short result = 0; + +if (!FoundObjectCount) + return(result); + +for (i = 0; i < FoundObjectCount; i++) + { + if (ObjectsSeen[i] == onum) + return(1); + } + +return(result); +} + + long x_xPos,x_yPos,x_xNext,x_yNext; + long y_xPos,y_yPos,y_xNext,y_yNext; + +//************************************************************************* +// +//************************************************************************* +void xRaySetup(void) +{ + +x_yNext = yNextTable[ViewAngle]; // PreCalc'd value of BITMAP_WIDTH * Tan(angle) + +if (ViewAngle > INT_ANGLE_270 || ViewAngle < INT_ANGLE_90) + { + x_xPos = xBegGlobal + BITMAP_WIDTH; // Looking to the right + x_xNext = BITMAP_WIDTH; // Positive direction + } +else + { + x_xPos = xBegGlobal; // Looking to the left + x_xNext = -BITMAP_WIDTH; // Negative direction + x_yNext = -x_yNext; + } + +// Calculate the Y coordinate for the current square +x_yPos = (((long)x_xPos - (long)xPglobal) * LongTanTable[ViewAngle]) + yPglobalHI; + +} + +//************************************************************************** +// +//************************************************************************** +UINT xRayCast(void) +{ + UINT Color; + short i,j,mx,my; + short TablePosn; + short MapPosn,CurPosn; + short xBeg; + short BitmapColumn; + short xCenter,yCenter,xAdj; + short ObjPosn; + short oBegX,oBegY; + long xd,yd,sy; + long ObjDist; + +while (1) + { + if (x_xPos < 0 || x_xPos > GRID_XMAX || + x_yPos < 0 || x_yPos > GRID_YMAXLONG) + break; + +//************* Fixed point Y/64 * 64 X / 64 ********** + MapPosn = ((x_yPos >> FP_SHIFT) & 0xFFC0) + (x_xPos >> 6); + + + if ((Color = ObjGrid[MapPosn]) != 0) + { + Color &= 0x7F; + if (!ObjectExist(Color)) + ObjectsSeen[FoundObjectCount++] = Color; + + } + + + +// Check to see if a wall is being struck by the ray + if ((Color = xGridGlobal[MapPosn]) != 0) + { + xMapPosn = MapPosn; // Hold onto the map location + iLastX = x_xPos; + LastY1 = x_yPos; + if ((Color & 0xFF) == DOOR_XCODE) // Is this a door? + { + yd = ((x_yPos >> FP_SHIFT) & GRID_MASK); // Get the left side + xd = yd + BITMAP_WIDTH; // And the right side + ObjDist = (x_yPos + (x_yNext >> 1)) >> FP_SHIFT; // Calc door distance + if (ObjDist < yd || ObjDist > xd) // Is door visible? + { + x_xPos += x_xNext; // Nope, continue casting + x_yPos += x_yNext; // the ray as before + continue; + } + + LastY1 = x_yPos + (x_yNext >> 1); // Adjust the X,Y values so + iLastX += (x_xNext >> 1); // the door is halfway in sq. + } + + if (Color & DOOR_TYPE_SECRET) + { + if (xSecretColumn != 0) + { + sy = xSecretColumn * LongTanTable[ViewAngle]; + ObjDist = (x_yPos + sy) >> FP_SHIFT; + yd = ((x_yPos >> FP_SHIFT) & GRID_MASK); // Get the left side + xd = yd + BITMAP_WIDTH; // And the right side + if (ObjDist < yd || ObjDist > xd) // Is door visible? + { + x_xPos += x_xNext; // Nope, continue casting + x_yPos += x_yNext; // the ray as before + continue; + } + LastY1 = x_yPos + sy; + iLastX += xSecretColumn; + } + } + + return(Color); + } + + x_xPos += x_xNext; // Next X coordinate (fixed at 64 or -64) + x_yPos += x_yNext; // Next calculated Y coord for a delta of X + } + +return(0); // Return that no wall was found +} + + +//************************************************************************* +// +//************************************************************************* +UINT OldxRay(void) +{ + UINT Color; + short i,j,mx,my; + short TablePosn; + short MapPosn,CurPosn; + short xBeg; + long xPos,xNext; + short BitmapColumn; + short xCenter,yCenter,xAdj; + short ObjPosn; + short oBegX,oBegY; + long yPos; + long yNext; + long xd,yd,sy; + long ObjDist; + +yNext = yNextTable[ViewAngle]; // PreCalc'd value of BITMAP_WIDTH * Tan(angle) + +if (ViewAngle > INT_ANGLE_270 || ViewAngle < INT_ANGLE_90) + { + xPos = xBegGlobal + BITMAP_WIDTH; // Looking to the right + xNext = BITMAP_WIDTH; // Positive direction + } +else + { + xPos = xBegGlobal; // Looking to the left + xNext = -BITMAP_WIDTH; // Negative direction + yNext = -yNext; + } + +// Calculate the Y coordinate for the current square +yPos = (((long)xPos - (long)xPglobal) * LongTanTable[ViewAngle]) + yPglobalHI; + +while (1) + { + if (xPos < 0 || xPos > GRID_XMAX || + yPos < 0 || yPos > GRID_YMAXLONG) + break; + +//************* Fixed point Y/64 * 64 X / 64 *********** + MapPosn = ((yPos >> FP_SHIFT) & 0xFFC0) + (xPos >> 6); + + + if ((Color = ObjGrid[MapPosn]) != 0) + { + Color &= 0x7F; + if (!ObjectExist(Color)) + ObjectsSeen[FoundObjectCount++] = Color; + + } + + + +// Check to see if a wall is being struck by the ray + if ((Color = xGridGlobal[MapPosn]) != 0) + { + xMapPosn = MapPosn; // Hold onto the map location + iLastX = xPos; + LastY1 = yPos; + if ((Color & 0xFF) == DOOR_XCODE) // Is this a door? + { + yd = ((yPos >> FP_SHIFT) & GRID_MASK); // Get the left side + xd = yd + BITMAP_WIDTH; // And the right side + ObjDist = (yPos + (yNext >> 1)) >> FP_SHIFT; // Calc door distance + if (ObjDist < yd || ObjDist > xd) // Is door visible? + { + xPos += xNext; // Nope, continue casting + yPos += yNext; // the ray as before + continue; + } + + LastY1 = yPos + (yNext >> 1); // Adjust the X,Y values so + iLastX += (xNext >> 1); // the door is halfway in sq. + } + + if (Color & DOOR_TYPE_SECRET) + { + if (xSecretColumn != 0) + { + sy = xSecretColumn * LongTanTable[ViewAngle]; + ObjDist = (yPos + sy) >> FP_SHIFT; + yd = ((yPos >> FP_SHIFT) & GRID_MASK); // Get the left side + xd = yd + BITMAP_WIDTH; // And the right side + if (ObjDist < yd || ObjDist > xd) // Is door visible? + { + xPos += xNext; // Nope, continue casting + yPos += yNext; // the ray as before + continue; + } + LastY1 = yPos + sy; + iLastX += xSecretColumn; + } + } + + return(Color); + } + + xPos += xNext; // Next X coordinate (fixed at 64 or -64) + yPos += yNext; // Next calculated Y coord for a delta of X + } + +return(0); // Return that no wall was found +} + + +//************************************************************************* +// +//************************************************************************* +void yRaySetup(void) +{ + +y_xNext = xNextTable[ViewAngle]; // Pre-calc'd value of BITMAP_WIDTH / tan(angle) + +if (ViewAngle < INT_ANGLE_180) + { + y_yPos = yBegGlobal + BITMAP_WIDTH; // Looking down + y_yNext = BITMAP_WIDTH; // Positive direction + } +else + { + y_yPos = yBegGlobal; // Looking up + y_yNext = -BITMAP_WIDTH; // Negative direction + y_xNext = -y_xNext; + } + +// Calculate the X coordinate for the current square +y_xPos = (((long)y_yPos - (long)yPglobal) * LongInvTanTable[ViewAngle]) + xPglobalHI; + +} + +//************************************************************************* +// +//************************************************************************* +UINT yRayCast(void) +{ + UINT Color; + short i,j,mx,my; + short MapPosn; + short yBeg; + short BitmapColumn; + short xCenter,yCenter,yAdj; + short ObjPosn; + short oBegX; + long xd,yd,ObjDist,sx; + +while (1) + { + if (y_xPos < 0 || y_xPos > GRID_XMAXLONG || + y_yPos < 0 || y_yPos > GRID_YMAX) + break; + +//********** Y/64 * 64 Fixed point and /64 ***** + MapPosn = (y_yPos & 0xFFC0) + (y_xPos >> (FP_SHIFT+6)); + + + if ((Color = ObjGrid[MapPosn]) != 0) + { + Color &= 0x7F; + if (!ObjectExist(Color)) + ObjectsSeen[FoundObjectCount++] = Color; + + } + + +// Check for a wall being struck + if ((Color = yGridGlobal[MapPosn]) != 0) + { + yMapPosn = MapPosn; // Hold onto map position + LastX1 = y_xPos; + iLastY = y_yPos; + + if ((Color & 0xFF) == DOOR_YCODE) // Is this a door? + { + yd = ((y_xPos >> FP_SHIFT) & GRID_MASK); // Calc top side of square + xd = yd + BITMAP_WIDTH; // And bottom side of square + ObjDist = (y_xPos + (y_xNext >> 1)) >> FP_SHIFT; + if (ObjDist < yd || ObjDist > xd) // Is door visible? + { + y_xPos += y_xNext; // No, continue on with ray cast + y_yPos += y_yNext; + continue; + } + + LastX1 = y_xPos + (y_xNext >> 1); // Adjust coordinates so door is + iLastY += (y_yNext >> 1); // Halfway into wall + } + + if (Color & DOOR_TYPE_SECRET) + { + if (ySecretColumn != 0) + { + sx = ySecretColumn * LongInvTanTable[ViewAngle]; + ObjDist = (y_xPos + sx) >> FP_SHIFT; + yd = ((y_xPos >> FP_SHIFT) & GRID_MASK); // Get the top side + xd = yd + BITMAP_WIDTH; // And the bottom side + if (ObjDist < yd || ObjDist > xd) // Is door visible? + { + y_xPos += y_xNext; // Nope, continue casting + y_yPos += y_yNext; // the ray as before + continue; + } + LastX1 = y_xPos + sx; + iLastY += ySecretColumn; + } + + } + + return(Color); + } + + y_xPos += y_xNext; // Next calculated X value for delta Y + y_yPos += y_yNext; // Next fixed value of 64 or -64 + + } + +return(0); // Return here if no Y wall is found +} + +//************************************************************************* +// +//************************************************************************* +UINT OldyRay(void) +{ + UINT Color; + short i,j,mx,my; + short MapPosn; + short yBeg; + long yPos,yNext; + short BitmapColumn; + short xCenter,yCenter,yAdj; + short ObjPosn; + short oBegX; + long xPos; + long xNext; + long xd,yd,ObjDist,sx; + +xNext = xNextTable[ViewAngle]; // Pre-calc'd value of BITMAP_WIDTH / tan(angle) + +if (ViewAngle < INT_ANGLE_180) + { + yPos = yBegGlobal + BITMAP_WIDTH; /* Looking down */ + yNext = BITMAP_WIDTH; /* Positive direction */ + } +else + { + yPos = yBegGlobal; /* Looking up */ + yNext = -BITMAP_WIDTH; /* Negative direction */ + xNext = -xNext; + } + +/* Calculate the X coordinate for the current square */ +xPos = (((long)yPos - (long)yPglobal) * LongInvTanTable[ViewAngle]) + xPglobalHI; + +while (1) + { + if (xPos < 0 || xPos > GRID_XMAXLONG || + yPos < 0 || yPos > GRID_YMAX) + break; + +/*********** Y/64 * 64 Fixed point and /64 ******/ +// MapPosn = ((yPos / BITMAP_WIDTH) * GRID_WIDTH) + (xPos >> (FP_SHIFT+BITMAP_SHIFT)); +// MapPosn = ((yPos & GRID_MASK) >> 1) + (xPos >> (FP_SHIFT+BITMAP_SHIFT)); + MapPosn = (yPos & 0xFFC0) + (xPos >> (FP_SHIFT+6)); + + + if ((Color = ObjGrid[MapPosn]) != 0) + { + Color &= 0x7F; + if (!ObjectExist(Color)) + ObjectsSeen[FoundObjectCount++] = Color; + + } + + +/** Check for a wall being struck **/ + if ((Color = yGridGlobal[MapPosn]) != 0) + { + yMapPosn = MapPosn; /* Hold onto map position */ + LastX1 = xPos; + iLastY = yPos; + + if ((Color & 0xFF) == DOOR_YCODE) /* Is this a door? */ + { + yd = ((xPos >> FP_SHIFT) & GRID_MASK); /* Calc top side of square */ + xd = yd + BITMAP_WIDTH; /* And bottom side of square */ + ObjDist = (xPos + (xNext >> 1)) >> FP_SHIFT; + if (ObjDist < yd || ObjDist > xd) /* Is door visible? */ + { + xPos += xNext; /* No, continue on with ray cast */ + yPos += yNext; + continue; + } + + LastX1 = xPos + (xNext >> 1); /* Adjust coordinates so door is */ + iLastY += (yNext >> 1); /* Halfway into wall */ + } + + if (Color & DOOR_TYPE_SECRET) + { + if (ySecretColumn != 0) + { + sx = ySecretColumn * LongInvTanTable[ViewAngle]; + ObjDist = (xPos + sx) >> FP_SHIFT; + yd = ((xPos >> FP_SHIFT) & GRID_MASK); /* Get the top side */ + xd = yd + BITMAP_WIDTH; /* And the bottom side */ + if (ObjDist < yd || ObjDist > xd) /* Is door visible? */ + { + xPos += xNext; /* Nope, continue casting */ + yPos += yNext; /* the ray as before */ + continue; + } + LastX1 = xPos + sx; + iLastY += ySecretColumn; + } + + } + + return(Color); + } + + xPos += xNext; /* Next calculated X value for delta Y */ + yPos += yNext; /* Next fixed value of 64 or -64 */ + + } + +return(0); /* Return here if no Y wall is found */ +} + + +/**************************************************************************** +** ** +****************************************************************************/ +UINT xRayMulti(UINT MinDist,short MinHeight) +{ + UINT Color; + short i,j,mx,my; + short TablePosn; + short MapPosn,CurPosn; + short xBeg; + long xPos,xNext; + short BitmapColumn; + short xCenter,yCenter,xAdj; + short ObjPosn; + short oBegX,oBegY; + long yPos; + long yNext; + long xd,yd,sy; + long ObjDist; + +yNext = yNextTable[ViewAngle]; /* PreCalc'd value of BITMAP_WIDTH * Tan(angle) */ + +if (ViewAngle > INT_ANGLE_270 || ViewAngle < INT_ANGLE_90) + { + xPos = xBegGlobal + BITMAP_WIDTH; /* Looking to the right */ + xNext = BITMAP_WIDTH; /* Positive direction */ + } +else + { + xPos = xBegGlobal; /* Looking to the left */ + xNext = -BITMAP_WIDTH; /* Negative direction */ + yNext = -yNext; + } + +/* Calculate the Y coordinate for the current square */ +yPos = (((long)xPos - (long)xPglobal) * LongTanTable[ViewAngle]) + yPglobalHI; + +while (1) + { + if (xPos < 0 || xPos > GRID_XMAX || + yPos < 0 || yPos > GRID_YMAXLONG) + break; + +/************** Fixed point Y/64 * 64 X / 64 ***********/ + MapPosn = ((yPos >> FP_SHIFT) & 0xFFC0) + (xPos >> 6); + +/* Check to see if a wall is being struck by the ray */ + if ((Color = xGridGlobal[MapPosn]) & WALL_TYPE_MULTI) + { + if ((Color & 0xFF) > MinHeight) + { + xd = xPos - xPglobal; + yd = InvCosTable[ViewAngle] >> 4; + if (MinDist < ((xd * yd) >> 10)) + { + xMapPosn = MapPosn; /* Hold onto the map location */ + iLastX = xPos; + LastY1 = yPos; + return(Color); + } + } + } + + xPos += xNext; /* Next X coordinate (fixed at 64 or -64) */ + yPos += yNext; /* Next calculated Y coord for a delta of X */ + } + +return(0); /* Return that no wall was found */ +} + +/**************************************************************************** +** ** +****************************************************************************/ +UINT yRayMulti(UINT MinDist,short MinHeight) +{ + UINT Color; + short i,j,mx,my; + short MapPosn; + short yBeg; + long yPos,yNext; + short BitmapColumn; + short xCenter,yCenter,yAdj; + short ObjPosn; + short oBegX; + long xPos; + long xNext; + long xd,yd,ObjDist,sx; + +xNext = xNextTable[ViewAngle]; /* Pre-calc'd value of BITMAP_WIDTH / tan(angle) */ + +if (ViewAngle < INT_ANGLE_180) + { + yPos = yBegGlobal + BITMAP_WIDTH; /* Looking down */ + yNext = BITMAP_WIDTH; /* Positive direction */ + } +else + { + yPos = yBegGlobal; /* Looking up */ + yNext = -BITMAP_WIDTH; /* Negative direction */ + xNext = -xNext; + } + +/* Calculate the X coordinate for the current square */ +xPos = (((long)yPos - (long)yPglobal) * LongInvTanTable[ViewAngle]) + xPglobalHI; + +while (1) + { + if (xPos < 0 || xPos > GRID_XMAXLONG || + yPos < 0 || yPos > GRID_YMAX) + break; + +/*********** Y/64 * 64 Fixed point and /64 ******/ + MapPosn = (yPos & 0xFFC0) + (xPos >> (FP_SHIFT+6)); + +/** Check for a wall being struck **/ + if ((Color = yGridGlobal[MapPosn]) & WALL_TYPE_MULTI) + { + if ((Color & 0xFF) > MinHeight) + { + xd = yPos - yPglobal; + yd = InvCosTable[ViewAngle] >> 4; + if (MinDist < ((xd * yd) >> 10)) + { + yMapPosn = MapPosn; /* Hold onto the map location */ + LastX1 = xPos; + iLastY = yPos; + return(Color); + } + } + } + + xPos += xNext; /* Next calculated X value for delta Y */ + yPos += yNext; /* Next fixed value of 64 or -64 */ + + } + +return(0); /* Return here if no Y wall is found */ +} + + \ No newline at end of file diff --git a/ack_lib/ACKRTN.ASM b/ack_lib/ACKRTN.ASM new file mode 100644 index 0000000..24f5350 --- /dev/null +++ b/ack_lib/ACKRTN.ASM @@ -0,0 +1,989 @@ + + locals + IDEAL + JUMPS + P386 + P387 ; Allow 386 processor + + + MASM + .MODEL FLAT,STDCALL ;32-bit OS/2 model + +.data + +COLOR dw ? +RETVAL dw ? +MAPPOSN dw ? +XBEG dd ? +XD dd ? +YD dd ? +YTEMP dd ? +SY dd ? +SX dd ? + + + + .CODE + IDEAL + + include "ackrtn.inc" + +SC_INDEX EQU 03C4h ; Sequencer Controller access +HIGH_ADDR equ 80h ; High byte of screen offset +GC_INDEX equ 3ceh ;Graphics Controller Index register +CRTC_INDEX equ 3d4h ;CRT Controller Index register +MAP_MASK equ 2 ;Map Mask register index in SC +MEMORY_MODE equ 4 ;Memory Mode register index in SC +MAX_SCAN_LINE equ 9 ;Maximum Scan Line reg index in CRTC +START_ADDRESS_HIGH equ 0ch ;Start Address High reg index in CRTC +UNDERLINE equ 14h ;Underline Location reg index in CRTC +MODE_CONTROL equ 17h ;Mode Control register index in CRTC +READ_MAP equ 4 ;Read Map register index in GC +GRAPHICS_MODE equ 5 ;Graphics Mode register index in GC +MISCELLANEOUS equ 6 ;Miscellaneous register index in GC +SCREEN_WIDTH equ 320 ;# of pixels across screen +SCREEN_HEIGHT equ 400 ;# of scan lines on screen + + + extrn _ViewAngle:word + extrn _ScreenOffset:word + extrn _xPglobal:dword + extrn _yPglobal:dword + extrn _xBegGlobal:dword + extrn _yBegGlobal:dword + extrn _aeGlobal:dword + extrn _xGridGlobal:dword + extrn _yGridGlobal:dword + extrn _xPglobalHI:dword + extrn _yPglobalHI:dword + extrn _rbaTable:dword + extrn _rsHandle:word + extrn _LastX1:dword + extrn _LastY1:dword + extrn _iLastX:dword + extrn _iLastY;dword + extrn _MaxDistance:word + extrn _ErrorCode:word + extrn _xMapPosn:dword + extrn _yMapPosn:dword + extrn _Grid:dword + extrn _ObjGrid:dword + + extrn _xSecretmPos:word + extrn _xSecretmPos1:word + extrn _xSecretColumn:word + + extrn _ySecretmPos:word + extrn _ySecretmPos1:word + extrn _ySecretColumn:word + + extrn _TotalSecret:word + extrn _ViewColumn:word + extrn _SinTable:dword + extrn _CosTable:dword + extrn _LongTanTable:dword + extrn _LongInvTanTable:dword + extrn _InvCosTable:byte + extrn _InvSinTable:byte + extrn _LongCosTable:dword + extrn _ViewCosTable:dword + extrn _xNextTable:dword + extrn _yNextTable:dword + + extrn _LastMapPosn:word + extrn _LastObjectHit:word + extrn _TotalObjects:word + extrn _FoundObjectCount:word + extrn _ObjectsSeen:byte + extrn _MoveObjectCount:word + extrn _MoveObjectList:byte + extrn _ObjNumber:byte + extrn _ObjRelDist:byte + extrn _ObjColumn:byte + + extrn _x_xPos:dword + extrn _x_yPos:dword + extrn _x_xNext:dword + extrn _x_yNext:dword + extrn _y_xPos:dword + extrn _y_yPos:dword + extrn _y_xNext:dword + extrn _y_yNext:dword + + extrn _Floorscr:dword + extrn _Floors1:dword + extrn _Floors2:dword + extrn _FloorscrTop:dword + extrn _Floorptr2:dword + extrn _Floorht:word + extrn _Floorwt:word + + extrn _gScrnBuffer:dword + extrn _gWinStartY:word + extrn _gWinStartX:word + extrn _gWinWidth:word + extrn _gWinHeight:word + + extrn _gWinFullWidth:word + extrn _gWinDWORDS:dword + extrn _gWinStartOffset:dword + + extrn _VidSeg:dword + extrn _AckKeys:byte + + extrn _OurDataSeg:word + + extrn _HitMap:byte + + ACKPUBS AckInkey + ACKPUBS AckKbdInt + ACKPUBS AckPutVideo + ACKPUBS AckGetVideo + ACKPUBS AckCopyToVideo + ACKPUBS AckSetPalette + ACKPUBS AckDisplayScreen + ACKPUBS AckInitVideoSelector + ACKPUBS AckGetIntVector + ACKPUBS AckSetIntVector + ACKPUBS AckSetVGAmode + ACKPUBS AckSetTextMode + ACKPUBS AckDrawPage + ACKPUBS xRaySetup + ACKPUBS yRaySetup + ACKPUBS xRayCast + ACKPUBS yRayCast + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Checks for a keystroke and returns 0 if none, else scan/char in AX +; This routine should NOT be used if the keyboard vector has been changed +; to the AckKbdInt routine below. +; unsigned short AckInkey(void); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckInkey + push ebx + mov ax,0100h + int 16h + mov bx,0 + jz ackinkey_10 + xor ax,ax + int 16h + mov bx,ax + +ackinkey_10: + mov ax,bx + pop ebx + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Interrupt 9 keyboard handler. Places keys into the keyboard array so they +; can be checked by the application. +; Do NOT call this routine directly!!! +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckKbdInt + push eax + push ebx + push esi + push ds + xor ebx,ebx + in al,60h + mov bl,al + in al,61h + mov ah,al + or al,80h + out 61h,al + mov al,ah + out 61h,al + mov al,20h + out 20h,al + mov ax,cs:[word ptr _OurDataSeg] + mov ds,ax + mov esi,offset _AckKeys + mov eax,ebx + and eax,127 + mov [byte ptr esi+eax],1 + test ebx,128 + jz aki_10 + mov [byte ptr esi+eax],0 + +aki_10: + pop ds + pop esi + pop ebx + pop eax + iretd + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Older version which now simply calls AckDrawPage. +; void AckDisplayScreen(void); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckDisplayScreen + push ebp + ACKCALL AckDrawPage + pop ebp + xor eax,eax + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Puts a single byte onto the video +; void AckPutVideo(unsigned int offset,unsigned char color); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckPutVideo + push ebp + mov ebp,esp + push es + push ebx + push edi + mov bx,[word ptr _VidSeg+2] + mov es,bx + movzx edi,[word ptr _VidSeg] + add edi,[dword ptr ebp+8] + mov edx,[ebp+12] + mov [edi],dl + pop edi + pop ebx + pop es + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Retrieves a single byte from the video +; unsigned char AckGetVideo(unsigned int offset); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckGetVideo + push ebp + mov ebp,esp + push ds + push ebx + push esi + movzx esi,[word ptr _VidSeg] + add esi,[dword ptr ebp+8] + mov ax,[word ptr _VidSeg+2] + mov ds,ax + mov al,[esi] + mov ah,0 + pop esi + pop ebx + pop ds + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Copies a block from the data segment buffer to the video screen +; void AckCopyToVideo(unsigned int SrcOff,unsigned int len); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckCopyToVideo + push ebp + mov ebp,esp + push es + push ebx + push ecx + push edx + push esi + push edi + mov esi,[ebp+8] + mov ecx,[ebp+12] + movzx edi,[word ptr _VidSeg] + mov ax,[word ptr _VidSeg+2] + mov es,ax + rep movsb + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop es + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Retrieves a selector to Video memory and stores it in the VidSeg global +; void AckInitVideoSelector(void); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckInitVideoSelector + push ebx + push ecx + push edx + mov ax,2 ; Allocate selector for real mode address + mov bx,0A000h ; Get Video address + int 31h + mov [word ptr _VidSeg+2],ax + mov [word ptr _VidSeg],0 + pop edx + pop ecx + pop ebx + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Retrieves a protected mode interrupt vector. +; void AckGetIntVector(int VectorNumber,int *sel,int *off); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckGetIntVector + push ebp + mov ebp,esp + push ebx + push ecx + push edx + push es + mov ebx,[ebp+8] + mov eax,204h + int 31h + mov ebx,[ebp+12] + mov [ebx],cx + mov ebx,[ebp+16] + mov [ebx],dx + pop es +; mov eax,ecx + pop edx + pop ecx + pop ebx + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Sets a protected mode interrupt handler. +; void AckSetIntVector(int VecNum,unsigned int VecSel,unsigned int VecOff); +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckSetIntVector + push ebp + mov ebp,esp + push ebx + push ecx + push edx + mov ebx,[ebp+8] + mov ecx,[ebp+12] + mov edx,[ebp+16] +;; mov cx,cs + mov eax,205h + cli + int 31h + sti + pop edx + pop ecx + pop ebx + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckSetPalette + push ebp + mov ebp,esp + push ds + push ebx + push ecx + push edx + mov esi,[ebp+8] + mov ebx,0 + mov ecx,256 + mov dx,3c8h + +asp_loop: + mov al,bl + out dx,al + inc bx + inc dx + lodsb + out dx,al + lodsb + out dx,al + lodsb + out dx,al + dec dx + loop asp_loop + + pop edx + pop ecx + pop ebx + pop ds + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckSetVGAmode + push ebx + push ecx + push edx + mov ax,0013h ;AH = 0 means mode set, AL = 13h selects + int 10h ;BIOS video interrupt + pop edx + pop ecx + pop ebx + ret + +;------------------------------------------------------------------------------ +; Uncomment the following code to setup a 320x200 modeX environment +;------------------------------------------------------------------------------ +;; Change CPU addressing of video memory to linear (not odd/even, +;; chain, or chain 4), to allow us to access all 256K of display +;; memory. When this is done, VGA memory will look just like memory +;; in modes 10h and 12h, except that each byte of display memory will +;; control one 256-color pixel, with 4 adjacent pixels at any given +;; address, one pixel per plane. +;;------------------------------------------------------------------------------ +; mov dx,SC_INDEX +; mov al,MEMORY_MODE +; out dx,al +; inc dx +; in al,dx +; and al,not 08h ;turn off chain 4 +; or al,04h ;turn off odd/even +; out dx,al +; mov dx,GC_INDEX +; mov al,GRAPHICS_MODE +; out dx,al +; inc dx +; in al,dx +; and al,not 10h ;turn off odd/even +; out dx,al +; dec dx +; mov al,MISCELLANEOUS +; out dx,al +; inc dx +; in al,dx +; and al,not 02h ;turn off chain +; out dx,al +; mov dx,SC_INDEX +; mov ax,(0fh shl 8) + MAP_MASK +; out dx,ax +; +; mov dx,CRTC_INDEX +;;------------------------------------------------------------------------------ +;; Change CRTC scanning from doubleword mode to byte mode, allowing +;; the CRTC to scan more than 64K of video data. +;;------------------------------------------------------------------------------ +; mov al,UNDERLINE +; out dx,al +; inc dx +; in al,dx +; and al,not 40h ;turn off doubleword +; out dx,al +; dec dx +; mov al,MODE_CONTROL +; out dx,al +; inc dx +; in al,dx +; or al,40h ;turn on the byte mode bit, so memory is +; ; scanned for video data in a purely +; ; linear way, just as in modes 10h and 12h +; out dx,al +; ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckSetTextMode + push ebx + push ecx + push edx + mov ax,3 + int 10h + pop edx + pop ecx + pop ebx + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckDrawPage + push esi + push edi + push ebx + push ecx + push edx + push es + push ds + +;; mov edi,[_VidSeg] + mov edi,0 + mov di,[word ptr _VidSeg] + mov esi,[_gScrnBuffer] + cmp [word ptr _gWinFullWidth],0 + jz short dp_smallscreen + + mov eax,[_gWinStartOffset] + add edi,eax + add esi,eax + mov ecx,[_gWinDWORDS] + + mov ax,[word ptr _VidSeg+2] + mov es,ax +;; mov ds,ax + + mov dx,3dah + +fp020: + in al,dx ;Wait until vertical retrace is on + test al,8 + jz fp020 + +fp030: + in al,dx ;Wait until vertical retrace is off + test al,8 + jnz fp030 + + rep movsd + + pop ds + pop es + pop edx + pop ecx + pop ebx + pop edi + pop esi + ret + +dp_smallscreen: + mov eax,[_gWinStartOffset] + add edi,eax + add esi,eax + movzx eax,[_gWinStartX] + add edi,eax + add esi,eax + mov dx,[_gWinHeight] + inc dx + movzx ebx,[_gWinWidth] + mov ebp,320 + sub ebp,ebx ;width to advance pointers + + mov ax,[word ptr _VidSeg+2] + mov es,ax +;; mov ds,ax + +dp010: + mov ecx,ebx + shr ecx,1 + rep movsw + rcl ecx,1 + rep movsb + add edi,ebp + add esi,ebp + dec dx + jnz dp010 + +dp090: + pop ds + pop es + pop edx + pop ecx + pop ebx + pop edi + pop esi + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC xRaySetup + push esi ; Save registers used + push ebx + push ecx + push edx + mov dx,[_ViewAngle] ; Get the current angle for casting + movzx esi,dx ; Use this angle for table look up + shl esi,2 ; Hold onto viewangle * 4 for table access + mov ebx,[_yNextTable] ; Base address of list of y increment coordinates + mov eax,[esi+ebx] ; Use angle as index into table + mov [dword ptr _x_yNext],eax; Store y increment value + cmp dx,INT_ANGLE_270 ; Is angle > 270 degrees? + jg short inbetween + cmp dx,INT_ANGLE_90 ; Is angle >= 90 degrees? + jge short not_inbetween +; Set up the ray for casting to the right (270 to 90 degrees) +inbetween: + mov ecx,[_xBegGlobal] ; Get left corner of grid square + add ecx,64 ; Calculate right corner + mov [dword ptr _x_xPos],ecx ; Store starting x position + mov [dword ptr _x_xNext],large 64 ; Store next x grid increment + jmp short xr_cont ; to the right (+64) +; Set up the ray for casting to the keft (90 to 270 degrees) +not_inbetween: + movzx ecx,[word ptr _xBegGlobal] ; Get left corner of grid square + mov [_x_xPos],ecx ; Store starting x position + mov [dword ptr _x_xNext],large -64 ; Store next x grid increment + ; to the left (-64) + neg [dword ptr _x_yNext] ; Negate the second y intersection + ; coordinate +xr_cont: + movzx eax,[word ptr _xPglobal] ; Get player’s x coordinate + sub ecx,eax ; x Distance from player’s position + ; to edge of grid + mov ebx,[dword ptr _LongTanTable] ; Get address of tangent table + imul ecx,[dword ptr esi+ebx] ; Tangent(angle) * Distance + add ecx,[dword ptr _yPglobalHI] ; + mov [dword ptr _x_yPos],ecx ; Store first y coordinate where + ; we hit an x boundary + pop edx ; Restore registers + pop ecx + pop ebx + pop esi + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC xRayCast + push esi ; Save registers used + push edi + push ebx + push ecx + push edx + mov esi,[_x_xPos] ; Get x,y starting position + mov edi,[_x_yPos] ; (1st grid border intersection point) + xor ecx,ecx ; Init our bitmap variable + +looptop: ; Start the ray casting loop + mov edx,esi ; Get x starting position + cmp edx,large 0 ; If it’s < 0 we’re done! + jl short getout + cmp edx,large 4096 ; If it’s > 4,096 we’re done + jge short getout + mov eax,edi ; Get y starting position + cmp eax,large 0 ; If it’s < 0 we’re done + jl short getout + cmp eax,large 010000000h ; If it’s out of range we’re done + jl short inbounds +getout: + jmp loopdone + +inbounds: ; We’re in-range; continue casting + sar eax,16 ; Scale y starting position + and eax,-64 + sar edx,6 ; Scale x starting position + add eax,edx ; Calculate grid map position + cmp eax,4095 ; Test map position to see if it’s in range + ja getout + mov [byte ptr _HitMap+eax],1 ; Record the square we cast through + mov ebx,[_xGridGlobal] ; Get pointer to x wall map + mov cx,[ebx+eax*2] ; Get the bitmap number there + jcxz next_square ; Nothing found in square--move ahead + +wall_here: ; We found something! + mov [word ptr COLOR],cx ; Save the bitmap number + mov [_xMapPosn],eax ; Save the map position + mov [dword ptr _iLastX],esi ; Save the x,y position of the grid + mov [dword ptr _LastY1],edi ; border where the hit was found + test cx,DOOR_WALL ; Did we find a door? + jz short not_door ; No match--go process a wall slice + +; At this point we’ve found a door, so we need to process it. + mov edx,edi ; Get y position + sar edx,16 ; Scale position + and edx,large 00000FFC0h + mov [dword ptr YD],edx ; YD = (yPos >> FP_SHIFT) & 0xFFC0; + ; YD is the left side of door (grid corner) + add edx,large 64 + mov [dword ptr XD],edx ; XD = YD + GRID_SIZE; + ; XD is the right side of door (grid corner) + mov eax,[dword ptr _x_yNext] ; Get y increment + sar eax,1 ; Use half of inrement + add eax,edi ; Add 1/2 y increment to y position + ; We need to calculate distance to door + mov [YTEMP],eax ; Store new y location + sar eax,16 ; We now have actual distance to door + cmp eax,[dword ptr YD] ; Is distance < YD? + jl short door_not_visible ; Process invisible door + cmp eax,[dword ptr XD] ; Is distance > XD? + jle short door_visible ; Process visible door + +door_not_visible: ; Door is invisible so skip to next square + add esi,[dword ptr _x_xNext] ; Add x,y increment to current grid border + add edi,[dword ptr _x_yNext] ; position + jmp looptop ; Cast again to check next position + +door_visible: ; Process a visible door + mov eax,[dword ptr YTEMP] ; Get y position of door + mov [dword ptr _LastY1],eax ; LastY1 = yTemp; + mov eax,[dword ptr _x_xNext] ; Adjust x,y position so that door + sar eax,1 ; is halfway in square + add [dword ptr _iLastX],eax ; iLastX += xNext >> 1; + +not_door: ; We don’t think we have a door + test cx,DOOR_TYPE_SECRET ; Check bitmap type + jz short br_no_secret ; Not a secret door + cmp [word ptr _xSecretColumn],0 + jne short secret_door ; We’ve found a secret door + +br_no_secret: + jmp short give_color ; Move on and get the wall’s color + +secret_door: ; Process a secret door + movzx eax,[word ptr _xSecretColumn] ; Get secret column location + movzx ebx,[word ptr _ViewAngle] ; Get ViewAngle to door + shl ebx,2 + add ebx,[dword ptr _LongTanTable] ; Add in address of tan table + imul eax,[dword ptr ebx] ; Look up tangent of angle + mov [dword ptr SY],eax ; SY = xSecretColumn * tan(ViewAngle) + mov ebx,edi ; Get x_yPos + add eax,ebx ; SY + x_yPos + mov [dword ptr YTEMP],eax ; Store distance to door + sar eax,16 ; eax = (x_yPos + SY) >> FP_SHIFT + ; This gives us actual distance to door + sar ebx,16 ; Now calculate the left side + and ebx,large 00000FFC0h + mov [dword ptr YD],ebx ; YD = x_yPos >> FP_SHIFT & GRID_MASK + mov ecx,ebx ; Calculate the right side (XD) + add ecx,large 64 ; XD = YD = BITMAP_WIDTH + cmp eax,ebx ; Is distance < YD? + jl short next_square + cmp eax,ecx ; Is distance <= XD? + jle short secret_is_visible + +secret_not_visible: ; Process invisible secret door + jmp short next_square + +secret_is_visible: ; Process visible secret door + mov eax,[dword ptr YTEMP] + mov [dword ptr _LastY1],eax + mov eax,[dword ptr _xSecretColumn] + add [dword ptr _iLastX],eax + jmp short give_color + +next_square: ; Didin’t find anything--go to next square + add esi,[dword ptr _x_xNext] ; Add x,y increment to current grid border + add edi,[dword ptr _x_yNext] ; position + jmp looptop ; Start over again + +loopdone: + mov [word ptr COLOR],0 ; Use bitmap value of 0 to indicate + ; nothing has been found +give_color: + movzx eax,[word ptr COLOR] ; Get color of wall + +xRayDone: + pop edx ; Restore registers + pop ecx + pop ebx + pop edi + pop esi + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC yRaySetup + push esi + push ebx + push ecx + push edx + mov dx,[word _ViewAngle] + movzx esi,dx + shl esi,2 ; // Hold onto viewangle * 4 + mov ebx,[dword ptr _xNextTable] + mov eax,[esi+ebx] + mov [dword ptr _y_xNext],eax + cmp dx,INT_ANGLE_180 + jge short y_not_inbetween + +y_inbetween: + mov ecx,[_yBegGlobal] + add ecx,64 + mov [dword ptr _y_yPos],ecx + mov [dword ptr _y_yNext],large 64 + jmp short y_yr_cont + +y_not_inbetween: + mov ecx,[_yBegGlobal] + mov [_y_yPos],ecx + mov [dword ptr _y_yNext],large -64 + neg [dword ptr _y_xNext] + +y_yr_cont: + mov eax,[_yPglobal] + sub ecx,eax + mov ebx,[dword ptr _LongInvTanTable] + imul ecx,[dword ptr esi+ebx] + add ecx,[dword ptr _xPglobalHI] + mov [dword ptr _y_xPos],ecx + pop edx + pop ecx + pop ebx + pop esi + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC yRayCast + push esi + push edi + push ebx + push ecx + push edx + + mov esi,[dword ptr _y_xPos] + mov edi,[dword ptr _y_yPos] + xor ecx,ecx + +y_looptop: +;-; mov edx,[dword ptr _y_xPos] + mov edx,esi + cmp edx,large 0 + jl short y_getout + cmp edx,large 010000000h + jge short y_getout +;-; mov eax,[_y_yPos] + mov eax,edi + cmp eax,large 0 + jl short y_getout + cmp eax,large 4096 + jl short y_inbounds + +y_getout: + jmp loopdone + +y_inbounds: + sar edx,22 + and eax,-64 + add eax,edx + cmp eax,4095 + ja y_getout + + mov [byte ptr _HitMap+eax],1 + + mov ebx,[dword ptr _yGridGlobal] + mov cx,[ebx+eax*2] +;-; test cx,cx +;-; jnz short y_wall_here +;-; jmp y_next_square + jcxz y_next_square + +y_wall_here: + mov [word ptr COLOR],cx + mov [_yMapPosn],eax + +;-; mov ebx,[dword ptr _y_xPos] + mov ebx,esi + mov [dword ptr _LastX1],ebx +;-; mov edx,[dword ptr _y_yPos] + mov edx,edi + mov [dword ptr _iLastY],edx + mov edx,ebx + + test cx,DOOR_WALL + jz short y_not_door + + sar ebx,16 + and ebx,large 00000FFC0h + mov [dword ptr YD],ebx + + add ebx,large 64 + mov [dword ptr XD],ebx + + mov eax,[dword ptr _y_xNext] + sar eax,1 + add edx,eax + mov [dword ptr YTEMP],edx + sar edx,16 + + cmp edx,[dword ptr YD] + jl short y_door_not_visible + cmp edx,[dword ptr XD] + jle short y_door_visible + +y_door_not_visible: +;-; mov eax,[dword ptr _y_xNext] +;-; add [dword ptr _y_xPos],eax +;-; mov eax,[dword ptr _y_yNext] +;-; add [dword ptr _y_yPos],eax + add esi,[dword ptr _y_xNext] + add edi,[dword ptr _y_yNext] + jmp y_looptop + +y_door_visible: + mov eax,[dword ptr YTEMP] + mov [dword ptr _LastX1],eax + mov eax,[dword ptr _y_yNext] + sar eax,1 + add [dword ptr _iLastY],eax + +y_not_door: + test cx,DOOR_TYPE_SECRET + jz short y_br_no_secret + cmp [word ptr _ySecretColumn],0 + jne short y_secret_door + +y_br_no_secret: + jmp y_give_color + +y_secret_door: + movzx eax,[word ptr _ySecretColumn] + movzx edx,[word ptr _ViewAngle] + mov ebx,[dword ptr _LongInvTanTable] + imul eax,[dword ptr ebx+edx*4] + mov [dword ptr SX],eax +;-; mov ebx,[dword ptr _y_xPos] + mov ebx,esi + add eax,ebx + mov [dword ptr YTEMP],eax + sar eax,16 + sar ebx,16 + and ebx,large 00000FFC0h + mov ecx,ebx + add ecx,large 64 + + cmp eax,ebx + jl short y_secret_not_visible + cmp eax,ecx + jle short y_secret_is_visible + +y_secret_not_visible: + jmp y_door_not_visible + +y_secret_is_visible: + mov eax,[dword ptr YTEMP] + mov [dword ptr _LastX1],eax + mov eax,[dword ptr _ySecretColumn] + add [dword ptr _iLastY],eax + jmp short y_give_color + +y_next_square: +;-; mov eax,[dword ptr _y_xNext] +;-; add [dword ptr _y_xPos],eax +;-; mov eax,[dword ptr _y_yNext] +;-; add [dword ptr _y_yPos],eax + add esi,[dword ptr _y_xNext] + add edi,[dword ptr _y_yNext] + jmp y_looptop + +y_loopdone: + mov [word ptr COLOR],0 + +y_give_color: + movzx eax,[word ptr COLOR] + +yRayDone: + pop edx + pop ecx + pop ebx + pop edi + pop esi + ret + endp + + end + + \ No newline at end of file diff --git a/ack_lib/ACKRTN.INC b/ack_lib/ACKRTN.INC new file mode 100644 index 0000000..b7233bc --- /dev/null +++ b/ack_lib/ACKRTN.INC @@ -0,0 +1,130 @@ + +_BORLANDC_ = 1 + +MACRO ACKPROC L +IFDEF _BORLANDC_ +PROC _&L NEAR +ENDIF +IFDEF _WATCOMC_ +PROC &L_ NEAR +ENDIF + ENDM + +MACRO ACKEXT L +IFDEF _BORLANDC_ + extrn _&L:NEAR +ENDIF +IFDEF _WATCOMC_ + extrn &L_:NEAR +ENDIF + ENDM + + +MACRO ACKPUBS LAB +IFDEF _BORLANDC_ + public _&LAB +ENDIF +IFDEF _WATCOMC_ + public &LAB_ +ENDIF + ENDM + +MACRO ACKCALL LAB +IFDEF _BORLANDC_ + call _&LAB +ENDIF +IFDEF _WATCOMC_ + call &LAB_ +ENDIF + ENDM + +IFDEF _BORLANDC_ +VIDSEG equ 0a0000000h +ENDIF + +IFDEF _WATCOMC_ +VIDSEG equ 0a0000h +ENDIF + +;; Equates for SLICE struct +sabMap equ 0 +samPtr equ 4 +sabNumber equ 8 +sabColumn equ 10 +saDist equ 12 +samPos equ 14 +saType equ 16 +saFnc equ 17 +saActive equ 21 +saPrev equ 22 +saNext equ 26 +saSize equ 30 ;size of SLICE structure + +; Equates for upper byte of walls +WALL_TYPE_UPPER equ 0200h +WALL_TYPE_MULTI equ 0400h +WALL_UPPER_MULTI equ 0600h +WALL_TYPE_TRANS equ 0800h +DOOR_TYPE_SECRET equ 8000h +DOOR_LOCKED equ 4000h +DOOR_TYPE_SLIDE equ 2000h +DOOR_TYPE_SPLIT equ 1000h +DOOR_WALL equ 3000h + +; Equates for LightFlag +SHADING_OFF equ 0 +SHADING_ON equ 1 + + +ST_WALL equ 1 +ST_OBJECT equ 2 + +DOOR_XCODE equ 60 +DOOR_YCODE equ 62 + +RES_LOW equ 1 + +;; Equates for SCOL struct +Vid equ 0 +Wall equ 4 +Pal equ 8 +dst equ 12 +wht equ 14 +multi equ 16 +topht equ 18 +botht equ 20 +savwht equ 22 +mulcnt equ 24 +savVid equ 26 +bNum equ 30 +scLen equ 32 ; length of structure + +; Equates for SysFlag +SYS_SOLID_BACK equ 8000h ; On if solid color bkgd vs picture +SYS_SOLID_FLOOR equ 4000h ; On if solid vs texture floor +SYS_SOLID_CEIL equ 2000h ; On if solid vs texture ceiling +SYS_NO_WALLS equ 1000h ; On if walls are NOT to display + +Color equ ebp-4 +retval equ ebp-6 +MapPosn equ ebp-8 +xBeg equ ebp-10 +;xPos equ ebp-14 +;xNext equ ebp-18 +;yPos equ ebp-22 +;yNext equ ebp-26 +xd equ ebp-30 +yd equ ebp-34 +yTemp equ ebp-38 +sy equ ebp-42 +sx equ ebp-46 + +INT_ANGLE_32 equ 160 +INT_ANGLE_90 equ 450 +INT_ANGLE_135 equ 675 +INT_ANGLE_180 equ 900 +INT_ANGLE_225 equ 1125 +INT_ANGLE_270 equ 1350 +INT_ANGLE_360 equ 1800 + + \ No newline at end of file diff --git a/ack_lib/ACKRTN1.ASM b/ack_lib/ACKRTN1.ASM new file mode 100644 index 0000000..4397900 --- /dev/null +++ b/ack_lib/ACKRTN1.ASM @@ -0,0 +1,571 @@ + + IDEAL + JUMPS + P386 + P387 + + + MASM + .MODEL FLAT + + .DATA + +SVTABLE dd ? +ENDPOS dd ? + + + .CODE + IDEAL + + + include "ackrtn.inc" + + extrn _VidSeg:dword + extrn _Resolution:word + extrn _ScreenOffset:word + extrn _bmDistance:dword + extrn _bmWall:dword + extrn _scPtr:dword + extrn _VidTop:dword + extrn _VidBottom:dword + extrn _Floors1:dword + extrn _Floors2:dword + + extrn _gPalTable:dword + extrn _gWinStartX:word + extrn _gWinStartY:word + extrn _gWinEndX:word + extrn _gWinEndY:word + extrn _gWinHeight:word + extrn _ViewHeight:word + extrn _SysFlags:word + extrn _Slice:byte + extrn _gScrnBuffer:dword + extrn _gCenterOff:word + extrn _Floorht:word + extrn _Floorscr:dword + extrn _gWinStartOffset:dword + + extrn _scVid:dword + extrn _scWall:dword + extrn _scPal:dword + extrn _scdst:word + extrn _scwht:word + extrn _scmulti:word + extrn _sctopht:word + extrn _scbotht:word + extrn _scsavwht:word + extrn _scmulcnt:word + extrn _scsavVid:dword + extrn _scbNum:word + extrn _scMulData:dword + extrn _scColumn:dword + extrn _WallbMaps:dword + extrn _LowerTable:dword + extrn _gBottomOff:dword + + ACKEXT ShowColLow + ACKEXT ShowColMaskLow + + ACKPUBS ShowCol + ACKPUBS ShowColMask + ACKPUBS DrawWalls + + +align 2 +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC ShowCol + push ebp + push esi + push edi + + mov edi,[_scVid] ;Video buffer position + mov ebp,[_scPal] ;Pointer to palette for shading + + mov ax,[_scwht] ;Height to use + mov [_scsavwht],ax ;save for later + + movzx ecx,[_sctopht] + mov ebx,[_scWall] + add ebx,ecx + mov [_bmWall],ebx + mov ch,al + inc ch + + mov esi,[_bmDistance] + mov esi,[_LowerTable+esi*4] + mov [SVTABLE],esi + + mov eax,0 + mov ebx,0 + + test [word ptr _scbNum],WALL_TYPE_UPPER + jz short toprun + mov edx,[_bmDistance] + +zztoploop: + dec ch + jz short zzdomulti + sub di,320 + add bx,dx + cmp bh,cl + jge short zzdomulti + jmp zztoploop + +zzdomulti: + mov [_scwht],cx + mov [_scsavVid],edi + jmp chkmulti + +toprun: + mov ebx,[_bmWall] + +toploop: + movzx edx,[word ptr esi] + cmp dl,cl + jg short botrun + lea esi,[esi+2] + neg edx + mov al,[ebx+edx] + mov al,[ebp+eax] + mov [edi],al + add edi,-320 + dec ch + jnz toploop + +botrun: + mov [_scwht],cx + mov [_scsavVid],edi + mov edi,[_scVid] + mov cx,[_scbotht] + mov dx,[_scsavwht] + mov ch,dl + mov esi,[SVTABLE] + inc ebx + dec cl + +botloop: + lea edi,[edi+320] + movzx edx,[word ptr esi] + cmp dl,cl + jge short chkmulti + lea esi,[esi+2] + mov al,[ebx+edx] + mov al,[ebp+eax] + mov [edi],al + dec ch + jnz botloop + +chkmulti: + mov edi,[_scsavVid] + cmp [word ptr _scmulti],0 + jz alldone + mov cx,[_scmulcnt] + + mov ebx,[_scMulData] ;ptr to count and wall data + mov cl,[ebx] ;get number of walls to draw + inc ebx + mov al,[ebx] ;first wall to show + inc ebx + mov [_scMulData],ebx + movzx ebx,al ;get wall number + mov esi,[_scColumn] ;Current bitmap column to display x 64 + lea esi,[esi+63] + mov [_scColumn],esi ;save for later use + mov eax,[_WallbMaps] ;Get array of bitmaps + mov ebx,[eax+ebx*4] ;Get the bitmap we are using + add ebx,esi ;point to bottom of column + + mov ax,[_scwht] ;Get height of window + mov ch,ah + cmp ch,0 ;Is there more room to draw? + jz short alldone ;br if at top of window + + mov esi,[SVTABLE] + mov eax,0 + +mulloop: + movzx eax,[word ptr esi] ;Get height displacement for this row + cmp al,64 ;Did we do the entire column? + jge short nextlevel ;Yes, see if more walls + lea esi,[esi+2] + neg eax ;Invert so we can add it below + movzx eax,[byte ptr ebx+eax] ;Get the pixel from the bitmap + mov al,[ebp+eax] ;Map it to the palette for shading + mov [edi],al ;Place it into the video buffer + dec ch ;Bump the window height + jz short alldone ;br if at the top of the window + sub edi,320 ;next video row + jmp mulloop + +nextlevel: + dec cl ;Bump wall count + jz short alldone ;br if no more walls + mov ebx,[_scMulData] ;Get pointer to the multi-ht data + movzx eax,[byte ptr ebx] ;next wall number + inc ebx ;Advance for next wall + mov [_scMulData],ebx + mov ebx,[_WallbMaps] ;Get wall array + mov ebx,[ebx+eax*4] ;Get wall bitmap to use + add ebx,[dword ptr _scColumn] ;add in current column + mov eax,0 + mov esi,[SVTABLE] + jmp mulloop + +alldone: + pop edi + pop esi + pop ebp + ret + endp + + +align 2 +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC ShowColMask + push ebp + push esi + push edi + mov ebp,eax + mov [_scPtr],eax + mov edi,[_scVid] + mov edx,[_scPal] + mov ax,[_scwht] + mov [_scsavwht],ax + movzx ecx,[_sctopht] + mov ebx,[_scWall] + add ebx,ecx + mov [_bmWall],ebx + mov ch,al + inc ch + mov eax,0 + mov ebx,0 + +;--TEST------------------------------------------------------------------------ + test [word ptr _scbNum],WALL_TYPE_UPPER + jz short m_toprun + mov ebp,[_bmDistance] + +m_zztoploop: + dec ch + jz short m_zzdomulti + add bx,bp + cmp bh,cl + jge short m_zzdomulti + sub edi,320 + jmp m_zztoploop + +m_zzdomulti: + mov ebp,[_scPtr] + mov [_scwht],cx + mov [_scsavVid],edi + jmp m_chkmulti + +;--TEST------------------------------------------------------------------------ +m_toprun: + mov ebp,[_bmDistance] + mov esi,[_bmWall] + +m_toploop: + movzx eax,bh + neg eax + movzx eax,[byte ptr esi+eax] + or al,al + jz short m_blank1 + mov al,[edx+eax] + mov [edi],al + +m_blank1: + add bx,bp + cmp bh,cl + jg short m_botrun + dec ch + jz short m_botrun + sub edi,320 + jmp m_toploop + +m_botrun: + mov ebp,[_scPtr] + mov [_scwht],cx + mov [_scsavVid],edi + mov edi,[_scVid] + mov cx,[_scbotht] + mov bx,[_scsavwht] + mov ch,bl + mov ebx,0 + inc esi + mov ebp,[_bmDistance] + mov eax,0 + dec cl + +m_botloop: + add edi,320 + mov al,bh + mov al,[esi+eax] + or al,al + jz short m_blank2 + mov al,[edx+eax] + mov [edi],al + +m_blank2: + dec ch + jz short m_chkmulti + add bx,bp + cmp bh,cl + jl m_botloop + +m_chkmulti: + mov ebp,[_scPtr] + cmp [word ptr _scmulti],0 + jz short m_alldone + mov cx,[_scmulcnt] + mov bx,[_scwht] + mov ch,bh + cmp ch,0 + jz short m_alldone + + mov edi,[_scsavVid] + mov ebx,[_scWall] + add ebx,63 + mov [_bmWall],ebx + mov ebx,0 + mov ebp,[_bmDistance] + mov esi,[_bmWall] + +m_mulloop: + sub edi,320 + movzx eax,bh + neg eax + movzx eax,[byte ptr esi+eax] + or al,al + jz m_blank3 + mov al,[edx+eax] + mov [edi],al + +m_blank3: + dec ch + jz short m_alldone + add bx,bp + cmp bh,64 + jge short m_nextlevel + jmp m_mulloop + +m_nextlevel: + mov ebx,0 + dec cl + jnz m_mulloop + +m_alldone: + pop edi + pop esi + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; This routine runs through the list of slices and draws the walls. +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawWalls + push ebp + push esi + push edi + + mov ax,[_gWinHeight] + shr ax,1 + mov [_Floorht],ax + mov ax,[_ViewHeight] + mov [_sctopht],ax + mov dx,64 + sub dx,ax + mov [_scbotht],dx + movsx ebx,[word ptr _gWinStartX] + movsx ecx,[_gCenterOff] + add ecx,ebx + add ecx,[_gScrnBuffer] + mov [_scVid],ecx + cmp [word ptr _Resolution],1 ;is this low resolution? + je stw_lowres ;yes, perform faster draw + + movzx eax,[_gWinEndX] + imul eax,saSize + mov [ENDPOS],eax + imul ebx,saSize + +stw010: + mov ebp,offset _Slice ; offset to slices array + add ebp,ebx + +stw020: + cmp [byte ptr ebp+saActive],0 ;is this an active slice? + jz short stw030 ;nope, so it's the last one + cmp [dword ptr ebp+saNext],0 ;is this the last slice? + jz short stw030 ;yes, so can't go further + mov ebp,[dword ptr ebp+saNext] ;point to next slice + jmp short stw020 + +stw030: + mov ax,[word ptr ebp+sabNumber] ;bitmap number + mov cx,ax + and cx,0ffh ;isolate number from flags + mov [_scmulcnt],cx ;save as current multi count + mov [_scbNum],ax ;and bitmap number and flags + + mov cx,0 ;set multi-flag to none + test ax,WALL_TYPE_MULTI + jz short stw040 + mov esi,[dword ptr ebp+samPtr] + mov [_scMulData],esi + or esi,esi ;is there multi-ht data? + jz short stw040 ;br if not + mov cx,1 ;set multi-flag to yes + +stw040: + mov [_scmulti],cx + mov ecx,[dword ptr ebp+sabMap] ;get pointer to bitmaps + movzx esi,al ;get low byte of bitmap number + shl esi,2 + + mov esi,[dword ptr ecx+esi] ;get actual bitmap pointer + movsx ecx,[word ptr ebp+saDist] ;distance to slice + mov [_bmDistance],ecx ;save for draw routine + shr ecx,6 ;bring it down into palette range + cmp ecx,15 ;check against max palette number + jbe short stw050 + mov ecx,15 ;force to max if above + +stw050: + shl ecx,8 ;x256 for palette entry + mov edi,[_gPalTable] ;pointer to palette table + add edi,ecx + movsx ecx,[word ptr ebp+sabColumn] ;column of bitmap slice + shl ecx,6 ;x64 to get correct row (column) + add esi,ecx ;adjust wall point to correct column + mov [_scColumn],ecx ;save for multi-ht walls + + mov [_scWall],esi + mov [_scPal],edi + mov eax,0 + mov [_scsavVid],eax ;null out saved video + + mov ax,[_Floorht] ;window height / 2 saved earlier + mov [_scwht],ax + + mov esi,ebx + call [dword ptr ebp+saFnc] + mov ebx,esi + +stw075: + cmp [dword ptr ebp+saPrev],0 ;is this the first slice? + jz short stw080 ;yes, go to next column + mov ebp,[dword ptr ebp+saPrev] ;pick up previous slice + jmp stw030 ;and start again with same column + + +stw080: + inc [dword ptr _scVid] ;next video position + add ebx,saSize + cmp ebx,[ENDPOS] + ja stw_getout ;yes, get out + jmp stw010 + +stw_lowres: + mov eax,ebx ; current column of display + imul eax,saSize ; size of slice structure + mov ebp,offset _Slice ; offset to slices + add ebp,eax + +stwlr020: + cmp [byte ptr ebp+saActive],0 ;is this an active slice? + jz short stwlr030 ;nope, so it's the last one + cmp [dword ptr ebp+saNext],0 ;is this the last slice? + jz short stwlr030 ;yes, so can't go further + mov ebp,[dword ptr ebp+saNext] ;point to next slice + jmp short stwlr020 + +stwlr030: + mov ax,[word ptr ebp+sabNumber] ;bitmap number + mov cx,ax + and cx,0ffh ;isolate number from flags + mov [_scmulcnt],cx ;save as current multi count + mov [_scbNum],ax ;and bitmap number and flags + + mov cx,0 ;set multi-flag to none + test ax,WALL_TYPE_MULTI + jz short stwlr040 + + mov esi,[dword ptr ebp+samPtr] + mov [_scMulData],esi + or esi,esi ;is there multi-height data? + jz short stwlr040 ;br if not + mov cx,1 ;set multi-flag to yes + +stwlr040: + mov [_scmulti],cx + mov ecx,[dword ptr ebp+sabMap] ;get pointer to bitmaps + movzx esi,al ;get low byte of bitmap number + shl esi,2 + mov esi,[dword ptr ecx+esi] ;get actual bitmap pointer + movsx ecx,[word ptr ebp+saDist] ;distance to slice + mov [_bmDistance],ecx ;save for draw routine + shr ecx,6 ;bring it down into palette range + cmp ecx,15 ;check against max palette number + jbe short stwlr050 + mov ecx,15 ;force to max if above + +stwlr050: + shl ecx,8 ;x256 for palette entry + mov edi,[_gPalTable] ;pointer to palette table + add edi,ecx + movsx ecx,[byte ptr ebp+sabColumn] ;column of bitmap slice + shl ecx,6 ;x64 to get correct row (column) + add esi,ecx ;adjust wall point to correct column + mov [_scColumn],ecx ;save for multi-ht walls + + mov [_scWall],esi + mov [_scPal],edi + mov eax,0 + mov [_scsavVid],eax ;null out saved video + + mov ax,[_Floorht] ;window height / 2 saved earlier + mov [_scwht],ax + + mov esi,ebx + cmp [byte ptr ebp+saType],ST_WALL ;transparent wall? + je short stwlr060 ;nope, use solid slice routine + ACKCALL ShowColMaskLow + jmp short stwlr070 + +stwlr060: + ACKCALL ShowColLow + +stwlr070: + mov ebx,esi + cmp [dword ptr ebp+saPrev],0 ;is this the first slice? + jz short stwlr080 ;yes, go to next column + mov ebp,[dword ptr ebp+saPrev] ;pick up previous slice + jmp stwlr030 ;and start again with same column + + +stwlr080: + add [dword ptr _scVid],2 ;next video position + inc ebx ;next column + inc ebx ;next column + cmp bx,[word ptr _gWinEndX] ;are we at the end of the window? + ja short stw_getout ;yes, get out + jmp stw_lowres + +stw_getout: + pop edi + pop esi + pop ebp + ret + endp + + end + + \ No newline at end of file diff --git a/ack_lib/ACKRTN2.ASM b/ack_lib/ACKRTN2.ASM new file mode 100644 index 0000000..19ee54b --- /dev/null +++ b/ack_lib/ACKRTN2.ASM @@ -0,0 +1,805 @@ + + IDEAL + JUMPS + P386 + P387 ; Allow 386 processor + + + MASM + .MODEL FLAT ;32-bit OS/2 model + + IDEAL + include "ackrtn.inc" + MASM + + extrn _WallDistTable:dword + extrn _FloorMap:word + extrn _CeilMap:word + extrn _LastWallHeight:word + extrn _ViewAngle:word + extrn _ScreenOffset:word + extrn _xPglobal:dword + extrn _yPglobal:dword + extrn _xBegGlobal:dword + extrn _yBegGlobal:dword + extrn _aeGlobal:dword + extrn _xGridGlobal:dword + extrn _yGridGlobal:dword + extrn _xPglobalHI:dword + extrn _yPglobalHI:dword + extrn _rbaTable:dword + extrn _rsHandle:word + extrn _LastX1:dword + extrn _LastY1:dword + extrn _iLastX:dword + extrn _iLastY;dword + extrn _MaxDistance:word + extrn _BackArray:dword + extrn _zdTable:dword + extrn _ErrorCode:word + extrn _xMapPosn:dword + extrn _yMapPosn:dword + extrn _Grid:dword + extrn _ObjGrid:dword + extrn _WallbMaps:dword + extrn _ViewHeight:word + extrn _CeilingHeight:word + extrn _gTopColor:byte + extrn _gBottomColor:byte + extrn _PlayerAngle:word + extrn _gScrnBuffer:dword + extrn _gBkgdBuffer:dword + extrn _gCenterOff:word + extrn _gWinStartOffset:dword + extrn _gWinHeight:word + extrn _gWinEndY:dword + extrn _SysFlags:word + extrn _sPtr:dword + extrn _mxGridGlobal:dword + extrn _myGridGlobal:dword + + extrn _xSecretmPos:word + extrn _xSecretmPos1:word + extrn _xSecretColumn:word + + extrn _ySecretmPos:word + extrn _ySecretmPos1:word + extrn _ySecretColumn:word + + extrn _TotalSecret:word + extrn _ViewColumn:word + extrn _SinTable:dword + extrn _CosTable:dword + extrn _LongTanTable:dword + extrn _LongInvTanTable:dword + extrn _InvCosTable:byte + extrn _InvSinTable:byte + extrn _LongCosTable:dword + extrn _ViewCosTable:dword + extrn _xNextTable:dword + extrn _yNextTable:dword + + extrn _LastMapPosn:word + extrn _LastObjectHit:word + extrn _TotalObjects:word + extrn _FoundObjectCount:word + extrn _ObjectsSeen:byte + extrn _MoveObjectCount:word + extrn _MoveObjectList:byte + extrn _ObjNumber:byte + extrn _ObjRelDist:byte + extrn _ObjColumn:byte + + extrn _x_xPos:dword + extrn _x_yPos:dword + extrn _x_xNext:dword + extrn _x_yNext:dword + extrn _y_xPos:dword + extrn _y_yPos:dword + extrn _y_xNext:dword + extrn _y_yNext:dword + + extrn _Resolution:word + extrn _Flooru:dword + extrn _Floorv:dword + extrn _Floordu:dword + extrn _Floordv:dword + extrn _Floorkx:dword + extrn _Floorky:dword + extrn _Floorku:dword + extrn _Floorkv:dword + extrn _Floorkdu:dword + extrn _Floorkdv:dword + extrn _Floorbm:dword + extrn _Floorscr:dword + extrn _Floors1:dword + extrn _Floors2:dword + extrn _FloorscrTop:dword + extrn _Floorptr2:dword + extrn _Floorht:dword + extrn _Floorwt:dword + extrn _Floorvht:word + extrn _Flooreht:word + extrn _FloorLastbNum:dword + extrn _FloorLastbm:dword + + extrn _bmDistance:dword + extrn _scwht:word + extrn _scWall:dword + extrn _scPal:dword + extrn _scVid:dword + extrn _scantables:dword + + ACKEXT DrawBackDrop + ACKEXT ShowCol + ACKEXT ShowColMask + ACKEXT FindDoor + ACKEXT xRayCast + ACKEXT yRayCast + + ACKPUBS xxxAckDrawFloor + ACKPUBS xxxAckDrawFloorOnly + ACKPUBS xxxAckDrawCeilingOnly + + .DATA + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Globals used by the AckDrawFloor routine +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +BCOL dd ? +HEIGHT dd ? +VA dd ? +SY dd ? +EY dd ? +BFSCRN dd ? +BCSCRN dd ? +FSCRN dd ? +CSCRN dd ? +CV dd ? +SV dd ? +BA dd ? +BA1 dd ? +ZDPTR dd ? +POS dd ? +BMPOS dd ? +MPOS dd ? +MPOSHI dd ? +SCANTBL dd ? +LINENUM dd ? +LASTDIST dd ? +;LASTX dd ? +;LASTY dd ? +LASTEBP dd ? +LASTEAX dd ? +LASTEDX dd ? +WALLDIST dd ? + + .CODE + IDEAL + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC xxxAckDrawFloor + push ebp + push esi + push edi + push ebx + push ecx + push edx + + movzx eax,[word ptr _PlayerAngle] + mov ecx,eax + sub eax,INT_ANGLE_32 + jnc short adf_20 + add eax,INT_ANGLE_360 + +adf_20: + mov ebx,640 + cdq + idiv ebx + mov [BCOL],edx + + mov eax,89 + sub ax,[word ptr _ViewHeight] + mov [HEIGHT],eax + + sub ecx,INT_ANGLE_32 + jnc short adf_30 + add ecx,INT_ANGLE_360 + +adf_30: + mov [VA],ecx + movzx eax,[word ptr _gWinHeight] + sar eax,1 + movzx ebx,[word ptr _gWinEndY] + sub ebx,eax + inc ebx + sub ebx,5 ; 6 + mov [EY],ebx + mov edi,[_gScrnBuffer] + movzx eax,[word ptr _gCenterOff] + mov ebx,eax + add eax,1920 + add eax,edi + mov [BFSCRN],eax + sub ebx,1600 ;1920 + add ebx,edi + mov [BCSCRN],ebx + mov ebp,0 + mov [LINENUM],ebp + mov [LASTDIST],ebp + mov ebx,[dword ptr _scantables] + mov [SCANTBL],ebx + mov ebx,[VA] + +adf_loop: + mov eax,[_CosTable] + shl ebx,2 + mov eax,[eax+ebx] + mov [CV],eax + mov eax,[_SinTable] + mov eax,[eax+ebx] + mov [SV],eax + + mov eax,[dword ptr _WallDistTable+ebp*4] + mov [WALLDIST],eax + + mov eax,[BCSCRN] + mov [CSCRN],eax + + mov ecx,[EY] + mov ebx,[BCOL] + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA],eax + inc ebx + cmp ebx,640 + jb short adf_l10 + sub ebx,ebx + +adf_l10: + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA1],eax + inc ebx + cmp ebx,640 + jb short adf_l20 + sub ebx,ebx + +adf_l20: + mov [BCOL],ebx + lea esi,[offset _zdTable] + mov ecx,[EY] ;Number of rows to draw + imul eax,ebp,800 + + add esi,eax + add esi,24 ;ebx + mov edi,[BFSCRN] + push ebp + +adf_yloop: + mov edx,[esi] + lea esi,[esi+4] + cmp edx,[WALLDIST] + jb short adf_distokay + lea edi,[edi+320] + jmp adf_ycont + +adf_distokay: + cmp edx,[LASTDIST] + jne short adf_newdist +; mov eax,[LASTX] +; mov ebx,[LASTY] + mov ebp,[LASTEBP] + mov eax,[LASTEAX] + mov edx,[LASTEDX] + jmp short adf_samedist + +adf_newdist: + mov [LASTDIST],edx + mov eax,[CV] + mov ebx,[SV] + imul eax,edx + imul ebx,edx + sar eax,16 + sar ebx,16 + mov edx,[_xPglobal] + add eax,edx + mov edx,[_yPglobal] + add ebx,edx +; mov [LASTX],eax +; mov [LASTY],ebx + +;adf_samedist: + mov edx,ebx + and edx,0FC0h + mov ebp,eax + sar ebp,6 + add ebp,edx ;Pos within floor and ceiling maps + + and ebx,63 + shl ebx,6 + and eax,63 + add eax,ebx ;bitmap position + +;; mov ebx,0 + + shl ebp,1 + mov [LASTEBP],ebp + mov [LASTEAX],eax + + movzx ebx,[word ptr _FloorMap+ebp] + mov edx,[_WallbMaps] + mov edx,[edx+ebx*4] + movzx edx,[byte ptr edx+eax] + mov ebx,[SCANTBL] + mov dl,[ebx+edx] + mov dh,dl + mov [LASTEDX],edx + +adf_samedist: + mov [edi],dx + lea edi,[edi+320] + + movzx ebx,[word ptr _CeilMap+ebp] + mov ebp,[CSCRN] + test bx,bx + jz short adf_yback + + mov edx,[_WallbMaps] + mov edx,[edx+ebx*4] + movzx eax,[byte ptr edx+eax] + mov edx,[SCANTBL] + mov al,[edx+eax] + mov ah,al + mov [ebp],ax + + mov eax,[LINENUM] + add eax,4 + mov [LINENUM],eax + mov eax,[_scantables+eax] + mov [SCANTBL],eax + lea ebp,[ebp-320] + mov [CSCRN],ebp + dec [dword ptr BA] + dec [dword ptr BA1] + dec ecx + jnz adf_yloop + + jmp short adf_ynext + +adf_yback: + mov eax,[BA] + mov dl,[eax] + mov eax,[BA1] + mov dh,[eax] + mov [ebp],dx + +adf_ycont: + mov eax,[LINENUM] + add eax,4 + mov [LINENUM],eax + mov eax,[_scantables+eax] + mov [SCANTBL],eax + sub [dword ptr CSCRN],320 + dec [dword ptr BA] + dec [dword ptr BA1] + dec ecx + jnz adf_yloop + +adf_ynext: + mov [dword ptr LINENUM],0 + mov eax,[dword ptr _scantables] + mov [SCANTBL],eax + mov ebx,[VA] + lea ebx,[ebx+2] + cmp ebx,INT_ANGLE_360 + jb short adf_l90 + sub ebx,INT_ANGLE_360 + +adf_l90: + mov [VA],ebx ;Note: EBX is used for VA at top of loop! + add [dword ptr BFSCRN],2 + add [dword ptr BCSCRN],2 + pop ebp + lea ebp,[ebp+2] + cmp ebp,320 + jb adf_loop + + +adf_exit: + pop edx + pop ecx + pop ebx + pop edi + pop esi + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC xxxAckDrawFloorOnly + push ebp + push esi + push edi + push ebx + push ecx + push edx + + movzx eax,[word ptr _PlayerAngle] + mov ecx,eax + sub eax,INT_ANGLE_32 + jnc short adfo_20 + add eax,INT_ANGLE_360 + +adfo_20: + mov ebx,640 + cdq + idiv ebx + mov [BCOL],edx + + mov eax,89 + sub ax,[word ptr _ViewHeight] + mov [HEIGHT],eax + + sub ecx,INT_ANGLE_32 + jnc short adfo_30 + add ecx,INT_ANGLE_360 + +adfo_30: + mov [VA],ecx + movzx eax,[word ptr _gWinHeight] + sar eax,1 + movzx ebx,[word ptr _gWinEndY] + sub ebx,eax + inc ebx + sub ebx,5 ; 6 + mov [EY],ebx + mov edi,[_gScrnBuffer] + movzx eax,[word ptr _gCenterOff] + mov ebx,eax + add eax,1920 + add eax,edi + mov [BFSCRN],eax + sub ebx,1600 ;1920 + add ebx,edi + mov [BCSCRN],ebx + mov ebp,0 + mov [LINENUM],ebp + mov ebx,[dword ptr _scantables] + mov [SCANTBL],ebx + mov ebx,[VA] + +adfo_loop: + mov eax,[_CosTable] + shl ebx,2 + mov eax,[eax+ebx] + mov [CV],eax + mov eax,[_SinTable] + mov eax,[eax+ebx] + mov [SV],eax + + mov eax,[BCSCRN] + mov [CSCRN],eax + + mov ecx,[EY] + mov ebx,[BCOL] + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA],eax + inc ebx + cmp ebx,640 + jb short adfo_l10 + sub ebx,ebx + +adfo_l10: + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA1],eax + inc ebx + cmp ebx,640 + jb short adfo_l20 + sub ebx,ebx + +adfo_l20: + mov [BCOL],ebx + lea esi,[offset _zdTable] + mov ecx,[EY] ;Number of rows to draw + imul eax,ebp,800 + + add esi,eax + add esi,24 ;ebx + mov edi,[BFSCRN] + push ebp + +adfo_yloop: + mov edx,[esi] + lea esi,[esi+4] + mov eax,[CV] + mov ebx,[SV] + imul eax,edx + imul ebx,edx + sar eax,16 + sar ebx,16 + mov edx,[_xPglobal] + add eax,edx + mov edx,[_yPglobal] + add ebx,edx + + mov edx,ebx + and edx,0FC0h + mov ebp,eax + sar ebp,6 + add ebp,edx ;Pos within floor and ceiling maps + + and ebx,63 + shl ebx,6 + and eax,63 + add eax,ebx ;bitmap position + + mov ebx,0 + + shl ebp,1 + mov bx,[word ptr _FloorMap+ebp] + mov edx,[_WallbMaps] + mov edx,[edx+ebx*4] + movzx edx,[byte ptr edx+eax] + mov ebx,[SCANTBL] + mov dl,[ebx+edx] + mov dh,dl + mov [edi],dx + lea edi,[edi+320] + + mov eax,[BA] + mov dl,[eax] + mov eax,[BA1] + mov dh,[eax] + mov ebp,[CSCRN] + mov [ebp],dx + +adfo_ycont: + lea ebp,[ebp-320] + mov [CSCRN],ebp + mov eax,[LINENUM] + add eax,4 + mov [LINENUM],eax + mov eax,[_scantables+eax] + mov [SCANTBL],eax + dec [dword ptr BA] + dec [dword ptr BA1] + dec ecx + jnz adfo_yloop + +adfo_ynext: + mov [dword ptr LINENUM],0 + mov eax,[dword ptr _scantables] + mov [SCANTBL],eax + mov ebx,[VA] + lea ebx,[ebx+2] + cmp ebx,INT_ANGLE_360 + jb short adfo_l90 + sub ebx,INT_ANGLE_360 + +adfo_l90: + mov [VA],ebx ;Note: EBX is used for VA at top of loop! + add [dword ptr BFSCRN],2 + add [dword ptr BCSCRN],2 + pop ebp + lea ebp,[ebp+2] + cmp ebp,320 + jb adfo_loop + +adfo_exit: + pop edx + pop ecx + pop ebx + pop edi + pop esi + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC xxxAckDrawCeilingOnly + push ebp + push esi + push edi + push ebx + push ecx + push edx + + movzx eax,[word ptr _PlayerAngle] + mov ecx,eax + sub eax,INT_ANGLE_32 + jnc short adco_20 + add eax,INT_ANGLE_360 + +adco_20: + mov ebx,640 + cdq + idiv ebx + mov [BCOL],edx + + mov eax,89 + sub ax,[word ptr _ViewHeight] + mov [HEIGHT],eax + + sub ecx,INT_ANGLE_32 + jnc short adco_30 + add ecx,INT_ANGLE_360 + +adco_30: + mov [VA],ecx + movzx eax,[word ptr _gWinHeight] + sar eax,1 + movzx ebx,[word ptr _gWinEndY] + sub ebx,eax + inc ebx + sub ebx,5 ; 6 + mov [EY],ebx + mov edi,[_gScrnBuffer] + movzx eax,[word ptr _gCenterOff] + mov ebx,eax + sub ebx,1600 ;1920 + add ebx,edi + mov [BCSCRN],ebx + mov ebp,0 + mov [LINENUM],ebp + mov ebx,[dword ptr _scantables] + mov [SCANTBL],ebx + mov ebx,[VA] + +adco_loop: + mov eax,[_CosTable] + shl ebx,2 + mov eax,[eax+ebx] + mov [CV],eax + mov eax,[_SinTable] + mov eax,[eax+ebx] + mov [SV],eax + + mov eax,[BCSCRN] + mov [CSCRN],eax + + mov ecx,[EY] + mov ebx,[BCOL] + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA],eax + inc ebx + cmp ebx,640 + jb short adco_l10 + sub ebx,ebx + +adco_l10: + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA1],eax + inc ebx + cmp ebx,640 + jb short adco_l20 + sub ebx,ebx + +adco_l20: + mov [BCOL],ebx + lea esi,[offset _zdTable] + mov ecx,[EY] ;Number of rows to draw + imul eax,ebp,800 + + add esi,eax + add esi,24 ;ebx + push ebp + +adco_yloop: + mov edx,[esi] + lea esi,[esi+4] + mov eax,[CV] + mov ebx,[SV] + imul eax,edx + imul ebx,edx + sar eax,16 + sar ebx,16 + mov edx,[_xPglobal] + add eax,edx + mov edx,[_yPglobal] + add ebx,edx + + mov edx,ebx + and edx,0FC0h + mov ebp,eax + sar ebp,6 + add ebp,edx ;Pos within floor and ceiling maps + + and ebx,63 + shl ebx,6 + and eax,63 + add eax,ebx ;bitmap position + + mov ebx,0 + + shl ebp,1 + movzx ebx,[word ptr _CeilMap+ebp] + mov ebp,[CSCRN] + test bx,bx + jz short adco_yback + + mov edx,[_WallbMaps] + mov edx,[edx+ebx*4] + movzx eax,[byte ptr edx+eax] + mov edx,[SCANTBL] + mov al,[edx+eax] + mov ah,al + mov [ebp],ax + + mov eax,[LINENUM] + add eax,4 + mov [LINENUM],eax + mov eax,[_scantables+eax] + mov [SCANTBL],eax + lea ebp,[ebp-320] + mov [CSCRN],ebp + dec [dword ptr BA] + dec [dword ptr BA1] + dec ecx + jnz adco_yloop + + jmp short adco_ynext + +adco_yback: + mov eax,[BA] + mov dl,[eax] + mov eax,[BA1] + mov dh,[eax] + mov [ebp],dx + +adco_ycont: + mov eax,[LINENUM] + add eax,4 + mov [LINENUM],eax + mov eax,[_scantables+eax] + mov [SCANTBL],eax + sub [dword ptr CSCRN],320 + dec [dword ptr BA] + dec [dword ptr BA1] + dec ecx + jnz adco_yloop + +adco_ynext: + mov [dword ptr LINENUM],0 + mov eax,[dword ptr _scantables] + mov [SCANTBL],eax + mov ebx,[VA] + lea ebx,[ebx+2] + cmp ebx,INT_ANGLE_360 + jb short adco_l90 + sub ebx,INT_ANGLE_360 + +adco_l90: + mov [VA],ebx ;Note: EBX is used for VA at top of loop! + add [dword ptr BCSCRN],2 + pop ebp + lea ebp,[ebp+2] + cmp ebp,320 + jb adco_loop + + +adco_exit: + pop edx + pop ecx + pop ebx + pop edi + pop esi + pop ebp + ret + endp + + + end + + \ No newline at end of file diff --git a/ack_lib/ACKRTN3.ASM b/ack_lib/ACKRTN3.ASM new file mode 100644 index 0000000..59ac135 --- /dev/null +++ b/ack_lib/ACKRTN3.ASM @@ -0,0 +1,830 @@ + + IDEAL + JUMPS + P386 + P387 ; Allow 386 processor + + + MASM + .MODEL FLAT ;32-bit OS/2 model + .CODE + IDEAL + + include "ackrtn.inc" + + extrn _gMultiWalls:word + extrn _WallDistTable:dword + extrn _BackDropRows:dword + extrn _FloorCeilRtn:dword + extrn _Resolution:word + extrn _ScreenOffset:word + extrn _bmDistance:dword + extrn _bmWall:dword + extrn _scPtr:dword + extrn _VidTop:dword + extrn _VidBottom:dword + extrn _Floors1:dword + extrn _Floors2:dword + extrn _PlayerAngle:word + extrn _BackArray:dword + extrn _gWinStartX:word + extrn _gWinStartY:word + extrn _gWinEndX:word + extrn _gWinEndY:word + extrn _gWinHeight:word + extrn _gWinWidth:word + extrn _gCenterRow:word + extrn _gCenterOff:word + + extrn _scVid:dword + extrn _scWall:dword + extrn _scPal:dword + extrn _scdst:word + extrn _scwht:word + extrn _scmulti:word + extrn _sctopht:word + extrn _scbotht:word + extrn _scsavwht:word + extrn _scmulcnt:word + extrn _scsavVid:dword + extrn _scbNum:word + extrn _scMulData:dword + extrn _scColumn:dword + extrn _WallbMaps:dword + + extrn _FloorMap:word + extrn _CeilMap:word + extrn _LastWallHeight:word + extrn _ViewAngle:word + extrn _ScreenOffset:word + extrn _xPglobal:dword + extrn _yPglobal:dword + extrn _xBegGlobal:dword + extrn _yBegGlobal:dword + extrn _aeGlobal:dword + extrn _xGridGlobal:dword + extrn _yGridGlobal:dword + extrn _xPglobalHI:dword + extrn _yPglobalHI:dword + extrn _rbaTable:dword + extrn _rsHandle:word + extrn _LastX1:dword + extrn _LastY1:dword + extrn _iLastX:dword + extrn _iLastY;dword + extrn _MaxDistance:word + extrn _ErrorCode:word + extrn _xMapPosn:dword + extrn _yMapPosn:dword + extrn _Grid:dword + extrn _ObjGrid:dword + extrn _ViewHeight:word + extrn _CeilingHeight:word + extrn _gTopColor:byte + extrn _gBottomColor:byte + extrn _PlayerAngle:word + extrn _gScrnBuffer:dword + extrn _gBkgdBuffer:dword + extrn _gCenterOff:word + extrn _gWinHeight:word + extrn _SysFlags:word + + + extrn _xSecretmPos:word + extrn _xSecretmPos1:word + extrn _xSecretColumn:word + + extrn _ySecretmPos:word + extrn _ySecretmPos1:word + extrn _ySecretColumn:word + + extrn _TotalSecret:word + extrn _ViewColumn:word + extrn _SinTable:dword + extrn _CosTable:dword + extrn _LongTanTable:dword + extrn _LongInvTanTable:dword + extrn _InvCosTable:byte + extrn _InvSinTable:byte + extrn _LongCosTable:dword + extrn _ViewCosTable:dword + extrn _xNextTable:dword + extrn _yNextTable:dword + + extrn _LastMapPosn:word + extrn _LastObjectHit:word + extrn _TotalObjects:word + extrn _FoundObjectCount:word + extrn _ObjectsSeen:byte + extrn _MoveObjectCount:word + extrn _MoveObjectList:byte + extrn _ObjNumber:byte + extrn _ObjRelDist:byte + extrn _ObjColumn:byte + + extrn _x_xPos:dword + extrn _x_yPos:dword + extrn _x_xNext:dword + extrn _x_yNext:dword + extrn _y_xPos:dword + extrn _y_yPos:dword + extrn _y_xNext:dword + extrn _y_yNext:dword + + extrn _Slice:dword + extrn _sPtr:dword + extrn _HitMap:byte + + ACKEXT xRaySetup + ACKEXT yRaySetup + ACKEXT BuildSlice + ACKEXT xRayCast + ACKEXT yRayCast + ACKEXT AckDrawFloor + ACKEXT AckDrawFloorOnly + ACKEXT AckDrawCeilingOnly + ACKEXT DrawWalls + ACKEXT CheckDoors + ACKEXT BuildSliceMulti + ACKEXT FindObject + + ACKPUBS xRayCastMulti + ACKPUBS yRayCastMulti + ACKPUBS ShowColLow + ACKPUBS ShowColMaskLow + ACKPUBS BuildUpView + +align 2 +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC ShowColLow + push ebp + push esi + push edi + mov ebp,eax + mov [_scPtr],eax + mov edi,[_scVid] + mov edx,[_scPal] + mov ax,[_scwht] + mov [_scsavwht],ax + movsx ecx,[_sctopht] + mov ebx,[_scWall] + add ebx,ecx + mov [_bmWall],ebx + mov ch,al + inc ch + mov eax,0 + mov ebx,0 + +;--TEST------------------------------------------------------------------------ + test [word ptr _scbNum],WALL_TYPE_UPPER + jz short toprun + mov ebp,[_bmDistance] + +zztoploop: + dec ch + jz short zzdomulti + add bx,bp + cmp bh,cl + jge short zzdomulti + sub di,320 + jmp zztoploop + +zzdomulti: + mov ebp,[_scPtr] + mov [_scwht],cx + mov [_scsavVid],edi + jmp chkmulti + +;--TEST------------------------------------------------------------------------ +toprun: + mov ebp,[_bmDistance] + +toploop: + movsx eax,bh + mov esi,[_bmWall] + sub esi,eax + mov al,[esi] + mov al,[edx+eax] + mov ah,al + mov [edi],ax + add bx,bp + cmp bh,cl + jg short botrun + dec ch + jz short botrun + sub edi,320 + jmp toploop + +botrun: + mov ebp,[_scPtr] + mov [_scwht],cx + mov [_scsavVid],edi + mov [_VidTop],edi + mov edi,[_scVid] + mov cx,[_scbotht] + mov bx,[_scsavwht] + mov ch,bl + mov ebx,0 + mov esi,[_bmWall] + inc esi + mov ebp,[_bmDistance] + dec cl + +botloop: + add edi,320 + movsx ax,bh + mov al,[esi+eax] + mov al,[edx+eax] + mov ah,al + mov [edi],ax + dec ch + jz short chkmulti + add bx,bp + cmp bh,cl + jl botloop + +chkmulti: + mov [_VidBottom],edi + mov ebp,[_scPtr] + mov edi,[_scsavVid] + cmp [word ptr _scmulti],0 + jz alldone + mov cx,[_scmulcnt] + + mov ebx,[_scMulData] ;ptr to count and wall data + mov cl,[ebx] ;get count of data + inc ebx + mov al,[ebx] ;first wall to show + inc ebx + mov [_scMulData],ebx + movsx ebx,al ;get wall number + shl ebx,2 ;x4 for index into wall bitmap array + mov eax,[_WallbMaps] + mov ebx,[eax+ebx] + add ebx,[dword ptr _scColumn] ;add in current column + add ebx,63 ;point to bottom of column + mov [_bmWall],ebx + + mov bx,[_scwht] + mov ch,bh + cmp ch,0 + jz short alldone + + mov ebx,0 + mov ebp,[_bmDistance] + +mulloop: + sub edi,320 + movsx eax,bh + mov esi,[_bmWall] + sub esi,eax + mov al,[esi] + mov al,[edx+eax] + mov ah,al + mov [edi],ax + dec ch + jz short alldone + add bx,bp + cmp bh,64 + jge short nextlevel + jmp mulloop + +nextlevel: + dec cl + jz short alldone + mov ebx,[_scMulData] + mov al,[ebx] ;next wall number + inc ebx + mov [_scMulData],ebx + movsx ebx,al + shl ebx,2 + mov eax,[_WallbMaps] + mov ebx,[eax+ebx] + add ebx,[dword ptr _scColumn] ;add in current column + add ebx,63 ;point to bottom of column + mov [_bmWall],ebx + mov ebx,0 + jmp mulloop + +alldone: + mov [_VidTop],edi + pop edi + pop esi + pop ebp + ret + endp + + +align 2 +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC ShowColMaskLow + push ebp + push esi + push edi + mov ebp,eax + mov [_scPtr],eax + mov edi,[_scVid] + mov edx,[_scPal] + mov ax,[_scwht] + mov [_scsavwht],ax + movsx ecx,[_sctopht] + mov ebx,[_scWall] + add ebx,ecx + mov [_bmWall],ebx + mov ch,al + mov eax,0 + mov ebx,0 + +;--TEST------------------------------------------------------------------------ + test [word ptr _scbNum],WALL_TYPE_UPPER + jz short m_toprun + mov ebp,[_bmDistance] + +m_zztoploop: + dec ch + jz short m_zzdomulti + add bx,bp + cmp bh,cl + jge short m_zzdomulti + sub edi,320 + jmp m_zztoploop + +m_zzdomulti: + mov ebp,[_scPtr] + mov [_scwht],cx + mov [_scsavVid],edi + jmp m_chkmulti + +;--TEST------------------------------------------------------------------------ +m_toprun: + mov ebp,[_bmDistance] + +m_toploop: + movsx eax,bh + mov esi,[_bmWall] + sub esi,eax + mov al,[esi] + or al,al + jz m_blank1 + mov al,[edx+eax] + mov ah,al + mov [edi],ax + +m_blank1: + add bx,bp + cmp bh,cl + jg short m_botrun + dec ch + jz short m_botrun + sub edi,320 + jmp m_toploop + +m_botrun: + mov ebp,[_scPtr] + mov [_scwht],cx + mov [_scsavVid],edi + mov edi,[_scVid] + mov cx,[_scbotht] + mov bx,[_scsavwht] + mov ch,bl + mov ebx,0 + mov esi,[_bmWall] + inc esi + mov ebp,[_bmDistance] + +m_botloop: + add edi,320 + movsx ax,bh + mov al,[esi+eax] + or al,al + jz m_blank2 + mov al,[edx+eax] + mov ah,al + mov [edi],ax + +m_blank2: + dec ch + jz short m_chkmulti + add bx,bp + cmp bh,cl + jl m_botloop + +m_chkmulti: + mov ebp,[_scPtr] + cmp [word ptr _scmulti],0 + jz short m_alldone + mov cx,[_scmulcnt] + mov bx,[_scwht] + mov ch,bh + cmp ch,0 + jz short m_alldone + + mov edi,[_scsavVid] + mov ebx,[_scWall] + add ebx,63 + mov [_bmWall],ebx + mov ebx,0 + mov ebp,[_bmDistance] + +m_mulloop: + sub edi,320 + movsx eax,bh + mov esi,[_bmWall] + sub esi,eax + mov al,[esi] + or al,al + jz m_blank3 + mov al,[edx+eax] + mov ah,al + mov [edi],ax + +m_blank3: + dec ch + jz short m_alldone + add bx,bp + cmp bh,64 + jge short m_nextlevel + jmp m_mulloop + +m_nextlevel: + mov ebx,0 + dec cl + jnz m_mulloop + +m_alldone: + pop edi + pop esi + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC xRayCastMulti + push ebp + mov ebp,esp + sub esp,50 + push esi + push edi + push ebx + push ecx + push edx + + mov [word ptr retval],0 + +looptop: + + mov edx,[dword ptr _x_xPos] + cmp edx,large 0 + jl short getout + cmp edx,large 4096 + jg short getout + mov eax,[_x_yPos] + cmp eax,large 0 + jl short getout + cmp eax,large 010000000h + jle short inbounds + +getout: + jmp loopdone + +inbounds: + sar eax,16 + and ax,-64 + sar edx,6 + add ax,dx + mov si,ax + + shl ax,1 + movsx eax,ax + mov ebx,[dword ptr _ObjGrid] + mov ax,[word ptr ebx+eax] + or ax,ax + jz short no_obj + + and ax,0FFh + mov [word ptr Color],ax + + movsx ecx,[_FoundObjectCount] ;// Get number of current objects seen + mov ebx,ecx + jcxz short nofound ;// None found yet, add this new one + mov edi,offset _ObjectsSeen + repne scasb ;// See if this object already seen + jz short no_obj ;// Yes, ignore this ray then + +nofound: + mov edi,offset _ObjectsSeen + mov [edi+ebx],al + inc bx + mov [word ptr _FoundObjectCount],bx + +no_obj: + movsx eax,si + + shl eax,1 + mov ebx,[dword ptr _xGridGlobal] + mov cx,[ebx+eax] + or cx,cx + jz short next_square + test cx,WALL_UPPER_MULTI + jz short next_square + cmp cl,[byte ptr _LastWallHeight] + jbe short next_square + + mov [word ptr _xMapPosn],si + mov ebx,[dword ptr _x_xPos] + mov [dword ptr _iLastX],ebx + mov edx,[dword ptr _x_yPos] + mov [dword ptr _LastY1],edx + + movsx eax,cx + jmp short xRayDone + +next_square: + mov eax,[dword ptr _x_xNext] + add [dword ptr _x_xPos],eax + mov eax,[dword ptr _x_yNext] + add [dword ptr _x_yPos],eax + jmp looptop + +loopdone: + movsx eax,[word ptr retval] + +xRayDone: + pop edx + pop ecx + pop ebx + pop edi + pop esi + mov esp,ebp + pop ebp + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC yRayCastMulti + push ebp + mov ebp,esp + sub esp,50 + push esi + push edi + push ebx + push ecx + push edx + mov [word ptr retval],0 + +y_looptop: + mov edx,[dword ptr _y_xPos] + cmp edx,large 0 + jl short y_getout + cmp edx,large 010000000h + jg short y_getout + mov eax,[_y_yPos] + cmp eax,large 0 + jl short y_getout + cmp eax,large 4096 + jle short y_inbounds + +y_getout: + jmp loopdone + +y_inbounds: + sar edx,22 + and ax,-64 + add ax,dx + mov si,ax + + shl ax,1 + movsx eax,ax + mov ebx,[dword ptr _ObjGrid] + mov ax,[ebx+eax] + or ax,ax + jz short y_no_obj + + and ax,0FFh + mov [word ptr Color],ax + + movsx ecx,[_FoundObjectCount] + mov ebx,ecx + jcxz short y_nofound + mov edi,offset _ObjectsSeen + repne scasb + jz short y_no_obj + +y_nofound: + mov edi,offset _ObjectsSeen + mov [edi+ebx],al + inc bx + mov [word ptr _FoundObjectCount],bx + +y_no_obj: + movsx eax,si + + shl eax,1 + mov ebx,[dword ptr _yGridGlobal] + mov cx,[ebx+eax] + or cx,cx + jz short y_next_square + +y_wall_here: + test cx,WALL_UPPER_MULTI + jz short y_next_square + cmp cl,[byte ptr _LastWallHeight] + jbe short y_next_square + mov [word ptr _yMapPosn],si + mov ebx,[dword ptr _y_xPos] + mov [dword ptr _LastX1],ebx + mov edx,[dword ptr _y_yPos] + mov [dword ptr _iLastY],edx + mov edx,ebx + + movsx eax,cx + jmp short yRayDone + +y_next_square: + mov eax,[dword ptr _y_xNext] + add [dword ptr _y_xPos],eax + mov eax,[dword ptr _y_yNext] + add [dword ptr _y_yPos],eax + jmp y_looptop + +y_loopdone: + movsx eax,[word ptr retval] + +yRayDone: + pop edx + pop ecx + pop ebx + pop edi + pop esi + mov esp,ebp + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC CheckHitMap + mov edi,offset _HitMap + mov edx,edi + mov ecx,4096 + mov esi,[dword ptr _ObjGrid] + +chmLoop: + xor eax,eax + repe scasb + jcxz chmDone + mov ebx,edi ;get current location + sub ebx,edx ;minus start for actual offset + dec ebx ;minus base 0 offset + shl ebx,1 ;times 2 for word array + mov ax,[word ptr esi+ebx] + or al,al ;is there an object there? + jz chmLoop ;nope, keep checking + movzx ebx,[word ptr _FoundObjectCount] + mov [byte ptr _ObjectsSeen+ebx],al + inc ebx + mov [word ptr _FoundObjectCount],bx + jmp chmLoop + +chmDone: + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC BuildUpView + push ebp ; Save registers used by this routine + push esi + push edi + push ebx + push ecx + push edx + + ACKCALL CheckDoors ; Determine the state of the doors used in the view + + mov edi,offset _HitMap ; Access the hit map + mov ecx,1024 ; Size of the hit map + xor eax,eax ; Clear each location + rep stosd ; Clear out entire hit map + +; Check to see if moveable objects are found in the view. If so, access the object list +; and store the objects. + mov [word ptr _MaxDistance],0 + movzx ecx,[word ptr _MoveObjectCount] ; Get # of moveable objects + mov [_FoundObjectCount],cx ; Store # of objects found in view + jcxz short buv010 ; No objects are used, jump ahead + mov edi,offset _ObjectsSeen ; Reference global object list + mov esi,offset _MoveObjectList ; List of movable objects + mov ebx,ecx ; # of moveable objects + sar ecx,2 ; Divide by 2 + rep movsd + mov ecx,ebx ; # of moveable objects + and ecx,3 + rep movsb ; Finish copying _ObjectSeen to + ; _MoveObjectList +buv010: + movzx esi,[_gWinEndX] ; Get right side of viewport + cmp si,320 ; Is right coord. = 320? + jae short buv020 + inc esi ; Adjust right viewport if not 320 + +buv020: + movzx eax,[_PlayerAngle] ; Get current player’s angle + sub eax,INT_ANGLE_32 ; Check with 32 degree point + jnc short buv030 ; Skip if in range + add eax,INT_ANGLE_360 ; Add 360 degrees to angle + +buv030: + movzx ebx,[_gWinStartX] ; Get left side of viewport + mov [_ViewColumn],bx ; Save location in _ViewColumn + add eax,ebx ; Add angle to left side + cmp eax,INT_ANGLE_360 ; Are we out of range? + jl short buv040 ; We’re ok! + sub eax,INT_ANGLE_360 ; Reduce angle by 360 degrees + +buv040: + mov [_ViewAngle],ax ; Store updated angle + +; The start of the main loop that builds individual slices for the view. +; This loop continues until the entire view has been built. Each time through +; the loop, the player’s viewing angle is increased. This continues until a full +; 64 degre range is casted (assuming a full width screen is used). +buv050: + movzx ebx,[_ViewColumn] ; Get left column position + mov [dword ptr _WallDistTable+ebx*4],4096 ; Max distance to walls + mov eax,offset _Slice ; Access base adress of Slice structure + imul ebx,saSize ; Calculate offset into actual slice + add eax,ebx ; Add offset to base address + mov [_sPtr],eax ; Set up pointer to actual column slice + movzx edi,[_ViewAngle] ; Use current viewing angle + ACKCALL xRaySetup ; Set up x ray to start casting + +buv060: + ACKCALL yRaySetup ; Set up y ray to start casting + +buv070: + mov [word ptr _LastWallHeight],0 ; For checking mult-height walls + ACKCALL BuildSlice ; Build the current slice + cmp [word ptr _gMultiWalls],0 ; Are multi-height walls used? + jz short buv080 ; Nope, no need to check + + cmp [word ptr _LastWallHeight],200 ; No need to check if > 200 + jg short buv080 + + mov eax,[_sPtr] ; Get base address of column slice + cmp [dword ptr eax+saNext],0 ; Check for any more walls + je short buv080 + cmp [word ptr eax+saDist],96 ; Distance from POV to slice is + jle short buv080 ; less than or equal to 96 + + ACKCALL BuildSliceMulti ; Build the current slice for + ; a multi-height wall +buv080: + movzx eax,[_ViewColumn] ; Get current column + inc eax ; Advance to the next column + inc edi ; Increment the casting angle + cmp [word ptr _Resolution],RES_LOW ; Check for screen resolution + jne short buv090 + inc edi ; Increment angle and position + inc eax ; for higher resolution casting + +buv090: + cmp edi,INT_ANGLE_360 ; Did we go past 360 degrees? + jl short buv100 ; We’re ok + sub edi,INT_ANGLE_360 ; Adjust angle for building next slice + +buv100: + mov [_ViewAngle],di ; Save current viewing angle + mov [_ViewColumn],ax ; Save column position + cmp eax,esi ; Are we done yet? + jl buv050 ; Nope; go build next slice + +buv_exit: + ACKCALL CheckHitMap + ACKCALL FindObject ; Update slice structures with objects found + call [dword ptr _FloorCeilRtn] ; Build the floor and ceiling + ACKCALL DrawWalls ; Build the walls + + pop edx ; Restore the registers used + pop ecx + pop ebx + pop edi + pop esi + pop ebp + ret + endp + + end + + \ No newline at end of file diff --git a/ack_lib/ACKRTN4.ASM b/ack_lib/ACKRTN4.ASM new file mode 100644 index 0000000..99d98bc --- /dev/null +++ b/ack_lib/ACKRTN4.ASM @@ -0,0 +1,412 @@ + + IDEAL + JUMPS + P386 + P387 ; Allow 386 processor + + + MASM + .MODEL FLAT ;32-bit OS/2 model + +.DATA + +SVTABLE dd ? + +SAVEVID dd ? +SAVEROW dd ? + + + .CODE + IDEAL + + + include "ackrtn.inc" + + extrn _BackDropRows:dword + extrn _PlayerAngle:word + extrn _BackArray:dword + extrn _Resolution:word + extrn _ScreenOffset:word + extrn _bmDistance:dword + extrn _bmWall:dword + extrn _scPtr:dword + extrn _VidTop:dword + extrn _VidBottom:dword + extrn _Floors1:dword + extrn _Floors2:dword + + extrn _gPalTable:dword + extrn _gWinStartX:word + extrn _gWinStartY:word + extrn _gWinEndX:word + extrn _gWinEndY:word + extrn _gWinHeight:word + extrn _ViewHeight:word + extrn _SysFlags:word + extrn _Slice:byte + extrn _gScrnBuffer:dword + extrn _gCenterOff:word + extrn _gCenterRow:word + extrn _Floorscr:dword + extrn _gWinStartOffset:dword + + extrn _scVid:dword + extrn _scWall:dword + extrn _scPal:dword + extrn _scdst:word + extrn _scwht:word + extrn _scmulti:word + extrn _sctopht:word + extrn _scbotht:word + extrn _scsavwht:word + extrn _scmulcnt:word + extrn _scsavVid:dword + extrn _scbNum:word + extrn _scMulData:dword + extrn _scColumn:dword + extrn _WallbMaps:dword + extrn _LowerTable:dword + extrn _gBottomOff:dword + extrn _LightFlag:word + + ACKEXT ShowCol + ACKEXT ShowColMask + + extrn _Resolution:word + extrn _Flooru:dword + extrn _Floorv:dword + extrn _Floordu:dword + extrn _Floordv:dword + extrn _Floorkx:dword + extrn _Floorky:dword + extrn _Floorku:dword + extrn _Floorkv:dword + extrn _Floorkdu:dword + extrn _Floorkdv:dword + extrn _Floorbm:dword + extrn _Floorscr:dword + extrn _Floors1:dword + extrn _Floors2:dword + extrn _FloorscrTop:dword + extrn _Floorptr2:dword + extrn _Floorwt:dword + extrn _Floorvht:word + extrn _Flooreht:word + extrn _FloorMap:word + extrn _gScrnBufferCenter:dword + extrn _gWinHalfHeight:word + extrn _zdTable:dword + extrn _CosTable:dword + extrn _SinTable:dword + extrn _xPglobal:dword + extrn _yPglobal:dword + extrn _WallDistTable:dword + extrn _CeilMap:word + extrn _AckTimerCounter:dword + + ACKPUBS Mymemset + ACKPUBS AckSpeedUp + ACKPUBS AckSlowDown + ACKPUBS ShowColNS + ACKPUBS ShowColMaskNS + ACKPUBS DrawBackDrop + ACKPUBS AckTimerHandler + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckTimerHandler + inc cs:[dword ptr _AckTimerCounter] + iretd + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC Mymemset + push edi + push ebx + push ecx + push edx + mov edi,eax + mov dh,dl + mov ax,dx + shl eax,16 + mov ax,dx + mov ecx,ebx + sar ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + pop edx + pop ecx + pop ebx + pop edi + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckSpeedUp + push ebx + push edx + mov bx,ax + mov ax,0FFFFH + xor dx,dx + idiv bx + mov bx,ax + mov dx,43h + mov al,36h + out dx,al + mov dx,40h + mov al,bl ; ffh = original value + out dx,al + mov dx,40h + mov al,bh ; 1fh = orignal value + out dx,al + pop edx + pop ebx + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckSlowDown + push edx + mov dx,43h + mov al,36h + out dx,al + mov dx,40h + mov al,0ffh + out dx,al + mov dx,40h + mov al,0ffh + out dx,al + pop edx + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawBackDrop + push ebp + push esi + push edi + push ebx + push ecx + push edx + mov ax,[_PlayerAngle] + sub ax,INT_ANGLE_32 + jnc ang_okay + add ax,INT_ANGLE_360 + +ang_okay: + mov cx,640 + cwd + idiv cx ; Do mod 640 to get starting posn + +; movsx ebx,dx + movzx ebx,dx + shl ebx,2 ; x 4 for memory pointers + mov edx,320 + mov ebp,[_gScrnBuffer] ; get screen buffer + +dbd010: + mov edi,ebp + mov esi,[dword ptr _BackArray+ebx] ;current image pointer + mov ecx,[_BackDropRows] ; rows to draw + +dbd020: +; mov al,[esi] +; mov [edi],al +; inc esi +; lea edi,[edi+320] + movsb + lea edi,[edi+319] + dec ecx + jnz dbd020 + + inc ebp ;next screen column + lea ebx,[ebx+4] + cmp ebx,2560 ;see if 640x4 column yet + jb short dbd030 ;nope + mov ebx,0 ;else wrap to 0 column + +dbd030: + dec edx ;see if done with all columns + jnz dbd010 + + pop edx + pop ecx + pop ebx + pop edi + pop esi + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC ShowColNS + push ebp + push esi + push edi + mov edi,[_scVid] + mov ebx,[_scWall] + mov ebp,[_bmDistance] + mov cx,[_gCenterRow] + mov edx,1FFFh + xor eax,eax + +sns_top: + mov al,dh + sub edx,ebp + mov al,[ebx+eax] + mov [edi],al + jc short sns_bot + sub edi,320 + dec cx + jns sns_top + xor ecx,ecx + +sns_bot: + mov [SAVEVID],edi + mov [SAVEROW],ecx + + mov edi,[_scVid] + mov cx,[_gCenterRow] + mov edx,2000h + +sns_botloop: + mov al,dh + add edi,320 + mov al,[ebx+eax] + add edx,ebp + mov [edi],al + cmp dh,64 + jae short sns_exit + dec cx + jnz sns_botloop + +sns_exit: + cmp [word ptr _scmulti],0 + jz sns_alldone + mov esi,[SAVEROW] + or si,si + jz sns_alldone + + mov ebx,[_scMulData] ;ptr to count and wall data + mov cl,[ebx] ;get number of walls to draw + inc ebx + mov al,[ebx] ;first wall to show + inc ebx + mov [_scMulData],ebx + movzx ebx,al ;get wall number + mov eax,[_WallbMaps] ;Get array of bitmaps + mov ebx,[eax+ebx*4] ;Get the bitmap we are using + mov eax,[_scColumn] + add ebx,eax + mov edi,[SAVEVID] + mov edx,3FFFh + mov ebp,[_bmDistance] + mov eax,0 + +sns_mulloop: + mov al,dh + sub edi,320 + mov al,[ebx+eax] + dec si + mov [edi],al + jz short sns_alldone + sub edx,ebp + jnc sns_mulloop + +sns_nextlevel: + dec cl ;Bump wall count + jz short sns_alldone ;br if no more walls + mov ebx,[_scMulData] ;Get pointer to the multi-ht data + movzx edx,[byte ptr ebx] ;next wall number + inc ebx ;Advance for next wall + mov [_scMulData],ebx + mov ebx,[_WallbMaps] ;Get wall array + mov ebx,[ebx+edx*4] ;Get wall bitmap to use + add ebx,[dword ptr _scColumn] ;add in current column + mov edx,3FFFh + jmp sns_mulloop + +sns_alldone: + + pop edi + pop esi + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC ShowColMaskNS + push ebp + push esi + push edi + mov edi,[_scVid] + mov ebx,[_scWall] + mov ebp,[_bmDistance] + mov cx,[_gCenterRow] + mov edx,1FFFh + xor eax,eax + +smns_top: + mov al,dh + mov al,[ebx+eax] + or al,al + jz short smns_blank + mov [edi],al + +smns_blank: + sub edx,ebp + jc short smns_bot + sub edi,320 + dec cx + jnz smns_top + +smns_bot: + mov edi,[_scVid] + mov cx,[_gCenterRow] + mov edx,2000h + +smns_botloop: + add edi,320 + mov al,dh + mov al,[ebx+eax] + or al,al + jz short smns_blank1 + mov [edi],al + +smns_blank1: + add edx,ebp + cmp dh,64 + jae short smns_exit + dec cx + jnz smns_botloop + +smns_exit: + pop edi + pop esi + pop ebp + ret + endp + + end + + \ No newline at end of file diff --git a/ack_lib/ACKRTN5.ASM b/ack_lib/ACKRTN5.ASM new file mode 100644 index 0000000..bd2ed79 --- /dev/null +++ b/ack_lib/ACKRTN5.ASM @@ -0,0 +1,552 @@ + + IDEAL + JUMPS + P386 + P387 ; Allow 386 processor + + + MASM + .MODEL FLAT ;32-bit OS/2 model + IDEAL + + + include "ackrtn.inc" + + extrn _WallDistTable:dword + extrn _FloorMap:word + extrn _CeilMap:word + extrn _LastWallHeight:word + extrn _ViewAngle:word + extrn _ScreenOffset:word + extrn _xPglobal:dword + extrn _yPglobal:dword + extrn _xBegGlobal:dword + extrn _yBegGlobal:dword + extrn _aeGlobal:dword + extrn _xGridGlobal:dword + extrn _yGridGlobal:dword + extrn _xPglobalHI:dword + extrn _yPglobalHI:dword + extrn _rbaTable:dword + extrn _rsHandle:word + extrn _LastX1:dword + extrn _LastY1:dword + extrn _iLastX:dword + extrn _iLastY;dword + extrn _MaxDistance:word + extrn _BackArray:dword + extrn _zdTable:dword + extrn _ErrorCode:word + extrn _xMapPosn:dword + extrn _yMapPosn:dword + extrn _Grid:dword + extrn _ObjGrid:dword + extrn _WallbMaps:dword + extrn _ViewHeight:word + extrn _CeilingHeight:word + extrn _gTopColor:byte + extrn _gBottomColor:byte + extrn _PlayerAngle:word + extrn _gScrnBuffer:dword + extrn _gBkgdBuffer:dword + extrn _gCenterOff:word + extrn _gWinStartOffset:dword + extrn _gWinHeight:word + extrn _gWinEndY:dword + extrn _SysFlags:word + extrn _sPtr:dword + extrn _mxGridGlobal:dword + extrn _myGridGlobal:dword + + extrn _xSecretmPos:word + extrn _xSecretmPos1:word + extrn _xSecretColumn:word + + extrn _ySecretmPos:word + extrn _ySecretmPos1:word + extrn _ySecretColumn:word + + extrn _TotalSecret:word + extrn _ViewColumn:word + extrn _SinTable:dword + extrn _CosTable:dword + extrn _LongTanTable:dword + extrn _LongInvTanTable:dword + extrn _InvCosTable:byte + extrn _InvSinTable:byte + extrn _LongCosTable:dword + extrn _ViewCosTable:dword + extrn _xNextTable:dword + extrn _yNextTable:dword + + extrn _LastMapPosn:word + extrn _LastObjectHit:word + extrn _TotalObjects:word + extrn _FoundObjectCount:word + extrn _ObjectsSeen:byte + extrn _MoveObjectCount:word + extrn _MoveObjectList:byte + extrn _ObjNumber:byte + extrn _ObjRelDist:byte + extrn _ObjColumn:byte + + extrn _x_xPos:dword + extrn _x_yPos:dword + extrn _x_xNext:dword + extrn _x_yNext:dword + extrn _y_xPos:dword + extrn _y_yPos:dword + extrn _y_xNext:dword + extrn _y_yNext:dword + + extrn _Resolution:word + extrn _Flooru:dword + extrn _Floorv:dword + extrn _Floordu:dword + extrn _Floordv:dword + extrn _Floorkx:dword + extrn _Floorky:dword + extrn _Floorku:dword + extrn _Floorkv:dword + extrn _Floorkdu:dword + extrn _Floorkdv:dword + extrn _Floorbm:dword + extrn _Floorscr:dword + extrn _Floors1:dword + extrn _Floors2:dword + extrn _FloorscrTop:dword + extrn _Floorptr2:dword + extrn _Floorht:dword + extrn _Floorwt:dword + extrn _Floorvht:word + extrn _Flooreht:word + extrn _FloorLastbNum:dword + extrn _FloorLastbm:dword + + extrn _bmDistance:dword + extrn _scwht:word + extrn _scWall:dword + extrn _scPal:dword + extrn _scVid:dword + extrn _scantables:dword + + ACKEXT AckDrawFloor + ACKEXT AckDrawFloorOnly + ACKEXT AckDrawCeilingOnly + ACKEXT DrawBackDrop + ACKEXT AckDrawCeilingOnlyNS + ACKEXT AckDrawFloorOnlyNS + + ACKPUBS AckDrawFloorNS + ACKPUBS DrawSolidCeilAndFloorNS + ACKPUBS DrawSolidCeilAndFloor + ACKPUBS DrawSolidFloorAndCeilNS + ACKPUBS DrawSolidFloorAndCeil + ACKPUBS DrawSolidCeilSolidFloor + ACKPUBS AckDoubleBuffer + + MASM + .DATA +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; Globals used by the AckDrawFloor routine +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +even + +BCOL dd ? +HEIGHT dd ? +VA dd ? +SY dd ? +EY dd ? +BFSCRN dd ? +BCSCRN dd ? +FSCRN dd ? +CSCRN dd ? +CV dd ? +SV dd ? +BA dd ? +BA1 dd ? +ZDPTR dd ? +POS dd ? +BMPOS dd ? +MPOS dd ? +MPOSHI dd ? +SCANTBL dd ? +ROWNUM dd ? +LASTDIST dd ? +LASTEBP dd ? +LASTEAX dd ? +LASTEDX dd ? +WALLDIST dd ? +DSTPTR dd ? +COLNUM dd ? + + .CODE + IDEAL + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckDrawFloorNS + push ebp + push esi + push edi + push ebx + push ecx + push edx + + movzx eax,[word ptr _PlayerAngle] + mov ecx,eax + sub eax,INT_ANGLE_32 + jnc short adf_20 + add eax,INT_ANGLE_360 + +adf_20: + mov ebx,640 + cdq + idiv ebx + mov [BCOL],edx + +;;; mov eax,89 +;;; sub ax,[word ptr _ViewHeight] +;;; mov [HEIGHT],eax + + sub ecx,INT_ANGLE_32 + jnc short adf_30 + add ecx,INT_ANGLE_360 + +adf_30: + mov [VA],ecx + movzx eax,[word ptr _gWinHeight] + sar eax,1 + movzx ebx,[word ptr _gWinEndY] + sub ebx,eax + inc ebx + sub ebx,5 ; 6 + mov [EY],ebx + mov edi,[_gScrnBuffer] + movzx eax,[word ptr _gCenterOff] + mov ebx,eax + add eax,1920 + add eax,edi + mov [BFSCRN],eax + sub ebx,1600 ;1920 + add ebx,edi + mov [BCSCRN],ebx + mov eax,0 + mov [DSTPTR],eax + mov ebx,[VA] + +adf_loop: + +;;; push ebp + +even + + mov [COLNUM],eax + mov eax,[dword ptr _WallDistTable+eax*4] + cmp eax,96 + jb adf_ynext + mov [WALLDIST],eax + + mov eax,[_CosTable] + shl ebx,2 + mov eax,[eax+ebx] + mov [CV],eax + mov eax,[_SinTable] + mov eax,[eax+ebx] + mov [SV],eax + + + mov eax,[BCSCRN] + mov [CSCRN],eax + + mov ecx,[EY] + mov ebx,[BCOL] + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA],eax + inc ebx + cmp ebx,640 + jb short adf_l10 + sub ebx,ebx + +adf_l10: + mov eax,[_BackArray+ebx*4] + add eax,ecx + mov [BA1],eax + inc ebx + cmp ebx,640 + jb short adf_l20 + sub ebx,ebx + +adf_l20: + mov [BCOL],ebx + lea esi,[offset _zdTable] + mov ecx,[EY] ;Number of rows to draw + mov [ROWNUM],ecx +;; imul eax,ebp,800 + mov eax,[DSTPTR] + + add esi,eax + add esi,24 ;ebx + + add eax,1600 + mov [DSTPTR],eax + + mov edi,[BFSCRN] + mov ecx,[_WallbMaps] + +adf_yloop: + mov edx,[esi] + cmp edx,[WALLDIST] + lea esi,[esi+4] + ja adf_ycont + +adf_distokay: + cmp [LASTDIST],edx + jne short adf_newdist + mov ebp,[LASTEBP] + mov eax,[LASTEAX] + mov edx,[LASTEDX] + jmp short adf_samedist + +adf_newdist: + mov [LASTDIST],edx + + mov eax,[CV] + mov ebx,[SV] + imul eax,edx + imul ebx,edx + sar eax,16 + sar ebx,16 + mov edx,[_xPglobal] + add eax,edx + mov edx,[_yPglobal] + add ebx,edx + mov edx,ebx + and edx,0FC0h + mov ebp,eax + sar ebp,6 + add ebp,edx ;Pos within floor and ceiling maps + + and ebx,63 + shl ebx,6 + and eax,63 + add eax,ebx ;bitmap position + + shl ebp,1 + movzx ebx,[word ptr _FloorMap+ebp] + mov [LASTEBP],ebp + mov [LASTEAX],eax + mov edx,[ecx+ebx*4] + add edx,eax + mov dl,[edx] + mov dh,dl + mov [LASTEDX],edx + +adf_samedist: + mov [edi],dx + + movzx ebx,[word ptr _CeilMap+ebp] + mov ebp,[CSCRN] + test bx,bx + jz short adf_yback + + mov edx,[ecx+ebx*4] + dec [dword ptr BA] + add edx,eax + mov al,[edx] + mov ah,al + lea edi,[edi+320] + dec [dword ptr BA1] + mov [ebp],ax + + add ebp,-320 + mov [CSCRN],ebp + dec [dword ptr ROWNUM] + jnz adf_yloop + + jmp short adf_ynext + +adf_yback: + mov eax,[BA] + mov dl,[eax] + mov eax,[BA1] + mov dh,[eax] + mov [ebp],dx + +adf_ycont: + lea edi,[edi+320] + add [dword ptr CSCRN],-320 + dec [dword ptr BA] + dec [dword ptr BA1] + dec [dword ptr ROWNUM] + jnz adf_yloop + +adf_ynext: + mov ebx,[VA] + lea ebx,[ebx+2] + cmp ebx,INT_ANGLE_360 + jb short adf_l90 + sub ebx,INT_ANGLE_360 + +adf_l90: + mov [VA],ebx ;Note: EBX is used for VA at top of loop! + add [dword ptr BFSCRN],2 + add [dword ptr BCSCRN],2 +;;; pop ebp +;;; lea ebp,[ebp+2] +;;; cmp ebp,320 +;;; jb adf_loop + mov eax,[COLNUM] + add eax,2 + cmp eax,320 + jb adf_loop + +adf_exit: + pop edx + pop ecx + pop ebx + pop edi + pop esi + pop ebp + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawSolidCeilAndFloor + mov edi,[_gScrnBuffer] + movzx ecx,[word ptr _gCenterOff] + mov al,[byte ptr _gTopColor] + mov ah,al + shr cx,1 + rep stosw + rcl cx,1 + rep stosb + ACKCALL AckDrawFloorOnly + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawSolidCeilAndFloorNS + mov edi,[_gScrnBuffer] + movzx ecx,[word ptr _gCenterOff] + mov al,[byte ptr _gTopColor] + mov ah,al + shr cx,1 + rep stosw + rcl cx,1 + rep stosb + ACKCALL AckDrawFloorOnlyNS + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawSolidFloorAndCeilNS + mov edi,[_gScrnBuffer] + movzx ecx,[word ptr _gCenterOff] + add edi,ecx + mov al,[byte ptr _gBottomColor] + mov ah,al + shr cx,1 + rep stosw + rcl cx,1 + rep stosb + ACKCALL AckDrawCeilingOnlyNS + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawSolidFloorAndCeil + mov edi,[_gScrnBuffer] + movzx ecx,[word ptr _gCenterOff] + add edi,ecx + mov al,[byte ptr _gBottomColor] + mov ah,al + shr cx,1 + rep stosw + rcl cx,1 + rep stosb + ACKCALL AckDrawCeilingOnly + ret + endp + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC DrawSolidCeilSolidFloor + mov edi,[_gScrnBuffer] + movzx ecx,[word ptr _gCenterOff] + mov al,[byte ptr _gTopColor] + mov ah,al + shr cx,1 + rep stosw + rcl cx,1 + rep stosb + mov edi,[_gScrnBuffer] + movzx ecx,[word ptr _gCenterOff] + add edi,ecx + mov al,[byte ptr _gBottomColor] + mov ah,al + shr cx,1 + rep stosw + rcl cx,1 + rep stosb + ret + endp + + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +ACKPROC AckDoubleBuffer + push ebp + mov ebp,esp + push esi + push edi + + mov esi,[ebp+8] ;Source buffer + mov edi,[ebp+12] ;Destination buffer + + mov ecx,200 ;Rows to double + +adb010: + mov edx,160 ;Cols to double + mov ebx,edi ;Hold onto start of row +adb020: + lodsw + stosb + stosb + mov al,ah ;Make word + stosw ;Store in dest + dec edx ;Bump column count + jnz adb020 + sub esi,4 + mov eax,esi ;Hold onto source + mov esi,ebx ;Point at start of row + mov edx,ecx ;hold onto row count + mov ecx,160 ;Number of dwords + rep movsd ;duplicate the row + mov esi,eax ;get back the source + mov ecx,edx ;get row count back + dec ecx + jnz adb010 ;loop for all rows + + pop edi + pop esi + pop ebp + ret + endp + + end + + \ No newline at end of file diff --git a/ack_lib/ACKSND.C b/ack_lib/ACKSND.C new file mode 100644 index 0000000..3c19b28 --- /dev/null +++ b/ack_lib/ACKSND.C @@ -0,0 +1,348 @@ +/******************* ( Animation Construction Kit 3D ) ***********************/ +/* Sound Routines: play CMF, VOC files in background on SB, Adlib or speaker */ +/* CopyRight (c) 1993 Authors: Lary Myers & Frank Sachse */ +/*****************************************************************************/ + +//***************************** WARNING ************************************** +// NOTE: The Worx sound functions have NOT been converted to flat model. +// **** DO NOT USE THESE FUNCTIONS!!!! **** +//***************************** WARNING ************************************** + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +#include "acksnd.h" +#include "worx.h" + +static short SoundDevice; + +void AckPlayNoSound(short index); +void AckPlayPCSpeaker(short index); +void AckPlayAdlib(short index); +void AckPlaySoundBlaster(short index); +short AckPlayMusic(char *MusicFile); + +short IRQ; /* SB */ + char *Cmf; + char *VocTable[SOUND_MAX_INDEX+1]; + + +/**************************************************************************** +** Call into here by the application before using any of the sound routines** +** The default sound device can be used as an overide, such as when the ** +** application or user doesn't want any sound. ** +****************************************************************************/ +short AckSoundInitialize(short DefaultSoundDevice) +{ + +if (DefaultSoundDevice == DEV_NOSOUND) + { + SoundDevice = DefaultSoundDevice; + return(0); + } + +/* Checks for sound board & sets SoundDevice to DEV_ define in ACKSND.H */ + +/* for use only with WORX lib, not WORXlite... +StartWorx(); +*/ + +StartWorx(); + +if (DefaultSoundDevice == DEV_PCSPEAKER) + { + SoundDevice = DefaultSoundDevice; + return(0); + } + +if (AdlibDetect()) + { + SoundDevice = DEV_ADLIB; + SetFMVolume(0xf,0xf); + } +else + SoundDevice = DEV_PCSPEAKER; + +if (DefaultSoundDevice == DEV_ADLIB) + { + SoundDevice = DEV_ADLIB; + return(0); + } + +IRQ = DSPReset(); +if (IRQ > 0) + { + SoundDevice = DEV_SOUNDBLASTER; + SetMasterVolume(0xf,0xf); + SetVOCVolume(0xf,0xf); + } + +#if 0 +/* for use only with WORX lib, not WORXlite... +IRQ = DSPReset() - if > 0, then SB present +if (WorxPresent()) /* use only with WORXlite */ + { + SoundDevice = DEV_SOUNDBLASTER; + SetMasterVolume(0xf,0xf); + SetVOCVolume(0xf,0xf); + } +*/ +#endif + +return(0); +} + +/**************************************************************************** +** Here the application can call in and load the VOC files that it needs ** +** ** +****************************************************************************/ +short AckLoadSound(short VocIndex,char *VocFile) +{ + char buf[81]; + +if (SoundDevice == DEV_NOSOUND) + return(0); + +if (VocIndex < 0 || VocIndex > SOUND_MAX_INDEX) + return(-1); /* index out of range */ + +strcpy(buf,VocFile); +strtok(buf,"."); + +switch (SoundDevice) + { + case DEV_SOUNDBLASTER: + strcat(buf,".VOC"); /* force extension */ + break; + + case DEV_ADLIB: /* adlib can't play voc's -> put thru pc spkr */ + case DEV_PCSPEAKER: + #if 0 + strcat(buf,".PWM"); /* force extension */ + #endif + strcat(buf,".VOC"); /* force extension */ + break; + + default: + return(-2); /* Error if unknown device */ + } + +VocTable[VocIndex] = LoadOneShot(buf); /* load voc/pwm into mem */ + +if (VocTable[VocIndex] == NULL) + return(-2); /* file not found */ + +return(0); +} + +/**************************************************************************** +** ** +****************************************************************************/ +void AckStopBackground(void) +{ + +switch (SoundDevice) + { + + case DEV_NOSOUND: + break; + + case DEV_PCSPEAKER: + break; + + case DEV_SOUNDBLASTER: + if (SequencePlaying()) + StopSequence(); + break; + + case DEV_ADLIB: + if (SequencePlaying()) + StopSequence(); + break; + + default: + break; + } + +} + + +/**************************************************************************** +** The Application would call this routine to begin playing background ** +** music. ** +** ** +****************************************************************************/ +short AckPlayBackground(char *MusicFile) +{ + short result = 0; + +switch (SoundDevice) + { + + case DEV_NOSOUND: + break; + + case DEV_PCSPEAKER: + break; + + case DEV_SOUNDBLASTER: + result = AckPlayMusic(MusicFile); + break; + + case DEV_ADLIB: + result = AckPlayMusic(MusicFile); + break; + + default: + break; + } + +return(result); +} + + +/**************************************************************************** +** Start the music file playing in this routine. ** +** ** +****************************************************************************/ +short AckPlayMusic(char *MusicFile) +{ + char *BufPtr; + char buf[81]; + +strcpy(buf,MusicFile); +strtok(buf,"."); /* force CMF extention */ +strcat(buf,".CMF"); + +Cmf = GetSequence(buf); /* load cmf into mem */ + +if(Cmf==NULL) + return(1); + +SetLoopMode(1); /* set for continuous play */ +PlayCMFBlock(Cmf); /* play background cmf */ + +return(0); +} + + +/**************************************************************************** +** Call into here to play a particular sound. The indexes available are ** +** listed in ACKSND.H which will be included by the application. ** +** ** +****************************************************************************/ +void AckPlaySound(short SoundIndex) +{ + +switch (SoundDevice) + { + + case DEV_NOSOUND: + break; + + case DEV_PCSPEAKER: + AckPlayPCSpeaker(SoundIndex); + break; + + case DEV_SOUNDBLASTER: + AckPlaySoundBlaster(SoundIndex); + break; + + case DEV_ADLIB: /* can't play VOC's on Adlib -> play thru speaker */ + AckPlayPCSpeaker(SoundIndex); + break; + + default: + break; + } + +} + + +/**************************************************************************** +** This routine is used for simple speaker sounds. The ones here are for ** +** testing at this point. Better ones would be need to be in the final ** +** version. ** +****************************************************************************/ +/* I will adjust this later to play VOC's thru speaker x*/ +void AckPlayPCSpeaker(short index) +{ + +if (VocTable[index] == NULL || index < 0 || index > SOUND_MAX_INDEX) + return; + +PlayPWMBlock(VocTable[index]); + +} + + +/**************************************************************************** +** Sound Blaster routines go here. ** +** ** +** ** +****************************************************************************/ +void AckPlaySoundBlaster(short index) +{ + +if (VocTable[index] == NULL || index < 0 || index > SOUND_MAX_INDEX) + return; + +PlayVOCBlock(VocTable[index],255); + +} + + +/**************************************************************************** +** Call into here by the application before exiting. ** +****************************************************************************/ +void AckSoundShutdown(void) +{ + +switch (SoundDevice) + { + + case DEV_NOSOUND: + break; + + case DEV_PCSPEAKER: + CloseWorx(); + break; + + case DEV_SOUNDBLASTER: + if (SequencePlaying()) + StopSequence(); /* stop background CMF, if playing */ + DSPClose(); /* free Sound Blaster DSP */ + /* for use only with WORX lib, not WORXlite... + CloseWorx(); + */ + CloseWorx(); + break; + + case DEV_ADLIB: + if (SequencePlaying()) + StopSequence(); + /* for use only with WORX lib, not WORXlite... + CloseWorx(); + */ + break; + + default: + break; + } + +} + + \ No newline at end of file diff --git a/ack_lib/ACKSND.H b/ack_lib/ACKSND.H new file mode 100644 index 0000000..f3e0bb1 --- /dev/null +++ b/ack_lib/ACKSND.H @@ -0,0 +1,35 @@ +/* Header file to accompany ACKSND.C */ + +#define DEV_NOSOUND 0 +#define DEV_PCSPEAKER 1 +#define DEV_SOUNDBLASTER 2 +#define DEV_ADLIB 3 + +#define SOUND_WALKING 0 +#define SOUND_HITWALL 1 +#define SOUND_HITOBJECT 2 +#define SOUND_HITBADOBJECT 3 +#define SOUND_DOOROPENING 4 +#define SOUND_SECRETDOOROPENING 5 +#define SOUND_FIRING 6 +#define SOUND_DOORCLOSING 7 +#define SOUND_LOST 8 +#define SOUND_WON 9 +#define SOUND_INTRO 10 +#define SOUND_EXPLODE 11 +#define SOUND_USER1 12 +#define SOUND_USER2 13 +#define SOUND_USER3 14 +#define SOUND_USER4 15 + +#define SOUND_MAX_INDEX 15 + + +short AckSoundInitialize(short DefaultSoundDevice); +short AckPlayBackground(char *MusicFile); +void AckPlaySound(short SoundIndex); +void AckSoundShutdown(void); +void AckStopBackground(void); +short AckLoadSound(short VocIndex,char *VocFileName); + + \ No newline at end of file diff --git a/ack_lib/ACKUTIL.C b/ack_lib/ACKUTIL.C new file mode 100644 index 0000000..96230ee --- /dev/null +++ b/ack_lib/ACKUTIL.C @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +typedef struct { + int sel; + int off; + } SELOFF; + +void AckGetIntVector(int VecNum,int *sel,int *off); +void AckSetIntVector(int VecNum,int sel,void *VecOff); +void AckKbdInt(void); +void AckTimerHandler(void); +void AckSetTextMode(void); + + long AckMemUsed; + short AckDisplayErrors; + SELOFF OldKeybdInt; + char AckKeyboardSetup; + SELOFF OldTimerInt; + char AckTimerSetup; + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Establish a hook into interrupt 9 for keyboard handling +// The application can access which key is pressed by looking at the +// AckKeys global array +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +#ifndef _MSC_VER +void AckSetupKeyboard(void) +{ +AckGetIntVector(9,&OldKeybdInt.sel,&OldKeybdInt.off); +AckSetIntVector(9,_CS,AckKbdInt); +AckKeyboardSetup = 1; +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Establish a hook into the user timer interrupt +// The application can access a counter by looking at the AckTimerCounter +// global variable. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckSetupTimer(void) +{ +AckGetIntVector(0x1C,&OldTimerInt.sel,&OldTimerInt.off); +AckSetIntVector(0x1C,_CS,AckKbdInt); +AckTimerSetup = 1; +} +#endif + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Utility routine used to track memory usage by the ACK engine and +// applications. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void *AckMalloc(size_t mSize) +{ + UCHAR *mBlock; + +mSize += sizeof(long); +mSize++; +mBlock = malloc(mSize); + +if (mBlock == NULL) + { + if (AckDisplayErrors) + { + AckSetTextMode(); + printf("\n\nOut of memory on call to AckMalloc.\n"); + printf("Memory used: %ld bytes.\n",AckMemUsed); + } + return(mBlock); + } + +(*(UCHAR *)mBlock) = 0xF2; +mBlock += 1; +(*(long *)mBlock) = mSize; +mBlock += sizeof(long); +AckMemUsed += mSize; + +return(mBlock); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Matching routine for AckMalloc(). This routine MUST be used to free +// memory if AckMalloc() is used to allocate memory. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckFree(void *m) +{ + UCHAR *mBlock; + long mSize; + +mBlock = (UCHAR *)m; +mBlock -= sizeof(long); +mBlock -= 1; +if ((*(UCHAR *)mBlock) != 0xF2) + { + if (AckDisplayErrors) + { + AckSetTextMode(); + printf("\n\nCorrupt memory block in AckFree.\n"); + printf("Mem ptr: %p",mBlock); + return; + } + } + +mBlock += 1; +mSize = (*(long *)mBlock); +mBlock -= 1; +AckMemUsed -= mSize; +free(mBlock); + +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Read a palette from a file and immediately set it into the VGA regs. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckLoadAndSetPalette(char *PalName) +{ + short handle,ErrCode; + char *buf; + +buf = AckMalloc(800); +if (buf == NULL) + return(ERR_NOMEMORY); + +ErrCode = 0; +if (!rsHandle) + handle = _lopen(PalName,O_RDWR|O_BINARY); +else + { + handle = rsHandle; + _llseek(handle,rbaTable[(ULONG)PalName],SEEK_SET); + } + +if (handle > 0) + { + read(handle,buf,768); + if (!rsHandle) + _lclose(handle); + + memset(buf,0,3); // Make sure color 0 is always black + AckSetPalette(buf); + } +else + ErrCode = ERR_BADFILE; + +AckFree(buf); +return(ErrCode); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Set up the palette range for light shading. The incoming ranges are in +// a 16 by 256 array where there are 16 different distance levels each +// having a full color tranlation table for light shading. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckSetupPalRanges(ACKENG *ae,ColorRange *ranges) +{ + short i,j,k,found; + short rangenos; + UCHAR plotcolor; + +if (ae->LightFlag == SHADING_OFF) + { + for ( i = 0;i<16;i++) + { + for (j=0;j<256;j++) + { + ae->PalTable[j+(i*256)] = j; + } + } + return; + } + +for (rangenos = 0; rangenos < 64; rangenos++) + { + if (ranges[rangenos].start == 0) + break; + } + +for ( i = 0;i<16;i++) + { + for (j=0;j<256;j++) + { + found = 0; + // find the range the color is in. + for ( k = 0; k < rangenos; k++ ) + { + if (j >= ranges[k].start && j < ranges[k].start+ranges[k].length) + { + found = 1; + break; + } + } + if (found) + { +//============================================================================= +// add color + i; +// if color + i > color+range then plot color = 0; +// otherwise plotcolor = color+i +//============================================================================= + if (j+i >= ranges[k].start+ranges[k].length) + plotcolor = 0; + else + plotcolor = j+i; + } + else + { + plotcolor = j; + } + ae->PalTable[j+(i*256)] = plotcolor; + } + } + + +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Returns the index of the last object hit by the POV. +// The variable LastObjectHit can also be accessed globally. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckGetObjectHit(void) +{ +return(LastObjectHit); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Returns the map location of the last wall hit. LastMapPosn is a global +// variable. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckGetWallHit(void) +{ +return(LastMapPosn); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Sets the object to inactive. The memory used by the object is NOT +// freed by this routine. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckDeleteObject(ACKENG *ae,short ObjIndex) +{ + +if (ae->ObjList[ObjIndex] == NULL) + return(-1); + +if (!ae->ObjList[ObjIndex]->Active) + return(-1); + +ae->ObjList[ObjIndex]->Active = 0; + +return(0); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Sets a new wall or object index into the map array specified. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckSetNewBitmap(short index,UCHAR **Maps,UCHAR *NewBitmap) +{ + UCHAR *bPtr; + +bPtr = Maps[index]; +Maps[index] = NewBitmap; + +if (bPtr != NULL) + AckFree(bPtr); + +return(0); +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Obsolete routine for real mode. Flat model memory can be freed by the +// application. Real mode required XMS memory to be handled. This routine +// is maintained for backward compatability with the older versions. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckFreeBitmap(UCHAR *bmType) +{ + short i; + UCHAR *Bmp; + +if (bmType != NULL) + AckFree(bmType); + +return(0); +} + +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKVIEW.C b/ack_lib/ACKVIEW.C new file mode 100644 index 0000000..3398df2 --- /dev/null +++ b/ack_lib/ACKVIEW.C @@ -0,0 +1,851 @@ +// This file contains the declarations and functions to set up views for the +// ray casting engine. +#include // Required for Windows version of engine +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" // Main ACK-3D internal and interface data structures +#include "ackeng.h" // Intrnal structures and constants +#include "ackext.h" // Defines external (global) variables + +extern long FloorCosTable[]; + +void (*FloorCeilRtn)(void); +void (*WallRtn)(void); +void (*WallMaskRtn)(void); + +short gWinFullWidth; // Global variables for setting up a viewport +long gWinDWORDS; // These are the global variables used by the +long gWinStartOffset; // low-level assembly language routines to draw slices +short gWinStartX; +short gWinStartY; +short gWinEndX; +short gWinEndY; +short gWinHeight; +short gWinHalfHeight; +short gWinWidth; +short gCenterRow; +short gCenterOff; +long gBottomOff; +UCHAR *gScrnBufferCenter; +UCHAR *gScrnBuffer; +UCHAR *gBkgdBuffer; +UCHAR *gPalTable; +short gMultiWalls; + +UCHAR **mxGridGlobal; // Global variables to reference the x and y +UCHAR **myGridGlobal; // map arrays + +UCHAR gTopColor; +UCHAR gBottomColor; + +UCHAR *scVid; // Variables used in low level routines for +UCHAR *scWall; // building and drawing slices +UCHAR *scPal; +short scdst; +short scwht; +short scmulti; +short sctopht; +short scbotht; +short scsavwht; +short scmulcnt; +UCHAR *scsavVid; +USHORT scbNum; +UCHAR *scMulData; +UCHAR *scColumn; +UCHAR *gPtr; +UCHAR *gmPtr; +short gBitmapNumber; +short gBitmapColumn; +short gyBitmapNumber; +short gyBitmapColumn; +long gWallDistance; +short gmPos; +DOORS *gDoor; +DOORS *gDoorPosn; +short wFound; +UCHAR *mgPtr; + +short BegX,EndX; + +extern long x_xPos; // Variables for tracking coordinates during +extern long x_yPos; // the ray casting process +extern long x_xNext; +extern long x_yNext; +extern long y_xPos; +extern long y_yPos; +extern long y_xNext; +extern long y_yNext; + +short LastVht; +long WallDistTable[VIEW_WIDTH]; + +// Functions used to build views and perform the ray casting process +void AckSetupWindow(ACKENG *ae); // Sets up variables for viewport +void BuildUpView(void); // Main assemply language routine for building views +void BuildSlice(void); // Assembly language routines for building slices +void BuildSliceMulti(void); // Assembly language routine for building multi-slices +void CheckDoors(void); // Internal routines for locating and checking doors +void FindObject(void); // and objects +short FindDoor(short MapPosn); + +void FloorLoop(void); +void CeilLoop(void); +void DrawSlices(void); +short BlankSlice(USHORT col,UCHAR *bmp); +void BuildSliceAsm(void); + +void xRaySetup(void); // Routines for setting up and casting rays +USHORT xRayCast(void); +void yRaySetup(void); +USHORT yRayCast(void); +USHORT xRayCastMulti(void); +USHORT yRayCastMulti(void); + +void ShowCol(void); // Routines for drawing a slice +void ShowColMask(void); // column by column +void ShowColNS(void); +void ShowColMaskNS(void); +void ShowColLow(void); +void ShowColMaskLow(void); + +void DrawFloorCeiling(void); // Routines for drawing floors and ceilings +void AckDrawFloor(void); +void AckDrawFloorOnly(void); +void AckDrawCeilingOnly(void); +void AckDrawFloorNS(void); +void AckDrawFloorOnlyNS(void); +void AckDrawCeilingOnlyNS(void); +void AckDrawFloorHz(void); +void AckDrawOneFloor(void); +void DrawSolidCeilAndFloor(void); +void DrawSolidCeilAndFloorNS(void); +void DrawSolidFloorAndCeil(void); +void DrawSolidFloorAndCeilNS(void); +void DrawSolidCeilSolidFloor(void); + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Transfers certain variables from the interface structure to global +// variables that can be accessed faster by the drawing functions. The +// interface structure is kept by the application and more than one of +// them can be used for different views. Each time a new view needs to +// be processed, this function MUST be called before calling the +// drawing routines. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckRegisterStructure(ACKENG *ae) +{ + int mode,i; + +aeGlobal = ae; // Global variable to reference ACKENG structure +AckSetupWindow(ae); // Assign window variables to ACKENG structure +xGridGlobal = ae->xGrid; // Global map for x walls +yGridGlobal = ae->yGrid; // Global map for y walls +mxGridGlobal = ae->mxGrid; // Wall data for multi-height walls +myGridGlobal = ae->myGrid; +WallbMaps = ae->bMaps; // Wall bitmap data +gWinStartX = ae->WinStartX; // Coordinates of viewport; upper-left +gWinStartY = ae->WinStartY; +gWinEndX = ae->WinEndX; // Lower-right viewport coordinates +gWinEndY = ae->WinEndY; +gWinHeight = ae->WinHeight; // Height of viewport +gWinWidth = ae->WinWidth; // Width of viewport +gWinHalfHeight = (gWinEndY - (gWinHeight >> 1)) + 1; +gCenterRow = ae->CenterRow; // Start of center row in viewport +gCenterOff = ae->CenterOffset; // Offset to center of viewport +gScrnBuffer = ae->ScreenBuffer; // Screen buffer access +gScrnBufferCenter = gScrnBuffer + gCenterOff; +gBkgdBuffer = ae->BkgdBuffer; // Buffer for ceiling and floors +gPalTable = ae->PalTable; // Palette of colors used +gDoor = &ae->Door[0]; // List of moving doors +gTopColor = ae->TopColor; // Base color of ceiling +gBottomColor = ae->BottomColor; // Base color of floor +LightFlag = ae->LightFlag; // Light shading on or off indicator +SysFlags = ae->SysFlags; // Scene display attributes (floors and ceilings) + +mode = 0; // Draw both textured floor and ceiling +if (SysFlags & SYS_SOLID_CEIL) // Soild ceiling is selcted + { + mode = 1; // Draw floor only (ceiling will be solid) + if (SysFlags & SYS_SOLID_FLOOR) // Solid floor is selcted + mode = 2; // Draw solid floor and ceiling + } + +if (SysFlags & SYS_SOLID_FLOOR) // Solid floor is selected + { + if (!mode) + mode = 3; // Draw Ceiling only (floor will be solid) + } + +if (!LightFlag) // No light shading used + { + WallRtn = ShowColNS; // Assembly routines for drawing slices + WallMaskRtn = ShowColMaskNS; // using light shading + switch (mode) // Check floor and ceiling type + { + case 0: // Draw both solid floor and ceiling + if (ae->SysFlags & SYS_SINGLE_BMP) + FloorCeilRtn = AckDrawOneFloor; // Use the same bitmap for each + else + FloorCeilRtn = AckDrawFloorHz; + break; + case 1: // Draw solid ceiling and texture floor + FloorCeilRtn = DrawSolidCeilAndFloorNS; + break; + case 2: // Draw both solid floor and solid ceiling + FloorCeilRtn = DrawSolidCeilSolidFloor; + break; + case 3: // Draw solid floor and texture ceiling + FloorCeilRtn = DrawSolidFloorAndCeilNS; + break; + default: + break; + } + } +else // Light shading is used + { + WallRtn = ShowCol; // Assembly routines for drawing slices + WallMaskRtn = ShowColMask; // using light shading + switch (mode) + { + case 0: // Draw both floor and ceiling + FloorCeilRtn = AckDrawFloor; + break; + case 1: // Draw solid ceiling and texture floor + FloorCeilRtn = DrawSolidCeilAndFloor; + break; + case 2: // Draw both solid floor and solid ceiling + FloorCeilRtn = DrawSolidCeilSolidFloor; + break; + case 3: // Draw solid floor and texture ceiling + FloorCeilRtn = DrawSolidFloorAndCeil; + break; + default: + break; + } + } + +// Test to see if viewport is full width (320 units) +gWinStartOffset = ae->WinStartOffset; // Offset to viewport +gBottomOff = (gWinEndY * 320); +gWinFullWidth = 0; // Set flag to indicate viewport is not full width +if (gWinStartX == 0 && gWinEndX == 319) // Viewport is full size + { + gWinFullWidth = 1; // Indicates viewport is full size + gWinDWORDS = (gWinEndY - gWinStartY) * 80; // Calculate number of double + } // words to access buffer + +// Test to see if multi-height walls are used +gMultiWalls = 0; +for (i = 0; i < GRID_MAX; i++) + { + if ((xGridGlobal[i] & WALL_TYPE_MULTI) || (yGridGlobal[i] & WALL_TYPE_MULTI)) + { + gMultiWalls = 1; // Indicates multi-height walls are used + break; + } + } +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Render the current scene into the off-screen buffer based on the POV +// coordinates and angle. This function does NOT display the scene once +// it is rendered so the application can overlay graphics into the buffer +// before it is actually displayed. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void AckBuildView(void) +{ +// Set up global variables to be used with assembly language routines +xPglobal = aeGlobal->xPlayer; // The player's x coordinate +xBegGlobal = xPglobal & GRID_MASK; // Upper left corner (x) of the grid + // square the player is in +xPglobalHI = ((long)xPglobal << FP_SHIFT); // Convert x coordinate to fixed point +yPglobal = aeGlobal->yPlayer; // The player's y coordinate +yBegGlobal = yPglobal & GRID_MASK; // Upper left corner (y) of grid square +yPglobalHI = ((long)yPglobal << FP_SHIFT); // Convert y coordinate to fixed point +PlayerAngle = aeGlobal->PlayerAngle; // The player's angle +SysFlags = aeGlobal->SysFlags; // Ceiling and floor attributes; +BuildUpView(); // Assembly routine defined in ACKRTN3.ASM. This routine + // kicks off the ray casting process. +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Stub function for drawing slices that do not contain walls. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void ShowNone(void) +{ +return; +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Internal function to cast the x and y rays and build a slice structure +// for each column of the viewing window. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void BuildSlice(void) +{ + short j,index,wFound; + long xBitmap,yBitmap,BitmapNumber; + short DoorOpenColumn; + ULONG xDistance,yDistance; + ULONG xd,yd,mf; + long WallDistance,UnAdjustDist; + long distance,LightAdj; + USHORT BitmapColumn,yBitmapColumn; + long OldMapPosn,OldMapPosn1; + long HoldAngle; + long offset; + long mPos; + + USHORT *gPtr; + UCHAR *mgPtr; + SLICE *spNext; + +WallDistance = 3000000; // Set to a ridiculous distance +sPtr->Distance = 200; // Initialize the distance from the POV to the slice +wFound = 0; // Wall not found yet +sPtr->Fnc = ShowNone; // Use the stub function for now for drawing the slice + +// Call the low level ray casting function and see if anything is hit. +if ((BitmapNumber = xRayCast()) != 0) // Something has been hit while casting + { // in the x plane + wFound = 1; // Set flag to indicate a hit + // Use the y intercept to determine the column of the slice + BitmapColumn = (LastY1 >> FP_SHIFT) & (BITMAP_WIDTH-1); + + // Keep the orientation the same no matter which side we're on + if ((short)iLastX < xPglobal) + BitmapColumn = (BITMAP_WIDTH-1) - BitmapColumn; + + // Did we strike a door? + if ((BitmapNumber & (DOOR_TYPE_SLIDE+DOOR_TYPE_SPLIT))) + { + index = FindDoor(xMapPosn); // Locate the position of the door + if (index >= 0) // Is this is a valid door? + { + j = aeGlobal->Door[index].ColOffset; // Get the door's current pos + offset = 0; + if (BitmapNumber & DOOR_TYPE_SLIDE) // Is the door a slider? + { + DoorOpenColumn = BITMAP_WIDTH-1; + if ((short)iLastX > xPglobal) // Handle orientation + j = -j; + BitmapColumn += j; // Adjust column to show + } + if (BitmapNumber & DOOR_TYPE_SPLIT) // Is the door a split door? + { + DoorOpenColumn = (BITMAP_WIDTH/2)-1; + if (BitmapColumn < (BITMAP_WIDTH/2)) + { + BitmapColumn += j; + if (BitmapColumn > (BITMAP_WIDTH/2)-1) + offset = 1; + } + else + { + BitmapColumn -= j; + if (BitmapColumn < (BITMAP_WIDTH/2)) + offset = 1; + } + } // End processing split door + + if (offset == 1 || BitmapColumn > (BITMAP_WIDTH-1)) + { + // Get the grid coordinates for this door + OldMapPosn = aeGlobal->Door[index].mPos; + OldMapPosn1 = aeGlobal->Door[index].mPos1; + // Fake the engine into thinking no door is there + xGridGlobal[OldMapPosn] = 0; + xGridGlobal[OldMapPosn1] = 0; + // Cast the ray to get walls beyond the door + BitmapNumber = xRayCast(); + // Put back the door codes if not fully open + if (aeGlobal->Door[index].ColOffset < DoorOpenColumn) + { + xGridGlobal[OldMapPosn] = aeGlobal->Door[index].mCode; + xGridGlobal[OldMapPosn1] = aeGlobal->Door[index].mCode1; + } + // Calc the new bitmap column of wall behind door + BitmapColumn = (LastY1 >> FP_SHIFT) & (BITMAP_WIDTH-1); + if ((short)iLastX < xPglobal) + BitmapColumn = (BITMAP_WIDTH-1) - BitmapColumn; + } + } + } // End processing doors + +//============================================================== +// Calculate the distance to the wall. Since the x position was +// fixed to move 64 or -64 we can use it to determine the actual +// wall distance. The InvCosTable values were stored with a fixed +// point of 20 decimal places. At this time we'll knock off 14 of +// them so we can later multiply with a fixed point value of 16 +//============================================================== + xd = iLastX - xPglobal; // x Distance to the found slice + mf = InvCosTable[ViewAngle]; // Get the cosine of the angle + WallDistance = (xd * mf) >> 10; // Calculate the actual distance to the slice + if (WallDistance > 33554432L) // Check for out of range + WallDistance = 1200000L; + gPtr = xGridGlobal; // Point to xGrid map + mgPtr = mxGridGlobal[xMapPosn]; // Point to grid map for multi-height walls + mPos = xMapPosn; // Access actual map position + } // End (if BitmapNumber = xRayCast() ...) + +// Time to cast the ray in the y plane +if ((yBitmap = yRayCast()) != 0) // Something has been hit while casting + { + // Use the X intercept to determine the column of the bitmap + yBitmapColumn = (LastX1 >> FP_SHIFT) & (BITMAP_WIDTH-1); + // Handle orientation from either side of the wall + if ((short)iLastY > yPglobal) + yBitmapColumn = (BITMAP_WIDTH-1) - yBitmapColumn; + + // Did we strike a door? + if ((yBitmap & (DOOR_TYPE_SLIDE+DOOR_TYPE_SPLIT))) + { + index = FindDoor(yMapPosn); + if (index >= 0) // Is this is a valid door? + { + // Get the current door column offset + j = aeGlobal->Door[index].ColOffset; + offset = 0; + // Deal with orientation + if (yBitmap & DOOR_TYPE_SLIDE) // Is this a sliding door? + { + DoorOpenColumn = BITMAP_WIDTH-1; + if ((short)iLastY < yPglobal) + j = -j; + yBitmapColumn += j; + } // End processing sliding door + if (yBitmap & DOOR_TYPE_SPLIT) // Is this a split door? + { + DoorOpenColumn = (BITMAP_WIDTH/2)-1; + if (yBitmapColumn < (BITMAP_WIDTH/2)) + { + yBitmapColumn += j; + if (yBitmapColumn > (BITMAP_WIDTH/2)-1) + offset = 1; + } + else + { + yBitmapColumn -= j; + if (yBitmapColumn < (BITMAP_WIDTH/2)) + offset = 1; + } + } // End processing split door + // If beyond width of bitmap than cast again + if (offset == 1 || yBitmapColumn > (BITMAP_WIDTH-1)) + { + // Get the yGrid coordinates for this door + OldMapPosn = aeGlobal->Door[index].mPos; + OldMapPosn1 = aeGlobal->Door[index].mPos1; + // Fool the engine into thinking no door is there + yGridGlobal[OldMapPosn] = 0; + yGridGlobal[OldMapPosn1] = 0; + // Cast again for walls beyond the door + yBitmap = yRayCast(); + // Put door code back if not fully open + if (aeGlobal->Door[index].ColOffset < DoorOpenColumn) + { + yGridGlobal[OldMapPosn] = aeGlobal->Door[index].mCode; + yGridGlobal[OldMapPosn1] = aeGlobal->Door[index].mCode1; + } + // Get the bitmap column of wall beyond door + yBitmapColumn = (LastX1 >> FP_SHIFT) & (BITMAP_WIDTH-1); + if ((short)iLastY > yPglobal) + yBitmapColumn = (BITMAP_WIDTH-1) - yBitmapColumn; + } + } + } + +//============================================================== +// Calculate the distance to the wall. Since the y position was +// fixed to move 64 or -64 we can use it to determine the actual +// wall distance. The InvSinTable values were stored with a fixed +// point of 20 decimal places. At this time we'll knock off 14 of +// them so we can later multiply with a fixed point value of 16 +//============================================================== + yd = iLastY - yPglobal; // Distance from player's position to intersection point + mf = InvSinTable[ViewAngle]; // Use angle with look up table + yDistance = (yd * mf) >> 8; // Calculate y distance + if (yDistance > 33554432L) // Distance is out of range, adjust + yDistance = 120000L; + +//============================================================== +// At this point check the distance to the y wall against the x +// wall to see which one is closer. The closer one is the one +// we'll draw at this column of the screen. +//============================================================== + if (yDistance < WallDistance) // Use distance to y slice if this slice is closer + { + wFound = 1; // Indicates that a slice has been found + WallDistance = yDistance; // Use distance to y slice + BitmapNumber = yBitmap; // Transfer bitmap number + BitmapColumn = yBitmapColumn; // Transfer bitmap column + gPtr = yGridGlobal; // Store pointer to global y grid + mPos = yMapPosn; // Store position of y wall in map + mgPtr = myGridGlobal[mPos]; + } + } // End (if yBitmap = yRayCast()) != 0) + +// A slice has been found so process it +if (wFound) + { +//============================================================= +// To avoid a fishbowl affect we need to adjust the distance so +// it appears perpendicular to the center point of the display +// which is relative angle 0 from the players current angle. We +// started at -32 degrees for the first screen column and will +// cycle from -32 down to 0 then back up to +32 degrees. This +// cosine value was pre-calculated and placed in ViewCosTable. +//============================================================= + UnAdjustDist = WallDistance >> 5; + yd = ViewCosTable[ViewColumn] >> 3; // Use current column as look up index + WallDistance *= yd; + + // Now we strip off somemore decimal points and check round-up + xd = WallDistance >> 12; + if (WallDistance - (xd << 12) >= 2048) + xd++; + + // The last decimal points from the multiplication after the x and + // y rays is stripped off and checked for round-up + WallDistance = xd >> 5; + if (xd - (WallDistance << 5) >= 16) + WallDistance++; + + // This is an arbitrary minimum distance to look for + if (WallDistance < 10) + WallDistance = 10; // Reset distance to minimum allowed + + // Don't want it to go outside our table boundaries + if (WallDistance >= MAX_DISTANCE) + WallDistance = MAX_DISTANCE - 1; // Reset distance to max allowed + + // Save the wall data to display when done with entire screen + sPtr->Distance = WallDistance; // Store the adjusted distance to the wall + sPtr->bNumber = BitmapNumber; // The bitmap number for the wall slice + sPtr->bColumn = BitmapColumn; // The screen column position to display the bitmap + sPtr->bMap = WallbMaps; // Pointer to the bitmap + sPtr->Active = 0; // Indicates slice is not displayable yetlast one + sPtr->Type = ST_WALL; // Indicates this slice is part of a wall + sPtr->Fnc = WallRtn; // Pointer to function to draw slice + sPtr->mPos = mPos; // Position of the slice in the grid map + sPtr->mPtr = mgPtr; // Grid pointer to reference multi-height wall + spNext = sPtr->Next; // Reference wall slice behind current slice + + if (WallDistance > MaxDistance) + MaxDistance = WallDistance; + if (CeilMap[mPos]) + LastWallHeight = 9000; + if (((BitmapNumber & WALL_TYPE_UPPER) || // Wall is above the floor level + (BitmapNumber & WALL_TYPE_TRANS)) && // or wall slice is transparent and it + spNext != NULL) // has something behind it + { + BitmapColumn = gPtr[mPos]; // Get position of the mult-height wall + gPtr[mPos] = 0; + sPtr->Active = 1; // More slices to follow if wall is displayable! + if (BitmapNumber & WALL_TYPE_TRANS) // The wall is transparent + { + sPtr->Type = ST_OBJECT; // We have an object + sPtr->Fnc = WallMaskRtn; // Using a different drawing routine + } + sPtr = spNext; // Point to slice behind + spNext->Active = 0; // Initialize the slice behind before building it + spNext->bNumber = 0; + BuildSlice(); // Call BuildSlice() again to build the slice behind + gPtr[mPos] = BitmapColumn; // Store position of slice + if (!sPtr->bNumber) // Slice behind is no longer visible + { + spNext = sPtr->Prev; // Link up slices + if (spNext != NULL) + spNext->Active = 0; + } + } + else + { // Keep tack of each distance to wall slice for drawing floor later on + if (UnAdjustDist < WallDistTable[ViewColumn]) + WallDistTable[ViewColumn] = UnAdjustDist; + } + } +} + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Continues the ray cast for multi-height walls so that any wall that is +// taller than the walls in front of it will be displayed. +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +void BuildSliceMulti(void) +{ + short j,index,wFound; + USHORT yHeight; + USHORT xBitmap,yBitmap,BitmapNumber; + short DoorOpenColumn; + short xWallHeight,yWallHeight; + ULONG xDistance,yDistance; + ULONG xd,yd,mf; + + long WallDistance; + USHORT distance,LightAdj; + USHORT BitmapColumn,yBitmapColumn; + short OldMapPosn,OldMapPosn1; + short HoldAngle,HoldX,HoldY,xp1,yp1; + USHORT offset; + short mPos; + USHORT *gPtr; + UCHAR *mgPtr; + SLICE *spNext; + +WallDistance = 3000000; // Set to a very far off distance +wFound = 0; // Indicates a slice has not been found + +// Don't cast an x ray if its impossible to intercept any x walls +if (ViewAngle != INT_ANGLE_90 && ViewAngle != INT_ANGLE_270) + { + if ((BitmapNumber = xRayCastMulti()) != 0) // Cast the x ray and build multi-height slice + { + xBitmap = BitmapNumber & 0xFF; // Get the bitmap code + if (((BitmapNumber & WALL_TYPE_MULTI) || // Check for multi-height + (BitmapNumber & WALL_TYPE_UPPER)) && // or slice that is taller than one in front + (xBitmap > LastWallHeight)) + { + // LastWallHeight = xBitmap; + wFound = 1; // Indicates a multi-slice wall has been found + // Use the y intercept to determine the wall column + BitmapColumn = (LastY1 >> FP_SHIFT) & (BITMAP_WIDTH-1); + + // Keep the orientation the same no matter which side we're on + if ((short)iLastX < xPglobal) + BitmapColumn = (BITMAP_WIDTH-1) - BitmapColumn; + xd = iLastX - xPglobal; + mf = InvCosTable[ViewAngle]; // Use angle to calculate distance to slice + WallDistance = (xd * mf) >> 10; + if (WallDistance > 33554432L) // Check for out of range + WallDistance = 1200000L; + gPtr = xGridGlobal; // Reference global map grid + mPos = xMapPosn; // Use position of slice + mgPtr = mxGridGlobal[mPos]; // Get pointer to multi-height slice found + } + } + } + +// Don't cast a y ray if its impossible to intercept any y walls +if (ViewAngle != 0 && ViewAngle != INT_ANGLE_180) + { + if ((yBitmap = yRayCastMulti()) != 0) // Cast the y ray to build multi-height slice + { + yHeight = yBitmap & 0xFF; // Get the bitmap code of slice + if (((yBitmap & WALL_TYPE_MULTI) || + (yBitmap & WALL_TYPE_UPPER)) && + (yHeight > LastWallHeight)) + { + yWallHeight = yHeight; + // Use the x intercept to determine the column of the bitmap + yBitmapColumn = (LastX1 >> FP_SHIFT) & (BITMAP_WIDTH-1); + // Handle orientation from either side of the wall + if ((short)iLastY > yPglobal) + yBitmapColumn = (BITMAP_WIDTH-1) - yBitmapColumn; + yd = iLastY - yPglobal; + mf = InvSinTable[ViewAngle]; // Use angle to calculate distance to slice + yDistance = (yd * mf) >> 8; + if (yDistance > 33554432L) // Is distance in range? + yDistance = 120000L; + +//============================================================== +// At this point check the distance to the Y wall against the +// wall to see which one is closer. The closer one is the one +// we'll draw at this column of the screen. +//============================================================== + if (yDistance < WallDistance) // Use distance to y slice if this slice is closer + { + wFound = 1; // Indicates that a slice has been found + WallDistance = yDistance; // Use distance to y slice + BitmapNumber = yBitmap; // Transfer bitmap number + BitmapColumn = yBitmapColumn; // Transfer bitmap column + gPtr = yGridGlobal; // Store pointer to global y grid + mPos = yMapPosn; // Store position of y wall in map + xBitmap = yHeight; + mgPtr = myGridGlobal[mPos]; // Store pointer to multi-height slice + } + } + } + } + +if (wFound) // A multi-wall slice has been found so process it + { + LastWallHeight = xBitmap; + yd = ViewCosTable[ViewColumn] >> 2; + WallDistance *= yd; + // Now we strip off somemore decimal points and check round-up + xd = WallDistance >> 12; + if (WallDistance - (xd << 12) >= 2048) + xd++; + +//============================================================== +// The last decimal points from the multiplication after the X and +// Y rays is stripped off and checked for round-up. +//============================================================== + WallDistance = xd >> 5; + if (xd - (WallDistance << 5) >= 16) + WallDistance++; + // Don't really need to, but put it into an integer for fast compare + distance = WallDistance; + // This is an arbitrary minimum distance to look for + if (distance < 10) + distance = 10; + + // Don't want it to go outside our table boundaries + if (distance >= MAX_DISTANCE) + distance = MAX_DISTANCE - 1; + + // Save the wall data to display when done with entire screen + sPtr->Active = 1; + sPtr = sPtr->Next; + if (sPtr == NULL) + return; + + sPtr->Distance = distance; // Update slice data in slice structure + sPtr->bNumber = BitmapNumber; + sPtr->bColumn = BitmapColumn; + sPtr->bMap = WallbMaps; + sPtr->Active = 0; + sPtr->Type = ST_WALL; + sPtr->Fnc = WallRtn; + sPtr->mPos = mPos; + sPtr->mPtr = mgPtr; + spNext = sPtr->Next; + + if (spNext != NULL) + { + BuildSliceMulti(); // Recursive call to build the slice behind + } + } +} + +//******************************************************************** +// Internal function called by AckCheckDoorOpen() to +// check for a collision with a wall within a certain distance. +//******************************************************************** +short AckCheckHit(short xPlayer,short yPlayer,short vAngle) +{ + short BitmapNumber,yBitmap; + short WallCode; + ULONG WallDistance; + ULONG xd,yd,mf,yDistance; + long CheckDist; + +WallDistance = 3000000; // Set to a ridiculous value +WallCode = POV_NOTHING; +CheckDist = 56L; // (was 48) Initial minimum distance to look for +BitmapNumber = 0; // Initialize to no bitmap found + +xPglobal = xPlayer; +xBegGlobal = xPglobal & GRID_MASK; +xPglobalHI = ((long)xPglobal << FP_SHIFT); +yPglobal = yPlayer; +yBegGlobal = yPglobal & GRID_MASK; +yPglobalHI = ((long)yPglobal << FP_SHIFT); + +ViewAngle = vAngle; + +if (MoveObjectCount) + memmove(ObjectsSeen,MoveObjectList,MoveObjectCount); + +FoundObjectCount = MoveObjectCount; + +//************************************************************************ +// Don't allow one of these angles, causes either the X or Y ray to not be +// cast which gives a false reading about an obstacle. +//************************************************************************ +if (ViewAngle == INT_ANGLE_45 || + ViewAngle == INT_ANGLE_135 || + ViewAngle == INT_ANGLE_225 || + ViewAngle == INT_ANGLE_315) + { + ViewAngle++; + } + +xRaySetup(); +BitmapNumber = xRayCast(); + +if (BitmapNumber & (WALL_TYPE_UPPER+WALL_TYPE_PASS)) + BitmapNumber = 0; + +if (BitmapNumber) + { + xd = iLastX - xPlayer; + mf = InvCosTable[ViewAngle]; + WallDistance = (xd * mf) >> 10; + if (WallDistance > 33554432L) + WallDistance = 1200000L; + // Set the wall struck code to an X wall + WallCode = POV_XWALL; + LastMapPosn = xMapPosn; + } + +yRaySetup(); +yBitmap = yRayCast(); + +if (yBitmap & (WALL_TYPE_UPPER+WALL_TYPE_PASS)) + yBitmap = 0; + +if (yBitmap) + { + yd = iLastY - yPlayer; + mf = InvSinTable[ViewAngle]; + yDistance = (yd * mf) >> 8; + if (yDistance > 33554432L) + yDistance = 120000L; + // If Y wall closer than X wall then use Y wall data + if (yDistance < WallDistance) + { + WallDistance = yDistance; + // Indicate the wall struck was a Y wall + WallCode = POV_YWALL; + BitmapNumber = yBitmap; + LastMapPosn = yMapPosn; + } + } + +//************************************************************************ +// Since doors appear in the middle of the wall, adjust the minimum distance +// to it. This handles walking up close to a door. +//************************************************************************ +if (BitmapNumber & (DOOR_TYPE_SLIDE+DOOR_TYPE_SPLIT)) + CheckDist += 64L; +BitmapNumber &= 0xFF; +if (WallCode) + { + yd = ViewCosTable[160] >> 3; + WallDistance *= yd; + // Now we strip off somemore decimal points and check round-up + xd = WallDistance >> 12; + if (WallDistance - (xd << 12) >= 2048) + xd++; + +//********************************************************************* +// The last decimal points from the multiplication after the X and +// Y rays is stripped off and checked for round-up. +//********************************************************************* + WallDistance = xd >> 5; + if (xd - (WallDistance << 5) >= 16) + WallDistance++; + +//********************************************************************* +// If the wall or object is further than the minimum distance, we can +// continue moving in this direction. +//********************************************************************* + if (WallDistance > CheckDist) + WallCode = POV_NOTHING; + } + +return(WallCode); +} +// **** End of Source **** + + \ No newline at end of file diff --git a/ack_lib/ACKWRAP.C b/ack_lib/ACKWRAP.C new file mode 100644 index 0000000..4a8552c --- /dev/null +++ b/ack_lib/ACKWRAP.C @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ack3d.h" +#include "ackeng.h" +#include "ackext.h" + +typedef struct { + int sel; + int off; + } SELOFF; + +extern char AckKeyboardSetup; +extern SELOFF OldKeybdInt; +extern char AckTimerSetup; +extern SELOFF OldTimerInt; + +void AckSetIntVector(int VecNum,int sel,int VecOff); + +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +// Frees up buffers and closes any resource file that may be open. +// After calling this function, do NOT call AckBuildView() or +// AckDisplayScreen() +//ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +short AckWrapUp (ACKENG * ae) +{ + + AckFree (LongTanTable); + AckFree (LongInvTanTable); + AckFree (CosTable); + AckFree (SinTable); + AckFree (LongCosTable); + AckFree (xNextTable); + AckFree (yNextTable); + AckFree (ViewCosTable); + AckFree (AdjustTable); + + if (ae->OverlayBuffer != NULL) + AckFree (ae->OverlayBuffer); + ae->OverlayBuffer = NULL; + + if (ae->BkgdBuffer != NULL) + AckFree (ae->BkgdBuffer); + ae->BkgdBuffer = NULL; + + if (ae->ScreenBuffer != NULL) + AckFree (ae->ScreenBuffer); + ae->ScreenBuffer = NULL; + + if (rsHandle) + { + close (rsHandle); + rsHandle = 0; + } + +if (AckKeyboardSetup) + { + AckSetIntVector(9,OldKeybdInt.sel,OldKeybdInt.off); + AckKeyboardSetup = 0; + } +if (AckTimerSetup) + { + AckSetIntVector(0x1C,OldTimerInt.sel,OldTimerInt.off); + AckTimerSetup = 0; + } + +return (0); +} + +// **** End of Source **** + \ No newline at end of file diff --git a/ack_lib/IFF.H b/ack_lib/IFF.H new file mode 100644 index 0000000..8de0ab7 --- /dev/null +++ b/ack_lib/IFF.H @@ -0,0 +1,98 @@ +#ifndef NULL +#define NULL 0 +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +#define LONG unsigned long +#define ULONG unsigned long +#define UBYTE unsigned char +#define UWORD unsigned short +#define DWORD unsigned long + +long ByteFlipLong(long); +UCHAR *Readiff(char *); /* this returns back a bitmap with a picture in it*/ + +typedef LONG ID; /* An ID is four printable ASCII chars like FORM or DPPV */ + +/* original +#define MakeID(d,c,b,a) ((DWORD)(a)<<24 | (DWORD)(b)<<16 | (DWORD)(c)<<8 | (DWORD)(d) )*/ +#define MakeID(d,c,b,a) (((DWORD)(a)<<24) | ((DWORD)(b)<<16) | ((DWORD)(c)<<8) | ((DWORD)(d))) + +#define FORM MakeID('F','O','R','M') +#define PROP MakeID('P','R','O','P') +#define LIST MakeID('L','I','S','T') +#define CAT MakeID('C','A','T',' ') +#define FILLER MakeID(' ',' ',' ',' ') + +typedef struct + { + ID type; + long cksize; + ID subtype; + } form_chunk; + +typedef struct { + ID ckID; + LONG ckSize; + } ChunkHeader; + +typedef struct { + ID ckID; + LONG ckSize; + UBYTE ckData[ 1 /*REALLY: ckSize*/ ]; + } Chunk; + +#define ID_PBM MakeID('P','B','M',' ') +#define ID_ILBM MakeID('I','L','B','M') +#define ID_BMHD MakeID('B','M','H','D') +#define ID_CMAP MakeID('C','M','A','P') +#define ID_GRAB MakeID('G','R','A','B') +#define ID_DEST MakeID('D','E','S','T') +#define ID_SPRT MakeID('S','P','R','T') +#define ID_CAMG MakeID('C','A','M','G') +#define ID_BODY MakeID('B','O','D','Y') + +/* ---------- BitMapHeader ---------------------------------------------*/ + +typedef UBYTE Masking; /* Choice of masking technique.*/ +#define mskNone 0 +#define mskHasMask 1 +#define mskHasTransparentColor 2 +#define mskLasso 3 + +#define cmpNone 0 +#define cmpByteRun1 1 + +/* A BitMapHeader is stored in a BMHD chunk. */ +typedef struct { + UWORD w, h; /* raster width & height in pixels */ + UWORD x, y; /* position for this image */ + UBYTE nPlanes; /* # source bitplanes */ + UBYTE masking; /* masking technique */ + UBYTE compression; /* compression algoithm */ + UBYTE pad1; /* UNUSED. For consistency, put 0 here.*/ + UWORD transparentColor; /* transparent "color number" */ + UBYTE xAspect, yAspect; /* aspect ratio, a rational number x/y */ + UWORD pageWidth, pageHeight; /* source "page" size in pixels */ + } BitMapHeader; +/* RowBytes computes the number of bytes in a row, from the width in pixels.*/ +#define RowBytes(w) (((w) + 15) >> 4 << 1) + +/* A CMAP chunk is a packed array of ColorRegisters (3 bytes each). */ +typedef struct { + UBYTE red, green, blue; /* MUST be UBYTEs so ">> 4" won't sign extend.*/ + } ColorRegister; + +/* Use this constant instead of sizeof(ColorRegister). */ +#define sizeofColorRegister 3 + +long ByteFlipLong(long); +void ByteFlipShort(short *); +short iffswab(unsigned short); +short swab(unsigned short); + + \ No newline at end of file diff --git a/ack_lib/KIT.H b/ack_lib/KIT.H new file mode 100644 index 0000000..94dce77 --- /dev/null +++ b/ack_lib/KIT.H @@ -0,0 +1,140 @@ +// Header file for 3D Construction Kit +// Started: 01/02/94 +// Author: Lary Myers +// Module: KIT.H +// (c) CopyRight 1994 All Rights Reserved + +#define MODE_GRAPHICS 0x13 +#define MODE_TEXT 0x03 + +#define SCREEN_LENGTH 64000 // Bytes in one full screen +#define SCREEN_LEN_WORDS 32000 // Words in one screen +#define SCREEN_LEN_DWORDS 16000 // DWORDS in one screen +#define SCREEN_PLANES 1 // Number of planes for this mode +#define SCREEN_WIDTH 320 // Number of bytes in one row +#define SCREEN_HEIGHT 200 // Number of rows on screen + +#define BUTTON_OK 0 +#define BUTTON_OKCANCEL 1 +#define BUTTON_YESNO 2 +#define BUTTON_ABORTRETRY 3 + +#define BUTTON_RET_OK 0 +#define BUTTON_RET_CANCEL 1 +#define BUTTON_RET_YES 2 +#define BUTTON_RET_NO 3 +#define BUTTON_RET_ABORT 4 +#define BUTTON_RET_RETRY 5 + +// Used in IORTN.C for message boxes +typedef struct { + short x; + short y; + short x1; + short y1; + short index; + } BUTTON; + + +// Used in IORTN.C for message boxes +typedef struct { + short Count; + short List[4]; + } INDEXES; + + +// Used in IORTN.C list boxes +typedef struct _lcb { + struct _lcb *Back; + struct _lcb *Fwd; + char Data[1]; + } LCB; + +typedef struct { + short ux; + short uy; + short ux1; + short uy1; + short dx; + short dy; + short dx1; + short dy1; + } ARROWRECT; + + +//============================================================================= +// Prototypes in UTIL.C +//============================================================================= +UINT Inkey(void); +char *GetExtent(char *s); +char *StripEndOfLine(char *s); +char *SkipSpaces(char *s); +char *AddExtent(char *s,char *ext); +char *CopyToComma(char *dest,char *src); +short HasWildCards(char *s); +void SetCurrentPath(char *p); +void InitDiskList(void); +char *GetPath(char *s); +//============================================================================= +// Prototypes in GRAPHICS.C +//============================================================================= +void SetMode(short mode); +void ClearScreen(UCHAR color); +void PutPixel(short x,short y,UCHAR color); +void DrawVerticalLine(short x,short y,short y1,UCHAR color); +void DrawHorizontalLine(short x,short y,short x1,UCHAR color); +void DrawBox(short x,short y,short x1,short y1,UCHAR color); +void DrawRoundBox(short x,short y,short x1,short y1,UCHAR color); +void Draw3DBox(short x,short y,short x1,short y1,UCHAR TopColor,UCHAR BottomColor); +void DrawSolidBox(short x,short y,short x1,short y1,UCHAR color); +void DrawXORBox(short x,short y,short x1,short y1,UCHAR color); +void DrawXORRect(short x,short y,short x1,short y1,UCHAR color); +void DrawLine(short x1,short y1,short x2,short y2,UCHAR color); +void PutBitmap(short x,short y,short width,short height,UCHAR *Bitmap); +UCHAR *GetBitmap(short x,short y,short width,short height,UCHAR *buffer); +UINT GetBufferSize(short width,short height); +void PutBitmapAsIcon(short x,short y,UCHAR *bm); +void PutAckBitmap(short x,short y,UCHAR *bm); +UCHAR *ScreenToBuffer(UCHAR *buffer); +void BufferToScreen(UCHAR *buffer); +void SetPalette(UCHAR *PalBuf); +void ShowChar(short x,short y,char ch); +void ShowString(short x,short y,char *s); +UCHAR *LoadAndShowScreen(char *PicName); +//============================================================================= +// Prototypes in MOUSE.C +//============================================================================= +short MouseInstalled(void); +void ShowMouse(void); +void HideMouse(void); +short ReadMouseCursor(short *mrow,short *mcol); +short ReadMouseButtons(void); +void SetMouseCursor(short mrow,short mcol); +void SetMouseMinMaxColumns(short mincol,short maxcol); +void SetMouseMinMaxRows(short minrow,short maxrow); +void SetMouseShape(short hsrow,short hscol,char *mask); +void MouseReleased(void); +void MouseSetFunction(UCHAR,void *); +//============================================================================= +// Prototypes in READIFF.C +//============================================================================= +UCHAR *Readiff(char *picname); +//============================================================================= +// Prototypes in IO.C +//============================================================================= +void GetFontTable(void); +void ShowChar(short x,short y,char ch); +void ShowString(short x,short y,char *s); +short ShowMessageBox(char *Body,UINT Buttons); +void ShowListBox(short x,short y,short x1,short y1,short index,LCB *lcb); +char *FileBox(char *fName,char *Path,char *rBuf,char *Title,char *ActiveButton); +void TestListBox(void); +short PickList(short x,short y,short x1,short y1,LCB *Anchor); +short CreateSelectPickList(short x,short y,short x1,short y1,short count,char **List,short SortFlag); +short ShowPickList(char **p,short SortFlag); +//============================================================================= +// Prototypes in SETS.C +//============================================================================= +short LoadSet(char *fName); + + \ No newline at end of file diff --git a/ack_lib/WING.H b/ack_lib/WING.H new file mode 100644 index 0000000..84038ce --- /dev/null +++ b/ack_lib/WING.H @@ -0,0 +1,78 @@ +/*****************************************************************************\ +* +* wing.h - WinG functions, types, and definitions +* +* Copyright (c) 1994 Microsoft Corp. All rights reserved. +* +\*****************************************************************************/ + +#ifndef _INC_WING +#define _INC_WING + +#ifndef _INC_WINDOWS +#include /* Include windows.h if not already included */ +#endif + +#ifdef __cplusplus +extern "C" { /* Assume C declarations for C++ */ +#endif + +#if defined(WIN32) || defined(_WIN32) +#define WINGAPI WINAPI +#else +#define WINGAPI WINAPI _loadds +#endif + + +/***** WingDC and WinGBitmap *************************************************/ + +HDC WINGAPI WinGCreateDC( void ); + +BOOL WINGAPI WinGRecommendDIBFormat( BITMAPINFO FAR *pFormat ); + +HBITMAP WINGAPI WinGCreateBitmap( HDC WinGDC, BITMAPINFO const FAR *pHeader, + void FAR *FAR *ppBits ); + +void FAR *WINGAPI WinGGetDIBPointer( HBITMAP WinGBitmap, + BITMAPINFO FAR *pHeader ); + +UINT WINGAPI WinGGetDIBColorTable( HDC WinGDC, UINT StartIndex, + UINT NumberOfEntries, RGBQUAD FAR *pColors ); + +UINT WINGAPI WinGSetDIBColorTable( HDC WinGDC, UINT StartIndex, + UINT NumberOfEntries, RGBQUAD const FAR *pColors ); + + +/***** Halftoning ************************************************************/ + +HPALETTE WINGAPI WinGCreateHalftonePalette( void ); + +typedef enum WING_DITHER_TYPE +{ + WING_DISPERSED_4x4, + WING_DISPERSED_8x8, + + WING_CLUSTERED_4x4 + +} WING_DITHER_TYPE; + +HBRUSH WINGAPI WinGCreateHalftoneBrush( HDC Context, COLORREF crColor, + WING_DITHER_TYPE DitherType ); + + +/***** Blts ******************************************************************/ + +BOOL WINGAPI WinGBitBlt( HDC hdcDest, int nXOriginDest, + int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc ); + +BOOL WINGAPI WinGStretchBlt( HDC hdcDest, int nXOriginDest, + int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc ); + +#ifdef __cplusplus +} /* End of extern "C" */ +#endif + +#endif // _INC_WING + diff --git a/ack_lib/WINGDLL.H b/ack_lib/WINGDLL.H new file mode 100644 index 0000000..685f1fa --- /dev/null +++ b/ack_lib/WINGDLL.H @@ -0,0 +1,60 @@ +///////////////////////////////////////////////////////////////////////////// +// WINGDLL.H +// +// Interface class for WinG. +///////////////////////////////////////////////////////////////////////////// + +#ifndef _INC_WINGDLL +#define _INC_WINGDLL + +typedef HDC (WINAPI *wingCreateDC)( void ); +typedef BOOL (WINAPI *wingRecommendDIBFormat)( BITMAPINFO FAR *pFormat ); +typedef HBITMAP (WINAPI *wingCreateBitmap)( HDC WinGDC, BITMAPINFO const *pHeader, + void *ppBits ); +typedef void FAR *(WINAPI *wingGetDIBPointer)( HBITMAP WinGBitmap, + BITMAPINFO FAR *pHeader ); +typedef UINT (WINAPI *wingGetDIBColorTable)( HDC WinGDC, UINT StartIndex, + UINT NumberOfEntries, RGBQUAD FAR *pColors ); +typedef UINT (WINAPI *wingSetDIBColorTable)( HDC WinGDC, UINT StartIndex, + UINT NumberOfEntries, RGBQUAD const FAR *pColors ); +typedef HPALETTE (WINAPI *wingCreateHalftonePalette)( void ); +typedef HBRUSH (WINAPI *wingCreateHalftoneBrush)( HDC, COLORREF, + WING_DITHER_TYPE); +typedef BOOL (WINAPI *wingBitBlt)( HDC hdcDest, int nXOriginDest, + int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc ); +typedef BOOL (WINAPI *wingStretchBlt)( HDC hdcDest, int nXOriginDest, + int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc ); + + +class WinGdll { + +public: + WinGdll(); + ~WinGdll(); + + BOOL Load(); + BOOL Free(); + + HINSTANCE m_hLib; + BOOL m_bLoaded; + + //I would have used m_ prefix, but too laborious to type when using + //the class. Would have inlined the function pointers, + //without typedef's, but the compiler didn't like it. + //Anyway, this method makes casting GetProcAddress() + //easier. + wingCreateDC pCreateDC; + wingRecommendDIBFormat pRecommendDIBFormat; + wingCreateBitmap pCreateBitmap; + wingGetDIBPointer pGetDIBPointer; + wingGetDIBColorTable pGetDIBColorTable; + wingSetDIBColorTable pSetDIBColorTable; + wingCreateHalftonePalette pCreateHalftonePalette; + wingCreateHalftoneBrush pCreateHalftoneBrush; + wingBitBlt pBitBlt; + wingStretchBlt pStretchBlt; +} ; + +#endif // _INC_WINGDLL diff --git a/fdemo/FDEMO.C b/fdemo/FDEMO.C new file mode 100644 index 0000000..a4b2c4b --- /dev/null +++ b/fdemo/FDEMO.C @@ -0,0 +1,2310 @@ +// Example using Watcom FLAT model Animation Construction Kit +// Started: 01/02/94 +// Author: Lary Myers +// Module: FDEMO.C +// (c) CopyRight 1994 All Rights Reserved + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ack3d.h" +#include "ackeng.h" +#include "kit.h" +#include "modplay.h" + +#define DEMO_RESOURCE 69 +#define HAND1_RESOURCE 135 +#define HAND2_RESOURCE 136 + +#define KEYBD 0x9 /* INTERRUPT 9 */ +#define RIGHT_ARROW_KEY 77 +#define UP_ARROW_KEY 72 +#define LEFT_ARROW_KEY 75 +#define DOWN_ARROW_KEY 80 +#define MINUS_KEY 74 +#define PLUS_KEY 78 +#define NUMBER_5_KEY 76 +#define ESCAPE_KEY 1 +#define PGUP_KEY 73 +#define PGDN_KEY 81 +#define B_KEY 48 +#define C_KEY 46 +#define F_KEY 33 +#define I_KEY 23 +#define R_KEY 19 +#define S_KEY 31 +#define W_KEY 17 +#define NUM_1_KEY 2 +#define NUM_2_KEY 3 +#define NUM_3_KEY 4 +#define NUM_4_KEY 5 +#define NUM_5_KEY 6 +#define NUM_6_KEY 7 +#define NUM_7_KEY 8 +#define NUM_8_KEY 9 +#define NUM_9_KEY 10 + +typedef struct { + int mdx; + int mdy; + int mButtons; +} MOUSE; + +#define MAX_AMOUNT 64 +#define MAX_MAG_AMOUNT 64 +#define MAX_STR_AMOUNT 128 +#define MAX_STR_HALF_AMOUNT 64 + +extern long AckMemUsed; // Running total of memory used +extern short AckDisplayErrors; // 1 = sw to text mode and display error + +extern long mFactor; +extern long dFactor; + +extern long zdTable[VIEW_WIDTH][200]; + +extern UCHAR colordat[]; +extern UINT *ObjGrid; +extern UINT *Grid; +extern UINT FloorMap[]; +extern short ViewHeight; +extern short CeilingHeight; +extern short Resolution; +extern long kduFactor; +extern long kdvFactor; +extern long kxFactor; +extern long kyFactor; +extern short tuFactor; +extern short tvFactor; +extern short rsHandle; +extern ULONG *rbaTable; +extern UCHAR *BackArray[]; +extern long *xNextTable; +extern long *yNextTable; +extern UINT *Grid; +extern long *CosTable; +extern long *SinTable; + + ACKENG *ae; + +// These are the ranges used for distance shading. Will need to be modified +// for the new color palette used. +#if 0 +ColorRange ranges[64] = { + 16,16, + 32,16, + 48,16, + 64,16, + 80,16, + 96,8, + 104,8, + 112,8, + 120,8, + 128,8, + 136,8, + 144,8, + 152,8, + 160,8, + 168,8, + 176,8, + 184,8, + 192,16, + 208,16, + 224,8, + 232,8, + 0,0 + }; +#endif + +ColorRange ranges[64] = { + 16,15, + 32,16, + 48,16, + 64,16, + 80,16, + 96,8, + 104,8, + 112,8, + 120,8, + 128,8, + 136,8, + 144,8, + 152,8, + 160,8, + 168,8, + 176,8, + 184,16, + 200,16, + 216,16, + 232,16, + 248,16, + 0,0 + }; + + + + UCHAR scanCode; + UCHAR KeyPressed; + UCHAR MiniKey; + UCHAR Keys[128]; + int HaveMouse; + short FlashIndex; + short FlashCount; + UCHAR *bmFlash[2]; + UCHAR *bmFlash1[2]; + char LineBuffer[200]; + char *smFont; + UCHAR FontTransparent; + UCHAR FontColor; + UCHAR TextBGcolor; + int Throwing; + int Shooting; + int ShutDownFlag; + short MagAmount; + short StrAmount; + int ShowHitFlag; + int MapResource; + int PalResource; + int ResScreenBack; + int ResScrollBack; + void *BGmusic; + + UCHAR *DemoPtr; + short Demoht; + short Demowt; + UCHAR *pHand1; + short Handw1; + short Handh1; + UCHAR *pHand2; + short Handw2; + short Handh2; + + long TimerCounter; + long ObjCounter[64]; + void (__interrupt __far *oldvec)(); + void __interrupt __far myInt(); + + void (__interrupt __far *oldTimer)(); + void __interrupt __far myTimer(); + + short LastObjectIndex; + +//----------------------------------------------------------------------------- +// Globals used by the frame counting routines +//----------------------------------------------------------------------------- +volatile short framespersec = 0; +volatile short cframes=0, count=0, ticks=0; +volatile short AckTmCount=0, AckTmDelay=0; + + char *ErrorMsgs[] = { + "ERR_BADFILE ", + "ERR_BADCOMMAND ", + "ERR_BADOBJNUMBER ", + "ERR_BADSYNTAX ", + "ERR_LOADINGBITMAP", + "ERR_BADDIRECTION ", + "ERR_BADSTARTX ", + "ERR_BADSTARTY ", + "ERR_BADANGLE ", + "ERR_BADMAPFILE ", + "ERR_READINGMAP ", + "ERR_BADPICNAME ", + "ERR_INVALIDFORM ", + "ERR_NOPBM ", + "ERR_BADPICFILE ", + "ERR_NOMEMORY ", + "ERR_BADPALFILE ", + "ERR_BADWINDOWSIZE", + "ERR_TOMANYVIEWS ", + "ERR_BADOBJECTNUM ", + "ERR_BADOBJTYPE " + }; + + +void AckRegisterStructure(ACKENG *ae); +void AckSpeedUp(short); +void AckSlowDown(void); + +//============================================================================= +// Keyboard interrupt 9 +//============================================================================= +void __interrupt __far myInt(void) +{ + register char x; + +//oldvec(); // Use when screen captures are wanted - calls orig vector + +scanCode = inp(0x60); // read keyboard data port +x = inp(0x61); +outp(0x61, (x | 0x80)); +outp(0x61, x); +outp(0x20, 0x20); + +Keys[scanCode & 127] = 1; +KeyPressed = 1; +if (scanCode & 128) + { + Keys[scanCode & 127] = 0; + KeyPressed = 0; + } +else + MiniKey = 1; + +} + +//============================================================================= +// Timer interrupt - simply increments a counter for use in program +// Calls the old timer after X iterations have cycled so clock stays correct +//============================================================================= +void __interrupt __far myTimer(void) +{ +if (ShutDownFlag) + { + _enable(); + outp(0x20,0x20); + return; + } + +TimerCounter++; + +AckTmCount++; +if (AckTmCount > AckTmDelay) + { + oldTimer(); + AckTmCount -= AckTmDelay; + } +else + { + _enable(); + outp(0x20,0x20); + } + +} + +//============================================================================= +// +//============================================================================= +/* MOD loading routines */ +void *MODLoadModule(char *Path) +{ + int handle; + unsigned modsize; + void *modfile = NULL; + if ((handle = open(Path,O_RDONLY | O_BINARY)) != -1) { + modsize = lseek(handle,0L,SEEK_END); + lseek(handle,0L,SEEK_SET); + if ((modfile=(void*)AckMalloc(modsize)) != NULL) { + if (read(handle,modfile,modsize) != modsize) { + AckFree(modfile); + modfile = NULL; + } + } + close(handle); + } + return modfile; +} + +//============================================================================= +// +//============================================================================= +void MODFreeModule(void *Module) +{ + if (Module) AckFree(Module); +} + +//============================================================================= +// +//============================================================================= +short StartBGmusic(void) +{ +if ((BGmusic = MODLoadModule("SAHARA.MOD")) == NULL) + return(-1); + +if (MODPlayModule(BGmusic,5,22000,0x220,5,1)) + return(-2); + +return(0); +} + +//============================================================================= +// +//============================================================================= +void EndBGmusic(void) +{ + +if (BGmusic == NULL) + return; + +MODStopModule(); +MODFreeModule(BGmusic); + +} + + +//============================================================================= +// +//============================================================================= +short LoadSmallFont(void) +{ + short ht,wt; + int len; + +ht = 2; +smFont = AckReadiff((UCHAR *)ht); +if (smFont == NULL) + return(-1); + +ht = (*(short *)smFont); +wt = (*(short *)&smFont[2]); +len = ht * wt; +memmove(smFont,&smFont[4],len); + +return(0); +} + +//============================================================================= +// +//============================================================================= +void smWriteChar(short x,short y,unsigned char ch) +{ + int FontOffset,VidOffset; + int row,col; + UCHAR *Video; + +VidOffset = (y * 320) + x; +Video = (UCHAR *)0xA0000; +Video += VidOffset; +FontOffset = ((ch-32) * 5); + +if (FontTransparent) + Video = ae->ScreenBuffer + VidOffset; + +for (row = 0; row < 5; row++) + { + if (!FontTransparent) + { + Video[0] = TextBGcolor; + Video[1] = TextBGcolor; + Video[2] = TextBGcolor; + Video[3] = TextBGcolor; + } + + if (smFont[FontOffset]) + Video[0] = FontColor; + if (smFont[FontOffset+1]) + Video[1] = FontColor; + if (smFont[FontOffset+2]) + Video[2] = FontColor; + if (smFont[FontOffset+3]) + Video[3] = FontColor; + + Video += 320; + FontOffset += 294; + } + + +} + +//============================================================================= +// +//============================================================================= +short smWriteString(short x,short y,char *s) +{ + short OrgX; + char ch; + +OrgX = x; + +while (*s) + { + ch = *s++; + + if (ch == 10) + { + x = OrgX; + y += 8; + continue; + } + + if (ch < ' ') + continue; + + ch = toupper(ch); + smWriteChar(x,y,ch); + x += 5; + } + +return(y); +} + +//============================================================================= +// +//============================================================================= +void smWriteHUD(short x,short y,UCHAR color,char *s) +{ +FontTransparent = 1; +FontColor = color; +smWriteString(x,y,s); +FontTransparent = 0; +FontColor = 15; +} + +//============================================================================= +// Checks mouse movement and calculates delta movement in X and Y directions +//============================================================================= +void CheckMouse(MOUSE *m) +{ + int dx,dy; + short x,y,buttons; + + +buttons = ReadMouseCursor(&y,&x); +dx = x - 160; +dy = y - 120; +m->mButtons = buttons; +SetMouseCursor(120,160); + +if (abs(dy) > 10 && abs(dx) < 32) + dx >>= 2; + +m->mdx = dx; +m->mdy = dy; + +} + +//============================================================================= +// Reads a text line from the resource file +//============================================================================= +int ReadLine(void) +{ + int len; + char ch; + +len = 0; +while (len < 200) + { + if (read(rsHandle,&LineBuffer[len],1) != 1) + break; + + ch = LineBuffer[len]; + if (ch == 10) + continue; + + if (ch == 13) + break; + + len++; + } + +LineBuffer[len] = '\0'; + +return(len); +} + +//============================================================================= +// Skips to the next parameter in a text line +//============================================================================= +char *GetNextParm(char *s) +{ + char ch; + +while (*s) + { + ch = *s++; + if (ch == ',') + { + while (*s) + { + ch = *s; + if (ch != ',' && ch != ' ' && ch != '\t') + return(s); + s++; + } + return(NULL); + } + + } + +return(NULL); +} + +//============================================================================= +// Loads a wall bitmap specified in info file +//============================================================================= +int LoadWall(void) +{ + int wnum,rnum,result; + long pos; + char *lb; + + +lb = LineBuffer; // Info file buffer +wnum = atoi(lb); // Wall number to load into + +lb = GetNextParm(lb); + +if (lb == NULL) + return(-1); + +rnum = atoi(lb); // Resource number + +pos = lseek(rsHandle,0L,SEEK_CUR); +result = AckLoadWall(ae,wnum,(char *)rnum); +lseek(rsHandle,pos,SEEK_SET); + +return(result); +} + + +//============================================================================= +// Loads an object bitmap specified in info file +//============================================================================= +int LoadObject(void) +{ + int onum,rnum,result; + long pos; + char *lb; + +lb = LineBuffer; +onum = atoi(lb); // Object bitmap number + +lb = GetNextParm(lb); + +if (lb == NULL) + return(-2); + +rnum = atoi(lb); // Resource number +pos = lseek(rsHandle,0L,SEEK_CUR); +result = AckLoadObject(ae,onum,(char *)rnum); +lseek(rsHandle,pos,SEEK_SET); + +return(result); +} + + +//============================================================================= +// Skip any leading spaces in the string +// NOTE: Actually modifies the string passed! +//============================================================================= +char *SkipSpaces(char *s) +{ + +while (*s == ' ' || *s == '\t' || *s == ',') + strcpy(s,&s[1]); + +return(s); +} + + +//============================================================================= +// Creates and object of the desired style +//============================================================================= +int CreateObject(void) +{ + int onum,vnum,speed; + short result,oType; + short NumViews,bmPerView; + UINT flags; + char *lb; + OBJSEQ os; + +lb = LineBuffer; + +if (!strnicmp(lb,"NUMBER:",7)) + { + lb = &lb[7]; + onum = atoi(lb); + if (onum < 1 || onum > MAX_OBJECTS) + return(-3); + + result = AckCreateObject(ae,onum); + if (result) + return(result); + + LastObjectIndex = onum; + lb = GetNextParm(lb); + if (lb == NULL) + return(-4); + + ae->ObjList[onum]->Speed = atoi(lb); + return(0); + } + +onum = LastObjectIndex; // Object number + +oType = 0; + +if (!strnicmp(lb,"CREATE:",7)) + { + oType = NO_CREATE; + lb = &lb[7]; + } + +if (!strnicmp(lb,"DESTROY:",8)) + { + oType = NO_DESTROY; + lb = &lb[8]; + } + +if (!strnicmp(lb,"WALK:",5)) + { + oType = NO_WALK; + lb = &lb[5]; + } + +if (!strnicmp(lb,"ATTACK:",7)) + { + oType = NO_ATTACK; + lb = &lb[7]; + } + +if (!strnicmp(lb,"INTERACT:",9)) + { + oType = NO_INTERACT; + lb = &lb[9]; + } + + +if (!oType) + return(-5); + + + +lb = SkipSpaces(lb); +if (lb == NULL) + return(-6); + +flags = 0; +strupr(lb); + +if (strstr(lb,"ANIMATE") != NULL) + flags |= OF_ANIMATE; + +if (strstr(lb,"MOVEABLE") != NULL) + flags |= OF_MOVEABLE; + +if (strstr(lb,"PASSABLE") != NULL) + flags |= OF_PASSABLE; + +if (strstr(lb,"MULTIVIEW") != NULL) + flags |= OF_MULTIVIEW; + +if (strstr(lb,"SHOWONCE") != NULL) + flags |= OF_ANIMONCE; + +lb = GetNextParm(lb); +if (lb == NULL) + return(-5); + +NumViews = atoi(lb); +if (NumViews < 1) + return(-6); + +lb = GetNextParm(lb); +if (lb == NULL) + return(-7); + +bmPerView = atoi(lb); +if (bmPerView < 1) + return(-7); + +vnum = NumViews * bmPerView; +if (vnum > MAX_OBJ_BITMAPS) + return(-8); + +lb = GetNextParm(lb); +if (lb == NULL) + return(-9); + +vnum = 0; + +while (lb != NULL && vnum < MAX_OBJ_BITMAPS) + { + os.bitmaps[vnum++] = atoi(lb); + lb = GetNextParm(lb); + } + +os.bmBitmapsPerView = bmPerView; +os.flags = flags; +os.MaxBitmaps = bmPerView; +os.bmSides = NumViews; + +result = AckSetupObject(ae,onum,oType,&os); + +return(result); +} + +//============================================================================= +// Reads the ASCII info file and processes the commands. +//============================================================================= +int ProcessInfoFile(short QuietFlag) +{ + int result; + int mode; + long pos; + char *lb; + + +// Position to start of info file within resource file +lseek(rsHandle,rbaTable[0],SEEK_SET); + +mode = result = 0; + +while (!result) + { + if (!ReadLine()) + continue; + + if (*LineBuffer == ';') + continue; + + if (!strnicmp(LineBuffer,"END:",4)) + break; + + if (!QuietFlag) + printf("."); + + switch (mode) + { + + case 1: // Read walls + if (!strnicmp(LineBuffer,"LOADTYPE:",9)) + { + ae->bmLoadType = atoi(&LineBuffer[9]); // Sets for GIF or BBM + break; + } + + if (!strnicmp(LineBuffer,"ENDBITMAPS:",11)) + mode = 4; + else + result = LoadWall(); + break; + + case 2: // Object bitmaps + if (!strnicmp(LineBuffer,"LOADTYPE:",9)) // Sets for GIF or BBM + { + ae->bmLoadType = atoi(&LineBuffer[9]); + break; + } + if (!strnicmp(LineBuffer,"ENDBITMAPS:",11)) + mode = 5; + else + result = LoadObject(); + break; + + case 3: // Create Object + if (!strnicmp(LineBuffer,"ENDDESC:",8)) + mode = 5; + else + result = CreateObject(); + break; + + case 4: // Walls topic + if (!strnicmp(LineBuffer,"BITMAPS:",8)) + mode = 1; + + if (!strnicmp(LineBuffer,"ENDWALLS:",9)) + mode = 0; + break; + + + case 5: // Objects topic + if (!strnicmp(LineBuffer,"BITMAPS:",8)) + mode = 2; + + if (!strnicmp(LineBuffer,"OBJDESC:",8)) + mode = 3; + + if (!strnicmp(LineBuffer,"ENDOBJECTS:",11)) + mode = 0; + break; + + + default: + if (!strnicmp(LineBuffer,"WALLS:",6)) + { + mode = 4; + break; + } + + if (!strnicmp(LineBuffer,"OBJECTS:",8)) + { + mode = 5; + break; + } + + if (!strnicmp(LineBuffer,"MAPFILE:",8)) + { + MapResource = atoi(&LineBuffer[8]); + pos = lseek(rsHandle,0L,SEEK_CUR); + result = AckReadMapFile(ae,(char *)MapResource); + lseek(rsHandle,pos,SEEK_SET); + break; + } + + if (!strnicmp(LineBuffer,"PALFILE:",8)) + { + PalResource = atoi(&LineBuffer[8]); + break; + } + + if (!strnicmp(LineBuffer,"XPLAYER:",8)) + { + ae->xPlayer = atoi(&LineBuffer[8]); + break; + } + + if (!strnicmp(LineBuffer,"YPLAYER:",8)) + { + ae->yPlayer = atoi(&LineBuffer[8]); + break; + } + + if (!strnicmp(LineBuffer,"PLAYERANGLE:",12)) + { + ae->PlayerAngle = atoi(&LineBuffer[12]); + break; + } + + if (!strnicmp(LineBuffer,"SCREENBACK:",11)) + { + ResScreenBack = atoi(&LineBuffer[11]); + break; + } + + if (!strnicmp(LineBuffer,"SCROLLBACK:",11)) + { + ResScrollBack = atoi(&LineBuffer[11]); + break; + } + + if (!strnicmp(LineBuffer,"TOPCOLOR:",9)) + { + ae->TopColor = atoi(&LineBuffer[9]); + break; + } + + if (!strnicmp(LineBuffer,"BOTTOMCOLOR:",12)) + { + ae->BottomColor = atoi(&LineBuffer[12]); + break; + } + + if (!strnicmp(LineBuffer,"SHADING:",8)) + { + strupr(LineBuffer); + if (strstr(&LineBuffer[8],"OFF") != NULL) + ae->LightFlag = SHADING_OFF; + else + ae->LightFlag = SHADING_ON; + break; + } + + if (!strnicmp(LineBuffer,"FLOORS:",7)) + { + strupr(LineBuffer); + if (strstr(&LineBuffer[7],"OFF") != NULL) + ae->SysFlags |= SYS_SOLID_FLOOR; + else + { + ae->SysFlags &= ~SYS_SOLID_FLOOR; + } + break; + } + + if (!strnicmp(LineBuffer,"RESOLUTION:",11)) + { + Resolution = atoi(&LineBuffer[11]); + break; + } + + if (!strnicmp(LineBuffer,"LOADTYPE:",9)) + { + ae->bmLoadType = atoi(&LineBuffer[9]); // Sets for GIF or BBM + break; + } + + break; + + } + + } + +if (!result) + { + result = AckCreateObject(ae,0); // Create a dummy object for later + if (!result) + ae->ObjList[0]->Active = 0; // Turn off checking the object + } + +if (!QuietFlag) + printf("done\n"); +return(result); +} + +//============================================================================= +// Quick routine to display a bitmap into the desired buffer. Handles +// transparent colors. Currently used to display the ACK3D Demo bitmap. +//============================================================================= +void ShowBitmap(short x,short y,UCHAR *dst,short ht,short wt,UCHAR *bm) +{ + int offset,col; + short endy; + UCHAR ch; + +offset = (y*320) + x; +dst += offset; +endy = ae->WinEndY + 2; + +while (ht-- > 0 && y++ <= endy) + { + for (col = 0; col < wt; col++) + { + ch = *bm++; + if (ch) + dst[col] = ch; + } + dst += 320; + } + + +} + + +//============================================================================= +// +//============================================================================= +void LoadDemoBitmap(void) +{ + UCHAR *d; + +DemoPtr = AckReadiff((char *)DEMO_RESOURCE); +if (DemoPtr == NULL) + return; + +d = DemoPtr; +Demowt = (*(short *)d); +d += 2; +Demoht = (*(short *)d); + +pHand1 = AckReadiff((char *)HAND1_RESOURCE); +if (pHand1 == NULL) + return; + +d = pHand1; +Handw1 = (*(short *)d); +d += 2; +Handh1 = (*(short *)d); + +pHand2 = AckReadiff((char *)HAND2_RESOURCE); +if (pHand2 == NULL) + return; + +d = pHand2; +Handw2 = (*(short *)d); +d += 2; +Handh2 = (*(short *)d); + +} + +//============================================================================= +// +//============================================================================= +short Initialize(short iFlag) +{ + short i,bnum; + +if (MouseInstalled() != -1) + { + printf("Mouse is required to run.\n"); + return(1); + } + +ae = AckMalloc(sizeof(ACKENG)); +if (ae == NULL) + { + printf("Unable to get required memory.\n"); + return(2); + } + +memset(ae,0,sizeof(ACKENG)); + +ae->WinStartX = 20; +ae->WinEndX = 299; +ae->WinStartY = 0; +ae->WinEndY = 156; + +// ae->WinStartX = 0; +// ae->WinEndX = 319; +// ae->WinStartY = 0; +// ae->WinEndY = 156; + +ae->LightFlag = SHADING_OFF; +ae->xPlayer = 192; +ae->yPlayer = 640; +ae->PlayerAngle = 0; +ae->TopColor = 0; +ae->BottomColor = 24; +ae->DoorSpeed = 6; +ae->NonSecretCode = 1; +ae->SysFlags |= SYS_SOLID_BACK; + +if (iFlag & 1) + { + ae->SysFlags |= SYS_SINGLE_BMP; + ae->FloorBitmap = 33; + ae->CeilBitmap = 45; + } + +printf("Initializing.\n"); + +i = AckOpenResource("kit.ovl"); +if (i) + { + printf("Unable to open resource KIT.OVL\n"); + return(3); + } + +i = LoadSmallFont(); +if (i) + { + printf("Error loading font BBM.\n"); + return(5); + } + +i = AckInitialize(ae); + +AckCloseResource(); + +if (i) + { + printf("Error initializing. Error code: %d\n",i); + return(3); + } + +printf("Processing DTF file "); + +i = AckOpenResource("pics.dtf"); +if (i) + { + printf("Unable to open resource PICS.DTF\n"); + return(3); + } + +i = ProcessInfoFile(0); +if (i) + { + printf("Error reading INF file.\n"); + if (i > 100 && i < 121) + printf("Last error was %s\n",ErrorMsgs[i-100]); + else + printf("Error code: %d\n",i); + + printf("Line was: \"%s\"\n",LineBuffer); + + return(4); + } + +bmFlash[0] = ae->bMaps[4]; +bmFlash[1] = ae->bMaps[8]; +bmFlash1[0] = ae->bMaps[24]; +bmFlash1[1] = ae->bMaps[2]; +FlashIndex = 0; +LoadDemoBitmap(); + +return(0); +} + +//============================================================================= +// Loads and displays the full screen picture and sets the palette to the one +// loaded with the picture. +//============================================================================= +short LoadAndShow(char *fName) +{ + short i,j; + UINT pos,begpos; + UCHAR pMask; + UCHAR *buf,*bPtr; + UCHAR *Video; + +buf = AckReadiff(fName); +if (buf == NULL) + return(-1); + +Video = (char *)0xA0000; +memmove(Video,&buf[4],64000); + +AckSetPalette(colordat); + +AckFree(buf); +return(0); +} + +//============================================================================= +// Loads a background image (mountains) and processes the image into the +// separate slices for use at display time. Currently a background image +// can be 640 columns wide. This number can be made dynamic if needbe and would +// need to be changed in the routine below and in the DrawBackground routine +// in the ACK engine. +//============================================================================= +int LoadBackDrop(void) +{ + int result; + int i,j,pos; + UCHAR *bPtr; + UCHAR *aPtr; + +result = 0; + +if (ResScrollBack) + { + printf("Loading background image....\n"); + bPtr = AckReadiff((char *)ResScrollBack); + printf("Processing background image.\n"); + if (bPtr != NULL) + { + for (i = 0; i < 320; i++) + { + pos = i + 4; + aPtr = BackArray[i]; + for (j = 0; j < 100; j++) + { + *aPtr++ = bPtr[pos]; + pos += 320; + } + } + + for (i = 320; i < 640; i++) + { + pos = (i - 320) + 32004; + aPtr = BackArray[i]; + for (j = 0; j < 100; j++) + { + *aPtr++ = bPtr[pos]; + pos += 320; + } + } + + AckFree(bPtr); + } + else + { + printf("Unable to load background image.\n"); + result = 8; + } + } + +return(result); +} + +//============================================================================= +// +//============================================================================= +void ShowHit(void) +{ + int x,y,result,wNum; + int flag; + short oNum,mPos; + +ae->ObjList[0]->x = ae->xPlayer; +ae->ObjList[0]->y = ae->yPlayer; + +wNum = 32; +oNum = 0; +flag = 0; + +while (wNum--) + { + result = AckMoveObjectPOV(0,ae->PlayerAngle,12); + + if (result == POV_OBJECT) + { + flag = 1; + oNum = AckGetObjectHit(); + break; + } + + if (result == POV_XWALL) + { + flag = 2; + oNum = ae->xGrid[AckGetWallHit()]; + if (oNum & (DOOR_TYPE_SLIDE+DOOR_TYPE_SPLIT)) + flag = 4; + oNum &= 0xFF; + break; + } + + if (result == POV_YWALL) + { + flag = 3; + oNum = ae->yGrid[AckGetWallHit()]; + if (oNum & (DOOR_TYPE_SLIDE+DOOR_TYPE_SPLIT)) + flag = 4; + oNum &= 0xFF; + break; + } + } + + +if (flag) + { + mPos = (ae->ObjList[0]->y & 0xFFC0) + (ae->ObjList[0]->x >> 6); + + switch (flag) + { + + case 1: + sprintf(LineBuffer,"Object %d hit\nat location %d",oNum,mPos); + break; + + case 2: + sprintf(LineBuffer,"Xwall %d hit\nat location %d",oNum,mPos); + break; + + case 3: + sprintf(LineBuffer,"Ywall %d hit\nat location %d",oNum,mPos); + break; + + case 4: + sprintf(LineBuffer,"Door %d hit\nat location %d",oNum,mPos); + break; + + default: + *LineBuffer = '\0'; + break; + } + + ShowHitFlag = 1; + } + + +} + +//============================================================================= +// +//============================================================================= +void UpdateBlast(void) +{ + short j; + +if (ae->ObjList[99]->Flags & OF_ANIMDONE && + ae->ObjList[99]->CurrentType != NO_WALK) + { + AckSetObjectType(ae,99,NO_WALK); + ae->ObjList[99]->Flags &= ~OF_ANIMDONE; + } + +j = AckMoveObjectPOV(99,ae->ObjList[99]->Dir,ae->ObjList[99]->Speed); + +if (j > 0 && j != POV_PLAYER) + { + if (j != POV_OBJECT) + { + ae->ObjList[99]->Active = 0; + Shooting = 0; + return; + } + + j = AckGetObjectHit(); + + if (j > 4 && j < 13) + { + if (ae->ObjList[j]->CurrentType == NO_WALK) + { + AckSetObjectType(ae,j,NO_INTERACT); + ObjCounter[j] = TimerCounter + 18 + (rand() % 120); + } + } + + if (j == 51) + ae->ObjList[j]->Active = 0; + + ae->ObjList[99]->Active = 0; + Shooting = 0; + } + +} + +//============================================================================= +// +//============================================================================= +void CheckMonsters(void) +{ + int i,tFlag,xp,yp; + int pMap,oMap,oRow,oCol; + int row,col,offset; + long dx,dy,dx2,dy2,dist; + short cType; + UCHAR oFlags; + +xp = ae->xPlayer; +yp = ae->yPlayer; + +pMap = (yp & 0xFFC0) + (xp >> 6); + +i = 5; +//for (i = 5; i < 13; i++) +while (1) + { + if (ae->ObjList[i]->Active) + { + oFlags = ae->ObjList[i]->Flags; + cType = ae->ObjList[i]->CurrentType; + dx = xp - ae->ObjList[i]->x; + dy = yp - ae->ObjList[i]->y; + + dx2 = dx * dx; + dy2 = dy * dy; + if ((dx2+dy2) < 128000) + { + ae->ObjList[i]->Dir = AckGetObjectAngle(dx,dy); + if (cType == NO_WALK) + { + ae->ObjList[i]->Flags |= OF_ANIMATE; + oFlags |= OF_ANIMATE; + } + + if (cType == NO_CREATE && !(oFlags & OF_ANIMATE)) + { + if (i < 50) + { + ae->ObjList[i]->Flags |= OF_ANIMATE; + oFlags |= OF_ANIMATE; + } + else + AckSetObjectType(ae,i,NO_WALK); + } + + } + + if (cType == NO_WALK && (oFlags & OF_ANIMATE)) + { + tFlag = AckMoveObjectPOV(i,ae->ObjList[i]->Dir,8); + oRow = ae->ObjList[i]->Dir / INT_ANGLE_90; + switch (tFlag) + { + case POV_PLAYER: + StrAmount--; + if (StrAmount < 0) StrAmount = 0; + ShowStatus(); + break; + + case POV_NOTHING: + break; + + case POV_SLIDEX: + if (oRow > 1) ae->ObjList[i]->Dir = INT_ANGLE_270; + else ae->ObjList[i]->Dir = INT_ANGLE_90; + break; + + case POV_SLIDEY: + if (oRow > 0 && oRow < 3) + ae->ObjList[i]->Dir = INT_ANGLE_180; + else + ae->ObjList[i]->Dir = 0; + break; + + case POV_OBJECT: + ae->ObjList[i]->Dir = rand() % INT_ANGLE_360; + break; + + default: + if (i < 50) + ae->ObjList[i]->Flags &= ~OF_ANIMATE; + break; + + } + } + } + + i++; + if (i == 13) i = 51; + if (i == 52) break; + } + +} + +//============================================================================= +// +//============================================================================= +void ShowStatus(void) +{ + UCHAR *Video; + int row,offset,rlen,glen; + + +offset = (163 * 320) + 34; +glen = MagAmount; +if (glen > MAX_MAG_AMOUNT) glen = MAX_MAG_AMOUNT; +rlen = MAX_MAG_AMOUNT - glen; +Video = (UCHAR *)0xA0000; +Video += offset; + +for (row = 0; row < 11; row++) + { + if (glen) + memset(Video,105,glen); + if (rlen) + memset(&Video[glen],39,rlen); + + Video += 320; + } + +offset = (183 * 320) + 34; +glen = StrAmount >> 1; +if (glen > MAX_STR_HALF_AMOUNT) glen = MAX_STR_HALF_AMOUNT; +rlen = MAX_STR_HALF_AMOUNT - glen; +Video = (UCHAR *)0xA0000; +Video += offset; + +for (row = 0; row < 11; row++) + { + if (glen) + memset(Video,105,glen); + if (rlen) + memset(&Video[glen],39,rlen); + + Video += 320; + } + + +} + +//============================================================================= +// +//============================================================================= +void DrawBmpBox(int x1,int y1,int x2,int y2,long d1,long d2) +{ + long NumCols,DeltaDist; + long htStep,dst; + long ht1,ht2,bmht; + long offset,xpos,ypos,bmPos,yposLow; + long bmCol,bmColStep; + UCHAR *Video,*bmp,*VidOrg,*VidEnd; + UCHAR *vPtr; + +dst = d1; +if (d1 > d2) dst = d2; + +bmht = 64 * 256; +DeltaDist = labs(d2 - d1); +ht1 = bmht / dst; +NumCols = ht1; +if (DeltaDist) + NumCols = (ht1 * (64 - DeltaDist)) / 64; + +if (NumCols < 1) NumCols = 1; + +bmColStep = (64*65536) / NumCols; +htStep = (DeltaDist<<16) / NumCols; +if (d2 < d1) htStep = -htStep; + +bmp = ae->bMaps[1]; +VidOrg = Video = ae->ScreenBuffer; +VidEnd = VidOrg + 64000; +offset = (y1 * 320) + x1; +Video += offset; + +bmCol = 0; +d2 = (d1<<16); + +for (xpos = 0; xpos < NumCols; xpos++) + { + vPtr = Video; + ypos = 0x1FFF; + yposLow = 0x1F; + bmPos = (bmCol>>16) * 64; + d1 = (d2>>16); + + while (yposLow > 0 && vPtr > VidOrg) + { + *vPtr = bmp[bmPos+yposLow]; + vPtr -= 320; + ypos -= d1; + yposLow = ypos >> 8; + } + + vPtr = Video + 320; + ypos = 0x2000; + yposLow = 0x20; + + while (yposLow < 64 && vPtr < VidEnd) + { + *vPtr = bmp[bmPos+yposLow]; + vPtr += 320; + ypos += d1; + yposLow = ypos >> 8; + } + + d2 += htStep; + bmCol += bmColStep; + Video++; + } + + +} + +//============================================================================= +// +//============================================================================= +void ClsBmpBox(int x1,int y1,int x2,int y2) +{ + int offset,wt; + UCHAR *Video; + +Video = (UCHAR *)0xA0000; +offset = (y1 * 320) + x1; +wt = (x2 - x1) + 1; +Video += offset; + +while (y1++ <= y2) + { + memset(Video,0,wt); + Video += 320; + } + +} + +typedef struct { + int x1; + int y1; + int x2; + int y2; + int d1; + int d2; + } AWALL; + +short near long_sqrt(long v); + +//============================================================================= +// +//============================================================================= +void DoTest(void) +{ + int done,cnt,Total; + int xp,yp,ColX; + long dx,dy; + UCHAR *Video; + AWALL aw[2]; + short i,minAngle,maxAngle,objAngle; + +AckRegisterStructure(ae); + +memset(ae->xGrid,0,(GRID_ARRAY*2)); +memset(ae->yGrid,0,(GRID_ARRAY*2)); +memset(Grid,0,(GRID_ARRAY*2)); + +aw[0].x1 = 64; +aw[0].y1 = 128; +aw[0].x2 = 128; +aw[0].y2 = 128; + +aw[1].x1 = 128; +aw[1].y1 = 128; +aw[1].x2 = 128; +aw[1].y2 = 64; +Total = 2; +Video = (UCHAR *)0xA0000; +done = 0; +xp = yp = 256; + + +while (!done) + { + memset(ae->ScreenBuffer,0,64000); + minAngle = ae->PlayerAngle - (INT_ANGLE_32 + 10); + if (minAngle < 0) + minAngle += INT_ANGLE_360; + + maxAngle = ae->PlayerAngle + (INT_ANGLE_32 + 10); + if (maxAngle >= INT_ANGLE_360) + maxAngle -= INT_ANGLE_360; + + xp = ae->xPlayer; + yp = ae->yPlayer; + + for (cnt = 0; cnt < Total; cnt++) + { + dx = aw[cnt].x1 - xp; + dy = aw[cnt].y1 - yp; + aw[cnt].d1 = long_sqrt((dx*dx)+(dy*dy)); + objAngle = AckGetObjectAngle(dx,dy); + dx = aw[cnt].x2 - xp; + dy = aw[cnt].y2 - yp; + aw[cnt].d2 = long_sqrt((dx*dx)+(dy*dy)); + + if (minAngle > maxAngle) + { + if (objAngle >= minAngle) + ColX = objAngle - minAngle; + else + ColX = (objAngle+INT_ANGLE_360) - minAngle; + } + else + { + ColX = objAngle - minAngle; + } + + if (ColX > -40 && ColX < 320) + DrawBmpBox(ColX,100,aw[cnt].x2,100,aw[cnt].d1,aw[cnt].d2); + } + memmove(Video,ae->ScreenBuffer,64000); + + if (Keys[ESCAPE_KEY]) + break; + + if (Keys[LEFT_ARROW_KEY]) + { + ae->PlayerAngle--; + if (ae->PlayerAngle < 0) + ae->PlayerAngle += INT_ANGLE_360; + } + + if (Keys[RIGHT_ARROW_KEY]) + { + ae->PlayerAngle++; + if (ae->PlayerAngle >= INT_ANGLE_360) + ae->PlayerAngle -= INT_ANGLE_360; + } + + if (Keys[UP_ARROW_KEY]) + { + AckMovePOV(ae->PlayerAngle,16); + } + + if (Keys[DOWN_ARROW_KEY]) + { + i = ae->PlayerAngle + INT_ANGLE_180; + if (i >= INT_ANGLE_360) + i -= INT_ANGLE_360; + + AckMovePOV(i,16); + } + + } + + +} + +//============================================================================= +// +//============================================================================= +short LoadNewLevel(char *LevelName) +{ + short i; + +i = AckOpenResource(LevelName); +if (i) + return(-1); + +for (i = 1; i < MAX_WALLBMPS; i++) + { + if (ae->bMaps[i] != NULL) + { + AckFree(ae->bMaps[i]); + ae->bMaps[i] = NULL; + } + } + +for (i = 1; i < MAX_OBJBMPS; i++) + { + if (ae->oMaps[i] != NULL) + { + AckFree(ae->oMaps[i]); + ae->oMaps[i] = NULL; + } + } + +for (i = 1; i < ae->MaxObjects; i++) + { + if (ae->ObjList[i] != NULL) + { + AckFree(ae->ObjList[i]); + ae->ObjList[i] = NULL; + } + } + +ae->MaxObjects = 0; + +for (i = 0; i < (GRID_ARRAY - GRID_WIDTH); i++) + { + if (ae->myGrid[i] != NULL) + AckFree(ae->myGrid[i]); + + ae->mxGrid[i] = NULL; + ae->mxGrid[i+1] = NULL; + ae->myGrid[i] = NULL; + ae->myGrid[i+GRID_WIDTH] = NULL; + + } + + +i = ProcessInfoFile(1); +if (i) + return(i); + +AckCloseResource(); +AckRegisterStructure(ae); +return(0); +} + + short LevelFlag; + +//============================================================================= +// +//============================================================================= +short main(short argc,char **argv) +{ + short i,j,done,fpos; + char ch; + MOUSE mouse; + UCHAR *Video; + int Spin,MoveAmount; + int SpinAngle,MoveAngle; + long TimerEnd; + short DemoFlag; + short lpx,lpy,lpa; + int StartTime,EndTime,InfoFlag; + int CkStart,CkEnd; + int TurnFactor,MoveFactor,MoveHalfFactor; + int HandX,HandY,HandDY; + long x1,y1; + +i = 0; +if (argc > 1) + { + for (j = 1; j <= argc; j++) + { + if (!strnicmp(argv[j],"-1",2)) + i |= 1; + + } + } + +if ((done = Initialize(i)) != 0) + return(done); + +if ((done = LoadBackDrop()) != 0) + return(done); + +// Do some test setup of our GooMonsters +AckSetObjectType(ae,6,NO_WALK); +ae->ObjList[5]->Flags &= ~OF_ANIMATE; +ae->ObjList[7]->Flags &= ~OF_ANIMATE; +ae->ObjList[8]->Flags &= ~OF_ANIMATE; +ae->ObjList[9]->Flags &= ~OF_ANIMATE; +ae->ObjList[10]->Flags &= ~OF_ANIMATE; +ae->ObjList[11]->Flags &= ~OF_ANIMATE; +ae->ObjList[12]->Flags &= ~OF_ANIMATE; +ae->ObjList[99]->Active = 0; +ae->ObjList[99]->Speed = 48; + +Shooting = 0; +MagAmount = MAX_MAG_AMOUNT; +StrAmount = MAX_STR_AMOUNT; + +// Setup keyboard and timer interrupts +oldvec=_dos_getvect(KEYBD); +_dos_setvect(KEYBD,myInt); +oldTimer=_dos_getvect(8); +_dos_setvect(8,myTimer); +AckTmDelay = 3; +AckSpeedUp(AckTmDelay); // Set the timer interrupt at 3 times normal + +StartBGmusic(); + +// Switch to mode 13 +AckSetVGAmode(); + +// Put up the main screen +LoadAndShow((char *)ResScreenBack); + +//DoTest(); + +// Set palette for shading if needed +AckSetupPalRanges(ae,ranges); + +ShowStatus(); + +LevelFlag = 0; +done = 0; +SpinAngle = Spin = MoveAmount = 0; +MouseReleased(); +SetMouseCursor(120,160); +fpos = 64; +DemoFlag = 0; +if (DemoPtr != NULL) DemoFlag = 1; +TimerEnd = TimerCounter + 180; + + +// MUST register each ACKENG structure once before use and after AckInitialize +AckRegisterStructure(ae); + +StartTime = TimerCounter; +AckBuildView(); +AckDisplayScreen(); +EndTime = TimerCounter - StartTime; + +if (!EndTime) EndTime = 1; + +TurnFactor = INT_ANGLE_1 * EndTime; +MoveFactor = 3 * EndTime; +MoveHalfFactor = MoveFactor >> 1; + +StartTime = clock(); +EndTime = StartTime; +InfoFlag = 0; + +HandX = 134; +HandY = 132; +HandDY = -2; +Throwing = 0; + +while (!done) + { + + if (SpinAngle) + { + ae->PlayerAngle += SpinAngle; + if (ae->PlayerAngle >= INT_ANGLE_360) + ae->PlayerAngle -= INT_ANGLE_360; + if (ae->PlayerAngle < 0) + ae->PlayerAngle += INT_ANGLE_360; + + SpinAngle >>= 3; + if (SpinAngle == -1) SpinAngle = 0; + } + + if (MoveAmount) + { + HandY += HandDY; + if (HandY < 125) HandDY = 2; + if (HandY > 132) HandDY = -2; + + j = AckMovePOV(MoveAngle,MoveAmount); + AckCheckDoorOpen(ae->xPlayer,ae->yPlayer,MoveAngle); + MoveAmount >>= 1; + + if (j == POV_OBJECT) + { + j = AckGetObjectHit(); + + if (j > 4 && j < 13) + { + StrAmount--; + if (StrAmount < 0) + StrAmount = 0; + + ShowStatus(); + } + + if (j >= 36 && j <= 49) + { + ae->ObjList[j]->Active = 0; + MagAmount += 4; + if (MagAmount > MAX_MAG_AMOUNT) + MagAmount = MAX_MAG_AMOUNT; + ShowStatus(); + } + + if (j == 50 && ae->ObjList[50]->CurrentType == NO_CREATE) + { + AckSetObjectType(ae,j,NO_WALK); + StrAmount = MAX_STR_AMOUNT; + ShowStatus(); + } + + } + } + else + { + if (HandY < 132) + HandY++; + } + + AckCheckObjectMovement(); // Animate objects if needed + + for (j = 5; j < 13; j++) + { + if (ae->ObjList[j]->Flags & OF_ANIMDONE && + ae->ObjList[j]->CurrentType == NO_DESTROY) + { + AckSetObjectType(ae,j,NO_CREATE); + ae->ObjList[j]->Flags &= ~OF_ANIMDONE; + ae->ObjList[j]->Flags &= ~OF_ANIMATE; + ae->ObjList[j]->Active = 0; + } + + if (ae->ObjList[j]->Flags & OF_ANIMDONE && + ae->ObjList[j]->CurrentType != NO_WALK) + { + AckSetObjectType(ae,j,NO_WALK); + ae->ObjList[j]->Flags &= ~OF_ANIMDONE; + ObjCounter[j] = TimerCounter + 18 + (rand() % 120); + } + + + + if (TimerCounter > ObjCounter[j]) + { + ObjCounter[j] = TimerCounter + 180 + (rand() % 180); + if (ae->ObjList[j]->CurrentType == NO_WALK) + AckSetObjectType(ae,j,NO_ATTACK); + else + { + if (ae->ObjList[j]->CurrentType == NO_INTERACT) + AckSetObjectType(ae,j,NO_DESTROY); + + } + } + } + + if (Shooting) + UpdateBlast(); + + CheckMonsters(); + + CkStart = TimerCounter; + AckBuildView(); // Build floor, ceiling, and walls into ScrnBuffer + + if (DemoFlag) + { + switch (DemoFlag) + { + case 1: + if (TimerCounter > TimerEnd) + { + DemoFlag++; + TimerEnd = TimerCounter + 540; + } + break; + + case 2: + ShowBitmap(130,20,ae->ScreenBuffer,Demoht,Demowt,&DemoPtr[4]); + if (TimerCounter > TimerEnd) + { + DemoFlag = 1; + TimerEnd = TimerCounter + 2160; + } + break; + + default: + break; + } + + } + + switch (Throwing) + { + case 0: + ShowBitmap(HandX,HandY,ae->ScreenBuffer,Handh1,Handw1,&pHand1[4]); + break; + + case 1: + HandY -= 20; + if (HandY < 97) + { + HandY = 97; + Throwing++; + } + ShowBitmap(HandX,HandY,ae->ScreenBuffer,Handh1,Handw1,&pHand1[4]); + break; + + case 2: + HandY -= 5; + Throwing++; + ShowBitmap(HandX,HandY,ae->ScreenBuffer,Handh2,Handw2,&pHand2[4]); + break; + + case 3: + ae->ObjList[99]->Active = 1; + ae->ObjList[99]->x = ae->xPlayer; + ae->ObjList[99]->y = ae->yPlayer; + ae->ObjList[99]->Dir = ae->PlayerAngle; + ae->ObjList[99]->mPos = (ae->yPlayer & 0xFFC0) + ae->xPlayer >> 6; + AckSetObjectType(ae,99,NO_CREATE); + Shooting = 1; + MagAmount--; + ShowStatus(); + Throwing++; + ShowBitmap(HandX,HandY,ae->ScreenBuffer,Handh2,Handw2,&pHand2[4]); + break; + + case 4: + HandY += 5; + Throwing++; + ShowBitmap(HandX,HandY,ae->ScreenBuffer,Handh2,Handw2,&pHand2[4]); + break; + + case 5: + HandY += 20; + if (HandY > 132) + Throwing = 0; + ShowBitmap(HandX,HandY,ae->ScreenBuffer,Handh1,Handw1,&pHand1[4]); + break; + + + default: + break; + } + + if (InfoFlag) + { + EndTime = clock(); + if (EndTime > StartTime+100 ) + { + StartTime = EndTime; + cframes = framespersec; + framespersec = 0; + } + + sprintf(LineBuffer,"FPS: %6ld,%d,%d,%d", + (long)cframes,ae->xPlayer,ae->yPlayer,ae->PlayerAngle); + smWriteHUD(10,20,32,LineBuffer); + framespersec++; + } + +// sprintf(LineBuffer,"MEM: %ld",AckMemUsed); +// smWriteHUD(22,30,32,LineBuffer); + + if (ShowHitFlag) + smWriteHUD(10,10,32,LineBuffer); + + + AckDisplayScreen(); // Copy ScrnBuffer to actual video + CkEnd = TimerCounter - CkStart; + if (!CkEnd) CkEnd = 1; + + TurnFactor = INT_ANGLE_1 * CkEnd; + MoveFactor = 3 * CkEnd; + MoveHalfFactor = MoveFactor >> 1; + + i = (ae->yPlayer & 0xFFC0) + (ae->xPlayer >> 6); + if (FloorMap[i] == 0x28) + { + StrAmount--; + if (StrAmount < 1) + break; + ShowStatus(); + } + + CheckMouse(&mouse); + + if (mouse.mButtons & 1) + { + if (!(ae->ObjList[99]->Active) && MagAmount > 0 && !Throwing) + { + Throwing = 1; + #if 0 + ae->ObjList[99]->Active = 1; + ae->ObjList[99]->x = ae->xPlayer; + ae->ObjList[99]->y = ae->yPlayer; + ae->ObjList[99]->Dir = ae->PlayerAngle; + ae->ObjList[99]->mPos = (ae->yPlayer & 0xFFC0) + ae->xPlayer >> 6; + AckSetObjectType(ae,99,NO_CREATE); + Shooting = 1; + MagAmount--; + ShowStatus(); + #endif + } + } + + if (mouse.mButtons & 2) + { + MoveAmount += MoveFactor; // 16; + if (MoveAmount > MAX_AMOUNT) + MoveAmount = MAX_AMOUNT; + MoveAngle = ae->PlayerAngle; + } + + if (mouse.mdx < 0) + { + Spin = -mouse.mdx; + Spin >>= 5; + SpinAngle = -TurnFactor * Spin; // -INT_ANGLE_1 * Spin; + Spin = 1; + } + + if (mouse.mdx > 0) + { + Spin = mouse.mdx; + Spin >>= 5; + SpinAngle = TurnFactor * Spin; // INT_ANGLE_1 * Spin; + Spin = 1; + } + + if (mouse.mdy < 0) + { + i = -mouse.mdy; + i >>= 2; + i += MoveHalfFactor; + MoveAmount += i; + if (MoveAmount > MAX_AMOUNT) + MoveAmount = MAX_AMOUNT; + MoveAngle = ae->PlayerAngle; + } + + if (mouse.mdy > 20) + { + i = mouse.mdy; + i >>= 3; + i += MoveHalfFactor; + j = ae->PlayerAngle + INT_ANGLE_180; + if (j >= INT_ANGLE_360) + j -= INT_ANGLE_360; + + MoveAmount += i; + if (MoveAmount > MAX_AMOUNT) + MoveAmount = MAX_AMOUNT; + MoveAngle = j; + } + + if (Keys[ESCAPE_KEY]) + break; + + if(Keys[RIGHT_ARROW_KEY]) + { + Spin += 1; + SpinAngle += TurnFactor; // INT_ANGLE_1 * Spin; + } + + if(Keys[LEFT_ARROW_KEY]) + { + Spin += 1; + SpinAngle -= TurnFactor; // -INT_ANGLE_1 * Spin; + } + + if(Keys[UP_ARROW_KEY]) + { + MoveAmount += (MoveFactor + MoveHalfFactor); // 12; + if (MoveAmount > MAX_AMOUNT) + MoveAmount = MAX_AMOUNT; + MoveAngle = ae->PlayerAngle; + } + + if(Keys[DOWN_ARROW_KEY]) + { + j = ae->PlayerAngle + INT_ANGLE_180; + if (j >= INT_ANGLE_360) + j -= INT_ANGLE_360; + + MoveAmount += (MoveFactor + MoveHalfFactor); // 12; + if (MoveAmount > MAX_AMOUNT) + MoveAmount = MAX_AMOUNT; + MoveAngle = j; + } + + + if (Keys[C_KEY]) + { + ae->SysFlags ^= SYS_SOLID_CEIL; + ae->SysFlags &= ~SYS_SOLID_BACK; + Keys[C_KEY] = 0; + + if ((ae->SysFlags & SYS_SOLID_CEIL) && (ResScrollBack != 0)) + ae->SysFlags |= SYS_SOLID_BACK; + AckRegisterStructure(ae); + } + + if (Keys[R_KEY]) + { + Keys[R_KEY] = 0; + Resolution++; + if (Resolution > 2) + Resolution = 0; + } + + if (Keys[F_KEY]) + { + ae->SysFlags ^= SYS_SOLID_FLOOR; + Keys[F_KEY] = 0; + AckRegisterStructure(ae); + } + + + if (Keys[PGUP_KEY] && ViewHeight < 60) + { + ViewHeight++; + CeilingHeight++; + } + + if (Keys[PGDN_KEY] && ViewHeight > 4) + { + ViewHeight--; + CeilingHeight--; + } + + if (Keys[NUM_1_KEY]) + { + Keys[NUM_1_KEY]=0; + dFactor--; + } + + if (Keys[NUM_2_KEY]) + { + Keys[NUM_2_KEY]=0; + dFactor++; + } + + + if (Keys[MINUS_KEY]) + { + Keys[MINUS_KEY]=0; + mFactor--; + } + + if (Keys[PLUS_KEY]) + { + Keys[PLUS_KEY]=0; + mFactor++; + } + + if (Keys[I_KEY]) + { + Keys[I_KEY] = 0; + InfoFlag ^= 1; + } + + if (Keys[B_KEY]) + { + Keys[B_KEY]=0; + // mFactor -= 64; + if (!LevelFlag) + LoadNewLevel("MALL.DTF"); + else + { + LoadNewLevel("PICS.DTF"); + ae->ObjList[99]->Active = 0; + ae->ObjList[99]->Speed = 48; + } + LevelFlag ^= 1; + } + + if (Keys[S_KEY]) + { + Keys[S_KEY] = 0; + mFactor += 64; + } + } + +EndBGmusic(); +ShutDownFlag = 1; +_disable(); +AckSlowDown(); // Set the timer back to normal speed +AckWrapUp(ae); +AckSetTextmode(); +_dos_setvect(KEYBD,oldvec); +_dos_setvect(8,oldTimer); +_enable(); + +return(0); +} + + \ No newline at end of file diff --git a/fdemo/KIT.H b/fdemo/KIT.H new file mode 100644 index 0000000..94dce77 --- /dev/null +++ b/fdemo/KIT.H @@ -0,0 +1,140 @@ +// Header file for 3D Construction Kit +// Started: 01/02/94 +// Author: Lary Myers +// Module: KIT.H +// (c) CopyRight 1994 All Rights Reserved + +#define MODE_GRAPHICS 0x13 +#define MODE_TEXT 0x03 + +#define SCREEN_LENGTH 64000 // Bytes in one full screen +#define SCREEN_LEN_WORDS 32000 // Words in one screen +#define SCREEN_LEN_DWORDS 16000 // DWORDS in one screen +#define SCREEN_PLANES 1 // Number of planes for this mode +#define SCREEN_WIDTH 320 // Number of bytes in one row +#define SCREEN_HEIGHT 200 // Number of rows on screen + +#define BUTTON_OK 0 +#define BUTTON_OKCANCEL 1 +#define BUTTON_YESNO 2 +#define BUTTON_ABORTRETRY 3 + +#define BUTTON_RET_OK 0 +#define BUTTON_RET_CANCEL 1 +#define BUTTON_RET_YES 2 +#define BUTTON_RET_NO 3 +#define BUTTON_RET_ABORT 4 +#define BUTTON_RET_RETRY 5 + +// Used in IORTN.C for message boxes +typedef struct { + short x; + short y; + short x1; + short y1; + short index; + } BUTTON; + + +// Used in IORTN.C for message boxes +typedef struct { + short Count; + short List[4]; + } INDEXES; + + +// Used in IORTN.C list boxes +typedef struct _lcb { + struct _lcb *Back; + struct _lcb *Fwd; + char Data[1]; + } LCB; + +typedef struct { + short ux; + short uy; + short ux1; + short uy1; + short dx; + short dy; + short dx1; + short dy1; + } ARROWRECT; + + +//============================================================================= +// Prototypes in UTIL.C +//============================================================================= +UINT Inkey(void); +char *GetExtent(char *s); +char *StripEndOfLine(char *s); +char *SkipSpaces(char *s); +char *AddExtent(char *s,char *ext); +char *CopyToComma(char *dest,char *src); +short HasWildCards(char *s); +void SetCurrentPath(char *p); +void InitDiskList(void); +char *GetPath(char *s); +//============================================================================= +// Prototypes in GRAPHICS.C +//============================================================================= +void SetMode(short mode); +void ClearScreen(UCHAR color); +void PutPixel(short x,short y,UCHAR color); +void DrawVerticalLine(short x,short y,short y1,UCHAR color); +void DrawHorizontalLine(short x,short y,short x1,UCHAR color); +void DrawBox(short x,short y,short x1,short y1,UCHAR color); +void DrawRoundBox(short x,short y,short x1,short y1,UCHAR color); +void Draw3DBox(short x,short y,short x1,short y1,UCHAR TopColor,UCHAR BottomColor); +void DrawSolidBox(short x,short y,short x1,short y1,UCHAR color); +void DrawXORBox(short x,short y,short x1,short y1,UCHAR color); +void DrawXORRect(short x,short y,short x1,short y1,UCHAR color); +void DrawLine(short x1,short y1,short x2,short y2,UCHAR color); +void PutBitmap(short x,short y,short width,short height,UCHAR *Bitmap); +UCHAR *GetBitmap(short x,short y,short width,short height,UCHAR *buffer); +UINT GetBufferSize(short width,short height); +void PutBitmapAsIcon(short x,short y,UCHAR *bm); +void PutAckBitmap(short x,short y,UCHAR *bm); +UCHAR *ScreenToBuffer(UCHAR *buffer); +void BufferToScreen(UCHAR *buffer); +void SetPalette(UCHAR *PalBuf); +void ShowChar(short x,short y,char ch); +void ShowString(short x,short y,char *s); +UCHAR *LoadAndShowScreen(char *PicName); +//============================================================================= +// Prototypes in MOUSE.C +//============================================================================= +short MouseInstalled(void); +void ShowMouse(void); +void HideMouse(void); +short ReadMouseCursor(short *mrow,short *mcol); +short ReadMouseButtons(void); +void SetMouseCursor(short mrow,short mcol); +void SetMouseMinMaxColumns(short mincol,short maxcol); +void SetMouseMinMaxRows(short minrow,short maxrow); +void SetMouseShape(short hsrow,short hscol,char *mask); +void MouseReleased(void); +void MouseSetFunction(UCHAR,void *); +//============================================================================= +// Prototypes in READIFF.C +//============================================================================= +UCHAR *Readiff(char *picname); +//============================================================================= +// Prototypes in IO.C +//============================================================================= +void GetFontTable(void); +void ShowChar(short x,short y,char ch); +void ShowString(short x,short y,char *s); +short ShowMessageBox(char *Body,UINT Buttons); +void ShowListBox(short x,short y,short x1,short y1,short index,LCB *lcb); +char *FileBox(char *fName,char *Path,char *rBuf,char *Title,char *ActiveButton); +void TestListBox(void); +short PickList(short x,short y,short x1,short y1,LCB *Anchor); +short CreateSelectPickList(short x,short y,short x1,short y1,short count,char **List,short SortFlag); +short ShowPickList(char **p,short SortFlag); +//============================================================================= +// Prototypes in SETS.C +//============================================================================= +short LoadSet(char *fName); + + \ No newline at end of file diff --git a/fdemo/KIT.OVL b/fdemo/KIT.OVL new file mode 100644 index 0000000000000000000000000000000000000000..9e70eaec2d0e2bddafb915a39ba2b74aaf8e4192 GIT binary patch literal 62924 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 literal 0 HcmV?d00001 diff --git a/fdemo/MODPLAY.ASM b/fdemo/MODPLAY.ASM new file mode 100644 index 0000000..808c7cf --- /dev/null +++ b/fdemo/MODPLAY.ASM @@ -0,0 +1,1166 @@ +;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² +; Tiny MOD Player for Watcom C/C++32 and DOS/4GW +; Version 2.02a  May 09th, 1994 +; +; Copyright 1993,94 Carlos Hasan +;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²² + +ideal +p386 +model flat,c +jumps + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; PUBLICS +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ + +global MODPlayModule:near +global MODStopModule:near +global MODPlaySample:near +global MODPlayVoice:near +global MODStopVoice:near +global MODSetPeriod:near +global MODSetVolume:near +;;global _GETDS:near +global OurDataSeg:dword + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; EQUATES +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ + +MAXVOICES = 8 ; Max number of voices +MIDCRATE = 8363 ; C-2 frequency rate +BUFLEN = 2048 ; DMA Buffer Length + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; MOD file structures +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ + +struc MODSample ; sample header +SampleName db 22 dup (?) +SampleLength dw ? +Finetune db ? +Volume db ? +RepStart dw ? +RepLength dw ? +ends MODSample + +struc MODHeader ; file header +SongName db 20 dup (?) +Samples MODSample 31 dup (?) +OrderLen db ? +ReStart db ? +Order db 128 dup (?) +Sign dd ? +ends MODHeader + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; MOD Player structures +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ + +struc Voice +Address dd ?,? ; Current Address +StartAddress dd ? ; Loop Start Address +EndAddress dd ? ; Loop End Address +Frequency dd ? ; Frequency +Volume db ?,? ; Volume +ends Voice + +struc Track +Period dw ? ; Note Period +Instr db ? ; Instr Number +Volume db ? ; Volume +Effect dw ? ; Effect +ends Track + +struc Sample +Period dw ? ; Default Period +Volume dw ? ; Default Volume +SampleLength dd ? ; Sample Length +SampleData dd ? ; Sample 8-bit signed Data +ends Sample + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; DATA +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +dataseg + +IOAddr dw ? ; SB I/O Port Address +IRQNum db ? ; SB IRQ Level +DRQNum db ? ; SB DRQ Channel +SBRate dw ? ; SB Sampling Rate + +IRQVector df ? ; IRQ Saved Vector + +BufSelector dw ? ; DPMI Buffer Selector +VolBuffer dd ? ; Volume Buffer Address +DMABuffer dd ? ; DMA Buffer Address +DMALength dw ? ; DMA Buffer Length + +BufferPos dw ? ; Current Buffer Position +BufferLen dw ? ; Current Buffer Length +TimerCount dw ? ; Timer Accumulator +TimerSpeed dw ? ; Timer Speed Value +FreqDivisor dd ? ; Frequency Divisor + +ActiveVoices dw ? ; Number of Active Voices +Voices Voice MAXVOICES dup (?) ; Array of Voices + + label VoiceTable dword ; Lookup Table + I=0 + rept MAXVOICES + dd (offset Voices)+I*(size Voice) + I=I+1 + endm + +OrderPos db ? ; Order Position +OrderLen db ? ; Order Length +PatternRow db ? ; Pattern Row +OrderList db 128 dup (?) ; Order List +PatternPtr dd ? ; Current Pattern Address +Tempo db ? ; Tempo +TempoCount db ? ; Tempo Internal Counter +BPM db ? ; BPM value + +ActiveTracks dw ? ; Number of Active Tracks +Tracks Track MAXVOICES dup (?) ; Array of Tracks + +PatternList dd 128 dup (?) ; Pattern List of Pointers +SmpAddress dd 32 dup (?) ; Samples Addresses and Volume +SmpStartAddress dd 32 dup (?) +SmpEndAddress dd 32 dup (?) +SmpVolume db 32 dup (?) + +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +; CODE +;ħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħħ +codeseg + +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ +; MOD Player Stuff +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODPlayModule - Start playing a modulefile +; In: +; Song = Modulefile address +; Chans = Number of channels +; Rate = Sampling Rate +; Port = I/O Port +; IRQ = IRQ Level +; DRQ = DRQ Channel +; Out: +; EAX = status +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODPlayModule Song:dword,Chans:dword,Rate:dword,Port:dword,IRQ:dword,DRQ:dword + pushad + mov edi,[Song] ; Load parameters from stack + mov eax,[Chans] + mov [ActiveVoices],ax + mov eax,[Rate] + mov [SBRate],ax + mov eax,[Port] + mov [IOAddr],ax + mov eax,[IRQ] + mov [IRQNum],al + mov eax,[DRQ] + mov [DRQNum],al + mov eax,MIDCRATE*428 ; setup frequency divisor + xor edx,edx + shld edx,eax,16 + shl eax,16 + movzx ebx,[SBRate] + div ebx + mov [FreqDivisor],eax + call MODSetup ; setup MOD player stuff + jc MODPlayDone + call MCInit ; setup the muti-channel stuff + jc MODPlayDone + mov dl,[BPM] ; setup the multi-channel timer + call MCStartTimer + call DMASetBuf ; setup DMA controller + call IRQSetVect ; setup PIC interrupt + call SBOpenDevice ; setup SB for playback + jnc MODPlayDone + call MODStopModule +MODPlayDone: + popad + sbb eax,eax ; return status in EAX + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODStopModule - Stop playing the current modulefile +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODStopModule + pushad + call SBCloseDevice ; done SB playback + call IRQRestVect ; done PIC interrupt + call DMAReset ; done DMA controller + call MCDone ; done multi-channel stuff + popad + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODPlaySample - Play instrument sample +; In: +; Voice = Audio channel +; Instr = Instrument +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODPlaySample AVoice:dword,AInstr:dword + pushad + mov ebx,[AVoice] ; change the voice sample parameters + mov eax,[AInstr] + mov edx,[eax+Sample.SampleData] + mov esi,[eax+Sample.SampleLength] + add esi,edx + mov edi,esi + call MCStartVoice + movzx ecx,[eax+Sample.Period] ; change the voice frequency + call MCSetFrequency + mov ax,[eax+Sample.Volume] ; and set the voice volume + call MCSetVolume + popad + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODPlayVoice - Set the voice instrument, period and volume +; In: +; Voice = Audio channel +; Instr = Instrument Number +; Period = Amiga Period +; Volume = Volume +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODPlayVoice AVoice:dword,AInstr:dword,APeriod:dword,AVolume:dword + pushad + mov ebx,[AVoice] ; change the voice sample parameters + mov eax,[AInstr] + mov edx,[4*eax+SmpAddress] + mov esi,[4*eax+SmpStartAddress] + mov edi,[4*eax+SmpEndAddress] + call MCStartVoice + mov ecx,[APeriod] ; change the voice frequency + call MCSetFrequency + mov eax,[AVolume] ; and set the voice volume + call MCSetVolume + popad + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODStopVoice - Turn off the specified voice +; In: +; Voice = Audio channel +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODStopVoice AVoice:dword + pushad + mov ebx,[AVoice] ; set the voice volume to zero + xor al,al + call MCSetVolume + popad + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODSetPeriod - Changes the voice period value +; In: +; Voice = Audio channel +; Period = Amiga Period +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODSetPeriod AVoice:dword,APeriod:dword + pushad + mov ebx,[AVoice] + mov ecx,[APeriod] + call MCSetFrequency + popad + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODSetVolume - Set the voice volume +; In: +; Voice = Audio channel +; Volume = New Volume +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +proc MODSetVolume AVoice:dword,AVolume:dword + pushad + mov ebx,[AVoice] + mov eax,[AVolume] + call MCSetVolume + popad + ret +endp + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODSetup - Setup all the MOD player stuff +; In: +; EDI = modulefile address +; Out: +; CF = status +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MODSetup: + pushad ; check for valid modfile formats + mov [ActiveTracks],4 + cmp [edi+MODHeader.Sign],'.K.M' + je MODParse + cmp [edi+MODHeader.Sign],'4TLF' + je MODParse + mov [ActiveTracks],6 + cmp [edi+MODHeader.Sign],'NHC6' + je MODParse + mov [ActiveTracks],8 + cmp [edi+MODHeader.Sign],'NHC8' + je MODParse + stc + jmp MODSetupDone +MODParse: + mov [OrderPos],0FFh ; setup variables + mov [PatternRow],40h + mov [Tempo],6 + mov [TempoCount],0 + mov [BPM],125 + mov al,[edi+MODHeader.OrderLen] + mov [OrderLen],al + + mov ecx,80h ; copy order list + xor ebx,ebx + xor ah,ah +MODCopyOrder: + mov al,[edi+ebx+MODHeader.Order] + mov [ebx+OrderList],al + cmp al,ah + jb MODCopyNext + mov ah,al +MODCopyNext: + inc ebx + loop MODCopyOrder + + movzx ecx,ah ; copy pattern pointers + inc ecx + xor ebx,ebx + movzx eax,[ActiveTracks] + shl eax,8 + mov esi,edi + add esi,size MODHeader +MODCopyPatterns: + mov [4*ebx+PatternList],esi + add esi,eax + inc ebx + loop MODCopyPatterns + + mov ecx,31 ; setup samples data + lea edi,[edi+MODHeader.Samples] + xor ebx,ebx + inc ebx +MODCopySamples: + mov al,[edi+MODSample.Volume] + mov [ebx+SmpVolume],al + movzx eax,[edi+MODSample.SampleLength] + movzx edx,[edi+MODSample.RepLength] + movzx ebp,[edi+MODSample.RepStart] + xchg al,ah + xchg dl,dh + xchg ax,bp + xchg al,ah + xchg ax,bp + add eax,eax + add edx,edx + add ebp,ebp + cmp edx,2 ; if not looped, then the loop + ja MODSetSmpLength ; start and end are the same. + xor edx,edx + mov ebp,eax +MODSetSmpLength: + add edx,ebp + add eax,esi + add edx,esi + add ebp,esi + mov [4*ebx+SmpAddress],esi + mov [4*ebx+SmpEndAddress],edx + mov [4*ebx+SmpStartAddress],ebp + mov esi,eax + add edi,size MODSample + inc ebx + loop MODCopySamples + clc +MODSetupDone: + popad + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODCallBack - Internal callback routine called by the mixer +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MODCallBack: + pushad + dec [TempoCount] ; decrease tempo counter + jle MODNextRow ; next row? + lea esi,[Tracks] + xor ebx,ebx +MODUpdateLoop: + call MODUpNote ; update note events + add esi,size Track + inc ebx + cmp bx,[ActiveTracks] + jb MODUpdateLoop + popad + ret +MODNextRow: + mov al,[Tempo] ; reload tempo counter + mov [TempoCount],al + inc [PatternRow] ; next pattern row + cmp [PatternRow],40h + jb MODDontWrap ; next order? + xor ebx,ebx + mov [PatternRow],bl ; reset the pattern row + mov bl,[OrderPos] + inc bl ; update the order position + cmp bl,[OrderLen] + jb MODSetOrder + xor bl,bl +MODSetOrder: + mov [OrderPos],bl + mov bl,[ebx+OrderList] ; get the pattern from the order list + mov edi,[4*ebx+PatternList] + mov [PatternPtr],edi ; set the pattern address +MODDontWrap: + mov edi,[PatternPtr] ; read the pattern row... + lea esi,[Tracks] + xor ebx,ebx +MODGetNoteLoop: + call MODGetNote + add esi,size Track + add edi,4 + inc ebx + cmp bx,[ActiveTracks] + jb MODGetNoteLoop + mov [PatternPtr],edi + popad + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODGetNote - Read the note event from the pattern sheet +; In: +; EBX = voice number +; ESI = track address +; EDI = pattern data address +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MODGetNote: + mov al,[edi+2] ; get the sample number + shr al,4 + mov ah,[edi] + and ah,0F0h + or al,ah + test al,al ; nonzero value? + je MODGetPeriod + mov [esi+Track.Instr],al ; set new sample instrument + movzx eax,al ; and set the track volume + mov al,[eax+SmpVolume] + mov [esi+Track.Volume],al + call MCSetVolume +MODGetPeriod: + mov ax,[edi] ; get the period Amiga value + xchg al,ah + and ax,0FFFh + test ax,ax + je MODGetEffect ; nonzero value? + mov [esi+Track.Period],ax ; set new period value + movzx ecx,ax + call MCSetFrequency + push esi edi ; set track instrument data + movzx eax,[esi+Track.Instr] + mov edx,[4*eax+SmpAddress] + mov esi,[4*eax+SmpStartAddress] + mov edi,[4*eax+SmpEndAddress] + call MCStartVoice + pop edi esi +MODGetEffect: + mov ax,[edi+2] ; get the effect value + xchg al,ah + and ax,0FFFh + mov [esi+Track.Effect],ax + cmp ah,0Ch ; dispatch effects + je EFXSetVolume + cmp ah,0Fh + je EFXSetSpeed + cmp ah,0Bh + je EFXJumpPos + cmp ah,0Dh + je EFXBreak + cmp ah,09h + je EFXSampleOffset + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MODUpNote - Updates the current track's note event +; In: +; EBX = voice number +; ESI = track address +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MODUpNote: + mov ax,[esi+Track.Effect] ; dispatch the effects + cmp ah,0Ah + je EFXSlideVolume + ret + + +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ +; MOD Player Effects Stuff +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;  Only a small set of effects are implemented. +;  The registers EBX,ESI,EDI must be preserved. +;  The effect parameters are passed through the registers: +; EBX - voice number +; ESI - track address +; AL - effect parameter +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; EFXSetVolume - changes the track volume. +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +EFXSetVolume: + call MCSetVolume +EFXNone: + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; EFXSetSpeed - changes the current tempo or BPM rate. +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +EFXSetSpeed: + test al,al + je EFXNone + cmp al,20h + jae EFXSetBPM + mov [Tempo],al + mov [TempoCount],al + ret +EFXSetBPM: + mov [BPM],al + mov dl,al + call MCStartTimer + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; EFXJumpPos - jump to the spec order position. +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +EFXJumpPos: + dec al + mov [OrderPos],al + mov [PatternRow],40h + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; EFXBreak - breaks the current pattern. +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +EFXBreak: + mov [PatternRow],40h + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; EFXSlideVolume - slides the volume up or down. +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +EFXSlideVolume: + mov ah,al + mov al,[esi+Track.Volume] + test ah,0F0h + je EFXVolumeDown + shr ah,4 + add al,ah + cmp al,40h + jbe EFXVolSlided + mov al,40h +EFXVolSlided: + mov [esi+Track.Volume],al + call MCSetVolume + ret +EFXVolumeDown: + sub al,ah + jge EFXVolSlided + xor al,al + jmp EFXVolSlided + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; EFXSampleOffset - Set the sample offset +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +EFXSampleOffset: + xor edx,edx + mov dh,al + push esi edi + movzx eax,[esi+Track.Instr] + add edx,[4*eax+SmpAddress] + mov esi,[4*eax+SmpStartAddress] + mov edi,[4*eax+SmpEndAddress] + call MCStartVoice + pop edi esi + ret + + +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ +; MultiChannel Mixer Stuff +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCInit - Initializes the multi-channel stuff for playback +; Out: +; CF = status +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCInit: + pushad + xor ax,ax ; initialize variables + mov [BufferPos],ax + mov [TimerCount],ax + mov [TimerSpeed],ax + mov [BufSelector],ax + mov ax,0100h ; DPMI allocate DOS memory block + mov bx,(BUFLEN/16)+(420h) ; (2K=DMA buffer 16K=VolBuffer) + int 31h + jc MCInitDone + mov [BufSelector],dx ; save DOS memory block selector + mov ecx,BUFLEN + movzx edi,ax + shl edi,4 ; get the memory block linear address + mov esi,edi + add esi,ecx + shl ax,4 ; check for cross pages in + neg ax ; the DMA buffer memory area + cmp ax,cx + jae MCAlignVolBuffer + mov esi,edi + add edi,4200h +MCAlignVolBuffer: ; VolBuffer must be page aligned + add esi,0FFh ; (256 bytes = 1 page) + and esi,not 0FFh + mov [VolBuffer],esi ; save the addresses and length + mov [DMABuffer],edi + mov [DMALength],cx + push es ; clear DMA buffer + mov ax,ds + mov es,ax + cld + mov al,80h + rep stosb + pop es + mov edi,[VolBuffer] ; make the volume table + mov cx,[ActiveVoices] + cmp cx,4 + setae cl + xor bx,bx +MCDoVolBuffer: + mov al,bl + imul bh + sar ax,cl + mov [edi],ah + inc edi + inc bl + jne MCDoVolBuffer + inc bh + cmp bh,40h + jbe MCDoVolBuffer + clc +MCInitDone: + popad + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCDone - Shutdown the multi-channel stuff +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCDone: + pushad + mov ax,0101h ; DPMI free DOS memory block + mov dx,[BufSelector] + test dx,dx + je MCDoneExit + int 31h + mov [BufSelector],0 +MCDoneExit: + popad + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCStartVoice - Set the voice sample instrument parameters +; In: +; EBX = voice number +; EDX = start address +; ESI = loop start +; EDI = loop end +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCStartVoice: + push ebx + mov ebx,[4*ebx+VoiceTable] + mov [ebx+Voice.Address],edx + mov [ebx+Voice.StartAddress],esi + mov [ebx+Voice.EndAddress],edi + pop ebx + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCSetFrequency - Set the voice frequency counter +; In: +; EBX = voice number +; ECX = frequency (Amiga value) +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCSetFrequency: + push eax + push ebx + push edx + jecxz MCSetFreqDone + mov ebx,[4*ebx+VoiceTable] + mov eax,[FreqDivisor] + xor edx,edx + div ecx + mov [ebx+Voice.Frequency],eax +MCSetFreqDone: + pop edx + pop ebx + pop eax + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCSetVolume - set the voice volume +; In: +; EBX = voice number +; AL = volume +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCSetVolume: + push ebx + mov ebx,[4*ebx+VoiceTable] + mov [ebx+Voice.Volume],al + pop ebx + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCStartTimer - Set the timer frequency +; In: +; DL = timer frequency in BPMs +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCStartTimer: + push ax + push cx + push dx + mov ch,dl ; set the timer frequency + xor cl,cl ; at 24/60*BPM hertz + mov ax,[SBRate] + mov dx,0280h + mul dx + div cx + mov [TimerSpeed],ax + pop dx + pop cx + pop ax + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCPolls - Polling routine for the multi-channel stuff +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCPolls: + movzx edi,[BufferPos] ; get the buffer address + add edi,[DMABuffer] + movzx ecx,[DMALength] ; get the buffer length + shr ecx,1 + mov [BufferLen],cx + xor [BufferPos],cx + push edi es ; clear the buffer + mov ax,ds + mov es,ax + mov al,80h + cld + rep stosb + pop es edi +MCPollLoop: + cmp [TimerCount],0 ; time to call the MOD callback? + jg MCPollChunk + call MODCallBack + mov ax,[TimerSpeed] ; update timer accumulator + add [TimerCount],ax +MCPollChunk: + mov ax,[BufferLen] ; get the chunk length + mov cx,[TimerCount] + add cx,63 ; round up to 64 bytes to avoid + and cx,not 63 ; small chunks in the mixing + cmp ax,cx + jle MCPollVoices + mov ax,cx +MCPollVoices: + sub [BufferLen],ax ; decrease counters + sub [TimerCount],ax + movzx ecx,ax ; mixes all the voices + lea ebx,[Voices] + mov dx,[ActiveVoices] +MCVoicesLoop: + push ebx + push ecx + push edx + push edi + call MCMixVoice + pop edi + pop edx + pop ecx + pop ebx + add ebx,size Voice + dec dx + jg MCVoicesLoop + add edi,ecx + cmp [BufferLen],0 ; more samples left? + jg MCPollLoop +MCPollDone: + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; MCMixVoice - Mixes the Voice's sample instrument +; In: +; EBX = Voice address +; EDI = Buffer address +; ECX = Number of samples +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +MCMixVoice: + push ebx + +; Modify the mixing chunk of code + + mov eax,[ebx+Voice.EndAddress] + mov [dword ds:MCMixCode0],eax + mov [dword ds:MCMixCode2],eax + sub eax,[ebx+Voice.StartAddress] + mov [dword ds:MCMixCode1],eax + +; Load the sample address and frequency values + + mov esi,[ebx+Voice.Address] + mov ebp,[ebx+4+Voice.Address] + mov eax,[ebx+Voice.Frequency] + xor edx,edx + shld edx,eax,16 + shl eax,16 + +; Load the volume table address + + mov bh,[ebx+Voice.Volume] + and ebx,0FF00h + add ebx,[VolBuffer] + +; Start of the mixing chunk of code (non pure code) + +MCMixLoop: + cmp esi,12345678h +MCMixCode0 = $-4 + jb MCMixCont + sub esi,12345678h +MCMixCode1 = $-4 + cmp esi,12345678h +MCMixCode2 = $-4 + jae MCMixBreak +MCMixCont: + mov bl,[esi] + mov bl,[ebx] + add [edi],bl + add ebp,eax + adc esi,edx + inc edi + dec ecx + jg MCMixLoop +MCMixBreak: + +; Updates the sample current address + + pop ebx + mov [ebx+Voice.Address],esi + mov [ebx+4+Voice.Address],ebp + ret + + +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ +; Sound Blaster 1.5 Stuff +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; SBWrite - write command or data byte to the DSP chip +; In: +; AL = data +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +SBWrite: + push eax + push ecx + push edx + mov dx,[IOAddr] + add dx,0Ch + mov ecx,10000h ; wait until the DSP is ready + mov ah,al ; to receive the data byte +SBWriteWait: + in al,dx + and al,80h + loopnz SBWriteWait + mov al,ah ; write data byte + out dx,al + pop edx + pop ecx + pop eax + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; SBResetDSP - reset the Sound Blaster DSP chip +; Out: +; CF = status +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +SBResetDSP: + pusha + mov dx,[IOAddr] + add dx,06h + mov al,01h ; write 01h to the reset register + out dx,al + in al,dx ; delay + in al,dx + in al,dx + in al,dx + mov al,00h ; write 00h to the reset register + out dx,al + add dx,08h ; wait until data is available + mov ecx,10000h +SBReadWait: + in al,dx + and al,80h + loopz SBReadWait + sub dx,04h + in al,dx ; read the DSP data byte + cmp al,0AAh + clc + je SBResetDone ; check SB DSP signature + stc +SBResetDone: + popa + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; SBOpenDevice - Initialize and start the SB card for playback +; In: +; IOAddr = I/O Port Address +; IRQNum = IRQ Level +; DRQNum = DMA Channel +; SBRate = Sampling Rate +; Out: +; CF = initialization status +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +SBOpenDevice: + pusha + call SBResetDSP ; check for a SB card + jc SBOpenDone + mov al,0D1h + call SBWrite ; turn on SB speaker + mov al,40h + call SBWrite ; set playback sampling rate + mov ax,1000 + mul ax + div [SBRate] + neg ax + call SBWrite + mov al,14h ; set the 8-bit DMA buffer length + call SBWrite ; and start the playback transfer + mov ax,[DMALength] + shr ax,1 + dec ax + call SBWrite + mov al,ah + call SBWrite + clc ; set successful status +SBOpenDone: + popa + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; SBCloseDevice - shutdown the SB playback +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +SBCloseDevice: + pusha + call SBResetDSP ; reset the SB DSP chip + mov al,0D3h + call SBWrite ; turn off the SB speaker + popa + ret + +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ +; DMA Controller Stuff +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; DMAReset - reset the DMA channel +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +DMAReset: + pusha + mov al,[DRQNum] ; reset the DMA channel + or al,04h + out 0Ah,al + popa + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; DMASetBuf = set the DMA channel parameters for playback +; In: +; DMABuffer = DMA Buffer Address +; DMALength = DMA Buffer Length +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +DMASetBuf: + pushad + mov cl,[DRQNum] ; get the DMA channel number + mov al,cl + or al,04h ; reset the DMA channel + out 0Ah,al + out 0Ch,al ; clear DMA flip-flop register + mov al,cl + or al,58h ; set the DMA autoinit mode + out 0Bh,al + movzx dx,cl ; set the DMA buffer address + add dx,dx + mov eax,[DMABuffer] + out dx,al + mov al,ah + out dx,al + inc dx ; set the DMA buffer length + mov ax,[DMALength] + dec ax + out dx,al + mov al,ah + out dx,al + mov edx,82818387h ; set the DMA buffer page + shl cl,3 + shr edx,cl + xor dh,dh + shr cl,3 + shr eax,16 + out dx,al + mov al,cl ; set the DMA channel + out 0Ah,al + popad + ret + +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ +; PIC IRQ Stuff +;°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ°ħ + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; IRQGetIntNum - Returns the PIC interrupt base +; In: +; AL = IRQ level +; Out: +; AL = PIC interrupt number +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +IRQGetIntNum: + push dx + push ax bx cx ; get the virtual master and slave + mov ax,0400h ; PIC base interrupts in DX + int 31h + pop cx bx ax + cmp al,08h ; get the PIC base interrupt for + jb IRQGetDone ; the IRQ number in AL + mov dh,dl + sub al,08h +IRQGetDone: + add al,dh + pop dx + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; IRQRestVect - Restores the previous IRQ vector +; In: +; IRQNum = IRQ level +; IRQVect = IRQ Vector +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +IRQRestVect: + pushad + push ds + in al,0A1h ; disable the IRQ interrupt using + mov ah,al ; the PIC interrupt mask registers + in al,21h + mov dx,01h + mov cl,[IRQNum] + shl dx,cl + or ax,dx + out 21h,al + mov al,ah + out 0A1h,al + mov ah,25h ; restore the IRQ vector using + mov al,cl ; the DOS/4GW INT 21h stuff + call IRQGetIntNum + lds edx,[IRQVector] + xor ebx,ebx + mov bx,ds + or ebx,edx + test ebx,ebx + je IRQRestDone + int 21h +IRQRestDone: + pop ds + xor ebx,ebx + mov [dword IRQVector],ebx + mov [word 4+IRQVector],bx + popad + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; IRQSetVect - Set the new IRQ driver handler +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +IRQSetVect: + pushad + push ds es + mov ah,35h ; get and saves the IRQ vector + mov al,[IRQNum] ; using the DOS/4GW INT 21h stuff + call IRQGetIntNum + int 21h + mov [dword IRQVector],ebx + mov [word IRQVector+4],es + mov ah,25h ; set the new IRQ vector using + mov al,[IRQNum] ; the DOS/4GW INT 21h stuff + call IRQGetIntNum + lea edx,[IRQHandler] + push cs + pop ds + int 21h + pop es ds + in al,0A1h ; enable this IRQ using the + mov al,ah ; PIC interrupt mask registers + in al,21h + mov dx,01h + mov cl,[IRQNum] + shl dx,cl + not dx + and ax,dx + out 21h,al + mov al,ah + out 0A1h,al + popad + ret + +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +; IRQHandler - Hardware IRQ handler +;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°° +IRQHandler: + pushad ; pushes all the registers + push ds +;;; mov ax,DGROUP ; load the DS selector +;;; mov ds,ax +;;; call _GETDS + mov ax,cs:[dword ptr OurDataSeg] + mov ds,ax + mov dx,[IOAddr] ; send ack to the SB DSP chip + add dx,0Eh + in al,dx + mov al,14h ; restart DMA transfer + call SBWrite + mov ax,[DMALength] + shr ax,1 + dec ax + call SBWrite + mov al,ah + call SBWrite + call MCPolls ; fill the samples buffer + mov al,20h + cmp [IRQNum],08h ; send ack to 8259 PIC controllers + jb IRQAckPIC + out 0A0h,al +IRQAckPIC: + out 20h,al + pop ds + popad ; restores all the registers + iretd + +end + \ No newline at end of file diff --git a/fdemo/MODPLAY.H b/fdemo/MODPLAY.H new file mode 100644 index 0000000..73f5ae6 --- /dev/null +++ b/fdemo/MODPLAY.H @@ -0,0 +1,162 @@ +/* modplay.h - Tiny MOD Player V2.02 for Watcom C/C++32 and DOS/4GW header file + + Module player for Sound Blaster and compatibles. + + Copyright 1993,94 Carlos Hasan +*/ + +/******************************************************************************\ + QUICK REFERENCE GUIDE: + + MODPlayModule + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Start playing a Modulefile. + + Prototype: int MODPlayModule(Song,Chans,Rate,Port,IRQ,DRQ) + + Parameters: Song - Address of the Modulefile Image + Chans - Number of channels (1-8) + Rate - Mixing speed in Hertz (4kHz-22kHz) + Port - I/O Port address (220h..280h) + IRQ - IRQ level (2,3,5,7,11) + DRQ - DMA channel (0,1,3) + + Returns: On success, return a zero value. + On error, return a non zero value. + + Remarks: This function will initialize the Sound Blaster card and + will start playing the module file immediately. The module + file must be a 4,6 or 8 channels Protracker or FastTracker + music module file. + The player supports two kinds of channels, music and sample + audio channels. The music channels are used by the player + to play the module, and the sample channels are used for + sound effects and the like. + The channels voices 0 to N-1 are used for music channels, + where N is the number of channels of the module file. + + + MODStopModule + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Stop playing the current Modulefile. + + Prototype: void MODStopModule() + + Parameters: None. + + Returns: None. + + Remarks: This function shut down the playing system. Must be called + before exiting the user program. + + + MODPlaySample + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Play instrument at specified period and volume. + + Prototype: void MODPlaySample(Voice,Instr) + + Parameters: Voice - Audio channel number (0-7) + Instr - Instrument address + + Returns: None. + + Remarks: This function is useful to play samples over music. The sample + structure holds the period, volume and the address of the 8-bit + signed samples to be played in the channel. + + + MODPlayVoice + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Set the sample, period and volume for a sample channel. + + Prototype: void MODPlayVoice(Voice,Instr,Period,Volume) + + Parameters: Voice - Audio channel number (0-7) + Instr - Instrument number (1-31) + Period - Amiga period value (113-856) + Volume - Volume value (0-64) + + Returns: None. + + Remarks: This function is useful to play samples over music. You must + use a sample channel or the music system will override + these parameters when polling the current module. + The amiga period value can be translated to hertz using the + following formula: Hertz = 8363*428/Period + + + MODStopVoice + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Stop a sample channel. + + Prototype: void MODStopVoice(Voice) + + Parameters: Voice - Audio channel number (0-7) + + Remarks: This function will stop the specified voice setting the channel + volume to zero. The voice should be a sample channel. + + + MODSetPeriod + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Set the sample channel period value. + + Prototype: void MODSetPeriod(Voice,Period) + + Parameters: Voice - Audio channel number (0-7) + Period - Amiga Period (113-856) + + Returns: None. + + Remarks: This function will change the current frequency of the sample + channel. The voice should be a sample channel. + + + MODSetVolume + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + Function: Set the sample channel volume. + + Prototype: void MODSetVolume(Voice,Volume) + + Parameters: Voice - Audio channel number (0-7) + Volume - Volume (0-64) + + Returns: None. + + Remarks: This function will change the channel volume. The voice should + be a sample channel. +\******************************************************************************/ + +#ifndef __MODPLAY_H +#define __MODPLAY_H + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +typedef struct { + word Period; + word Volume; + dword Length; + void *Data; +} Sample; + +#pragma aux MODPlayModule "_*" parm caller []; +#pragma aux MODStopModule "_*" parm caller []; +#pragma aux MODPlaySample "_*" parm caller []; +#pragma aux MODPlayVoice "_*" parm caller []; +#pragma aux MODStopVoice "_*" parm caller []; +#pragma aux MODSetPeriod "_*" parm caller []; +#pragma aux MODSetVolume "_*" parm caller []; + +extern int MODPlayModule(void *Modulefile,word Chans,word Rate,word Port,byte IRQ,byte DRQ); +extern void MODStopModule(void); +extern void MODPlaySample(byte Voice,Sample *Instr); +extern void MODPlayVoice(byte Voice,byte Instr,word Period,byte Volume); +extern void MODStopVoice(byte Voice); +extern void MODSetPeriod(byte Voice,word Period); +extern void MODSetVolume(byte Voice,byte Volume); + +#endif + diff --git a/fdemo/MOUSE.C b/fdemo/MOUSE.C new file mode 100644 index 0000000..fc31ab1 --- /dev/null +++ b/fdemo/MOUSE.C @@ -0,0 +1,181 @@ +// 3D Construction Kit +// Started: 01/02/94 +// Author: Lary Myers +// Module: MOUSE.C +// (c) CopyRight 1994 All Rights Reserved + +#include +#include +#include + + +void Mymemset(void *,int,int); + + int MouseModifier = 2; + +//============================================================================= +// Check if mouse is installed, returns -1 if it IS installed +//============================================================================= +int MouseInstalled(void) +{ + int yesno; + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +intr(0x33,®s); +yesno = regs.w.ax; + +return(yesno); +} + +//============================================================================= +// Show the mouse cursor +//============================================================================= +void ShowMouse(void) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 1; +intr(0x33,®s); + +} + +//============================================================================= +// Hide the mouse cursor +//============================================================================= +void HideMouse(void) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 2; +intr(0x33,®s); + +} + +//============================================================================= +// Returns button status, mouse row and column +//============================================================================= +int ReadMouseCursor(int *mrow,int *mcol) +{ + int bstatus; + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 3; +intr(0x33,®s); +bstatus = regs.w.bx; +*mrow = regs.w.dx; +*mcol = regs.w.cx / MouseModifier; + +return(bstatus); +} + +//============================================================================= +// Returns just the mouse button status +//============================================================================= +int ReadMouseButtons(void) +{ + int bstatus; + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 3; +intr(0x33,®s); +bstatus = regs.w.bx; + +return(bstatus); +} + +//============================================================================= +// Set mouse cursor to desired row and column +//============================================================================= +void SetMouseCursor(int mrow,int mcol) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 4; +regs.w.dx = mrow; +regs.w.cx = mcol * MouseModifier; +intr(0x33,®s); + +} + +//============================================================================= +// Defines left and right columns for mouse travel +//============================================================================= +void SetMouseMinMaxColumns(int mincol,int maxcol) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 7; +regs.w.cx = mincol * MouseModifier; +regs.w.dx = maxcol * MouseModifier; +intr(0x33,®s); + +} + + +//============================================================================= +// Defines top and bottom rows for mouse travel +//============================================================================= +void SetMouseMinMaxRows(int minrow,int maxrow) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 8; +regs.w.cx = minrow; +regs.w.dx = maxrow; +intr(0x33,®s); + +} + +//============================================================================= +// Set shape of mouse cursor. 8 byte mask, hotspot row,col +//============================================================================= +void SetMouseShape(int hsrow,int hscol,char far *mask) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 9; +regs.w.dx = FP_OFF(mask); +regs.w.es = FP_SEG(mask); +regs.w.bx = hscol; +regs.w.cx = hsrow; +intr(0x33,®s); + +} + +//============================================================================= +// Wait for the mouse button to be released +//============================================================================= +void MouseReleased(void) +{ + +while (ReadMouseButtons()); + +} + + +//============================================================================= +// +//============================================================================= +void MouseSetFunction(unsigned char mFlag,void *Func) +{ + union REGPACK regs; + +Mymemset(®s,0,sizeof(union REGPACK)); +regs.w.ax = 12; +regs.w.dx = FP_OFF(Func); +regs.w.es = FP_SEG(Func); +regs.w.cx = mFlag; +intr(0x33,®s); + +} + + \ No newline at end of file diff --git a/fdemo/PICS.DTF b/fdemo/PICS.DTF new file mode 100644 index 0000000000000000000000000000000000000000..21fc1a6679eaa1626979c59be313cdce4059c772 GIT binary patch literal 1250885 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