diff --git a/sku.0/sys.server/compiled/game/datatables/collection/use_object_on_object.tab b/sku.0/sys.server/compiled/game/datatables/collection/use_object_on_object.tab index d9a3805f3..a6fb95043 100755 --- a/sku.0/sys.server/compiled/game/datatables/collection/use_object_on_object.tab +++ b/sku.0/sys.server/compiled/game/datatables/collection/use_object_on_object.tab @@ -1,3 +1,3 @@ -objectKey collectionName collectonSlot handleTarget questRequired questName taskName -s[unique] s s s i[0] s s -nym_themepark_miner_dna nyms_miner_dna "icon_nyms_miner_dna_01,icon_nyms_miner_dna_02,icon_nyms_miner_dna_03,icon_nyms_miner_dna_04,icon_nyms_miner_dna_05,icon_nyms_miner_dna_06,icon_nyms_miner_dna_07,icon_nyms_miner_dna_08,icon_nyms_miner_dna_09,icon_nyms_miner_dna_10" incapaciteSelf 1 u16_nym_themepark_miner_dna_collection extractMinerDNA +objectKey collectionName handleTarget questRequired questName taskName targetContainedScript targetCreature targetEffect +s[unique] s s i[0] s s s s s +nym_themepark_miner_dna nyms_miner_dna incapacitateSelf 1 u16_nym_themepark_miner_dna_collection extractMinerDNA theme_park.nym.mining_cave u13_nym_enraged_miner appearance/pt_heal_prof_medic_1.prt diff --git a/sku.0/sys.server/compiled/game/script/player/base/base_player.java b/sku.0/sys.server/compiled/game/script/player/base/base_player.java index 6e0bbe096..a42b84284 100755 --- a/sku.0/sys.server/compiled/game/script/player/base/base_player.java +++ b/sku.0/sys.server/compiled/game/script/player/base/base_player.java @@ -1640,10 +1640,6 @@ public class base_player extends script.base_script outparams.put("player", self); messageTo(cityHall, "msgCheckMyCityMotd", outparams, 0, false); } - if(groundquests.hasCompletedQuest(self, "u16_nym_themepark_miner_dna_collection") && utils.playerHasStaticItemInBankOrInventory(self, "item_nym_themepark_dna_extractor")) { - obj_id dna_extractor = utils.getStaticItemInBankOrInventory(self, "item_nym_themepark_dna_extractor"); - if(isValidId(dna_extractor)) utils.destroyObject(dna_extractor); - } return SCRIPT_CONTINUE; } public int disconnectPlayerCtsCompletedOrInProgress(obj_id self, dictionary params) throws InterruptedException diff --git a/sku.0/sys.server/compiled/game/script/player/player_collection.java b/sku.0/sys.server/compiled/game/script/player/player_collection.java index c3a835edd..f91df51bd 100755 --- a/sku.0/sys.server/compiled/game/script/player/player_collection.java +++ b/sku.0/sys.server/compiled/game/script/player/player_collection.java @@ -24,6 +24,7 @@ public class player_collection extends script.base_script public static final string_id SID_INTERRUPTED_DAMAGED = new string_id("quest/groundquests", "countdown_interrupted_damaged"); public static final string_id SID_INVIS_COLLECTION_FAIL = new string_id("collection", "invis_collection_failed"); public static final int GCW_INSURGENT_LOCKOUT = 3600; + public int OnCollectionSlotModified(obj_id self, String bookName, String pageName, String collectionName, String slotName, boolean isCounterTypeSlot, int previousValue, int currentValue, int maxSlotValue, boolean slotCompleted) throws InterruptedException { if (bookName.equals(badge.BADGE_BOOK)) @@ -133,8 +134,30 @@ public class player_collection extends script.base_script } } } + + // remove collection-based items from inventory as applicable + final obj_id[] items = getContents(utils.getInventoryContainer(self)); + for(obj_id item : items) + { + if(hasScript(item, "systems.collections.collection_use_object_on_object")) + { + final String objectKey = getStringObjVar(item, "quest.object_key"); + if(objectKey != null && objectKey.length() > 0) + { + final dictionary d = dataTableGetRow("datatables/collection/use_object_on_object.iff", objectKey); + if(d != null && d.size() > 0 && d.containsKey("collectionName")) + { + if(hasCompletedCollection(self, d.getString("collectionName"))) + { + messageTo(item, "onRequestDestroy", null, 1f, false); + } + } + } + } + } return SCRIPT_CONTINUE; } + public int OnCollectionServerFirst(obj_id self, String bookName, String pageName, String collectionName) throws InterruptedException { prose_package pp = new prose_package(); diff --git a/sku.0/sys.server/compiled/game/script/systems/collections/collection_handle_object_on_object.java b/sku.0/sys.server/compiled/game/script/systems/collections/collection_handle_object_on_object.java index 3fe5dcedc..7bc0625ed 100755 --- a/sku.0/sys.server/compiled/game/script/systems/collections/collection_handle_object_on_object.java +++ b/sku.0/sys.server/compiled/game/script/systems/collections/collection_handle_object_on_object.java @@ -8,7 +8,7 @@ public class collection_handle_object_on_object extends script.base_script public collection_handle_object_on_object() { } - public int incapaciteSelf(obj_id self, dictionary params) throws InterruptedException + public int incapacitateSelf(obj_id self, dictionary params) throws InterruptedException { setPosture(self, POSTURE_INCAPACITATED); return SCRIPT_CONTINUE; diff --git a/sku.0/sys.server/compiled/game/script/systems/collections/collection_use_object_on_object.java b/sku.0/sys.server/compiled/game/script/systems/collections/collection_use_object_on_object.java index 9c3ea2977..74abb8afb 100755 --- a/sku.0/sys.server/compiled/game/script/systems/collections/collection_use_object_on_object.java +++ b/sku.0/sys.server/compiled/game/script/systems/collections/collection_use_object_on_object.java @@ -9,196 +9,274 @@ public class collection_use_object_on_object extends script.base_script public collection_use_object_on_object() { } - public static final string_id SID_INVALID_TARGET = new string_id("collection", "invalid_target"); - public static final string_id NOT_WHILE_INCAPPED = new string_id("quest/ground/util/quest_giver_object", "not_while_incapped"); - public static final string_id MUST_DISMOUNT = new string_id("collection", "must_dismount"); - public static final string_id CREATURE_IS_DEAD = new string_id("collection", "creature_is_dead"); - public static final string_id OBJECT_DESTROYED = new string_id("collection", "object_destroyed"); - public static final String OBJECT_KEY = "quest.object_key"; - public static final String COLLECTION_OBJECT_TABLE = "datatables/collection/use_object_on_object.iff"; - public int OnObjectMenuRequest(obj_id self, obj_id player, menu_info mi) throws InterruptedException + + public static final string_id SID_INVALID_TARGET_MSG = new string_id("collection", "invalid_target"); + public static final string_id SID_INVALID_TARGET_FLY = new string_id("spam", "bad_target"); + public static final string_id SID_NOT_WHILE_INCAPACITATED_OR_DEAD = new string_id("spam", "while_dead"); + public static final string_id SID_MUST_DISMOUNT = new string_id("spam", "mounted"); + public static final string_id SID_CREATURE_IS_DEAD = new string_id("collection", "creature_is_dead"); + public static final string_id SID_OBJECT_DESTROYED = new string_id("collection", "object_destroyed"); + public static final string_id SID_NO_MOVE = new string_id("spam", "no_move_item"); + public static final string_id SID_TOO_FAR = new string_id("spam", "too_far"); + public static final string_id SID_CANT_SEE = new string_id("combat_effects", "cant_see"); + + public static final String VAR_OBJECT_KEY = "quest.object_key"; + public static final String TABLE_COLLECTION_OBJECT = "datatables/collection/use_object_on_object.iff"; + + public int OnInitialize(obj_id self) throws InterruptedException { - if (!canManipulate(player, self, true, true, 15, true)) + messageTo(self, "setupVars", null, 3f, false); + return SCRIPT_CONTINUE; + } + + // just in case it isn't added by static item, we shouldn't be able to trade this + public int OnAttach(obj_id self) throws InterruptedException + { + if(!hasObjVar(self, "noTrade")) { - return SCRIPT_CONTINUE; - } - if (!utils.isNestedWithinAPlayer(self)) - { - return SCRIPT_CONTINUE; - } - menu_info_data mid = mi.getMenuItemByType(menu_info_types.ITEM_USE); - if (mid != null) - { - mid.setServerNotify(true); + setObjVar(self, "noTrade", true); } return SCRIPT_CONTINUE; } + + // keep in inventory, and don't allow placement into volume containers inside inventory + // so we don't have to recursively search for this later on collection completion + public int OnAboutToBeTransferred(obj_id self, obj_id destContainer, obj_id transferer) throws InterruptedException + { + if(isIdValid(transferer) && isPlayerConnected(transferer)) + { + sendSystemMessage(transferer, SID_NO_MOVE); + } + return SCRIPT_OVERRIDE; + } + + public int OnObjectMenuRequest(obj_id self, obj_id player, menu_info mi) throws InterruptedException + { + if(isObjectInvalidInContext(self)) + { + dictionary d = new dictionary(); + d.put("player", player); + messageTo(self, "onRequestDestroy", d, 0f, false); + } + return SCRIPT_CONTINUE; + } + public int OnObjectMenuSelect(obj_id self, obj_id player, int item) throws InterruptedException { - sendDirtyObjectMenuNotification(self); - if (utils.getContainingPlayer(self) != player) - { - return SCRIPT_CONTINUE; - } if (item != menu_info_types.ITEM_USE) { return SCRIPT_CONTINUE; } if (isIncapacitated(player) || isDead(player)) { - sendSystemMessage(player, new string_id("player_structure", "while_dead")); + sendSystemMessage(player, SID_NOT_WHILE_INCAPACITATED_OR_DEAD); return SCRIPT_CONTINUE; } - if (getState(player, STATE_RIDING_MOUNT) == 1) + if (getState(player, STATE_RIDING_MOUNT) == 1 || isIdValid(getMountId(player))) { - sendSystemMessage(player, MUST_DISMOUNT); + sendSystemMessage(player, SID_MUST_DISMOUNT); return SCRIPT_CONTINUE; } - if (isDead(player) || isIncapacitated(player)) - { - sendSystemMessage(player, NOT_WHILE_INCAPPED); - return SCRIPT_CONTINUE; - } - obj_id intended = getIntendedTarget(player); + final obj_id intended = getLookAtTarget(player); if (!isIdValid(intended)) { - sendSystemMessage(player, SID_INVALID_TARGET); - } - else - { - setObjVar(self, "owner", player); - checkValidTarget(player, intended, self); + sendSystemMessage(player, SID_INVALID_TARGET_MSG); + return SCRIPT_CONTINUE; } + handleTarget(player, intended, self); return SCRIPT_CONTINUE; } - public boolean checkValidTarget(obj_id player, obj_id target, obj_id self) throws InterruptedException + + public static void handleTarget(obj_id player, obj_id target, obj_id self) throws InterruptedException { - if (!isValidId(player) || !exists(player)) - { - return false; - } - if (!isValidId(target) || !exists(target)) - { - return false; - } - if (!isValidId(self) || !exists(self)) - { - return false; - } - if (!hasObjVar(self, OBJECT_KEY)) - { - if (hasScript(self, "item.special.nodestroy")) - { - detachScript(self, "item.special.nodestroy"); - } - messageTo(self, "handleDestroy", null, 0, false); - return false; - } - String keyString = getStringObjVar(self, OBJECT_KEY); - if (keyString == null || keyString.length() <= 0) - { - if (hasScript(self, "item.special.nodestroy")) - { - detachScript(self, "item.special.nodestroy"); - } - messageTo(self, "handleDestroy", null, 0, false); - return false; - } - int tableRow = dataTableSearchColumnForString(keyString, "objectKey", COLLECTION_OBJECT_TABLE); - if (tableRow < 0) - { - return false; - } - dictionary collectionDataDict = dataTableGetRow(COLLECTION_OBJECT_TABLE, tableRow); - if (collectionDataDict == null) - { - return false; - } - String collectionName = collectionDataDict.getString("collectionName"); - if (collectionName == null || collectionName.length() <= 0) - { - return false; - } - int questRequired = collectionDataDict.getInt("questRequired"); - String questName = collectionDataDict.getString("questName"); - String taskName = collectionDataDict.getString("taskName"); - if (questRequired > 0 && ((questName == null || questName.length() <= 0) || (taskName == null || taskName.length() <= 0))) - { - return false; - } - if (hasCompletedCollection(player, collectionName)) - { - messageTo(self, "handleDestroy", null, 0, false); - return false; - } - if (hasCompletedCollection(player, collectionName)) - { - if (hasScript(self, "item.special.nodestroy")) - { - detachScript(self, "item.special.nodestroy"); - } - messageTo(self, "handleDestroy", null, 0, false); - return false; - } - if (questRequired > 0) - { - if (!groundquests.isQuestActive(player, questName)) - { - if (hasScript(self, "item.special.nodestroy")) - { - detachScript(self, "item.special.nodestroy"); - } - messageTo(self, "handleDestroy", null, 0, false); - return false; - } - if (!groundquests.isTaskActive(player, questName, taskName)) - { - if (hasScript(self, "item.special.nodestroy")) - { - detachScript(self, "item.special.nodestroy"); - } - messageTo(self, "handleDestroy", null, 0, false); - return false; - } - } - if (!keyString.equals(getStringObjVar(target, OBJECT_KEY))) - { - return false; - } if (isMob(target) && (isDead(target) || isIncapacitated(target))) { - sendSystemMessage(player, CREATURE_IS_DEAD); - return false; + sendSystemMessage(player, SID_CREATURE_IS_DEAD); + return; } - String collectonSlot = collectionDataDict.getString("collectonSlot"); - if (collectonSlot != null && collectonSlot.length() > 0) + + // targetContainedScript validates if the mobs are in (and in the correct) + // container, e.g. a dungeon, and therefore using dungeon spawner + final String targetContainedScript = utils.getStringScriptVar(self, "collection.targetContainedScript"); + if(targetContainedScript != null && targetContainedScript.length() > 0) { - String[] allSlots = split(collectonSlot, ','); - for (String allSlot : allSlots) { - if (!hasCompletedCollectionSlotPrereq(player, allSlot)) { + if(!hasScript(getTopMostContainer(target), targetContainedScript)) + { + showFlyTextPrivate(target, player, SID_INVALID_TARGET_FLY, 1.5f, color.RED); + return; + } + } + + // targetCreature validates a specific type of mob from creatures table + final String targetCreature = utils.getStringScriptVar(self, "collection.targetCreature"); + if(targetCreature != null && targetCreature.length() > 0) + { + final String creature = getCreatureName(target); + if(creature != null && !getCreatureName(target).equalsIgnoreCase(targetCreature)) + { + showFlyTextPrivate(target, player, SID_INVALID_TARGET_FLY, 1.5f, color.RED); + return; + } + } + + // make sure we aren't an unreasonable distance away + if(utils.getDistance2D(player, target) > 5.0f) + { + showFlyTextPrivate(target, player, SID_TOO_FAR, 1.5f, color.RED); + return; + } + + // and make sure we can actually see the thing + if(!canSee(self, target) || !canSee(target, self)) + { + showFlyTextPrivate(self, player, SID_CANT_SEE, 1.5f, color.RED); + return; + } + + // effectively do a ++ on the next available slot in the collection + final String collectionName = utils.getStringScriptVar(self, "collection.collectionName"); + final String targetEffect = utils.getStringScriptVar(self, "collection.targetEffect"); + if(collectionName != null && collectionName.length() > 0) + { + final String[] slots = utils.getStringArrayScriptVar(self, "collection.collectionSlots"); + if(slots != null && slots.length > 0) + { + for(String slot : slots) + { + if(!hasCompletedCollectionSlotPrereq(player, slot)) + { + continue; + } + if(hasCompletedCollectionSlot(player, slot)) + { + continue; + } + modifyCollectionSlotValue(player, slot, 1); + messageTo(target, utils.getStringScriptVar(self, "collection.handleTarget"), null, 1f, false); + if(targetEffect != null && targetEffect.length() > 0) + { + playClientEffectObj(self, targetEffect, target, "root"); + } break; } - if (hasCompletedCollectionSlot(player, allSlot)) { - continue; - } - modifyCollectionSlotValue(player, allSlot, 1); - String handleTarget = collectionDataDict.getString("handleTarget"); - if (handleTarget != null && handleTarget.length() > 0) { - messageTo(target, handleTarget, null, 0, false); - } - break; } } + } + + /** + * Determine if we should delete the special collection object + * based on our current player's context + */ + public static boolean isObjectInvalidInContext(obj_id self) throws InterruptedException + { + final obj_id player = getOwner(self); + if(isIdValid(player) && isPlayer(player)) + { + final String collectionName = utils.getStringScriptVar(self, "collection.collectionName"); + final int questRequired = utils.getIntScriptVar(self, "collection.questRequired"); + if(questRequired > 0) // a quest is required to have this item + { + final String questName = utils.getStringScriptVar(self, "collection.questName"); + final String taskName = utils.getStringScriptVar(self, "taskName"); + if(questName != null && questName.length() > 0) // a specific quest must be active + { + if(!groundquests.isQuestActive(player, questName)) + { + return true; + } + } + // check if a specific task is *related* but don't specifically check + // whether the task is active or not as this can pre-maturely delete the + // object, e.g. if we were just given it and click on it while the + // travel to whatever location task is still active before the actual required task + if(taskName != null && taskName.length() > 0) + { + if(groundquests.hasCompletedTask(player, questName, taskName)) + { + return true; + } + } + } + // if we've already completed the collection that is relevant + // to this object, we don't need it anymore + // note: player_collection also validates inventory contents for this + // on collection slot modify trigger + if(collectionName != null && collectionName.length() > 0) + { + return hasCompletedCollection(player, collectionName); + } + return false; + } return true; } - public int handleDestroy(obj_id self, dictionary params) throws InterruptedException + + /** + * Store all the crap we need as ScriptVars so we don't have to keep reading from a table + */ + public int setupVars(obj_id self, dictionary params) throws InterruptedException { - if (hasObjVar(self, "owner")) + final String keyString = getStringObjVar(self, VAR_OBJECT_KEY); + if(keyString == null || keyString.length() < 1) { - obj_id player = getObjIdObjVar(self, "owner"); - sendSystemMessage(player, OBJECT_DESTROYED); + WARNING("Bad object of collection_use_object_on_object -> missing Key String Var ("+self+")"); + messageTo(self, "onRequestDestroy", null, 0f, false); + return SCRIPT_CONTINUE; + } + final dictionary d = dataTableGetRow(TABLE_COLLECTION_OBJECT, keyString); + if(d == null || d.size() < 1) + { + WARNING("Bad object of collection_use_object_on_object -> Key String "+keyString+" is missing from collection object table ("+self+")"); + return SCRIPT_CONTINUE; + } + final String collectionName = d.getString("collectionName"); + if(collectionName == null || collectionName.length() < 1) + { + WARNING("Bad object of collection_use_object_on_object -> Collection Name is missing from collection table ("+self+")"); + return SCRIPT_CONTINUE; + } + else + { + final String[] slots = getAllCollectionSlotsInCollection(collectionName); + if(slots == null || slots.length < 1) + { + WARNING("Bad object of collection_use_object_on_object -> Collection name is invalid or doesn't have any slots ("+self+")"); + return SCRIPT_CONTINUE; + } + else + { + utils.setScriptVar(self, "collection.collectionSlots", slots); + } + } + utils.setScriptVar(self, "collection.collectionName", collectionName); + utils.setScriptVar(self, "collection.questName", d.getString("questName")); + utils.setScriptVar(self, "collection.taskName", d.getString("taskName")); + utils.setScriptVar(self, "collection.handleTarget", d.getString("handleTarget")); + utils.setScriptVar(self, "collection.targetContainedScript", d.getString("targetContainedScript")); + utils.setScriptVar(self, "collection.targetCreature", d.getString("targetCreature")); + utils.setScriptVar(self, "collection.questRequired", d.getInt("questRequired")); + utils.setScriptVar(self, "collection.targetEffect", d.getString("targetEffect")); + return SCRIPT_CONTINUE; + } + + /** + * We shouldn't have this item anymore, so destroy it + */ + public int onRequestDestroy(obj_id self, dictionary params) throws InterruptedException + { + if(hasScript(self, "item.special.nodestroy")) + { + detachScript(self, "item.special.nodestroy"); + } + if(params.containsKey("player")) + { + final obj_id player = params.getObjId("player"); + if(isIdValid(player) && isPlayerConnected(player)) + { + sendSystemMessage(player, SID_OBJECT_DESTROYED); + } } destroyObject(self); return SCRIPT_CONTINUE; } + }