From 0a8d775b026272180d5d90326ab9596ea6bf05ec Mon Sep 17 00:00:00 2001 From: Heron Date: Mon, 11 May 2026 22:38:15 -0400 Subject: [PATCH] Added logic for lair interactivity --- .../game/script/library/config_utils.java | 187 ++++++++++++ .../compiled/game/script/library/luck.java | 30 ++ .../systems/npc_lair/lair_interactivity.java | 278 +++++++++++++++++- 3 files changed, 494 insertions(+), 1 deletion(-) create mode 100755 sku.0/sys.server/compiled/game/script/library/config_utils.java diff --git a/sku.0/sys.server/compiled/game/script/library/config_utils.java b/sku.0/sys.server/compiled/game/script/library/config_utils.java new file mode 100755 index 000000000..9743530d4 --- /dev/null +++ b/sku.0/sys.server/compiled/game/script/library/config_utils.java @@ -0,0 +1,187 @@ +package script.library; + +import script.*; + +public class config_utils extends script.base_script +{ + public config_utils() + { + } + + public static String getStringConfig(String section, String key) throws InterruptedException + { + return getStringConfig(section, key, null); + } + + public static String getStringConfig(String section, String key, String defaultValue) throws InterruptedException + { + if (section == null || section.length() <= 0 || key == null || key.length() <= 0) + { + return defaultValue; + } + + String value = getConfigSetting(section, key); + + if (value == null || value.length() <= 0) + { + return defaultValue; + } + + return value; + } + + public static boolean getBooleanConfig(String section, String key) throws InterruptedException + { + return getBooleanConfig(section, key, false); + } + + public static boolean getBooleanConfig(String section, String key, boolean defaultValue) throws InterruptedException + { + String value = getStringConfig(section, key, null); + + if (value == null) + { + return defaultValue; + } + + value = value.toLowerCase(); + + if (value.equals("true") || value.equals("on") || value.equals("1") || value.equals("yes")) + { + return true; + } + + if (value.equals("false") || value.equals("off") || value.equals("0") || value.equals("no")) + { + return false; + } + + return defaultValue; + } + + public static int getIntConfig(String section, String key) throws InterruptedException + { + return getIntConfig(section, key, 0); + } + + public static int getIntConfig(String section, String key, int defaultValue) throws InterruptedException + { + String value = getStringConfig(section, key, null); + + if (value == null) + { + return defaultValue; + } + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + public static long getLongConfig(String section, String key) throws InterruptedException + { + return getLongConfig(section, key, 0); + } + + public static long getLongConfig(String section, String key, long defaultValue) throws InterruptedException + { + String value = getStringConfig(section, key, null); + + if (value == null) + { + return defaultValue; + } + + try + { + return Long.parseLong(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + public static float getFloatConfig(String section, String key) throws InterruptedException + { + return getFloatConfig(section, key, 0.0f); + } + + public static float getFloatConfig(String section, String key, float defaultValue) throws InterruptedException + { + String value = getStringConfig(section, key, null); + + if (value == null) + { + return defaultValue; + } + + try + { + return Float.parseFloat(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + public static double getDoubleConfig(String section, String key) throws InterruptedException + { + return getDoubleConfig(section, key, 0.0); + } + + public static double getDoubleConfig(String section, String key, double defaultValue) throws InterruptedException + { + String value = getStringConfig(section, key, null); + + if (value == null) + { + return defaultValue; + } + + try + { + return Double.parseDouble(value); + } + catch (NumberFormatException e) + { + return defaultValue; + } + } + + public static int getPercentConfig(String section, String key) throws InterruptedException + { + return getPercentConfig(section, key, 0); + } + + public static int getPercentConfig(String section, String key, int defaultValue) throws InterruptedException + { + return clampInt(getIntConfig(section, key, defaultValue), 0, 100); + } + + public static int getIntConfigClamped(String section, String key, int defaultValue, int minValue, int maxValue) throws InterruptedException + { + return clampInt(getIntConfig(section, key, defaultValue), minValue, maxValue); + } + + public static int clampInt(int value, int minValue, int maxValue) + { + if (value < minValue) + { + return minValue; + } + + if (value > maxValue) + { + return maxValue; + } + + return value; + } +} diff --git a/sku.0/sys.server/compiled/game/script/library/luck.java b/sku.0/sys.server/compiled/game/script/library/luck.java index 5da2e8321..7c6fbc990 100755 --- a/sku.0/sys.server/compiled/game/script/library/luck.java +++ b/sku.0/sys.server/compiled/game/script/library/luck.java @@ -51,4 +51,34 @@ public class luck extends script.base_script pp = prose.setStringId(pp, new string_id("system_msg", "lucky_fly_text")); showFlyTextPrivateProseWithFlags(player, player, pp, 1.5f, colors.GOLD, FLY_TEXT_FLAG_IS_LUCKY); } + + public static float getLevelCappedLuck(obj_id player) throws InterruptedException + { + if (!isPlayer(player)) + { + return 0.0f; + } + + int level = getLevel(player); + int cap = level * 5; + + float luckValue = getSkillStatisticModifier(player, "luck"); + float luckBonus = getEnhancedSkillStatisticModifierUncapped(player, "luck_modified"); + + luckValue += luckBonus; + + if (luckValue < 0.0f) + { + return 0.0f; + } + + if (luckValue > cap) + { + return cap; + } + + return luckValue; + } + + } diff --git a/sku.0/sys.server/compiled/game/script/systems/npc_lair/lair_interactivity.java b/sku.0/sys.server/compiled/game/script/systems/npc_lair/lair_interactivity.java index 410eaa4cd..1a9ec653a 100755 --- a/sku.0/sys.server/compiled/game/script/systems/npc_lair/lair_interactivity.java +++ b/sku.0/sys.server/compiled/game/script/systems/npc_lair/lair_interactivity.java @@ -1,9 +1,13 @@ package script.systems.npc_lair; import script.*; +import script.library.luck; import script.library.buff; +import script.library.collection; import script.library.resource; +import script.library.static_item; import script.library.utils; +import script.library.config_utils; public class lair_interactivity extends script.base_script { @@ -16,6 +20,7 @@ public class lair_interactivity extends script.base_script public static final int LAIR_BUFF_INCREASE = 2; public static final string_id SID_SEARCH_LAIR = new string_id("lair_n", "search_lair"); public static final string_id SID_FOUND_NOTHING = new string_id("lair_n", "found_nothing"); + public static final string_id SID_LAIR_EMPTY = new string_id("lair_n", "lair_empty"); public static final string_id SID_INVENTORY_FULL = new string_id("lair_n", "inventory_full"); public static final string_id SID_FOUND_EGGS = new string_id("lair_n", "found_eggs"); public static final string_id SID_FOUND_BUGS = new string_id("lair_n", "found_bugs"); @@ -34,6 +39,21 @@ public class lair_interactivity extends script.base_script "object/tangible/fishing/bait/bait_worm.iff", "object/tangible/fishing/bait/bait_insect.iff" }; + + public static final String CONFIG_SECTION_LAIR_INTERACTIVITY = "LairInteractivity"; + + public static final String CONFIG_LAIR_INTERACTIVITY_ENABLED = "enabled"; + public static final String CONFIG_LAIR_EGG_CHANCE = "eggChance"; + public static final String CONFIG_LAIR_BUG_CHANCE = "bugChance"; + public static final String CONFIG_LAIR_NOTHING_CHANCE = "nothingChance"; + public static final String CONFIG_LAIR_EMPTY_CHANCE = "emptyChance"; + public static final String CONFIG_LAIR_EGG_AMOUNT_MULTIPLIER_PERCENT = "eggAmountMultiplierPercent"; + public static final String CONFIG_LAIR_RESET_MINUTES = "resetMinutes"; + public static final String CONFIG_LAIR_SHOW_EMPTY_MESSAGE = "showEmptyMessage"; + public static final String CONFIG_LAIR_USE_LUCK = "useLuck"; + + public static final String LAIR_SEARCHED_TIME = "lair.searched_time"; + public int OnObjectMenuRequest(obj_id self, obj_id player, menu_info mi) throws InterruptedException { if (!hasObjVar(self, "npc_lair.isCreatureLair")) @@ -57,8 +77,264 @@ public class lair_interactivity extends script.base_script searchLair(self, player); return SCRIPT_CONTINUE; } - public void searchLair(obj_id self, obj_id player) throws InterruptedException + + public void searchLair(obj_id self, obj_id player) throws InterruptedException + { + // Missing [LairInteractivity] or enabled=false preserves vanilla/public behavior. + if (!config_utils.getBooleanConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_INTERACTIVITY_ENABLED)) + { + searchLairVanilla(self, player); + return; + } + + searchLairInteractivity(self, player); + } + + private void searchLairInteractivity(obj_id self, obj_id player) throws InterruptedException + { + if (utils.hasScriptVar(self, LAIR_SEARCHED)) + { + if (hasLairSearchResetExpired(self)) + { + resetLairSearchState(self); + } + else + { + if (config_utils.getBooleanConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_SHOW_EMPTY_MESSAGE, true)) + { + sendSystemMessage(player, SID_LAIR_EMPTY); + } + + return; + } + } + + obj_id pInv = utils.getInventoryContainer(player); + + if (!isValidId(pInv) || !exists(pInv)) + { + return; + } + + if (getVolumeFree(pInv) <= 0) + { + sendSystemMessage(player, SID_INVENTORY_FULL); + return; + } + + int result = rollLairSearchResult(); + + if (result == 0) + { + sendSystemMessage(player, SID_FOUND_NOTHING); + return; + } + + if (result == 1) + { + searchLairEggs(self, player, pInv); + } + else + { + searchLairBugs(player, pInv); + } + + rollLairEmpty(self, player); + } + + private int rollLairSearchResult() throws InterruptedException + { + int nothingChance = config_utils.getPercentConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_NOTHING_CHANCE, 10); + int eggChance = config_utils.getPercentConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_EGG_CHANCE, 80); + int bugChance = config_utils.getPercentConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_BUG_CHANCE, 10); + + int totalChance = nothingChance + eggChance + bugChance; + + if (totalChance <= 0) + { + nothingChance = 10; + eggChance = 80; + bugChance = 10; + totalChance = 100; + } + + int roll = rand(1, totalChance); + + if (roll <= nothingChance) + { + return 0; // nothing + } + + if (roll <= nothingChance + eggChance) + { + return 1; // eggs + } + + return 2; // bugs + } + + private void searchLairEggs(obj_id self, obj_id player, obj_id pInv) throws InterruptedException + { + sendSystemMessage(player, SID_FOUND_EGGS); + + int amount = rand(10, 20); + + int skillMod = getSkillStatMod(player, "creature_harvesting"); + + if (skillMod > 0) + { + amount += rand(1, skillMod); + } + + if (buff.hasBuff(player, "lair_egg_buff")) + { + amount = amount * LAIR_BUFF_INCREASE; + } + + int multiplierPercent = config_utils.getIntConfigClamped( + CONFIG_SECTION_LAIR_INTERACTIVITY, + CONFIG_LAIR_EGG_AMOUNT_MULTIPLIER_PERCENT, + 100, + 0, + 10000 + ); + + amount = amount * multiplierPercent / 100; + + if (amount <= 0) + { + sendSystemMessage(player, SID_FOUND_NOTHING); + return; + } + + obj_id[] resourceList = resource.createRandom("meat_egg_" + getCurrentSceneName(), amount, getLocation(self), pInv, player, 2); + + if (resourceList == null) + { + return; + } + + location curloc = getLocation(player); + + if (curloc == null) + { + return; + } + + for (obj_id obj_id : resourceList) + { + setLocation(obj_id, curloc); + putIn(obj_id, pInv, player); + } + + if (config_utils.getBooleanConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_USE_LUCK, true)) + { + float luckValue = luck.getLevelCappedLuck(player); + + if (luck.isLucky(player)) + { + static_item.createNewItemFunction(collection.PRISTINE_EGG, player); + sendSystemMessage(player, new string_id("collection", "resource_egg")); + } + + collection.collectionResource(player, "egg", Math.round(luckValue / 100)); + } + } + + private void searchLairBugs(obj_id player, obj_id pInv) throws InterruptedException + { + sendSystemMessage(player, SID_FOUND_BUGS); + + if (rand(1, 100) < 50) + { + createObject(RARE_BUG_SAMPLE_OBJECTS[rand(0, RARE_BUG_SAMPLE_OBJECTS.length - 1)], pInv, ""); + } + else + { + createObject(BUG_SAMPLE_OBJECTS[rand(0, BUG_SAMPLE_OBJECTS.length - 1)], pInv, ""); + } + } + + private void rollLairEmpty(obj_id self, obj_id player) throws InterruptedException + { + int emptyChance = config_utils.getPercentConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_EMPTY_CHANCE, 34); + + if (emptyChance <= 0) + { + return; + } + + int roll = rand(1, 100); + + if (config_utils.getBooleanConfig(CONFIG_SECTION_LAIR_INTERACTIVITY, CONFIG_LAIR_USE_LUCK, true)) + { + float luckValue = luck.getLevelCappedLuck(player); + roll -= Math.round(luckValue / 100); + } + + if (roll <= emptyChance) + { + markLairSearched(self); + } + } + + private void markLairSearched(obj_id self) throws InterruptedException + { + utils.setScriptVar(self, LAIR_SEARCHED, 1); + + int resetMinutes = getLairResetMinutes(); + + if (resetMinutes > 0) + { + utils.setScriptVar(self, LAIR_SEARCHED_TIME, getGameTime()); + } + } + + private void resetLairSearchState(obj_id self) throws InterruptedException + { + utils.removeScriptVar(self, LAIR_SEARCHED); + utils.removeScriptVar(self, LAIR_SEARCHED_TIME); + } + + + private boolean hasLairSearchResetExpired(obj_id self) throws InterruptedException + { + int resetMinutes = getLairResetMinutes(); + + if (resetMinutes <= 0) + { + return false; + } + + if (!utils.hasScriptVar(self, LAIR_SEARCHED_TIME)) + { + return false; + } + + int searchedTime = utils.getIntScriptVar(self, LAIR_SEARCHED_TIME); + int resetSeconds = resetMinutes * 60; + + return getGameTime() - searchedTime >= resetSeconds; + } + + + private int getLairResetMinutes() throws InterruptedException + { + return config_utils.getIntConfigClamped( + CONFIG_SECTION_LAIR_INTERACTIVITY, + CONFIG_LAIR_RESET_MINUTES, + 0, + 0, + 10080 + ); + } + + + public void searchLairVanilla(obj_id self, obj_id player) throws InterruptedException { + + //This is the original lair seraching code + if (utils.hasScriptVar(self, LAIR_SEARCHED)) { return;