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 "ultima/ultima4/game/item.h"
24 #include "ultima/ultima4/game/codex.h"
25 #include "ultima/ultima4/game/game.h"
26 #include "ultima/ultima4/game/names.h"
27 #include "ultima/ultima4/game/player.h"
28 #include "ultima/ultima4/game/portal.h"
29 #include "ultima/ultima4/game/context.h"
30 #include "ultima/ultima4/game/weapon.h"
31 #include "ultima/ultima4/controllers/alpha_action_controller.h"
32 #include "ultima/ultima4/controllers/combat_controller.h"
33 #include "ultima/ultima4/core/utils.h"
34 #include "ultima/ultima4/filesys/savegame.h"
35 #include "ultima/ultima4/gfx/screen.h"
36 #include "ultima/ultima4/map/annotation.h"
37 #include "ultima/ultima4/map/dungeon.h"
38 #include "ultima/ultima4/map/location.h"
39 #include "ultima/ultima4/map/map.h"
40 #include "ultima/ultima4/map/mapmgr.h"
41 #include "ultima/ultima4/map/tileset.h"
42 #include "ultima/ultima4/ultima4.h"
43
44 namespace Ultima {
45 namespace Ultima4 {
46
47 Items *g_items;
48
49 const ItemLocation Items::ITEMS[N_ITEMS] = {
50 {
51 "Mandrake Root", nullptr, "mandrake1",
52 &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY
53 },
54 {
55 "Mandrake Root", nullptr, "mandrake2",
56 &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY
57 },
58 {
59 "Nightshade", nullptr, "nightshade1",
60 &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY
61 },
62 {
63 "Nightshade", nullptr, "nightshade2",
64 &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY
65 },
66 {
67 "the Bell of Courage", "bell", "bell",
68 &Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BELL, 0
69 },
70 {
71 "the Book of Truth", "book", "book",
72 &Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BOOK, 0
73 },
74 {
75 "the Candle of Love", "candle", "candle",
76 &Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_CANDLE, 0
77 },
78 {
79 "A Silver Horn", "horn", "horn",
80 &Items::isItemInInventory, &Items::putItemInInventory, &Items::useHorn, ITEM_HORN, 0
81 },
82 {
83 "the Wheel from the H.M.S. Cape", "wheel", "wheel",
84 &Items::isItemInInventory, &Items::putItemInInventory, &Items::useWheel, ITEM_WHEEL, 0
85 },
86 {
87 "the Skull of Modain the Wizard", "skull", "skull",
88 &Items::isSkullInInventory, &Items::putItemInInventory, &Items::useSkull, ITEM_SKULL, SC_NEWMOONS
89 },
90 {
91 "the Red Stone", "red", "redstone",
92 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_RED, 0
93 },
94 {
95 "the Orange Stone", "orange", "orangestone",
96 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_ORANGE, 0
97 },
98 {
99 "the Yellow Stone", "yellow", "yellowstone",
100 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_YELLOW, 0
101 },
102 {
103 "the Green Stone", "green", "greenstone",
104 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_GREEN, 0
105 },
106 {
107 "the Blue Stone", "blue", "bluestone",
108 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLUE, 0
109 },
110 {
111 "the Purple Stone", "purple", "purplestone",
112 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_PURPLE, 0
113 },
114 {
115 "the Black Stone", "black", "blackstone",
116 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLACK, SC_NEWMOONS
117 },
118 {
119 "the White Stone", "white", "whitestone",
120 &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_WHITE, 0
121 },
122
123 /* handlers for using generic objects */
124 { nullptr, "stone", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 },
125 { nullptr, "stones", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 },
126 { nullptr, "key", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 },
127 { nullptr, "keys", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 },
128
129 /* Lycaeum telescope */
130 { nullptr, nullptr, "telescope", nullptr, &Items::useTelescope, nullptr, 0, 0 },
131
132 {
133 "Mystic Armor", nullptr, "mysticarmor",
134 &Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, ARMR_MYSTICROBES, SC_FULLAVATAR
135 },
136 {
137 "Mystic Swords", nullptr, "mysticswords",
138 &Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, WEAP_MYSTICSWORD, SC_FULLAVATAR
139 },
140 {
141 "the sulfury remains of an ancient Sosarian Laser Gun. It turns to ash in your fingers", nullptr, "lasergun", // lol, where'd that come from?
142 //Looks like someone was experimenting with "maps.xml". It effectively increments sulfur ash by one due to '16' being an invalid weapon index.
143 &Items::isWeaponInInventory, &Items::putWeaponInInventory, nullptr, 16, 0
144 },
145 {
146 "the rune of Honesty", nullptr, "honestyrune",
147 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONESTY, 0
148 },
149 {
150 "the rune of Compassion", nullptr, "compassionrune",
151 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_COMPASSION, 0
152 },
153 {
154 "the rune of Valor", nullptr, "valorrune",
155 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_VALOR, 0
156 },
157 {
158 "the rune of Justice", nullptr, "justicerune",
159 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_JUSTICE, 0
160 },
161 {
162 "the rune of Sacrifice", nullptr, "sacrificerune",
163 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SACRIFICE, 0
164 },
165 {
166 "the rune of Honor", nullptr, "honorrune",
167 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONOR, 0
168 },
169 {
170 "the rune of Spirituality", nullptr, "spiritualityrune",
171 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SPIRITUALITY, 0
172 },
173 {
174 "the rune of Humility", nullptr, "humilityrune",
175 &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HUMILITY, 0
176 }
177 };
178
Items()179 Items::Items() : destroyAllCreaturesCallback(nullptr),
180 needStoneNames(0), stoneMask(0) {
181 g_items = this;
182 }
183
~Items()184 Items::~Items() {
185 g_items = nullptr;
186 }
187
setDestroyAllCreaturesCallback(DestroyAllCreaturesCallback callback)188 void Items::setDestroyAllCreaturesCallback(DestroyAllCreaturesCallback callback) {
189 destroyAllCreaturesCallback = callback;
190 }
191
isRuneInInventory(int virt)192 bool Items::isRuneInInventory(int virt) {
193 return g_ultima->_saveGame->_runes & virt;
194 }
195
putRuneInInventory(int virt)196 void Items::putRuneInInventory(int virt) {
197 g_context->_party->member(0)->awardXp(100);
198 g_context->_party->adjustKarma(KA_FOUND_ITEM);
199 g_ultima->_saveGame->_runes |= virt;
200 #ifdef IOS_ULTIMA4
201 Common::String virtueName;
202 switch (virt) {
203 default:
204 case RUNE_HONESTY:
205 virtueName = "Honesty";
206 break;
207 case RUNE_HONOR:
208 virtueName = "Honor";
209 break;
210 case RUNE_HUMILITY:
211 virtueName = "Humility";
212 break;
213 case RUNE_JUSTICE:
214 virtueName = "Justice";
215 break;
216 case RUNE_SACRIFICE:
217 virtueName = "Sacrifice";
218 break;
219 case RUNE_SPIRITUALITY:
220 virtueName = "Spirituality";
221 break;
222 case RUNE_VALOR:
223 virtueName = "Valor";
224 break;
225 case RUNE_COMPASSION:
226 virtueName = "Compassion";
227 break;
228 }
229 U4IOS::testFlightPassCheckPoint("Player got stone: " + virtueName);
230 #endif
231 g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
232 }
233
isStoneInInventory(int virt)234 bool Items::isStoneInInventory(int virt) {
235 /* generic test: does the party have any stones yet? */
236 if (virt == -1)
237 return (g_ultima->_saveGame->_stones > 0);
238 /* specific test: does the party have a specific stone? */
239 else
240 return g_ultima->_saveGame->_stones & virt;
241 }
242
putStoneInInventory(int virt)243 void Items::putStoneInInventory(int virt) {
244 g_context->_party->member(0)->awardXp(200);
245 g_context->_party->adjustKarma(KA_FOUND_ITEM);
246 g_ultima->_saveGame->_stones |= virt;
247 #ifdef IOS_ULTIMA4
248 Common::String stoneName;
249 switch (virt) {
250 default:
251 case STONE_BLACK:
252 stoneName = "Black";
253 break;
254 case STONE_BLUE:
255 stoneName = "Blue";
256 break;
257 case STONE_GREEN:
258 stoneName = "Green";
259 break;
260 case STONE_ORANGE:
261 stoneName = "Orange";
262 break;
263 case STONE_PURPLE:
264 stoneName = "Purple";
265 break;
266 case STONE_RED:
267 stoneName = "Red";
268 break;
269 case STONE_WHITE:
270 stoneName = "White";
271 break;
272 case STONE_YELLOW:
273 stoneName = "Yellow";
274 break;
275 }
276 U4IOS::testFlightPassCheckPoint("Player got rune: " + stoneName);
277 #endif
278 g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
279 }
280
isItemInInventory(int item)281 bool Items::isItemInInventory(int item) {
282 return g_ultima->_saveGame->_items & item;
283 }
284
isSkullInInventory(int unused)285 bool Items::isSkullInInventory(int unused) {
286 return (g_ultima->_saveGame->_items & (ITEM_SKULL | ITEM_SKULL_DESTROYED));
287 }
288
putItemInInventory(int item)289 void Items::putItemInInventory(int item) {
290 g_context->_party->member(0)->awardXp(400);
291 g_context->_party->adjustKarma(KA_FOUND_ITEM);
292 g_ultima->_saveGame->_items |= item;
293 #ifdef IOS_ULTIMA4
294 Common::String itemName;
295 switch (item) {
296 default:
297 case ITEM_BELL:
298 itemName = "Bell";
299 break;
300 case ITEM_BOOK:
301 itemName = "Book";
302 break;
303 case ITEM_CANDLE:
304 itemName = "Candle";
305 break;
306 case ITEM_HORN:
307 itemName = "Horn";
308 break;
309 case ITEM_KEY_C:
310 itemName = "Key Courage";
311 break;
312 case ITEM_KEY_L:
313 itemName = "Key Love";
314 break;
315 case ITEM_KEY_T:
316 itemName = "Key Truth";
317 break;
318 case ITEM_SKULL:
319 itemName = "Skull";
320 break;
321 case ITEM_WHEEL:
322 itemName = "Wheel";
323 break;
324
325 }
326 U4IOS::testFlightPassCheckPoint("Player got rune: " + itemName);
327 #endif
328 g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
329 }
330
useBBC(int item)331 void Items::useBBC(int item) {
332 Coords abyssEntrance(0xe9, 0xe9);
333 /* on top of the Abyss entrance */
334 if (g_context->_location->_coords == abyssEntrance) {
335 /* must use bell first */
336 if (item == ITEM_BELL) {
337 #ifdef IOS_ULTIMA4
338 U4IOS::testFlightPassCheckPoint("The Bell rings on and on!");
339 #endif
340 g_screen->screenMessage("\nThe Bell rings on and on!\n");
341 g_ultima->_saveGame->_items |= ITEM_BELL_USED;
342 }
343 /* then the book */
344 else if ((item == ITEM_BOOK) && (g_ultima->_saveGame->_items & ITEM_BELL_USED)) {
345 #ifdef IOS_ULTIMA4
346 U4IOS::testFlightPassCheckPoint("The words resonate with the ringing!");
347 #endif
348 g_screen->screenMessage("\nThe words resonate with the ringing!\n");
349 g_ultima->_saveGame->_items |= ITEM_BOOK_USED;
350 }
351 /* then the candle */
352 else if ((item == ITEM_CANDLE) && (g_ultima->_saveGame->_items & ITEM_BOOK_USED)) {
353 g_screen->screenMessage("\nAs you light the Candle the Earth Trembles!\n");
354 #ifdef IOS_ULTIMA4
355 U4IOS::testFlightPassCheckPoint("As you light the Candle the Earth Trembles!");
356 #endif
357 g_ultima->_saveGame->_items |= ITEM_CANDLE_USED;
358 } else {
359 g_screen->screenMessage("\nHmm...No effect!\n");
360 }
361 }
362 /* somewhere else */
363 else {
364 g_screen->screenMessage("\nHmm...No effect!\n");
365 }
366 }
367
useHorn(int item)368 void Items::useHorn(int item) {
369 g_screen->screenMessage("\nThe Horn sounds an eerie tone!\n");
370 g_context->_aura->set(Aura::HORN, 10);
371 }
372
useWheel(int item)373 void Items::useWheel(int item) {
374 if ((g_context->_transportContext == TRANSPORT_SHIP) && (g_ultima->_saveGame->_shipHull == 50)) {
375 g_screen->screenMessage("\nOnce mounted, the Wheel glows with a blue light!\n");
376 g_context->_party->setShipHull(99);
377 } else {
378 g_screen->screenMessage("\nHmm...No effect!\n");
379 }
380 }
381
useSkull(int item)382 void Items::useSkull(int item) {
383 /* FIXME: check to see if the abyss must be opened first
384 for the skull to be *able* to be destroyed */
385
386 /* We do the check here instead of in the table, because we need to distinguish between a
387 never-found skull and a destroyed skull. */
388 if (g_ultima->_saveGame->_items & ITEM_SKULL_DESTROYED) {
389 g_screen->screenMessage("\nNone owned!\n");
390 return;
391 }
392
393 /* destroy the skull! pat yourself on the back */
394 if (g_context->_location->_coords.x == 0xe9 && g_context->_location->_coords.y == 0xe9) {
395 g_screen->screenMessage("\n\nYou cast the Skull of Mondain into the Abyss!\n");
396 #ifdef IOS_ULTIMA4
397 U4IOS::testFlightPassCheckPoint("You cast the Skull of Mondain into the Abyss!");
398 #endif
399
400 g_ultima->_saveGame->_items = (g_ultima->_saveGame->_items & ~ITEM_SKULL) | ITEM_SKULL_DESTROYED;
401 g_context->_party->adjustKarma(KA_DESTROYED_SKULL);
402 }
403
404 /* use the skull... bad, very bad */
405 else {
406 g_screen->screenMessage("\n\nYou hold the evil Skull of Mondain the Wizard aloft...\n");
407 #ifdef IOS_ULTIMA4
408 U4IOS::testFlightPassCheckPoint("You hold the evil Skull of Mondain the Wizard aloft...");
409 #endif
410
411 /* destroy all creatures */
412 (*destroyAllCreaturesCallback)();
413
414 /* we don't lose the skull until we toss it into the abyss */
415 //c->saveGame->_items = (c->saveGame->_items & ~ITEM_SKULL);
416 g_context->_party->adjustKarma(KA_USED_SKULL);
417 }
418 }
419
useStone(int item)420 void Items::useStone(int item) {
421 MapCoords coords;
422 byte stone = static_cast<byte>(item);
423
424 static byte truth = STONE_WHITE | STONE_PURPLE | STONE_GREEN | STONE_BLUE;
425 static byte love = STONE_WHITE | STONE_YELLOW | STONE_GREEN | STONE_ORANGE;
426 static byte courage = STONE_WHITE | STONE_RED | STONE_PURPLE | STONE_ORANGE;
427 static byte *attr = nullptr;
428
429 g_context->_location->getCurrentPosition(&coords);
430
431 /**
432 * Named a specific stone (after using "stone" or "stones")
433 */
434 if (item != -1) {
435 CombatMap *cm = getCombatMap();
436
437 if (needStoneNames) {
438 /* named a stone while in a dungeon altar room */
439 if (g_context->_location->_context & CTX_ALTAR_ROOM) {
440 needStoneNames--;
441
442 switch (cm->getAltarRoom()) {
443 case VIRT_TRUTH:
444 attr = &truth;
445 break;
446 case VIRT_LOVE:
447 attr = &love;
448 break;
449 case VIRT_COURAGE:
450 attr = &courage;
451 break;
452 default:
453 break;
454 }
455
456 /* make sure we're in an altar room */
457 if (attr) {
458 /* we need to use the stone, and we haven't used it yet */
459 if ((*attr & stone) && (stone & ~stoneMask))
460 stoneMask |= stone;
461 /* we already used that stone! */
462 else if (stone & stoneMask) {
463 g_screen->screenMessage("\nAlready used!\n");
464 needStoneNames = 0;
465 stoneMask = 0; /* reset the mask so you can try again */
466 return;
467 }
468 } else {
469 error("Not in an altar room!");
470 }
471 /* see if we have all the stones, if not, get more names! */
472 if (attr && needStoneNames) {
473 g_screen->screenMessage("\n%c:", 'E' - needStoneNames);
474 #ifdef IOS_ULTIMA4
475 U4IOS::IOSConversationHelper::setIntroString("Which Color?");
476 #endif
477 itemHandleStones(gameGetInput());
478 }
479 /* all the stones have been entered, verify them! */
480 else {
481 unsigned short key = 0xFFFF;
482 switch (cm->getAltarRoom()) {
483 case VIRT_TRUTH:
484 key = ITEM_KEY_T;
485 break;
486 case VIRT_LOVE:
487 key = ITEM_KEY_L;
488 break;
489 case VIRT_COURAGE:
490 key = ITEM_KEY_C;
491 break;
492 default:
493 break;
494 }
495
496 /* in an altar room, named all of the stones, and don't have the key yet... */
497 if (attr && (stoneMask == *attr) && !(g_ultima->_saveGame->_items & key)) {
498 #ifdef IOS_ULTIMA4
499 Common::String keyName;
500 switch (key) {
501 case ITEM_KEY_C:
502 keyName = "Key Courage";
503 break;
504 case ITEM_KEY_L:
505 keyName = "Key Love";
506 break;
507 case ITEM_KEY_T:
508 keyName = "Key Truth";
509 break;
510 }
511 U4IOS::testFlightPassCheckPoint("Receive a key: " + keyName);
512 #endif
513 g_screen->screenMessage("\nThou doth find one third of the Three Part Key!\n");
514 g_ultima->_saveGame->_items |= key;
515 } else {
516 g_screen->screenMessage("\nHmm...No effect!\n");
517 }
518
519 stoneMask = 0; /* reset the mask so you can try again */
520 }
521 } else {
522 /* Otherwise, we're asking for a stone while in the abyss on top of an altar */
523 /* see if they entered the correct stone */
524 if (stone == (1 << g_context->_location->_coords.z)) {
525 if (g_context->_location->_coords.z < 7) {
526 /* replace the altar with a down-ladder */
527 MapCoords pos;
528 g_screen->screenMessage("\n\nThe altar changes before thyne eyes!\n");
529 g_context->_location->getCurrentPosition(&pos);
530 g_context->_location->_map->_annotations->add(pos, g_context->_location->_map->_tileSet->getByName("down_ladder")->getId());
531 } else {
532 // Start chamber of the codex sequence...
533 g_codex->start();
534 }
535 } else {
536 g_screen->screenMessage("\nHmm...No effect!\n");
537 }
538 }
539 } else {
540 g_screen->screenMessage("\nNot a Usable Item!\n");
541 stoneMask = 0; /* reset the mask so you can try again */
542 }
543 }
544
545 /**
546 * in the abyss, on an altar to place the stones
547 */
548 else if ((g_context->_location->_map->_id == MAP_ABYSS) &&
549 (g_context->_location->_context & CTX_DUNGEON) &&
550 (static_cast<Dungeon *>(g_context->_location->_map)->currentToken() == DUNGEON_ALTAR)) {
551
552 int virtueMask = getBaseVirtues((Virtue)g_context->_location->_coords.z);
553 if (virtueMask > 0)
554 g_screen->screenMessage("\n\nAs thou doth approach, a voice rings out: What virtue dost stem from %s?\n\n", getBaseVirtueName(virtueMask));
555 else
556 g_screen->screenMessage("\n\nA voice rings out: What virtue exists independently of Truth, Love, and Courage?\n\n");
557 #ifdef IOS_ULTIMA4
558 U4IOS::IOSConversationHelper::setIntroString("Which virtue?");
559 #endif
560 Common::String virtue = gameGetInput();
561
562 if (scumm_strnicmp(virtue.c_str(), getVirtueName((Virtue)g_context->_location->_coords.z), 6) == 0) {
563 /* now ask for stone */
564 g_screen->screenMessage("\n\nThe Voice says: Use thy Stone.\n\nColor:\n");
565 needStoneNames = 1;
566 #ifdef IOS_ULTIMA4
567 U4IOS::IOSConversationHelper::setIntroString("Which color?");
568 #endif
569 itemHandleStones(gameGetInput());
570 } else {
571 g_screen->screenMessage("\nHmm...No effect!\n");
572 }
573 }
574
575 /**
576 * in a dungeon altar room, on the altar
577 */
578 else if ((g_context->_location->_context & CTX_ALTAR_ROOM) &&
579 coords.x == 5 && coords.y == 5) {
580 needStoneNames = 4;
581 g_screen->screenMessage("\n\nThere are holes for 4 stones.\nWhat colors:\nA:");
582 #ifdef IOS_ULTIMA4
583 U4IOS::IOSConversationHelper::setIntroString("Which color?");
584 #endif
585 itemHandleStones(gameGetInput());
586 } else {
587 g_screen->screenMessage("\nNo place to Use them!\n");
588 // This used to say "\nNo place to Use them!\nHmm...No effect!\n"
589 // That doesn't match U4DOS; does it match another?
590 }
591 }
592
useKey(int item)593 void Items::useKey(int item) {
594 g_screen->screenMessage("\nNo place to Use them!\n");
595 }
596
isMysticInInventory(int mystic)597 bool Items::isMysticInInventory(int mystic) {
598 /* FIXME: you could feasibly get more mystic weapons and armor if you
599 have 8 party members and equip them all with everything,
600 then search for Mystic Weapons/Armor again
601
602 or, you could just sell them all and search again. What an easy
603 way to make some cash!
604
605 This would be a good candidate for an xu4 "extended" savegame
606 format.
607 */
608 if (mystic == WEAP_MYSTICSWORD)
609 return g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] > 0;
610 else if (mystic == ARMR_MYSTICROBES)
611 return g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] > 0;
612 else
613 error("Invalid mystic item was tested in isMysticInInventory()");
614 return false;
615 }
616
putMysticInInventory(int mystic)617 void Items::putMysticInInventory(int mystic) {
618 g_context->_party->member(0)->awardXp(400);
619 g_context->_party->adjustKarma(KA_FOUND_ITEM);
620 if (mystic == WEAP_MYSTICSWORD)
621 g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] += 8;
622 else if (mystic == ARMR_MYSTICROBES)
623 g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] += 8;
624 else
625 error("Invalid mystic item was added in putMysticInInventory()");
626 g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
627 }
628
isWeaponInInventory(int weapon)629 bool Items::isWeaponInInventory(int weapon) {
630 if (g_ultima->_saveGame->_weapons[weapon])
631 return true;
632 else {
633 for (int i = 0; i < g_context->_party->size(); i++) {
634 if (g_context->_party->member(i)->getWeapon()->getType() == weapon)
635 return true;
636 }
637 }
638 return false;
639 }
640
putWeaponInInventory(int weapon)641 void Items::putWeaponInInventory(int weapon) {
642 g_ultima->_saveGame->_weapons[weapon]++;
643 }
644
useTelescope(int notused)645 void Items::useTelescope(int notused) {
646 g_screen->screenMessage("You see a knob\non the telescope\nmarked A-P\nYou Select:");
647 #ifdef IOS_ULTIMA4
648 U4IOS::IOSConversationChoiceHelper telescopeHelper;
649 telescopeHelper.updateChoices("abcdefghijklmnop ");
650 #endif
651 int choice = AlphaActionController::get('p', "You Select:");
652
653 if (choice == -1)
654 return;
655
656 gamePeerCity(choice, nullptr);
657 }
658
isReagentInInventory(int reag)659 bool Items::isReagentInInventory(int reag) {
660 return false;
661 }
662
putReagentInInventory(int reag)663 void Items::putReagentInInventory(int reag) {
664 g_context->_party->adjustKarma(KA_FOUND_ITEM);
665 g_ultima->_saveGame->_reagents[reag] += xu4_random(8) + 2;
666 g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0;
667
668 if (g_ultima->_saveGame->_reagents[reag] > 99) {
669 g_ultima->_saveGame->_reagents[reag] = 99;
670 g_screen->screenMessage("Dropped some!\n");
671 }
672 }
673
itemConditionsMet(byte conditions)674 bool Items::itemConditionsMet(byte conditions) {
675 int i;
676
677 if ((conditions & SC_NEWMOONS) &&
678 !(g_ultima->_saveGame->_trammelPhase == 0 && g_ultima->_saveGame->_feluccaPhase == 0))
679 return false;
680
681 if (conditions & SC_FULLAVATAR) {
682 for (i = 0; i < VIRT_MAX; i++) {
683 if (g_ultima->_saveGame->_karma[i] != 0)
684 return false;
685 }
686 }
687
688 if ((conditions & SC_REAGENTDELAY) &&
689 (g_ultima->_saveGame->_moves & 0xF0) == g_ultima->_saveGame->_lastReagent)
690 return false;
691
692 return true;
693 }
694
itemAtLocation(const Map * map,const Coords & coords)695 const ItemLocation *Items::itemAtLocation(const Map *map, const Coords &coords) {
696 uint i;
697 for (i = 0; i < N_ITEMS; i++) {
698 if (!ITEMS[i]._locationLabel)
699 continue;
700 if (map->getLabel(ITEMS[i]._locationLabel) == coords &&
701 itemConditionsMet(ITEMS[i]._conditions))
702 return &(ITEMS[i]);
703 }
704 return nullptr;
705 }
706
itemUse(const Common::String & shortName)707 void Items::itemUse(const Common::String &shortName) {
708 uint i;
709 const ItemLocation *item = nullptr;
710
711 for (i = 0; i < N_ITEMS; i++) {
712 if (ITEMS[i]._shortName &&
713 scumm_stricmp(ITEMS[i]._shortName, shortName.c_str()) == 0) {
714
715 item = &ITEMS[i];
716
717 /* item name found, see if we have that item in our inventory */
718 if (!ITEMS[i]._isItemInInventory || (this->*(ITEMS[i]._isItemInInventory))(ITEMS[i]._data)) {
719
720 /* use the item, if we can! */
721 if (!item || !item->_useItem)
722 g_screen->screenMessage("\nNot a Usable item!\n");
723 else
724 (this->*(item->_useItem))(ITEMS[i]._data);
725 } else
726 g_screen->screenMessage("\nNone owned!\n");
727
728 /* we found the item, no need to keep searching */
729 break;
730 }
731 }
732
733 /* item was not found */
734 if (!item)
735 g_screen->screenMessage("\nNot a Usable item!\n");
736 }
737
itemHandleStones(const Common::String & color)738 void Items::itemHandleStones(const Common::String &color) {
739 bool found = false;
740
741 for (int i = 0; i < 8; i++) {
742 if (scumm_stricmp(color.c_str(), getStoneName((Virtue)i)) == 0 &&
743 isStoneInInventory(1 << i)) {
744 found = true;
745 itemUse(color.c_str());
746 }
747 }
748
749 if (!found) {
750 g_screen->screenMessage("\nNone owned!\n");
751 stoneMask = 0; /* make sure stone mask is reset */
752 }
753 }
754
isAbyssOpened(const Portal * p)755 bool Items::isAbyssOpened(const Portal *p) {
756 /* make sure the bell, book and candle have all been used */
757 int items = g_ultima->_saveGame->_items;
758 int isopened = (items & ITEM_BELL_USED) && (items & ITEM_BOOK_USED) && (items & ITEM_CANDLE_USED);
759
760 if (!isopened)
761 g_screen->screenMessage("Enter Can't!\n");
762 return isopened;
763 }
764
765 } // End of namespace Ultima4
766 } // End of namespace Ultima
767