Added logic for lair interactivity

This commit is contained in:
Heron
2026-05-11 22:38:15 -04:00
parent ba7a1962e5
commit 0a8d775b02
3 changed files with 494 additions and 1 deletions
@@ -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;
}
}
@@ -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;
}
}
@@ -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;