diff options
Diffstat (limited to 'engines/agi/view.cpp')
-rw-r--r-- | engines/agi/view.cpp | 748 |
1 files changed, 511 insertions, 237 deletions
diff --git a/engines/agi/view.cpp b/engines/agi/view.cpp index 6a274a1b73..a13e40e60d 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 = ¤tVl->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,513 @@ 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; + + 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 + } - debugC(5, kDebugLevelResources, "decode_view(%d)", n); - v = _game.views[n].rdata; + 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 (celCompressedSize == 0) + error("compressed size of loop within view %d is 0 bytes", viewNr); + + if (!isAGI256Data) { + unpackViewCelData(celData, celCompressedData, celCompressedSize); + } else { + unpackViewCelDataAGI256(celData, celCompressedData, celCompressedSize); + } + celData++; + } + } - assert(v != NULL); + loopData++; + } - _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); + return errOK; +} - // if no loops exist, return! - if ((_game.views[n].numLoops = *(v + 2)) == 0) - return errNoLoopsInView; +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; + } - // allocate memory for all views - _game.views[n].loop = (ViewLoop *)calloc(_game.views[n].numLoops, sizeof(ViewLoop)); + while (remainingHeight) { + if (!compressedSize) + error("unexpected end of data, while unpacking AGI256 data"); - if (_game.views[n].loop == NULL) - return errNotEnoughMemory; + curByte = *compressedData++; + compressedSize--; - // decode all of the loops in this view - lptr = v + 5; // first loop address + if (curByte == 0) { + curColor = celData->clearKey; + curChunkLen = remainingWidth; + } else { + curColor = curByte >> 4; + curChunkLen = curByte & 0x0F; + if (curChunkLen > remainingWidth) + error("invalid chunk in view data"); + } + + 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; + } - 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 + remainingWidth -= curChunkLen; - vl->numCels = *(v + lofs); - debugC(6, kDebugLevelResources, "view %d, num_cels = %d", n, vl->numCels); - vl->cel = (ViewCel *)calloc(vl->numCels, sizeof(ViewCel)); + if (curByte == 0) { + remainingWidth = celData->width; + remainingHeight--; - if (vl->cel == NULL) { - free(_game.views[n].loop); - _game.views[n].numLoops = 0; - return errNotEnoughMemory; + if (isMirrored) + rawBitmap += celData->width * 2; } + } - // 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; - } + // for CGA rendering, apply dithering + switch (_renderMode) { + case Common::kRenderCGA: { + uint16 totalPixels = celData->width * celData->height; + + // dither clear key + celData->clearKey = _gfx->getCGAMixtureColor(celData->clearKey); + + rawBitmap = celData->rawBitmap; + for (uint16 pixelNr = 0; pixelNr < totalPixels; pixelNr++) { + curColor = *rawBitmap; + *rawBitmap = _gfx->getCGAMixtureColor(curColor); + rawBitmap++; + } + break; + } + default: + break; + } +} - // skip over width/height/trans|mirror data - cofs += 3; +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; - vc->data = v + cofs; + celData->rawBitmap = rawBitmap; - // If mirror_loop is pointing to the current loop, - // then this is the original. - if (vc->mirrorLoop == loop) - vc->mirror = 0; - } // cel - } // loop + while (remainingHeight) { + if (!compressedSize) + error("unexpected end of data, while unpacking AGI256 view"); - return errOK; + curByte = *compressedData++; + compressedSize--; + + 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 { + if (!remainingWidth) { + error("broken view data, while unpacking AGI256 view"); + break; + } + *rawBitmap = curByte; + rawBitmap++; + remainingWidth--; + } + + 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 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]; + + delete[] celData->rawBitmap; + } + delete[] loopData->cel; + } + delete[] viewData->loop; + + if (viewData->description) + delete[] viewData->description; - // free all the loops - for (x = 0; x < _game.views[n].numLoops; x++) - free(_game.views[n].loop[x].cel); + viewData->headerCycleTime = 0; + viewData->headerStepSize = 0; + viewData->description = nullptr; + viewData->loop = nullptr; + viewData->loopCount = 0; - free(_game.views[n].loop); - free(_game.views[n].rdata); + // Mark this view as not loaded anymore + _game.dirView[viewNr].flags &= ~RES_LOADED; - _game.dirView[n].flags &= ~RES_LOADED; + _sprites->buildAllSpriteLists(); + _sprites->drawAllSpriteLists(); } /** - * 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 + * 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::setCel(VtEntry *v, int n) { - assert(v->viewData != NULL); - assert(v->numCels >= n); +void AgiEngine::setView(ScreenObjEntry *screenObj, int16 viewNr) { + if (!(_game.dirView[viewNr].flags & RES_LOADED)) { + // View resource currently not loaded, this is probably a game bug + // Load the resource now to fix the issue, and give out a warning + // This happens in at least Larry 1 for Apple IIgs right after getting beaten up by taxi driver + // Original interpreter bombs out in this situation saying "view not loaded, Press ESC to quit" + warning("setView() called on screen object %d to use view %d, but view not loaded", screenObj->objectNr, viewNr); + warning("probably game script bug, trying to load view into memory"); + if (agiLoadResource(RESOURCETYPE_VIEW, viewNr) != errOK) { + // loading failed, we better error() out now + error("setView() called to set view %d for screen object %d, which is not loaded atm and loading failed", viewNr, screenObj->objectNr); + return; + }; + } - lSetCel(v, n); + screenObj->viewResource = &_game.views[viewNr]; + screenObj->currentViewNr = viewNr; + screenObj->loopCount = screenObj->viewResource->loopCount; + screenObj->viewReplaced = true; - // If position isn't appropriate, update it accordingly - clipViewCoordinates(v); + if (getVersion() < 0x2000) { + screenObj->stepSize = screenObj->viewResource->headerStepSize; + screenObj->cycleTime = screenObj->viewResource->headerCycleTime; + screenObj->cycleTimeCount = 0; + } + if (screenObj->currentLoopNr >= screenObj->loopCount) { + setLoop(screenObj, 0); + } else { + setLoop(screenObj, screenObj->currentLoopNr); + } } /** - * 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 loop of the current view. + * @param screenObj pointer to screen object + * @param loopNr number of loop */ -void AgiEngine::clipViewCoordinates(VtEntry *v) { - if (v->xPos + v->xSize > _WIDTH) { - v->flags |= fUpdatePos; - v->xPos = _WIDTH - v->xSize; - } - if (v->yPos - v->ySize + 1 < 0) { - v->flags |= fUpdatePos; - v->yPos = v->ySize - 1; +void AgiEngine::setLoop(ScreenObjEntry *screenObj, int16 loopNr) { + if (!(_game.dirView[screenObj->currentViewNr].flags & RES_LOADED)) { + error("setLoop() called on screen object %d, which has no loaded view resource assigned to it", screenObj->objectNr); + return; } - if (v->yPos <= _game.horizon && (~v->flags & fIgnoreHorizon)) { - v->flags |= fUpdatePos; - v->yPos = _game.horizon + 1; + assert(screenObj->viewResource); + + 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 + // TODO: This causes an issue in KQ1, when bowing to the king in room 53 + // Ego will face away from the king, because the scripts set the loop first and then the view + // Loop is corrected by us, because at that time it's invalid. Was already present in 1.7.0 + // We should probably script-patch it out. + 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) { + if (!(_game.dirView[screenObj->currentViewNr].flags & RES_LOADED)) { + error("setCel() called on screen object %d, which has no loaded view resource assigned to it", screenObj->objectNr); + return; + } + assert(screenObj->viewResource); + + if (screenObj->loopCount == 0) { + warning("setLoop() called on screen object %d, which has no loops (view %d)", screenObj->objectNr, screenObj->currentViewNr); + return; + } + + 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 +601,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 +625,67 @@ 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 (getVersion() <= 0x2272 || - v->stepTimeCount == 1) { - setLoop(v, loop); + if (loopNr != 4 && loopNr != screenObj->currentLoopNr) { + if (getVersion() <= 0x2272 || 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 |