From d6a4ffc2b034aa4ee065451ce7d611e0501d305b Mon Sep 17 00:00:00 2001 From: Andrew Kurushin Date: Tue, 28 Dec 2004 21:27:18 +0000 Subject: - many actor walk related functions added - implemented script functions for scriptDoors (except iso mode) - introduced getDisplayWidth() getDisplayHeight() getStatusYOffset getPathYOffset() svn-id: r16363 --- saga/actor.cpp | 573 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- saga/actor.h | 63 ++++++- saga/events.cpp | 2 +- saga/game.cpp | 24 +++ saga/saga.h | 21 ++- saga/scene.cpp | 133 +++++++++++-- saga/script.h | 8 +- saga/sfuncs.cpp | 60 ++++-- saga/sprite.cpp | 16 +- 9 files changed, 816 insertions(+), 84 deletions(-) diff --git a/saga/actor.cpp b/saga/actor.cpp index 9dab75e774..3cc796e81f 100644 --- a/saga/actor.cpp +++ b/saga/actor.cpp @@ -53,9 +53,8 @@ static int actorCompare(const ActorDataPointer& actor1, const ActorDataPointer& } } - // Lookup table to convert 8 cardinal directions to 4 -int ActorDirectectionsLUT[8] = { +int actorDirectectionsLUT[8] = { ACTOR_DIRECTION_BACK, // kDirUp ACTOR_DIRECTION_RIGHT, // kkDirUpRight ACTOR_DIRECTION_RIGHT, // kDirRight @@ -66,6 +65,18 @@ int ActorDirectectionsLUT[8] = { ACTOR_DIRECTION_LEFT, // kDirUpLeft }; +PathDirectionData pathDirectionLUT[8][4] = { + {{0, 0, -1}, {7, -1, -1}, {4, 1, -1}}, + {{1, 1, 0}, {4, 1, -1}, {5, 1, 1}}, + {{2, 0, 1}, {5, 1, 1}, {6, -1, 1}}, + {{3, -1, 0}, {6, -1, 1}, {7, -1, -1}}, + {{0, 0, -1}, {1, 1, 0}, {4, 1, -1}}, + {{1, 1, 0}, {2, 0, 1}, {5, 1, 1}}, + {{2, 0, 1}, {3, -1, 0}, {6, -1, 1}}, + {{3, -1, 0}, {0, 0, -1}, {7, -1, -1}} +}; + + Actor::Actor(SagaEngine *vm) : _vm(vm) { int i; ActorData *actor; @@ -73,6 +84,18 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) { _centerActor = _protagonist = NULL; _lastTickMsec = 0; + + _yCellCount = _vm->getStatusYOffset() - _vm->getPathYOffset(); + _xCellCount = _vm->getDisplayWidth() / 2; + + _pathCellCount = _yCellCount * _xCellCount; + _pathCell = (byte*)malloc(_pathCellCount); + + _pathRect.left = 0; + _pathRect.right = _vm->getDisplayWidth(); + _pathRect.top = _vm->getPathYOffset(); + _pathRect.bottom = _vm->getStatusYOffset() - _vm->getPathYOffset(); + // Get actor resource file context _actorContext = _vm->getFileContext(GAME_RESOURCEFILE, 0); if (_actorContext == NULL) { @@ -112,6 +135,7 @@ Actor::~Actor() { ActorData *actor; debug(9, "Actor::~Actor()"); + free(_pathCell); //release resources for (i = 0; i < ACTORCOUNT; i++) { actor = &_actors[i]; @@ -175,7 +199,7 @@ bool Actor::loadActorResources(ActorData * actor) { i = _vm->_sprite->getListLen(actor->spriteList); - if ( (lastFrame >= i)) { + if (lastFrame >= i) { debug(9, "Appending to sprite list 0x%X", actor->spriteListResourceId); if (_vm->_sprite->appendList(actor->spriteListResourceId + 1, actor->spriteList) != SUCCESS) { warning("Unable append sprite list"); @@ -244,7 +268,7 @@ ActorFrameRange *Actor::getActorFrameRange(uint16 actorId, int frameType) { if ((actor->facingDirection < kDirUp) || (actor->facingDirection > kDirUpLeft)) error("Actor::getActorFrameRange Wrong direction 0x%X actorId 0x%X", actor->facingDirection, actorId); - fourDirection = ActorDirectectionsLUT[actor->facingDirection]; + fourDirection = actorDirectectionsLUT[actor->facingDirection]; return &actor->frames[frameType].directions[fourDirection]; } @@ -343,7 +367,10 @@ void Actor::handleActions(int msec, bool setup) { ActorData *actor; ActorFrameRange *frameRange; int state; - + int speed; + ActorLocation delta; + ActorLocation addDelta; + for (i = 0; i < ACTORCOUNT; i++) { actor = &_actors[i]; if (actor->disabled) continue; @@ -354,7 +381,7 @@ void Actor::handleActions(int msec, bool setup) { switch(actor->currentAction) { case kActionWait: { if (!setup && (actor->flags & kFollower)) { - //todo: followProtagonist + followProtagonist(actor); if (actor->currentAction != kActionWait) break; } @@ -364,7 +391,7 @@ void Actor::handleActions(int msec, bool setup) { } if (actor->flags & kCycle) { - frameRange = getActorFrameRange( actor->actorId, kFrameStand); + frameRange = getActorFrameRange(actor->actorId, kFrameStand); if (frameRange->frameCount > 0) { actor->actionCycle++; actor->actionCycle = (actor->actionCycle) % frameRange->frameCount; @@ -378,9 +405,9 @@ void Actor::handleActions(int msec, bool setup) { if ((actor->actionCycle & 3) == 0) { actor->cycleWrap(100); - frameRange = getActorFrameRange( actor->actorId, kFrameWait); + frameRange = getActorFrameRange(actor->actorId, kFrameWait); if ((frameRange->frameCount < 1 || actor->actionCycle > 33)) - frameRange = getActorFrameRange( actor->actorId, kFrameStand); + frameRange = getActorFrameRange(actor->actorId, kFrameStand); if (frameRange->frameCount) { actor->frameNumber = frameRange->frameIndex + (uint16)rand() % frameRange->frameCount; @@ -392,8 +419,87 @@ void Actor::handleActions(int msec, bool setup) { } break; case kActionWalkToPoint: case kActionWalkToLink: { - //todo: do it - debug(9,"kActionWalk* not implemented"); + // tiled stuff + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + actor->partialTarget.delta(actor->location, delta); + + while ((delta.x == 0) && (delta.y == 0)) { + int xstep; + + if (actor->walkStepIndex >= actor->walkStepsCount) { + actorEndWalk(actor->actorId, true); + break; + } + + xstep = actor->walkPath[actor->walkStepIndex++]; + if (xstep > 256-32) { + xstep -= 256; + } + + actor->partialTarget.x = xstep * 2 * ACTOR_LMULT; + actor->partialTarget.y = actor->walkPath[actor->walkStepIndex++] * ACTOR_LMULT; + actor->partialTarget.z = 0; + + actor->partialTarget.delta(actor->location, delta); + + if (ABS(delta.y) > ABS(delta.x)) { + actor->actionDirection = delta.y > 0 ? kDirDown : kDirUp; + } else { + actor->actionDirection = delta.x > 0 ? kDirRight : kDirLeft; + } + } + + speed = (ACTOR_LMULT * 2 * actor->screenScale + 63) / 256; + if (speed < 1) { + speed = 1; + } + + if ((actor->actionDirection == kDirUp) || (actor->actionDirection == kDirDown)) { // move by 2's in vertical dimension + addDelta.y = clamp(-speed, delta.y, speed); + if (addDelta.y == delta.y) { + addDelta.x = delta.x; + } else { + addDelta.x = delta.x * addDelta.y; + addDelta.x += (addDelta.x > 0) ? (delta.y / 2) : (-delta.y / 2); + addDelta.x /= delta.y; + actor->facingDirection = actor->actionDirection; + } + } else { + addDelta.x = clamp(-2 * speed, delta.x, 2 * speed); + if (addDelta.x == delta.x) { + addDelta.y = delta.y; + } else { + addDelta.y = delta.y * addDelta.x; + addDelta.y += (addDelta.y > 0) ? (delta.x / 2) : (-delta.x / 2); + addDelta.y /= delta.x; + actor->facingDirection = actor->actionDirection; + } + } + + actor->location.add(addDelta); + } + + + if (actor->actorFlags & kActorBackwards) { + actor->facingDirection = (actor->actionDirection + 4) & 7; + actor->actionCycle--; + } else { + actor->actionCycle++; + } + + frameRange = getActorFrameRange(actor->actorId, actor->walkFrameSequence); + + if (actor->actionCycle < 0) { + actor->actionCycle = frameRange->frameCount - 1; + } else { + if (actor->actionCycle >= frameRange->frameCount) { + actor->actionCycle = 0; + } + } + + actor->frameNumber = frameRange->frameIndex + actor->actionCycle; } break; case kActionWalkDir: { debug(9,"kActionWalkDir not implemented"); @@ -403,15 +509,15 @@ void Actor::handleActions(int msec, bool setup) { actor->actionCycle++; actor->cycleWrap(64); - frameRange = getActorFrameRange( actor->actorId, kFrameGesture); + frameRange = getActorFrameRange(actor->actorId, kFrameGesture); if (actor->actionCycle >= frameRange->frameCount) { if (actor->actionCycle & 1) break; - frameRange = getActorFrameRange( actor->actorId, kFrameSpeak); + frameRange = getActorFrameRange(actor->actorId, kFrameSpeak); state = (uint16)rand() % (frameRange->frameCount + 1); if (state == 0) { - frameRange = getActorFrameRange( actor->actorId, kFrameStand); + frameRange = getActorFrameRange(actor->actorId, kFrameStand); } else { state--; } @@ -436,7 +542,7 @@ void Actor::handleActions(int msec, bool setup) { actor->cycleTimeCount = actor->cycleDelay; actor->actionCycle++; - frameRange = getActorFrameRange( actor->actorId, actor->cycleFrameSequence); + frameRange = getActorFrameRange(actor->actorId, actor->cycleFrameSequence); if (actor->currentAction == kActionPongFrames) { if (actor->actionCycle >= frameRange->frameCount * 2 - 2) { @@ -507,11 +613,10 @@ int Actor::direct(int msec) { void Actor::calcActorScreenPosition(ActorData * actor) { int beginSlope, endSlope, middle; - // tiled stuff - { - } - { - middle = ITE_STATUS_Y - actor->location.y / ACTOR_LMULT; + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + middle = _vm->getStatusYOffset() - actor->location.y / ACTOR_LMULT; _vm->_scene->getSlopes(beginSlope, endSlope); @@ -582,10 +687,11 @@ int Actor::drawActors() { continue; } - // tiled stuff - { + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + _vm->_sprite->drawOccluded(back_buf, spriteList, frameNumber, actor->screenPosition, actor->screenScale, actor->screenDepth); } - _vm->_sprite->drawOccluded(back_buf, spriteList, frameNumber, actor->screenPosition, actor->screenScale, actor->screenDepth); } // draw speeches @@ -640,6 +746,85 @@ void Actor::StoA(Point &actorPoint, const Point &screenPoint) { } bool Actor::followProtagonist(ActorData * actor) { + ActorLocation protagonistLocation; + ActorLocation newLocation; + ActorLocation delta; + int protagonistBGMaskType; + Point prefer1; + Point prefer2; + Point prefer3; + + assert(_protagonist); + + actor->flags &= ~(kFaster | kFastest); + protagonistLocation = _protagonist->location; + + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + prefer1.x = (100 * _protagonist->screenScale) >> 8; + prefer1.y = (50 * _protagonist->screenScale) >> 8; + + if (_protagonist->currentAction == kActionWalkDir) { + prefer1.x /= 2; + } + + if (prefer1.x < 8) { + prefer1.x = 8; + } + + if (prefer1.y < 8) { + prefer1.y = 8; + } + + prefer2.x = prefer1.x * 2; + prefer2.y = prefer1.y * 2; + prefer3.x = prefer1.x + prefer1.x / 2; + prefer3.y = prefer1.y + prefer1.y / 2; + + actor->location.delta(protagonistLocation, delta); + + calcActorScreenPosition(_protagonist); + protagonistBGMaskType = _vm->_scene->getBGMaskType(_protagonist->screenPosition); + + + if ((rand() & 0x7) == 0) + actor->actorFlags &= ~kActorNoFollow; + + if (actor->actorFlags & kActorNoFollow) { + return false; + } + + if ((delta.x > prefer2.x) || (delta.x < -prefer2.x) || + (delta.y > prefer2.y) || (delta.y < -prefer2.y) || + ((_protagonist->currentAction == kActionWait) && + (delta.x * 2 < prefer1.x) && (delta.x * 2 > -prefer1.x) && + (delta.y < prefer1.y) && (delta.y > -prefer1.y))) { + + if (ABS(delta.x) > ABS(delta.y)) { + + delta.x = (delta.x > 0) ? prefer3.x : -prefer3.x; + + newLocation.x = delta.x + protagonistLocation.x; + newLocation.y = clamp(-prefer2.y, delta.y, prefer2.y) + protagonistLocation.y; + } else { + delta.y = (delta.y > 0) ? prefer3.y : -prefer3.y; + + newLocation.x = clamp(-prefer2.x, delta.x, prefer2.x) + protagonistLocation.x; + newLocation.y = delta.y + protagonistLocation.y; + } + newLocation.z = 0; + + if (protagonistBGMaskType != 3) { + newLocation.x += (rand() % prefer1.x) - prefer1.x / 2; + newLocation.y += (rand() % prefer1.y) - prefer1.y / 2; + } + + newLocation.x = clamp(-31*4, newLocation.x, (_vm->getDisplayWidth() + 31) * 4); //fixme + + return actorWalkTo(actor->actorId, newLocation); + } + } return false; } @@ -680,27 +865,154 @@ bool Actor::actorEndWalk(uint16 actorId, bool recurse) { bool Actor::actorWalkTo(uint16 actorId, const ActorLocation &toLocation) { ActorData *actor; + ActorData *anotherActor; + int i; + + Rect testBox; + Rect testBox2; + Point anotherActorScreenPosition; + Point collision; + Point pointFrom, pointTo, pointBest, pointAdd; + Point delta, bestDelta; + bool extraStartNode; + bool extraEndNode; actor = getActor(actorId); + if (actor == _protagonist) { + _vm->_scene->setDoorState(2, 0xff); + _vm->_scene->setDoorState(3, 0); + } else { + _vm->_scene->setDoorState(2, 0); + _vm->_scene->setDoorState(3, 0xff); + } - // tiled stuff - { + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { //todo: it - } - { - Point pointFrom, pointTo; + } else { pointFrom.x = actor->location.x / ACTOR_LMULT; pointFrom.y = actor->location.y / ACTOR_LMULT; + extraStartNode = _vm->_scene->offscreenPath(pointFrom); pointTo.x = toLocation.x / ACTOR_LMULT; pointTo.y = toLocation.y / ACTOR_LMULT; + extraEndNode = _vm->_scene->offscreenPath(pointTo); if (_vm->_scene->isBGMaskPresent()) { - //todo: it + + if ((((actor->currentAction >= kActionWalkToPoint) && + (actor->currentAction <= kActionWalkDir)) || (actor == _protagonist)) && + !_vm->_scene->canWalk(pointFrom)) { + for (i = 1; i < 8; i++) { + pointAdd = pointFrom; + pointAdd.y += i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + pointAdd = pointFrom; + pointAdd.y -= i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + pointAdd = pointFrom; + pointAdd.x += i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + pointAdd = pointFrom; + pointAdd.x -= i; + if (_vm->_scene->canWalk(pointAdd)) { + pointFrom = pointAdd; + break; + } + } + } + + if (!(actor->actorFlags & kActorNoCollide)) { + collision.x = ACTOR_COLLISION_WIDTH * actor->screenScale / (256 * 2); + collision.y = ACTOR_COLLISION_HEIGHT * actor->screenScale / (256 * 2); + + _barrierCount = 0; + + for (i = 0; (i < ACTORCOUNT) && (_barrierCount < ACTOR_BARRIERS_MAX); i++) { + anotherActor = &_actors[i]; + if (anotherActor->disabled) continue; + if (anotherActor->sceneNumber != _vm->_scene->currentSceneNumber()) continue; + if (anotherActor == actor ) continue; + + + anotherActorScreenPosition = anotherActor->screenPosition; + testBox.left = (anotherActorScreenPosition.x - collision.x) & ~1; + testBox.right = (anotherActorScreenPosition.x + collision.x) & ~1; + testBox.top = anotherActorScreenPosition.y - collision.y; + testBox.bottom = anotherActorScreenPosition.y + collision.y; + testBox2 = testBox; + testBox2.right += 2; + testBox2.left -= 1; + testBox2.bottom += 1; + + if (testBox2.contains(pointFrom)) { + if (pointFrom.x > anotherActorScreenPosition.x + 4) { + testBox.right = pointFrom.x - 2; + } else { + if (pointFrom.x < anotherActorScreenPosition.x - 4) { + testBox.left = pointFrom.x + 2; + } else { + if (pointFrom.y > anotherActorScreenPosition.y) { + testBox.bottom = pointFrom.y - 1; + } else { + testBox.top = pointFrom.y + 1 ; + } + } + } + } + + if ((testBox.left <= testBox.right) && (testBox.top <= testBox.bottom)) { + _barrierList[_barrierCount++] = testBox; + } + } + } + + + pointBest = pointTo; + actor->walkStepsCount = 0; + findActorPath(actor, pointFrom, pointTo); + + if (extraStartNode) { + actor->walkStepIndex = 0; + } else { + actor->walkStepIndex = 2; + } + + if (extraEndNode) { + actor->walkPath[actor->walkStepsCount - 2] = pointTo.x / (ACTOR_LMULT * 2); + actor->walkPath[actor->walkStepsCount - 1] = pointTo.y / ACTOR_LMULT; + } + + pointBest.x = actor->walkPath[actor->walkStepsCount - 2] * 2; + pointBest.y = actor->walkPath[actor->walkStepsCount - 1]; + + pointFrom.x &= ~1; + delta.x = ABS(pointFrom.x - pointTo.x); + delta.y = ABS(pointFrom.y - pointTo.y); + + bestDelta.x = ABS(pointBest.x - pointTo.x); + bestDelta.y = ABS(pointBest.y - pointTo.y); + + if (delta.x + delta.y <= bestDelta.x + bestDelta.y) { + if (actor->flags & kFollower) + actor->actorFlags |= kActorNoFollow; + } + + if (pointBest == pointFrom) { + actor->walkStepsCount = 0; + } } else { actor->walkPath[0] = pointTo.x / 2; actor->walkPath[1] = pointTo.y; @@ -717,8 +1029,7 @@ bool Actor::actorWalkTo(uint16 actorId, const ActorLocation &toLocation) { if (actor->flags & kProtagonist) { _actors[1].actorFlags &= ~kActorNoFollow; _actors[2].actorFlags &= ~kActorNoFollow; - } - + } actor->currentAction = (actor->walkStepsCount == ACTOR_STEPS_COUNT) ? kActionWalkToLink : kActionWalkToPoint; actor->walkFrameSequence = kFrameWalk; } @@ -798,6 +1109,206 @@ void Actor::abortSpeech() { _activeSpeech.playingTime = 0; } +void Actor::findActorPath(ActorData * actor, const Point &pointFrom, const Point &pointTo) { + Point tempPoint; + Point iteratorPoint; + Point bestPoint; + Point maskPoint; + int maskType1, maskType2; + byte cellValue; + int i; + Rect intersect; + + tempPoint.y = pointTo.y; + tempPoint.x = pointTo.x >> 1; + + actor->walkStepsCount = 0; + if (pointFrom == pointTo) { + actor->addWalkPath(tempPoint.x, tempPoint.y); + return; + } + + for (iteratorPoint.y = 0; iteratorPoint.y < _yCellCount; iteratorPoint.y++) { + maskPoint.y = iteratorPoint.y + _vm->getPathYOffset(); + for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) { + maskPoint.x = iteratorPoint.x * 2; + maskType1 = _vm->_scene->getBGMaskType(maskPoint); + maskPoint.x += 1; + maskType2 = _vm->_scene->getBGMaskType(maskPoint); + cellValue = (maskType1 | maskType2) ? 'W' : -1; + setPathCell(iteratorPoint, cellValue); + } + } + + for (i = 0; i < _barrierCount; i++) { + intersect.left = MAX(_pathRect.left, _barrierList[i].left); + intersect.top = MAX(_pathRect.top, _barrierList[i].top); + intersect.right = MIN(_pathRect.right, _barrierList[i].right); + intersect.bottom = MIN(_pathRect.bottom, _barrierList[i].bottom); + + intersect.left >>= 1; + intersect.top -= _vm->getPathYOffset(); + intersect.right >>= 1; + intersect.bottom -= _vm->getPathYOffset(); + + for (iteratorPoint.y = intersect.top; iteratorPoint.y < intersect.bottom; iteratorPoint.y++) { + for (iteratorPoint.x = 0; iteratorPoint.x < _xCellCount; iteratorPoint.x++) { + setPathCell(iteratorPoint, 'W'); + } + } + } + + + + if (scanPathLine(pointFrom, pointTo)) { + iteratorPoint.y = pointFrom.y; + iteratorPoint.x = pointFrom.x >> 1; + actor->addWalkPath(iteratorPoint.x, iteratorPoint.y); + actor->addWalkPath(tempPoint.x, tempPoint.y); + return; + } + + + i = fillPathArray(pointFrom, pointTo, bestPoint); + + if (pointFrom == bestPoint) { + iteratorPoint.y = bestPoint.y; + iteratorPoint.x = bestPoint.x >> 1; + actor->addWalkPath(iteratorPoint.x, iteratorPoint.y); + return; + } +/* + if (i != 0) + result = SetPath(pcell, from, &bestpoint, nodelist); +*/ +} + +bool Actor::scanPathLine(const Point &point1, const Point &point2) { + Point point; + Point delta; + bool interchange = false; + Point fDelta; + Point iteratorPoint; + int errterm; + int s1; + int s2; + int i; + + point = point1; + delta.x = ABS(point1.x - point2.x); + delta.y = ABS(point1.y - point2.y); + s1 = integerCompare(point2.x, point1.x); + s2 = integerCompare(point2.y, point1.y); + + if (delta.y > delta.x) { + SWAP(delta.y, delta.x); + interchange = true; + } + + fDelta.x = delta.x * 2; + fDelta.y = delta.y * 2; + + errterm = fDelta.y - delta.x; + + for (i = 0; i < delta.x; i++) { + while (errterm >= 0) { + if (interchange) { + point.x += s1; + } else { + point.y += s2; + } + errterm -= fDelta.x; + } + + if (interchange) + point.y += s2; + else + point.x += s1; + + errterm += fDelta.y; + + iteratorPoint.x = point.x >> 1; + iteratorPoint.y = point.y - _vm->getPathYOffset(); + if (getPathCell(iteratorPoint) == 'W') + return false; + } + return true; +} + +int Actor::fillPathArray(const Point &pointFrom, const Point &pointTo, Point &bestPoint) { + Point pathFrom; + Point pathTo; + int bestRating; + int currentRating; + Point bestPath; + int pointCounter; + int startDirection; + PathDirectionList pathDirectionList; + PathDirectionData *pathDirection; + PathDirectionData *samplePathDirection; + PathDirectionList::iterator pathDirectionIterator; + PathDirectionList::iterator newPathDirectionIterator; + int directionCount; + + + pathFrom.x = pointFrom.x >> 1; + pathFrom.y = pointFrom.y - _vm->getPathYOffset(); + + pathTo.x = pointTo.x >> 1; + pathTo.y = pointTo.y - _vm->getPathYOffset(); + + pointCounter = 0; + bestRating = quickDistance(pathFrom, pathTo); + bestPath = pathFrom; + + for (startDirection = 0; startDirection < 4; startDirection++) { + newPathDirectionIterator = pathDirectionList.pushBack(); + pathDirection = newPathDirectionIterator.operator->(); + pathDirection->x = pathFrom.x; + pathDirection->y = pathFrom.y; + pathDirection->direction = startDirection; + } + setPathCell(pathFrom, 0); + + pathDirectionIterator = pathDirectionList.begin(); + + do { + pathDirection = pathDirectionIterator.operator->(); + for (directionCount = 0; directionCount < 4; directionCount++) { + samplePathDirection = &pathDirectionLUT[pathDirection->direction][directionCount]; + Point nextPoint; + nextPoint.x = samplePathDirection->x + pathDirection->x; + nextPoint.y = samplePathDirection->y + pathDirection->y; + if ((nextPoint.x >= 0) && (nextPoint.y >= 0) && (nextPoint.x < _xCellCount) && (nextPoint.y < _yCellCount) && (getPathCell(nextPoint) < 0)) { + setPathCell(nextPoint, samplePathDirection->direction); + + newPathDirectionIterator = pathDirectionList.pushBack(); + pathDirection = newPathDirectionIterator.operator->(); + pathDirection->x = nextPoint.x; + pathDirection->y = nextPoint.y; + pathDirection->direction = samplePathDirection->direction; + ++pointCounter; + if (nextPoint == pathTo) { + bestPoint.x = pointTo.x & ~1; + bestPoint.y = pointTo.y; + return pointCounter; + } + currentRating = quickDistance(nextPoint, pathTo); + if (currentRating < bestRating) { + bestRating = currentRating; + bestPath = nextPoint; + } + } + } + ++pathDirectionIterator; + } while (pathDirectionIterator != pathDirectionList.end()); + + bestPoint.x = bestPath.x * 2; + bestPoint.y = bestPath.y + _vm->getPathYOffset(); + + return pointCounter; +} + /* // Console wrappers - must be safe to run // TODO - checkup ALL arguments, cause wrong arguments may fall function with "error" diff --git a/saga/actor.h b/saga/actor.h index b48f321f0d..b842b5b380 100644 --- a/saga/actor.h +++ b/saga/actor.h @@ -32,18 +32,18 @@ namespace Saga { -#define ACTOR_BASE_SPEED 0.25 -#define ACTOR_BASE_ZMOD 0.5 +#define ACTOR_BARRIERS_MAX 16 #define ACTOR_STEPS_COUNT 32 #define ACTOR_STEPS_MAX (ACTOR_STEPS_COUNT*2) -#define ACTOR_ACTIONTIME 80 - #define ACTOR_DIALOGUE_HEIGHT 100 #define ACTOR_LMULT 4 +#define ACTOR_COLLISION_WIDTH 32 +#define ACTOR_COLLISION_HEIGHT 8 + #define ACTOR_DIRECTIONS_COUNT 4 // for ActorFrameSequence #define ACTOR_DIRECTION_RIGHT 0 #define ACTOR_DIRECTION_LEFT 1 @@ -112,6 +112,14 @@ enum ActorFlagsEx { }; +struct PathDirectionData { + int direction; + int x; + int y; +}; + +typedef SortedList PathDirectionList; + struct ActorFrameRange { int frameIndex; int frameCount; @@ -125,10 +133,24 @@ struct ActorLocation { int x; // Actor's logical coordinates int y; // int z; // + ActorLocation() { + x = y = z = 0; + } int distance(const ActorLocation &location) { - return MAX(abs(x - location.x), abs(y - location.y)); + return MAX(ABS(x - location.x), ABS(y - location.y)); + } + void delta(const ActorLocation &location, ActorLocation &result) { + result.x = x - location.x; + result.y = x - location.y; + result.z = z - location.z; + } + void add(const ActorLocation &location) { + x += location.x; + y += location.y; + z += location.z; } }; + struct ActorData { bool disabled; // Actor disabled in init section int index; // Actor index @@ -176,6 +198,12 @@ struct ActorData { if (actionCycle >= cycleLimit) actionCycle = 0; } + void addWalkPath(int x, int y) { + if (walkStepsCount + 2 > ACTOR_STEPS_MAX) + error("walkStepsCount exceeds"); + walkPath[walkStepsCount++] = x; + walkPath[walkStepsCount++] = y; + } ActorData() { memset(this, 0xFE, sizeof(*this)); @@ -248,17 +276,40 @@ private: void createDrawOrderList(); void calcActorScreenPosition(ActorData * actor); bool followProtagonist(ActorData * actor); + void findActorPath(ActorData * actor, const Point &pointFrom, const Point &pointTo); void handleSpeech(int msec); void handleActions(int msec, bool setup); - + void setPathCell(const Point &testPoint, byte value) { + _pathCell[testPoint.x + testPoint.y * _xCellCount] = value; + } + byte getPathCell(const Point &testPoint) { + return _pathCell[testPoint.x + testPoint.y * _xCellCount]; + } + bool scanPathLine(const Point &point1, const Point &point2); + int fillPathArray(const Point &pointFrom, const Point &pointTo, Point &bestPoint); + int _lastTickMsec; SagaEngine *_vm; RSCFILE_CONTEXT *_actorContext; ActorOrderList _drawOrderList; ActorData _actors[ACTORCOUNT]; SpeechData _activeSpeech; + Rect _barrierList[ACTOR_BARRIERS_MAX]; + int _barrierCount; + byte *_pathCell; + int _pathCellCount; + int _xCellCount; + int _yCellCount; + Rect _pathRect; + }; +inline int16 quickDistance(const Point &point1, const Point &point2) { + Point delta; + delta.x = ABS(point1.x - point2.x); + delta.y = ABS(point1.y - point2.y); + return ((delta.x < delta.y) ? (delta.y + delta.x / 2) : (delta.x + delta.y / 2)); +} } // End of namespace Saga #endif diff --git a/saga/events.cpp b/saga/events.cpp index 1c9313f40d..bbaf7c9811 100644 --- a/saga/events.cpp +++ b/saga/events.cpp @@ -178,7 +178,7 @@ int Events::handleContinuous(EVENT *event) { size_t len; _vm->_render->getBufferInfo(&buf_info); - _vm->_scene->getBGMaskInfo(&w, &h, &mask_buf, &len); + _vm->_scene->getBGMaskInfo(w, h, mask_buf, len); _vm->transitionDissolve(buf_info.bg_buf, buf_info.bg_buf_w, buf_info.bg_buf_h, buf_info.bg_buf_w, mask_buf, w, h, 0, 1, (320 - w) / 2, (200 - h) / 2, event_pc); diff --git a/saga/game.cpp b/saga/game.cpp index 0df41e6d0a..7c9a21427c 100644 --- a/saga/game.cpp +++ b/saga/game.cpp @@ -790,6 +790,30 @@ const GAME_SOUNDINFO SagaEngine::getSoundInfo(void) { return *GameModule.gamedesc->gd_soundinfo; } +int SagaEngine::getDisplayWidth() { + return GameDescs[GameModule.game_number].gd_logical_w; +} + +int SagaEngine::getDisplayHeight() { + return GameDescs[GameModule.game_number].gd_logical_h; +} + +int SagaEngine::getPathYOffset() { //fixme: should be in GameDesc + if (_gameType == GType_ITE) { + return 35; + } else { + return 35; //fixme i don't know exact value + } +} + +int SagaEngine::getStatusYOffset() { //fixme: should be in GameDesc + if (_gameType == GType_ITE) { + return ITE_STATUS_Y; + } else { + return IHNM_STATUS_Y; + } +} + int SagaEngine::getDisplayInfo(GAME_DISPLAYINFO *disp_info) { int game_n; diff --git a/saga/saga.h b/saga/saga.h index 77c93cdb86..735b6d4151 100644 --- a/saga/saga.h +++ b/saga/saga.h @@ -215,6 +215,22 @@ inline int ticksToMSec(int tick) { return tick * 1000 / kScriptTimeTicksPerSecond; } +inline int clamp(int minValue, int value, int maxValue) { + if (value <= minValue) { + return minValue; + } else { + if (value >= maxValue) { + return maxValue; + } else { + return value; + } + } +} + +inline int integerCompare(int i1, int i2) { + return ((i1) > (i2) ? 1 : ((i1) < (i2) ? -1 : 0)); +} + class SagaEngine : public Engine { void errorString(const char *buf_input, char *buf_output); @@ -298,7 +314,10 @@ public: const GAME_SOUNDINFO getSoundInfo(void); int getDisplayInfo(GAME_DISPLAYINFO *disp_info); int getSceneInfo(GAME_SCENEDESC *); - + int getDisplayWidth(); + int getDisplayHeight(); + int getStatusYOffset(); + int getPathYOffset(); private: int loadLanguage(void); int loadGame(int game_n_p); diff --git a/saga/scene.cpp b/saga/scene.cpp index ccfc96c092..58e41191bd 100644 --- a/saga/scene.cpp +++ b/saga/scene.cpp @@ -318,8 +318,8 @@ int Scene::getMode() { } void Scene::getSlopes(int &beginSlope, int &endSlope) { - beginSlope = ITE_STATUS_Y - _desc.beginSlope; // fixme: implement also IHNM_STATUS_Y - endSlope = ITE_STATUS_Y - _desc.endSlope; + beginSlope = _vm->getStatusYOffset() - _desc.beginSlope; + endSlope = _vm->getStatusYOffset() - _desc.endSlope; } int Scene::getBGInfo(SCENE_BGINFO *bginfo) { @@ -359,25 +359,132 @@ int Scene::getBGPal(PALENTRY **pal) { return SUCCESS; } -int Scene::getBGMaskInfo(int *w, int *h, byte **buf, size_t *buf_len) { - assert(_initialized); +int Scene::getBGMaskType(const Point &testPoint) { + uint offset; + if (!_bgMask.loaded) { + return 0; + } + offset = testPoint.x + testPoint.y * _vm->getDisplayWidth(); + if (offset >= _bgMask.buf_len) { + error("Scene::getBGMaskType offset 0x%X exceed bufferLength 0x%X", offset, _bgMask.buf_len); + } + + return (_bgMask.buf[offset] >> 4) & 0x0f; +} +bool Scene::canWalk(const Point &testPoint) { + int maskType; if (!_bgMask.loaded) { - return FAILURE; + return true; } + if ((testPoint.x < 0) || (testPoint.x > _bgMask.w) || + (testPoint.y < 0) || (testPoint.y > _bgMask.h)) { + return true; + } + maskType = getBGMaskType(testPoint); + return getDoorState(maskType) == 0; +} - *w = _bgMask.w; - *h = _bgMask.h; - *buf = _bgMask.buf; - *buf_len = _bgMask.buf_len; +bool Scene::offscreenPath(Point &testPoint) { + Point first; + Point second; + Point third; + int maskType; - return SUCCESS; + if (!_bgMask.loaded) { + return false; + } + + + first.x = clamp( 0, testPoint.x, _vm->getDisplayWidth() - 1 ); + first.y = clamp( 0, testPoint.y, _vm->getDisplayHeight() - 1 ); + if (first == testPoint) { + return false; + } + + if (first.y >= _vm->getDisplayHeight() - 1) { + first.y = 200 -1 - 1; + } + testPoint = first; + + if (testPoint.y != first.y) { + second.x = third.x = testPoint.x; + second.y = third.y = first.y; + for (;; second.x--, third.x++ ) { + if (second.x > 1) { + maskType = getBGMaskType(second); + if (getDoorState(maskType) == 0) { + testPoint.x = second.x - 1; + break; + } + } else { + if (third.x >= _vm->getDisplayWidth()) { + return false; + } + } + + if (third.x < _vm->getDisplayWidth()) { + maskType = getBGMaskType(third); + if (getDoorState(maskType) == 0) { + testPoint.x = third.x + 1; + break; + } + } + } + } + + if (testPoint.x != first.x) { + second.y = third.y = testPoint.y; + second.x = third.x = first.x; + for (;; second.y--, third.y++ ) { + if (second.y > 1) { + maskType = getBGMaskType(second); + if (getDoorState(maskType) == 0) { + testPoint.y = second.y - 1; + break; + } + } else { + if (third.y > _vm->getDisplayHeight() - 1) { + return false; + } + } + + if (third.y <= _vm->getDisplayHeight() - 1) { + maskType = getBGMaskType(third); + if (getDoorState(maskType) == 0) { + testPoint.y = third.y + 1; + break; + } + } + } + } + return true; } -int Scene::isBGMaskPresent() { - assert(_initialized); - return _bgMask.loaded; +void Scene::getBGMaskInfo(int &width, int &height, byte *&buffer, size_t &bufferLength) { + if (!_bgMask.loaded) { + error("Scene::getBGMaskInfo _bgMask not loaded"); + } + + width = _bgMask.w; + height = _bgMask.h; + buffer = _bgMask.buf; + bufferLength = _bgMask.buf_len; +} + +void Scene::setDoorState(int doorNumber, int doorState) { + if ((doorNumber < 0) || (doorNumber >= SCENE_DOORS_MAX)) + error("Scene::setDoorState wrong doorNumber"); + + _sceneDoors[doorNumber] = doorState; +} + +int Scene::getDoorState(int doorNumber) { + if ((doorNumber < 0) || (doorNumber >= SCENE_DOORS_MAX)) + error("Scene::getDoorState wrong doorNumber"); + + return _sceneDoors[doorNumber]; } int Scene::getInfo(SCENE_INFO *si) { diff --git a/saga/script.h b/saga/script.h index 4183dfc3f4..804b311150 100644 --- a/saga/script.h +++ b/saga/script.h @@ -319,11 +319,11 @@ private: int SF_setObjName(SCRIPTFUNC_PARAMS); int SF_getObjImage(SCRIPTFUNC_PARAMS); int SF_getNumber(SCRIPTFUNC_PARAMS); - int SF_openDoor(SCRIPTFUNC_PARAMS); - int SF_closeDoor(SCRIPTFUNC_PARAMS); + int sfScriptOpenDoor(SCRIPTFUNC_PARAMS); + int sfScriptCloseDoor(SCRIPTFUNC_PARAMS); int sfSetBgdAnimSpeed(SCRIPTFUNC_PARAMS); int SF_cycleColors(SCRIPTFUNC_PARAMS); - int SF_centerActor(SCRIPTFUNC_PARAMS); + int sfDoCenterActor(SCRIPTFUNC_PARAMS); int sfStartBgdAnimSpeed(SCRIPTFUNC_PARAMS); int SF_actorWalkToAsync(SCRIPTFUNC_PARAMS); int SF_enableZone(SCRIPTFUNC_PARAMS); @@ -355,7 +355,7 @@ private: int SF_sceneID(SCRIPTFUNC_PARAMS); int SF_changeActorScene(SCRIPTFUNC_PARAMS); int SF_climb(SCRIPTFUNC_PARAMS); - int SF_setDoorState(SCRIPTFUNC_PARAMS); + int sfSetDoorState(SCRIPTFUNC_PARAMS); int SF_setActorZ(SCRIPTFUNC_PARAMS); int SF_text(SCRIPTFUNC_PARAMS); int SF_getActorX(SCRIPTFUNC_PARAMS); diff --git a/saga/sfuncs.cpp b/saga/sfuncs.cpp index 4c8b3ffc5a..500742acc3 100644 --- a/saga/sfuncs.cpp +++ b/saga/sfuncs.cpp @@ -66,11 +66,11 @@ void Script::setupScriptFuncList(void) { OPCODE(SF_setObjName), OPCODE(SF_getObjImage), OPCODE(SF_getNumber), - OPCODE(SF_openDoor), - OPCODE(SF_closeDoor), + OPCODE(sfScriptOpenDoor), + OPCODE(sfScriptCloseDoor), OPCODE(sfSetBgdAnimSpeed), OPCODE(SF_cycleColors), - OPCODE(SF_centerActor), + OPCODE(sfDoCenterActor), OPCODE(sfStartBgdAnimSpeed), OPCODE(SF_actorWalkToAsync), OPCODE(SF_enableZone), @@ -102,7 +102,7 @@ void Script::setupScriptFuncList(void) { OPCODE(SF_sceneID), OPCODE(SF_changeActorScene), OPCODE(SF_climb), - OPCODE(SF_setDoorState), + OPCODE(sfSetDoorState), OPCODE(SF_setActorZ), OPCODE(SF_text), OPCODE(SF_getActorX), @@ -404,20 +404,30 @@ int Script::SF_getNumber(SCRIPTFUNC_PARAMS) { } // Script function #21 (0x15) -int Script::SF_openDoor(SCRIPTFUNC_PARAMS) { - for (int i = 0; i < nArgs; i++) - thread->pop(); +// Param1: door # +int Script::sfScriptOpenDoor(SCRIPTFUNC_PARAMS) { + int doorNumber; + doorNumber = getUWord(thread->pop()); - debug(1, "stub: SF_openDoor(), %d args", nArgs); + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + _vm->_scene->setDoorState(doorNumber, 0); + } return SUCCESS; } // Script function #22 (0x16) -int Script::SF_closeDoor(SCRIPTFUNC_PARAMS) { - for (int i = 0; i < nArgs; i++) - thread->pop(); +// Param1: door # +int Script::sfScriptCloseDoor(SCRIPTFUNC_PARAMS) { + int doorNumber; + doorNumber = getUWord(thread->pop()); - debug(1, "stub: SF_closeDoor(), %d args", nArgs); + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + _vm->_scene->setDoorState(doorNumber, 0xff); + } return SUCCESS; } @@ -442,10 +452,12 @@ int Script::SF_cycleColors(SCRIPTFUNC_PARAMS) { } // Script function #25 (0x19) -int Script::SF_centerActor(SCRIPTFUNC_PARAMS) { - ScriptDataWord param = thread->pop(); +// Param1: actor id +int Script::sfDoCenterActor(SCRIPTFUNC_PARAMS) { + uint16 actorId; + actorId = getSWord(thread->pop()); - debug(1, "stub: SF_centerActor(%d)", param); + _vm->_actor->_centerActor = _vm->_actor->getActor(actorId); return SUCCESS; } @@ -986,11 +998,19 @@ int Script::SF_climb(SCRIPTFUNC_PARAMS) { } // Script function #57 (0x39) -int Script::SF_setDoorState(SCRIPTFUNC_PARAMS) { - for (int i = 0; i < nArgs; i++) - thread->pop(); - - debug(1, "stub: SF_setDoorState(), %d args", nArgs); +// Param1: door # +// Param2: door state +int Script::sfSetDoorState(SCRIPTFUNC_PARAMS) { + int doorNumber; + int doorState; + doorNumber = getUWord(thread->pop()); + doorState = getUWord(thread->pop()); + + if (_vm->_scene->getMode() == SCENE_MODE_ISO) { + //todo: it + } else { + _vm->_scene->setDoorState(doorNumber, doorState); + } return SUCCESS; } diff --git a/saga/sprite.cpp b/saga/sprite.cpp index 6b8289a105..a68482afa1 100644 --- a/saga/sprite.cpp +++ b/saga/sprite.cpp @@ -286,10 +286,10 @@ int Sprite::drawOccluded(SURFACE *ds, SPRITELIST *sprite_list, int sprite_num, c CLIPINFO ci; // BG mask variables - int mask_w; - int mask_h; - byte *mask_buf; - size_t mask_buf_len; + int maskWidth; + int maskHeight; + byte *maskBuffer; + size_t maskBufferLength; byte *mask_row_p; int mask_z; @@ -326,7 +326,7 @@ int Sprite::drawOccluded(SURFACE *ds, SPRITELIST *sprite_list, int sprite_num, c sprite_data_p = sprite_p + readS.pos(); - _vm->_scene->getBGMaskInfo(&mask_w, &mask_h, &mask_buf, &mask_buf_len); + _vm->_scene->getBGMaskInfo(maskWidth, maskHeight, maskBuffer, maskBufferLength); if (scale < 256) scaleSpriteCoords(scale, &s_width, &s_height, &x_align, &y_align); @@ -339,7 +339,7 @@ int Sprite::drawOccluded(SURFACE *ds, SPRITELIST *sprite_list, int sprite_num, c spr_dst_rect.left = 0; spr_dst_rect.top = 0; spr_dst_rect.right = ds->clip_rect.right; - spr_dst_rect.bottom = MIN(ds->clip_rect.bottom, (int16)mask_h); + spr_dst_rect.bottom = MIN(ds->clip_rect.bottom, (int16)maskHeight); spr_pt.x = screenCoord.x + x_align; spr_pt.y = screenCoord.y + y_align; @@ -363,7 +363,7 @@ int Sprite::drawOccluded(SURFACE *ds, SPRITELIST *sprite_list, int sprite_num, c src_row_p = _decodeBuf + ci.src_draw_x + (ci.src_draw_y * s_width); dst_row_p = (byte *)ds->pixels + ci.dst_draw_x + (ci.dst_draw_y * ds->pitch); - mask_row_p = mask_buf + ci.dst_draw_x + (ci.dst_draw_y * mask_w); + mask_row_p = maskBuffer + ci.dst_draw_x + (ci.dst_draw_y * maskWidth); for (y = 0; y < ci.draw_h; y++) { src_p = src_row_p; @@ -381,7 +381,7 @@ int Sprite::drawOccluded(SURFACE *ds, SPRITELIST *sprite_list, int sprite_num, c mask_p++; } dst_row_p += ds->pitch; - mask_row_p += mask_w; + mask_row_p += maskWidth; src_row_p += s_width; } /* -- cgit v1.2.3