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 "dm/objectman.h"
29 #include "dm/dungeonman.h"
30 #include "dm/text.h"
31 
32 namespace DM {
33 
initConstants()34 void ObjectMan::initConstants() {
35 	int16 iconGraphicHeight[7] = {32, 32, 32, 32, 32, 32, 32}; // @ K0077_ai_IconGraphicHeight
36 	int16 iconGraphicFirstIndex[7] = { // G0026_ai_Graphic562_IconGraphicFirstIconIndex
37 		0,     /* First icon index in graphic #42 */
38 		32,    /* First icon index in graphic #43 */
39 		64,    /* First icon index in graphic #44 */
40 		96,    /* First icon index in graphic #45 */
41 		128,   /* First icon index in graphic #46 */
42 		160,   /* First icon index in graphic #47 */
43 		192    /* First icon index in graphic #48 */
44 	};
45 
46 	/* 8 for champion hands in status boxes, 30 for champion inventory, 8 for chest */
47 	_slotBoxes[0] = SlotBox(4, 10, 0);    /* Champion Status Box 0 Ready Hand */
48 	_slotBoxes[1] = SlotBox(24, 10, 0);   /* Champion Status Box 0 Action Hand */
49 	_slotBoxes[2] = SlotBox(73, 10, 0);   /* Champion Status Box 1 Ready Hand */
50 	_slotBoxes[3] = SlotBox(93, 10, 0);   /* Champion Status Box 1 Action Hand */
51 	_slotBoxes[4] = SlotBox(142, 10, 0);  /* Champion Status Box 2 Ready Hand */
52 	_slotBoxes[5] = SlotBox(162, 10, 0);  /* Champion Status Box 2 Action Hand */
53 	_slotBoxes[6] = SlotBox(211, 10, 0);  /* Champion Status Box 3 Ready Hand */
54 	_slotBoxes[7] = SlotBox(231, 10, 0);  /* Champion Status Box 3 Action Hand */
55 	_slotBoxes[8] = SlotBox(6, 53, 0);    /* Ready Hand */
56 	_slotBoxes[9] = SlotBox(62, 53, 0);   /* Action Hand */
57 	_slotBoxes[10] = SlotBox(34, 26, 0);  /* Head */
58 	_slotBoxes[11] = SlotBox(34, 46, 0);  /* Torso */
59 	_slotBoxes[12] = SlotBox(34, 66, 0);  /* Legs */
60 	_slotBoxes[13] = SlotBox(34, 86, 0);  /* Feet */
61 	_slotBoxes[14] = SlotBox(6, 90, 0);   /* Pouch 2 */
62 	_slotBoxes[15] = SlotBox(79, 73, 0);  /* Quiver Line2 1 */
63 	_slotBoxes[16] = SlotBox(62, 90, 0);  /* Quiver Line1 2 */
64 	_slotBoxes[17] = SlotBox(79, 90, 0);  /* Quiver Line2 2 */
65 	_slotBoxes[18] = SlotBox(6, 33, 0);   /* Neck */
66 	_slotBoxes[19] = SlotBox(6, 73, 0);   /* Pouch 1 */
67 	_slotBoxes[20] = SlotBox(62, 73, 0);  /* Quiver Line1 1 */
68 	_slotBoxes[21] = SlotBox(66, 33, 0);  /* Backpack Line1 1 */
69 	_slotBoxes[22] = SlotBox(83, 16, 0);  /* Backpack Line2 2 */
70 	_slotBoxes[23] = SlotBox(100, 16, 0); /* Backpack Line2 3 */
71 	_slotBoxes[24] = SlotBox(117, 16, 0); /* Backpack Line2 4 */
72 	_slotBoxes[25] = SlotBox(134, 16, 0); /* Backpack Line2 5 */
73 	_slotBoxes[26] = SlotBox(151, 16, 0); /* Backpack Line2 6 */
74 	_slotBoxes[27] = SlotBox(168, 16, 0); /* Backpack Line2 7 */
75 	_slotBoxes[28] = SlotBox(185, 16, 0); /* Backpack Line2 8 */
76 	_slotBoxes[29] = SlotBox(202, 16, 0); /* Backpack Line2 9 */
77 	_slotBoxes[30] = SlotBox(83, 33, 0);  /* Backpack Line1 2 */
78 	_slotBoxes[31] = SlotBox(100, 33, 0); /* Backpack Line1 3 */
79 	_slotBoxes[32] = SlotBox(117, 33, 0); /* Backpack Line1 4 */
80 	_slotBoxes[33] = SlotBox(134, 33, 0); /* Backpack Line1 5 */
81 	_slotBoxes[34] = SlotBox(151, 33, 0); /* Backpack Line1 6 */
82 	_slotBoxes[35] = SlotBox(168, 33, 0); /* Backpack Line1 7 */
83 	_slotBoxes[36] = SlotBox(185, 33, 0); /* Backpack Line1 8 */
84 	_slotBoxes[37] = SlotBox(202, 33, 0); /* Backpack Line1 9 */
85 	_slotBoxes[38] = SlotBox(117, 59, 0); /* Chest 1 */
86 	_slotBoxes[39] = SlotBox(106, 76, 0); /* Chest 2 */
87 	_slotBoxes[40] = SlotBox(111, 93, 0); /* Chest 3 */
88 	_slotBoxes[41] = SlotBox(128, 98, 0); /* Chest 4 */
89 	_slotBoxes[42] = SlotBox(145, 101, 0); /* Chest 5 */
90 	_slotBoxes[43] = SlotBox(162, 103, 0); /* Chest 6 */
91 	_slotBoxes[44] = SlotBox(179, 104, 0); /* Chest 7 */
92 	_slotBoxes[45] = SlotBox(196, 105, 0); /* Chest 8 */
93 
94 	for (int i = 0; i < 7; i++) {
95 		_iconGraphicHeight[i] = iconGraphicHeight[i];
96 		_iconGraphicFirstIndex[i] = iconGraphicFirstIndex[i];
97 	}
98 }
99 
ObjectMan(DMEngine * vm)100 ObjectMan::ObjectMan(DMEngine *vm) : _vm(vm) {
101 	for (uint16 i = 0; i < kDMObjectNameCount; ++i)
102 		_objectNames[i] = nullptr;
103 
104 	_objectIconForMousePointer = nullptr;
105 
106 	initConstants();
107 }
108 
~ObjectMan()109 ObjectMan::~ObjectMan() {
110 	delete[] _objectIconForMousePointer;
111 	delete[] _objectNames[0];
112 }
113 
loadObjectNames()114 void ObjectMan::loadObjectNames() {
115 	DisplayMan &dispMan = *_vm->_displayMan;
116 
117 	_objectIconForMousePointer = new byte[16 * 16];
118 
119 	char *objectNames = new char[dispMan.getCompressedDataSize(kDMObjectNamesGraphicIndice) + kDMObjectNameCount];
120 	Common::MemoryReadStream stream = dispMan.getCompressedData(kDMObjectNamesGraphicIndice);
121 
122 	for (uint16 objNameIndex = 0; objNameIndex < kDMObjectNameCount; ++objNameIndex) {
123 		_objectNames[objNameIndex] = objectNames;
124 
125 		byte tmpByte;
126 		for (tmpByte = stream.readByte(); !(tmpByte & 0x80); tmpByte = stream.readByte()) // last char of object name has 7th bit on
127 			*objectNames++ = tmpByte; // write while not last char
128 
129 		*objectNames++ = tmpByte & 0x7F; // write without the 7th bit
130 		*objectNames++ = '\0'; // terminate string
131 	}
132 }
133 
getObjectType(Thing thing)134 IconIndice ObjectMan::getObjectType(Thing thing) {
135 	if (thing == _vm->_thingNone)
136 		return kDMIconIndiceNone;
137 
138 	int16 objectInfoIndex = _vm->_dungeonMan->getObjectInfoIndex(thing);
139 	if (objectInfoIndex != -1)
140 		objectInfoIndex = _vm->_dungeonMan->_objectInfos[objectInfoIndex]._type;
141 
142 	return (IconIndice)objectInfoIndex;
143 }
144 
getIconIndex(Thing thing)145 IconIndice ObjectMan::getIconIndex(Thing thing) {
146 	static byte chargeCountToTorchType[16] = {0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3}; // @ G0029_auc_Graphic562_ChargeCountToTorchType
147 
148 	int16 iconIndex = getObjectType(thing);
149 	if (iconIndex != kDMIconIndiceNone) {
150 		if (((iconIndex < kDMIconIndiceWeaponDagger) && (iconIndex >= kDMIconIndiceJunkCompassNorth)) ||
151 			((iconIndex >= kDMIconIndicePotionMaPotionMonPotion) && (iconIndex <= kDMIconIndicePotionWaterFlask)) ||
152 			(iconIndex == kDMIconIndicePotionEmptyFlask)) {
153 			Junk *junkThing = (Junk*)_vm->_dungeonMan->getThingData(thing);
154 			switch (iconIndex) {
155 			case kDMIconIndiceJunkCompassNorth:
156 				iconIndex += _vm->_dungeonMan->_partyDir;
157 				break;
158 			case kDMIconIndiceWeaponTorchUnlit:
159 				if (((Weapon*)junkThing)->isLit())
160 					iconIndex += chargeCountToTorchType[((Weapon*)junkThing)->getChargeCount()];
161 				break;
162 			case kDMIconIndiceScrollOpen:
163 				if (((Scroll*)junkThing)->getClosed())
164 					iconIndex++;
165 				break;
166 			case kDMIconIndiceJunkWater:
167 			case kDMIconIndiceJunkIllumuletUnequipped:
168 			case kDMIconIndiceJunkJewelSymalUnequipped:
169 				if (junkThing->getChargeCount())
170 					iconIndex++;
171 				break;
172 			case kDMIconIndiceWeaponBoltBladeStormEmpty:
173 			case kDMIconIndiceWeaponFlamittEmpty:
174 			case kDMIconIndiceWeaponStormringEmpty:
175 			case kDMIconIndiceWeaponFuryRaBladeEmpty:
176 			case kDMIconIndiceWeaponEyeOfTimeEmpty:
177 			case kDMIconIndiceWeaponStaffOfClawsEmpty:
178 				if (((Weapon*)junkThing)->getChargeCount())
179 					iconIndex++;
180 				break;
181 			default:
182 				break;
183 			}
184 		}
185 	}
186 	return (IconIndice)iconIndex;
187 }
188 
extractIconFromBitmap(uint16 iconIndex,byte * destBitmap)189 void ObjectMan::extractIconFromBitmap(uint16 iconIndex, byte *destBitmap) {
190 	uint16 counter;
191 	for (counter = 0; counter < 7; counter++) {
192 		if (_iconGraphicFirstIndex[counter] > iconIndex)
193 			break;
194 	}
195 	--counter;
196 	byte *iconBitmap = _vm->_displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxObjectIcons000To031 + counter);
197 	iconIndex -= _iconGraphicFirstIndex[counter];
198 	_vm->_displayMan->_useByteBoxCoordinates = true;
199 	Box blitBox(0, 15, 0, 15);
200 	_vm->_displayMan->blitToBitmap(iconBitmap, destBitmap, blitBox, (iconIndex & 0x000F) << 4, iconIndex & 0x0FF0, 128, 8, kDMColorNoTransparency, _iconGraphicHeight[counter], 16);
201 }
202 
drawIconInSlotBox(uint16 slotBoxIndex,int16 iconIndex)203 void ObjectMan::drawIconInSlotBox(uint16 slotBoxIndex, int16 iconIndex) {
204 	SlotBox *slotBox = &_slotBoxes[slotBoxIndex];
205 	slotBox->_iconIndex = iconIndex;
206 	if (slotBox->_iconIndex == kDMIconIndiceNone)
207 		return;
208 
209 	Box blitBox;
210 	blitBox._rect.left = slotBox->_x;
211 	blitBox._rect.right = blitBox._rect.left + 15;
212 	blitBox._rect.top = slotBox->_y;
213 	blitBox._rect.bottom = blitBox._rect.top + 15;
214 
215 	uint16 iconGraphicIndex;
216 	for (iconGraphicIndex = 0; iconGraphicIndex < 7; iconGraphicIndex++) {
217 		if (_iconGraphicFirstIndex[iconGraphicIndex] > iconIndex)
218 			break;
219 	}
220 	iconGraphicIndex--;
221 	byte *iconBitmap = _vm->_displayMan->getNativeBitmapOrGraphic(iconGraphicIndex + kDMGraphicIdxObjectIcons000To031);
222 	iconIndex -= _iconGraphicFirstIndex[iconGraphicIndex];
223 	int16 byteWidth;
224 	byte* blitDestination;
225 	int16 destHeight;
226 	if (slotBoxIndex >= kDMSlotBoxInventoryFirstSlot) {
227 		blitDestination = _vm->_displayMan->_bitmapViewport;
228 		byteWidth = k112_byteWidthViewport;
229 		destHeight = 136;
230 	} else {
231 		blitDestination = (unsigned char*)_vm->_displayMan->_bitmapScreen;
232 		byteWidth = k160_byteWidthScreen;
233 		destHeight = 200;
234 	}
235 	_vm->_displayMan->_useByteBoxCoordinates = false;
236 	_vm->_displayMan->blitToBitmap(iconBitmap, blitDestination, blitBox, (iconIndex & 0x000F) << 4, iconIndex & 0x0FF0, k128_byteWidth, byteWidth, kDMColorNoTransparency, _iconGraphicHeight[iconGraphicIndex], destHeight);
237 }
238 
drawLeaderObjectName(Thing thing)239 void ObjectMan::drawLeaderObjectName(Thing thing) {
240 	Common::String objectName;
241 	int16 iconIndex = getIconIndex(thing);
242 	if (iconIndex == kDMIconIndiceJunkChampionBones) {
243 		Junk *junk = (Junk*)_vm->_dungeonMan->getThingData(thing);
244 		Common::String champBonesName;
245 
246 		switch (_vm->getGameLanguage()) { // localized
247 		case Common::FR_FRA:
248 			// Fix original bug: strcpy was coming after strcat
249 			champBonesName = Common::String(_objectNames[iconIndex]);
250 			champBonesName += Common::String(_vm->_championMan->_champions[junk->getChargeCount()]._name);
251 			break;
252 		default: // English and German version are the same
253 			champBonesName = Common::String(_vm->_championMan->_champions[junk->getChargeCount()]._name);
254 			champBonesName += Common::String(_objectNames[iconIndex]);
255 			break;
256 		}
257 
258 		objectName = champBonesName;
259 	} else
260 		objectName = Common::String(_objectNames[iconIndex]);
261 
262 	_vm->_textMan->printWithTrailingSpaces(_vm->_displayMan->_bitmapScreen, k160_byteWidthScreen, 233, 37, kDMColorCyan, kDMColorBlack, objectName.c_str(), kDMObjectNameMaximumLength, k200_heightScreen);
263 }
264 
getIconIndexInSlotBox(uint16 slotBoxIndex)265 IconIndice ObjectMan::getIconIndexInSlotBox(uint16 slotBoxIndex) {
266 	return (IconIndice)_slotBoxes[slotBoxIndex]._iconIndex;
267 }
268 
clearLeaderObjectName()269 void ObjectMan::clearLeaderObjectName() {
270 	static Box boxLeaderHandObjectName(233, 319, 33, 38); // @ G0028_s_Graphic562_Box_LeaderHandObjectName
271 	_vm->_displayMan->fillScreenBox(boxLeaderHandObjectName, kDMColorBlack);
272 }
273 
drawIconToScreen(int16 iconIndex,int16 posX,int16 posY)274 void ObjectMan::drawIconToScreen(int16 iconIndex, int16 posX, int16 posY) {
275 	static byte iconBitmap[16 * 16];
276 	Box blitBox(posX, posX + 15, posY, posY + 15);
277 	extractIconFromBitmap(iconIndex, iconBitmap);
278 	_vm->_displayMan->blitToScreen(iconBitmap, &blitBox, k8_byteWidth, kDMColorNoTransparency, 16);
279 }
280 }
281