mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-18 07:39:08 +00:00
6e508eed29
artifacts left on screen. Anyone want to fix this? :) svn-id: r3909
1367 lines
26 KiB
C++
1367 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 (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;
|
|
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)
|
|
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)
|
|
{
|
|
// Yazoo: need to recheck with Loom for the class data flags (0x400000)
|
|
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 &= ~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;
|
|
}
|