diff options
| author | Ludvig Strigeus | 2001-10-09 14:30:12 +0000 |
|---|---|---|
| committer | Ludvig Strigeus | 2001-10-09 14:30:12 +0000 |
| commit | c30932afbe1af874e3a2aeb95fa4ee5de4d6e38e (patch) | |
| tree | 192b56f3908880c5a513a366f616341bcb47056e /actor.cpp | |
| download | scummvm-rg350-c30932afbe1af874e3a2aeb95fa4ee5de4d6e38e.tar.gz scummvm-rg350-c30932afbe1af874e3a2aeb95fa4ee5de4d6e38e.tar.bz2 scummvm-rg350-c30932afbe1af874e3a2aeb95fa4ee5de4d6e38e.zip | |
Initial revision
svn-id: r3408
Diffstat (limited to 'actor.cpp')
| -rw-r--r-- | actor.cpp | 953 |
1 files changed, 953 insertions, 0 deletions
diff --git a/actor.cpp b/actor.cpp new file mode 100644 index 0000000000..ccf1435817 --- /dev/null +++ b/actor.cpp @@ -0,0 +1,953 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * + * 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. + * + * Change Log: + * $Log$ + * Revision 1.1 2001/10/09 14:30:14 strigeus + * Initial revision + * + * + */ + +#include "stdafx.h" +#include "scumm.h" + +void Scumm::initActor(Actor *a, int mode) { + if (mode) { + a->facing = 2; + a->costume = 0; + a->room = 0; + a->x = 0; + a->y = 0; + } + + a->elevation = 0; + a->width = 0x18; + a->talkColor = 0xF; + a->scaley = a->scalex = 0xFF; + a->charset = 0; + a->sound = 0; + a->moving = 0; + + setActorWalkSpeed(a, 8, 2); + + a->ignoreBoxes = 0; + a->neverZClip = 0; + a->initFrame = 1; + a->walkFrame = 2; + a->standFrame = 3; + a->talkFrame1 = 4; + a->talkFrame2 = 5; + + _classData[a->number] = 0; +} + +void Scumm::setActorWalkSpeed(Actor *a, int speedx, int 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::calcMovementFactor(Actor *a, int newX, int newY) { + int actorX, actorY; + int diffX, diffY; + int32 XYFactor, YXFactor; + int32 tmp; + + 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; + + if (diffY != 0) { + XYFactor = YXFactor * diffX / diffY; + } else { + XYFactor = YXFactor * diffX; + YXFactor = 0; + } + + tmp = XYFactor >> 16; + if (tmp<0) + tmp = -tmp; + + if (tmp > a->speedx) { + XYFactor = a->speedx<<16; + if (diffX < 0) + XYFactor = -XYFactor; + + if (diffX != 0) { + YXFactor = XYFactor * diffY / diffX; + } else { + YXFactor = XYFactor * diffY; + 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; + + return actorWalkStep(a); +} + +int Scumm::actorWalkStep(Actor *a) { + int32 XYFactor, YXFactor; + int actorX, actorY, newx, newy; + int newXDist; + int32 tmp,tmp2; + + byte direction; + + a->needRedraw = true; + a->needBgReset = true; + + XYFactor = a->walkdata.XYFactor; + YXFactor = a->walkdata.YXFactor; + + direction = XYFactor>0 ? 1 : 0; + if (abs(YXFactor) * 3 > abs(XYFactor)) + direction = YXFactor>0 ? 2 : 3; + a->newDirection = direction; + + direction = getProgrDirChange(a, 1); + + if (!(a->moving&2) || a->facing!=direction) { + if (a->walkFrame != a->animIndex || a->facing != direction) { + startAnimActor(a, a->walkFrame, direction); + } + a->moving|=2; + } + + actorX = a->walkdata.x; + actorY = a->walkdata.y; + newx = a->walkdata.newx; + newy = a->walkdata.newy; + + if (a->walkbox != a->walkdata.curbox) { + if (checkXYInBoxBounds(a->walkdata.curbox, a->x, a->y)) { + a->walkbox = a->walkdata.curbox; + a->mask = getMaskFromBox(a->walkdata.curbox); + setupActorScale(a); + } + } + + newXDist = abs(newx - actorX); + + if (newXDist <= abs(a->x - actorX) && + abs(newy - actorY) <= abs(a->y - actorY) ){ + a->moving&=~2; + return 0; + } + + XYFactor = (XYFactor>>8) * a->scalex; + YXFactor = (YXFactor>>8) * a->scalex; + + tmp = ((a->x + 8000)<<16) + a->walkdata.xfrac + XYFactor; + tmp2 = (a->y<<16) + a->walkdata.yfrac + YXFactor; + + a->x = (tmp>>16)-8000; + a->y = tmp2>>16; + + if (abs(a->x - actorX) > newXDist) { + a->x = newx; + } + + if (abs(a->y - actorY) > abs(newy - actorY)) { + a->y = newy; + } + + a->walkdata.xfrac = tmp&0xFFFF; + a->walkdata.yfrac = tmp2&0xFFFF; + + if (a->x == newx && + a->y == newy) { + a->moving&=~2; + return 0; + } + + return 1; +} + +void Scumm::setupActorScale(Actor *a) { + uint16 scale; + byte *resptr; + + if (a->ignoreBoxes != 0) + return; + + scale = getBoxScale(a->walkbox); + + if (scale & 0x8000) { + scale = (scale&0x7FFF)+1; + resptr = getResourceAddress(0xB, scale); + if (resptr==NULL) + error("Scale table %d not defined",scale); + if (a->y >= 0) + resptr += a->y; + scale = *resptr; + } + + if (scale>255) + error("Actor %d at %d, scale %d out of range", a->number, a->y, scale); + + a->scalex = (byte)scale; + a->scaley = (byte)scale; +} + +int Scumm::getProgrDirChange(Actor *a, int mode) { + int flags; + byte facing, newdir; + byte XYflag, YXflag; + byte lookdir; + + const byte direction_transtab[] = { + 0,2,2,3,2,1,2,3,0,1,2,1,0,1,0,3 + }; + + flags = 0; + if (!a->ignoreBoxes) + flags = getBoxFlags(a->walkbox); + + facing = a->facing; + newdir = a->newDirection; + + XYflag = a->walkdata.XYFactor>0 ? 1 : 0; + YXflag = a->walkdata.YXFactor>0 ? 1 : 0; + + if ((flags&8) || getClass(a->number, 0x1E)) { + if (!(newdir&2)) + newdir^=1; + XYflag = 1 - XYflag; + } + + if ((flags&0x10) || getClass(a->number, 0x1D)) { + if (newdir&2) + newdir^=1; + YXflag = 1 - YXflag; + } + + lookdir = direction_transtab[facing*4+newdir]; + + if (!(flags&=0x7)) + return lookdir; + + if (mode==0) { + lookdir = newdir; + if (flags==1 && newdir!=1) + lookdir = 0; + + if (flags==2 && newdir!=3) + lookdir = 2; + } else { + if (flags==1) + lookdir = XYflag; + if (flags==2) + lookdir = 3 - YXflag; + } + if (flags==3) + lookdir=0; + if (flags==4) + lookdir=1; + if (flags==6) + lookdir=2; + if (flags==5) + lookdir = 3; + return lookdir; +} + +void Scumm::startAnimActor(Actor *a, int frame, byte direction) { + 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; + + cost.loadCostume(a->costume); + + if (a->initFrame==frame) + initActorCostumeData(a); + + if (frame!=0x3E) { + decodeCostData(a, frame*4 + direction, -1); + } + + if (a->facing != direction) + fixActorDirection(a, direction); + } + + a->facing = direction; + a->needBgReset = true; +} + +void Scumm::initActorCostumeData(Actor *a) { + CostumeData *cd = &a->cost; + int i; + + cd->hdr = 0; + for (i=0; i<16; i++) + cd->a[i] = cd->b[i] = cd->c[i] = cd->d[i] = 0xFFFF; +} + +void Scumm::fixActorDirection(Actor *a, byte direction) { + uint mask; + int i; + uint16 vald; + + if (a->facing == direction) + return; + + mask = 0x8000; + for (i=0; i<16; i++,mask>>=1) { + vald = a->cost.d[i]; + if (vald==0xFFFF || (vald&3)==direction) + continue; + decodeCostData(a, (vald&0xFC)|direction, mask); + } + a->facing = direction; +} + +void Scumm::decodeCostData(Actor *a, int frame, uint usemask) { + byte *p,*r; + uint mask,j; + int i; + byte extra,cmd; + byte *dataptr; + + p = cost._ptr; + if (frame > p[6]) + return; + + r = p + READ_LE_UINT16(p + frame*2 + cost._numColors + 42); + if (r==p) + return; + + dataptr = p + READ_LE_UINT16(p + cost._numColors + 8); + + mask = READ_LE_UINT16(r); + r+=2; + i = 0; + do { + if (mask&0x8000) { + j = READ_LE_UINT16(r); + r+=2; + if (usemask&0x8000) { + if (j==0xFFFF) { + a->cost.a[i] = 0xFFFF; + a->cost.b[i] = 0; + a->cost.d[i] = frame; + } else { + extra = *r++; + cmd = dataptr[j]; + if (cmd==0x7A) { + a->cost.hdr &= ~(1<<i); + } else if (cmd==0x79) { + a->cost.hdr |= (1<<i); + } else { + a->cost.a[i] = a->cost.b[i] = j; + a->cost.c[i] = j + (extra&0x7F); + if (extra&0x80) + a->cost.a[i] |= 0x8000; + a->cost.d[i] = frame; + } + } + } else { + if (j!=0xFFFF) + r++; + } + } + i++; + usemask <<= 1; + mask <<= 1; + } while ((uint16)mask); +} + +void Scumm::putActor(Actor *a, int x, int y, byte room) { + if (a->visible && _currentRoom!=room && vm.vars[VAR_TALK_ACTOR]==a->number) { + clearMsgQueue(); + } + + a->x = x; + a->y = y; + a->room = room; + a->needRedraw = true; + a->needBgReset = true; + + if (vm.vars[VAR_UNK_ACTOR]==a->number) { + dseg_3A76 = 1; + } + + if (a->visible) { + if (_currentRoom == room) { + if (a->moving) { + startAnimActor(a, a->standFrame, a->facing); + a->moving = 0; + } + adjustActorPos(a); + } else { + hideActor(a); + } + } else { + if (_currentRoom == room) + showActor(a); + } +} + +int Scumm::getActorXYPos(Actor *a) { + if (a->room != _currentRoom) + return -1; + _xPos = a->x; + _yPos = a->y; + return 0; +} + +AdjustBoxResult Scumm::adjustXYToBeInBox(Actor *a, int x, int y) { + AdjustBoxResult abr,tmp; + int threshold; + uint best; + int box; + byte flags, b; + + if (a && a->ignoreBoxes==0) { + threshold = 30; + + while(1) { + box = getNumBoxes() - 1; + best = (uint)0xFFFF; + b = 0; + + do { + flags = getBoxFlags(box); + if (flags&0x80 && (!(flags&0x20) || getClass(a->number, 0x1F)) ) + continue; + + if (!inBoxQuickReject(box, x, y, threshold)) + continue; + + if (checkXYInBoxBounds(box, x, y)) { + abr.x = x; + abr.y = y; + abr.dist = box; + return abr; + } + + tmp = getClosestPtOnBox(box, x, y); + + if (tmp.dist >= best) + continue; + + abr.x = tmp.x; + abr.y = tmp.y; + + if (tmp.dist==0) { + abr.dist = box; + return abr; + } + best = tmp.dist; + b = box; + } while (--box); + + if (threshold==0 || threshold * threshold >= best) { + abr.dist = b; + return abr; + } + threshold = (threshold==30) ? 80 : 0; + } + } else { + abr.x = x; + abr.y = y; + abr.dist = 0; + } + return abr; +} + +void Scumm::adjustActorPos(Actor *a) { + AdjustBoxResult abr; + byte flags; + + abr = adjustXYToBeInBox(a, a->x, a->y); + + a->x = abr.x; + a->y = abr.y; + a->walkbox = abr.dist; /* not a dist */ + a->walkdata.destbox = abr.dist; + a->mask = getMaskFromBox(abr.dist); + a->walkdata.destx = -1; + setupActorScale(a); + + 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->facing); + a->moving = 0; + } + a->visible = false; + a->cost.animCounter2 = 0; + a->needRedraw = false; + a->needBgReset = true; +} + +void Scumm::turnToDirection(Actor *a, int newdir) { + a->moving = 4; + a->newDirection = newdir; +} + +void Scumm::showActor(Actor *a) { + if (_currentRoom == 0 || a->visible) + return; + + adjustActorPos(a); + + ensureResourceLoaded(3, a->costume); + + if (a->costumeNeedsInit) { + startAnimActor(a, a->initFrame, a->facing); + a->costumeNeedsInit = false; + } + a->moving = 0; + a->visible = true; + a->needRedraw = true; +} + +void Scumm::showActors() { + int i; + Actor *a; + + for (i=1; i<13; i++) { + a = derefActor(i); + if (a->room == _currentRoom) + showActor(a); + } +} + +void Scumm::stopTalk() { + int act; + + _haveMsg = 0; + _talkDelay = 0; + + act = vm.vars[VAR_TALK_ACTOR]; + if (act && act<0x80) { + Actor *a = derefActorSafe(act, "stopTalk"); + if (_currentRoom == a->room) { + startAnimActor(a, a->talkFrame2, a->facing); + } + vm.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<13; i++) { + a = derefActor(i); + if (a->room==_currentRoom) + walkActor(a); + } +} + +void Scumm::playActorSounds() { + int i; + Actor *a; + + for (i=1; i<13; i++) { + a = derefActor(i); + if (a->cost.animCounter2 && a->room==_currentRoom && a->sound) { + _currentScript = 0xFF; + addSoundToQueue(a->sound); + for (i=1; i<13; i++) { + a = derefActor(i); + a->cost.animCounter2 = 0; + } + return; + } + } +} + +void Scumm::walkActor(Actor *a) { + int j; + + if (!a->moving) + return; + + if (!(a->moving&1)) { + if (a->moving&2 && actorWalkStep(a)) + return; + + if (a->moving&8) { + a->moving = 0; + + j = a->walkdata.destbox; + if (j) { + a->walkbox = j; + a->mask = getMaskFromBox(j); + } + startAnimActor(a, a->standFrame, a->facing); + if (a->walkdata.destdir==0xFF || + a->walkdata.destdir==a->newDirection) + return; + a->newDirection = a->walkdata.destdir; + a->moving = 4; + return; + } + + if (a->moving&4) { + j = getProgrDirChange(a, 0); + if (a->facing != j) + startAnimActor(a, 0x3E, j); + else + a->moving = 0; + return; + } + + a->walkbox = a->walkdata.curbox; + a->mask = getMaskFromBox(a->walkdata.curbox); + + setupActorScale(a); + a->moving = (a->moving&2)|1; + } + + do { + a->moving&=~1; + if (!a->walkbox) { + a->walkbox = 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==0) { + a->walkdata.destbox = a->walkbox; + a->moving |= 8; + return; + } + a->walkdata.curbox = j; + if (findPathTowards(a, a->walkbox, j, a->walkdata.destbox)) + break; + if (calcMovementFactor(a, _foundPathX, _foundPathY)) + return; + + a->walkbox = a->walkdata.curbox; + a->mask = getMaskFromBox(a->walkdata.curbox); + setupActorScale(a); + } while (1); + a->moving |= 8; + calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty); +} + +void Scumm::processActors() { + int i; + Actor *actors[13],*a,**ac,**ac2,*tmp; + int numactors = 0, cnt,cnt2; + + for (i=1; i<13; 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 > (*ac)->y ) { + tmp = *ac; + *ac = *ac2; + *ac2 = tmp; + } + } while (ac2++, --cnt2); + } while (ac++,--cnt); + + ac = actors; + cnt = numactors; + do { + a = *ac; + if (a->costume) { + setupActorScale(a); + setupCostumeRenderer(&cost, a); + setActorCostPalette(a); + checkHeap(); + drawActorCostume(a); + checkHeap(); + actorAnimate(a); + } + } while (ac++,--cnt); +} + +void Scumm::setupCostumeRenderer(CostumeRenderer *c, Actor *a) { + c->_actorX = a->x - virtscr->xstart; + c->_actorY = a->y - a->elevation; + c->_zbuf = a->mask; + if (c->_zbuf > _numZBuffer) + c->_zbuf = _numZBuffer; + if (a->neverZClip) + c->_zbuf = a->neverZClip; + + c->_scaleX = a->scalex; + c->_scaleY = a->scaley; +} + +void Scumm::setActorCostPalette(Actor *a) { + int i; + byte color; + + cost.loadCostume(a->costume); + + for (i=0; i<cost._numColors; i++) { + color = a->palette[i]; + if (color==255) + color = cost._ptr[8+i]; + cost._palette[i] = color; + } +} + +void Scumm::drawActorCostume(Actor *a) { + if (a==NULL || !a->needRedraw) + return; + + a->top = 0xFF; + a->needRedraw = 0; + a->bottom = 0; + cost.loadCostume(a->costume); + cost._mirror = a->facing!=0 || (cost._ptr[7]&0x80); + + if (cost.drawCostume(a)) { + a->needRedraw = true; + a->needBgReset = true;; + } +} + +void Scumm::actorAnimate(Actor *a) { + if (a==NULL) + return; + + a->animProgress++; + if (a->animProgress >= a->animSpeed) { + a->animProgress = 0; + cost.loadCostume(a->costume); + if (cost.animate(&a->cost)) { + a->needRedraw = true; + a->needBgReset = true; + } + } +} + +void Scumm::setActorRedrawFlags() { + int i,j; + int bits; + + for (i=0; i<40; i++) { + bits = actorDrawBits[_screenStartStrip+i]; + if (bits&0x3FFF) { + for(j=0; j<13; j++) { + if ((bits&(1<<j)) && bits!=(1<<j)) { + Actor *a = derefActor(j); + a->needRedraw = true; + a->needBgReset = true; + } + } + } + } +} + +int Scumm::getActorFromPos(int x, int y) { + uint16 drawbits; + int i; + + drawbits = actorDrawBits[x>>3]; + if (!(drawbits & 0x3FFF)) + return 0; + for (i=1; i<13; 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); + + if (_actorToPrintStrFor==0xFF) { + if (!_keepText) + stopTalk(); + vm.vars[VAR_TALK_ACTOR] = 0xFF; + oldact = 0; + } else { + a = derefActorSafe(_actorToPrintStrFor, "actorTalk"); + if (a->room!=_currentRoom) { + oldact = 0xFF; + } else { + if (!_keepText) + stopTalk(); + vm.vars[VAR_TALK_ACTOR] = a->number; + startAnimActor(a,a->talkFrame1,a->facing); + oldact = vm.vars[VAR_TALK_ACTOR]; + } + } + if (oldact>=0x80) + return; + + if (vm.vars[VAR_TALK_ACTOR]>0x7F) { + _charsetColor = _stringColor[0]; + } else { + a = derefActorSafe(vm.vars[VAR_TALK_ACTOR], "actorTalk(2)"); + _charsetColor = a->talkColor; + } + charset._bufPos = 0; + _talkDelay = 0; + _haveMsg = 0xFF; + vm.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); + + _xPos = abr.x; + _yPos = abr.y; + + if (a->room != _currentRoom) { + a->x = _xPos; + a->y = _yPos; + if (dir != 0xFF) + a->facing = dir; + return; + } + + if (a->ignoreBoxes!=0) { + abr.x = _xPos; + abr.y = _yPos; + abr.dist = 0; + a->walkbox = 0; + } else { + if (checkXYInBoxBounds(a->walkdata.destbox, _xPos,_yPos)) { + abr.x = _xPos; + abr.y = _yPos; + abr.dist = a->walkdata.destbox; + } else { + abr = adjustXYToBeInBox(a, _xPos, _yPos); + } + 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) { + if (dir!=0xFF && dir!=a->facing) { + a->newDirection = dir; + a->moving = 4; + } + return; + } + + a->walkdata.destx = abr.x; + a->walkdata.desty = abr.y; + a->walkdata.destbox = abr.dist; /* a box */ + a->walkdata.destdir = dir; + a->moving = (a->moving&2)|1; + a->walkdata.curbox = a->walkbox; +} + +byte *Scumm::getActorName(Actor *a) { + byte *ptr = getResourceAddress(9, a->number); + if(ptr==NULL) + return (byte*)" "; + return ptr; +} |
