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 #include "kyra/engine/kyra_hof.h"
24 
25 #include "common/system.h"
26 
27 namespace Kyra {
28 
checkItemCollision(int x,int y)29 int KyraEngine_HoF::checkItemCollision(int x, int y) {
30 	int itemPos = -1, yPos = -1;
31 
32 	for (int i = 0; i < 30; ++i) {
33 		const ItemDefinition &curItem = _itemList[i];
34 
35 		if (curItem.id == kItemNone || curItem.sceneId != _mainCharacter.sceneId)
36 			continue;
37 
38 		int itemX1 = curItem.x - 8 - 3;
39 		int itemX2 = curItem.x + 7 + 3;
40 
41 		if (x < itemX1 || x > itemX2)
42 			continue;
43 
44 		int itemY1 = curItem.y - _itemHtDat[curItem.id] - 3;
45 		int itemY2 = curItem.y + 3;
46 
47 		if (y < itemY1 || y > itemY2)
48 			continue;
49 
50 		if (curItem.y >= yPos) {
51 			itemPos = i;
52 			yPos = curItem.y;
53 		}
54 	}
55 
56 	return itemPos;
57 }
58 
updateWaterFlasks()59 void KyraEngine_HoF::updateWaterFlasks() {
60 	for (int i = 22; i < 24; i++) {
61 		if (_itemInHand == i)
62 			setHandItem(i - 1);
63 
64 		for (int ii = 0; ii < 20; ii++) {
65 			if (_mainCharacter.inventory[ii] == i) {
66 				_mainCharacter.inventory[ii]--;
67 				if (ii < 10) {
68 					clearInventorySlot(ii, 0);
69 					_screen->drawShape(0, getShapePtr(i + 63), _inventoryX[ii], _inventoryY[ii], 0, 0);
70 				}
71 			}
72 		}
73 
74 		for (int ii = 0; ii < 30; ii++) {
75 			if (_itemList[ii].id == i)
76 				_itemList[ii].id--;
77 		}
78 	}
79 }
80 
dropItem(int unk1,Item item,int x,int y,int unk2)81 bool KyraEngine_HoF::dropItem(int unk1, Item item, int x, int y, int unk2) {
82 	if (_mouseState <= -1)
83 		return false;
84 
85 	bool success = processItemDrop(_mainCharacter.sceneId, item, x, y, unk1, unk2);
86 	if (!success) {
87 		snd_playSoundEffect(0x0D);
88 		if (countAllItems() >= 30)
89 			showMessageFromCCode(5, 0x84, 0);
90 	}
91 
92 	return success;
93 }
94 
processItemDrop(uint16 sceneId,Item item,int x,int y,int unk1,int unk2)95 bool KyraEngine_HoF::processItemDrop(uint16 sceneId, Item item, int x, int y, int unk1, int unk2) {
96 	int itemPos = checkItemCollision(x, y);
97 
98 	if (unk1)
99 		itemPos = -1;
100 
101 	if (itemPos >= 0) {
102 		exchangeMouseItem(itemPos);
103 		return false;
104 	}
105 
106 	int freeItemSlot = -1;
107 
108 	if (unk1 != 3) {
109 		for (int i = 0; i < 30; ++i) {
110 			if (_itemList[i].id == kItemNone) {
111 				freeItemSlot = i;
112 				break;
113 			}
114 		}
115 	}
116 
117 	if (freeItemSlot == -1)
118 		return false;
119 
120 	if (sceneId != _mainCharacter.sceneId) {
121 		_itemList[freeItemSlot].x = x;
122 		_itemList[freeItemSlot].y = y;
123 		_itemList[freeItemSlot].id = item;
124 		_itemList[freeItemSlot].sceneId = sceneId;
125 		return true;
126 	}
127 
128 	int itemHeight = _itemHtDat[item];
129 
130 	// no idea why it's '&&' here and not single checks for x and y
131 	if (x == -1 && y == -1) {
132 		x = _rnd.getRandomNumberRng(0x10, 0x130);
133 		y = _rnd.getRandomNumberRng(0x10, 0x87);
134 	}
135 
136 	int posX = x, posY = y;
137 	int itemX = -1, itemY = -1;
138 	bool needRepositioning = true;
139 
140 	while (needRepositioning) {
141 		if ((_screen->getDrawLayer(posX, posY) <= 1 && _screen->getDrawLayer2(posX, posY, itemHeight) <= 1 && isDropable(posX, posY)) || posY == 136) {
142 			int posX2 = posX, posX3 = posX;
143 			bool repositioning = true;
144 
145 			while (repositioning) {
146 				if (isDropable(posX3, posY) && _screen->getDrawLayer(posX3, posY) < 7 && checkItemCollision(posX3, posY) == -1) {
147 					itemX = posX3;
148 					itemY = posY;
149 					needRepositioning = false;
150 					repositioning = false;
151 				}
152 
153 				if (isDropable(posX2, posY) && _screen->getDrawLayer(posX2, posY) < 7 && checkItemCollision(posX2, posY) == -1) {
154 					itemX = posX2;
155 					itemY = posY;
156 					needRepositioning = false;
157 					repositioning = false;
158 				}
159 
160 				if (repositioning) {
161 					posX3 = MAX(posX3 - 2, 16);
162 					posX2 = MIN(posX2 + 2, 304);
163 
164 					if (posX3 <= 16 && posX2 >= 304)
165 						repositioning = false;
166 				}
167 			}
168 		}
169 
170 		if (posY == 136)
171 			needRepositioning = false;
172 		else
173 			posY = MIN(posY + 2, 136);
174 	}
175 
176 	if (itemX == -1 || itemY == -1)
177 		return false;
178 
179 	if (unk1 == 3) {
180 		_itemList[freeItemSlot].x = itemX;
181 		_itemList[freeItemSlot].y = itemY;
182 		return true;
183 	} else if (unk1 == 2) {
184 		itemDropDown(x, y, itemX, itemY, freeItemSlot, item);
185 	}
186 
187 	if (!unk1)
188 		removeHandItem();
189 
190 	itemDropDown(x, y, itemX, itemY, freeItemSlot, item);
191 
192 	if (!unk1 && unk2) {
193 		int itemStr = 3;
194 		if (_lang == 1)
195 			itemStr = getItemCommandStringDrop(item);
196 		updateCommandLineEx(item+54, itemStr, 0xD6);
197 	}
198 
199 	return true;
200 }
201 
itemDropDown(int startX,int startY,int dstX,int dstY,int itemSlot,Item item)202 void KyraEngine_HoF::itemDropDown(int startX, int startY, int dstX, int dstY, int itemSlot, Item item) {
203 	uint8 *itemShape = getShapePtr(item + 64);
204 
205 	if (startX == dstX && startY == dstY) {
206 		if (_layerFlagTable[_screen->getLayer(dstX, dstY)] && item != 13) {
207 			updateCharFacing();
208 			snd_playSoundEffect(0x2D);
209 			removeHandItem();
210 			objectChat(getTableString(0xFF, _cCodeBuffer, true), 0, 0x83, 0xFF);
211 		} else {
212 			_itemList[itemSlot].x = dstX;
213 			_itemList[itemSlot].y = dstY;
214 			_itemList[itemSlot].id = item;
215 			_itemList[itemSlot].sceneId = _mainCharacter.sceneId;
216 			snd_playSoundEffect(0x0C);
217 			addItemToAnimList(itemSlot);
218 		}
219 	} else {
220 		_screen->hideMouse();
221 
222 		if (startY <= dstY) {
223 			int speed = 2;
224 			int curY = startY;
225 			int curX = startX - 8;
226 
227 			backUpGfxRect24x24(curX, curY-16);
228 			while (curY < dstY) {
229 				restoreGfxRect24x24(curX, curY-16);
230 
231 				curY = MIN(curY + speed, dstY);
232 				++speed;
233 
234 				backUpGfxRect24x24(curX, curY-16);
235 				uint32 endDelay = _system->getMillis() + _tickLength;
236 
237 				_screen->drawShape(0, itemShape, curX, curY-16, 0, 0);
238 				_screen->updateScreen();
239 
240 				delayUntil(endDelay, false, true);
241 			}
242 
243 			if (dstX != dstY || (dstY - startY > 16)) {
244 				snd_playSoundEffect(0x69);
245 				speed = MAX(speed, 6);
246 				int speedX = ((dstX - startX) << 4) / speed;
247 				int origSpeed = speed;
248 				speed >>= 1;
249 
250 				if (dstY - startY <= 8)
251 					speed >>= 1;
252 
253 				speed = -speed;
254 
255 				curX = startX << 4;
256 
257 				int x = 0, y = 0;
258 				while (--origSpeed) {
259 					x = (curX >> 4) - 8;
260 					y = curY - 16;
261 
262 					restoreGfxRect24x24(x, y);
263 					curY = MIN(curY + speed, dstY);
264 					curX += speedX;
265 					++speed;
266 
267 					x = (curX >> 4) - 8;
268 					y = curY - 16;
269 					backUpGfxRect24x24(x, y);
270 
271 					uint16 endDelay = _system->getMillis() + _tickLength;
272 					_screen->drawShape(0, itemShape, x, y, 0, 0);
273 					_screen->updateScreen();
274 
275 					delayUntil(endDelay, false, true);
276 				}
277 
278 				restoreGfxRect24x24(x, y);
279 			} else {
280 				restoreGfxRect24x24(curX, curY-16);
281 			}
282 		}
283 
284 		if (_layerFlagTable[_screen->getLayer(dstX, dstY)] && item != 13) {
285 			updateCharFacing();
286 			snd_playSoundEffect(0x2D);
287 			removeHandItem();
288 			_screen->showMouse();
289 			objectChat(getTableString(0xFF, _cCodeBuffer, true), 0, 0x83, 0xFF);
290 		} else {
291 			_itemList[itemSlot].x = dstX;
292 			_itemList[itemSlot].y = dstY;
293 			_itemList[itemSlot].id = item;
294 			_itemList[itemSlot].sceneId = _mainCharacter.sceneId;
295 			snd_playSoundEffect(0x0C);
296 			addItemToAnimList(itemSlot);
297 			_screen->showMouse();
298 		}
299 	}
300 }
301 
exchangeMouseItem(int itemPos)302 void KyraEngine_HoF::exchangeMouseItem(int itemPos) {
303 	deleteItemAnimEntry(itemPos);
304 
305 	int itemId = _itemList[itemPos].id;
306 	_itemList[itemPos].id = _itemInHand;
307 	_itemInHand = itemId;
308 
309 	addItemToAnimList(itemPos);
310 	snd_playSoundEffect(0x0B);
311 	setMouseCursor(_itemInHand);
312 	int str2 = 7;
313 
314 	if (_lang == 1)
315 		str2 = getItemCommandStringPickUp(itemId);
316 
317 	updateCommandLineEx(itemId + 54, str2, 0xD6);
318 
319 	runSceneScript6();
320 }
321 
pickUpItem(int x,int y)322 bool KyraEngine_HoF::pickUpItem(int x, int y) {
323 	int itemPos = checkItemCollision(x, y);
324 
325 	if (itemPos <= -1)
326 		return false;
327 
328 	if (_itemInHand >= 0) {
329 		exchangeMouseItem(itemPos);
330 	} else {
331 		deleteItemAnimEntry(itemPos);
332 		int itemId = _itemList[itemPos].id;
333 		_itemList[itemPos].id = kItemNone;
334 		snd_playSoundEffect(0x0B);
335 		setMouseCursor(itemId);
336 		int str2 = 7;
337 
338 		if (_lang == 1)
339 			str2 = getItemCommandStringPickUp(itemId);
340 
341 		updateCommandLineEx(itemId + 54, str2, 0xD6);
342 		_itemInHand = itemId;
343 
344 		runSceneScript6();
345 	}
346 
347 	return true;
348 }
349 
isDropable(int x,int y)350 bool KyraEngine_HoF::isDropable(int x, int y) {
351 	if (x < 14 || x > 304 || y < 14 || y > 136)
352 		return false;
353 
354 	x -= 8;
355 	y -= 1;
356 
357 	for (int xpos = x; xpos < x + 16; ++xpos) {
358 		if (_screen->getShapeFlag1(xpos, y) == 0)
359 			return false;
360 	}
361 
362 	return true;
363 }
364 
getItemCommandStringDrop(Item item)365 int KyraEngine_HoF::getItemCommandStringDrop(Item item) {
366 	assert(item >= 0 && item < _itemStringMapSize);
367 	int stringId = _itemStringMap[item];
368 
369 	static const int dropStringIds[] = {
370 		0x2D, 0x103, 0x003, 0x106
371 	};
372 	assert(stringId < ARRAYSIZE(dropStringIds));
373 
374 	return dropStringIds[stringId];
375 }
376 
getItemCommandStringPickUp(Item item)377 int KyraEngine_HoF::getItemCommandStringPickUp(Item item) {
378 	assert(item >= 0 && item < _itemStringMapSize);
379 	int stringId = _itemStringMap[item];
380 
381 	static const int pickUpStringIds[] = {
382 		0x02B, 0x102, 0x007, 0x105
383 	};
384 	assert(stringId < ARRAYSIZE(pickUpStringIds));
385 
386 	return pickUpStringIds[stringId];
387 }
388 
getItemCommandStringInv(Item item)389 int KyraEngine_HoF::getItemCommandStringInv(Item item) {
390 	assert(item >= 0 && item < _itemStringMapSize);
391 	int stringId = _itemStringMap[item];
392 
393 	static const int pickUpStringIds[] = {
394 		0x02C, 0x104, 0x008, 0x107
395 	};
396 	assert(stringId < ARRAYSIZE(pickUpStringIds));
397 
398 	return pickUpStringIds[stringId];
399 }
400 
itemIsFlask(Item item)401 bool KyraEngine_HoF::itemIsFlask(Item item) {
402 	for (int i = 0; _flaskTable[i] != kItemNone; ++i) {
403 		if (_flaskTable[i] == item)
404 			return true;
405 	}
406 
407 	return false;
408 }
409 
setMouseCursor(Item item)410 void KyraEngine_HoF::setMouseCursor(Item item) {
411 	int shape = 0;
412 	int hotX = 1;
413 	int hotY = 1;
414 
415 	if (item != kItemNone) {
416 		hotX = 8;
417 		hotY = 15;
418 		shape = item+64;
419 	}
420 
421 	_screen->setMouseCursor(hotX, hotY, getShapePtr(shape));
422 }
423 
424 } // End of namespace Kyra
425