/* ScummVM - Scumm Interpreter * Copyright (C) 2001 Ludvig Strigeus * Copyright (C) 2001/2002 The ScummVM project * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ #include "stdafx.h" #include "scumm.h" #include "math.h" void Scumm::initActor(Actor * a, int mode) { if (mode == 1) { a->costume = 0; a->room = 0; a->x = 0; a->y = 0; a->facing = 180; a->newDirection = 180; } else if (mode == 2) { a->facing = 180; a->newDirection = 180; } a->elevation = 0; a->width = 24; a->talkColor = 15; a->new_2 = 0; a->new_1 = -80; a->scaley = a->scalex = 0xFF; a->charset = 0; a->sound[0] = 0; a->sound[1] = 0; a->sound[2] = 0; a->sound[3] = 0; a->sound[4] = 0; a->sound[5] = 0; a->sound[6] = 0; a->sound[7] = 0; a->newDirection = 0; stopActorMoving(a); a->shadow_mode = 0; a->layer = 0; setActorWalkSpeed(a, 8, 2); a->ignoreBoxes = 0; a->forceClip = 0; a->new_3 = 0; a->initFrame = 1; a->walkFrame = 2; a->standFrame = 3; a->talkFrame1 = 4; a->talkFrame2 = 5; a->walk_script = 0; a->talk_script = 0; if (_features & GF_AFTER_V7) { _classData[a->number] = _classData[0]; } else { _classData[a->number] = 0; } } void Scumm::stopActorMoving(Actor * a) { stopScriptNr(a->walk_script); a->moving = 0; } void Scumm::setActorWalkSpeed(Actor * a, uint speedx, uint speedy) { if (speedx == a->speedx && speedy == a->speedy) return; a->speedx = speedx; a->speedy = speedy; if (a->moving) { calcMovementFactor(a, a->walkdata.newx, a->walkdata.newy); } } int Scumm::getAngleFromPos(int x, int y) { if (_gameId == GID_DIG) { double temp = atan2(x, -y); return normalizeAngle((int)(temp * 1.8e2 / 3.14)); } else { if (abs(y) * 2 < abs(x)) { if (x > 0) return 90; return 270; } else { if (y > 0) return 180; return 0; } } } int Scumm::calcMovementFactor(Actor * a, int newX, int newY) { int actorX, actorY; int diffX, diffY; int32 XYFactor, YXFactor; actorX = a->x; actorY = a->y; if (actorX == newX && actorY == newY) return 0; diffX = newX - actorX; diffY = newY - actorY; YXFactor = a->speedy << 16; if (diffY < 0) YXFactor = -YXFactor; XYFactor = YXFactor * diffX; if (diffY != 0) { XYFactor /= diffY; } else { YXFactor = 0; } if ((uint) abs(XYFactor >> 16) > a->speedx) { XYFactor = a->speedx << 16; if (diffX < 0) XYFactor = -XYFactor; YXFactor = XYFactor * diffY; if (diffX != 0) { YXFactor /= diffX; } else { XYFactor = 0; } } a->walkdata.x = actorX; a->walkdata.y = actorY; a->walkdata.newx = newX; a->walkdata.newy = newY; a->walkdata.XYFactor = XYFactor; a->walkdata.YXFactor = YXFactor; a->walkdata.xfrac = 0; a->walkdata.yfrac = 0; a->newDirection = getAngleFromPos(XYFactor, YXFactor); return actorWalkStep(a); } int Scumm::remapDirection(Actor * a, int dir) { int specdir; byte flags; bool flipX; bool flipY; if (!a->ignoreBoxes) { specdir = _extraBoxFlags[a->walkbox]; if (specdir) { if (specdir & 0x8000) { dir = specdir & 0x3FFF; } else { error("getProgrDirChange: special dir not implemented"); } } flags = getBoxFlags(a->walkbox); flipX = (a->walkdata.XYFactor > 0); flipY = (a->walkdata.YXFactor > 0); // Check for X-Flip if ((flags & 0x08) || getClass(a->number, 0x1E)) { dir = 360 - dir; flipX = !flipX; } // Check for Y-Flip if ((flags & 0x10) || getClass(a->number, 0x1D)) { dir = 180 - dir; flipY = !flipY; } switch (flags & 7) { case 1: if (a->moving&~MF_TURN) // Actor is walking return flipX ? 90 : 270; else // Actor is standing/turning return (dir == 90) ? 90 : 270; case 2: if (a->moving&~MF_TURN) // Actor is walking return flipY ? 180 : 0; else // Actor is standing/turning return (dir == 0) ? 0 : 180; case 3: return 270; case 4: return 90; case 5: return 0; case 6: return 180; } } /* Or 1024 in to signal direction interpolation should be done */ return normalizeAngle(dir) | 1024; } int Scumm::updateActorDirection(Actor * a) { int from, to; int diff; int dirType; int dir; int num; bool shouldInterpolate; dirType = akos_hasManyDirections(a); from = toSimpleDir(dirType, a->facing); dir = remapDirection(a, a->newDirection); shouldInterpolate = (dir & 1024); to = toSimpleDir(dirType, dir & 1023); diff = to - from; num = numSimpleDirDirections(dirType); if (shouldInterpolate) { // Turn left or right, depending on which is shorter. if (abs(diff) > (num >> 1)) diff = -diff; if (diff == 0) { } else if (diff > 0) { from++; } else { from--; } } else from = to; dir = fromSimpleDir(dirType, from & (num - 1)); return dir; } void Scumm::setActorBox(Actor * a, int box) { a->walkbox = box; a->mask = getMaskFromBox(box); setupActorScale(a); } int Scumm::actorWalkStep(Actor * a) { int tmpX, tmpY; int actorX, actorY; int distX, distY; int direction; a->needRedraw = true; a->needBgReset = true; direction = updateActorDirection(a); if (!(a->moving & MF_IN_LEG) || a->facing != direction) { if (a->walkFrame != a->frame || a->facing != direction) { startWalkAnim(a, a->walkFrame == a->frame ? 2 : 1, direction); } a->moving |= MF_IN_LEG; } actorX = a->x; actorY = a->y; if (a->walkbox != a->walkdata.curbox && checkXYInBoxBounds(a->walkdata.curbox, actorX, actorY)) { setActorBox(a, a->walkdata.curbox); } distX = abs(a->walkdata.newx - a->walkdata.x); distY = abs(a->walkdata.newy - a->walkdata.y); if (abs(actorX - a->walkdata.x) >= distX && abs(actorY - a->walkdata.y) >= distY) { a->moving &= ~MF_IN_LEG; return 0; } tmpX = ((actorX + 8000) << 16) + a->walkdata.xfrac + (a->walkdata.XYFactor >> 8) * a->scalex; a->walkdata.xfrac = (uint16)tmpX; actorX = (tmpX >> 16) - 8000; tmpY = (actorY << 16) + a->walkdata.yfrac + (a->walkdata.YXFactor >> 8) * a->scalex; a->walkdata.yfrac = (uint16)tmpY; actorY = (tmpY >> 16); if (abs(actorX - a->walkdata.x) > distX) { actorX = a->walkdata.newx; } if (abs(actorY - a->walkdata.y) > distY) { actorY = a->walkdata.newy; } a->x = actorX; a->y = actorY; return 1; } void Scumm::setupActorScale(Actor * a) { uint16 scale; byte *resptr; int y; // FIXME: Special 'no scaling' class for MI1 VGA Floppy // Not totally sure if this is correct. if(_gameId == GID_MONKEY_VGA && getClass(a->number, 0x96)) return; if (_features & GF_NO_SCALLING) { a->scalex = 0xFF; a->scaley = 0xFF; return; } if (a->ignoreBoxes != 0) return; if(getBoxFlags(a->walkbox) & 0x20) return; scale = getBoxScale(a->walkbox); if (scale & 0x8000) { scale = (scale & 0x7FFF) + 1; resptr = getResourceAddress(rtScaleTable, scale); if (resptr == NULL) error("Scale table %d not defined", scale); y = a->y; if (y >= 200) y = 199; if (y < 0) y = 0; scale = resptr[y]; } if (scale > 255) warning("Actor %d at %d, scale %d out of range", a->number, a->y, scale); if(scale == 1 && _currentRoom == 76) scale = 0xFF; a->scalex = (byte)scale; a->scaley = (byte)scale; } void Scumm::startAnimActor(Actor * a, int frame) { if (_features & GF_NEW_COSTUMES) { switch (frame) { case 1001: frame = a->initFrame; break; case 1002: frame = a->walkFrame; break; case 1003: frame = a->standFrame; break; case 1004: frame = a->talkFrame1; break; case 1005: frame = a->talkFrame2; break; } if (a->costume != 0) { a->animProgress = 0; a->needRedraw = true; a->needBgReset = true; if (frame == a->initFrame) initActorCostumeData(a); akos_decodeData(a, frame, (uint) - 1); } } else { if (frame == 0x38) frame = a->initFrame; if (frame == 0x39) frame = a->walkFrame; if (frame == 0x3A) frame = a->standFrame; if (frame == 0x3B) frame = a->talkFrame1; if (frame == 0x3C) frame = a->talkFrame2; if (a->room == _currentRoom && a->costume) { a->animProgress = 0; a->cost.animCounter1 = 0; a->needRedraw = true; if (a->initFrame == frame) initActorCostumeData(a); if (frame != 0x3E) { cost_decodeData(a, frame, (uint) - 1); } } a->needBgReset = true; } } void Scumm::setActorDirection(Actor * a, int direction) { uint mask; int i; uint16 vald; if (a->facing == direction) return; a->facing = normalizeAngle(direction); if (a->costume == 0) return; mask = 0x8000; for (i = 0; i < 16; i++, mask >>= 1) { vald = a->cost.frame[i]; if (vald == 0xFFFF) continue; if (_features & GF_AFTER_V7) akos_decodeData(a, vald, mask); else cost_decodeData(a, vald, mask); } a->needRedraw = true; a->needBgReset = true; } void Scumm::putActor(Actor * a, int x, int y, byte room) { if (a->visible && _currentRoom != room && _vars[VAR_TALK_ACTOR] == a->number) { clearMsgQueue(); } a->x = x; a->y = y; a->room = room; a->needRedraw = true; a->needBgReset = true; if (_vars[VAR_EGO] == a->number) { _egoPositioned = true; } if (a->visible) { if (_currentRoom == room) { if (a->moving) { startAnimActor(a, a->standFrame); a->moving = 0; } adjustActorPos(a); } else { hideActor(a); } } else { if (_currentRoom == room) showActor(a); } } int Scumm::getActorXYPos(Actor * a) { if (!a) return -1; if (a->room != _currentRoom) return -1; _xPos = a->x; _yPos = a->y; return 0; } AdjustBoxResult Scumm::adjustXYToBeInBox(Actor * a, int x, int y, int pathfrom) { AdjustBoxResult abr, tmp; uint threshold; uint best; int box, iterations = 0; /* Use iterations for those odd times we get stuck in the loop */ int firstValidBox, j; byte flags, b; if (_features & GF_SMALL_HEADER) firstValidBox = 0; else firstValidBox = 1; abr.x = x; abr.y = y; abr.dist = 0; if ((_features & GF_SMALL_HEADER) && getClass(a->number, 22)) return abr; if (a && a->ignoreBoxes == 0) { threshold = 30; while (1) { iterations++; if (iterations > 1000) return abr; /* Safety net */ box = getNumBoxes() - 1; if (box == 0) return abr; best = (uint) 0xFFFF; b = 0; if (((_features & GF_SMALL_HEADER) && box) || !(_features & GF_SMALL_HEADER)) for (j = box; j >= firstValidBox; j--) { flags = getBoxFlags(j); if (flags & 0x80 && (!(flags & 0x20) || getClass(a->number, 0x1F))) continue; if (pathfrom && (getPathToDestBox(pathfrom, j) == -1)) continue; if (!inBoxQuickReject(j, x, y, threshold)) continue; if (checkXYInBoxBounds(j, x, y)) { abr.x = x; abr.y = y; abr.dist = j; return abr; } tmp = getClosestPtOnBox(j, x, y); if (tmp.dist >= best) continue; abr.x = tmp.x; abr.y = tmp.y; if (tmp.dist == 0) { abr.dist = j; return abr; } best = tmp.dist; b = j; } if (threshold == 0 || threshold * threshold >= best) { abr.dist = b; return abr; } threshold = (threshold == 30) ? 80 : 0; } } return abr; } void Scumm::adjustActorPos(Actor * a) { AdjustBoxResult abr; byte flags; abr = adjustXYToBeInBox(a, a->x, a->y, 0); a->x = abr.x; a->y = abr.y; a->walkdata.destbox = (byte)abr.dist; setActorBox(a, abr.dist); a->walkdata.destx = -1; a->moving = 0; a->cost.animCounter2 = 0; flags = getBoxFlags(a->walkbox); if (flags & 7) { turnToDirection(a, a->facing); } } void Scumm::hideActor(Actor * a) { if (!a->visible) return; if (a->moving) { startAnimActor(a, a->standFrame); a->moving = 0; } a->visible = false; a->cost.animCounter2 = 0; a->needRedraw = false; a->needBgReset = true; } void Scumm::turnToDirection(Actor * a, int newdir) { if (newdir == -1) return; a->moving &= ~MF_TURN; if (newdir != a->facing) { a->moving = MF_TURN; a->newDirection = newdir; } } void Scumm::showActor(Actor * a) { if (_currentRoom == 0 || a->visible) return; adjustActorPos(a); ensureResourceLoaded(rtCostume, a->costume); if (a->costumeNeedsInit) { startAnimActor(a, a->initFrame); a->costumeNeedsInit = false; } a->moving = 0; a->visible = true; a->needRedraw = true; } void Scumm::showActors() { int i; Actor *a; for (i = 1; i < NUM_ACTORS; i++) { a = derefActor(i); if (a->room == _currentRoom) showActor(a); } } void Scumm::stopTalk() { int act; stopTalkSound(); _haveMsg = 0; _talkDelay = 0; act = _vars[VAR_TALK_ACTOR]; if (act && act < 0x80) { Actor *a = derefActorSafe(act, "stopTalk"); if (_currentRoom == a->room && _useTalkAnims) { startAnimActor(a, a->talkFrame2); _useTalkAnims = false; } _vars[VAR_TALK_ACTOR] = 0xFF; } _keepText = false; restoreCharsetBg(); } void Scumm::clearMsgQueue() { _messagePtr = (byte *)" "; stopTalk(); } void Scumm::walkActors() { int i; Actor *a; for (i = 1; i < NUM_ACTORS; i++) { a = derefActor(i); if (a->room == _currentRoom) if (_features & GF_OLD256) walkActorOld(a); else walkActor(a); } } /* Used in Scumm v5 only. Play sounds associated with actors */ void Scumm::playActorSounds() { int i; Actor *a; for (i = 1; i < NUM_ACTORS; i++) { a = derefActor(i); if (a->cost.animCounter2 && a->room == _currentRoom && a->sound) { _currentScript = 0xFF; addSoundToQueue(a->sound[0]); for (i = 1; i < NUM_ACTORS; i++) { a = derefActor(i); a->cost.animCounter2 = 0; } return; } } } void Scumm::startWalkAnim(Actor * a, int cmd, int angle) { int16 args[16]; if (angle == -1) angle = a->facing; if (a->walk_script != 0) { args[2] = angle; args[0] = a->number; args[1] = cmd; runScript(a->walk_script, 1, 0, args); } else { switch (cmd) { case 1: /* start walk */ setActorDirection(a, angle); startAnimActor(a, a->walkFrame); break; case 2: /* change dir only */ setActorDirection(a, angle); break; case 3: /* stop walk */ turnToDirection(a, angle); startAnimActor(a, a->standFrame); break; } } } void Scumm::walkActor(Actor * a) { int j; if (!a->moving) return; if (!(a->moving & MF_NEW_LEG)) { if (a->moving & MF_IN_LEG && actorWalkStep(a)) return; if (a->moving & MF_LAST_LEG) { a->moving = 0; setActorBox(a, a->walkdata.destbox); startWalkAnim(a, 3, a->walkdata.destdir); return; } if (a->moving & MF_TURN) { j = updateActorDirection(a); if (a->facing != j) setActorDirection(a, j); else a->moving = 0; return; } setActorBox(a, a->walkdata.curbox); a->moving &= MF_IN_LEG; } #if OLD a->moving &= ~MF_NEW_LEG; if (!a->walkbox) { a->walkbox = a->walkdata.destbox; a->walkdata.curbox = a->walkdata.destbox; a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } if (a->ignoreBoxes || a->walkbox == a->walkdata.destbox) { a->walkdata.curbox = a->walkbox; a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } j = getPathToDestBox(a->walkbox, a->walkdata.destbox); if (j == -1) { error("walkActor: no path found between %d and %d", a->walkbox, a->walkdata.destbox); } a->walkdata.curbox = j; if (findPathTowards(a, a->walkbox, j, a->walkdata.destbox)) { a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } calcMovementFactor(a, _foundPathX, _foundPathY); #endif #if 1 do { a->moving &= ~MF_NEW_LEG; if ((!a->walkbox && (!(_features & GF_SMALL_HEADER)))) { setActorBox(a, a->walkdata.destbox); a->walkdata.curbox = a->walkdata.destbox; break; } if (a->walkbox == a->walkdata.destbox) break; j = getPathToDestBox(a->walkbox, a->walkdata.destbox); if (j == -1 || j > 0xF0) { a->walkdata.destbox = a->walkbox; a->moving |= MF_LAST_LEG; return; } a->walkdata.curbox = j; if (_features & GF_OLD256) { findPathTowardsOld(a, a->walkbox, j, a->walkdata.destbox); if (p[2].x == 32000 && p[3].x == 32000) { a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } if (p[2].x != 32000) { if (calcMovementFactor(a, p[2].x, p[2].y)) { a->walkdata.destx = p[3].x; a->walkdata.desty = p[3].y; return; } } if (calcMovementFactor(a, p[3].x, p[3].y)) return; } else { if (findPathTowards(a, a->walkbox, j, a->walkdata.destbox)) break; if (calcMovementFactor(a, _foundPathX, _foundPathY)) return; } setActorBox(a, a->walkdata.curbox); } while (1); a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); #endif } void Scumm::processActors() { int i; Actor *actors[MAX_ACTORS], *a, **ac, **ac2, *tmp; int numactors = 0, cnt, cnt2; for (i = 1; i < NUM_ACTORS; i++) { a = derefActor(i); if (a->room == _currentRoom) actors[numactors++] = a; } if (!numactors) return; ac = actors; cnt = numactors; do { ac2 = actors; cnt2 = numactors; do { if ((*ac2)->y - ((*ac2)->layer << 11) > (*ac)->y - ((*ac)->layer << 11)) { tmp = *ac; *ac = *ac2; *ac2 = tmp; } } while (ac2++, --cnt2); } while (ac++, --cnt); ac = actors; cnt = numactors; do { a = *ac; if (a->costume) { CHECK_HEAP getMaskFromBox(a->walkbox); drawActorCostume(a); CHECK_HEAP actorAnimate(a); } } while (ac++, --cnt); } void Scumm::drawActorCostume(Actor * a) { if (!(_features & GF_AFTER_V7)) { CostumeRenderer cr; if (a == NULL || !a->needRedraw) return; if (getClass(a->number, 20)) a->mask = 0; else if (getClass(a->number, 21)) a->forceClip = 1; if (_gameId==GID_SAMNMAX && getState(995)) // FIXME: ugly fix for samnmax inventory return; a->needRedraw = false; setupActorScale(a); /* First, zero initialize all fields */ memset(&cr, 0, sizeof(cr)); cr._actorX = a->x - virtscr->xstart; cr._actorY = a->y - a->elevation; cr._scaleX = a->scalex; cr._scaleY = a->scaley; cr._outheight = virtscr->height; cr._vm = this; cr._zbuf = a->mask; if (cr._zbuf > gdi._numZBuffer) cr._zbuf = (byte)gdi._numZBuffer; if (a->forceClip) cr._zbuf = a->forceClip; cr._shadow_table = _shadowPalette; cost_setCostume(&cr, a->costume); cost_setPalette(&cr, a->palette); cost_setFacing(&cr, a); a->top = 0xFF; a->bottom = 0; /* if the actor is partially hidden, redraw it next frame */ if (cr.drawCostume(a) & 1) { a->needBgReset = true; a->needRedraw = true; } } else { AkosRenderer ar; if (a == NULL || !a->needRedraw) return; a->needRedraw = false; setupActorScale(a); /* First, zero initialize all fields */ memset(&ar, 0, sizeof(ar)); ar.x = a->x - virtscr->xstart; ar.y = a->y - a->elevation; ar.scale_x = a->scalex; ar.scale_y = a->scaley; ar.clipping = a->forceClip; if (ar.clipping == 100) { ar.clipping = a->mask; if (ar.clipping > (byte)gdi._numZBuffer) ar.clipping = gdi._numZBuffer; } ar.charsetmask = _vars[VAR_CHARSET_MASK] != 0; ar.outptr = virtscr->screenPtr + virtscr->xstart; ar.outwidth = virtscr->width; ar.outheight = virtscr->height; ar.shadow_mode = a->shadow_mode; ar.shadow_table = _shadowPalette; akos_setCostume(&ar, a->costume); akos_setPalette(&ar, a->palette); akos_setFacing(&ar, a); ar.dirty_id = a->number; ar.cd = &a->cost; ar.draw_top = a->top = 0x7fffffff; ar.draw_bottom = a->bottom = 0; akos_drawCostume(&ar); a->top = ar.draw_top; a->bottom = ar.draw_bottom; } } void Scumm::actorAnimate(Actor * a) { byte *akos; LoadedCostume lc; if (a == NULL || a->costume == 0) return; a->animProgress++; if (a->animProgress >= a->animSpeed) { a->animProgress = 0; if (_features & GF_AFTER_V7) { akos = getResourceAddress(rtCostume, a->costume); assert(akos); if (akos_increaseAnims(akos, a)) { a->needRedraw = true; a->needBgReset = true; } } else { loadCostume(&lc, a->costume); if (cost_increaseAnims(&lc, a)) { a->needRedraw = true; a->needBgReset = true; } } } } void Scumm::setActorRedrawFlags() { int i, j; uint32 bits; for (i = 0; i < 40; i++) { bits = gfxUsageBits[_screenStartStrip + i]; if (bits & 0x3FFFFFFF) { for (j = 0; j < NUM_ACTORS; j++) { if ((bits & (1 << j)) && bits != (uint32)(1 << j)) { Actor *a = derefActor(j); a->needRedraw = true; a->needBgReset = true; } } } } } int Scumm::getActorFromPos(int x, int y) { uint32 drawbits; int i; drawbits = gfxUsageBits[x >> 3]; if (!(drawbits & 0x3FFFFFFF)) return 0; for (i = 1; i < NUM_ACTORS; i++) { Actor *a = derefActor(i); if (drawbits & (1 << i) && !getClass(i, 32) && y >= a->top && y <= a->bottom) { return i; } } return 0; } void Scumm::actorTalk() { int oldact; Actor *a; _msgPtrToAdd = charset._buffer; _messagePtr = addMessageToStack(_messagePtr); assert((int)(_msgPtrToAdd - charset._buffer) < (int)(sizeof(charset._buffer))); if (_actorToPrintStrFor == 0xFF) { if (!_keepText) stopTalk(); _vars[VAR_TALK_ACTOR] = 0xFF; oldact = 0; } else { a = derefActorSafe(_actorToPrintStrFor, "actorTalk"); if (a->room != _currentRoom && !(_features & GF_AFTER_V7)) { oldact = 0xFF; } else { if (!_keepText) stopTalk(); _vars[VAR_TALK_ACTOR] = a->number; if (!string[0].no_talk_anim) { startAnimActor(a, a->talkFrame1); _useTalkAnims = true; } oldact = _vars[VAR_TALK_ACTOR]; } } if (oldact >= 0x80) return; if (_vars[VAR_TALK_ACTOR] > 0x7F) { _charsetColor = (byte)string[0].color; } else { a = derefActorSafe(_vars[VAR_TALK_ACTOR], "actorTalk(2)"); _charsetColor = a->talkColor; } charset._bufPos = 0; _talkDelay = 0; _haveMsg = 0xFF; _vars[VAR_HAVE_MSG] = 0xFF; CHARSET_1(); } void Scumm::setActorCostume(Actor * a, int c) { int i; a->costumeNeedsInit = true; if (a->visible) { hideActor(a); initActorCostumeData(a); a->costume = c; showActor(a); } else { a->costume = c; initActorCostumeData(a); } for (i = 0; i < 32; i++) a->palette[i] = 0xFF; } void Scumm::startWalkActor(Actor * a, int x, int y, int dir) { AdjustBoxResult abr; abr = adjustXYToBeInBox(a, x, y, a->walkbox); if (a->room != _currentRoom) { a->x = abr.x; a->y = abr.y; if (dir != -1) setActorDirection(a, dir); // a->facing = dir; return; } if (a->ignoreBoxes != 0) { abr.dist = 0; a->walkbox = 0; } else { if (checkXYInBoxBounds(a->walkdata.destbox, abr.x, abr.y)) { abr.dist = a->walkdata.destbox; } else { abr = adjustXYToBeInBox(a, abr.x, abr.y, a->walkbox); } if (a->moving && a->walkdata.destdir == dir && a->walkdata.destx == abr.x && a->walkdata.desty == abr.y) return; } if (a->x == abr.x && a->y == abr.y) { turnToDirection(a, dir); return; } a->walkdata.destx = abr.x; a->walkdata.desty = abr.y; a->walkdata.destbox = (byte)abr.dist; /* a box */ a->walkdata.destdir = dir; a->moving = (a->moving & MF_IN_LEG) | MF_NEW_LEG; a->walkdata.point3x = 32000; a->walkdata.curbox = a->walkbox; } byte *Scumm::getActorName(Actor * a) { byte *ptr = getResourceAddress(rtActorName, a->number); if (ptr == NULL) return (byte *)" "; return ptr; } bool Scumm::isCostumeInUse(int cost) { int i; Actor *a; if (_roomResource != 0) for (i = 1; i < NUM_ACTORS; i++) { a = derefActor(i); if (a->room == _currentRoom && a->costume == cost) return true; } return false; } void Scumm::remapActor(Actor * a, int r_fact, int g_fact, int b_fact, int threshold) { byte *akos, *rgbs, *akpl; int akpl_size, i; int r, g, b; byte akpl_color; if (a->room != _currentRoom) { warning("Remap actor %d not in current room", a->number); return; } if (a->costume < 1 || a->costume >= _numCostumes - 1) { warning("Remap actor %d invalid costume", a->number, a->costume); return; } akos = getResourceAddress(rtCostume, a->costume); akpl = findResource(MKID('AKPL'), akos); //get num palette entries akpl_size = RES_SIZE(akpl) - 8; //skip resource header akpl = RES_DATA(akpl); rgbs = findResource(MKID('RGBS'), akos); if (!rgbs) { warning("Can't remap actor %d costume %d doesn't contain an RGB block", a->number, a->costume); return; } // skip resource header rgbs = RES_DATA(rgbs); for (i = 0; i < akpl_size; i++) { r = *rgbs++; g = *rgbs++; b = *rgbs++; akpl_color = *akpl++; // allow remap of generic palette entry? if (!a->shadow_mode || akpl_color >= 16) { if (r_fact != 256) r = (r * r_fact) >> 8; if (g_fact != 256) g = (g * g_fact) >> 8; if (b_fact != 256) b = (b * b_fact) >> 8; a->palette[i] = remapPaletteColor(r, g, b, threshold); } } } void Scumm::setupShadowPalette(int slot, int rfact, int gfact, int bfact, int from, int to) { byte *table; int i, num; byte *curpal; if (slot < 0 || slot > 7) error("setupShadowPalette: invalid slot %d", slot); if (from < 0 || from > 255 || to < 0 || from > 255 || to < from) error("setupShadowPalette: invalid range from %d to %d", from, to); table = _shadowPalette + slot * 256; for (i = 0; i < 256; i++) table[i] = i; table += from; curpal = _currentPalette + from * 3; num = to - from + 1; do { *table++ = remapPaletteColor(curpal[0] * rfact >> 8, curpal[1] * gfact >> 8, curpal[2] * bfact >> 8, (uint) - 1); curpal += 3; } while (--num); } void Scumm::walkActorOld(Actor * a) { int new_dir, next_box; if (!a->moving) return; if (a->moving & MF_NEW_LEG) { restart: a->moving &= ~MF_NEW_LEG; if (a->walkbox == 0xFF) { a->walkbox = a->walkdata.destbox; a->walkdata.curbox = a->walkdata.destbox; a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } if (a->walkbox == a->walkdata.destbox) { a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } next_box = getPathToDestBox(a->walkbox, a->walkdata.destbox); if (next_box == -1) { a->moving |= MF_LAST_LEG; return; } a->walkdata.curbox = next_box; findPathTowardsOld(a, a->walkbox, next_box, a->walkdata.destbox); if (p[2].x == 32000 && p[3].x == 32000) { a->moving |= MF_LAST_LEG; calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); return; } if (p[2].x != 32000) { if (calcMovementFactor(a, p[2].x, p[2].y)) { actor->walkdata.point3x = p[3].x; actor->walkdata.point3y = p[3].y; return; } } if (calcMovementFactor(a, p[3].x, p[3].y)) return; a->walkbox = a->walkdata.destbox; a->mask = getMaskFromBox(a->walkbox); goto restart; } if (a->moving & MF_IN_LEG) { if (actorWalkStep(a)) return; } if (a->moving & MF_LAST_LEG) { a->moving = 0; startWalkAnim(a, 3, a->walkdata.destdir); return; } if (a->moving & MF_TURN) { new_dir = updateActorDirection(a); if (a->facing != new_dir) { setActorDirection(a, new_dir); return; } a->moving = 0; return; } if (a->walkdata.point3x != 32000) { if (calcMovementFactor(a, a->walkdata.point3x, a->walkdata.point3y)) { a->walkdata.point3x = 32000; return; } a->walkdata.point3x = 32000; } a->walkbox = a->walkdata.curbox; a->mask = getMaskFromBox(a->walkbox); a->moving &= MF_IN_LEG; a->moving |= MF_NEW_LEG; goto restart; }