/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/textconsole.h" #include "cruise/cruise.h" #include "cruise/staticres.h" namespace Cruise { enum AnimPathIds { ANIM_WAIT = -1, ANIM_FINISH = -2, ANIM_STATIC = -3 }; bool isAnimFinished(int overlayIdx, int idx, actorStruct *pStartEntry, int objType) { actorStruct *pCurrentEntry = pStartEntry->next; while (pCurrentEntry) { if ((pCurrentEntry->overlayNumber == overlayIdx || overlayIdx == -1) && (pCurrentEntry->idx == idx || idx == -1) && (pCurrentEntry->type == objType || objType == -1)) { if (pCurrentEntry->pathId != ANIM_FINISH) { return false; } } pCurrentEntry = pCurrentEntry->next; } return 1; } actorStruct *findActor(actorStruct *pStartEntry, int overlayIdx, int objIdx, int type) { actorStruct *pCurrentEntry = pStartEntry->next; while (pCurrentEntry) { if ((pCurrentEntry->overlayNumber == overlayIdx || overlayIdx == -1) && (pCurrentEntry->idx == objIdx || objIdx == -1) && (pCurrentEntry->type == type || type == -1)) { return pCurrentEntry; } pCurrentEntry = pCurrentEntry->next; } return NULL; } int nclick_noeud; int flag_aff_chemin; void getPixel(int x, int y) { for (uint i = 0; i < _vm->_polyStructs->size(); ++i) { CtStruct &ct = (*_vm->_polyStructs)[i]; numPoly = ct.num; if (walkboxState[numPoly] == 0 && ct.bounds.contains(x, y)) { // click was in given box int u = y - ct.bounds.top; CtEntry &cte = ct.slices[u]; if ((x >= cte.minX && x <= cte.maxX)) { flag_obstacle = walkboxColor[numPoly]; return; } } } flag_obstacle = 0; } int x_mouse; int y_mouse; int point_select; int table_ptselect[2][2]; int X; int Y; int modelVar9; int modelVar10; void polydroite(int x1, int y1, int x2, int y2) { int dx; int dy; int mD0; int mD1; int mA0; int mA1; int bp; int cx; int si; int ax; int bx; modelVar9 = x1; modelVar10 = y1; dx = x2 - x1; dy = y2 - y1; mD0 = mD1 = 1; if (dx < 0) { dx = -dx; mD0 = -1; } if (dy < 0) { dy = -dy; mD1 = -1; } if (dx < dy) { mA0 = 0; bp = dx; cx = dy; mA1 = mD1; } else { mA1 = 0; bp = dy; cx = dx; mA0 = mD0; } bp = bp * 2; dx = bp - cx; si = dx - cx; ax = modelVar9; bx = modelVar10; getPixel(modelVar9, modelVar10); X = modelVar9; Y = modelVar10; if ((flag_obstacle == 0) || (cx == 0)) { flag_obstacle = 1; return; } while (--cx >= 0) { if (dx > 0) { ax += mD0; bx += mD1; dx += si; } else { ax += mA0; bx += mA1; dx += bp; } getPixel(ax, bx); X = ax; Y = bx; if (flag_obstacle == 0) { flag_obstacle = 1; return; } } flag_obstacle = 0; } void poly2(int x1, int y1, int x2, int y2) { int dx; int dy; int mD0; int mD1; int mA0; int mA1; int bp; int cx; int si; int ax; int bx; modelVar9 = x1; modelVar10 = y1; dx = x2 - x1; dy = y2 - y1; mD0 = mD1 = 1; if (dx < 0) { dx = -dx; mD0 = -1; } if (dy < 0) { dy = -dy; mD1 = -1; } if (dx < dy) { mA0 = 0; bp = dx; cx = dy; mA1 = mD1; } else { mA1 = 0; bp = dy; cx = dx; mA0 = mD0; } bp = bp * 2; dx = bp - cx; si = dx - cx; ax = modelVar9; bx = modelVar10; getPixel(modelVar9, modelVar10); X = modelVar9; Y = modelVar10; if ((flag_obstacle != 0) || (cx == 0)) { flag_obstacle = 1; return; } while (--cx >= 0) { if (dx > 0) { ax += mD0; bx += mD1; dx += si; } else { ax += mA0; bx += mA1; dx += bp; } getPixel(ax, bx); X = ax; Y = bx; if (flag_obstacle != 0) { flag_obstacle = 1; return; } } flag_obstacle = 0; } int point_proche(int16 table[][2]) { int d1 = 1000; _vm->_polyStructs = &_vm->_polyStructNorm; if (nclick_noeud == 1) { int x = x_mouse; int y = y_mouse; int x1 = table_ptselect[0][0]; int y1 = table_ptselect[0][1]; _vm->_polyStructs = &_vm->_polyStructExp; getPixel(x, y); if (!flag_obstacle) { _vm->_polyStructs = &_vm->_polyStructNorm; getPixel(x, y); if (flag_obstacle) { polydroite(x1, y1, x, y); } _vm->_polyStructs = &_vm->_polyStructExp; } if (!flag_obstacle) { /* dans flag_obstacle --> couleur du point */ x1 = table_ptselect[0][0]; y1 = table_ptselect[0][1]; poly2(x, y, x1, y1); x_mouse = X; y_mouse = Y; } } _vm->_polyStructs = &_vm->_polyStructNorm; int p = -1; for (int i = 0; i < ctp_routeCoordCount; i++) { int x = table[i][0]; int y = table[i][1]; int pointDistance = computeDistance(x_mouse, y_mouse, x, y); if (pointDistance < d1) { polydroite(x_mouse, y_mouse, x, y); if (!flag_obstacle && ctp_routes[i][0] > 0) { d1 = pointDistance; p = i; } } } return (p); } #define NBNOEUD 20 int16 select_noeud[3]; int8 solution[20 + 1]; int prem; int prem2; int dist_chemin; int idsol; int solmax; int8 fl[NBNOEUD + 1]; int8 sol[NBNOEUD + 1]; int8 Fsol[NBNOEUD + 1]; int D; void explore(int depart, int arrivee) { int id1 = depart; fl[id1]++; sol[idsol++] = (char)id1; if (idsol > solmax) { fl[id1] = -1; idsol--; return; } int i; while ((i = fl[id1]) < 20) { int id2 = ctp_routes[id1][i + 1]; if (id2 == arrivee) { if (idsol < solmax) { sol[idsol] = (char)arrivee; D = 0; for (i = 0; i < idsol; i++) { D = D + distanceTable[(int)sol[i]][(int)sol[i + 1]]; Fsol[i] = sol[i]; } prem2 = 0; if (!prem) { dist_chemin = D; prem = 1; for (i = 0; i <= idsol; i++) { solution[i] = sol[i]; } solution[i++] = -1; solution[i] = -1; } else if (D < dist_chemin) { dist_chemin = D; for (i = 0; i <= idsol; i++) { solution[i] = sol[i]; } solution[i++] = -1; solution[i] = -1; } } fl[id1] = -1; idsol--; return; } else if ((id2 != -1) && ((int)fl[id2] == -1)) explore(id2, arrivee); else if (id2 == -1) { fl[id1] = -1; idsol--; return; } fl[id1]++; } fl[id1] = -1; idsol--; } void chemin0(int depart, int arrivee) { prem = 0; prem2 = 0; dist_chemin = 0; idsol = 0; solmax = 999; for (int i = 0; i < 20 + 1; i++) fl[i] = -1; X = 0; Y = 30; explore(depart, arrivee); } void valide_noeud(int16 table[], int16 p, int *nclick, int16 solution0[20 + 3][2]) { table[*nclick] = p; table[(*nclick) + 1] = -1; table_ptselect[*nclick][0] = x_mouse; table_ptselect[*nclick][1] = y_mouse; (*nclick)++; _vm->_polyStructs = &_vm->_polyStructNorm; if (*nclick == 2) { // second point int x1 = table_ptselect[0][0]; int y1 = table_ptselect[0][1]; int x2 = table_ptselect[1][0]; int y2 = table_ptselect[1][1]; if ((x1 == x2) && (y1 == y2)) { return; } flag_aff_chemin = 1; _vm->_polyStructs = &_vm->_polyStructExp; // can we go there directly ? polydroite(x1, y1, x2, y2); if (!flag_obstacle) { solution0[0][0] = x1; solution0[0][1] = y1; _vm->_polyStructs = &_vm->_polyStructExp; poly2(x2, y2, ctp_routeCoords[select_noeud[1]][0], ctp_routeCoords[select_noeud[1]][1]); solution0[1][0] = table_ptselect[1][0] = X; solution0[1][1] = table_ptselect[1][1] = Y; solution0[2][0] = -1; if ((x1 == X) && (y1 == Y)) { flag_aff_chemin = 0; return; } } else { // no, we take the fastest way solution[0] = -1; if (ctp_routes[select_noeud[0]][0] > 0) chemin0(table[0], table[1]); if (solution[0] == -1) { x1 = table_ptselect[0][0]; y1 = table_ptselect[0][1]; polydroite(x1, y1, x_mouse, y_mouse); solution0[0][0] = x1; solution0[0][1] = y1; solution0[1][0] = X; solution0[1][1] = Y; solution0[2][0] = -1; if ((x1 == X) && (y1 == Y)) { flag_aff_chemin = 0; return; } } else { solution0[0][0] = x1; solution0[0][1] = y1; int i = 0; while (solution[i] != -1) { int p1 = solution[i]; solution0[i + 1][0] = ctp_routeCoords[p1][0]; solution0[++i][1] = ctp_routeCoords[p1][1]; } _vm->_polyStructs = &_vm->_polyStructExp; poly2(x2, y2, ctp_routeCoords[select_noeud[1]][0], ctp_routeCoords[select_noeud[1]][1]); solution0[i + 1][0] = table_ptselect[1][0] = X; solution0[i + 1][1] = table_ptselect[1][1] = Y; solution0[i + 2][0] = -1; if ((x1 == X) && (y1 == Y)) { flag_aff_chemin = 0; return; } /****** Trim down any un-necessary walk points ******/ i++; int d = 0; int a = i; flag_obstacle = 1; while (d != a) { x1 = solution0[d][0]; y1 = solution0[d][1]; while (flag_obstacle && i != d) { x2 = solution0[i][0]; y2 = solution0[i][1]; _vm->_polyStructs = &_vm->_polyStructExp; polydroite(x1, y1, x2, y2); i--; } flag_obstacle = 1; if (d != i) { i++; for (int b = d + 1; b < i; b++) { solution0[b][0] = -2; } } else i++; d = i; i = a; } flag_obstacle = 0; } } } } /** * Computes a path for an actor to walk between a given source and destination position */ int16 computePathfinding(MovementEntry &moveInfo, int16 x, int16 y, int16 destX, int16 destY, int16 stepX, int16 stepY, int16 oldPathId) { persoStruct *perso; int num; if (!_vm->_polyStruct) { moveInfo.x = -1; moveInfo.y = -1; return -1; } if (oldPathId >= 0) { if (persoTable[oldPathId]) { freePerso(oldPathId); } } if (!flagCt) { int16 *ptr; int i = 0; for (; i < NUM_PERSONS; i++) { // 10 = num perso if (!persoTable[i]) { break; } } if (i == NUM_PERSONS) { moveInfo.x = -1; moveInfo.y = -1; return -1; } perso = persoTable[i] = (persoStruct *) MemAlloc(sizeof(persoStruct)); ptr = perso->solution[0]; perso->inc_jo1 = stepX; perso->inc_jo2 = stepY; *(ptr++) = x; *(ptr++) = y; *(ptr++) = moveInfo.x = destX; *(ptr++) = moveInfo.y = destY; *(ptr++) = -1; moveInfo.poly = numPoly; perso->inc_droite = 0; perso->inc_chemin = 0; return i; } nclick_noeud = 0; _vm->_polyStructs = &_vm->_polyStructNorm; flag_aff_chemin = 0; if (x == destX && y == destY) { moveInfo.x = -1; moveInfo.y = -1; return (-1); } /******* cherche le premier noeud ******/ getPixel(x, y); moveInfo.poly = numPoly; x_mouse = x; y_mouse = y; if (!flag_obstacle || (point_select = point_proche(ctp_routeCoords)) == -1) { moveInfo.x = -1; moveInfo.y = -1; return (-1); } valide_noeud(select_noeud, point_select, &nclick_noeud, NULL); flag_aff_chemin = 0; /******* cherche le deuxieme noeud ******/ num = 0; while (num < NUM_PERSONS && persoTable[num] != NULL) num++; if (num == NUM_PERSONS) { moveInfo.x = -1; moveInfo.y = -1; return (-1); } perso = persoTable[num] = (persoStruct *) MemAlloc(sizeof(persoStruct)); perso->inc_jo1 = stepX; perso->inc_jo2 = stepY; x_mouse = destX; y_mouse = destY; if ((point_select = point_proche(ctp_routeCoords)) != -1) valide_noeud(select_noeud, point_select, &nclick_noeud, perso->solution); if ((!flag_aff_chemin) || ((table_ptselect[0][0] == table_ptselect[1][0]) && (table_ptselect[0][1] == table_ptselect[1][1]))) { moveInfo.x = -1; moveInfo.y = -1; freePerso(num); return (-1); } moveInfo.x = table_ptselect[1][0]; moveInfo.y = table_ptselect[1][1]; moveInfo.poly = numPoly; perso->inc_chemin = 0; perso->inc_droite = 0; return (num); } void set_anim(int ovl, int obj, int start, int x, int y, int mat, int state) { int newf, zoom; newf = ABS(mat) - 1; zoom = computeZoom(y); if (mat < 0) zoom = -zoom; setObjectPosition(ovl, obj, 0, x); setObjectPosition(ovl, obj, 1, y); setObjectPosition(ovl, obj, 2, y); setObjectPosition(ovl, obj, 4, zoom); setObjectPosition(ovl, obj, 3, newf + start); setObjectPosition(ovl, obj, 5, state); } /** * Handles the processing of any active actors to allow for handling movement */ void processAnimation() { objectParamsQuery params; MovementEntry moveInfo; actorStruct *currentActor = actorHead.next; actorStruct *nextActor; while (currentActor) { nextActor = currentActor->next; if (!currentActor->freeze && ((currentActor->type == ATP_MOUSE) || (currentActor->type == 1))) { getMultipleObjectParam(currentActor->overlayNumber, currentActor->idx, ¶ms); if (((animationStart && !currentActor->flag) || (!animationStart && currentActor->x_dest != -1 && currentActor->y_dest != -1)) && (currentActor->type == ATP_MOUSE)) { // mouse animation if (!animationStart) { aniX = currentActor->x_dest; aniY = currentActor->y_dest; currentActor->x_dest = -1; currentActor->y_dest = -1; currentActor->flag = 1; } currentActor->pathId = computePathfinding(moveInfo, params.X, params.Y, aniX, aniY, currentActor->stepX, currentActor->stepY, currentActor->pathId); if (currentActor->pathId == ANIM_WAIT) { if ((currentActor->endDirection != -1) && (currentActor->endDirection != currentActor->startDirection)) { currentActor->phase = ANIM_PHASE_STATIC_END; currentActor->nextDirection = currentActor->endDirection; currentActor->endDirection = -1; currentActor->counter = 0; } else { currentActor->pathId = ANIM_FINISH; currentActor->flag = 0; currentActor->endDirection = -1; currentActor->phase = ANIM_PHASE_WAIT; } } else { currentActor->phase = ANIM_PHASE_STATIC; currentActor->counter = -1; } } else if ((currentActor->type == 1) && (currentActor->x_dest != -1) && (currentActor->y_dest != -1)) { // track animation currentActor->pathId = computePathfinding(moveInfo, params.X, params.Y, currentActor->x_dest, currentActor->y_dest, currentActor->stepX, currentActor->stepY, currentActor->pathId); currentActor->x_dest = -1; currentActor->y_dest = -1; if (currentActor->pathId == ANIM_WAIT) { if ((currentActor->endDirection != -1) && (currentActor->endDirection != currentActor->startDirection)) { currentActor->phase = ANIM_PHASE_STATIC_END; currentActor->nextDirection = currentActor->endDirection; currentActor->endDirection = -1; currentActor->counter = 0; } else { currentActor->pathId = -2; currentActor->flag = 0; currentActor->endDirection = -1; currentActor->phase = ANIM_PHASE_WAIT; } } else { currentActor->phase = ANIM_PHASE_STATIC; currentActor->counter = -1; } } animationStart = false; if ((currentActor->pathId >= 0) || (currentActor->phase == ANIM_PHASE_STATIC_END)) { // Main switch statement for handling various phases of movement // IMPORTANT: This switch relies on falling through cases in certain circumstances // , so 'break' statements should *not* be used at the end of case areas switch (currentActor->phase) { case ANIM_PHASE_STATIC_END: case ANIM_PHASE_STATIC: { // In-place (on the spot) animationos if ((currentActor->counter == -1) && (currentActor->phase == ANIM_PHASE_STATIC)) { affiche_chemin(currentActor->pathId, moveInfo); if (moveInfo.x == -1) { currentActor->pathId = ANIM_FINISH; currentActor->flag = 0; currentActor->endDirection = -1; currentActor->phase = ANIM_PHASE_WAIT; break; } currentActor->x = moveInfo.x; currentActor->y = moveInfo.y; currentActor->nextDirection = moveInfo.direction; currentActor->poly = moveInfo.poly; currentActor->counter = 0; if (currentActor->startDirection == currentActor->nextDirection) currentActor->phase = ANIM_PHASE_MOVE; } if ((currentActor->counter >= 0) && ((currentActor->phase == ANIM_PHASE_STATIC_END) || (currentActor->phase == ANIM_PHASE_STATIC))) { int newA; int inc = 1; int t_inc = currentActor->startDirection - 1; if (t_inc < 0) t_inc = 3; if (currentActor->nextDirection == t_inc) inc = -1; if (inc > 0) newA = actor_stat[currentActor->startDirection][currentActor->counter++]; else newA = actor_invstat[currentActor->startDirection][currentActor->counter++]; if (newA == 0) { currentActor->startDirection = currentActor->startDirection + inc; if (currentActor->startDirection > 3) currentActor->startDirection = 0; if (currentActor->startDirection < 0) currentActor-> startDirection = 3; currentActor->counter = 0; if (currentActor->startDirection == currentActor->nextDirection) { if (currentActor->phase == ANIM_PHASE_STATIC) currentActor->phase = ANIM_PHASE_MOVE; else currentActor->phase = ANIM_PHASE_END; } else { newA = actor_stat[currentActor->startDirection][currentActor->counter++]; if (inc == -1) newA = -newA; set_anim(currentActor->overlayNumber, currentActor->idx, currentActor->start, params.X, params.Y, newA, currentActor->poly); break; } } else { set_anim(currentActor->overlayNumber,currentActor->idx, currentActor->start, params.X, params.Y, newA, currentActor->poly); break; } } } // fall through case ANIM_PHASE_MOVE: { // Walk animations if (currentActor->counter >= 1) { affiche_chemin(currentActor->pathId, moveInfo); if (moveInfo.x == -1) { if ((currentActor->endDirection == -1) || (currentActor->endDirection == currentActor->nextDirection)) { currentActor->phase = ANIM_PHASE_END; } else { currentActor->phase = ANIM_PHASE_STATIC_END; currentActor->nextDirection = currentActor->endDirection; } currentActor->counter = 0; break; } else { currentActor->x = moveInfo.x; currentActor->y = moveInfo.y; currentActor->nextDirection = moveInfo.direction; currentActor->poly = moveInfo.poly; } } if (currentActor->phase == ANIM_PHASE_MOVE) { int newA; currentActor->startDirection = currentActor->nextDirection; newA = actor_move[currentActor->startDirection][currentActor->counter++]; if (!newA) { currentActor->counter = 0; newA = actor_move[currentActor->startDirection][currentActor->counter++]; } set_anim(currentActor->overlayNumber, currentActor->idx, currentActor->start, currentActor->x, currentActor->y, newA, currentActor->poly); break; } } // fall through case ANIM_PHASE_END: { // End of walk animation int newA = actor_end[currentActor->startDirection][0]; set_anim(currentActor->overlayNumber, currentActor->idx, currentActor->start, currentActor->x, currentActor->y, newA, currentActor->poly); currentActor->pathId = ANIM_FINISH; currentActor->phase = ANIM_PHASE_WAIT; currentActor->flag = 0; currentActor->endDirection = -1; break; } default: { warning("Unimplemented currentActor->phase=%d in processAnimation()", currentActor->phase); // exit(1); } } } } currentActor = nextActor; } } } // End of namespace Cruise