/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ultima/ultima4/game/item.h" #include "ultima/ultima4/game/codex.h" #include "ultima/ultima4/game/game.h" #include "ultima/ultima4/game/names.h" #include "ultima/ultima4/game/player.h" #include "ultima/ultima4/game/portal.h" #include "ultima/ultima4/game/context.h" #include "ultima/ultima4/game/weapon.h" #include "ultima/ultima4/controllers/alpha_action_controller.h" #include "ultima/ultima4/controllers/combat_controller.h" #include "ultima/ultima4/core/utils.h" #include "ultima/ultima4/filesys/savegame.h" #include "ultima/ultima4/gfx/screen.h" #include "ultima/ultima4/map/annotation.h" #include "ultima/ultima4/map/dungeon.h" #include "ultima/ultima4/map/location.h" #include "ultima/ultima4/map/map.h" #include "ultima/ultima4/map/mapmgr.h" #include "ultima/ultima4/map/tileset.h" #include "ultima/ultima4/ultima4.h" namespace Ultima { namespace Ultima4 { Items *g_items; const ItemLocation Items::ITEMS[N_ITEMS] = { { "Mandrake Root", nullptr, "mandrake1", &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY }, { "Mandrake Root", nullptr, "mandrake2", &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_MANDRAKE, SC_NEWMOONS | SC_REAGENTDELAY }, { "Nightshade", nullptr, "nightshade1", &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY }, { "Nightshade", nullptr, "nightshade2", &Items::isReagentInInventory, &Items::putReagentInInventory, nullptr, REAG_NIGHTSHADE, SC_NEWMOONS | SC_REAGENTDELAY }, { "the Bell of Courage", "bell", "bell", &Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BELL, 0 }, { "the Book of Truth", "book", "book", &Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_BOOK, 0 }, { "the Candle of Love", "candle", "candle", &Items::isItemInInventory, &Items::putItemInInventory, &Items::useBBC, ITEM_CANDLE, 0 }, { "A Silver Horn", "horn", "horn", &Items::isItemInInventory, &Items::putItemInInventory, &Items::useHorn, ITEM_HORN, 0 }, { "the Wheel from the H.M.S. Cape", "wheel", "wheel", &Items::isItemInInventory, &Items::putItemInInventory, &Items::useWheel, ITEM_WHEEL, 0 }, { "the Skull of Modain the Wizard", "skull", "skull", &Items::isSkullInInventory, &Items::putItemInInventory, &Items::useSkull, ITEM_SKULL, SC_NEWMOONS }, { "the Red Stone", "red", "redstone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_RED, 0 }, { "the Orange Stone", "orange", "orangestone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_ORANGE, 0 }, { "the Yellow Stone", "yellow", "yellowstone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_YELLOW, 0 }, { "the Green Stone", "green", "greenstone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_GREEN, 0 }, { "the Blue Stone", "blue", "bluestone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLUE, 0 }, { "the Purple Stone", "purple", "purplestone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_PURPLE, 0 }, { "the Black Stone", "black", "blackstone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_BLACK, SC_NEWMOONS }, { "the White Stone", "white", "whitestone", &Items::isStoneInInventory, &Items::putStoneInInventory, &Items::useStone, STONE_WHITE, 0 }, /* handlers for using generic objects */ { nullptr, "stone", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 }, { nullptr, "stones", nullptr, &Items::isStoneInInventory, nullptr, &Items::useStone, -1, 0 }, { nullptr, "key", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 }, { nullptr, "keys", nullptr, &Items::isItemInInventory, nullptr, &Items::useKey, (ITEM_KEY_C | ITEM_KEY_L | ITEM_KEY_T), 0 }, /* Lycaeum telescope */ { nullptr, nullptr, "telescope", nullptr, &Items::useTelescope, nullptr, 0, 0 }, { "Mystic Armor", nullptr, "mysticarmor", &Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, ARMR_MYSTICROBES, SC_FULLAVATAR }, { "Mystic Swords", nullptr, "mysticswords", &Items::isMysticInInventory, &Items::putMysticInInventory, nullptr, WEAP_MYSTICSWORD, SC_FULLAVATAR }, { "the sulfury remains of an ancient Sosarian Laser Gun. It turns to ash in your fingers", nullptr, "lasergun", // lol, where'd that come from? //Looks like someone was experimenting with "maps.xml". It effectively increments sulfur ash by one due to '16' being an invalid weapon index. &Items::isWeaponInInventory, &Items::putWeaponInInventory, nullptr, 16, 0 }, { "the rune of Honesty", nullptr, "honestyrune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONESTY, 0 }, { "the rune of Compassion", nullptr, "compassionrune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_COMPASSION, 0 }, { "the rune of Valor", nullptr, "valorrune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_VALOR, 0 }, { "the rune of Justice", nullptr, "justicerune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_JUSTICE, 0 }, { "the rune of Sacrifice", nullptr, "sacrificerune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SACRIFICE, 0 }, { "the rune of Honor", nullptr, "honorrune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HONOR, 0 }, { "the rune of Spirituality", nullptr, "spiritualityrune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_SPIRITUALITY, 0 }, { "the rune of Humility", nullptr, "humilityrune", &Items::isRuneInInventory, &Items::putRuneInInventory, nullptr, RUNE_HUMILITY, 0 } }; Items::Items() : destroyAllCreaturesCallback(nullptr), needStoneNames(0), stoneMask(0) { g_items = this; } Items::~Items() { g_items = nullptr; } void Items::setDestroyAllCreaturesCallback(DestroyAllCreaturesCallback callback) { destroyAllCreaturesCallback = callback; } bool Items::isRuneInInventory(int virt) { return g_ultima->_saveGame->_runes & virt; } void Items::putRuneInInventory(int virt) { g_context->_party->member(0)->awardXp(100); g_context->_party->adjustKarma(KA_FOUND_ITEM); g_ultima->_saveGame->_runes |= virt; #ifdef IOS_ULTIMA4 Common::String virtueName; switch (virt) { default: case RUNE_HONESTY: virtueName = "Honesty"; break; case RUNE_HONOR: virtueName = "Honor"; break; case RUNE_HUMILITY: virtueName = "Humility"; break; case RUNE_JUSTICE: virtueName = "Justice"; break; case RUNE_SACRIFICE: virtueName = "Sacrifice"; break; case RUNE_SPIRITUALITY: virtueName = "Spirituality"; break; case RUNE_VALOR: virtueName = "Valor"; break; case RUNE_COMPASSION: virtueName = "Compassion"; break; } U4IOS::testFlightPassCheckPoint("Player got stone: " + virtueName); #endif g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0; } bool Items::isStoneInInventory(int virt) { /* generic test: does the party have any stones yet? */ if (virt == -1) return (g_ultima->_saveGame->_stones > 0); /* specific test: does the party have a specific stone? */ else return g_ultima->_saveGame->_stones & virt; } void Items::putStoneInInventory(int virt) { g_context->_party->member(0)->awardXp(200); g_context->_party->adjustKarma(KA_FOUND_ITEM); g_ultima->_saveGame->_stones |= virt; #ifdef IOS_ULTIMA4 Common::String stoneName; switch (virt) { default: case STONE_BLACK: stoneName = "Black"; break; case STONE_BLUE: stoneName = "Blue"; break; case STONE_GREEN: stoneName = "Green"; break; case STONE_ORANGE: stoneName = "Orange"; break; case STONE_PURPLE: stoneName = "Purple"; break; case STONE_RED: stoneName = "Red"; break; case STONE_WHITE: stoneName = "White"; break; case STONE_YELLOW: stoneName = "Yellow"; break; } U4IOS::testFlightPassCheckPoint("Player got rune: " + stoneName); #endif g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0; } bool Items::isItemInInventory(int item) { return g_ultima->_saveGame->_items & item; } bool Items::isSkullInInventory(int unused) { return (g_ultima->_saveGame->_items & (ITEM_SKULL | ITEM_SKULL_DESTROYED)); } void Items::putItemInInventory(int item) { g_context->_party->member(0)->awardXp(400); g_context->_party->adjustKarma(KA_FOUND_ITEM); g_ultima->_saveGame->_items |= item; #ifdef IOS_ULTIMA4 Common::String itemName; switch (item) { default: case ITEM_BELL: itemName = "Bell"; break; case ITEM_BOOK: itemName = "Book"; break; case ITEM_CANDLE: itemName = "Candle"; break; case ITEM_HORN: itemName = "Horn"; break; case ITEM_KEY_C: itemName = "Key Courage"; break; case ITEM_KEY_L: itemName = "Key Love"; break; case ITEM_KEY_T: itemName = "Key Truth"; break; case ITEM_SKULL: itemName = "Skull"; break; case ITEM_WHEEL: itemName = "Wheel"; break; } U4IOS::testFlightPassCheckPoint("Player got rune: " + itemName); #endif g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0; } void Items::useBBC(int item) { Coords abyssEntrance(0xe9, 0xe9); /* on top of the Abyss entrance */ if (g_context->_location->_coords == abyssEntrance) { /* must use bell first */ if (item == ITEM_BELL) { #ifdef IOS_ULTIMA4 U4IOS::testFlightPassCheckPoint("The Bell rings on and on!"); #endif g_screen->screenMessage("\nThe Bell rings on and on!\n"); g_ultima->_saveGame->_items |= ITEM_BELL_USED; } /* then the book */ else if ((item == ITEM_BOOK) && (g_ultima->_saveGame->_items & ITEM_BELL_USED)) { #ifdef IOS_ULTIMA4 U4IOS::testFlightPassCheckPoint("The words resonate with the ringing!"); #endif g_screen->screenMessage("\nThe words resonate with the ringing!\n"); g_ultima->_saveGame->_items |= ITEM_BOOK_USED; } /* then the candle */ else if ((item == ITEM_CANDLE) && (g_ultima->_saveGame->_items & ITEM_BOOK_USED)) { g_screen->screenMessage("\nAs you light the Candle the Earth Trembles!\n"); #ifdef IOS_ULTIMA4 U4IOS::testFlightPassCheckPoint("As you light the Candle the Earth Trembles!"); #endif g_ultima->_saveGame->_items |= ITEM_CANDLE_USED; } else { g_screen->screenMessage("\nHmm...No effect!\n"); } } /* somewhere else */ else { g_screen->screenMessage("\nHmm...No effect!\n"); } } void Items::useHorn(int item) { g_screen->screenMessage("\nThe Horn sounds an eerie tone!\n"); g_context->_aura->set(Aura::HORN, 10); } void Items::useWheel(int item) { if ((g_context->_transportContext == TRANSPORT_SHIP) && (g_ultima->_saveGame->_shipHull == 50)) { g_screen->screenMessage("\nOnce mounted, the Wheel glows with a blue light!\n"); g_context->_party->setShipHull(99); } else { g_screen->screenMessage("\nHmm...No effect!\n"); } } void Items::useSkull(int item) { /* FIXME: check to see if the abyss must be opened first for the skull to be *able* to be destroyed */ /* We do the check here instead of in the table, because we need to distinguish between a never-found skull and a destroyed skull. */ if (g_ultima->_saveGame->_items & ITEM_SKULL_DESTROYED) { g_screen->screenMessage("\nNone owned!\n"); return; } /* destroy the skull! pat yourself on the back */ if (g_context->_location->_coords.x == 0xe9 && g_context->_location->_coords.y == 0xe9) { g_screen->screenMessage("\n\nYou cast the Skull of Mondain into the Abyss!\n"); #ifdef IOS_ULTIMA4 U4IOS::testFlightPassCheckPoint("You cast the Skull of Mondain into the Abyss!"); #endif g_ultima->_saveGame->_items = (g_ultima->_saveGame->_items & ~ITEM_SKULL) | ITEM_SKULL_DESTROYED; g_context->_party->adjustKarma(KA_DESTROYED_SKULL); } /* use the skull... bad, very bad */ else { g_screen->screenMessage("\n\nYou hold the evil Skull of Mondain the Wizard aloft...\n"); #ifdef IOS_ULTIMA4 U4IOS::testFlightPassCheckPoint("You hold the evil Skull of Mondain the Wizard aloft..."); #endif /* destroy all creatures */ (*destroyAllCreaturesCallback)(); /* we don't lose the skull until we toss it into the abyss */ //c->saveGame->_items = (c->saveGame->_items & ~ITEM_SKULL); g_context->_party->adjustKarma(KA_USED_SKULL); } } void Items::useStone(int item) { MapCoords coords; byte stone = static_cast(item); static const byte truth = STONE_WHITE | STONE_PURPLE | STONE_GREEN | STONE_BLUE; static const byte love = STONE_WHITE | STONE_YELLOW | STONE_GREEN | STONE_ORANGE; static const byte courage = STONE_WHITE | STONE_RED | STONE_PURPLE | STONE_ORANGE; static const byte *attr = nullptr; g_context->_location->getCurrentPosition(&coords); /** * Named a specific stone (after using "stone" or "stones") */ if (item != -1) { CombatMap *cm = getCombatMap(); if (needStoneNames) { /* named a stone while in a dungeon altar room */ if (g_context->_location->_context & CTX_ALTAR_ROOM) { needStoneNames--; switch (cm->getAltarRoom()) { case VIRT_TRUTH: attr = &truth; break; case VIRT_LOVE: attr = &love; break; case VIRT_COURAGE: attr = &courage; break; default: break; } /* make sure we're in an altar room */ if (attr) { /* we need to use the stone, and we haven't used it yet */ if ((*attr & stone) && (stone & ~stoneMask)) stoneMask |= stone; /* we already used that stone! */ else if (stone & stoneMask) { g_screen->screenMessage("\nAlready used!\n"); needStoneNames = 0; stoneMask = 0; /* reset the mask so you can try again */ return; } } else { error("Not in an altar room!"); } /* see if we have all the stones, if not, get more names! */ if (attr && needStoneNames) { g_screen->screenMessage("\n%c:", 'E' - needStoneNames); #ifdef IOS_ULTIMA4 U4IOS::IOSConversationHelper::setIntroString("Which Color?"); #endif itemHandleStones(gameGetInput()); } /* all the stones have been entered, verify them! */ else { unsigned short key = 0xFFFF; switch (cm->getAltarRoom()) { case VIRT_TRUTH: key = ITEM_KEY_T; break; case VIRT_LOVE: key = ITEM_KEY_L; break; case VIRT_COURAGE: key = ITEM_KEY_C; break; default: break; } /* in an altar room, named all of the stones, and don't have the key yet... */ if (attr && (stoneMask == *attr) && !(g_ultima->_saveGame->_items & key)) { #ifdef IOS_ULTIMA4 Common::String keyName; switch (key) { case ITEM_KEY_C: keyName = "Key Courage"; break; case ITEM_KEY_L: keyName = "Key Love"; break; case ITEM_KEY_T: keyName = "Key Truth"; break; } U4IOS::testFlightPassCheckPoint("Receive a key: " + keyName); #endif g_screen->screenMessage("\nThou doth find one third of the Three Part Key!\n"); g_ultima->_saveGame->_items |= key; } else { g_screen->screenMessage("\nHmm...No effect!\n"); } stoneMask = 0; /* reset the mask so you can try again */ } } else { /* Otherwise, we're asking for a stone while in the abyss on top of an altar */ /* see if they entered the correct stone */ if (stone == (1 << g_context->_location->_coords.z)) { if (g_context->_location->_coords.z < 7) { /* replace the altar with a down-ladder */ MapCoords pos; g_screen->screenMessage("\n\nThe altar changes before thyne eyes!\n"); g_context->_location->getCurrentPosition(&pos); g_context->_location->_map->_annotations->add(pos, g_context->_location->_map->_tileSet->getByName("down_ladder")->getId()); } else { // Start chamber of the codex sequence... g_codex->start(); } } else { g_screen->screenMessage("\nHmm...No effect!\n"); } } } else { g_screen->screenMessage("\nNot a Usable Item!\n"); stoneMask = 0; /* reset the mask so you can try again */ } } /** * in the abyss, on an altar to place the stones */ else if ((g_context->_location->_map->_id == MAP_ABYSS) && (g_context->_location->_context & CTX_DUNGEON) && (static_cast(g_context->_location->_map)->currentToken() == DUNGEON_ALTAR)) { int virtueMask = getBaseVirtues((Virtue)g_context->_location->_coords.z); if (virtueMask > 0) g_screen->screenMessage("\n\nAs thou doth approach, a voice rings out: What virtue dost stem from %s?\n\n", getBaseVirtueName(virtueMask)); else g_screen->screenMessage("\n\nA voice rings out: What virtue exists independently of Truth, Love, and Courage?\n\n"); #ifdef IOS_ULTIMA4 U4IOS::IOSConversationHelper::setIntroString("Which virtue?"); #endif Common::String virtue = gameGetInput(); if (scumm_strnicmp(virtue.c_str(), getVirtueName((Virtue)g_context->_location->_coords.z), 6) == 0) { /* now ask for stone */ g_screen->screenMessage("\n\nThe Voice says: Use thy Stone.\n\nColor:\n"); needStoneNames = 1; #ifdef IOS_ULTIMA4 U4IOS::IOSConversationHelper::setIntroString("Which color?"); #endif itemHandleStones(gameGetInput()); } else { g_screen->screenMessage("\nHmm...No effect!\n"); } } /** * in a dungeon altar room, on the altar */ else if ((g_context->_location->_context & CTX_ALTAR_ROOM) && coords.x == 5 && coords.y == 5) { needStoneNames = 4; g_screen->screenMessage("\n\nThere are holes for 4 stones.\nWhat colors:\nA:"); #ifdef IOS_ULTIMA4 U4IOS::IOSConversationHelper::setIntroString("Which color?"); #endif itemHandleStones(gameGetInput()); } else { g_screen->screenMessage("\nNo place to Use them!\n"); // This used to say "\nNo place to Use them!\nHmm...No effect!\n" // That doesn't match U4DOS; does it match another? } } void Items::useKey(int item) { g_screen->screenMessage("\nNo place to Use them!\n"); } bool Items::isMysticInInventory(int mystic) { /* FIXME: you could feasibly get more mystic weapons and armor if you have 8 party members and equip them all with everything, then search for Mystic Weapons/Armor again or, you could just sell them all and search again. What an easy way to make some cash! This would be a good candidate for an xu4 "extended" savegame format. */ if (mystic == WEAP_MYSTICSWORD) return g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] > 0; else if (mystic == ARMR_MYSTICROBES) return g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] > 0; else error("Invalid mystic item was tested in isMysticInInventory()"); return false; } void Items::putMysticInInventory(int mystic) { g_context->_party->member(0)->awardXp(400); g_context->_party->adjustKarma(KA_FOUND_ITEM); if (mystic == WEAP_MYSTICSWORD) g_ultima->_saveGame->_weapons[WEAP_MYSTICSWORD] += 8; else if (mystic == ARMR_MYSTICROBES) g_ultima->_saveGame->_armor[ARMR_MYSTICROBES] += 8; else error("Invalid mystic item was added in putMysticInInventory()"); g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0; } bool Items::isWeaponInInventory(int weapon) { if (g_ultima->_saveGame->_weapons[weapon]) return true; else { for (int i = 0; i < g_context->_party->size(); i++) { if (g_context->_party->member(i)->getWeapon()->getType() == weapon) return true; } } return false; } void Items::putWeaponInInventory(int weapon) { g_ultima->_saveGame->_weapons[weapon]++; } void Items::useTelescope(int notused) { g_screen->screenMessage("You see a knob\non the telescope\nmarked A-P\nYou Select:"); #ifdef IOS_ULTIMA4 U4IOS::IOSConversationChoiceHelper telescopeHelper; telescopeHelper.updateChoices("abcdefghijklmnop "); #endif int choice = AlphaActionController::get('p', "You Select:"); if (choice == -1) return; gamePeerCity(choice, nullptr); } bool Items::isReagentInInventory(int reag) { return false; } void Items::putReagentInInventory(int reag) { g_context->_party->adjustKarma(KA_FOUND_ITEM); g_ultima->_saveGame->_reagents[reag] += xu4_random(8) + 2; g_ultima->_saveGame->_lastReagent = g_ultima->_saveGame->_moves & 0xF0; if (g_ultima->_saveGame->_reagents[reag] > 99) { g_ultima->_saveGame->_reagents[reag] = 99; g_screen->screenMessage("Dropped some!\n"); } } bool Items::itemConditionsMet(byte conditions) { if ((conditions & SC_NEWMOONS) && !(g_ultima->_saveGame->_trammelPhase == 0 && g_ultima->_saveGame->_feluccaPhase == 0)) return false; if (conditions & SC_FULLAVATAR) { for (int i = 0; i < VIRT_MAX; i++) { if (g_ultima->_saveGame->_karma[i] != 0) return false; } } if ((conditions & SC_REAGENTDELAY) && (g_ultima->_saveGame->_moves & 0xF0) == g_ultima->_saveGame->_lastReagent) return false; return true; } const ItemLocation *Items::itemAtLocation(const Map *map, const Coords &coords) { for (uint i = 0; i < N_ITEMS; i++) { if (!ITEMS[i]._locationLabel) continue; if (map->getLabel(ITEMS[i]._locationLabel) == coords && itemConditionsMet(ITEMS[i]._conditions)) return &(ITEMS[i]); } return nullptr; } void Items::itemUse(const Common::String &shortName) { const ItemLocation *item = nullptr; for (uint i = 0; i < N_ITEMS; i++) { if (ITEMS[i]._shortName && scumm_stricmp(ITEMS[i]._shortName, shortName.c_str()) == 0) { item = &ITEMS[i]; /* item name found, see if we have that item in our inventory */ if (!ITEMS[i]._isItemInInventory || (this->*(ITEMS[i]._isItemInInventory))(ITEMS[i]._data)) { /* use the item, if we can! */ if (!item || !item->_useItem) g_screen->screenMessage("\nNot a Usable item!\n"); else (this->*(item->_useItem))(ITEMS[i]._data); } else g_screen->screenMessage("\nNone owned!\n"); /* we found the item, no need to keep searching */ break; } } /* item was not found */ if (!item) g_screen->screenMessage("\nNot a Usable item!\n"); } void Items::itemHandleStones(const Common::String &color) { bool found = false; for (int i = 0; i < 8; i++) { if (scumm_stricmp(color.c_str(), getStoneName((Virtue)i)) == 0 && isStoneInInventory(1 << i)) { found = true; itemUse(color.c_str()); } } if (!found) { g_screen->screenMessage("\nNone owned!\n"); stoneMask = 0; /* make sure stone mask is reset */ } } bool Items::isAbyssOpened(const Portal *p) { /* make sure the bell, book and candle have all been used */ int items = g_ultima->_saveGame->_items; int isopened = (items & ITEM_BELL_USED) && (items & ITEM_BOOK_USED) && (items & ITEM_CANDLE_USED); if (!isopened) g_screen->screenMessage("Enter Can't!\n"); return isopened; } } // End of namespace Ultima4 } // End of namespace Ultima