/* 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 3 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, see . * */ #include "chewy/movclass.h" #include "chewy/defines.h" #include "chewy/globals.h" namespace Chewy { #define LEFT_VECT -1 #define RIGHT_VECT 1 #define MOV_START 0 #define MOV_FOUND 1 #define MOV_UNPASSABLE 2 #define Y_DOWN 1 #define Y_UP 2 #define SOURCE_COL 14 #define DEST_COL 19 #define CHECK_COL 255 #define MIN_COL 8 #define UNBEGEHBAR_COL 3 #define CONNECT_COL 15 #define CUR_COL 13 #define S_OBEN 0 #define S_RECHTS 1 #define S_UNTEN 2 #define S_LINKS 3 #define MOV_X 1 #define MOV_Y 2 MovClass::MovClass() { _vecTbl[0] = -40; _vecTbl[1] = 1; _vecTbl[2] = 40; _vecTbl[3] = -1; } MovClass::~MovClass() { } void MovClass::goto_xy(GotoPkt *gp) { const int16 width = _G(room)->_barriers->getWidth(); _gpkt = gp; _agv.AutoGo = false; _agv.Continue = false; _vecTbl[0] = -width; _vecTbl[2] = width; _plotDelay = 20; calc_xy(); _plotDelay = 0; int16 start_feld = get_feld_nr(_gpkt->Sx, _gpkt->Sy); int16 ziel_feld = get_feld_nr(_gpkt->Dx, _gpkt->Dy); int16 tmp = ziel_feld; calc_go(start_feld, &ziel_feld); if (tmp != ziel_feld) { get_mov_line(); if (_gml.MLineFeld != -1) { calc_mov_line_xit(_gml.MLineFeld); if (_mle.FNr != -1) { _agv.AutoGo = true; _agv.Start = 0; _agv.LastFeld = -1; _agv.AkKnoten = 0; start_feld = get_feld_nr(_gpkt->Sx, _gpkt->Sy); ziel_feld = _gml.MLineFeld; calc_go(start_feld, &ziel_feld); if (_gml.MLineFeld != ziel_feld) { _agv.PktAnz = 2; } else { _agv.PktAnz = 1; } } } } } int16 MovClass::calc_auto_go(int16 x, int16 y, int16 *auto_x, int16 *auto_y) { int16 ret = -1; const int16 width = _G(room)->_barriers->getWidth(); const int16 height = _G(room)->_barriers->getHeight(); if (_agv.AutoGo == true) { const byte *data = _G(room)->_barriers->getData(); const byte *buffer = data + 6; buffer += (width * height) * _gpkt->AkMovEbene; if (!_agv.Start) { _agv.Start = _agv.PktAnz; if (_agv.PktAnz == 1) { _agv.Start = 3; get_feld_xy(_gml.MLineFeld, &_agv.X_End, &_agv.Y_End); _agv.AkFeld = _gml.MLineFeld; } else { get_feld_xy(_gml.GotoFeld, &_agv.X_End, &_agv.Y_End); _agv.AkFeld = _gml.GotoFeld; } *auto_x = _agv.X_End + 4; *auto_y = _agv.Y_End + 4; ret = true; } else { if (get_feld_nr(x, y) == _agv.AkFeld) { if (_agv.Start == 2) { _agv.Start = 3; get_feld_xy(_gml.GotoFeld, &_agv.X_End, &_agv.Y_End); *auto_x = _agv.X_End + 4; *auto_y = _agv.Y_End + 4; ret = true; } else if (_agv.Start == 3) { if (_agv.LastFeld == -1) { _agv.LastFeld = _gml.MLineFeld; _agv.AkFeld = _gml.MLineFeld + _mle.Direction; if (buffer[_agv.AkFeld] == MOV_LINE_KNOTEN) { _feld1knoten = true; } else _feld1knoten = false; get_feld_xy(_agv.AkFeld, &_agv.X_End, &_agv.Y_End); *auto_x = _agv.X_End + 4; *auto_y = _agv.Y_End + 4; ret = true; } else if (get_feld_nr(x, y) == _agv.AkFeld) { if (_agv.AkFeld == _mle.FNr) { _agv.AutoGo = false; *auto_x = _gpkt->Dx; *auto_y = _gpkt->Dy; ret = true; } else { int16 ok = 0; if (_feld1knoten) { _feld1knoten = false; _agv.LastFeld = _agv.AkFeld; _agv.AkFeld = _mle.KnPkt[_agv.AkKnoten]; ++_agv.AkKnoten; if (_agv.AkFeld != -1) ok = 1; } else { for (int16 i = 0; i < 4 && !ok; i++) { if (buffer[_agv.AkFeld + _vecTbl[i]] == MOV_LINE_IDX) { if (_agv.AkFeld + _vecTbl[i] != _agv.LastFeld) { _agv.LastFeld = _agv.AkFeld; _agv.AkFeld += _vecTbl[i]; ok = 1; } } else if (buffer[_agv.AkFeld + _vecTbl[i]] == MOV_LINE_KNOTEN) { if (_agv.AkFeld + _vecTbl[i] != _agv.LastFeld) { _agv.LastFeld = _agv.AkFeld + _vecTbl[i]; _agv.AkFeld = _mle.KnPkt[_agv.AkKnoten]; ++_agv.AkKnoten; if (_agv.AkFeld == -1) ok = 0; else ok = 1; } } } } if (ok) { get_feld_xy(_agv.AkFeld, &_agv.X_End, &_agv.Y_End); *auto_x = _agv.X_End + 4; *auto_y = _agv.Y_End + 4; } else { _agv.AutoGo = false; *auto_x = _gpkt->Dx; *auto_y = _gpkt->Dy; } ret = true; } } } } } delete[] data; } return ret; } void MovClass::stop_auto_go() { _agv.AutoGo = false; _agv.Continue = true; } void MovClass::continue_auto_go() { _agv.AutoGo = _agv.Continue; } bool MovClass::auto_go_status() { return _agv.AutoGo; } void MovClass::calc_xy() { int16 xvector = 0; int16 yvector = 0; if (!_G(barriers)->getBarrierId(_gpkt->Dx, _gpkt->Dy)) { int16 ende = 0; int16 count = 0; int16 min_x = -1; int16 min_y = -1; const int16 width = _G(room)->_barriers->getWidth(); const int16 height = _G(room)->_barriers->getHeight(); while (!ende) { int16 min_steps = 30000; int16 x = _gpkt->Dx - (count + 1) * 8; int16 y = _gpkt->Dy - (count + 1) * 8; for (int16 seite = 0; seite < 4; seite++) { int16 anz = 2 + (count * 2); switch (seite) { case S_OBEN: xvector = 8; yvector = 0; break; case S_RECHTS: xvector = 0; yvector = 8; break; case S_UNTEN: xvector = -8; yvector = 0; break; case S_LINKS: xvector = 0; yvector = -8; break; default: break; } for (int16 i = 0; i < anz; i++) { if (x >= 0 && x < width * 8 && y >= 0 && y < height * 8) { if (_G(barriers)->getBarrierId(x, y)) { int16 aksteps = abs(_gpkt->Dx - x); aksteps += abs(_gpkt->Dy - y); if (aksteps < min_steps) { min_steps = aksteps; min_x = x; min_y = y; } } } x += xvector; y += yvector; } } ++count; if (min_steps != 30000) { ende = 1; int16 tmp_feld = get_feld_nr(min_x, min_y); get_feld_xy(tmp_feld, &_gpkt->Dx, &_gpkt->Dy); _gpkt->Dx += 4; _gpkt->Dy += 4; } } } } short MovClass::calc_go(int16 src_feld, int16 *dst_feld) { int16 new_vector = true; ObjMov om; MovInfo mi; mi.Vorschub = _gpkt->Vorschub; get_feld_xy(src_feld, &om.Xypos[0], &om.Xypos[1]); get_feld_xy(*dst_feld, &mi.XyzEnd[0], &mi.XyzEnd[1]); mi.XyzStart[0] = om.Xypos[0]; mi.XyzStart[1] = om.Xypos[1]; get_mov_vector(mi.XyzStart, mi.XyzEnd, mi.Vorschub, &om); bool endFl = false; int16 mov_ok = 0; int16 steps = 0; int16 abbruch = MOV_START; while (!endFl && !abbruch) { if (om.Count > 0) { --om.Count; int16 tmpx = om.Xyvo[0]; int16 tmpy = om.Xyvo[1]; int16 tmpz = om.Xyvo[2]; om.Xyna[0][0] += om.Xyna[0][1]; if (om.Xyna[0][1] < 0) { if (om.Xyna[0][0] < -1000) { om.Xyna[0][0] += 1000; --tmpx; } } else if (om.Xyna[0][0] > 1000) { om.Xyna[0][0] -= 1000; ++tmpx; } om.Xyna[1][0] += om.Xyna[1][1]; if (om.Xyna[1][1] < 0) { if (om.Xyna[1][0] < -1000) { om.Xyna[1][0] += 1000; --tmpy; } } else if (om.Xyna[1][0] > 1000) { om.Xyna[1][0] -= 1000; ++tmpy; } om.Xyna[2][0] += om.Xyna[2][1]; if (om.Xyna[2][1] < 0) { if (om.Xyna[2][0] < -1000) { om.Xyna[2][0] += 1000; } } else if (om.Xyna[2][0] > 1000) { om.Xyna[2][0] -= 1000; } tmpz = 0; if (!_G(barriers)->getBarrierId(om.Xypos[0] + tmpx + tmpz, om.Xypos[1] + tmpy + tmpz)) { if (!_G(barriers)->getBarrierId(om.Xypos[0] + tmpx + tmpz, om.Xypos[1] + tmpz)) { if (!_G(barriers)->getBarrierId(om.Xypos[0] + tmpz, om.Xypos[1] + tmpy + tmpz)) { abbruch = MOV_UNPASSABLE; } else { if (!tmpy) { abbruch = MOV_UNPASSABLE; } else if (abs(om.Xypos[1] - mi.XyzEnd[1]) <= abs(tmpy)) { abbruch = MOV_FOUND; } else { mov_ok = MOV_Y; } } } else { if (!tmpx) { abbruch = MOV_UNPASSABLE; } else if (abs(om.Xypos[0] - mi.XyzEnd[0]) <= abs(tmpx)) { abbruch = MOV_FOUND; } else { mov_ok = MOV_X; } } } else { ++steps; om.Xypos[0] += tmpx; om.Xypos[1] += tmpy; om.Xypos[2] += tmpz; if (new_vector) { new_vector = false; mi.XyzStart[0] = om.Xypos[0]; mi.XyzStart[1] = om.Xypos[1]; get_mov_vector(mi.XyzStart, mi.XyzEnd, mi.Vorschub, &om); } if (om.Count == 0) { abbruch = MOV_FOUND; } } if (mov_ok) { ++steps; if (mov_ok == MOV_X) om.Xypos[0] += tmpx; else om.Xypos[1] += tmpy; om.Xypos[2] += tmpz; mi.XyzStart[0] = om.Xypos[0]; mi.XyzStart[1] = om.Xypos[1]; get_mov_vector(mi.XyzStart, mi.XyzEnd, mi.Vorschub, &om); if (mov_ok == MOV_X) { if (om.Xyvo[0] != 0) new_vector = true; } else { if (om.Xyvo[1] != 0) new_vector = true; } if (om.Count == 0) { abbruch = MOV_FOUND; } mov_ok = false; } } else endFl = true; } if (abbruch == MOV_UNPASSABLE) *dst_feld = get_feld_nr(om.Xypos[0], om.Xypos[1]); return steps; } void MovClass::get_mov_line() { const int16 width = _G(room)->_barriers->getWidth(); const int16 height = _G(room)->_barriers->getHeight(); const int16 level = _G(room)->_barriers->getLevel(); const byte *data = _G(room)->_barriers->getData(); const byte *buffer = data + 6; int16 ak_steps; _gml.GotoFeld = -1; _gml.MLineFeld = -1; if (level > 1) { buffer += (width * height) * _gpkt->AkMovEbene; int16 start_feld = get_feld_nr(_gpkt->Sx, _gpkt->Sy); int16 ende = 0; int16 count = 0; int16 min_steps = 30000; int16 count_vect = width; int16 y_richtung = Y_DOWN; while (!ende) { int16 tmp_feld = start_feld + count * count_vect; if (_G(barriers)->getBarrierId(tmp_feld, data + 6)) { int16 abbruch = 0; int16 vector = 0; while ((tmp_feld % width) < (width - 1) && !abbruch) { if (_G(barriers)->getBarrierId(tmp_feld + vector, data + 6)) { tmp_feld += vector; if (buffer[tmp_feld] == MOV_LINE_IDX) { ak_steps = abs((tmp_feld % width) - (start_feld % width)); ak_steps += abs((tmp_feld / width) - (start_feld / width)); if (ak_steps < min_steps) { min_steps = ak_steps; _gml.GotoFeld = start_feld + count * count_vect; _gml.MLineFeld = tmp_feld; } } vector = RIGHT_VECT; } else { abbruch = MOV_UNPASSABLE; } } tmp_feld = start_feld + count * count_vect; vector = LEFT_VECT; abbruch = 0; while ((tmp_feld % width) > 0 && !abbruch) { if (_G(barriers)->getBarrierId(tmp_feld + vector, data + 6)) { tmp_feld += vector; if (buffer[tmp_feld] == MOV_LINE_IDX) { ak_steps = abs((tmp_feld % width) - (start_feld % width)); ak_steps += abs((tmp_feld / width) - (start_feld / width)); if (ak_steps < min_steps) { min_steps = ak_steps; _gml.GotoFeld = start_feld + count * count_vect; _gml.MLineFeld = tmp_feld; } } } else { abbruch = MOV_UNPASSABLE; } } ++count; } else { if (y_richtung == Y_DOWN) { y_richtung = Y_UP; count = 1; count_vect = -_G(room)->_barriers->getWidth(); } else ende = 1; } } } delete[] data; } void MovClass::calc_mov_line_xit(int16 start_feld) { const int16 width = _G(room)->_barriers->getWidth(); const int16 height = _G(room)->_barriers->getHeight(); const int16 level = _G(room)->_barriers->getLevel(); const byte *data = _G(room)->_barriers->getData(); int16 tmp_feld; int16 i; int16 dest_steps; int16 k; int16 TmpKnPkt[MAX_KNOTEN_PKT]; int16 dir_unerreicht = 0; int16 KnPkt_unrreicht[MAX_KNOTEN_PKT]; int16 feld_unerreicht = 0; int16 knoten_felder[MAX_KNOTEN_PKT][6]; for (i = 0; i < MAX_KNOTEN_PKT; i++) TmpKnPkt[i] = -1; if (level > 1) { memset(knoten_felder, -1, MAX_KNOTEN_PKT * 6); const byte *buffer = data + 6; buffer += (width * height) * _gpkt->AkMovEbene; _mle.Steps = 30000; int16 min_step_unerreicht = 30000; _mle.FNr = -1; int16 ziel_feld = get_feld_nr(_gpkt->Dx, _gpkt->Dy); int16 calc_feld = start_feld; int16 last_feld = start_feld; int16 steps = 0; int16 found = 0; int16 ak_knoten = -1; int16 tbl_dir = 1; int16 tbl_ptr = 0; int16 knoten_flag = 0; //found_min = 0; int16 dir = 30000; while (!found) { tmp_feld = ziel_feld; calc_go(calc_feld, &tmp_feld); if (tmp_feld == ziel_feld) { dest_steps = abs((ziel_feld % width) - (calc_feld % width)); dest_steps += abs((ziel_feld / width) - (calc_feld / width)); if (dest_steps < _mle.Steps) { _mle.Steps = dest_steps; _mle.FNr = calc_feld; if (dir == 30000) _mle.Direction = 0; else _mle.Direction = dir; for (i = 0; i < MAX_KNOTEN_PKT; i++) _mle.KnPkt[i] = TmpKnPkt[i]; } } else { dest_steps = abs((ziel_feld % width) - (tmp_feld % width)); dest_steps += abs((ziel_feld / width) - (tmp_feld / width)); if (dest_steps < min_step_unerreicht) { min_step_unerreicht = dest_steps; feld_unerreicht = tmp_feld; if (dir == 30000) dir_unerreicht = 0; else dir_unerreicht = dir; for (i = 0; i < MAX_KNOTEN_PKT; i++) KnPkt_unrreicht[i] = TmpKnPkt[i]; } } int16 ok = 0; for (i = 0; i < 4 && !ok; i++) { if (buffer[calc_feld + _vecTbl[tbl_ptr + i * tbl_dir]] == MOV_LINE_IDX) { if (calc_feld + _vecTbl[tbl_ptr + i * tbl_dir] != last_feld) { last_feld = calc_feld; calc_feld += _vecTbl[tbl_ptr + i * tbl_dir]; ++steps; ok = 1; if (dir == 30000) { dir = _vecTbl[tbl_ptr + i * tbl_dir]; } if (knoten_flag) { TmpKnPkt[ak_knoten] = calc_feld; knoten_felder[ak_knoten][tbl_ptr + i * tbl_dir] = calc_feld; knoten_flag = false; } } } else if (buffer[calc_feld + _vecTbl[tbl_ptr + i * tbl_dir]] == MOV_LINE_KNOTEN) { if (calc_feld + _vecTbl[tbl_ptr + i * tbl_dir] != last_feld) { if (dir == 30000) { dir = _vecTbl[tbl_ptr + i * tbl_dir]; } ++ak_knoten; for (k = 0; k < 6; k++) knoten_felder[ak_knoten][k] = -1; switch (tbl_ptr + i * tbl_dir) { case MLE_UP: k = MLE_DOWN; break; case MLE_RIGHT: k = MLE_LEFT; break; case MLE_LEFT: k = MLE_RIGHT; break; case MLE_DOWN: k = MLE_UP; break; } knoten_felder[ak_knoten][k] = calc_feld; last_feld = calc_feld; calc_feld += _vecTbl[tbl_ptr + i * tbl_dir]; knoten_felder[ak_knoten][4] = calc_feld; ++steps; knoten_felder[ak_knoten][5] = steps; knoten_flag = true; ok = 1; } } } if (!ok) { if (ak_knoten != -1) { ok = 0; for (i = 0; i < 4 && !ok; i++) { if (knoten_felder[ak_knoten][tbl_ptr + i * tbl_dir] == -1) { tmp_feld = knoten_felder[ak_knoten][4]; if (buffer[tmp_feld + _vecTbl[tbl_ptr + i * tbl_dir]] == MOV_LINE_IDX) { last_feld = knoten_felder[ak_knoten][4]; calc_feld = tmp_feld + _vecTbl[tbl_ptr + i * tbl_dir]; TmpKnPkt[ak_knoten] = calc_feld; knoten_felder[ak_knoten][tbl_ptr + i * tbl_dir] = calc_feld; steps = knoten_felder[ak_knoten][5]; ok = 1; } } } if (!ok) { --ak_knoten; } } else if (tbl_dir == 1) { tbl_dir = -1; tbl_ptr = 3; dir = 30000; //found_min = 0; calc_feld = start_feld; last_feld = start_feld; } else { found = 1; if (_mle.FNr == -1) { _mle.Steps = min_step_unerreicht; _mle.FNr = feld_unerreicht; _mle.Direction = dir_unerreicht; for (i = 0; i < MAX_KNOTEN_PKT; i++) _mle.KnPkt[i] = KnPkt_unrreicht[i]; get_feld_xy(feld_unerreicht, &_gpkt->Dx, &_gpkt->Dy); _gpkt->Dx += 4; _gpkt->Dy += 4; } } } } } delete[] data; } int16 MovClass::get_feld_nr(int16 x, int16 y) { const int16 width = _G(room)->_barriers->getWidth(); x >>= 3; y >>= 3; return x + (y * width); } void MovClass::get_feld_xy(int16 fnr, int16 *x, int16 *y) { const int16 width = _G(room)->_barriers->getWidth(); *x = (fnr % width); *y = (fnr / width); *x <<= 3; *y <<= 3; } void MovClass::get_mov_vector(int16 *startXyz, int16 *endXyz, int16 vorschub, ObjMov *om) { if (vorschub > 0) { int16 x = startXyz[0]; int16 y = startXyz[1]; int16 z = startXyz[2]; int16 x1 = endXyz[0]; int16 y1 = endXyz[1]; int16 z1 = endXyz[2]; om->Xypos[0] = x; om->Xypos[1] = y; om->Xypos[2] = z; x = x - x1; y = y - y1; z = z - z1; uint8 xvector; if (x < 0) { xvector = 1; --x; } else { xvector = 0; ++x; } uint8 yvector; if (y < 0) { yvector = 1; --y; } else { yvector = 0; ++y; } uint8 zvector; if (z < 0) { zvector = 1; --z; } else { zvector = 0; ++z; } int16 absx = abs(x); int16 absy = abs(y); int16 absz = abs(z); float xstep; float ystep; if (absx < absy) { xstep = (float)absx / ((float)absy / (float)vorschub); ystep = vorschub; om->Count = (int16)((float) y / (float)ystep); } else { xstep = vorschub; ystep = (float)absy / ((float)absx / (float)vorschub); om->Count = (int16)((float) x / (float)xstep); } float zstep; om->Count = abs(om->Count); if (!om->Count) { zstep = (float)absz / 1.0; } else zstep = (float)absz / ((float)((abs)(om->Count))); if (!xvector) xstep = -xstep; if (!yvector) ystep = -ystep; if (!zvector) zstep = -zstep; om->Xyvo[0] = (int16)xstep; om->Xyvo[1] = (int16)ystep; om->Xyvo[2] = (int16)zstep; int16 tmp = (int16)((float)xstep * 1000.0); om->Xyna[0][1] = tmp - (om->Xyvo[0] * 1000); om->Xyna[0][0] = 0; tmp = (int16)((float)ystep * 1000.0); om->Xyna[1][1] = tmp - (om->Xyvo[1] * 1000); om->Xyna[1][0] = 0; tmp = (int16)((float)zstep * 1000.0); om->Xyna[2][1] = tmp - (om->Xyvo[2] * 1000); om->Xyna[2][0] = 0; if (!om->Count) { startXyz[0] = endXyz[0]; startXyz[1] = endXyz[1]; om->Xypos[0] = endXyz[0]; om->Xypos[1] = endXyz[1]; } } else { om->Count = 0; om->Xypos[0] = startXyz[0]; om->Xypos[1] = startXyz[1]; om->Xypos[2] = startXyz[2]; om->Xyvo[0] = 0; om->Xyvo[1] = 0; om->Xyvo[2] = 0; om->Xyna[0][1] = 0; om->Xyna[0][0] = 0; om->Xyna[1][1] = 0; om->Xyna[1][0] = 0; om->Xyna[2][1] = 0; om->Xyna[2][0] = 0; } } } // namespace Chewy