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 * Based on the Reverse Engineering work of Christophe Fontanel,
25 * maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/)
26 */
27 
28 #include "graphics/surface.h"
29 #include "graphics/thumbnail.h"
30 
31 #include "dm/inventory.h"
32 #include "dm/dungeonman.h"
33 #include "dm/eventman.h"
34 #include "dm/group.h"
35 #include "dm/menus.h"
36 #include "dm/gfx.h"
37 #include "dm/text.h"
38 #include "dm/objectman.h"
39 #include "dm/timeline.h"
40 #include "dm/projexpl.h"
41 #include "dm/sounds.h"
42 
43 
44 namespace DM {
45 
initConstants()46 void InventoryMan::initConstants() {
47 	static const char* skillLevelNamesEN[15] = {"NEOPHYTE", "NOVICE", "APPRENTICE", "JOURNEYMAN", "CRAFTSMAN",
48 		"ARTISAN", "ADEPT", "EXPERT", "` MASTER", "a MASTER","b MASTER", "c MASTER", "d MASTER", "e MASTER", "ARCHMASTER"};
49 	static const char* skillLevelNamesDE[15] = {"ANFAENGER", "NEULING", "LEHRLING", "ARBEITER", "GESELLE", "HANDWERKR", "FACHMANN",
50 		"EXPERTE", "` MEISTER", "a MEISTER", "b MEISTER", "c MEISTER", "d MEISTER", "e MEISTER", "ERZMEISTR"};
51 	static const char* skillLevelNamesFR[15] = {"NEOPHYTE", "NOVICE", "APPRENTI", "COMPAGNON", "ARTISAN", "PATRON",
52 		"ADEPTE", "EXPERT", "MAITRE '", "MAITRE a", "MAITRE b", "MAITRE c", "MAITRE d", "MAITRE e", "SUR-MAITRE"};
53 	const char **translatedSkillLevel;
54 	switch (_vm->getGameLanguage()) { // localized
55 	default:
56 	case Common::EN_ANY:
57 		translatedSkillLevel = skillLevelNamesEN;
58 		break;
59 	case Common::DE_DEU:
60 		translatedSkillLevel = skillLevelNamesDE;
61 		break;
62 	case Common::FR_FRA:
63 		translatedSkillLevel = skillLevelNamesFR;
64 		break;
65 	}
66 	for (int i = 0; i < 15; ++i)
67 		_skillLevelNames[i] = translatedSkillLevel[i];
68 
69 	_boxPanel = Box(80, 223, 52, 124); // @ G0032_s_Graphic562_Box_Panel
70 }
71 
InventoryMan(DMEngine * vm)72 InventoryMan::InventoryMan(DMEngine *vm) : _vm(vm) {
73 	_inventoryChampionOrdinal = 0;
74 	_panelContent = kDMPanelContentFoodWaterPoisoned;
75 	for (uint16 i = 0; i < 8; ++i)
76 		_chestSlots[i] = Thing(0);
77 	_openChest = _vm->_thingNone;
78 	_objDescTextXpos = 0;
79 	_objDescTextYpos = 0;
80 
81 	for (int i = 0; i < 15; i++)
82 		_skillLevelNames[i] = nullptr;
83 
84 	initConstants();
85 }
86 
toggleInventory(ChampionIndex championIndex)87 void InventoryMan::toggleInventory(ChampionIndex championIndex) {
88 	static Box boxFloppyZzzCross(174, 218, 2, 12); // @ G0041_s_Graphic562_Box_ViewportFloppyZzzCross
89 
90 	DisplayMan &display = *_vm->_displayMan;
91 	ChampionMan &championMan = *_vm->_championMan;
92 
93 	if ((championIndex != kDMChampionCloseInventory) && !championMan._champions[championIndex]._currHealth)
94 		return;
95 
96 	if (_vm->_pressingMouth || _vm->_pressingEye)
97 		return;
98 
99 	_vm->_stopWaitingForPlayerInput = true;
100 	uint16 inventoryChampionOrdinal = _inventoryChampionOrdinal;
101 	if (_vm->indexToOrdinal(championIndex) == inventoryChampionOrdinal)
102 		championIndex = kDMChampionCloseInventory;
103 
104 	_vm->_eventMan->showMouse();
105 	if (inventoryChampionOrdinal) {
106 		_inventoryChampionOrdinal = _vm->indexToOrdinal(kDMChampionNone);
107 		closeChest();
108 		Champion *champion = &championMan._champions[_vm->ordinalToIndex(inventoryChampionOrdinal)];
109 		if (champion->_currHealth && !championMan._candidateChampionOrdinal) {
110 			setFlag(champion->_attributes, kDMAttributeStatusBox);
111 			championMan.drawChampionState((ChampionIndex)_vm->ordinalToIndex(inventoryChampionOrdinal));
112 		}
113 		if (championMan._partyIsSleeping) {
114 			_vm->_eventMan->hideMouse();
115 			return;
116 		}
117 		if (championIndex == kDMChampionCloseInventory) {
118 			_vm->_eventMan->_refreshMousePointerInMainLoop = true;
119 			_vm->_menuMan->drawMovementArrows();
120 			_vm->_eventMan->hideMouse();
121 			_vm->_eventMan->_secondaryMouseInput = _vm->_eventMan->_secondaryMouseInputMovement;
122 			_vm->_eventMan->_secondaryKeyboardInput = _vm->_eventMan->_secondaryKeyboardInputMovement;
123 			_vm->_eventMan->discardAllInput();
124 			display.drawFloorAndCeiling();
125 			return;
126 		}
127 	}
128 	display._useByteBoxCoordinates = false;
129 	_inventoryChampionOrdinal = _vm->indexToOrdinal(championIndex);
130 	if (!inventoryChampionOrdinal)
131 		display.shadeScreenBox(&display._boxMovementArrows, kDMColorBlack);
132 
133 	Champion *champion = &championMan._champions[championIndex];
134 	display.loadIntoBitmap(kDMGraphicIdxInventory, display._bitmapViewport);
135 	if (championMan._candidateChampionOrdinal)
136 		display.fillBoxBitmap(display._bitmapViewport, boxFloppyZzzCross, kDMColorDarkestGray, k112_byteWidthViewport, k136_heightViewport);
137 
138 	switch (_vm->getGameLanguage()) { // localized
139 	default:
140 	case Common::EN_ANY:
141 		_vm->_textMan->printToViewport(5, 116, kDMColorLightestGray, "HEALTH");
142 		_vm->_textMan->printToViewport(5, 124, kDMColorLightestGray, "STAMINA");
143 		break;
144 	case Common::DE_DEU:
145 		_vm->_textMan->printToViewport(5, 116, kDMColorLightestGray, "GESUND");
146 		_vm->_textMan->printToViewport(5, 124, kDMColorLightestGray, "KRAFT");
147 		break;
148 	case Common::FR_FRA:
149 		_vm->_textMan->printToViewport(5, 116, kDMColorLightestGray, "SANTE");
150 		_vm->_textMan->printToViewport(5, 124, kDMColorLightestGray, "VIGUEUR");
151 		break;
152 	}
153 
154 	_vm->_textMan->printToViewport(5, 132, kDMColorLightestGray, "MANA");
155 
156 	for (uint16 i = kDMSlotReadyHand; i < kDMSlotChest1; i++)
157 		championMan.drawSlot(championIndex, i);
158 
159 	setFlag(champion->_attributes, kDMAttributeViewport | kDMAttributeStatusBox | kDMAttributePanel | kDMAttributeLoad | kDMAttributeStatistics | kDMAttributeNameTitle);
160 	championMan.drawChampionState(championIndex);
161 	_vm->_eventMan->_mousePointerBitmapUpdated = true;
162 	_vm->_eventMan->hideMouse();
163 	_vm->_eventMan->_secondaryMouseInput = _vm->_eventMan->_secondaryMouseInputChampionInventory;
164 	_vm->_eventMan->_secondaryKeyboardInput = nullptr;
165 	_vm->_eventMan->discardAllInput();
166 }
167 
drawStatusBoxPortrait(ChampionIndex championIndex)168 void InventoryMan::drawStatusBoxPortrait(ChampionIndex championIndex) {
169 	DisplayMan &dispMan = *_vm->_displayMan;
170 
171 	dispMan._useByteBoxCoordinates = false;
172 	Box box;
173 	box._rect.top = 0;
174 	box._rect.bottom = 28;
175 	box._rect.left = championIndex * kDMChampionStatusBoxSpacing + 7;
176 	box._rect.right = box._rect.left + 31;
177 	dispMan.blitToScreen(_vm->_championMan->_champions[championIndex]._portrait, &box, k16_byteWidth, kDMColorNoTransparency, 29);
178 }
179 
drawPanelHorizontalBar(int16 x,int16 y,int16 pixelWidth,Color color)180 void InventoryMan::drawPanelHorizontalBar(int16 x, int16 y, int16 pixelWidth, Color color) {
181 	DisplayMan &display = *_vm->_displayMan;
182 	Box box;
183 	box._rect.left = x;
184 	box._rect.right = box._rect.left + pixelWidth;
185 	box._rect.top = y;
186 	box._rect.bottom = box._rect.top + 6;
187 	display._useByteBoxCoordinates = false;
188 	display.fillBoxBitmap(display._bitmapViewport, box, color, k112_byteWidthViewport, k136_heightViewport);
189 }
190 
drawPanelFoodOrWaterBar(int16 amount,int16 y,Color color)191 void InventoryMan::drawPanelFoodOrWaterBar(int16 amount, int16 y, Color color) {
192 	if (amount < -512)
193 		color = kDMColorRed;
194 	else if (amount < 0)
195 		color = kDMColorYellow;
196 
197 	int16 pixelWidth = amount + 1024;
198 	if (pixelWidth == 3072)
199 		pixelWidth = 3071;
200 
201 	pixelWidth /= 32;
202 	drawPanelHorizontalBar(115, y + 2, pixelWidth, kDMColorBlack);
203 	drawPanelHorizontalBar(113, y, pixelWidth, color);
204 }
205 
drawPanelFoodWaterPoisoned()206 void InventoryMan::drawPanelFoodWaterPoisoned() {
207 	static Box boxFood(112, 159, 60, 68); // @ G0035_s_Graphic562_Box_Food
208 	static Box boxWater(112, 159, 83, 91); // @ G0036_s_Graphic562_Box_Water
209 	static Box boxPoisoned(112, 207, 105, 119); // @ G0037_s_Graphic562_Box_Poisoned
210 
211 	Champion &champ = _vm->_championMan->_champions[_inventoryChampionOrdinal];
212 	closeChest();
213 	DisplayMan &dispMan = *_vm->_displayMan;
214 
215 	dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxPanelEmpty), _boxPanel, k72_byteWidth, kDMColorRed, 73);
216 
217 	switch (_vm->getGameLanguage()) { // localized
218 	default:
219 	case Common::EN_ANY:
220 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxFoodLabel), boxFood, k24_byteWidth, kDMColorDarkestGray, 9);
221 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxWaterLabel), boxWater, k24_byteWidth, kDMColorDarkestGray, 9);
222 		break;
223 	case Common::DE_DEU:
224 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxFoodLabel), boxFood, k32_byteWidth, kDMColorDarkestGray, 9);
225 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxWaterLabel), boxWater, k32_byteWidth, kDMColorDarkestGray, 9);
226 		break;
227 	case Common::FR_FRA:
228 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxFoodLabel), boxFood, k48_byteWidth, kDMColorDarkestGray, 9);
229 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxWaterLabel), boxWater, k24_byteWidth, kDMColorDarkestGray, 9);
230 		break;
231 	}
232 
233 	if (champ._poisonEventCount)
234 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxPoisionedLabel),
235 								   boxPoisoned, k48_byteWidth, kDMColorDarkestGray, 15);
236 
237 	drawPanelFoodOrWaterBar(champ._food, 69, kDMColorLightBrown);
238 	drawPanelFoodOrWaterBar(champ._water, 92, kDMColorBlue);
239 }
240 
drawPanelResurrectReincarnate()241 void InventoryMan::drawPanelResurrectReincarnate() {
242 	DisplayMan &display = *_vm->_displayMan;
243 
244 	_panelContent = kDMPanelContentResurrectReincarnate;
245 	display.blitToViewport(display.getNativeBitmapOrGraphic(kDMGraphicIdxPanelResurectReincarnate),
246 										 _boxPanel, k72_byteWidth, kDMColorDarkGreen, 73);
247 }
248 
drawPanel()249 void InventoryMan::drawPanel() {
250 	closeChest();
251 
252 	ChampionMan &cm = *_vm->_championMan;
253 	if (cm._candidateChampionOrdinal) {
254 		drawPanelResurrectReincarnate();
255 		return;
256 	}
257 
258 	Thing thing = cm._champions[_vm->ordinalToIndex(_inventoryChampionOrdinal)].getSlot(kDMSlotActionHand);
259 
260 	_panelContent = kDMPanelContentFoodWaterPoisoned;
261 	switch (thing.getType()) {
262 	case kDMThingTypeContainer:
263 		_panelContent = kDMPanelContentChest;
264 		break;
265 	case kDMThingTypeScroll:
266 		_panelContent = kDMPanelContentScroll;
267 		break;
268 	default:
269 		thing = _vm->_thingNone;
270 		break;
271 	}
272 
273 	if (thing == _vm->_thingNone)
274 		drawPanelFoodWaterPoisoned();
275 	else
276 		drawPanelObject(thing, false);
277 }
278 
closeChest()279 void InventoryMan::closeChest() {
280 	DungeonMan &dunMan = *_vm->_dungeonMan;
281 
282 	bool processFirstChestSlot = true;
283 	if (_openChest == _vm->_thingNone)
284 		return;
285 	Container *container = (Container *)dunMan.getThingData(_openChest);
286 	_openChest = _vm->_thingNone;
287 	container->getSlot() = _vm->_thingEndOfList;
288 	Thing prevThing;
289 	for (int16 chestSlotIndex = 0; chestSlotIndex < 8; ++chestSlotIndex) {
290 		Thing thing = _chestSlots[chestSlotIndex];
291 		if (thing != _vm->_thingNone) {
292 			_chestSlots[chestSlotIndex] = _vm->_thingNone; // CHANGE8_09_FIX
293 
294 			if (processFirstChestSlot) {
295 				processFirstChestSlot = false;
296 				*dunMan.getThingData(thing) = _vm->_thingEndOfList.toUint16();
297 				container->getSlot() = prevThing = thing;
298 			} else {
299 				dunMan.linkThingToList(thing, prevThing, kDMMapXNotOnASquare, 0);
300 				prevThing = thing;
301 			}
302 		}
303 	}
304 }
305 
drawPanelScrollTextLine(int16 yPos,char * text)306 void InventoryMan::drawPanelScrollTextLine(int16 yPos, char *text) {
307 	for (char *iter = text; *iter != '\0'; ++iter) {
308 		if ((*iter >= 'A') && (*iter <= 'Z'))
309 			*iter -= 64;
310 		else if (*iter >= '{') // this branch is CHANGE5_03_IMPROVEMENT
311 			*iter -= 96;
312 	}
313 	_vm->_textMan->printToViewport(162 - (6 * strlen(text) / 2), yPos, kDMColorBlack, text, kDMColorWhite);
314 }
315 
drawPanelScroll(Scroll * scroll)316 void InventoryMan::drawPanelScroll(Scroll *scroll) {
317 	DisplayMan &dispMan = *_vm->_displayMan;
318 
319 	char stringFirstLine[300];
320 	_vm->_dungeonMan->decodeText(stringFirstLine, Thing(scroll->getTextStringThingIndex()), (TextType)(kDMTextTypeScroll | kDMMaskDecodeEvenIfInvisible));
321 	char *charRed = stringFirstLine;
322 	while (*charRed && (*charRed != '\n'))
323 		charRed++;
324 
325 	*charRed = '\0';
326 	dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxPanelOpenScroll),
327 							   _boxPanel, k72_byteWidth, kDMColorRed, 73);
328 	int16 lineCount = 1;
329 	charRed++;
330 	char *charGreen = charRed; // first char of the second line
331 	while (*charGreen) {
332 		/* BUG0_47 Graphical glitch when you open a scroll. If there is a single line of text in a scroll
333 		(with no carriage return) then charGreen points to undefined data. This may result in a graphical
334 		glitch and also corrupt other memory. This is not an issue in the original dungeons where all
335 		scrolls contain at least one carriage return character */
336 		if (*charGreen == '\n')
337 			lineCount++;
338 
339 		charGreen++;
340 	}
341 
342 	if (*(charGreen - 1) != '\n')
343 		lineCount++;
344 	else if (*(charGreen - 2) == '\n')
345 		lineCount--;
346 
347 	int16 yPos = 92 - (7 * lineCount) / 2; // center the text vertically
348 	drawPanelScrollTextLine(yPos, stringFirstLine);
349 	charGreen = charRed;
350 	while (*charGreen) {
351 		yPos += 7;
352 		while (*charRed && (*charRed != '\n'))
353 			charRed++;
354 
355 		if (!(*charRed))
356 			charRed[1] = '\0';
357 
358 		*charRed++ = '\0';
359 		drawPanelScrollTextLine(yPos, charGreen);
360 		charGreen = charRed;
361 	}
362 }
363 
openAndDrawChest(Thing thingToOpen,Container * chest,bool isPressingEye)364 void InventoryMan::openAndDrawChest(Thing thingToOpen, Container *chest, bool isPressingEye) {
365 	DisplayMan &dispMan = *_vm->_displayMan;
366 	ObjectMan &objMan = *_vm->_objectMan;
367 
368 	if (_openChest == thingToOpen)
369 		return;
370 
371 	if (_openChest != _vm->_thingNone)
372 		closeChest(); // CHANGE8_09_FIX
373 
374 	_openChest = thingToOpen;
375 	if (!isPressingEye) {
376 		objMan.drawIconInSlotBox(kDMSlotBoxInventoryActionHand, kDMIconIndiceContainerChestOpen);
377 	}
378 	dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxPanelOpenChest),
379 							   _boxPanel, k72_byteWidth, kDMColorRed, 73);
380 	int16 chestSlotIndex = 0;
381 	Thing thing = chest->getSlot();
382 	int16 thingCount = 0;
383 	while (thing != _vm->_thingEndOfList) {
384 		if (++thingCount > 8)
385 			break; // CHANGE8_08_FIX, make sure that no more than the first 8 objects in a chest are drawn
386 
387 		objMan.drawIconInSlotBox(chestSlotIndex + kDMSlotBoxChestFirstSlot, objMan.getIconIndex(thing));
388 		_chestSlots[chestSlotIndex++] = thing;
389 		thing = _vm->_dungeonMan->getNextThing(thing);
390 	}
391 	while (chestSlotIndex < 8) {
392 		objMan.drawIconInSlotBox(chestSlotIndex + kDMSlotBoxChestFirstSlot, kDMIconIndiceNone);
393 		_chestSlots[chestSlotIndex++] = _vm->_thingNone;
394 	}
395 }
396 
drawIconToViewport(IconIndice iconIndex,int16 xPos,int16 yPos)397 void InventoryMan::drawIconToViewport(IconIndice iconIndex, int16 xPos, int16 yPos) {
398 	static byte iconBitmap[16 * 16];
399 	Box boxIcon(xPos, xPos + 15, yPos, yPos + 15);
400 
401 	_vm->_objectMan->extractIconFromBitmap(iconIndex, iconBitmap);
402 	_vm->_displayMan->blitToViewport(iconBitmap, boxIcon, k8_byteWidth, kDMColorNoTransparency, 16);
403 }
404 
buildObjectAttributeString(int16 potentialAttribMask,int16 actualAttribMask,const char ** attribStrings,char * destString,const char * prefixString,const char * suffixString)405 void InventoryMan::buildObjectAttributeString(int16 potentialAttribMask, int16 actualAttribMask, const char **attribStrings, char *destString, const char *prefixString, const char *suffixString) {
406 	uint16 identicalBitCount = 0;
407 	int16 attribMask = 1;
408 	for (uint16 stringIndex = 0; stringIndex < 16; stringIndex++, attribMask <<= 1) {
409 		if (attribMask & potentialAttribMask & actualAttribMask)
410 			identicalBitCount++;
411 	}
412 
413 	if (identicalBitCount == 0) {
414 		*destString = '\0';
415 		return;
416 	}
417 
418 	strcpy(destString, prefixString);
419 
420 	attribMask = 1;
421 	for (uint16 stringIndex = 0; stringIndex < 16; stringIndex++, attribMask <<= 1) {
422 		if (attribMask & potentialAttribMask & actualAttribMask) {
423 			strcat(destString, attribStrings[stringIndex]);
424 			if (identicalBitCount-- > 2) {
425 				strcat(destString, ", ");
426 			} else if (identicalBitCount == 1) {
427 
428 				switch (_vm->getGameLanguage()) { // localized
429 				default:
430 				case Common::EN_ANY: strcat(destString, " AND "); break;
431 				case Common::DE_DEU: strcat(destString, " UND "); break;
432 				case Common::FR_FRA: strcat(destString, " ET "); break;
433 				}
434 			}
435 		}
436 	}
437 
438 	strcat(destString, suffixString);
439 }
440 
drawPanelObjectDescriptionString(const char * descString)441 void InventoryMan::drawPanelObjectDescriptionString(const char *descString) {
442 	if (descString[0] == '\f') { // form feed
443 		descString++;
444 		_objDescTextXpos = 108;
445 		_objDescTextYpos = 59;
446 	}
447 
448 	if (descString[0]) {
449 		char stringTmpBuff[128];
450 		strcpy(stringTmpBuff, descString);
451 
452 		char *stringLine = stringTmpBuff;
453 		bool severalLines = false;
454 		char *string = nullptr;
455 		while (*stringLine) {
456 			if (strlen(stringLine) > 18) { // if string is too long to fit on one line
457 				string = &stringLine[17];
458 				while (*string != ' ') // go back to the last space character
459 					string--;
460 
461 				*string = '\0'; // and split the string there
462 				severalLines = true;
463 			}
464 
465 			_vm->_textMan->printToViewport(_objDescTextXpos, _objDescTextYpos, kDMColorLightestGray, stringLine);
466 			_objDescTextYpos += 7;
467 			if (severalLines) {
468 				severalLines = false;
469 				stringLine = ++string;
470 			} else {
471 				*stringLine = '\0';
472 			}
473 		}
474 	}
475 }
476 
drawPanelArrowOrEye(bool pressingEye)477 void InventoryMan::drawPanelArrowOrEye(bool pressingEye) {
478 	static Box boxArrowOrEye(83, 98, 57, 65); // @ G0033_s_Graphic562_Box_ArrowOrEye
479 
480 	DisplayMan &dispMan = *_vm->_displayMan;
481 	dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(pressingEye ? kDMGraphicIdxEyeForObjectDescription : kDMGraphicIdxArrowForChestContent),
482 							   boxArrowOrEye, k8_byteWidth, kDMColorRed, 9);
483 }
484 
drawPanelObject(Thing thingToDraw,bool pressingEye)485 void InventoryMan::drawPanelObject(Thing thingToDraw, bool pressingEye) {
486 	static Box boxObjectDescCircle(105, 136, 53, 79); // @ G0034_s_Graphic562_Box_ObjectDescriptionCircle
487 
488 	DungeonMan &dungeon = *_vm->_dungeonMan;
489 	ObjectMan &objMan = *_vm->_objectMan;
490 	DisplayMan &dispMan = *_vm->_displayMan;
491 	ChampionMan &champMan = *_vm->_championMan;
492 	TextMan &textMan = *_vm->_textMan;
493 
494 	if (_vm->_pressingEye || _vm->_pressingMouth)
495 		closeChest();
496 
497 	uint16 *rawThingPtr = dungeon.getThingData(thingToDraw);
498 	drawPanelObjectDescriptionString("\f"); // form feed
499 	ThingType thingType = thingToDraw.getType();
500 	if (thingType == kDMThingTypeScroll)
501 		drawPanelScroll((Scroll *)rawThingPtr);
502 	else if (thingType == kDMThingTypeContainer)
503 		openAndDrawChest(thingToDraw, (Container *)rawThingPtr, pressingEye);
504 	else {
505 		IconIndice iconIndex = objMan.getIconIndex(thingToDraw);
506 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxPanelEmpty),
507 								   _boxPanel, k72_byteWidth, kDMColorRed, 73);
508 		dispMan.blitToViewport(dispMan.getNativeBitmapOrGraphic(kDMGraphicIdxObjectDescCircle),
509 								   boxObjectDescCircle, k16_byteWidth, kDMColorDarkestGray, 27);
510 
511 		Common::String descString;
512 		Common::String str;
513 		if (iconIndex == kDMIconIndiceJunkChampionBones) {
514 			switch (_vm->getGameLanguage()) { // localized
515 			case Common::FR_FRA:
516 				// Fix original bug dur to a cut&paste error: string was concatenated then overwritten by the name
517 				str = Common::String::format("%s %s", objMan._objectNames[iconIndex], champMan._champions[((Junk *)rawThingPtr)->getChargeCount()]._name);
518 				break;
519 			default: // German and English versions are the same
520 				str = Common::String::format("%s %s", champMan._champions[((Junk *)rawThingPtr)->getChargeCount()]._name, objMan._objectNames[iconIndex]);
521 				break;
522 			}
523 
524 			descString = str;
525 		} else if ((thingType == kDMThingTypePotion)
526 				   && (iconIndex != kDMIconIndicePotionWaterFlask)
527 				   && (champMan.getSkillLevel((ChampionIndex)_vm->ordinalToIndex(_inventoryChampionOrdinal), kDMSkillPriest) > 1)) {
528 			str = ('_' + ((Potion *)rawThingPtr)->getPower() / 40);
529 			str += " ";
530 			str += objMan._objectNames[iconIndex];
531 			descString = str;
532 		} else {
533 			descString = objMan._objectNames[iconIndex];
534 		}
535 
536 		textMan.printToViewport(134, 68, kDMColorLightestGray, descString.c_str());
537 		drawIconToViewport(iconIndex, 111, 59);
538 
539 
540 		_objDescTextYpos = 87;
541 
542 		uint16 potentialAttribMask = 0;
543 		uint16 actualAttribMask = 0;
544 		switch (thingType) {
545 		case kDMThingTypeWeapon: {
546 			potentialAttribMask = kDMDescriptionMaskCursed | kDMDescriptionMaskPoisoned | kDMDescriptionMaskBroken;
547 			Weapon *weapon = (Weapon *)rawThingPtr;
548 			actualAttribMask = (weapon->getCursed() << 3) | (weapon->getPoisoned() << 1) | (weapon->getBroken() << 2);
549 			if ((iconIndex >= kDMIconIndiceWeaponTorchUnlit)
550 				&& (iconIndex <= kDMIconIndiceWeaponTorchLit)
551 				&& (weapon->getChargeCount() == 0)) {
552 
553 				switch (_vm->getGameLanguage()) { // localized
554 				default:
555 				case Common::EN_ANY:
556 					drawPanelObjectDescriptionString("(BURNT OUT)");
557 					break;
558 				case Common::DE_DEU:
559 					drawPanelObjectDescriptionString("(AUSGEBRANNT)");
560 					break;
561 				case Common::FR_FRA:
562 					drawPanelObjectDescriptionString("(CONSUME)");
563 					break;
564 				}
565 			}
566 			break;
567 		}
568 		case kDMThingTypeArmour: {
569 			potentialAttribMask = kDMDescriptionMaskCursed | kDMDescriptionMaskBroken;
570 			Armour *armour = (Armour *)rawThingPtr;
571 			actualAttribMask = (armour->getCursed() << 3) | (armour->getBroken() << 2);
572 			break;
573 		}
574 		case kDMThingTypePotion: {
575 			potentialAttribMask = kDMDescriptionMaskConsumable;
576 			Potion *potion = (Potion *)rawThingPtr;
577 			actualAttribMask = dungeon._objectInfos[kDMObjectInfoIndexFirstPotion + potion->getType()].getAllowedSlots();
578 			break;
579 		}
580 		case kDMThingTypeJunk: {
581 			if ((iconIndex >= kDMIconIndiceJunkWater) && (iconIndex <= kDMIconIndiceJunkWaterSkin)) {
582 				potentialAttribMask = 0;
583 				const char *descStringEN[4] = {"(EMPTY)", "(ALMOST EMPTY)", "(ALMOST FULL)", "(FULL)"};
584 				const char *descStringDE[4] = {"(LEER)", "(FAST LEER)", "(FAST VOLL)", "(VOLL)"};
585 				const char *descStringFR[4] = {"(VIDE)", "(PRESQUE VIDE)", "(PRESQUE PLEINE)", "(PLEINE)"};
586 
587 				Junk *junk = (Junk *)rawThingPtr;
588 				switch (_vm->getGameLanguage()) { // localized
589 				case Common::DE_DEU:
590 					descString = descStringDE[junk->getChargeCount()];
591 					break;
592 				case Common::FR_FRA:
593 					descString = descStringFR[junk->getChargeCount()];
594 					break;
595 				default:
596 					descString = descStringEN[junk->getChargeCount()];
597 					break;
598 				}
599 
600 				drawPanelObjectDescriptionString(descString.c_str());
601 			} else if ((iconIndex >= kDMIconIndiceJunkCompassNorth) && (iconIndex <= kDMIconIndiceJunkCompassWest)) {
602 				const static char *directionNameEN[4] = {"NORTH", "EAST", "SOUTH", "WEST"};
603 				const static char *directionNameDE[4] = {"NORDEN", "OSTEN", "SUEDEN", "WESTEN"};
604 				const static char *directionNameFR[4] = {"AU NORD", "A L'EST", "AU SUD", "A L'OUEST"};
605 
606 				potentialAttribMask = 0;
607 
608 				switch (_vm->getGameLanguage()) { // localized
609 				case Common::DE_DEU:
610 					str = "GRUPPE BLICKT NACH ";
611 					str += directionNameDE[iconIndex];
612 					break;
613 				case Common::FR_FRA:
614 					str = "GROUPE FACE ";
615 					str += directionNameFR[iconIndex];
616 					break;
617 				default:
618 					str = "PARTY FACING ";
619 					str += directionNameEN[iconIndex];
620 					break;
621 				}
622 
623 				drawPanelObjectDescriptionString(str.c_str());
624 			} else {
625 				Junk *junk = (Junk *)rawThingPtr;
626 				potentialAttribMask = kDMDescriptionMaskConsumable;
627 				actualAttribMask = dungeon._objectInfos[kDMObjectInfoIndexFirstJunk + junk->getType()].getAllowedSlots();
628 			}
629 			break;
630 		}
631 		default:
632 			break;
633 		} // end of switch
634 
635 		if (potentialAttribMask) {
636 			static const char *attribStringEN[4] = {"CONSUMABLE", "POISONED", "BROKEN", "CURSED"};
637 			static const char *attribStringDE[4] = {"ESSBAR", "VERGIFTET", "DEFEKT", "VERFLUCHT"};
638 			static const char *attribStringFR[4] = {"COMESTIBLE", "EMPOISONNE", "BRISE", "MAUDIT"};
639 			const char **attribString = nullptr;
640 
641 			switch (_vm->getGameLanguage()) { // localized
642 			case Common::DE_DEU:
643 				attribString = attribStringDE;
644 				break;
645 			case Common::FR_FRA:
646 				attribString = attribStringFR;
647 				break;
648 			default:
649 				attribString = attribStringEN;
650 				break;
651 			}
652 
653 			char destString[40];
654 			buildObjectAttributeString(potentialAttribMask, actualAttribMask, attribString, destString, "(", ")");
655 			drawPanelObjectDescriptionString(destString);
656 		}
657 
658 		uint16 weight = dungeon.getObjectWeight(thingToDraw);
659 		switch (_vm->getGameLanguage()) { // localized
660 		case Common::DE_DEU:
661 			str = "WIEGT " + champMan.getStringFromInteger(weight / 10, false, 3) + ",";
662 			break;
663 		case Common::FR_FRA:
664 			str = "PESE " + champMan.getStringFromInteger(weight / 10, false, 3) + "KG,";
665 			break;
666 		default:
667 			str = "WEIGHS " + champMan.getStringFromInteger(weight / 10, false, 3) + ".";
668 			break;
669 		}
670 
671 		weight -= (weight / 10) * 10;
672 		str += champMan.getStringFromInteger(weight, false, 1);
673 
674 		switch (_vm->getGameLanguage()) { // localized
675 		case Common::FR_FRA:
676 			str += ".";
677 			break;
678 		default:
679 			str += " KG.";
680 			break;
681 		}
682 
683 		drawPanelObjectDescriptionString(str.c_str());
684 	}
685 	drawPanelArrowOrEye(pressingEye);
686 }
687 
setDungeonViewPalette()688 void InventoryMan::setDungeonViewPalette() {
689 	static const int16 palIndexToLightAmmount[6] = {99, 75, 50, 25, 1, 0}; // @ G0040_ai_Graphic562_PaletteIndexToLightAmount
690 	DisplayMan &display = *_vm->_displayMan;
691 	ChampionMan &championMan = *_vm->_championMan;
692 	DungeonMan &dungeon = *_vm->_dungeonMan;
693 
694 	if (dungeon._currMap->_difficulty == 0) {
695 		display._dungeonViewPaletteIndex = 0; /* Brightest color palette index */
696 	} else {
697 		/* Get torch light power from both hands of each champion in the party */
698 		int16 counter = 4; /* BUG0_01 Coding error without consequence. The hands of four champions are inspected even if there are less champions in the party. No consequence as the data in unused champions is set to 0 and _vm->_objectMan->f32_getObjectType then returns -1 */
699 		Champion *curChampion = championMan._champions;
700 		int16 torchesLightPower[8];
701 		int16 *curTorchLightPower = torchesLightPower;
702 		while (counter--) {
703 			uint16 slotIndex = kDMSlotActionHand + 1;
704 			while (slotIndex--) {
705 				Thing slotThing = curChampion->_slots[slotIndex];
706 				if ((_vm->_objectMan->getObjectType(slotThing) >= kDMIconIndiceWeaponTorchUnlit) &&
707 					(_vm->_objectMan->getObjectType(slotThing) <= kDMIconIndiceWeaponTorchLit)) {
708 					Weapon *curWeapon = (Weapon *)dungeon.getThingData(slotThing);
709 					*curTorchLightPower = curWeapon->getChargeCount();
710 				} else {
711 					*curTorchLightPower = 0;
712 				}
713 				curTorchLightPower++;
714 			}
715 			curChampion++;
716 		}
717 		/* Sort torch light power values so that the four highest values are in the first four entries in the array L1045_ai_TorchesLightPower in decreasing order. The last four entries contain the smallest values but they are not sorted */
718 		curTorchLightPower = torchesLightPower;
719 		int16 torchIndex = 0;
720 		while (torchIndex != 4) {
721 			counter = 7 - torchIndex;
722 			int16 *L1041_pi_TorchLightPower = &torchesLightPower[torchIndex + 1];
723 			while (counter--) {
724 				if (*L1041_pi_TorchLightPower > *curTorchLightPower) {
725 					int16 AL1044_ui_TorchLightPower = *L1041_pi_TorchLightPower;
726 					*L1041_pi_TorchLightPower = *curTorchLightPower;
727 					*curTorchLightPower = AL1044_ui_TorchLightPower;
728 				}
729 				L1041_pi_TorchLightPower++;
730 			}
731 			curTorchLightPower++;
732 			torchIndex++;
733 		}
734 		/* Get total light amount provided by the four torches with the highest light power values and by the fifth torch in the array which may be any one of the four torches with the smallest ligh power values */
735 		uint16 torchLightAmountMultiplier = 6;
736 		torchIndex = 5;
737 		int16 totalLightAmount = 0;
738 		curTorchLightPower = torchesLightPower;
739 		while (torchIndex--) {
740 			if (*curTorchLightPower) {
741 				totalLightAmount += (championMan._lightPowerToLightAmount[*curTorchLightPower] << torchLightAmountMultiplier) >> 6;
742 				torchLightAmountMultiplier = MAX(0, torchLightAmountMultiplier - 1);
743 			}
744 			curTorchLightPower++;
745 		}
746 		totalLightAmount += championMan._party._magicalLightAmount;
747 		/* Select palette corresponding to the total light amount */
748 		const int16 *curLightAmount = palIndexToLightAmmount;
749 		int16 paletteIndex;
750 		if (totalLightAmount > 0) {
751 			paletteIndex = 0; /* Brightest color palette index */
752 			while (*curLightAmount++ > totalLightAmount)
753 				paletteIndex++;
754 		} else {
755 			paletteIndex = 5; /* Darkest color palette index */
756 		}
757 		display._dungeonViewPaletteIndex = paletteIndex;
758 	}
759 
760 	display._refreshDungeonViewPaleteRequested = true;
761 }
762 
decreaseTorchesLightPower()763 void InventoryMan::decreaseTorchesLightPower() {
764 	ChampionMan &championMan = *_vm->_championMan;
765 	DungeonMan &dungeon = *_vm->_dungeonMan;
766 
767 	bool torchChargeCountChanged = false;
768 	int16 championCount = championMan._partyChampionCount;
769 	if (championMan._candidateChampionOrdinal)
770 		championCount--;
771 
772 	Champion *curChampion = championMan._champions;
773 	while (championCount--) {
774 		int16 slotIndex = kDMSlotActionHand + 1;
775 		while (slotIndex--) {
776 			int16 iconIndex = _vm->_objectMan->getIconIndex(curChampion->_slots[slotIndex]);
777 			if ((iconIndex >= kDMIconIndiceWeaponTorchUnlit) && (iconIndex <= kDMIconIndiceWeaponTorchLit)) {
778 				Weapon *curWeapon = (Weapon *)dungeon.getThingData(curChampion->_slots[slotIndex]);
779 				if (curWeapon->getChargeCount()) {
780 					if (curWeapon->setChargeCount(curWeapon->getChargeCount() - 1) == 0) {
781 						curWeapon->setDoNotDiscard(false);
782 					}
783 					torchChargeCountChanged = true;
784 				}
785 			}
786 		}
787 		curChampion++;
788 	}
789 
790 	if (torchChargeCountChanged) {
791 		setDungeonViewPalette();
792 		championMan.drawChangedObjectIcons();
793 	}
794 }
795 
drawChampionSkillsAndStatistics()796 void InventoryMan::drawChampionSkillsAndStatistics() {
797 	static const char *statisticNamesEN[7] = {"L", "STRENGTH", "DEXTERITY", "WISDOM", "VITALITY", "ANTI-MAGIC", "ANTI-FIRE"};
798 	static const char *statisticNamesDE[7] = {"L", "STAERKE", "FLINKHEIT", "WEISHEIT", "VITALITAET", "ANTI-MAGIE", "ANTI-FEUER"};
799 	static const char *statisticNamesFR[7] = {"L", "FORCE", "DEXTERITE", "SAGESSE", "VITALITE", "ANTI-MAGIE", "ANTI-FEU"};
800 
801 	DisplayMan &display = *_vm->_displayMan;
802 	ChampionMan &championMan = *_vm->_championMan;
803 	const char **statisticNames;
804 
805 	switch (_vm->getGameLanguage()) { // localized
806 	case Common::DE_DEU:
807 		statisticNames = statisticNamesDE;
808 		break;
809 	case Common::FR_FRA:
810 		statisticNames = statisticNamesFR;
811 		break;
812 	default:
813 		statisticNames = statisticNamesEN;
814 		break;
815 	}
816 
817 	closeChest();
818 	uint16 championIndex = _vm->ordinalToIndex(_inventoryChampionOrdinal);
819 	Champion *curChampion = &championMan._champions[championIndex];
820 	display.blitToViewport(display.getNativeBitmapOrGraphic(kDMGraphicIdxPanelEmpty), _boxPanel, k72_byteWidth, kDMColorRed, 73);
821 	int16 textPosY = 58;
822 	for (uint16 idx = kDMSkillFighter; idx <= kDMSkillWizard; idx++) {
823 		int16 skillLevel = MIN((uint16)16, championMan.getSkillLevel(championIndex, idx | kDMIgnoreTemporaryExperience));
824 		if (skillLevel == 1)
825 			continue;
826 
827 		Common::String displayString;
828 
829 		switch (_vm->getGameLanguage()) { // localized
830 		case Common::FR_FRA:
831 			// Fix original bug: Due to a copy&paste error, the string was concatenate then overwritten be the last part
832 			displayString = Common::String::format("%s %s", championMan._baseSkillName[idx], _skillLevelNames[skillLevel - 2]);
833 			break;
834 		default: // English and German versions are built the same way
835 			displayString = Common::String::format("%s %s", _skillLevelNames[skillLevel - 2], championMan._baseSkillName[idx]);
836 			break;
837 		}
838 		_vm->_textMan->printToViewport(108, textPosY, kDMColorLightestGray, displayString.c_str());
839 		textPosY += 7;
840 	}
841 	textPosY = 86;
842 	for (uint16 idx = kDMStatStrength; idx <= kDMStatAntifire; idx++) {
843 		_vm->_textMan->printToViewport(108, textPosY, kDMColorLightestGray, statisticNames[idx]);
844 		int16 statisticCurrentValue = curChampion->_statistics[idx][kDMStatCurrent];
845 		uint16 statisticMaximumValue = curChampion->_statistics[idx][kDMStatMaximum];
846 		int16 statisticColor;
847 		if (statisticCurrentValue < statisticMaximumValue)
848 			statisticColor = kDMColorRed;
849 		else if (statisticCurrentValue > statisticMaximumValue)
850 			statisticColor = kDMColorLightGreen;
851 		else
852 			statisticColor = kDMColorLightestGray;
853 
854 		_vm->_textMan->printToViewport(174, textPosY, (Color)statisticColor, championMan.getStringFromInteger(statisticCurrentValue, true, 3).c_str());
855 		Common::String displayString = "/" + championMan.getStringFromInteger(statisticMaximumValue, true, 3);
856 		_vm->_textMan->printToViewport(192, textPosY, kDMColorLightestGray, displayString.c_str());
857 		textPosY += 7;
858 	}
859 }
860 
drawStopPressingMouth()861 void InventoryMan::drawStopPressingMouth() {
862 	drawPanel();
863 	_vm->_displayMan->drawViewport(k0_viewportNotDungeonView);
864 	_vm->_eventMan->_hideMousePointerRequestCount = 1;
865 	_vm->_eventMan->showMouse();
866 	_vm->_eventMan->showMouse();
867 	_vm->_eventMan->showMouse();
868 }
869 
drawStopPressingEye()870 void InventoryMan::drawStopPressingEye() {
871 	drawIconToViewport(kDMIconIndiceEyeNotLooking, 12, 13);
872 	drawPanel();
873 	_vm->_displayMan->drawViewport(k0_viewportNotDungeonView);
874 	Thing leaderHandObject = _vm->_championMan->_leaderHandObject;
875 	if (leaderHandObject != _vm->_thingNone)
876 		_vm->_objectMan->drawLeaderObjectName(leaderHandObject);
877 
878 	_vm->_eventMan->showMouse();
879 	_vm->_eventMan->showMouse();
880 	_vm->_eventMan->showMouse();
881 }
882 
clickOnMouth()883 void InventoryMan::clickOnMouth() {
884 	static int16 foodAmounts[8] = {
885 		500,    /* Apple */
886 		600,    /* Corn */
887 		650,    /* Bread */
888 		820,    /* Cheese */
889 		550,    /* Screamer Slice */
890 		350,    /* Worm round */
891 		990,    /* Drumstick / Shank */
892 		1400    /* Dragon steak */
893 	};
894 
895 	DisplayMan &display = *_vm->_displayMan;
896 	ChampionMan &championMan = *_vm->_championMan;
897 	DungeonMan &dungeon = *_vm->_dungeonMan;
898 
899 
900 	if (championMan._leaderEmptyHanded) {
901 		if (_panelContent == kDMPanelContentFoodWaterPoisoned)
902 			return;
903 
904 		_vm->_eventMan->_ignoreMouseMovements = true;
905 		_vm->_pressingMouth = true;
906 		if (!_vm->_eventMan->isMouseButtonDown(kDMMouseButtonLeft)) {
907 			_vm->_eventMan->_ignoreMouseMovements = false;
908 			_vm->_pressingMouth = false;
909 			_vm->_stopPressingMouth = false;
910 		} else {
911 			_vm->_eventMan->showMouse();
912 			_vm->_eventMan->_hideMousePointerRequestCount = 1;
913 			drawPanelFoodWaterPoisoned();
914 			display.drawViewport(k0_viewportNotDungeonView);
915 		}
916 		return;
917 	}
918 
919 	if (championMan._candidateChampionOrdinal)
920 		return;
921 
922 	Thing handThing = championMan._leaderHandObject;
923 	if (!getFlag(dungeon._objectInfos[dungeon.getObjectInfoIndex(handThing)]._allowedSlots, kDMMaskMouth))
924 		return;
925 
926 	uint16 iconIndex = _vm->_objectMan->getIconIndex(handThing);
927 	uint16 handThingType = handThing.getType();
928 	uint16 handThingWeight = dungeon.getObjectWeight(handThing);
929 	uint16 championIndex = _vm->ordinalToIndex(_inventoryChampionOrdinal);
930 	Champion *curChampion = &championMan._champions[championIndex];
931 	Junk *junkData = (Junk *)dungeon.getThingData(handThing);
932 	bool removeObjectFromLeaderHand;
933 	if ((iconIndex >= kDMIconIndiceJunkWater) && (iconIndex <= kDMIconIndiceJunkWaterSkin)) {
934 		if (!(junkData->getChargeCount()))
935 			return;
936 
937 		curChampion->_water = MIN(curChampion->_water + 800, 2048);
938 		junkData->setChargeCount(junkData->getChargeCount() - 1);
939 		removeObjectFromLeaderHand = false;
940 	} else if (handThingType == kDMThingTypePotion)
941 		removeObjectFromLeaderHand = false;
942 	else {
943 		junkData->setNextThing(_vm->_thingNone);
944 		removeObjectFromLeaderHand = true;
945 	}
946 	_vm->_eventMan->showMouse();
947 	if (removeObjectFromLeaderHand)
948 		championMan.getObjectRemovedFromLeaderHand();
949 
950 	if (handThingType == kDMThingTypePotion) {
951 		uint16 potionPower = ((Potion *)junkData)->getPower();
952 		uint16 counter = ((511 - potionPower) / (32 + (potionPower + 1) / 8)) >> 1;
953 		uint16 adjustedPotionPower = (potionPower / 25) + 8; /* Value between 8 and 18 */
954 
955 		switch (((Potion *)junkData)->getType()) {
956 		case kDMPotionTypeRos:
957 			adjustStatisticCurrentValue(curChampion, kDMStatDexterity, adjustedPotionPower);
958 			break;
959 		case kDMPotionTypeKu:
960 			adjustStatisticCurrentValue(curChampion, kDMStatStrength, (((Potion *)junkData)->getPower() / 35) + 5); /* Value between 5 and 12 */
961 			break;
962 		case kDMPotionTypeDane:
963 			adjustStatisticCurrentValue(curChampion, kDMStatWisdom, adjustedPotionPower);
964 			break;
965 		case kDMPotionTypeNeta:
966 			adjustStatisticCurrentValue(curChampion, kDMStatVitality, adjustedPotionPower);
967 			break;
968 		case kDMPotionTypeAntivenin:
969 			championMan.unpoison(championIndex);
970 			break;
971 		case kDMPotionTypeMon:
972 			curChampion->_currStamina += MIN(curChampion->_maxStamina - curChampion->_currStamina, curChampion->_maxStamina / counter);
973 			break;
974 		case kDMPotionTypeYa: {
975 			adjustedPotionPower += adjustedPotionPower >> 1;
976 			if (curChampion->_shieldDefense > 50)
977 				adjustedPotionPower >>= 2;
978 
979 			curChampion->_shieldDefense += adjustedPotionPower;
980 			TimelineEvent newEvent;
981 			newEvent._type = kDMEventTypeChampionShield;
982 			newEvent._mapTime = _vm->setMapAndTime(dungeon._partyMapIndex, _vm->_gameTime + (adjustedPotionPower * adjustedPotionPower));
983 			newEvent._priority = championIndex;
984 			newEvent._Bu._defense = adjustedPotionPower;
985 			_vm->_timeline->addEventGetEventIndex(&newEvent);
986 			setFlag(curChampion->_attributes, kDMAttributeStatusBox);
987 			}
988 			break;
989 		case kDMPotionTypeEe: {
990 			uint16 mana = MIN(900, (curChampion->_currMana + adjustedPotionPower) + (adjustedPotionPower - 8));
991 			if (mana > curChampion->_maxMana)
992 				mana -= (mana - MAX(curChampion->_currMana, curChampion->_maxMana)) >> 1;
993 
994 			curChampion->_currMana = mana;
995 			}
996 			break;
997 		case kDMPotionTypeVi: {
998 			uint16 healWoundIterationCount = MAX(1, (((Potion *)junkData)->getPower() / 42));
999 			curChampion->_currHealth += curChampion->_maxHealth / counter;
1000 			int16 wounds = curChampion->_wounds;
1001 			if (wounds) { /* If the champion is wounded */
1002 				counter = 10;
1003 				do {
1004 					for (uint16 i = 0; i < healWoundIterationCount; i++)
1005 						curChampion->_wounds &= _vm->getRandomNumber(65536);
1006 
1007 					healWoundIterationCount = 1;
1008 				} while ((wounds == curChampion->_wounds) && --counter); /* Loop until at least one wound is healed or there are no more heal iterations */
1009 			}
1010 			setFlag(curChampion->_attributes, kDMAttributeLoad | kDMAttributeWounds);
1011 			}
1012 			break;
1013 		case kDMPotionTypeWaterFlask:
1014 			curChampion->_water = MIN(curChampion->_water + 1600, 2048);
1015 			break;
1016 		default:
1017 			break;
1018 		}
1019 		((Potion *)junkData)->setType(kDMPotionTypeEmptyFlask);
1020 	} else if ((iconIndex >= kDMIconIndiceJunkApple) && (iconIndex < kDMIconIndiceJunkIronKey))
1021 		curChampion->_food = MIN(curChampion->_food + foodAmounts[iconIndex - kDMIconIndiceJunkApple], 2048);
1022 
1023 	if (curChampion->_currStamina > curChampion->_maxStamina)
1024 		curChampion->_currStamina = curChampion->_maxStamina;
1025 
1026 	if (curChampion->_currHealth > curChampion->_maxHealth)
1027 		curChampion->_currHealth = curChampion->_maxHealth;
1028 
1029 	if (removeObjectFromLeaderHand) {
1030 		for (uint16 i = 5; --i; _vm->delay(8)) { /* Animate mouth icon */
1031 			_vm->_objectMan->drawIconToScreen(kDMIconIndiceMouthOpen + !(i & 0x0001), 56, 46);
1032 			_vm->_eventMan->discardAllInput();
1033 			if (_vm->_engineShouldQuit)
1034 				return;
1035 			display.updateScreen();
1036 		}
1037 	} else {
1038 		championMan.drawChangedObjectIcons();
1039 		championMan._champions[championMan._leaderIndex]._load += dungeon.getObjectWeight(handThing) - handThingWeight;
1040 		setFlag(championMan._champions[championMan._leaderIndex]._attributes, kDMAttributeLoad);
1041 	}
1042 	_vm->_sound->requestPlay(kDMSoundIndexSwallow, dungeon._partyMapX, dungeon._partyMapY, kDMSoundModePlayImmediately);
1043 	setFlag(curChampion->_attributes, kDMAttributeStatistics);
1044 
1045 	if (_panelContent == kDMPanelContentFoodWaterPoisoned)
1046 		setFlag(curChampion->_attributes, kDMAttributePanel);
1047 
1048 	championMan.drawChampionState((ChampionIndex)championIndex);
1049 	_vm->_eventMan->hideMouse();
1050 }
1051 
adjustStatisticCurrentValue(Champion * champ,uint16 statIndex,int16 valueDelta)1052 void InventoryMan::adjustStatisticCurrentValue(Champion *champ, uint16 statIndex, int16 valueDelta) {
1053 	int16 delta;
1054 	if (valueDelta >= 0) {
1055 		int16 currentValue = champ->_statistics[statIndex][kDMStatCurrent];
1056 		if (currentValue > 120) {
1057 			valueDelta >>= 1;
1058 			if (currentValue > 150) {
1059 				valueDelta >>= 1;
1060 			}
1061 			valueDelta++;
1062 		}
1063 		delta = MIN(valueDelta, (int16)(170 - currentValue));
1064 	} else { /* BUG0_00 Useless code. The function is always called with valueDelta having a positive value */
1065 		delta = MAX(valueDelta, int16(champ->_statistics[statIndex][kDMStatMinimum] - champ->_statistics[statIndex][kDMStatCurrent]));
1066 	}
1067 	champ->_statistics[statIndex][kDMStatCurrent] += delta;
1068 }
1069 
clickOnEye()1070 void InventoryMan::clickOnEye() {
1071 	ChampionMan &championMan = *_vm->_championMan;
1072 
1073 	_vm->_eventMan->_ignoreMouseMovements = true;
1074 	_vm->_pressingEye = true;
1075 	if (!_vm->_eventMan->isMouseButtonDown(kDMMouseButtonLeft)) {
1076 		_vm->_eventMan->_ignoreMouseMovements = false;
1077 		_vm->_pressingEye = false;
1078 		_vm->_stopPressingEye = false;
1079 		return;
1080 	}
1081 	_vm->_eventMan->discardAllInput();
1082 	_vm->_eventMan->hideMouse();
1083 	_vm->_eventMan->hideMouse();
1084 	_vm->_eventMan->hideMouse();
1085 	_vm->delay(8);
1086 	drawIconToViewport(kDMIconIndiceEyeLooking, 12, 13);
1087 	if (championMan._leaderEmptyHanded)
1088 		drawChampionSkillsAndStatistics();
1089 	else {
1090 		_vm->_objectMan->clearLeaderObjectName();
1091 		drawPanelObject(championMan._leaderHandObject, true);
1092 	}
1093 	_vm->_displayMan->drawViewport(k0_viewportNotDungeonView);
1094 }
1095 
1096 }
1097