
/* Clean Game Library by Mike Wiering, Nijmegen */

#include "util_12.h"
#include "intrface_12.h"

#include "cGameLib_12.h"

#include "cOSGameLib_12.h"  /* OS specific functions */


// #define SMART_DRAW

/* defined in cCrossCall_12.h and cCrossCall_12.c */
extern CrossCallInfo gCci;
extern char *gAppName;
extern HWND ghMainWindow;
extern HINSTANCE ghInst;

extern void SendMessageToClean (int mess,
				         int p1,int p2,int p3, int p4,int p5,int p6);

#define SendMessage0ToClean(mess)                   SendMessageToClean((mess), 0,0,0,0,0,0)
#define SendMessage1ToClean(mess,p1)                SendMessageToClean((mess), (int)(p1),0,0,0,0,0)
#define SendMessage2ToClean(mess,p1,p2)             SendMessageToClean((mess), (int)(p1),(int)(p2),0,0,0,0)
#define SendMessage3ToClean(mess,p1,p2,p3)          SendMessageToClean((mess), (int)(p1),(int)(p2),(int)(p3),0,0,0)
#define SendMessage4ToClean(mess,p1,p2,p3,p4)       SendMessageToClean((mess), (int)(p1),(int)(p2),(int)(p3),(int)(p4),0,0)
#define SendMessage5ToClean(mess,p1,p2,p3,p4,p5)    SendMessageToClean((mess), (int)(p1),(int)(p2),(int)(p3),(int)(p4),(int)(p5),0)
#define SendMessage6ToClean(mess,p1,p2,p3,p4,p5,p6) SendMessageToClean((mess), (int)(p1),(int)(p2),(int)(p3),(int)(p4),(int)(p5),(int)(p6))



typedef struct SPRITEANIMATION
{
	int iSpriteID;
    int iBitmapID;
    int iSequenceLength;
    char *sSequence;
    int iPosition;
    int iCounter;
    struct SPRITEANIMATION *saNext;
    BOOL bLoop;
} SPRITEANIMATION;

typedef struct OBJECTREC
{
    int iObjectID;
    int iMapX;
    int iMapY;
    int iObjType;
    int iSubType;
	BOOL bActive;
    int iXPos;
    int iYPos;
    int iXSize;
    int iYSize;
    int iXOffset;
    int iYOffset;
    int iSpriteID;
    int iSpriteIndex;
    int iDisplayOptions;
    int iOwnBounds;
    int iBounceBounds;
    int iCollideBounds;
    int iForgetX;
    int iForgetY;
    int iLastXPos;
    int iLastYPos;
    int iTimeCounter;
    int iLayer;
    int iXAcc;
    int iYAcc;
    int iXSpeed;
    int iYSpeed;
    int iXBounce;
    int iYBounce;
    int iMaxXSpeed;
    int iMaxYSpeed;
    int iXSlowDown;
    int iYSlowDown;
    int iSkipMove;
    int iOptions;

    int iFixedXPos;
    int iFixedYPos;
    int iLastFixedXPos;
    int iLastFixedYPos;
    int iLastSprite;
    int iPosition;
    int iCounter;
    struct OBJECTREC *objNext;
} OBJECTREC;


typedef struct GAMELAYERMAPINFO
{
	int iMapID;
    int iBitmapID;
    int iMapWidth;
    int iMapHeight;
    char *sMap;
    int iTotalXSize;
    int iTotalYSize;
    BOOL bTile;
    struct GAMELAYERMAPINFO *glmipNext;
} GAMELAYERMAPINFO;


extern BOOL bGameActive;
extern HWND ghGameWindow;
extern int ScreenWidth;
extern int ScreenHeight;
extern int BitsPerPixel;
extern BOOL FullScreen;

int iFrameCounter;

static GAMELAYERMAPINFO *glmipGameLayerMapInfo = NULL;
static SPRITEANIMATION *saSprites = NULL;
static OBJECTREC *objObjects = NULL;

static int XView = 0;
static int YView = 0;
static int XScrollSpeed = 0;
static int YScrollSpeed = 0;

static int iNextLayer = 0;

/* bound map */
static char *sBoundMap = NULL;
static int BoundMapWidth = 0;
static int BoundMapHeight = 0;
static int BoundBlockWidth = 0;
static int BoundBlockHeight = 0;
static int TotalWidth = 0;
static int TotalHeight = 0;

#ifdef SMART_DRAW
/* smart draw data (only draw changed tiles) */
static char *sLastTile = NULL;
static int iLastTileW;
static int iLastTileH;
static int iTopLayerLastX;
static int iTopLayerLastY;
static int bSmartDraw;
static int bAllowSmartDraw;
#endif

/* min. value for objects */
static int iObjectValueStart;  

/* smallest distance to visible screen before initializing object */
static int iStartObjX = 5;
static int iStartObjY = 5;

/* ID of the object that is performing a crosscall */
static int iCCObjectID = 0;

static BOOL bCheckEscape = TRUE;
static BOOL DebugScroll = FALSE;
static BOOL bFillBackground = TRUE;
static COLORREF FillBackgroundRGB = 0;
static BOOL bFadeIn = FALSE;
static BOOL bFadeOut = FALSE;

static char FullScreenGameWindowClassName[] = "__CleanFullScreenGameWindow";
static char GameWindowClassName[] = "__CleanGameWindow";


/* focusing the main character */
static int iFollowID = 0;
static int iFollowX1 = 0;
static int iFollowY1 = 0;
static int iFollowX2 = 0;
static int iFollowY2 = 0;

static int iMaxXScrollSpeed = 1;
static int iMaxYScrollSpeed = 1;
static int iActualXView = 0;
static int iActualYView = 0;

/* info for user defined events and scheduled */
typedef struct USER_EVENT_INFO
{
    int iEventID;
    int iEventParameter1;
    int iEventParameter2;
    int iEventParameter3;
    int iEventParameter4;
    int iDestination;
    int iSubDestination;
	int iTimeCounter;
    struct USER_EVENT_INFO *ueiNext;
} USER_EVENT_INFO;

static USER_EVENT_INFO *ueiUserEventInfo = NULL;

#define EV_PLAY_SOUND  (-1)


ClearUserEvents ()
{
    struct USER_EVENT_INFO *ueiNext;

    while (ueiUserEventInfo)
    {
        ueiNext = ueiUserEventInfo->ueiNext;
        rfree (ueiUserEventInfo);
        ueiUserEventInfo = ueiNext;
    }
}


int NextFrame (int);


/* use one integer to save info for both pages */
static int setflipint (int x, int value)
{
    if ((iFrameCounter % 2) == 0)
        return ((x & 0xFFFF0000) + (value & 0xFFFF));
    else
        return ((x & 0xFFFF) + (value << 16));
}

static int getflipint (int value)
{
    if ((iFrameCounter % 2) == 0)
        return (value & 0xFFFF);
    else
        return (value >> 16);
}

static BOOL cmpflipint (int x, int value)
{
    if ((iFrameCounter % 2) == 0)
        return ((x & 0xFFFF0000) == (value << 16));
    else
        return ((x & 0xFFFF) == (value & 0xFFFF));
}



/*
	 3 / 2 =  1
	 2 / 2 =  1
     1 / 2 =  0
     0 / 2 =  0
    -1 / 2 =  0	  <-- fill up this gap
    -2 / 2 = -1
    -3 / 2 = -1
*/

int intdiv (int x, int y)
{
 	return (x < 0)? (x - y + 1) / y : x / y;
}

int Min (int x, int y)
{
 	return (x < y)? x : y;
}

int Max (int x, int y)
{
  	return (x > y)? x : y;
}


/* get a pointer to the SPRITEANIMATION structure with id ID */
SPRITEANIMATION *GetSpriteAnimation (int ID)
{
    SPRITEANIMATION *sa = saSprites;
    BOOL bFound = FALSE;

	while (sa && (!bFound))
    {
     	if (sa->iSpriteID == ID)
          	bFound = TRUE;
        else
          	sa = sa->saNext;
    }
    if (bFound)
		return sa;
    else
        return NULL;
}

/* free the SPRITEANIMATION list */
void FreeSpriteAnimationList ()
{
    SPRITEANIMATION *sa;

	while (saSprites)
    {
       	sa = saSprites->saNext;
	    if (saSprites->sSequence)
	    {
	        rfree (saSprites->sSequence);
	        saSprites->sSequence = NULL;
	    }
        rfree (saSprites);
        saSprites = sa;
    }
}

/* get a pointer to the GAMELAYERMAPINFO structure with id MID */
GAMELAYERMAPINFO *GetGameLayerMapInfo (int MapID)
{
    GAMELAYERMAPINFO *glmip = glmipGameLayerMapInfo;
    BOOL bFound = FALSE;

	while (glmip && (!bFound))
    {
     	if (glmip->iMapID == MapID)
          	bFound = TRUE;
        else
          	glmip = glmip->glmipNext;
    }
	return glmip;
}

/* free the GAMELAYERMAPINFO list and all the map strings */
void FreeGameLayerMapInfoList ()
{
    GAMELAYERMAPINFO *glmip;

	while (glmipGameLayerMapInfo)
    {
       	glmip = glmipGameLayerMapInfo->glmipNext;
        if (glmipGameLayerMapInfo->sMap != NULL)
        {
            rfree (glmipGameLayerMapInfo->sMap);
            glmipGameLayerMapInfo->sMap = NULL;
        }
        rfree (glmipGameLayerMapInfo);
        glmipGameLayerMapInfo = glmip;
    }
}


/* get a pointer to the OBJECTREC structure with id ID */
OBJECTREC *GetObjectRec (int ID)
{
    OBJECTREC *obj = objObjects;
    BOOL bFound = FALSE;

	while (obj && (!bFound))
    {
     	if (obj->iObjectID == ID)
          	bFound = TRUE;
        else
          	obj = obj->objNext;
    }
	return obj;
}


/* initialize a new game object */
void InitGameObject (int mapx, int mapy)
{
    OBJECTREC *objNew;
    OBJECTREC *obj1;
    OBJECTREC *obj2;
    int mappos;
    int origmapval, mapval, above;
    int newid;

    if ((mapx < 0) || (mapy < 0))
      	return;
    if ((mapx > BoundMapWidth - 1) || (mapy > BoundMapHeight - 1))
        return;
    mappos = (mapy * BoundMapWidth + mapx) * sizeof (int);
    origmapval = (*(int *) &sBoundMap[mappos]);
    mapval = origmapval >> 8;
    if (mapval < iObjectValueStart)
      	return;

    (*(int *) &sBoundMap[mappos]) = origmapval & 0x00FF;  // remove object

    // create a unique id number
    newid = 1;
	while (GetObjectRec (newid))
        newid++;

    objNew = rmalloc (sizeof (OBJECTREC));
    objNew->objNext = NULL;
    objNew->iMapX = mapx;
    objNew->iMapY = mapy;
  /*
    objNew->iObjType = mapval;
    objNew->iSubType = above >> 8;
  */
    if (mapy == 0)
        above = 0;
    else
    {
        origmapval = (*(int *) &sBoundMap[mappos - BoundMapWidth * sizeof (int)]);
        above = origmapval >> 8;
    }

    if (above >= iObjectValueStart)
        above = 0;

    if (above > 0)
        (*(int *) &sBoundMap[mappos - BoundMapWidth * sizeof (int)]) =
            origmapval & 0x00FF;  // remove subcode

    objNew->iObjectID = newid;
    objNew->bActive = FALSE;

    objNew->iPosition = 0;
    objNew->iCounter = 0;

    obj1 = objObjects;
    obj2 = NULL;

    while (obj1)
    {
     	obj2 = obj1;
        obj1 = obj1->objNext;
    }

    if (obj2)
    {
      	obj2->objNext = objNew;
    }
    else
    {
        objObjects = objNew;
    }
    
        
    // inform Clean that new object has been created
    iCCObjectID = newid;
    SendMessage6ToClean (CcWmINITOBJECT, mapval, above, newid,
                           mapx * BoundBlockWidth,
                           mapy * BoundBlockHeight, iFrameCounter);
    iCCObjectID = 0;

    UpdatePosition (objNew);
}

/* update last position if pos has been changed during an event */
UpdatePosition (OBJECTREC *obj)
{
    obj->iLastXPos = obj->iXPos;
    obj->iLastYPos = obj->iYPos;

  	obj->iFixedXPos = obj->iXPos << 8;
  	obj->iFixedYPos = obj->iYPos << 8;
    obj->iLastFixedXPos = obj->iFixedXPos;
    obj->iLastFixedYPos = obj->iFixedYPos;
}

/* remove game object with id ID and all objects that are not active (id==0) */
void GameObjectDone (int id)
{
    OBJECTREC *objCur = objObjects;
    OBJECTREC *objNext = NULL;
    OBJECTREC *objPrev = NULL;
    int mappos, mapval;

    while (objCur)
    {
        if ((id != -1) &&
            (((id > 0) && (objCur->iObjectID != id)) ||
             ((id == 0) && (objCur->bActive))
            )
           )
        {
         	/* skip this object */
         	objPrev = objCur;
            objNext = objCur->objNext;
        }
        else
        {
         	/* remove this object */

         	if (objCur->iObjectID == iFollowID)
              	iFollowID = 0;

	        /* link previous node to next node */
			if (objPrev)
              	objPrev->objNext = objCur->objNext;
            else
                objObjects = objCur->objNext;

            objNext = objCur->objNext;

            /* call object's done function */

            if (id != -1)
            {
	            iCCObjectID = objCur->iObjectID;
			    SendMessage2ToClean (CcWmOBJECTDONE, objCur->iObjType, objCur->iObjectID);
	            iCCObjectID = 0;

              /*  UpdatePosition (objCur);  */
            }
         /*
            objCur->iObjType = gCci.p1;
            objCur->iSubType = gCci.p2;
         */
            /* put ObjType and SubType back into map if >= 0 */
            if (!(objCur->iOptions & OO_REMOVE_MAP_CODE))
	            if ((objCur->iMapX >= 0) && (objCur->iMapY >= 0))
		            if (objCur->iObjType >= 0)
		            {
			            mappos = (objCur->iMapY * BoundMapWidth + objCur->iMapX) * sizeof (int);
						mapval = (*(int *) &sBoundMap[mappos]);
			            (*(int *) &sBoundMap[mappos]) = mapval | (objCur->iObjType << 8);
	
			            if (objCur->iSubType >= 0)
			            {
				            mappos = ((objCur->iMapY - 1) * BoundMapWidth + objCur->iMapX) * sizeof (int);
							mapval = (*(int *) &sBoundMap[mappos]);
				            (*(int *) &sBoundMap[mappos]) = mapval | (objCur->iSubType << 8);
			            }
		            }

            /* free the current node */
        	rfree (objCur);
        }
        objCur = objNext;
    }
}


void InitGameGlobals ()
{
    ghGameWindow = NULL;
    bGameActive = FALSE;
    iFrameCounter = 0;
}



/* GameTranslateKey translates WM_KEYDOWN wPara fields to GameKey values */
static int
GameTranslateKey (WPARAM wPara)
{
	switch (wPara)
	{
        case VK_RETURN:      return GK_RETURN;    break;
		case VK_ESCAPE:      return GK_ESCAPE;    break;
		case VK_UP:          return GK_UP;        break;
        case VK_DOWN:        return GK_DOWN;      break;
		case VK_LEFT:        return GK_LEFT;      break;
		case VK_RIGHT:       return GK_RIGHT;     break;
        case VK_HOME:        return GK_HOME;      break;
        case VK_END:         return GK_END;       break;
//        case VK_PAGE_UP:     return GK_PAGE_UP;   break;
//        case VK_PAGE_DOWN:   return GK_PAGE_DOWN; break;
        case VK_F1:          return GK_F1;        break;
        case VK_F2:          return GK_F2;        break;
        case VK_F3:          return GK_F3;        break;
        case VK_F4:          return GK_F4;        break;
        case VK_F5:          return GK_F5;        break;
        case VK_F6:          return GK_F6;        break;
        case VK_F7:          return GK_F7;        break;
        case VK_F8:          return GK_F8;        break;
        case VK_F9:          return GK_F9;        break;
        case VK_F10:         return GK_F10;       break;
        case VK_F11:         return GK_F11;       break;
        case VK_F12:         return GK_F12;       break;
        case VK_SPACE:       return GK_SPACE;     break;
      	default:
        {
             if ((wPara > 32) && (wPara <= 127))
                 return wPara;
             else
                 return GK_UNKNOWN;
             break;
        }
	}
}



/* buffer which holds the last state of each key */
static BOOL gk_state[GK_MAX_KEY + 1] =
	{FALSE};


//   MessageBox (NULL, "", NULL, MB_OK);


/* GameWindowProcedure is the Windows callback routine for a game window. */
static LRESULT CALLBACK
GameWindowProcedure (HWND hWin, UINT uMess, WPARAM wPara, LPARAM lPara)
{
	printMessage ("Game Window", hWin, uMess, wPara, lPara);
	switch (uMess)
	{
        case WM_CLOSE:
            bGameActive = FALSE;
            return 0;
            break;

        case WM_DESTROY:
            bGameActive = FALSE;
            ClearUserEvents ();
            GameObjectDone (-1);
		    FreeGameLayerMapInfoList ();
            FreeSpriteAnimationList ();
   		    OSFreeGameBitmaps ();
            OSStopMusic ();
            OSDeInitSound ();
            OSDeInitGameWindow ();
            ShowCursor (TRUE);
            return 0;
            break;
        case MCI_NOTIFY:
	        {
                // MessageBeep (MB_ICONASTERISK);
    	        if (wPara == MCI_NOTIFY_SUCCESSFUL)
                    OSPlayMusic (NULL, TRUE);  /* restart music */
    	    }
        	break;
        case WM_PAINT:
            if (!FullScreen)
            {
		    	PAINTSTRUCT ps;
      			HDC hdc = BeginPaint (hWin, &ps);

                NextFrame (0);
      			EndPaint (hWin, &ps);
            }
           	break;
        case WM_KEYUP:
            {
				int keycode = GameTranslateKey (wPara);

                if (gk_state[keycode])
                {
                    OBJECTREC *obj = objObjects;
                    while (obj)
                    {
                     	if ((obj->bActive) && (obj->iOptions & OO_CHECK_KEYBOARD))
                        {
                            int ix = obj->iXPos;
                            int iy = obj->iYPos;

                            iCCObjectID = obj->iObjectID;
    						SendMessage3ToClean (CcWmOBJECTKEYUP, obj->iObjType, obj->iObjectID, keycode);
                            iCCObjectID = 0;

                            if ((ix != obj->iXPos) || (iy != obj->iYPos))
                                UpdatePosition (obj);
                        }
                    	obj = obj->objNext;
                    }
	                gk_state[keycode] = FALSE;
                }
            }
            break;
        case WM_KEYDOWN:
			{
				int keycode = GameTranslateKey (wPara);
                if (keycode == GK_ESCAPE)
                    if (bCheckEscape)
                        bGameActive = FALSE;

				switch (keycode)
				{
                    case GK_UNKNOWN:
						return DefWindowProc (hWin, uMess, wPara, lPara);
                    	break;
      				default:
						{
                            BOOL found = FALSE;
                            OBJECTREC *obj = objObjects;

                            while (obj)
                            {
                             	if ( (obj->bActive) &&
                                     (obj->iOptions & OO_CHECK_KEYBOARD) &&
                                     ( (!gk_state[keycode]) ||
                                       (obj->iOptions & OO_ALLOW_KEYBOARD_REPEAT)
                                     )
                                   )
                                {
                                    int ix = obj->iXPos;
                                    int iy = obj->iYPos;

		                            iCCObjectID = obj->iObjectID;
								    SendMessage3ToClean (CcWmOBJECTKEYDOWN, obj->iObjType, obj->iObjectID, keycode);
		                            iCCObjectID = 0;

                                    if ((ix != obj->iXPos) || (iy != obj->iYPos))
                                        UpdatePosition (obj);

                                    found = TRUE;
                                }
                            	obj = obj->objNext;
                            }

                            gk_state[keycode] = TRUE;
						}

                       	// debugging level, just move around
                    	if (DebugScroll == TRUE)
                        {
							SendMessage3ToClean (CcWmGAMEKEYBOARD, keycode, XScrollSpeed, YScrollSpeed);
							XScrollSpeed = gCci.p1;
							YScrollSpeed = gCci.p2;
                            iMaxXScrollSpeed = 8;
                            iMaxYScrollSpeed = 8;
                        }
				}
			}
            break;
		default:
			return DefWindowProc (hWin, uMess, wPara, lPara);
			break;
	}
	return 0;
}


/* move XView and YView towards iActualXView and iActualYView */
void CorrectView ()
{
    if (iActualXView > XView)
      	XView = Min (XView + iMaxXScrollSpeed, iActualXView);
    if (iActualYView > YView)
      	YView = Min (YView + iMaxYScrollSpeed, iActualYView);
    if (iActualXView < XView)
      	XView = Max (XView - iMaxXScrollSpeed, iActualXView);
    if (iActualYView < YView)
     	YView = Max (YView - iMaxYScrollSpeed, iActualYView);

	if (XView > TotalWidth - ScreenWidth)
	{
	    XView = TotalWidth - ScreenWidth;
        iActualXView = XView;
	    XScrollSpeed = 0;
	}
	if (YView > TotalHeight - ScreenHeight)
	{
	    YView = TotalHeight - ScreenHeight;
        iActualYView = YView;
	    YScrollSpeed = 0;
	}

	if (XView < 0)
	{
	    XView = 0;
        iActualXView = XView;
	    XScrollSpeed = 0;
	}
	if (YView < 0)
	{
	    YView = 0;
        iActualYView = YView;
	    YScrollSpeed = 0;
	}
}


void RunGame ()
{
    MSG msg;
    int i, j, x, y, w, h;
    int lastx, lasty;
    int fade;

    int result;
    int oos;

    iFrameCounter = 0;
    CorrectView ();

    x = XView / BoundBlockWidth;
    y = YView / BoundBlockHeight;
    w = ScreenWidth / BoundBlockWidth;
    h = ScreenHeight / BoundBlockHeight;
    for (i = -iStartObjX - 1; i < w + iStartObjX + 2; i++)
        for (j = -iStartObjY - 1; j < h + iStartObjY + 2; j++)
		    InitGameObject (x + i, y + j);

    lastx = x;
    lasty = y;

#ifdef SMART_DRAW
    bSmartDraw = 0;
    bAllowSmartDraw = 0;
#endif

    /* initialize autoinit object */
    CreateGameObject (0, 0, -1, -1, &result);

    fade = (bFadeIn)? -1 : 0;
    bGameActive = TRUE;
    while (bGameActive)
    {
        if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
        {
        	TranslateMessage (&msg);
        	DispatchMessage (&msg);
        }

        if (iFollowID == 0)
        {
            iActualXView += XScrollSpeed;
	        iActualYView += YScrollSpeed;
        }

        CorrectView ();

	    x = XView / BoundBlockWidth;
	    y = YView / BoundBlockHeight;
        if (y != lasty)
		    for (i = -iStartObjX - 1; i < w + iStartObjX + 2; i++)
            {
                j = -iStartObjY;
                if (y < lasty)
                    InitGameObject (x + i, y + j);
                j = h + iStartObjY + 1;
                if (y > lasty)
                    InitGameObject (x + i, y + j);
             }
        if (x != lastx)
	        for (j = -iStartObjY - 1; j < h + iStartObjY + 2; j++)
	        {
	            i = -iStartObjX;
                if (x < lastx)
				    InitGameObject (x + i, y + j);
	            i = w + iStartObjX + 1;
                if (x > lastx)
				    InitGameObject (x + i, y + j);
	        }
        lastx = x;
        lasty = y;

	    NextFrame (fade);
        fade = 0;
	    iFrameCounter++;

		SendMessage0ToClean (CcWmCHECKQUIT);
        if ((BOOL) gCci.p1)
           	bGameActive = FALSE;

    }

    if (bFadeOut)
    {
        fade = 1;
        NextFrame (fade);
    }
	bGameActive = FALSE;

    ClearUserEvents ();
	GameObjectDone (-1);

    iFollowID = 0;

	//  MessageBeep (MB_ICONASTERISK);
}


/* play (actually schedule) a sound sample */
void PlaySoundSample (int id, int vol, int pan, int freq, int delay)
{
    ScheduleEvent (EV_PLAY_SOUND, id, vol, pan, freq, 0, -1, delay);
}

/* schedule any user event */
void ScheduleUserGameEvent (int event, int par1, int par2,
                            int target, int subtarget, int time)
{
    struct USER_EVENT_INFO *uei;

    if (event >= 0)
    {
        uei = rmalloc (sizeof (USER_EVENT_INFO));
        uei->iEventID = event;
        uei->iEventParameter1 = par1;
        uei->iEventParameter2 = par2;
        uei->iEventParameter3 = 0;
        uei->iEventParameter4 = 0;
        if (target == 0)
            uei->iDestination = -iCCObjectID;  /* only target current object */
        else
            if (target == -1)
                uei->iDestination = 0;  /* all objects */
            else
                uei->iDestination = target;  /* bounds */
        uei->iSubDestination = subtarget;
        uei->iTimeCounter = time;
        uei->ueiNext = ueiUserEventInfo;
        ueiUserEventInfo = uei;
    }
}


/* create the game window */
int CreateGameWindow (int w, int h, int bpp, BOOL fs)
{
    ScreenWidth = w;
    ScreenHeight = h;
    BitsPerPixel = bpp;
    FullScreen = fs;

    if (FullScreen)
        ghGameWindow =
            CreateWindowEx (WS_EX_TOPMOST,       /* extended style */
                            (LPCTSTR) FullScreenGameWindowClassName,  /* class */
                            (LPCTSTR) gAppName,  /* title */
                            WS_POPUP,            /* window style */
                            0, 0, ScreenWidth, ScreenHeight,
                            ghMainWindow,        /* ??? parent */
                            NULL,                /* menu */
                            (HANDLE) ghInst,
                            NULL                 /* window creation data */
                           );
    else
        ghGameWindow =
  			CreateWindowEx (WS_OVERLAPPED,
                            (LPCTSTR) GameWindowClassName,
                            (LPCTSTR) gAppName,
                            WS_THICKFRAME,
                            CW_USEDEFAULT,
                            CW_USEDEFAULT,
                            ScreenWidth,
                            ScreenHeight,
                            ghMainWindow,
                            NULL,
                            (HANDLE) ghInst,
                            NULL
                           );

	ShowWindow (ghGameWindow, SW_SHOWNORMAL);
	UpdateWindow (ghGameWindow);

    if (OSInitGameWindow ())
    {
        OSClearScreen ();
        OSClearVirtualScreen (FillBackgroundRGB);
        OSInitSound ();
    }
    else
        ErrorExit ("Game Window could not be created");

   	iFrameCounter = 0;

    return ((int) ghGameWindow);
}


/* register game window class */
void RegisterGameWindowClass ()
{
	WNDCLASS wclass;

    wclass.style = CS_HREDRAW | CS_VREDRAW;
    wclass.lpfnWndProc = (WNDPROC) GameWindowProcedure;
    wclass.cbClsExtra = 0;
    wclass.cbWndExtra = 0;
    wclass.hInstance = ghInst;
    wclass.hIcon = LoadIcon (ghInst, IDI_APPLICATION);
    wclass.hCursor = NULL;
    wclass.hbrBackground = NULL;
    wclass.lpszMenuName = NULL;
    wclass.lpszClassName = FullScreenGameWindowClassName;
    RegisterClass (&wclass);

    wclass.style = CS_HREDRAW | CS_VREDRAW;
    wclass.lpfnWndProc = (WNDPROC) GameWindowProcedure;
    wclass.cbClsExtra = 0;
    wclass.cbWndExtra = 0;
    wclass.hInstance = ghInst;
    wclass.hIcon = LoadIcon (ghInst, IDI_APPLICATION);
    wclass.hCursor = NULL;
    wclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
    wclass.lpszMenuName = NULL;
    wclass.lpszClassName = GameWindowClassName;
    RegisterClass (&wclass);
}


/* convert an integer to a 4-byte string */
extern EXPORT_TO_CLEAN CLEAN_STRING
WinBinaryIntStr (int x)
{
	static CLEAN_STRING result = NULL;

    if (result)
      	rfree (result);

    result = rmalloc (sizeof (x) + 1 + sizeof (int));
	result->length = sizeof (x);
	rsncopy (result->characters, (const char *) &x, sizeof (x));

	return result;
}

/* convert a boolean value to a Clean string */
extern EXPORT_TO_CLEAN CLEAN_STRING
WinBinaryBoolStr (BOOL b)
{
	static CLEAN_STRING result = NULL;

    if (result)
      	rfree (result);

    result = rmalloc (sizeof (b) + 1 + sizeof (int));
	result->length = sizeof (b);
	rsncopy (result->characters, (const char *) &b, sizeof (b));

	return result;
}

/* read a new bitmap into (offscreen) memory (id==0: create new id: result) */
extern EXPORT_TO_CLEAN void
WinInitGameBitmap (int id, CLEAN_STRING name,
                   int bitmapwidth, int bitmapheight,
                   int blockwidth, int blockheight,
                   OS ios, int *result, OS *oos)
{
    int resultcode = OSInitGameBitmap (id, cstring (name),
                                       bitmapwidth, bitmapheight,
                                       blockwidth, blockheight);

    *result = resultcode;
	*oos = ios;
}

/* remove all bitmaps from memory, including all sprites */
extern EXPORT_TO_CLEAN void
WinClearAllGameBitmaps (OS ios, int *result, OS* oos)
{
    FreeSpriteAnimationList ();
    OSFreeGameBitmaps ();

    *result = GR_OK;
	*oos = ios;
}

/* remove bitmap from memory */
extern EXPORT_TO_CLEAN void
WinGameBitmapDone (int id, OS ios, int *result, OS* oos)
{
    *result = OSFreeGameBitmap (id);
	*oos = ios;
}

/* set the transparent color for a bitmap: (x,y) is a transparent pixel */
extern EXPORT_TO_CLEAN void
WinSetTransparentColor (int id, int x, int y, OS ios, int *result, OS* oos)
{
    *result = OSSetTransparentColor (id, x, y);
	*oos = ios;
}

/* initialize a block animation sequence (list of repeating blocks) */
extern EXPORT_TO_CLEAN void
WinInitBlockSequence (int bitmapid, int seqid, CLEAN_STRING seq,
                      OS ios, int *result, OS *oos)
{
    *result = OSInitBlockSequence (bitmapid, seqid,
                                     cstring (seq), seq->length);
	*oos = ios;
}

/* initialize the bound map */
extern EXPORT_TO_CLEAN void
WinSetGameBoundMap (int blockwidth, int blockheight, CLEAN_STRING map,
                    int mapwidth, int mapheight, int objectstartvalue,
                    int startobjx, int startobjy,
                    OS ios, int *result, OS *oos)
{
    int resultcode = GR_OK;

    BoundMapWidth = mapwidth;
    BoundMapHeight = mapheight;
    BoundBlockWidth = blockwidth;
    BoundBlockHeight = blockheight;
    TotalWidth = mapwidth * blockwidth;
    TotalHeight = mapheight * blockheight;

#ifdef SMART_DRAW
    if (sLastTile)
      	rfree (sLastTile);
    iLastTileW = ScreenWidth / BoundBlockWidth + 1;
    iLastTileH = ScreenHeight / BoundBlockHeight + 1;
    sLastTile = rmalloc (((iLastTileW + 1) * (iLastTileH + 1) + 1) *
                           sizeof (int));
#endif

    if (sBoundMap)
      	rfree (sBoundMap);
    sBoundMap = rmalloc ((map->length) + 1);

    rsncopy (sBoundMap, map->characters, map->length);
    iObjectValueStart = objectstartvalue;
    iStartObjX = startobjx;
    iStartObjY = startobjy;

    XView = 0;
    YView = 0;
    XScrollSpeed = 0;
    YScrollSpeed = 0;

    *result = resultcode;
	*oos = ios;
}


/* set the level's initial position */
extern EXPORT_TO_CLEAN void
WinMoveScreenTo (int x, int y, OS ios, int *result ,OS *oos)
{
    XView = x;  // ((x * BoundBlockWidth) + BoundBlockWidth / 2) - ScreenWidth / 2;
    YView = y;  // ((y * BoundBlockHeight) + BoundBlockHeight / 2) - ScreenHeight / 2;
    iActualXView = XView;
    iActualYView = YView;

    CorrectView ();

    *result = GR_OK;
	*oos = ios;
}


/* initialize a new layer map */
extern EXPORT_TO_CLEAN void
WinInitGameLayerMap (int mapid, int bitmapid, CLEAN_STRING map,
                     int mapwidth, int mapheight,
                     BOOL tile,
                   /*  int scrollspeed,  */
                     OS ios, int *result, OS *oos)
{
    int resultcode;
    GAMELAYERMAPINFO *glmip1;
    GAMELAYERMAPINFO *glmip2;

    resultcode = GR_INVALID_MAP_ID;
    if (!GetGameLayerMapInfo (mapid))  /* identifier not used yet */
    {
        int gbW, gbH, gbBW, gbBH, gbCX, gbCY;
	    resultcode = GR_INVALID_BITMAP_ID;

        if (OSGetGameBitmapInfo (bitmapid,
                                    &gbW, &gbH, &gbBW, &gbBH, &gbCX, &gbCY))
	    {
	        /* find last element of linked list */
	        glmip1 = glmipGameLayerMapInfo;
	        glmip2 = NULL;
	        while (glmip1)
	        {
	            glmip2 = glmip1;
	        	glmip1 = glmip1->glmipNext;
	        }
	
	       	/* create new node */
			glmip1 = rmalloc (sizeof (GAMELAYERMAPINFO));
	        glmip1->iMapID = mapid;
	        glmip1->iBitmapID = bitmapid;
	        glmip1->iMapWidth = mapwidth;
	        glmip1->iMapHeight = mapheight;
	        glmip1->sMap = rmalloc ((map->length) + 1);
	        rsncopy (glmip1->sMap, map->characters, map->length);
            glmip1->iTotalXSize = (mapwidth * gbBW);
            glmip1->iTotalYSize = (mapheight * gbBH);
            glmip1->bTile = tile;
	        glmip1->glmipNext = NULL;
	
	        /* glmip2 points to the last element or is NULL */
	        if (glmip2)
	          	glmip2->glmipNext = glmip1;
	        else
	          	glmipGameLayerMapInfo = glmip1;  /* first element */
	
	        resultcode = GR_OK;
        }
    }

    *result = resultcode;
	*oos = ios;
}

/* remove layer map from memory */
extern EXPORT_TO_CLEAN void
WinGameLayerMapDone (int mapid, OS ios, int *result, OS* oos)
{
    GAMELAYERMAPINFO *glmipCurrent = glmipGameLayerMapInfo;
    GAMELAYERMAPINFO *glmipNext = NULL;
    GAMELAYERMAPINFO *glmipPrevious = NULL;

    *result = GR_INVALID_MAP_ID;
	while (glmipCurrent)
    {
        if (glmipCurrent->iMapID != mapid)
        {
	        glmipPrevious = glmipCurrent;
	       	glmipNext = glmipCurrent->glmipNext;
        }
    	else
        {
         	/* link previous node to next node */
            if (glmipPrevious)
              	glmipPrevious->glmipNext = glmipCurrent->glmipNext;
            else
                glmipGameLayerMapInfo = glmipCurrent->glmipNext;

	       	glmipNext = glmipCurrent->glmipNext;

            /* free the current node */
	        if (glmipCurrent->sMap)
            {
	            rfree (glmipCurrent->sMap);
                glmipCurrent->sMap = NULL;
            }
	        rfree (glmipCurrent);

            *result = GR_OK;
        }
        glmipCurrent = glmipNext;
    }

	*oos = ios;
}


/* initialize an animation sequence (if succes, result = id) */
extern EXPORT_TO_CLEAN void
WinInitSpriteAnimation (int bitmapid, CLEAN_STRING seq, BOOL loop,
                        OS ios, int* result, OS* oos)
{
    SPRITEANIMATION *saNew;
    SPRITEANIMATION *sa1;
    SPRITEANIMATION *sa2;
    int newid = 1;
    int resultcode;

    int gbW, gbH, gbBW, gbBH, gbCX, gbCY;

    resultcode = GR_INVALID_BITMAP_ID;
    if (OSGetGameBitmapInfo (bitmapid,
                                &gbW, &gbH, &gbBW, &gbBH, &gbCX, &gbCY))
    {
       /* if (loop) */
        {
	        /* first look if animation sequence already is initialized */
		    SPRITEANIMATION *sa = saSprites;
            int slen = seq->length / (2 * sizeof (int));
            BOOL equal;
            int i, j, k;
	
			while (sa)
		    {
	            if (sa->iBitmapID == bitmapid)
	            {
	              	if ((sa->bLoop == loop) && (sa->iSequenceLength == slen))
                    {
			            equal = TRUE;
                        for (k = 0; k < slen; k++)
                        {
                            i = (*(int *) &seq->characters[k * sizeof (int)]);
                            j = (*(int *) &sa->sSequence[k * sizeof (int)]);
                         	if (i != j)
                              	equal = FALSE;
                        }
                    	if (equal)
                        {
                          	*result = sa->iSpriteID;
                            *oos = ios;
                            return;
                        }
                    }
	            }
	        	sa = sa->saNext;
	        }
        }

        saNew = rmalloc (sizeof (SPRITEANIMATION));
        saNew->iBitmapID = bitmapid;
        saNew->iSequenceLength = seq->length / (2 * sizeof (int));
        saNew->sSequence = rmalloc ((seq->length) + 1);
        rsncopy (saNew->sSequence, seq->characters, seq->length);
		saNew->iPosition = 0;  // saNew->iSequenceLength;
        saNew->iCounter = (*(int *) &saNew->sSequence[sizeof (int)]);  // 0
		saNew->saNext = NULL;
        saNew->bLoop = loop;

        sa1 = saSprites;
        sa2 = NULL;
        while (sa1)
        {
            if (sa1->iSpriteID >= newid)
            	newid = sa1->iSpriteID + 1;
         	sa2 = sa1;
            sa1 = sa1->saNext;
        }
		saNew->iSpriteID = newid;
		if (sa2)
          	sa2->saNext = saNew;
        else
            saSprites = saNew;

       	resultcode = GR_OK;
    }

	if (resultcode == GR_OK)
        *result = newid;
    else
	    *result = resultcode;
	*oos = ios;
}


/* get value of boundmap[x,y] */
int ReadBoundMapValue (int x, int y, int options, int bounds, int mccode)
{
    int mappos;
    int result;

    // off map: all bounds, code 0xFF
    if ((x < 0) ||
        (y < 0) ||
        (x > BoundMapWidth - 1) ||
        (y > BoundMapHeight - 1))
    {
        if (options & OO_IGNORE_LEVEL_BOUNDS)
          	result = 0x0000;
        else
	        result = 0xFF0F;
    }
    else
    {
	    mappos = (y * BoundMapWidth + x) * sizeof (int);
        result = (*(int *) &sBoundMap[mappos]);
    }

    if (!(bounds & BND_STATIC_BOUNDS))
        result &= 0xFF00;
    if (bounds & BND_MAP_CODES)
    {
        if ((result >> 8) > 0)
            result |= mccode;
    }
    else
        result &= 0x00FF;

    return result;
}


Bounce (OBJECTREC *obj, int bound)
{
    int xb = obj->iXBounce;
    int yb = obj->iYBounce;

    if (xb < 0)
      	xb = abs (xb * obj->iXSpeed) / 256;
    if (yb < 0)
      	yb = abs (yb * obj->iYSpeed) / 256;

 	if ((bound & UPPER_BOUND) > 0)
    {
        obj->iYPos = obj->iLastYPos;
        obj->iFixedYPos = obj->iLastFixedYPos;
        obj->iYSpeed = -yb;
    }
 	if ((bound & LEFT_BOUND) > 0)
    {
        obj->iXPos = obj->iLastXPos;
        obj->iFixedXPos = obj->iLastFixedXPos;
        obj->iXSpeed = -xb;
    }
 	if ((bound & LOWER_BOUND) > 0)
    {
        obj->iYPos = obj->iLastYPos;
        obj->iFixedYPos = obj->iLastFixedYPos;
        obj->iYSpeed = yb;
    }
 	if ((bound & RIGHT_BOUND) > 0)
    {
        obj->iXPos = obj->iLastXPos;
        obj->iFixedXPos = obj->iLastFixedXPos;
        obj->iXSpeed = xb;
    }
}


/* move objects around */
void MoveObjects ()
{
    OBJECTREC *obj;
    int x, y, w, h, i, j, k, xbnd, ybnd, code, bound, Bound;
    int oldmapx1, oldmapy1, oldmapx2, oldmapy2;
    int newmapx1, newmapy1, newmapx2, newmapy2;
    int newx, newy;
    BOOL xblock, yblock, xbump, ybump;
    SPRITEANIMATION *sa;
    int iObjectCount = 0;
    struct USER_EVENT_INFO *uei;
    struct USER_EVENT_INFO *ueiNext;
    struct USER_EVENT_INFO *ueiPrev;

    // broadcast user events
    uei = ueiUserEventInfo;
    ueiPrev = NULL;
    while (uei)
    {
        ueiNext = uei->ueiNext;
        if (--uei->iTimeCounter <= 0)
        {
            if (uei->iEventID == EV_PLAY_SOUND)
            {
                int id     = uei->iEventParameter1;
                int volume = uei->iEventParameter2;
                int pan    = uei->iEventParameter3;
                int freq   = uei->iEventParameter4;

                OSPlaySoundSample (id, volume, pan, freq);
            }
            else
            {
	 	        obj = objObjects;
	 		    while (obj)
	            {
				    if (obj->bActive)
	                {
	                    if (((uei->iDestination == 0) ||
	                         (uei->iDestination == -obj->iObjectID) ||
	                         ((uei->iDestination > 0) &&
                              (uei->iDestination & obj->iOwnBounds)
                             )
                            )
                           &&
                            ((uei->iSubDestination == -1) ||
                             (uei->iSubDestination == obj->iSubType)
                            )
                           )
	                    {
                            int ix = obj->iXPos;
                            int iy = obj->iYPos;

			                iCCObjectID = obj->iObjectID;
						    SendMessage5ToClean (CcWmUSEREVENT,
			                                     obj->iObjType,
			                                     obj->iObjectID,
			                                     uei->iEventID,
			                                     uei->iEventParameter1,
			                                     uei->iEventParameter2);
			                iCCObjectID = 0;

                            if ((ix != obj->iXPos) || (iy != obj->iYPos))
                                UpdatePosition (obj);
	                    }
		            }
		         	obj = obj->objNext;
	            }
            }

            if (ueiPrev)
                ueiPrev->ueiNext = ueiNext;
            else
                ueiUserEventInfo = ueiNext;
            rfree (uei);
        }
        else
        {
            ueiPrev = uei;
        }
        uei = ueiNext;
    }


    // check position and call object's move function
    obj = objObjects;
 	while (obj)
    {
		if (obj->bActive)
        {
            if (obj->iOptions & OO_STATIC)
            {
                obj->iXPos += XView;
				obj->iYPos += YView;
            }

            // check if the animation sequence has ended
            sa = GetSpriteAnimation (obj->iSpriteID);
            if (sa)
                if (!sa->bLoop)
	            {
	                int ps;
	
	                if (obj->iCounter == 0)
	                    obj->iCounter = (*(int *) &sa->sSequence[sizeof (int)]);
	
	                if (--obj->iCounter <= 0)
	                {
	                    obj->iPosition++;
	                    if (obj->iPosition >= sa->iSequenceLength)
	                        obj->iPosition = -1;
	                    if (obj->iPosition > -1)
	                    {
		             	    ps = (2 * obj->iPosition * sizeof (int)) + sizeof (int);
		            	    obj->iCounter = (*(int *) &sa->sSequence[ps]);
	                    }
	                }
	            }

            sa = GetSpriteAnimation (obj->iSpriteID);
            if (sa)
                if (!(sa->bLoop))
	                if (obj->iPosition == -1)
	                {
	                    // send msg to Clean
                        int ix = obj->iXPos;
                        int iy = obj->iYPos;

		                iCCObjectID = obj->iObjectID;
                        obj->iLastSprite = -1;
					    SendMessage2ToClean (CcWmANIMATION, obj->iObjType, obj->iObjectID);
		                iCCObjectID = 0;

                        if ((ix != obj->iXPos) || (iy != obj->iYPos))
                            UpdatePosition (obj);
	                }


            // object too far away?

            x = obj->iXPos;
            y = obj->iYPos;

            if (obj->bActive)
            {
       	/*
            if (sa)
            {
		        gbip = GetGameBitmapInfo (sa->iBitmapID);
		    	if (gbip)
			    {
	               	w = gbip->iBlockWidth;
	               	h = gbip->iBlockHeight;
                */
                  	w = obj->iXSize;
                    h = obj->iYSize;

                    if (x + w < XView - ((obj->iForgetX + iStartObjX) * BoundBlockWidth))
 						obj->bActive = FALSE;
                    if (y + h < YView - ((obj->iForgetY + iStartObjY) * BoundBlockHeight))
 						obj->bActive = FALSE;
                    if (x > XView + ScreenWidth + ((obj->iForgetX + iStartObjX) * BoundBlockWidth))
 						obj->bActive = FALSE;
                    if (y > YView + ScreenHeight + ((obj->iForgetY + iStartObjY) * BoundBlockHeight))
 						obj->bActive = FALSE;

                    if ((obj->iTimeCounter >= 0) || (!(obj->iOptions & OO_FREEZE)))
                    {
			            if ((obj->bActive) && (!(obj->iOptions & OO_STATIC)))
				        {
                        //    int md = obj->iMoveDelay;
                            int slx = obj->iXSlowDown;
                            int sly = obj->iYSlowDown;
                            int xv, yv;

                            // save last position
				            obj->iLastXPos = obj->iXPos;
	            		    obj->iLastYPos = obj->iYPos;

						    obj->iLastFixedXPos = obj->iFixedXPos;
						    obj->iLastFixedYPos = obj->iFixedYPos;

                            // acceleration
                       //     if ((md == 0) || ((iFrameCounter % md) == 0))
                       //     {
	                            obj->iXSpeed += obj->iXAcc;
	                            obj->iYSpeed += obj->iYAcc;
                       //     }

                            // slow down the object?
                       //     if (obj->iOptions & OO_FIXED)
                       //     {
	                            if (slx > 0)
	                           	{
	 								if (obj->iXSpeed > 0)
                                        obj->iXSpeed = Max (obj->iXSpeed - slx, 0);
	 								if (obj->iXSpeed < 0)
                                        obj->iXSpeed = Min (obj->iXSpeed + slx, 0);
	                            }
	                            if (sly > 0)
	                           	{
	 								if (obj->iYSpeed > 0)
                                        obj->iYSpeed = Max (obj->iYSpeed - sly, 0);
	 								if (obj->iYSpeed < 0)
                                        obj->iYSpeed = Min (obj->iYSpeed + sly, 0);
	                            }
                       //     }
                       /*
                        	else
                            {
	                            if ((slx > 0) && ((iFrameCounter % slx) == 0))
	                           	{
	 								if (obj->iXSpeed > 0) obj->iXSpeed--;
	 								if (obj->iXSpeed < 0) obj->iXSpeed++;
	                            }
	                            if ((sly > 0) && ((iFrameCounter % sly) == 0))
	                            {
	 								if (obj->iYSpeed > 0) obj->iYSpeed--;
	 								if (obj->iYSpeed < 0) obj->iYSpeed++;
	                            }
                        	}
                       */
                            if (slx < 0)
                                obj->iXSpeed = obj->iXSpeed * (256 + slx) / 256;
                            if (sly < 0)
                                obj->iYSpeed = obj->iYSpeed * (256 + sly) / 256;

                            // check object's maximum speed
                            if (obj->iXSpeed > obj->iMaxXSpeed)
                              	obj->iXSpeed = obj->iMaxXSpeed;
                            if (obj->iXSpeed < -obj->iMaxXSpeed)
                              	obj->iXSpeed = -obj->iMaxXSpeed;
                            if (obj->iYSpeed > obj->iMaxYSpeed)
                              	obj->iYSpeed = obj->iMaxYSpeed;
                            if (obj->iYSpeed < -obj->iMaxYSpeed)
                              	obj->iYSpeed = -obj->iMaxYSpeed;

                            // move the object
                        //    if (obj->iOptions & OO_FIXED)
                        //    {
								obj->iFixedXPos += obj->iXSpeed;
                                obj->iFixedYPos += obj->iYSpeed;
                                obj->iXPos = (obj->iFixedXPos + 128) >> 8;
                                obj->iYPos = (obj->iFixedYPos + 128) >> 8;
                        //    }
                        /*	else
                            {
    	                    	obj->iXPos += obj->iXSpeed;
							    obj->iYPos += obj->iYSpeed;
                            }  */

                            if (obj->iSkipMove > 0)
                                obj->iSkipMove--;
                            else
                                if (obj->iSkipMove == 0)
	                            {
                                    // inform Clean that new object may move now
                                    int ix = obj->iXPos;
                                    int iy = obj->iYPos;

					                iCCObjectID = obj->iObjectID;
								    SendMessage2ToClean (CcWmMOVEOBJECT, obj->iObjType, obj->iObjectID);
					                iCCObjectID = 0;
/*
					              	obj->iFixedXPos = obj->iXPos << 8;
					              	obj->iFixedYPos = obj->iYPos << 8;
					                obj->iLastFixedXPos = obj->iFixedXPos;
					                obj->iLastFixedYPos = obj->iFixedYPos;
*/

                                    if ((ix != obj->iXPos) || (iy != obj->iYPos))
                                        UpdatePosition (obj);

    	                        }
	                    }
                    }
            /*
                }
            */
            }
        }

        if (obj->bActive)
	    	iObjectCount++;

        obj = obj->objNext;
    }

	// check collisions between objects

    if (iObjectCount > 1)
    {
    	int pos = 0;
        int p = 0;
	    int *collisions = rmalloc (4 * iObjectCount *
                            (iObjectCount - 1) * sizeof (int));

	    obj = objObjects;
	 	while (obj)
	    {
	        if ((obj->bActive) &&
                ((obj->iBounceBounds) || (obj->iCollideBounds)))
			{
			    int X1 = obj->iXPos;
		    	int Y1 = obj->iYPos;
	            int X2 = X1 + obj->iXSize - 1;
	            int Y2 = Y1 + obj->iYSize - 1;
			    int OldX1 = obj->iLastXPos;
		    	int OldY1 = obj->iLastYPos;
	            int OldX2 = OldX1 + obj->iXSize - 1;
	            int OldY2 = OldY1 + obj->iYSize - 1;
	
	            OBJECTREC *objTmp = objObjects;
	
	 			while (objTmp)
	            {
			        if ((objTmp->bActive) &&
                        (obj != objTmp) &&
                        ((obj->iBounceBounds & objTmp->iOwnBounds) ||
                         (obj->iCollideBounds & objTmp->iOwnBounds))
                       )
					{
					    int x1 = objTmp->iXPos;
				    	int y1 = objTmp->iYPos;
			            int x2 = x1 + objTmp->iXSize - 1;
			            int y2 = y1 + objTmp->iYSize - 1;
					    int oldx1 = objTmp->iLastXPos;
				    	int oldy1 = objTmp->iLastYPos;
			            int oldx2 = oldx1 + objTmp->iXSize - 1;
			            int oldy2 = oldy1 + objTmp->iYSize - 1;
		
		                // do the two objects collide?
		 				if ((x1 <= X2) && (x2 >= X1) &&
	                        (y1 <= Y2) && (y2 >= Y1))
	                    {
				            /*
				               bit 0: upper bound
				               bit 1: left bound
				               bit 2: lower bound
				               bit 3: right bound
				            */
	
	                        // for each collision two messages are sent
	                       	Bound = 0;
	                        bound = 0;
	    	                {
	                        	// detect directions of new collisions
	                            if (OldY1 > oldy2)
	                            {
									Bound |= UPPER_BOUND;
									bound |= LOWER_BOUND;
	                            }
	                            if (OldX1 > oldx2)
	                            {
									Bound |= LEFT_BOUND;
									bound |= RIGHT_BOUND;
	                            }
	                            if (OldY2 < oldy1)
	                            {
									Bound |= LOWER_BOUND;
									bound |= UPPER_BOUND;
	                            }
	                            if (OldX2 < oldx1)
	                            {
									Bound |= RIGHT_BOUND;
									bound |= LEFT_BOUND;
	                            }
	 						}
	
                            // first store all collisions, then call
                            //   the collide function for each
                            collisions[pos++] = obj->iObjectID;
                            collisions[pos++] = objTmp->iObjectID;
                            collisions[pos++] = bound;
                            collisions[pos++] = Bound;
		                }
		            }
	                objTmp = objTmp->objNext;
	            }
	        }
	        obj = obj->objNext;
	    }

    	// maybe sort the list of colliding objects?
        // ...

        // call the collide functions
		while (p < pos)
        {
            OBJECTREC *objTmp;
            int bnds;

        	obj = GetObjectRec (collisions[p++]);
        	objTmp = GetObjectRec (collisions[p++]);

            bnds = collisions[p++];
            if (obj->iBounceBounds & objTmp->iOwnBounds)
				Bounce (obj, bnds);

            if (obj->iCollideBounds & objTmp->iOwnBounds)
            {
                int ix = obj->iXPos;
                int iy = obj->iYPos;

                iCCObjectID = obj->iObjectID;
 	        	SendMessage5ToClean (CcWmCOLLISION,
                    obj->iObjType, obj->iObjectID,
	                objTmp->iObjType, objTmp->iObjectID, bnds);
                iCCObjectID = 0;

                if ((ix != obj->iXPos) || (iy != obj->iYPos))
                    UpdatePosition (obj);
            }
            p++;
        /*
            if (objTmp->iOptions & OO_BOUNCE_AT_COLLISIONS)
				Bounce (objTmp, collisions[p++]);
            else
            {
                iCCObjectID = objTmp->iObjectID;
	        	SendMessage5ToClean (CcWmCOLLISION,
                    objTmp->iObjType, objTmp->iObjectID,
	                obj->iObjType, obj->iObjectID, collisions[p++]);
                iCCObjectID = 0;
            }
        */

        }

    	rfree (collisions);
    }


	// check collisions with static bounds and map codes
	obj = objObjects;
 	while (obj)
    {
        if ((obj->bActive) &&
            ((obj->iBounceBounds & (BND_STATIC_BOUNDS | BND_MAP_CODES)) ||
             (obj->iCollideBounds & (BND_STATIC_BOUNDS | BND_MAP_CODES)))
           )
		{
			// check if the object touches a bound in the boundmap
		    x = obj->iXPos;
	    	y = obj->iYPos;

            newmapx1 = intdiv (x, BoundBlockWidth);
            newmapy1 = intdiv (y, BoundBlockHeight);
            newmapx2 = intdiv (x + obj->iXSize - 1, BoundBlockWidth);
            newmapy2 = intdiv (y + obj->iYSize - 1, BoundBlockHeight);

            oldmapx1 = intdiv (obj->iLastXPos, BoundBlockWidth);
            oldmapy1 = intdiv (obj->iLastYPos, BoundBlockHeight);
            oldmapx2 = intdiv (obj->iLastXPos + obj->iXSize - 1, BoundBlockWidth);
            oldmapy2 = intdiv (obj->iLastYPos + obj->iYSize - 1, BoundBlockHeight);

            xblock = FALSE;
            yblock = FALSE;

            /* object's X position on map changed? */
            if (newmapx1 < oldmapx1)  // moving left
            {
                xblock = TRUE;
                newx = newmapx1;
                xbnd = RIGHT_BOUND;  // check right bound
            }
            if (newmapx2 > oldmapx2)  // moving right
            {
                xblock = TRUE;
                newx = newmapx2;
                xbnd = LEFT_BOUND;  // check left bound
            }

            /* object's Y position on map changed? */
            if (newmapy1 < oldmapy1)  // moving up
            {
                yblock = TRUE;
                newy = newmapy1;
                ybnd = LOWER_BOUND;  // check lower bound
            }
            if (newmapy2 > oldmapy2)  // moving down
            {
                yblock = TRUE;
                newy = newmapy2;
                ybnd = UPPER_BOUND;  // check upper bound
            }


        /* --------------- bounce -------------- */

            xbump = FALSE;
            ybump = FALSE;
            bound = 0;
            /*
               bit 0: upper bound
               bit 1: left bound
               bit 2: lower bound
               bit 3: right bound
            */

            /* object touches a vertical bound (wall)? */
            if (xblock)
                for (j = oldmapy1; j <= oldmapy2; j++)
                    bound |= (ReadBoundMapValue (newx, j, obj->iOptions,
                                 obj->iBounceBounds, 0x0F) & xbnd);
            if (bound & xbnd)
                xbump = TRUE;


            /* object touches a horizontal bound (floor)? */
            if (yblock)
                for (i = oldmapx1; i <= oldmapx2; i++)
                    bound |= (ReadBoundMapValue (i, newy, obj->iOptions,
                                 obj->iBounceBounds, 0x0F) & ybnd);
            if (bound & ybnd)
                ybump = TRUE;


            /* object touches a corner? */
			if ((xblock && yblock) && ((!xbump) && (!ybump)))
            {
                bound |= (ReadBoundMapValue (newx, newy, obj->iOptions,
                             obj->iBounceBounds, 0x0F) & (xbnd | ybnd));
                xbump = TRUE;
                ybump = TRUE;
            }

           	Bounce (obj, bound);

        /* --------------- collide -------------- */

            xbump = FALSE;
            ybump = FALSE;
            bound = 0;
            /*
               bit 0: upper bound
               bit 1: left bound
               bit 2: lower bound
               bit 3: right bound
            */
            code = 0;

            /* object touches a vertical bound (wall)? */
            if (xblock)
                for (j = oldmapy1; j <= oldmapy2; j++)
                {
                    k = ReadBoundMapValue (newx, j,
                             obj->iOptions, obj->iCollideBounds, 0);
                    if ((k >> 8) > code)
                        code = (k >> 8);
                    bound |= (k & xbnd);
                }
            if (bound & xbnd)
                xbump = TRUE;

            /* object touches a horizontal bound (floor)? */
            if (yblock)
                for (i = oldmapx1; i <= oldmapx2; i++)
                {
                    k = ReadBoundMapValue (i, newy,
                                 obj->iOptions, obj->iCollideBounds, 0);
                    if ((k >> 8) > code)
                        code = (k >> 8);
                    bound |= (k & ybnd);
                }
            if (bound & ybnd)
                ybump = TRUE;

            /* object touches a corner? */
			if ((xblock && yblock) && ((!xbump) && (!ybump)))
            {
                k = ReadBoundMapValue (newx, newy, obj->iOptions,
                                 obj->iCollideBounds, 0);
                if ((k >> 8) > code)
                    code = (k >> 8);
                bound |= (k & (xbnd | ybnd));

                xbump = TRUE;
                ybump = TRUE;
            }

        	if ((bound > 0) || (code > 0))
            {
                int ix = obj->iXPos;
                int iy = obj->iYPos;

               	// obj has touched something, send msg to Clean
                iCCObjectID = obj->iObjectID;
		        SendMessage4ToClean (CcWmTOUCHBOUND, obj->iObjType, obj->iObjectID, bound, code);
                iCCObjectID = 0;

                if ((ix != obj->iXPos) || (iy != obj->iYPos))
                    UpdatePosition (obj);
            }

        	/* update last directions */
            if (obj->iXPos < obj->iLastXPos)  /* moving left */
            	obj->iOptions |= OO_LAST_DIRECTION_LEFT;
            else
	            if (obj->iXPos > obj->iLastXPos)  /* moving right */
    	        	obj->iOptions &= ~ OO_LAST_DIRECTION_LEFT;

            if (obj->iYPos < obj->iLastYPos)  /* moving up */
            	obj->iOptions |= OO_LAST_DIRECTION_UP;
            else
	            if (obj->iYPos > obj->iLastYPos)  /* moving down */
    	        	obj->iOptions &= ~ OO_LAST_DIRECTION_UP;
        }

        // increment object's frame counter
        if (obj->bActive)
        {
	    	obj->iTimeCounter++;
	        if (obj->iTimeCounter == 0)
	        {
			    // send timer msg
                int ix = obj->iXPos;
                int iy = obj->iYPos;

	            iCCObjectID = obj->iObjectID;
			    SendMessage2ToClean (CcWmOBJECTTIMER, obj->iObjType, obj->iObjectID);
	            iCCObjectID = 0;

                if ((ix != obj->iXPos) || (iy != obj->iYPos))
                    UpdatePosition (obj);
	        }

	    	if (obj->iObjectID == iFollowID)
	        {
				// screen focus
			    int x1 = obj->iXPos;
		    	int y1 = obj->iYPos;
			    int x2 = x1 + obj->iXSize;
		    	int y2 = y1 + obj->iYSize;

                if (x1 < obj->iLastXPos)
	                if (iFollowX1 != FC_OFFSCREEN)
	                    if (x1 - iFollowX1 < iActualXView)
	                      	iActualXView = x1 - iFollowX1;
                if (x1 > obj->iLastXPos)
	                if (iFollowX2 != FC_OFFSCREEN)
	                    if (x2 + iFollowX2 > iActualXView + ScreenWidth)
	                      	iActualXView = x2 + iFollowX2 - ScreenWidth;

                if (y1 < obj->iLastYPos)
	                if (iFollowY1 != FC_OFFSCREEN)
    	                if (y1 - iFollowY1 < iActualYView)
        	              	iActualYView = y1 - iFollowY1;
                if (y1 > obj->iLastYPos)
		            if (iFollowY2 != FC_OFFSCREEN)
        	            if (y2 + iFollowY2 > iActualYView + ScreenHeight)
            	          	iActualYView = y2 + iFollowY2 - ScreenHeight;
	        }
        }

        if (obj->iOptions & OO_STATIC)
        {
            obj->iXPos -= XView;
			obj->iYPos -= YView;
        }

        obj = obj->objNext;
    }


    // initialize new objects
	obj = objObjects;
 	while (obj)
    {
     	if (!(obj->bActive))
        {
         	// new object?
            if ((obj->iMapX == -1) && (obj->iMapY == -1))
            {
                obj->iMapX = -2;
                obj->iMapY = -2;

                iCCObjectID = obj->iObjectID;
			    SendMessage6ToClean (CcWmINITOBJECT,
                                     obj->iObjType, obj->iSubType,
                                     obj->iObjectID, obj->iXPos, obj->iYPos,
                                     iFrameCounter);
                iCCObjectID = 0;

                if (obj->bActive)
                {
				    obj->iLastXPos = obj->iXPos;
				    obj->iLastYPos = obj->iYPos;
				
				  	obj->iFixedXPos = obj->iXPos << 8;
				  	obj->iFixedYPos = obj->iYPos << 8;
				    obj->iLastFixedXPos = obj->iFixedXPos;
				    obj->iLastFixedYPos = obj->iFixedYPos;
                }
			}
        }
        obj = obj->objNext;
    }


	// remove all objects that are not active
   	GameObjectDone (0);

	CorrectView ();
}

#ifdef SMART_DRAW
/* avoid drawing the same tile if it is already there */
static void SaveSmart (int x, int y, int tilenr)
{
    int pos = (y * iLastTileW + x) * sizeof (int);

    (*(int *) &sLastTile[pos]) =
        setflipint ((*(int *) &sLastTile[pos]), tilenr);
}

static int GetSmart (int x, int y)
{
    int pos = (y * iLastTileW + x) * sizeof (int);

    return (*(int *) &sLastTile[pos]);
}

static void EraseSmart (int x, int y)
{
    if ((x >= 0) && (x <= iLastTileW))
        if ((y >= 0) && (y <= iLastTileH))
            SaveSmart (x, y, 0);
}

static void EraseSmartArea (int x1, int y1, int x2, int y2)
{
    int x, y;
    int i, j;

    x1--;
    y1--;
    x2++;
    y2++;

    x1 = (x1 - (getflipint (iTopLayerLastX) % BoundBlockWidth));
    x1 = x1 / BoundBlockWidth;
    y1 = (y1 - (getflipint (iTopLayerLastY) % BoundBlockHeight));
    y1 = y1 / BoundBlockHeight;

    x2 = (x2 - (getflipint (iTopLayerLastX) % BoundBlockWidth));
    x2 = x2 / BoundBlockWidth;
    y2 = (y2 - (getflipint (iTopLayerLastY) % BoundBlockHeight));
    y2 = y2 / BoundBlockHeight;

    for (j = y1; j <= (y2 + 1); j++)
        for (i = x1; i <= (x2 + 1); i++)
            EraseSmart (i, j);
}
#endif

/* draw objects */
void DrawObjects (int FromLayer, int ToLayer)
{
    OBJECTREC *obj = objObjects;
    int x, y, w, h, sw, sh, dw, dh;
    int bmpx, bmpy;
    int seqpos, maxblock, mappos, mapblock;
    BOOL mir, ups;
    int bid;

    SPRITEANIMATION *sa;

  	RECT src, dst;
    int flags, fx;

 	while (obj)
    {
		if (
             (obj->bActive)
             &&
             ((!(obj->iDisplayOptions & DO_BLINK)) || (iFrameCounter % 2 == 0))
           )
        if (
            (obj->iLayer >= FromLayer) && (obj->iLayer <= ToLayer)
           )
        {
            x = obj->iXPos + obj->iXOffset;
            y = obj->iYPos + obj->iYOffset;

            if (!(obj->iOptions & OO_STATIC))
            {
                x -= XView;
				y -= YView;
            }

            if ((x < ScreenWidth) && (y < ScreenHeight))
            {
                sa = GetSpriteAnimation (obj->iSpriteID);
                if (sa)
                {
                    int gbW, gbH, gbBW, gbBH, gbCX, gbCY;

                    bid = sa->iBitmapID;
			        if (OSGetGameBitmapInfo (bid,
                                   &gbW, &gbH, &gbBW, &gbBH, &gbCX, &gbCY))
				    {
	                 	w = gbBW;
	                 	h = gbBH;

                        dw = w;  // dst size
                        dh = h;
                        if (obj->iDisplayOptions & DO_STRETCH)
                        {
                         	dw = obj->iXSize;
                            dh = obj->iYSize;
                        }
		  //
            			src.left = 0;
                        src.top = 0;
                        src.right = w;
                        src.bottom = h;

 						dst.left = x;
                        dst.top = y;
                        dst.right = x + dw;
                        dst.bottom = y + dh;

                        seqpos = sa->iPosition;
                        if (!(sa->bLoop))
                            seqpos = obj->iPosition;

                        if ((dst.right >= 0) &&
                            (dst.bottom >= 0) &&
                            (seqpos >= 0))
                        {
                            maxblock = gbCX * gbCY;
							mappos = 2 * sizeof (int) * seqpos;
	                      	mapblock = (*(int *) &sa->sSequence[mappos]) - 1;

		                    fx = (mapblock / maxblock) & 3;
	 	                    mapblock %= maxblock;
	                       	bmpx = (mapblock % gbCX) * w;
	                       	bmpy = (mapblock / gbCX) * h;

                         	mir = FALSE;
							if ( ((fx & 1) == 1)
                               ^ (obj->iDisplayOptions & DO_MIRROR_LEFT_RIGHT)
                               ^ ( (obj->iOptions & OO_AUTO_MIRROR_LEFT_RIGHT) &&
                                   (obj->iOptions & OO_LAST_DIRECTION_LEFT)
                                 )
                               )
                            {
                                mir = TRUE;
                            }

                        	ups = FALSE;
							if ( ((fx & 2) == 2)
                               ^ (obj->iDisplayOptions & DO_MIRROR_UP_DOWN)
                               ^ ( (obj->iOptions & OO_AUTO_MIRROR_UP_DOWN) &&
                                   (obj->iOptions & OO_LAST_DIRECTION_UP)
                                 )
                               )
                            {
                                ups = TRUE;
                            }

                            if (dst.right > ScreenWidth)
                            {
								sw = ((ScreenWidth - dst.left) * w + dw - 1) / dw;
                                if (!mir)
	                                src.right = src.left + sw;
                                else
	                                src.left = src.right - sw;
								dw = ScreenWidth - dst.left;
                                dst.right = ScreenWidth;
                            }
                            if (dst.bottom > ScreenHeight)
                            {
								sh = ((ScreenHeight - dst.top) * h + dh - 1) / dh;
                                if (!ups)
	                                src.bottom = src.top + sh;
                                else
	                                src.top = src.bottom - sh;
								dh = ScreenHeight - dst.top;
                                dst.bottom = ScreenHeight;
                            }
                            if (dst.left < 0)
                            {
								sw = ((dst.right) * w + dw - 1) / dw;
                                if (!mir)
	                                src.left = src.right - sw;
                                else
	                                src.right = src.left + sw;
								dw = -dst.right;
                                dst.left = 0;
                            }
                            if (dst.top < 0)
                            {
								sh = ((dst.bottom) * h + dh - 1) / dh;
                                if (!ups)
	                                src.top = src.bottom - sh;
                                else
	                                src.bottom = src.top + sh;
								dh = -dst.bottom;
                                dst.top = 0;
                            }

                            src.left += bmpx;
                            src.top += bmpy;
                            src.right += bmpx;
                            src.bottom += bmpy;

#ifdef SMART_DRAW
                            if (cmpflipint (bAllowSmartDraw, 1))
                                EraseSmartArea (dst.left, dst.top,
                                                dst.right, dst.bottom);
#endif

                            OSDraw (&dst, bid, &src, mir, ups,
                                    obj->iDisplayOptions);
	                    }
	                }
				}
            }
        }
        else
        {
            if ((obj->iLayer > ToLayer) && (obj->iLayer < iNextLayer))
                iNextLayer = obj->iLayer;
        }
        obj = obj->objNext;
    }
}


/* calculate layer offset and draw layer */
int DrawLayer (int mapid)
{
/*
  	HRESULT ddrval;
	DDBLTFX ddbltfx;
*/
  	RECT r, src, dst;

    int resultcode;
    int xstart, ystart;
    int	x, y;
  	int w, h;
    int mapx, mapy;
    int mapw, maph;
    int curw, curh;
    int mappos, mapblock;
    int bmpx, bmpy;
    int flags, bltflags, fx;
    int maxblock;
    BOOL mir, ups;

    GAMELAYERMAPINFO *glmip = GetGameLayerMapInfo (mapid);

   	resultcode = GR_INVALID_MAP_ID;
   	if (glmip)
    {
        int gbW, gbH, gbBW, gbBH, gbCX, gbCY;

    	resultcode = GR_INVALID_BITMAP_ID;
      /*
        gbip = GetGameBitmapInfo (glmip->iBitmapID);
    	if (gbip)
      */

        if (OSGetGameBitmapInfo (glmip->iBitmapID,
                                    &gbW, &gbH, &gbBW, &gbBH, &gbCX, &gbCY))
	    {
           	w = gbBW;
          	h = gbBH;

            maxblock = gbCX * gbCY;

            mapw = glmip->iMapWidth;
			maph = glmip->iMapHeight;


          	/* calculate layer position */

			SendMessage4ToClean (CcWmSCROLL, mapid, XView, YView, iFrameCounter);
			xstart = gCci.p1;
			ystart = gCci.p2;

#ifdef SMART_DRAW
            if ((glmip->glmipNext == NULL) &&
                (mapid == 1) &&
                (bFillBackground == FALSE) &&
                (cmpflipint (iTopLayerLastX, xstart)) &&
                (cmpflipint (iTopLayerLastY, ystart)))
            {
                if (cmpflipint (bAllowSmartDraw, 1))
                    bSmartDraw = setflipint (bSmartDraw, 1);
                else
                    bSmartDraw = setflipint (bSmartDraw, 0);
                bAllowSmartDraw = setflipint (bAllowSmartDraw, 1);
            }
            else
            {
                bSmartDraw = setflipint (bSmartDraw, 0);
                bAllowSmartDraw = setflipint (bAllowSmartDraw, 0);
            }

            if (glmip->glmipNext == NULL)
            {
                iTopLayerLastX = setflipint (iTopLayerLastX, xstart);
                iTopLayerLastY = setflipint (iTopLayerLastY, ystart);
            }
#endif
			mapy = ystart / h;
           	r.top = ystart % h;

           	for (y = 0; y < ScreenHeight; )
           	{
              	mapx = xstart / w;
           		r.left = xstart % w;
      			curh = Min (ScreenHeight - y, h - r.top);

               	for (x = 0; x < ScreenWidth; )
               	{
                   	curw = Min (ScreenWidth - x, w - r.left);

                   	/* calculate position in map, read block number */
                   	mappos = ((mapx % mapw) + ((mapy % maph) * mapw));
                    mappos *= sizeof (int);
           			mapblock = (*(int *) &glmip->sMap[mappos]);

                    if (mapblock < 0)  // sequence ID
                        mapblock = OSGetCurrentBlock (glmip->iBitmapID,
                                                      mapblock);

#ifdef SMART_DRAW
                    if (cmpflipint (bAllowSmartDraw, 1))
                        SaveSmart (x / w, y / h, mapblock);
                    if (cmpflipint (bSmartDraw, 1))
                        if (cmpflipint (GetSmart (x / w, y / h), mapblock))
                            mapblock = 0;
#endif
              		mapblock--;
                    fx = (mapblock / maxblock) & 3;

                   	if (mapblock >= 0)
                   	{
	                    mapblock %= maxblock;
                      	bmpx = (mapblock % gbCX) * w;
                      	bmpy = (mapblock / gbCX) * h;
                       	src.left = r.left + bmpx;
                       	src.top = r.top + bmpy;

                        mir = FALSE;
                        ups = FALSE;
                        if ((fx & 1) == 1)
                        {
                            mir = TRUE;
                            src.left = bmpx + w - curw - (r.left % w);
                        }
                        if ((fx & 2) == 2)
                        {
                            ups = TRUE;
                            src.top = bmpy + h - curh - (r.top % w);
                        }
                       	src.right = src.left + curw;
                       	src.bottom = src.top + curh;
                       	dst.left = x;
                        dst.top = y;
                        dst.right = x + curw;
                        dst.bottom = y + curh;
                        OSDraw (&dst, glmip->iBitmapID, &src, mir, ups, 0);

                   	}
                   	x += curw;
                   	r.left = 0;
                   	mapx++;
               	}
           		y += curh;
               	r.top = 0;
               	mapy++;
           	}
	   		resultcode = GR_OK;
       	}
   	}
	return resultcode;
}


/* run sequences, draw layers and flip pages */
int NextFrame (int fade)
{
    int resultcode;
    DDBLTFX ddbltfx;
    BOOL clear;
    GAMELAYERMAPINFO *glmip = glmipGameLayerMapInfo;
    SPRITEANIMATION *sa = saSprites;
    int i;

    resultcode = GR_FAILED;
  	if (bGameActive)
    {
	    /* run block sequences */
        OSRunBlockSequences ();
	
		/* run sprite animations */
		while (sa)
        {
			if ((sa->bLoop) && (--sa->iCounter <= 0))
            {
             	sa->iPosition++;
                if (sa->iPosition >= sa->iSequenceLength)
                 	sa->iPosition = 0;
            	i = (2 * sa->iPosition * sizeof (int)) + sizeof (int);
            	sa->iCounter = (*(int *) &sa->sSequence[i]);
            }
         	sa = sa->saNext;
        }

    	MoveObjects ();


        if (iFrameCounter < 2)
            clear = TRUE;
        else
            clear = bFillBackground;

        if (clear)
            OSClearVirtualScreen (FillBackgroundRGB);
    /* */

        DrawObjects (-0x80000000, 0);
        iNextLayer = 0x7FFFFFFF;

        i = 1;
		while (glmip)
        {
			DrawLayer (glmip->iMapID);
            iNextLayer = 0x7FFFFFFF;
	    	DrawObjects (i, i);
            i++;
            glmip = glmip->glmipNext;
        }
        i = iNextLayer;
        while (i < 0x7FFFFFFF)
        {
         	iNextLayer = 0x7FFFFFFF;
            DrawObjects (i, i);
            i = iNextLayer;
        }
    	DrawObjects (0x7FFFFFFF, 0x7FFFFFFF);  /* InFront */

        /* now ask for the statistics */
        SendMessage0ToClean (CcWmSTATISTICS);

    	resultcode = GR_OK;
    }

    if (fade != 0)  /* fade in */
    {
        int i;
       /*
   	    int flags = DDBLT_WAIT + DDBLT_DDFX;
        DDBLTFX ddbltfx;
        memset (&ddbltfx, 0, sizeof (ddbltfx));
        ddbltfx.dwSize = sizeof (ddbltfx);
        ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
       */

        if (fade == 1)          
            for (i = 0; i < ScreenHeight / 2 - 1; i++)
            {
                RECT r = {0, 0, ScreenWidth, ScreenHeight};

                RECT r1 = {0, 0, ScreenWidth, i};
                RECT r2 = {0, 0, i, ScreenHeight};
                RECT r3 = {ScreenWidth - i, 0, ScreenWidth, ScreenHeight};
                RECT r4 = {0, ScreenHeight - i, ScreenWidth, ScreenHeight};

                OSFillBlack (FALSE, r1);
                OSFillBlack (FALSE, r2);
                OSFillBlack (FALSE, r3);
                OSFillBlack (FALSE, r4);

                OSBlit (&r);
              /*
    	      	ddrval = IDirectDrawSurface_Blt
    	             (lpDDSFront, &dst, lpDDSBack, &src, flags, &ddbltfx);
    	        if (ddrval == DDERR_SURFACELOST)
    	            DDRestoreAll ();
              */
            }

        OSClearScreen ();

        if (fade == -1)
            for (i = ScreenHeight / 2 - 1; i > 0; i--)
            {
                RECT r = {i, i, ScreenWidth - i, ScreenHeight - i};

                RECT r1 = {0, 0, ScreenWidth, i};
                RECT r2 = {0, 0, i, ScreenHeight};
                RECT r3 = {ScreenWidth - i, 0, ScreenWidth, ScreenHeight};
                RECT r4 = {0, ScreenHeight - i, ScreenWidth, ScreenHeight};

                OSFillBlack (TRUE, r1);
                OSFillBlack (TRUE, r2);
                OSFillBlack (TRUE, r3);
                OSFillBlack (TRUE, r4);

                OSBlit (&r);
            /*
    	      	ddrval = IDirectDrawSurface_Blt
    	             (lpDDSFront, &dst, lpDDSBack, &src, flags, &ddbltfx);
    	        if (ddrval == DDERR_SURFACELOST)
    	            DDRestoreAll ();
            */
            }
    }

    if (fade == 0)
    {
		if (FullScreen)
            OSFlip ();
	    else
	    {
	        /* running in a window */
			RECT r = {0, 0, ScreenWidth, ScreenHeight};
            OSBlit (&r);
         /*
			int flags = DDBLTFAST_WAIT;
			RECT src = {0, 0, ScreenWidth, ScreenHeight};
	    / /
	        RECT dst = {20, 20, ScreenWidth - 40, ScreenHeight - 40};
	        DDBLTFX ddbltfx;
	
	        memset (&ddbltfx, 0, sizeof (ddbltfx));
	        ddbltfx.dwSize = sizeof (ddbltfx);
	        ddbltfx.dwAlphaDestConst = 0x001020F0;
	        ddbltfx.dwAlphaEdgeBlend = 0x10203040;
	        ddbltfx.dwFillDepth = 0x10709040;
	
	        flags = DDBLT_ALPHADESTCONSTOVERRIDE
	              | DDBLT_ALPHAEDGEBLEND
	              | DDBLT_ALPHASRC
	              | DDBLT_DEPTHFILL
	              | DDBLT_WAIT;
	      	ddrval = IDirectDrawSurface_Blt
	             (lpDDSFront, &dst, lpDDSBack, &src, flags, &ddbltfx);
          / /
	      	ddrval = IDirectDrawSurface_BltFast (lpDDSFront, 0, 0, lpDDSBack, &src, flags);
	
	        if (ddrval == DDERR_SURFACELOST)
	            DDRestoreAll ();
	    */
	    }
    }

	return resultcode;
}


/* create a new event */
ScheduleEvent (int event, int par1, int par2, int par3, int par4,
               int target, int subtarget, int time)
{
    struct USER_EVENT_INFO *uei;

    uei = rmalloc (sizeof (USER_EVENT_INFO));
    uei->iEventID = event;
    uei->iEventParameter1 = par1;
    uei->iEventParameter2 = par2;
    uei->iEventParameter3 = par3;
    uei->iEventParameter4 = par4;
    uei->iDestination = target;
    uei->iSubDestination = subtarget;
    uei->iTimeCounter = time;
    uei->ueiNext = ueiUserEventInfo;
    ueiUserEventInfo = uei;
}

/* make the screen follow an object */
extern EXPORT_TO_CLEAN void
WinSetObjectFocus (int x1, int y1, int x2, int y2, int maxxv, int maxyv,
                   OS ios, int *result, OS *oos)
{
    OBJECTREC *obj = GetObjectRec (iCCObjectID);
 	int resultcode = GR_NOT_FOUND;

    if (obj)
    {
   		iFollowID = iCCObjectID;

        iFollowX1 = x1;
        iFollowY1 = y1;
        iFollowX2 = x2;
        iFollowY2 = y2;

        iMaxXScrollSpeed = maxxv;
        iMaxYScrollSpeed = maxyv;

        iActualXView = XView;
        iActualYView = YView;

       	XScrollSpeed = 0;
        YScrollSpeed = 0;

        resultcode = GR_OK;
    }

	*result = resultcode;
    *oos = ios;
}


/* create a new game object during the game */
void CreateGameObject (int mapval, int above, int x, int y,
                  int *result)
{
    OBJECTREC *objNew;
    OBJECTREC *obj1;
    OBJECTREC *obj2;
    int newid;


    // create a unique id number
    newid = 1;
	while (GetObjectRec (newid))
        newid++;

    objNew = rmalloc (sizeof (OBJECTREC));
    objNew->iMapX = -1;
    objNew->iMapY = -1;
    objNew->iObjType = mapval;
    objNew->iSubType = above;
    objNew->iXPos = x;
    objNew->iYPos = y;
    objNew->iObjectID = newid;
    objNew->bActive = FALSE;
    objNew->objNext = NULL;

    obj1 = objObjects;
    obj2 = NULL;

    while (obj1)
    {
     	obj2 = obj1;
        obj1 = obj1->objNext;
    }

    if (obj2)
      	obj2->objNext = objNew;
    else
        objObjects = objNew;

    // object remains inactive until moveobject() is run

	*result = GR_OK;
}


extern EXPORT_TO_CLEAN void
WinSetObjectRec (int id, int objtype, int subtype, BOOL active,
                 int xpos, int ypos, int xsize, int ysize,
                 int xoffset, int yoffset, int spriteid,
                 int displayoptions, int ownbounds, int bouncebounds,
                 int collidebounds, int forgetx, int forgety, int time,
                 int layer, int xacc, int yacc, int xv, int yv,
                 int xbnc, int ybnc, int maxxv, int maxyv,
                 int xslowdown, int yslowdown, int skipmove, int options,
                 OS ios, int *result, OS *oos)
{
    OBJECTREC *obj = GetObjectRec (id);
 	int resultcode = GR_NOT_FOUND;

    if (obj)
    {
        obj->iObjType = objtype;
        obj->iSubType = subtype;
        obj->bActive = active;
        obj->iXPos = xpos;
        obj->iYPos = ypos;
        obj->iXSize = xsize;
        obj->iYSize = ysize;
		obj->iXOffset = xoffset;
        obj->iYOffset = yoffset;
        if (spriteid != obj->iLastSprite)
        {
            obj->iPosition = 0;
            obj->iCounter = 0;
            obj->iLastSprite = spriteid;
        }
        obj->iSpriteID = spriteid & 0xFFFF;
        obj->iSpriteIndex = spriteid >> 16;
        obj->iDisplayOptions = displayoptions;
        obj->iOwnBounds = ownbounds;
        obj->iBounceBounds = bouncebounds;
        obj->iCollideBounds = collidebounds;
        obj->iForgetX = forgetx;
        obj->iForgetY = forgety;
        obj->iTimeCounter = time;
        obj->iLayer = layer;
        obj->iXAcc = xacc;
        obj->iYAcc = yacc;
        obj->iXSpeed = xv;
        obj->iYSpeed = yv;
        obj->iXBounce = xbnc;
        obj->iYBounce = ybnc;
        obj->iMaxXSpeed = maxxv;
        obj->iMaxYSpeed = maxyv;
        obj->iXSlowDown = xslowdown;
        obj->iYSlowDown = yslowdown;
        obj->iSkipMove = skipmove;
        obj->iOptions = options;

        resultcode = GR_OK;
    }

	*result = resultcode;
	*oos = ios;
}


extern EXPORT_TO_CLEAN void
WinGetObjectRec (int id, OS ios, int *objtype, int *subtype, BOOL *active,
                 int *xpos, int *ypos, int *xsize, int *ysize,
                 int *xoffset, int *yoffset, int *spriteid,
                 int *displayoptions, int *ownbounds, int *bouncebounds,
                 int *collidebounds, int *forgetx, int *forgety, int *time,
                 int *layer, int *xacc, int *yacc, int *xv, int *yv,
                 int *xbnc, int *ybnc, int *maxxv, int *maxyv,
                 int *xslowdown, int *yslowdown, int *skipmove, int *options,
                 int *result, OS *oos)
{
    OBJECTREC *obj = GetObjectRec (id);
 	int resultcode = GR_NOT_FOUND;

    if (obj)
    {
        *objtype = obj->iObjType;
        *subtype = obj->iSubType;
        *active = obj->bActive;
        *xpos = obj->iXPos;
        *ypos = obj->iYPos;
        *xsize = obj->iXSize;
        *ysize = obj->iYSize;
		*xoffset = obj->iXOffset;
        *yoffset = obj->iYOffset;
        *spriteid = (obj->iSpriteID & 0xFFFF) | (obj->iSpriteIndex << 16);
        *displayoptions = obj->iDisplayOptions;
        *ownbounds = obj->iOwnBounds;
        *bouncebounds = obj->iBounceBounds;
        *collidebounds = obj->iCollideBounds;
        *forgetx = obj->iForgetX;
        *forgety = obj->iForgetY;
        *time = obj->iTimeCounter;
        *layer = obj->iLayer;
        *xacc = obj->iXAcc;
        *yacc = obj->iYAcc;
        *xv = obj->iXSpeed;
        *yv = obj->iYSpeed;
        *xbnc = obj->iXBounce;
        *ybnc = obj->iYBounce;
        *maxxv = obj->iMaxXSpeed;
        *maxyv = obj->iMaxYSpeed;
        *xslowdown = obj->iXSlowDown;
        *yslowdown = obj->iYSlowDown;
        *skipmove = obj->iSkipMove;
        *options = obj->iOptions;

        resultcode = GR_OK;
    }
 	*result = resultcode;
	*oos = ios;
}


extern EXPORT_TO_CLEAN void
WinShowStatistic (int x, int y, CLEAN_STRING format, int value,
                  int r, int g, int b,
                  CLEAN_STRING font, int fontsize, BOOL bold, BOOL italic,
                  BOOL shadow, int shadowx, int shadowy,
                  int shadowr, int shadowg, int shadowb,
                  int options,
                  OS ios, int *result, OS *oos)
{
    HDC hdc;
    char Buffer[1024];
    RECT rect = {0, 0, ScreenWidth, ScreenHeight};

    if (OSGetGameWindowHDC (&hdc))
    {
     	static HFONT hfont = NULL;
        SIZE size;

        if (hfont)
            DeleteObject (hfont);
        hfont = CreateFont (fontsize, 0, 0, 0,
                            bold? FW_BOLD : FW_NORMAL,
                            italic,
                            FALSE,
                            FALSE,
                            ANSI_CHARSET,
                            OUT_DEFAULT_PRECIS,
                            CLIP_DEFAULT_PRECIS,
                            DEFAULT_QUALITY,
                            VARIABLE_PITCH,
                            cstring (font));
        if (hfont)
        {
	        SelectObject (hdc, hfont);
	
	     	if (value == NOTHING)
				wsprintf (Buffer, cstring (format));
	        else
				wsprintf (Buffer, cstring (format), value);
	
			SetBkColor (hdc, 0);
	        SetBkMode (hdc, TRANSPARENT);

            GetTextExtentPoint (hdc, Buffer, lstrlen (Buffer), &size);

            if (options & SO_X_FROM_SCREEN_CENTER)
              	x += ScreenWidth / 2;
            if (options & SO_X_CENTERED)
                x -= size.cx / 2;
            if (x < 0)
                x = ScreenWidth - size.cx + x;

            if (options & SO_Y_FROM_SCREEN_CENTER)
              	y += ScreenHeight / 2;
            if (options & SO_Y_CENTERED)
                y -= size.cy / 2;
            if (y < 0)
                y = ScreenHeight - size.cy + y;

            if (shadow)
            {
				SetTextColor (hdc, RGB (shadowr, shadowg, shadowb));
		        ExtTextOut (hdc, x + shadowx, y + shadowy, ETO_CLIPPED,
                            &rect, Buffer, lstrlen (Buffer), NULL);
            }

			SetTextColor (hdc, RGB (r, g, b));
	        ExtTextOut (hdc, x, y, ETO_CLIPPED, &rect,
	                    Buffer, lstrlen (Buffer), NULL);

        }
        OSReleaseGameWindowHandle (hdc);
    }


 	*result = GR_OK;
	*oos = ios;
}

extern EXPORT_TO_CLEAN
void WinPlayMusic (CLEAN_STRING midifile, BOOL restart, OS ios,
                   int *result, OS *oos)
{
    int resultcode = GR_OS_ERROR;

    if (OSPlayMusic (cstring (midifile), restart))
        resultcode = GR_OK;

    *result = resultcode;
	*oos = ios;
}

extern EXPORT_TO_CLEAN
void WinStopMusic (OS ios, int *result, OS *oos)
{
    *result = OSStopMusic ();
	*oos = ios;
}

/* set extra level options */
extern EXPORT_TO_CLEAN
void WinGameLevelOptions (int r, int g, int b,
                             BOOL esc, BOOL debug,
                             BOOL fadein, BOOL fadeout,
                             OS ios, int *result, OS *oos)
{
    bCheckEscape = esc;
    DebugScroll = debug;
    bFillBackground = FALSE;
    if (r * g * b >= 0)
    {
        bFillBackground = TRUE;
    	FillBackgroundRGB = RGB (r, g, b);
    }
    bFadeIn = fadein;
    bFadeOut = fadeout;
    *result = GR_OK;
	*oos = ios;
}

/* init a sound sample or clear all samples if id = -1 */
extern EXPORT_TO_CLEAN
void WinInitSoundSample (int id, CLEAN_STRING name, int buffers,
                         OS ios, int *result, OS *oos)
{
    *result = GR_OK;
    if (id == -1)
    {
        OSFreeSoundSamples ();
    }
    else
    {
        if (!OSInitSoundSample (id, cstring (name), buffers))
            *result = GR_OS_ERROR;
    }
	*oos = ios;
}


extern EXPORT_TO_CLEAN
void WinGetBoundMap (int x, int y, OS ios,
                     int *mapvalue, int *result, OS *oos)
{
    int mappos;

    *result = GR_FAILED;
    *oos = ios;

    *mapvalue = -1;

    if (!(sBoundMap))
        return;
    if ((x < 0) || (y < 0))
      	return;
    if ((x > BoundMapWidth - 1) || (y > BoundMapHeight - 1))
        return;

    mappos = (y * BoundMapWidth + x) * sizeof (int);
    *mapvalue = (*(int *) &sBoundMap[mappos]);
}

extern EXPORT_TO_CLEAN
void WinSetBoundMap (int x, int y, int newvalue, OS ios,
                     int *result, OS *oos)
{
    int mappos;

    *result = GR_FAILED;
    *oos = ios;

    if (!(sBoundMap))
        return;
    if ((x < 0) || (y < 0))
      	return;
    if ((x > BoundMapWidth - 1) || (y > BoundMapHeight - 1))
        return;

    mappos = (y * BoundMapWidth + x) * sizeof (int);
    (*(int *) &sBoundMap[mappos]) = newvalue;
}

/*
extern EXPORT_TO_CLEAN
void WinPlaySoundSample (int id, int volume, int pan, int freq, int delay,
                         OS ios, int *result, OS *oos)
{

    if (delay == 0)
    {
	    SOUNDSAMPLEINFO *ssi = GetSoundSampleInfo (id);
	
	    if (ssi)
	    {
		    IDirectSoundBuffer *pDSB = SndObjGetFreeBuffer (ssi->hsoSoundBuffer);
		
		 	if (bSoundEnabled)
		    {
		     	if (pDSB)
		        {
		            IDirectSoundBuffer_SetVolume (pDSB, (LONG) volume);
		            IDirectSoundBuffer_SetPan (pDSB, (LONG) pan);
		            IDirectSoundBuffer_SetFrequency (pDSB, (LONG) freq);
		            IDirectSoundBuffer_Play (pDSB, 0, 0, 0);
		        }
		    }
	    }
    }
    else
    {
        ScheduleEvent (EV_PLAY_SOUND, id, volume, pan, freq, 0, delay);
    }

    *result = GR_OK;
	*oos = ios;
}
*/


