aboutsummaryrefslogtreecommitdiff
path: root/engines/agi/view.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agi/view.cpp')
-rw-r--r--engines/agi/view.cpp713
1 files changed, 475 insertions, 238 deletions
diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp
index 6a274a1b73..66508a6c27 100644
--- a/engines/agi/view.cpp
+++ b/engines/agi/view.cpp
@@ -21,107 +21,60 @@
*/
#include "agi/agi.h"
+#include "agi/graphics.h"
#include "agi/sprite.h"
namespace Agi {
-void AgiEngine::lSetCel(VtEntry *v, int n) {
- ViewLoop *currentVl;
- ViewCel *currentVc;
+void AgiEngine::updateView(ScreenObjEntry *screenObj) {
+ int16 celNr, lastCelNr;
- v->currentCel = n;
-
- currentVl = &_game.views[v->currentView].loop[v->currentLoop];
-
- // Added by Amit Vainsencher <amitv@subdimension.com> to prevent
- // crash in KQ1 -- not in the Sierra interpreter
- if (currentVl->numCels == 0)
- return;
-
- // WORKAROUND: This is a very nasty hack to fix a bug in the KQ4 introduction
- // In its original form, it caused a lot of regressions, including KQ4 bugs and crashes
- // Refer to Sarien bug #588899 for the original issue
- // Modifying this workaround to only work for a specific view in the KQ4 intro fixes several
- // ScummVM bugs. Refer to bugs #1660486, #1660169, #1660192, #1660162 and #1660354
- // FIXME: Remove this workaround and investigate the reason for the erroneous actor behavior
- // in the KQ4 introduction
- // It seems there's either a bug with KQ4's logic script 120 (the intro script)
- // or flag 64 is not set correctly, which causes the erroneous behavior from the actors
- if (getGameID() == GID_KQ4 && !(v->flags & fUpdate) && (v->currentView == 172))
- return;
-
- currentVc = &currentVl->cel[n];
- v->celData = currentVc;
- v->xSize = currentVc->width;
- v->ySize = currentVc->height;
-}
-
-void AgiEngine::lSetLoop(VtEntry *v, int n) {
- ViewLoop *currentVl;
- debugC(7, kDebugLevelResources, "vt entry #%d, loop = %d", v->entry, n);
-
- // Added to avoid crash when leaving the arcade machine in MH1
- // -- not in AGI 2.917
- if (n >= v->numLoops)
- n = 0;
-
- v->currentLoop = n;
- currentVl = &_game.views[v->currentView].loop[v->currentLoop];
-
- v->numCels = currentVl->numCels;
- if (v->currentCel >= v->numCels)
- v->currentCel = 0;
-
- v->loopData = &_game.views[v->currentView].loop[n];
-}
-
-void AgiEngine::updateView(VtEntry *v) {
- int cel, lastCel;
-
- if (v->flags & fDontupdate) {
- v->flags &= ~fDontupdate;
+ if (screenObj->flags & fDontupdate) {
+ screenObj->flags &= ~fDontupdate;
return;
}
- cel = v->currentCel;
- lastCel = v->numCels - 1;
+ celNr = screenObj->currentCelNr;
+ lastCelNr = screenObj->celCount - 1;
- switch (v->cycle) {
+ switch (screenObj->cycle) {
case kCycleNormal:
- if (++cel > lastCel)
- cel = 0;
+ celNr++;
+ if (celNr > lastCelNr)
+ celNr = 0;
break;
case kCycleEndOfLoop:
- if (cel < lastCel) {
- debugC(5, kDebugLevelResources, "cel %d (last = %d)", cel + 1, lastCel);
- if (++cel != lastCel)
+ if (celNr < lastCelNr) {
+ debugC(5, kDebugLevelResources, "cel %d (last = %d)", celNr + 1, lastCelNr);
+ if (++celNr != lastCelNr)
break;
}
- setflag(v->parm1, true);
- v->flags &= ~fCycling;
- v->direction = 0;
- v->cycle = kCycleNormal;
+ setflag(screenObj->loop_flag, true);
+ screenObj->flags &= ~fCycling;
+ screenObj->direction = 0;
+ screenObj->cycle = kCycleNormal;
break;
case kCycleRevLoop:
- if (cel) {
- if (--cel)
+ if (celNr) {
+ celNr--;
+ if (celNr)
break;
}
- setflag(v->parm1, true);
- v->flags &= ~fCycling;
- v->direction = 0;
- v->cycle = kCycleNormal;
+ setflag(screenObj->loop_flag, true);
+ screenObj->flags &= ~fCycling;
+ screenObj->direction = 0;
+ screenObj->cycle = kCycleNormal;
break;
case kCycleReverse:
- if (cel == 0) {
- cel = lastCel;
+ if (celNr == 0) {
+ celNr = lastCelNr;
} else {
- cel--;
+ celNr--;
}
break;
}
- setCel(v, cel);
+ setCel(screenObj, celNr);
}
/*
@@ -134,191 +87,475 @@ void AgiEngine::updateView(VtEntry *v) {
* and fills the corresponding views array element.
* @param n number of view resource to decode
*/
-int AgiEngine::decodeView(int n) {
- int loop, cel;
- uint8 *v, *lptr;
- uint16 lofs, cofs;
- ViewLoop *vl;
- ViewCel *vc;
+int AgiEngine::decodeView(byte *resourceData, uint16 resourceSize, int16 viewNr) {
+ AgiView *viewData = &_game.views[viewNr];
+ uint16 headerId = 0;
+ byte headerStepSize = 0;
+ byte headerCycleTime = 0;
+ byte headerLoopCount = 0;
+ uint16 headerDescriptionOffset = 0;
+ bool isAGI256Data = false;
+
+ AgiViewLoop *loopData = nullptr;
+ uint16 loopOffset = 0;
+ byte loopHeaderCelCount = 0;
+
+ AgiViewCel *celData = nullptr;
+ uint16 celOffset = 0;
+ byte celHeaderWidth = 0;
+ byte celHeaderHeight = 0;
+ byte celHeaderTransparencyMirror = 0;
+ byte celHeaderClearKey = 0;
+ bool celHeaderMirrored = false;
+ byte celHeaderMirrorLoop = 0;
+
+ byte *celCompressedData = nullptr;
+ uint16 celCompressedSize = 0;
+// byte *rawBitmap = nullptr;
+
+ debugC(5, kDebugLevelResources, "decode_view(%d)", viewNr);
+
+ if (resourceSize < 5)
+ error("unexpected end of view data for view %d", viewNr);
+
+ headerId = READ_LE_UINT16(resourceData);
+ if (getVersion() < 0x2000) {
+ headerStepSize = resourceData[0];
+ headerCycleTime = resourceData[1];
+ }
+ headerLoopCount = resourceData[2];
+ headerDescriptionOffset = READ_LE_UINT16(resourceData + 3);
+
+ if (headerId == 0xF00F)
+ isAGI256Data = true; // AGI 256-2 view detected, 256 color view
+
+ viewData->headerStepSize = headerStepSize;
+ viewData->headerCycleTime = headerCycleTime;
+ viewData->loopCount = headerLoopCount;
+ viewData->description = nullptr;
+ viewData->loop = nullptr;
+
+ if (headerDescriptionOffset) {
+ // Figure out length of description
+ uint16 descriptionPos = headerDescriptionOffset;
+ uint16 descriptionLen = 0;
+ while (descriptionPos < resourceSize) {
+ if (resourceData[descriptionPos] == 0)
+ break;
+ descriptionPos++;
+ descriptionLen++;
+ }
+ // Allocate memory for description
+ viewData->description = new byte[descriptionLen + 1];
+ // Copy description over
+ memcpy(viewData->description, resourceData + headerDescriptionOffset, descriptionLen);
+ viewData->description[descriptionLen] = 0; // set terminator
+ }
+
+ if (!viewData->loopCount) // no loops, exit now
+ return errOK;
+
+ // Check, if at least the loop-offsets are available
+ if (resourceSize < 5 + (headerLoopCount * 2))
+ error("unexpected end of view data for view %d", viewNr);
+
+ // Allocate space for loop-information
+ loopData = new AgiViewLoop[headerLoopCount];
+ viewData->loop = loopData;
+
+ for (int16 loopNr = 0; loopNr < headerLoopCount; loopNr++) {
+ loopOffset = READ_LE_UINT16(resourceData + 5 + (loopNr * 2));
+
+ // Check, if at least the loop-header is available
+ if (resourceSize < (loopOffset + 1))
+ error("unexpected end of view data for view %d", viewNr);
+
+ // loop-header:
+ // celCount:BYTE
+ // relativeCelOffset[0]:WORD
+ // relativeCelOffset[1]:WORD
+ // etc.
+ loopHeaderCelCount = resourceData[loopOffset];
+
+ loopData->celCount = loopHeaderCelCount;
+ loopData->cel = nullptr;
+
+ // Check, if at least the cel-offsets for current loop are available
+ if (resourceSize < (loopOffset + 1 + (loopHeaderCelCount * 2)))
+ error("unexpected end of view data for view %d", viewNr);
+
+ if (loopHeaderCelCount) {
+ // Allocate space for cel-information of current loop
+ celData = new AgiViewCel[loopHeaderCelCount];
+ loopData->cel = celData;
+
+ for (int16 celNr = 0; celNr < loopHeaderCelCount; celNr++) {
+ celOffset = READ_LE_UINT16(resourceData + loopOffset + 1 + (celNr * 2));
+ celOffset += loopOffset; // cel offset is relative to loop offset, so adjust accordingly
+
+ // Check, if at least the cel-header is available
+ if (resourceSize < (celOffset + 3))
+ error("unexpected end of view data for view %d", viewNr);
+
+ // cel-header:
+ // width:BYTE
+ // height:BYTE
+ // Transparency + Mirroring:BYTE
+ // celData follows
+ celHeaderWidth = resourceData[celOffset + 0];
+ celHeaderHeight = resourceData[celOffset + 1];
+ celHeaderTransparencyMirror = resourceData[celOffset + 2];
+
+ if (!isAGI256Data) {
+ // regular AGI view data
+ // Transparency + Mirroring byte is as follows:
+ // Bit 0-3 - clear key
+ // Bit 4-6 - original loop, that is not supposed to be mirrored in any case
+ // Bit 7 - apply mirroring
+ celHeaderClearKey = celHeaderTransparencyMirror & 0x0F; // bit 0-3 is the clear key
+ celHeaderMirrored = false;
+ if (celHeaderTransparencyMirror & 0x80) {
+ // mirror bit is set
+ celHeaderMirrorLoop = (celHeaderTransparencyMirror >> 4) & 0x07;
+ if (celHeaderMirrorLoop != loopNr) {
+ // only set to mirror'd in case we are not the original loop
+ celHeaderMirrored = true;
+ }
+ }
+ } else {
+ // AGI256-2 view data
+ celHeaderClearKey = celHeaderTransparencyMirror; // full 8 bits for clear key
+ celHeaderMirrored = false;
+ }
+
+ celData->width = celHeaderWidth;
+ celData->height = celHeaderHeight;
+ celData->clearKey = celHeaderClearKey;
+ celData->mirrored = celHeaderMirrored;
+
+ // Now decompress cel-data
+ if ((celHeaderWidth == 0) && (celHeaderHeight == 0))
+ error("view cel is 0x0");
+
+ celCompressedData = resourceData + celOffset + 3;
+ celCompressedSize = resourceSize - (celOffset + 3);
+
+ if (!isAGI256Data) {
+ unpackViewCelData(celData, celCompressedData, celCompressedSize);
+ } else {
+ unpackViewCelDataAGI256(celData, celCompressedData, celCompressedSize);
+ }
+ celData++;
+ }
+ }
+
+ loopData++;
+ }
+
+ return errOK;
+}
- debugC(5, kDebugLevelResources, "decode_view(%d)", n);
- v = _game.views[n].rdata;
+void AgiEngine::unpackViewCelData(AgiViewCel *celData, byte *compressedData, uint16 compressedSize) {
+ byte *rawBitmap = new byte[celData->width * celData->height];
+ int16 remainingHeight = celData->height;
+ int16 remainingWidth = celData->width;
+ bool isMirrored = celData->mirrored;
+ byte curByte;
+ byte curColor;
+ byte curChunkLen;
+ int16 adjustPreChangeSingle = 0;
+ int16 adjustAfterChangeSingle = +1;
+
+ celData->rawBitmap = rawBitmap;
+
+ if (isMirrored) {
+ adjustPreChangeSingle = -1;
+ adjustAfterChangeSingle = 0;
+ rawBitmap += celData->width;
+ }
- assert(v != NULL);
+ while (remainingHeight) {
+ if (!compressedSize)
+ error("unexpected end of data, while unpacking AGI256 data");
- _game.views[n].agi256_2 = (READ_LE_UINT16(v) == 0xf00f); // Detect AGI256-2 views by their header bytes
- _game.views[n].descr = READ_LE_UINT16(v + 3) ? (char *)(v + READ_LE_UINT16(v + 3)) : (char *)(v + 3);
+ curByte = *compressedData++;
+ compressedSize--;
- // if no loops exist, return!
- if ((_game.views[n].numLoops = *(v + 2)) == 0)
- return errNoLoopsInView;
+ if (curByte == 0) {
+ curColor = celData->clearKey;
+ curChunkLen = remainingWidth;
+ } else {
+ curColor = curByte >> 4;
+ curChunkLen = curByte & 0x0F;
+ if (curChunkLen > remainingWidth)
+ error("invalid chunk in view data");
+ }
- // allocate memory for all views
- _game.views[n].loop = (ViewLoop *)calloc(_game.views[n].numLoops, sizeof(ViewLoop));
+ switch (curChunkLen) {
+ case 0:
+ break;
+ case 1:
+ rawBitmap += adjustPreChangeSingle;
+ *rawBitmap = curColor;
+ rawBitmap += adjustAfterChangeSingle;
+ break;
+ default:
+ if (isMirrored)
+ rawBitmap -= curChunkLen;
+ memset(rawBitmap, curColor, curChunkLen);
+ if (!isMirrored)
+ rawBitmap += curChunkLen;
+ break;
+ }
- if (_game.views[n].loop == NULL)
- return errNotEnoughMemory;
+ remainingWidth -= curChunkLen;
+
+ if (curByte == 0) {
+ remainingWidth = celData->width;
+ remainingHeight--;
- // decode all of the loops in this view
- lptr = v + 5; // first loop address
+ if (isMirrored)
+ rawBitmap += celData->width * 2;
+ }
+ }
- for (loop = 0; loop < _game.views[n].numLoops; loop++, lptr += 2) {
- lofs = READ_LE_UINT16(lptr); // loop header offset
- vl = &_game.views[n].loop[loop]; // the loop struct
+ // for CGA rendering, apply dithering
+ switch (_renderMode) {
+ case RENDERMODE_CGA: {
+ uint16 totalPixels = celData->width * celData->height;
- vl->numCels = *(v + lofs);
- debugC(6, kDebugLevelResources, "view %d, num_cels = %d", n, vl->numCels);
- vl->cel = (ViewCel *)calloc(vl->numCels, sizeof(ViewCel));
+ // dither clear key
+ celData->clearKey = _gfx->getCGAMixtureColor(celData->clearKey);
- if (vl->cel == NULL) {
- free(_game.views[n].loop);
- _game.views[n].numLoops = 0;
- return errNotEnoughMemory;
+ rawBitmap = celData->rawBitmap;
+ for (uint16 pixelNr = 0; pixelNr < totalPixels; pixelNr++) {
+ curColor = *rawBitmap;
+ *rawBitmap = _gfx->getCGAMixtureColor(curColor);
+ rawBitmap++;
}
+ break;
+ }
+ default:
+ break;
+ }
+}
- // decode the cells
- for (cel = 0; cel < vl->numCels; cel++) {
- cofs = lofs + READ_LE_UINT16(v + lofs + 1 + (cel * 2));
- vc = &vl->cel[cel];
-
- vc->width = *(v + cofs);
- vc->height = *(v + cofs + 1);
-
- if (!_game.views[n].agi256_2) {
- vc->transparency = *(v + cofs + 2) & 0xf;
- vc->mirrorLoop = (*(v + cofs + 2) >> 4) & 0x7;
- vc->mirror = (*(v + cofs + 2) >> 7) & 0x1;
- } else {
- // Mirroring is disabled for AGI256-2 views because
- // AGI256-2 uses whole 8 bits for the transparency variable.
- vc->transparency = *(v + cofs + 2);
- vc->mirrorLoop = 0;
- vc->mirror = 0;
- }
+void AgiEngine::unpackViewCelDataAGI256(AgiViewCel *celData, byte *compressedData, uint16 compressedSize) {
+ byte *rawBitmap = new byte[celData->width * celData->height];
+ int16 remainingHeight = celData->height;
+ int16 remainingWidth = celData->width;
+ byte curByte;
- // skip over width/height/trans|mirror data
- cofs += 3;
+ celData->rawBitmap = rawBitmap;
- vc->data = v + cofs;
+ while (remainingHeight) {
+ if (!compressedSize)
+ error("unexpected end of data, while unpacking AGI256 data");
- // If mirror_loop is pointing to the current loop,
- // then this is the original.
- if (vc->mirrorLoop == loop)
- vc->mirror = 0;
- } // cel
- } // loop
+ curByte = *compressedData++;
+ compressedSize--;
- return errOK;
+ if (curByte == 0) {
+ // Go to next vertical position
+ if (remainingWidth) {
+ // fill remaining bytes with clear key
+ memset(rawBitmap, celData->clearKey, remainingWidth);
+ rawBitmap += remainingWidth;
+ remainingWidth = 0;
+ }
+ } else {
+ *rawBitmap = curByte;
+ rawBitmap++;
+ }
+
+ if (curByte == 0) {
+ remainingWidth = celData->width;
+ remainingHeight--;
+ }
+ }
}
/**
* Unloads all data in a view resource
* @param n number of view resource
*/
-void AgiEngine::unloadView(int n) {
- int x;
+void AgiEngine::unloadView(int16 viewNr) {
+ AgiView *viewData = &_game.views[viewNr];
- debugC(5, kDebugLevelResources, "discard view %d", n);
- if (~_game.dirView[n].flags & RES_LOADED)
+ debugC(5, kDebugLevelResources, "discard view %d", viewNr);
+ if (!(_game.dirView[viewNr].flags & RES_LOADED))
return;
// Rebuild sprite list, see Sarien bug #779302
- _sprites->eraseBoth();
- _sprites->blitBoth();
- _sprites->commitBoth();
+ _sprites->eraseSprites();
- // free all the loops
- for (x = 0; x < _game.views[n].numLoops; x++)
- free(_game.views[n].loop[x].cel);
+ // free data
+ for (int16 loopNr = 0; loopNr < viewData->loopCount; loopNr++) {
+ AgiViewLoop *loopData = &viewData->loop[loopNr];
+ for (int16 celNr = 0; celNr < loopData->celCount; celNr++) {
+ AgiViewCel *celData = &loopData->cel[celNr];
- free(_game.views[n].loop);
- free(_game.views[n].rdata);
+ delete[] celData->rawBitmap;
+ }
+ delete[] loopData->cel;
+ }
+ delete[] viewData->loop;
- _game.dirView[n].flags &= ~RES_LOADED;
-}
+ if (viewData->description)
+ delete[] viewData->description;
-/**
- * Set a view table entry to use the specified cel of the current loop.
- * @param v pointer to view table entry
- * @param n number of cel
- */
-void AgiEngine::setCel(VtEntry *v, int n) {
- assert(v->viewData != NULL);
- assert(v->numCels >= n);
+ viewData->headerCycleTime = 0;
+ viewData->headerStepSize = 0;
+ viewData->description = nullptr;
+ viewData->loop = nullptr;
+ viewData->loopCount = 0;
- lSetCel(v, n);
+ // Mark this view as not loaded anymore
+ _game.dirView[viewNr].flags &= ~RES_LOADED;
- // If position isn't appropriate, update it accordingly
- clipViewCoordinates(v);
+ _sprites->buildAllSpriteLists();
+ _sprites->drawAllSpriteLists();
}
/**
- * Restrict view table entry's position so it stays wholly inside the screen.
- * Also take horizon into account when clipping if not set to ignore it.
- * @param v pointer to view table entry
+ * Set a view table entry to use the specified view resource.
+ * @param screenObj pointer to screen object
+ * @param viewNr number of AGI view resource
*/
-void AgiEngine::clipViewCoordinates(VtEntry *v) {
- if (v->xPos + v->xSize > _WIDTH) {
- v->flags |= fUpdatePos;
- v->xPos = _WIDTH - v->xSize;
+void AgiEngine::setView(ScreenObjEntry *screenObj, int16 viewNr) {
+ screenObj->viewData = &_game.views[viewNr];
+ screenObj->currentViewNr = viewNr;
+ screenObj->loopCount = screenObj->viewData->loopCount;
+ screenObj->viewReplaced = true;
+
+ if (getVersion() < 0x2000) {
+ screenObj->stepSize = screenObj->viewData->headerStepSize;
+ screenObj->cycleTime = screenObj->viewData->headerCycleTime;
+ screenObj->cycleTimeCount = 0;
}
- if (v->yPos - v->ySize + 1 < 0) {
- v->flags |= fUpdatePos;
- v->yPos = v->ySize - 1;
+ if (screenObj->currentLoopNr >= screenObj->loopCount) {
+ setLoop(screenObj, 0);
+ } else {
+ setLoop(screenObj, screenObj->currentLoopNr);
}
- if (v->yPos <= _game.horizon && (~v->flags & fIgnoreHorizon)) {
- v->flags |= fUpdatePos;
- v->yPos = _game.horizon + 1;
+}
+
+/**
+ * Set a view table entry to use the specified loop of the current view.
+ * @param screenObj pointer to screen object
+ * @param loopNr number of loop
+ */
+void AgiEngine::setLoop(ScreenObjEntry *screenObj, int16 loopNr) {
+ assert(screenObj->viewData != NULL);
+
+ if (screenObj->loopCount == 0) {
+ warning("setLoop() called on screen object %d, which has no loops (view %d)", screenObj->objectNr, screenObj->currentViewNr);
+ return;
}
- if (getVersion() < 0x2000) {
- v->flags |= fDontupdate;
+ if (loopNr >= screenObj->loopCount) {
+ // requested loop not existant
+ // instead of error()ing out, we instead clip it
+ // At least required for possibly Manhunter 1 according to previous comment when leaving the arcade machine
+ // TODO: check MH1
+ int16 requestedLoopNr = loopNr;
+
+ loopNr = screenObj->loopCount - 1;
+
+ warning("Non-existant loop requested for screen object %d", screenObj->objectNr);
+ warning("view %d, requested loop %d -> clipped to loop %d", screenObj->currentViewNr, requestedLoopNr, loopNr);
}
+ AgiViewLoop *curViewLoop = &_game.views[screenObj->currentViewNr].loop[loopNr];
+
+ screenObj->currentLoopNr = loopNr;
+ screenObj->loopData = curViewLoop;
+ screenObj->celCount = curViewLoop->celCount;
+
+ if (screenObj->currentCelNr >= screenObj->celCount) {
+ setCel(screenObj, 0);
+ } else {
+ setCel(screenObj, screenObj->currentCelNr);
+ }
}
/**
- * Set a view table entry to use the specified loop of the current view.
- * @param v pointer to view table entry
- * @param n number of loop
+ * Set a view table entry to use the specified cel of the current loop.
+ * @param screenObj pointer to screen object
+ * @param celNr number of cel
*/
-void AgiEngine::setLoop(VtEntry *v, int n) {
- assert(v->viewData != NULL);
- assert(v->numLoops >= n);
- lSetLoop(v, n);
- setCel(v, v->currentCel);
+void AgiEngine::setCel(ScreenObjEntry *screenObj, int16 celNr) {
+ assert(screenObj->viewData != NULL);
+
+ AgiViewLoop *curViewLoop = &_game.views[screenObj->currentViewNr].loop[screenObj->currentLoopNr];
+
+ // Added by Amit Vainsencher <amitv@subdimension.com> to prevent
+ // crash in KQ1 -- not in the Sierra interpreter
+ if (curViewLoop->celCount == 0) {
+ warning("setCel() called on screen object %d, which has no cels (view %d)", screenObj->objectNr, screenObj->currentViewNr);
+ return;
+ }
+
+ if (celNr >= screenObj->celCount) {
+ // requested cel not existant
+ // instead of error()ing out, we instead clip it
+ // At least required for King's Quest 3 on Apple IIgs - walking the planks death cutscene
+ // see bug #5832, which is a game bug!
+ int16 requestedCelNr = celNr;
+
+ celNr = screenObj->celCount - 1;
+
+ warning("Non-existant cel requested for screen object %d", screenObj->objectNr);
+ warning("view %d, loop %d, requested cel %d -> clipped to cel %d", screenObj->currentViewNr, screenObj->currentLoopNr, requestedCelNr, celNr);
+ }
+
+ screenObj->currentCelNr = celNr;
+
+ AgiViewCel *curViewCel;
+ curViewCel = &curViewLoop->cel[celNr];
+ screenObj->celData = curViewCel;
+ screenObj->xSize = curViewCel->width;
+ screenObj->ySize = curViewCel->height;
+
+ // If position isn't appropriate, update it accordingly
+ clipViewCoordinates(screenObj);
}
/**
- * Set a view table entry to use the specified view resource.
+ * Restrict view table entry's position so it stays wholly inside the screen.
+ * Also take horizon into account when clipping if not set to ignore it.
* @param v pointer to view table entry
- * @param n number of AGI view resource
*/
-void AgiEngine::setView(VtEntry *v, int n) {
- v->viewData = &_game.views[n];
- v->currentView = n;
- v->numLoops = v->viewData->numLoops;
- v->viewReplaced = true;
+void AgiEngine::clipViewCoordinates(ScreenObjEntry *screenObj) {
+ if (screenObj->xPos + screenObj->xSize > SCRIPT_WIDTH) {
+ screenObj->flags |= fUpdatePos;
+ screenObj->xPos = SCRIPT_WIDTH - screenObj->xSize;
+ }
+ if (screenObj->yPos - screenObj->ySize + 1 < 0) {
+ screenObj->flags |= fUpdatePos;
+ screenObj->yPos = screenObj->ySize - 1;
+ }
+ if (screenObj->yPos <= _game.horizon && (~screenObj->flags & fIgnoreHorizon)) {
+ screenObj->flags |= fUpdatePos;
+ screenObj->yPos = _game.horizon + 1;
+ }
if (getVersion() < 0x2000) {
- v->stepSize = v->viewData->rdata[0];
- v->cycleTime = v->viewData->rdata[1];
- v->cycleTimeCount = 0;
+ screenObj->flags |= fDontupdate;
}
- setLoop(v, v->currentLoop >= v->numLoops ? 0 : v->currentLoop);
+
}
/**
* Set the view table entry as updating.
* @param v pointer to view table entry
*/
-void AgiEngine::startUpdate(VtEntry *v) {
+void AgiEngine::startUpdate(ScreenObjEntry *v) {
if (~v->flags & fUpdate) {
- _sprites->eraseBoth();
-
+ _sprites->eraseSprites();
v->flags |= fUpdate;
- _sprites->blitBoth();
- _sprites->commitBoth();
+ _sprites->buildAllSpriteLists();
+ _sprites->drawAllSpriteLists();
}
}
@@ -326,13 +563,12 @@ void AgiEngine::startUpdate(VtEntry *v) {
* Set the view table entry as non-updating.
* @param v pointer to view table entry
*/
-void AgiEngine::stopUpdate(VtEntry *v) {
- if (v->flags & fUpdate) {
- _sprites->eraseBoth();
-
- v->flags &= ~fUpdate;
- _sprites->blitBoth();
- _sprites->commitBoth();
+void AgiEngine::stopUpdate(ScreenObjEntry *viewPtr) {
+ if (viewPtr->flags & fUpdate) {
+ _sprites->eraseSprites();
+ viewPtr->flags &= ~fUpdate;
+ _sprites->buildAllSpriteLists();
+ _sprites->drawAllSpriteLists();
}
}
@@ -351,67 +587,68 @@ static int loopTable4[] = {
* This function is called at the end of each interpreter cycle
* to update the view table entries and blit the sprites.
*/
-void AgiEngine::updateViewtable() {
- VtEntry *v;
- int i, loop;
+void AgiEngine::updateScreenObjTable() {
+ ScreenObjEntry *screenObj;
+ int16 changeCount, loopNr;
- i = 0;
- for (v = _game.viewTable; v < &_game.viewTable[MAX_VIEWTABLE]; v++) {
- if ((v->flags & (fAnimated | fUpdate | fDrawn)) != (fAnimated | fUpdate | fDrawn)) {
+ changeCount = 0;
+ for (screenObj = _game.screenObjTable; screenObj < &_game.screenObjTable[SCREENOBJECTS_MAX]; screenObj++) {
+ if ((screenObj->flags & (fAnimated | fUpdate | fDrawn)) != (fAnimated | fUpdate | fDrawn)) {
continue;
}
- i++;
+ changeCount++;
- loop = 4;
- if (~v->flags & fFixLoop) {
- switch (v->numLoops) {
+ loopNr = 4;
+ if (!(screenObj->flags & fFixLoop)) {
+ switch (screenObj->loopCount) {
case 2:
case 3:
- loop = loopTable2[v->direction];
+ loopNr = loopTable2[screenObj->direction];
break;
case 4:
- loop = loopTable4[v->direction];
+ loopNr = loopTable4[screenObj->direction];
break;
default:
// for KQ4
if (getVersion() == 0x3086 || getGameID() == GID_KQ4)
- loop = loopTable4[v->direction];
+ loopNr = loopTable4[screenObj->direction];
break;
}
}
// AGI 2.272 (ddp, xmas) doesn't test step_time_count!
- if (loop != 4 && loop != v->currentLoop) {
+ if (loopNr != 4 && loopNr != screenObj->currentLoopNr) {
if (getVersion() <= 0x2272 ||
- v->stepTimeCount == 1) {
- setLoop(v, loop);
+ screenObj->stepTimeCount == 1) {
+ setLoop(screenObj, loopNr);
}
}
- if (~v->flags & fCycling)
- continue;
-
- if (v->cycleTimeCount == 0)
- continue;
-
- if (--v->cycleTimeCount == 0) {
- updateView(v);
- v->cycleTimeCount = v->cycleTime;
+ if (screenObj->flags & fCycling) {
+ if (screenObj->cycleTimeCount) {
+ screenObj->cycleTimeCount--;
+ if (screenObj->cycleTimeCount == 0) {
+ updateView(screenObj);
+ screenObj->cycleTimeCount = screenObj->cycleTime;
+ }
+ }
}
}
- if (i) {
- _sprites->eraseUpdSprites();
+ if (changeCount) {
+ _sprites->eraseRegularSprites();
updatePosition();
- _sprites->blitUpdSprites();
- _sprites->commitUpdSprites();
- _game.viewTable[0].flags &= ~(fOnWater | fOnLand);
+ _sprites->buildRegularSpriteList();
+ _sprites->drawRegularSpriteList();
+ _sprites->showRegularSpriteList();
+
+ _game.screenObjTable[SCREENOBJECTS_EGO_ENTRY].flags &= ~(fOnWater | fOnLand);
}
}
-bool AgiEngine::isEgoView(const VtEntry* v) {
- return v == _game.viewTable;
+bool AgiEngine::isEgoView(const ScreenObjEntry* screenObj) {
+ return screenObj == _game.screenObjTable;
}
} // End of namespace Agi