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