mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 04:07:05 +00:00
141bac6fcb
svn-id: r3880
1338 lines
26 KiB
C++
1338 lines
26 KiB
C++
/* 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 (a->init != 1) { /* Check if this is the first time we've */
|
|
a->costume = 0; /* used this actor. If so, init everything */
|
|
a->room = 0;
|
|
a->x = a->y = 0;
|
|
a->facing = 0;
|
|
a->newDirection = 0;
|
|
a->visible = 0;
|
|
a->mask = 0;
|
|
a->walkbox = 0;
|
|
a->animProgress = a->animSpeed = 0;
|
|
a->layer = 0;
|
|
|
|
a->init = 1;
|
|
}
|
|
|
|
|
|
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 (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;
|
|
|
|
if(_gameId==GID_DIG) {
|
|
double temp;
|
|
temp = atan2 (XYFactor, -YXFactor);
|
|
a->newDirection = normalizeAngle((int)(temp * 1.8e2 / 3.14));
|
|
} else {
|
|
a->newDirection = getAngleFromPos(XYFactor, YXFactor);
|
|
}
|
|
|
|
return actorWalkStep(a);
|
|
}
|
|
|
|
int Scumm::remapDirection(Actor *a, int dir) {
|
|
int specdir;
|
|
byte flags;
|
|
byte dirflag;
|
|
|
|
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);
|
|
|
|
dirflag = ((a->walkdata.XYFactor>0) ? 1 : 0) |
|
|
((a->walkdata.YXFactor>0) ? 2 : 0);
|
|
|
|
|
|
if ((flags&8) || getClass(a->number, 0x1E)) {
|
|
dir = 360 - dir;
|
|
dirflag ^= 1;
|
|
}
|
|
|
|
if ((flags&0x10) || getClass(a->number, 0x1D)) {
|
|
dir = 180 - dir;
|
|
dirflag ^= 2;
|
|
}
|
|
|
|
switch(flags & 7) {
|
|
case 1:
|
|
return (dirflag&1) ? 90 : 270;
|
|
case 2:
|
|
return (dirflag&2) ? 180 : 0;
|
|
case 3: return 270;
|
|
case 4: return 90;
|
|
case 5: return 0;
|
|
case 6: return 180;
|
|
}
|
|
}
|
|
return normalizeAngle(dir);
|
|
}
|
|
|
|
int Scumm::updateActorDirection(Actor *a) {
|
|
int from,to;
|
|
int diff;
|
|
int dirType;
|
|
int num;
|
|
|
|
dirType = akos_hasManyDirections(a);
|
|
|
|
from = toSimpleDir(dirType, a->facing);
|
|
to = toSimpleDir(dirType, remapDirection(a, a->newDirection));
|
|
diff = to - from;
|
|
|
|
num = numSimpleDirDirections(dirType);
|
|
|
|
if (abs(diff) > (num>>1))
|
|
diff = -diff;
|
|
|
|
if (diff==0) {
|
|
} else if (diff>0) {
|
|
from++;
|
|
} else {
|
|
from--;
|
|
}
|
|
|
|
return fromSimpleDir(dirType, from & (num-1));
|
|
}
|
|
|
|
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;
|
|
|
|
if (_features & GF_NO_SCALLING) {
|
|
a->scalex = 0xFF;
|
|
a->scaley = 0xFF;
|
|
return;
|
|
}
|
|
|
|
if (a->ignoreBoxes != 0)
|
|
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);
|
|
|
|
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::fixActorDirection(Actor *a, int direction) {
|
|
uint mask;
|
|
int i;
|
|
uint16 vald;
|
|
|
|
if (a->facing == direction)
|
|
return;
|
|
|
|
a->facing = 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->room != _currentRoom)
|
|
return -1;
|
|
_xPos = a->x;
|
|
_yPos = a->y;
|
|
return 0;
|
|
}
|
|
|
|
AdjustBoxResult Scumm::adjustXYToBeInBox(Actor *a, int x, int y, int pathfrom) {
|
|
// Yazoo: need to recheck with Loom for the class data flags (0x400000)
|
|
AdjustBoxResult abr,tmp;
|
|
uint threshold;
|
|
uint best;
|
|
int box, iterations = 0; /* Use inerations 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 (a && a->ignoreBoxes==0) {
|
|
threshold = 30;
|
|
|
|
if ((_features & GF_SMALL_HEADER) && (_classData[a->number] & 0x200000))
|
|
return abr;
|
|
|
|
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 &= ~4;
|
|
|
|
if (newdir != a->facing) {
|
|
a->moving = 4;
|
|
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 */
|
|
//a->facing = angle;
|
|
fixActorDirection(a, angle);
|
|
startAnimActor(a, a->walkFrame);
|
|
break;
|
|
case 2: /* change dir only */
|
|
fixActorDirection(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&1)) {
|
|
if (a->moving&2 && actorWalkStep(a))
|
|
return;
|
|
|
|
if (a->moving&8) {
|
|
a->moving = 0;
|
|
setActorBox(a, a->walkdata.destbox);
|
|
startWalkAnim(a, 3, a->walkdata.destdir);
|
|
return;
|
|
}
|
|
|
|
if (a->moving&4) {
|
|
j = updateActorDirection(a);
|
|
if (a->facing != j)
|
|
fixActorDirection(a,j);
|
|
else
|
|
a->moving = 0;
|
|
return;
|
|
}
|
|
|
|
setActorBox(a, a->walkdata.curbox);
|
|
a->moving &= 2;
|
|
}
|
|
|
|
#if OLD
|
|
a->moving &= ~1;
|
|
|
|
if (!a->walkbox) {
|
|
a->walkbox = a->walkdata.destbox;
|
|
a->walkdata.curbox = a->walkdata.destbox;
|
|
a->moving |= 8;
|
|
calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty);
|
|
return;
|
|
}
|
|
|
|
if (a->ignoreBoxes || a->walkbox==a->walkdata.destbox) {
|
|
a->walkdata.curbox = a->walkbox;
|
|
a->moving |= 8;
|
|
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 |= 8;
|
|
calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty);
|
|
return;
|
|
}
|
|
calcMovementFactor(a, _foundPathX, _foundPathY);
|
|
#endif
|
|
#if 1
|
|
do {
|
|
a->moving&=~1;
|
|
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 |= 8;
|
|
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 |= 8;
|
|
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 |= 8;
|
|
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;
|
|
|
|
a->needRedraw = false;
|
|
|
|
setupActorScale(a);
|
|
|
|
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);
|
|
|
|
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) {
|
|
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)
|
|
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&2)|1;
|
|
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&1)
|
|
{
|
|
restart:
|
|
a->moving &= ~1;
|
|
|
|
if (a->walkbox==0xFF)
|
|
{
|
|
a->walkbox = a->walkdata.destbox;
|
|
a->walkdata.curbox = a->walkdata.destbox;
|
|
a->moving |=8;
|
|
calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty);
|
|
return;
|
|
}
|
|
|
|
if (a->walkbox==a->walkdata.destbox)
|
|
{
|
|
a->moving |=8;
|
|
calcMovementFactor(a, a->walkdata.destx, a->walkdata.desty);
|
|
return;
|
|
}
|
|
|
|
next_box = getPathToDestBox(a->walkbox,a->walkdata.destbox);
|
|
|
|
if( next_box == -1)
|
|
{
|
|
a->moving |=8;
|
|
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 |=8;
|
|
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 & 2)
|
|
{
|
|
if(actorWalkStep(a))
|
|
return;
|
|
}
|
|
|
|
if(a->moving & 8)
|
|
{
|
|
a->moving = 0;
|
|
startWalkAnim(a, 3, a->walkdata.destdir);
|
|
return;
|
|
}
|
|
|
|
if(a->moving & 4)
|
|
{
|
|
new_dir = updateActorDirection(a);
|
|
if (a->facing != new_dir)
|
|
{
|
|
fixActorDirection(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 &= 2;
|
|
a->moving |= 1;
|
|
goto restart;
|
|
}
|