1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 /*
24 * This code is based on original Soltys source code
25 * Copyright (c) 1994-1995 Janus B. Wisniewski and L.K. Avalon
26 */
27
28 #include "cge/walk.h"
29 #include "cge/cge_main.h"
30
31 namespace CGE {
32
Cluster(CGEEngine * vm,int16 a,int16 b)33 Cluster::Cluster(CGEEngine *vm, int16 a, int16 b) : _vm(vm) {
34 _pt = Common::Point(a, b);
35 }
36
Cluster(CGEEngine * vm)37 Cluster::Cluster(CGEEngine *vm) : _vm(vm) {
38 _pt = Common::Point(-1, -1);
39 }
40
cell()41 uint8 &Cluster::cell() {
42 return _vm->_clusterMap[_pt.y][_pt.x];
43 }
44
isValid() const45 bool Cluster::isValid() const {
46 return (_pt.x >= 0) && (_pt.x < kMapXCnt) && (_pt.y >= 0) && (_pt.y < kMapZCnt);
47 }
48
Walk(CGEEngine * vm,BitmapPtr * shpl)49 Walk::Walk(CGEEngine *vm, BitmapPtr *shpl)
50 : Sprite(vm, shpl), _dir(kDirNone), _tracePtr(-1), _level(0), _target(-1, -1), _findLevel(-1), _here(vm), _vm(vm) {
51 for (int i = 0; i < kMaxFindLevel; i++) {
52 Cluster *tmpClust = new Cluster(_vm);
53 _trace.push_back(tmpClust);
54 }
55 }
56
~Walk()57 Walk::~Walk() {
58 for (uint idx = 0; idx < _trace.size(); ++idx)
59 delete _trace[idx];
60 }
61
tick()62 void Walk::tick() {
63 if (_flags._hide)
64 return;
65
66 _here = _vm->XZ(_x + _w / 2, _y + _h);
67
68 if (_dir != kDirNone) {
69 _vm->_sys->funTouch();
70 for (Sprite *spr = _vm->_vga->_showQ->first(); spr; spr = spr->_next) {
71 if (distance(spr) < 2) {
72 if (!spr->_flags._near) {
73 _vm->feedSnail(spr, kNear);
74 spr->_flags._near = true;
75 }
76 } else {
77 spr->_flags._near = false;
78 }
79 }
80 }
81
82 if (_flags._hold || _tracePtr < 0) {
83 park();
84 } else {
85 if (_here._pt == _trace[_tracePtr]->_pt) {
86 if (--_tracePtr < 0)
87 park();
88 } else {
89 Common::Point tmpPoint = _trace[_tracePtr]->_pt - _here._pt;
90 int16 dx = tmpPoint.x;
91 int16 dz = tmpPoint.y;
92 Dir d = (dx) ? ((dx > 0) ? kDirEast : kDirWest) : ((dz > 0) ? kDirSouth : kDirNorth);
93 turn(d);
94 }
95 }
96
97 step();
98
99 if ((_dir == kDirWest && _x <= 0) ||
100 (_dir == kDirEast && _x + _w >= kScrWidth) ||
101 (_dir == kDirSouth && _y + _w >= kWorldHeight - 2)) {
102 park();
103 } else {
104 // take current Z position
105 _z = _here._pt.y;
106 _vm->_commandHandlerTurbo->addCommand(kCmdZTrim, -1, 0, this); // update Hero's pos in show queue
107 }
108 }
109
distance(Sprite * spr)110 int Walk::distance(Sprite *spr) {
111 int dx = spr->_x - (_x + _w - kWalkSide);
112 if (dx < 0)
113 dx = (_x + kWalkSide) - (spr->_x + spr->_w);
114
115 if (dx < 0)
116 dx = 0;
117
118 dx /= kMapGridX;
119 int dz = spr->_z - _z;
120 if (dz < 0)
121 dz = - dz;
122
123 dx = dx * dx + dz * dz;
124 for (dz = 1; dz * dz < dx; dz++)
125 ;
126
127 return dz - 1;
128 }
129 /**
130 * Turn the character to a given direction
131 * @param d Direction
132 */
turn(Dir d)133 void Walk::turn(Dir d) {
134 Dir dir = (_dir == kDirNone) ? kDirSouth : _dir;
135 if (d != _dir) {
136 step((d == dir) ? (1 + dir + dir) : (9 + 4 * dir + d));
137 _dir = d;
138 }
139 }
140
141 /**
142 * Stop the character and reset his direction
143 */
park()144 void Walk::park() {
145 if (_time == 0)
146 _time++;
147
148 if (_dir != kDirNone) {
149 step(9 + 4 * _dir + _dir);
150 _dir = kDirNone;
151 _tracePtr = -1;
152 }
153 }
154
findWay(Cluster c)155 void Walk::findWay(Cluster c) {
156 if (c._pt == _here._pt)
157 return;
158
159 for (_findLevel = 1; _findLevel <= kMaxFindLevel; _findLevel++) {
160 _target = _here._pt;
161 int16 x = c._pt.x;
162 int16 z = c._pt.y;
163
164 if (find1Way(Cluster(_vm, x, z)))
165 break;
166 }
167
168 _tracePtr = (_findLevel > kMaxFindLevel) ? -1 : (_findLevel - 1);
169 if (_tracePtr < 0)
170 noWay();
171 _time = 1;
172 }
173
findWay(Sprite * spr)174 void Walk::findWay(Sprite *spr) {
175 if (!spr || spr == this)
176 return;
177
178 int x = spr->_x;
179 int z = spr->_z;
180 if (spr->_flags._east)
181 x += spr->_w + _w / 2 - kWalkSide;
182 else
183 x -= _w / 2 - kWalkSide;
184
185 findWay(Cluster(_vm, (x / kMapGridX),
186 ((z < kMapZCnt - kDistMax) ? (z + 1)
187 : (z - 1))));
188 }
189
lower(Sprite * spr)190 bool Walk::lower(Sprite *spr) {
191 return (spr->_y > _y + (_h * 3) / 5);
192 }
193
reach(Sprite * spr,int mode)194 void Walk::reach(Sprite *spr, int mode) {
195 if (spr) {
196 _vm->_hero->findWay(spr);
197 if (mode < 0) {
198 mode = spr->_flags._east;
199 if (lower(spr))
200 mode += 2;
201 }
202 }
203 // note: insert SNAIL commands in reverse order
204 _vm->_commandHandler->insertCommand(kCmdPause, -1, 64, NULL);
205 _vm->_commandHandler->insertCommand(kCmdSeq, -1, kTSeq + mode, this);
206 if (spr) {
207 _vm->_commandHandler->insertCommand(kCmdWait, -1, -1, _vm->_hero);
208 //SNINSERT(SNWALK, -1, -1, spr);
209 }
210 // sequence is not finished,
211 // now it is just at sprite appear (disappear) point
212 }
213
noWay()214 void Walk::noWay() {
215 _vm->trouble(kSeqNoWay, kNoWay);
216 }
217
chkBar() const218 bool Cluster::chkBar() const {
219 assert(_vm->_now <= kSceneMax);
220 return (_pt.x == _vm->_barriers[_vm->_now]._horz) || (_pt.y == _vm->_barriers[_vm->_now]._vert);
221 }
222
find1Way(Cluster c)223 bool Walk::find1Way(Cluster c) {
224 const Cluster tab[4] = { Cluster(_vm, -1, 0), Cluster(_vm, 1, 0), Cluster(_vm, 0, -1), Cluster(_vm, 0, 1)};
225 const int tabLen = 4;
226
227 if (c._pt == _target)
228 // Found destination
229 return true;
230
231 if (_level >= _findLevel)
232 // Nesting limit
233 return false;
234
235 // Look for barriers
236 if (c.chkBar())
237 return false;
238
239 if (c.cell())
240 // Location is occupied
241 return false;
242
243 // Loop through each direction
244 Cluster start = c;
245 for (int i = 0; i < tabLen; i++) {
246 // Reset to starting position
247 c = start;
248
249 do {
250 c._pt += tab[i]._pt;
251 if (!c.isValid())
252 // Break to check next direction
253 break;
254
255 // Recursively check for further paths
256 ++_level;
257 ++start.cell();
258 bool foundPath = find1Way(c);
259 --start.cell();
260 --_level;
261
262 if (foundPath) {
263 // Set route point
264 _trace[_level]->_pt = start._pt;
265 return true;
266 }
267 } while (!c.chkBar() && !c.cell());
268 }
269
270 return false;
271 }
272
273 } // End of namespace CGE
274