/* * Copyright (c)2000-2002 Sony Online Entertainment Inc. * All Rights Reserved * * Title: loot.scriptlib * Description: ai loot reference * @author $Author:$ * @version $Revision:$ **********************************************************************/ /***** INCLUDES ********************************************************/ include library.ai_lib; include library.buff; include library.corpse; include library.consumable; include library.craftinglib; include library.factions; include library.healing; include library.hue; include library.money; include library.pet_lib; include library.resource; include library.slots; include library.static_item; include library.storyteller; include library.trace; include library.utils; include library.weapons; include java.util.Arrays; include java.util.Enumeration; include java.util.Vector; /***** CONSTANTS *******************************************************/ /******************** Collections Loot Increase Amount ******************* ************* This Value should be set to one at all times *************** ******************* with the exception of Test Center *******************/ const int COL_LOOT_MULTIPLIER_ON = 1; //GENERAL const string TBL_CREATURES = "datatables/mob/creatures.iff"; const string TBL_COMPONENT_DATA = "datatables/loot/component_data.iff"; const string VAR_DENY_LOOT = "denyLoot"; const string VAR_LOOT_QUALITY = "loot.loot_quality"; //CHRONICLES const string CHRONICLES_LOOT_TOGGLE_OBJVAR = "chroniclesLoot_toggledOff"; //LOOT ESCLUSION const string TBL_EXCLUSION = "datatables/loot/exclusion.iff"; const string COL_TEMPLATE = "TEMPLATE"; const string COL_EXCLUDE = "EXCLUDE"; //FACTION DATA ITEM DEFINES //objvars const string DATA_ITEM = "dataItem"; const string DATA_ITEM_FACTION = DATA_ITEM + ".faction"; const string DATA_ITEM_VALUE = DATA_ITEM + ".value"; //lookup datatable const string TBL_DATA_ITEM_VALUE = "datatables/data_item/redeem_value.iff"; //DATA DISK DEFINES const string ENCODED_DISK = "object/tangible/encoded_disk/encoded_disk_base.iff"; // Special loot table. const string SPECIAL_LOOT = "datatables/loot/npc_special_loot.iff"; //Collectible loot table const string COLLECTIBLE_LOOT = "datatables/loot/npc_collectible_loot.iff"; // Item template for cash as loot const string CASH_ITEM_TEMPLATE = "object/tangible/item/loot_cash.iff"; // Item template for meatlump lumps const string MEATLUMP_LUMP = "item_meatlump_lump_01_01"; const string MEATLUMP_LOOT_TABLE = "datatables/loot/dungeon/meatlump_dungeon_container_loot.iff"; //Creature Table const string CREATURES_TABLE = "datatables/mob/creatures.iff"; // Collections Table const string COLLECTIONS_LOOT_TABLE = "datatables/loot/loot_items/collectible/collection_loot.iff"; const string COLLECTIONS_PVP_RANK_TABLE = "datatables/loot/loot_items/collectible/collection_pvp_rank.iff"; const string COLLECTIONS_PVP_LOOT_TABLE = "datatables/loot/loot_items/collectible/collection_pvp_loot.iff"; // Values used to calculate the intensity of loot attributes skewed by creature level const int MIN_CREATURE_LEVEL = 1; const int MAX_CREATURE_LEVEL = 85; // Weapons specific attribute knobs const float MAX_ALLOWED_WEAPON_INTENSITY = 0.90f; // Max weapon attribute intensity based on crafting values const float MIN_ALLOWED_WEAPON_INTENSITY = 0.35f; // Min weapon attribute intensity based on crafting values const int BASE_CHANCE_FOR_CASH = 40; const int BASE_CHANCE_FOR_RESOURCES = 40; //Beast Master Enzyme Loot const int BASE_CHANCE_FOR_ENZYME = 25; const string_id SID_REWARD_ITEM = new string_id("collection","reward_item"); const int COLLECTION_PVP_ROLL = 80; //foraging functions /***** LOGGING CONSTANTS ***********************************************/ const string FORAGE_LOGGING_CATEGORY = "foraging"; const boolean FORAGE_LOGGING_ON = false; const int RARE_COMPONENT_DIVISOR = 10; const int SUPER_RARE_COMPONENT_DIVISOR = 5; const int WORM_ROLL_INT = 80; //forage categories const int ENZYME = 0; const int WORM = 1; const int COMPONENT = 2; const int TREASURE = 3; const int BAIT = 99; const int NOTHING = 98; const int FORAGE_BASE_ONE = 14; const int FORAGE_BASE_TWO = 28; const int PLAYER_DEFAULT_FORAGE_CHANCE = 0; const int DEFAULT_ENZYME_CHANCE = 40; const int DEFAULT_WORM_CHANCE = 40; const int DEFAULT_TREASURE_CHANCE = 0; const int DEFAULT_COMPONENT_CHANCE = 40; const float ENZYME_MODIFIER = 0.f; const float TREASURE_MODIFIER = 0.f; const float PLAYER_DEFAULT_FIND_MODIFIER = 0.f; const float LUCKY_FIND_MODIFIER = 6.f; const string FORAGING_RARE_TABLE = "datatables/foraging/forage_global_rare.iff"; const string FORAGING_ENEMY_TABLE = "datatables/foraging/forage_enemy.iff"; const string FORAGING_LOOT_ROLL_TABLE = "datatables/foraging/foraging_loot_roll.iff"; const string LOOT_LOW_COL = "low"; const string LOOT_HIGH_COL = "high"; const string LOOT_ENZYME = "enzyme"; const string LOOT_COMPONENT = "component"; const string LOOT_WORM = "worm"; const string LOOT_BAIT = "bait"; const string LOOT_TREASURE = "treasure"; const string FORAGE_ENEMY_SCRIPT = "creature.foraging_enemy"; const string FORAGING_STF = "player/player_utility"; const string_id FOUND_TREASURE = new string_id("player/player_utility", "forage_found_treasure"); const string_id TREASURE_BONUS_ROLL = new string_id("player/player_utility", "treasure_buff_extra_roll"); const string_id TREASURE_BONUS = new string_id("player/player_utility", "treasure_map_buff_bonus"); const string_id SID_FULL_INVENTORY = new string_id("player/player_utility", "forage_full_inventory"); const string_id FOUND_NOTHING = new string_id("player/player_utility", "forage_found_nothing"); const string_id FOUND_SOMETHING = new string_id("player/player_utility", "forage_found_something"); const string_id FOUND_WORM = new string_id("player/player_utility", "forage_found_worm"); const string_id FOUND_COMPONENT = new string_id("player/player_utility", "forage_found_component"); const string_id FOUND_ENZYME = new string_id("player/player_utility", "forage_found_enzyme"); const string_id ENZYME_BONUS_ROLL = new string_id("player/player_utility", "enzyme_buff_extra_roll"); const string_id ENZYME_BONUS = new string_id("player/player_utility", "truffle_pig_buff_bonus"); const string_id FORAGE_BONUS = new string_id("player/player_utility", "ice_cream_buff_bonus"); const string_id LUCK_BONUS = new string_id("player/player_utility", "luck_buff_bonus"); const string[] COMPONENT_PLANETS = { "corellia", "dantooine", "dathomir", "endor", "naboo", "tatooine", "yavin4" }; /***** FUNCTIONS **************************************************/ /***** BASE FUNCTION ************************************/ /************************************************************ * @brief adds loot to the target object * * @param obj_id redeemer * @param obj_id coupon * * @return boolean; false on error ************************************************************/ boolean addLoot( obj_id target ) { if( !isIdValid(target) ) { return false; } if( hasObjVar(target, VAR_DENY_LOOT) ) { return false; } boolean hasLoot = false; obj_id inv = utils.getInventoryContainer(target); if( inv == null ) return false; ////LOG("loot", "addLoot: retrieving mob type"); string mobType = ai_lib.getCreatureName(target); if( mobType == null || mobType.length() < 1) { string err = "WARNING: loot::addLoot(" + target + ") returning false because getCreatureName failed. Template=" + getTemplateName(target) + ", IsAuthoritative=" + target.isAuthoritative() + ". Stack Trace as follows:"; CustomerServiceLog("creatureNameErrors", err); debugServerConsoleMsg(target, err); Thread.dumpStack(); return false; } if ( !hasObjVar(target, "storytellerid") ) { int cash = getNpcMoney(target); if (addCashAsLoot(target, cash)) { hasLoot = true; } } // LOG("Loot", "Adding loot"); // hasLoot |= addResourceLoot(target); hasLoot |= setupLootItems(target); // hasLoot |= addMilkOrEgg(target); hasLoot |= addCollectionLoot(target); /* hasLoot |= addChronicleLoot(target); moved to ai.ai in messageHandler aiCorpsePrepared() */ /**removing this: if ( getConfigSetting("GameServer", "enableLevelUpLoot")!=null ) { if ( rand(1,50000) <= getLevel(target) ) { obj_id objTest = createObject( "object/tangible/loot/quest/levelup_lifeday_orb.iff", inv, ""); utils.setScriptVar( target, "orbCreated", objTest ); hasLoot = true; } } */ int niche = ai_lib.aiGetNiche(mobType); // set harvesting resource flag, if appropriate if(niche == NICHE_MONSTER || niche == NICHE_HERBIVORE || niche == NICHE_CARNIVORE || niche == NICHE_PREDATOR) { //has harvestable resource? int[] hasResource = corpse.hasResource(mobType); if( hasResource != null && hasResource.length > 0) { setObjVar(target, corpse.VAR_HAS_RESOURCE, hasResource); } //Do not add Enzymes to a tutorial object if(isInTutorialArea(target)) return false; //Adding Beast Enzymes to looted creatures hasLoot |= addBeastEnzymes(target); } return hasLoot; } int getCashForLevel(string mobType, int level) { if( mobType == null || mobType.equals("") || level < 1 ) return 0; int lbound = dataTableGetInt(TBL_CREATURES, mobType, "minCash"); int ubound = dataTableGetInt(TBL_CREATURES, mobType, "maxCash"); if( ubound == 0 ) return 0; if( lbound > 0 && ubound > 0 ) return rand(lbound, ubound); int minCash = level; if( lbound > 0 && minCash < lbound ) minCash = lbound; int maxCash = level*17; if( ubound > 0 && maxCash > ubound ) maxCash = ubound; return rand(minCash, maxCash); } boolean addResourceLoot(obj_id target) { if ( rand(1,100) > BASE_CHANCE_FOR_RESOURCES ) return false; // Check for niche with resources string mobType = ai_lib.getCreatureName(target); int niche = ai_lib.aiGetNiche(mobType); if (niche != NICHE_MONSTER && niche != NICHE_HERBIVORE && niche != NICHE_CARNIVORE && niche != NICHE_PREDATOR) return false; int[] hasResource = corpse.hasResource(mobType); if( hasResource == null || hasResource.length == 0) return false; obj_id inv = utils.getInventoryContainer(target); dictionary resourceData = corpse.getRandomHarvestCorpseResources(obj_id.NULL_ID, target); java.util.Enumeration keys = resourceData.keys(); int finalAmount = 0; while ( keys.hasMoreElements() ) { // Determine max amount to harvest. string resourceType = (string)(keys.nextElement()); int amt = resourceData.getInt(resourceType); if ( amt <= 0 ) continue; // Adjust resource name for the current planet. // 4/25/05 (wwallace): This now checks a datatable that maps scene names to planet names. // This was done so that all 7 Kashyyyk zones could use a single "_kashyyyk" resource type // instead of a resource type for each zone. // kashyyyk_dead_forest maps to kashyyyk, etc... // Scene names for the original 10 planets map to themselves. string sceneName = getCurrentSceneName(); string rsrcMapTable = "datatables/creature_resource/resource_scene_map.iff"; string correctedPlanetName = dataTableGetString(rsrcMapTable, sceneName, 1); if ( correctedPlanetName == null || correctedPlanetName == "" ) { correctedPlanetName = "tatooine"; } resourceType = resourceType + "_" + correctedPlanetName; int useDistMap = dataTableGetInt(rsrcMapTable, sceneName, "useDistributionMap"); location worldLoc = getWorldLocation(target); if (useDistMap == 0) worldLoc.area = correctedPlanetName; finalAmount += corpse.extractCorpseResource(resourceType, amt, worldLoc, obj_id.NULL_ID, inv, 1); } return (finalAmount > 0); } // WHAT THE HELL ARE THESE? WHO WRITES FUNCTIONS LIKE THIS? // NO IDEA WHAT THIS STUFF DOES, BUT I'M NOT DELETING IT. // @minDropLevel is the minimum level of creature that drops this item, and max level creature for @maxDropLevel // These parms are used to tweak the distribution of the attribute value returned. If these parms are not given // 1 and 350 will be used for these values. // int getCalculatedAttribute(int minVal, int maxVal, int creatureLevel, int minDropLevel, int maxDropLevel) { return Math.round(getCalculatedAttribute((float)minVal, (float)maxVal, creatureLevel, minDropLevel, maxDropLevel)); } float getCalculatedAttribute(float minVal, float maxVal, int creatureLevel, int minDropLevel, int maxDropLevel) { float rank = (float)(creatureLevel - minDropLevel) / (float)(maxDropLevel - minDropLevel); // utils.distributeRand requires sepcific ranges if(rank < -1.0f) { rank = -1.0f; } else if(rank > 2.0f) { rank = 2.0f; } float rslt = random.distributedRand(minVal, maxVal, rank); LOG("loot", "getCalculatedAttribute: -> returning " + rslt); return rslt; } int getCalculatedAttribute(int minVal, int maxVal, int creatureLevel) { return getCalculatedAttribute(minVal, maxVal, creatureLevel, MIN_CREATURE_LEVEL, MAX_CREATURE_LEVEL); } // // generates a semi-random number between @minVal and @maxVal, given @creatureLevel as a weight. The closer // the @creatureLevel gets to MAX_CREATURE_LEVEL, the more heavily the @returnValue will be skewed to @maxValue // float getCalculatedAttribute(float minVal, float maxVal, int creatureLevel) { return getCalculatedAttribute(minVal, maxVal, creatureLevel, MIN_CREATURE_LEVEL, MAX_CREATURE_LEVEL); } // // Finds the first rown in the component data table at which the component's data starts // int getRowIndexForComponent(string componentTemplate) { if(componentTemplate == null || componentTemplate == "") { return -1; } return dataTableSearchColumnForString(componentTemplate, "template", TBL_COMPONENT_DATA); } // // Packages all the rows of component data from the component data table into a dictionary. // Keys are the "stringArg", Values are float[] with data from "min" and "max" columns // dictionary getComponentData(obj_id component) { return getComponentData(getTemplateName(component)); } dictionary getComponentData(string componentTemplate) { int startRow = getRowIndexForComponent(componentTemplate); if(startRow < 0) { return null; } dictionary dat = new dictionary(); int totalRows = dataTableGetNumRows(TBL_COMPONENT_DATA); if(totalRows < startRow + 1) { trace.log("loot", "loot::getComponentData: -> Gave starting row of " + startRow + ", but " + TBL_COMPONENT_DATA + " only has " + totalRows + " rows.", null, trace.TL_ERROR_LOG); return null; } string templateString = ""; for(int i = startRow; i < totalRows; i++) { templateString = dataTableGetString(TBL_COMPONENT_DATA, i, "template"); // There are no more objects if the next template name is reached if(!templateString.equals(componentTemplate) && !templateString.equals("")) { trace.log("loot", "loot::getComponentData: -> Done fetching data for this object. Stopped at data table row " + i); break; } dat.put(dataTableGetString(TBL_COMPONENT_DATA, i, "stringArg"), new float[]{dataTableGetFloat(TBL_COMPONENT_DATA, i, "min"), dataTableGetFloat(TBL_COMPONENT_DATA, i, "max")}); } trace.log("loot", "loot:getComponentData: -> Component data has " + dat.size() + " elements."); return dat; } /************************ /***** FACTIONAL DATA ITEM/COUPONS ************************************/ /************************************************************ * @brief redeems a factional data item for faction points! * * @param obj_id redeemer * @param obj_id coupon * * @return boolean; false on error ************************************************************/ boolean redeemFactionCoupon(obj_id redeemer, obj_id player, obj_id coupon) { if( (redeemer == null) || (player == null) || (coupon == null) ) { return false; } if( !isFactionalDataItem(coupon) ) { return false; } string rFaction = getStringObjVar(redeemer, "faction"); if( (rFaction == null) || (rFaction.equals("")) ) { return false; } int val = getFactionCouponValue(redeemer, coupon); if( val < 0 ) { return false; } //debugSpeakMsg(redeemer, "attempting to redeem coupon for " + val + " points of " + rFaction + " faction standing"); factions.awardFactionStanding(player, rFaction, val); return destroyObject(coupon); } /************************************************************ * @brief redeems a factional data item for faction points! * * @param obj_id redeemer * @param obj_id coupon * * @return int, -1 on error ************************************************************/ int getFactionCouponValue(obj_id redeemer, obj_id coupon) { if( (redeemer == null) || (coupon == null) ) { return -1; } if( !isFactionalDataItem(coupon) ) { return -1; } if( !hasObjVar(redeemer, factions.FACTION) ) { return -1; } string rFaction = getStringObjVar(redeemer, factions.FACTION); string cFaction = getStringObjVar(coupon, DATA_ITEM_FACTION); int base_value = getIntObjVar(coupon, DATA_ITEM_VALUE); if( !dataTableOpen(TBL_DATA_ITEM_VALUE) ) { return -1; } float multiplier = utils.dataTableGetFloat(TBL_DATA_ITEM_VALUE, rFaction, cFaction); if( multiplier < 0 ) { return -1; } int point_value = (int)(base_value * multiplier); return point_value; } /************************************************************ * @brief redeems a factional data item for faction points! * * @param obj_id redeemer * @param obj_id coupon * * @return int, -1 on error ************************************************************/ boolean isFactionalDataItem(obj_id item) { if( (item == null) || (item == obj_id.NULL_ID) ) { return false; } if( getGameObjectType(item) == GOT_data_fictional ) { return true; } return false; } /************************************************************ * @brief gets the factional data items of the specified faction for target * note: requires that the object have a container in DATAPAD slot * * @param obj_id target * @param string faction * * @return obj_id[]; null on error ************************************************************/ obj_id[] getFactionalDataItems(obj_id target, string faction) { if( (target == null) || (target == obj_id.NULL_ID) ) { return null; } resizeable obj_id[] ret = new obj_id[0]; obj_id dp = utils.getDatapad(target); if( (dp == null) || (dp == obj_id.NULL_ID) ) { return null; } else { obj_id[] contents = getContents(dp); if( (contents == null) || (contents.length == 0) ) { return null; } else { for ( int i = 0; i < contents.length; i++ ) { obj_id item = contents[i]; if( isFactionalDataItem(item) ) { if( faction.equals("") ) { ret = utils.addElement(ret, item); } else { string ifac = getStringObjVar(item, DATA_ITEM_FACTION); if( ifac != null ) { if( ifac.equals(faction) ) { ret = utils.addElement(ret, item); } } } } } } } if( (ret == null) || (ret.length == 0) ) { return null; } else { return ret; } } obj_id[] getFactionalDataItems(obj_id target) { return getFactionalDataItems(target, ""); } boolean randomizeWeapon(obj_id weapon, int creatureLevel) { if(!isIdValid(weapon)) { return false; } // see at what percent of the crafting max to set the weapon, based on min allowed intensity, max alloqwed intensity // and using the level of the mob from which the weapon came as a modifier to the roll float percentOfMax = getCalculatedAttribute(MIN_ALLOWED_WEAPON_INTENSITY, MAX_ALLOWED_WEAPON_INTENSITY, creatureLevel); // grab the crafting min/max values via the weapons library dictionary weaponCraftingData = weapons.getWeaponDat(weapon); if(weaponCraftingData == null) { return false; } // set the attribute values on the weapon weapons.setWeaponAttributes(weapon, weaponCraftingData, percentOfMax); return true; } boolean randomizeArmor(obj_id item, int level) { LOG("loot", "loot:randomizeArmor: -> Randomizing " + item); if(!isIdValid(item)) { return false; } string template = getTemplateName(item); int armorCat = armor.getArmorCategoryByTemplate(template); //LOG("loot", "loot:randomizeArmor: -> Armor cat=" + armorCat); if(armorCat < 0 || armorCat > AC_max) { return false; } if(!armor.setArmorDataPercent(item, getCalculatedAttribute(0, AL_max-1, level), armorCat, getCalculatedAttribute(0.4f, 0.9f, level), getCalculatedAttribute(0.4f, 1.0f, level))) { return false; } //add recon or assualt layer if armor piece is recon or assault if(armorCat == 0) { armor.setArmorSpecialProtectionPercent(item, armor.DATATABLE_RECON_LAYER, 1.0f); } if(armorCat == 2) { armor.setArmorSpecialProtectionPercent(item, armor.DATATABLE_ASSAULT_LAYER, 1.0f); } //make sure armor is valid after randomization process if(!armor.isValidArmor(item)) { LOG("loot", "loot:randomizeArmor: -> Not valid armor."); return false; } return true; } boolean randomizeMedicine(obj_id item, int level) { if(!isIdValid(item)) { return false; } // generate some random mods if(hasObjVar(item, consumable.VAR_CONSUMABLE_MODS)) { // Modify attribute mod power attrib_mod[] am = getAttribModArrayObjVar(item, consumable.VAR_CONSUMABLE_MODS); attrib_mod[] am_new = new attrib_mod[am.length]; for (int i = 0; i < am.length; i++) { int attrib = am[i].getAttribute(); int val = am[i].getValue(); float duration = am[i].getDuration(); float atk = am[i].getAttack(); float decay = am[i].getDecay(); attrib_mod tmp; if(healing.isBuffMedicine(item)) { tmp = new attrib_mod(attrib, getCalculatedAttribute(600, 900, level), getCalculatedAttribute(9000, 12000, level), atk, decay); } else { tmp = new attrib_mod(attrib, getCalculatedAttribute(150, 400, level), duration, atk, decay); } am_new[i] = tmp; } if(am_new.length == 0) { // Zero length array check, this should not evaluate to true return false; } setObjVar(item, consumable.VAR_CONSUMABLE_MODS, am_new); } if(healing.isCureDotMedicine(item) || healing.isApplyDotMedicine(item)) { // Modify dot/cure power setObjVar(item, healing.VAR_HEALING_DOT_POWER, getCalculatedAttribute(40, 100, level)); if(healing.isApplyDotMedicine(item)) { // Modify potency int potency = healing.getDotPotency(item); setObjVar(item, healing.VAR_HEALING_DOT_POTENCY, getCalculatedAttribute(70, 150, level)); // Modify duration int duration = healing.getDotDuration(item); setObjVar(item, healing.VAR_HEALING_DOT_DURATION, getCalculatedAttribute(400, 800, level)); } } // Modify charges setCount(item, getCalculatedAttribute(5, 25, level)); return true; } // // makes sure we have good data in the component data min/max columns // float[] validateComponentVals(float[] dat) { //LOG("loot", "loot::validateComponentVals: -> # elements=" + dat.length); float[] defaultVal = new float[]{0.0f, 0.0f}; if(dat == null) { return defaultVal; } switch(dat.length) { case 1: return new float[]{dat[0], dat[0]}; case 2: return dat; } return defaultVal; } // // @item is the item being created // @level is the creature level for which this item is being created // @container is the container in which this item is being created (the creature's inventory most likely) // boolean randomizeComponent(obj_id item, int level, obj_id container) { if(!isIdValid(item)) return false; if(!isIdValid(container)) return false; // look for the component in the component loot datatable to see what objvars to attach. string template = getTemplateName(item); dictionary componentData = getComponentData(template); if(componentData == null) { return false; // not in table or other catastrophic error - can't make a component without this data } int minToDrop = 1; int maxToDrop = 1; float[] fVals = null; // temp variable to store floating point array that contains min/max values from the component table // min/maxCreatureLevelDrop needs to be looked up before the while loop because many of the if blocks require the // creature level to figure out the random distribution for the attribute intensity int minCreatureLevelDrop = MIN_CREATURE_LEVEL; int maxCreatureLevelDrop = MAX_CREATURE_LEVEL; if(componentData.containsKey("level")) { fVals = validateComponentVals(componentData.getFloatArray("level")); minCreatureLevelDrop = (int)fVals[0]; maxCreatureLevelDrop = (int)fVals[1]; componentData.remove("level"); } // Loop through all of the attribs listed and do what we need to do based on the attrib Enumeration keys = componentData.keys(); boolean hasAttribBonus = false; int[] attribBonus = null; // we won't actually allocate memory until we find the first attrib bonus while ( keys.hasMoreElements() ) { string key = (string)keys.nextElement(); fVals = validateComponentVals(componentData.getFloatArray(key)); key = key.trim(); // trim has to be done AFTER the lookup, because there may have been whitespace in the table which is how the key would have been stored LOG("loot", "loot::randomizeComponent: -> processing key '" + key + "' where data[0]=" + fVals[0] + ", data[1]=" + fVals[1] ); // The amount of objects we are to drop if(key.equals("amount")) { LOG("loot", "loot::randomizeComponent: -> Grabbing amount from dataset"); minToDrop = (int)fVals[0]; maxToDrop = (int)fVals[1]; } else if(key.startsWith("attribute.bonus.")) { string strAttrib = key.substring(key.length()-1); int intAttrib = utils.stringToInt(strAttrib); LOG("loot", "loot:randomizeComponent: -> Processing Attributes : " + intAttrib); if(intAttrib < 0 || intAttrib >= NUM_ATTRIBUTES) { LOG("loot", "loot:randomizeComponent: -> IntAttrib Out of Range : " + intAttrib); continue; } if(attribBonus == null) { attribBonus = new int[NUM_ATTRIBUTES]; } attribBonus[intAttrib] = getCalculatedAttribute((int)fVals[0], (int)fVals[1], level, minCreatureLevelDrop, maxCreatureLevelDrop); hasAttribBonus = true; } else if(key.startsWith("combat_critical")) { int val = rand((int)fVals[0], (int)fVals[1]); LOG("loot", "loot:randomizeComponent: -> Not an attribute modifier or armor modifier : " + key); // speed or attack drops as negative setObjVar(item, craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + "." + key, val); LOG("loot", "loot:randomizeComponent: -> setting objvar : " + craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + "." + key); } else { float val = getCalculatedAttribute(fVals[0], fVals[1], level, minCreatureLevelDrop, maxCreatureLevelDrop); //armor level and armor categorty must be ints if(key.equals("armorCategory")|| key.equals("armorLevel")) { setObjVar(item, craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + "." + key,(int)val); //check for armor category and add appropriate layer int armorCat = armor.getArmorCategory(item); if(armorCat == 0) { armor.setArmorSpecialProtectionPercent(item, armor.DATATABLE_RECON_LAYER, 1.0f); } if(armorCat == 2) { armor.setArmorSpecialProtectionPercent(item, armor.DATATABLE_ASSAULT_LAYER, 1.0f); } } else { // speed or attack drops as negative setObjVar(item, craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + "." + key, key.equals("attackSpeed")|| key.equals("attackCost")? val * -1 : val); } } } if(hasAttribBonus && attribBonus != null && attribBonus.length == NUM_ATTRIBUTES) { LOG("loot", "loot:randomizeComponent: -> Calling Set Attributes : " + attribBonus.toString()); for (int i=0; i Attribute " + i + " = " + attribBonus[i]); } setAttributeBonuses(item, attribBonus); } LOG("loot", "loot:randomizeComponent: -> Amount to drop : " + minToDrop + "/" + maxToDrop); // Serialize for crafting setCraftedId(item, container); setCrafter(item, container); // see if we need to drop more than one int numDropped = 1; if(minToDrop <= maxToDrop) { //numDropped = getCalculatedAttribute(minToDrop, maxToDrop, level, minCreatureLevelDrop, maxCreatureLevelDrop); numDropped = rand(minToDrop, maxToDrop); } LOG("loot", "loot:randomizeComponent: -> Dropping " + numDropped + " " + template); if(numDropped > 1) { setObjVar(item, "unstack.serialNumber", container); // for unstacking purposes - see object.onewayunstack.script for details setCount(item, numDropped); attachScript(item, "object.onewayunstack"); } return true; } string getCreatureType(string name) { return dataTableGetString("datatables/mob/creatures.iff", name, "lootList"); } int getAdjustedCreatureLevel(obj_id target, string name) { int level = getLevel(target); int difficultyClass = dataTableGetInt ("datatables/mob/creatures.iff", name, "difficultyClass"); if(difficultyClass > 0) { float levelMod = 0.4f * difficultyClass; level += (int)(level * levelMod); } return level; } boolean generateTheftLootRare(obj_id container, obj_id mark, int maxItems, obj_id thief) { string name = getCreatureName(mark); string type = getCreatureType(name); if(type == null || type == "none") { return false; } string strTreasureTable = "treasure/treasure_"; string treasureLevel = "1_10"; int intLevel = getIntObjVar(mark, "intCombatDifficulty"); if(intLevel <= 90) { treasureLevel = "81_90"; } else if(intLevel <= 80) { treasureLevel = "71_80"; } else if(intLevel <= 70) { treasureLevel = "61_70"; } else if(intLevel <= 60) { treasureLevel = "51_60"; } else if(intLevel <= 50) { treasureLevel = "41_50"; } else if(intLevel <= 40) { treasureLevel = "31_40"; } else if(intLevel <= 30) { treasureLevel = "21_30"; } else if(intLevel <= 20) { treasureLevel = "11_20"; } else if(intLevel <= 10) { treasureLevel = "1_10"; } else { return false; } string strTable = strTreasureTable + treasureLevel; utils.setScriptVar(container, "theft_in_progress", 1); return makeLootInContainer(container, strTable, 1, intLevel); } boolean generateTheftLoot(obj_id container, obj_id mark, float chanceMod, int maxItems) { string name = getCreatureName(mark); string type = getCreatureType(name); if(type == null || type == "none") { return false; } int level = getAdjustedCreatureLevel(mark, name); string strTable = getStringObjVar(mark, "loot.lootTable"); int intLevel = getIntObjVar(mark, "intCombatDifficulty"); utils.setScriptVar(container, "theft_in_progress", 1); return makeLootInContainer(container, strTable, 1, intLevel); } int getNpcMoney(obj_id target) { string mobType = ai_lib.getCreatureName(target); if( mobType == null ) return -1; int niche = ai_lib.aiGetNiche(target); if(niche != NICHE_NPC) return -1; int level = ai_lib.getLevel(target); int cash = getCashForLevel(mobType, level); if( cash < 1 ) { cash = rand (1,10); cash = cash * level; } return cash; } boolean addCashAsLoot(obj_id target, int cash) { if (cash < 1) return false; obj_id inv = utils.getInventoryContainer(target); if( inv == null ) return false; obj_id cashItem = createObject(CASH_ITEM_TEMPLATE, inv, ""); if (!isIdValid(cashItem)) return false; setObjVar(cashItem, "loot.cashAmount", cash); setName(cashItem, formatCashAmount(cash)); return true; } string formatCashAmount(int cash) { const java.text.DecimalFormat CASH_NAME_FORMAT = new java.text.DecimalFormat("#,### cr"); return CASH_NAME_FORMAT.format(cash); } boolean isCashLootItem(obj_id item) { string template = getTemplateName(item); if (template.equals(CASH_ITEM_TEMPLATE)) { if (hasObjVar(item, "loot.cashAmount")) return true; } return false; } boolean doGroupLooting (obj_id corpseId, obj_id transferer, obj_id item) { obj_id leader = group.getLeader(transferer); obj_id team = getGroupObject (transferer); int groupLootType = getGroupLootRule(team); obj_id[] objMembersWhoExist = utils.getLocalGroupMemberIds(team); obj_id corpseInv = utils.getInventoryContainer(corpseId); obj_id [] contents = getContents(corpseInv); int numContents = contents.length; if(objMembersWhoExist == null) { return false; } if(groupLootType == 0) { setGroupLootRule(team, group.FREE_FOR_ALL); return true; } if(groupLootType == 1) { obj_id master = getGroupMasterLooterId(team); string masterName = getEncodedName (master); if(transferer != master) { prose_package pp = new prose_package(); string_id masterMsg = new string_id (group.GROUP_STF, "master_only"); pp = prose.setStringId(pp, masterMsg); pp = prose.setTO(pp, masterName); sendSystemMessageProse (transferer, pp); return false; } } if(groupLootType == 2) { if(hasObjVar (corpseId, "autoLootComplete" )) { return true; } int numWindows = 0; if(hasObjVar (corpseId, "numWindowsOpen" )) { numWindows = getIntObjVar(corpseId, "numWindowsOpen"); } else { for(int i = 0; i < objMembersWhoExist.length; i++) { // Do NOT open a dang window if it's just a cred stick. if(numContents > 0 && !(numContents == 1 && isCashLootItem(contents[0]))) { openLotteryWindow(objMembersWhoExist[i], corpseInv); } // There's just a credit stick. Bypass the window, corpse clicker loots normally and credits split. People rejoice. else if(numContents == 1 && isCashLootItem(contents[0])) { return true; } else { string_id emptyCorpse = new string_id (group.GROUP_STF, "corpse_empty"); sendSystemMessage (objMembersWhoExist[i], emptyCorpse); lootAiCorpse(transferer, corpseId); return false; } } numWindows = objMembersWhoExist.length; setObjVar (corpseId, "numWindowsOpen", numWindows); } if (numWindows > 0) { dictionary lotto = new dictionary(); lotto.put ("player", transferer); lotto.put ("corpseInv", corpseInv); messageTo (corpseId, "fireLotteryPulse", lotto, 2, true); return false; } } if(groupLootType == 3) { return true; } return true; } boolean doGroupLootAllCheck (obj_id player, obj_id corpseId) { if(player != null && corpseId != null) { obj_id leader = group.getLeader(player); obj_id team = group.getGroupObject(leader); int lootType = getGroupLootRule(team); obj_id[] objMembersWhoExist = utils.getLocalGroupMemberIds(team); if(objMembersWhoExist == null) { return true; } int number = objMembersWhoExist.length; if(number < 1) { return true; } obj_id corpseInv = utils.getInventoryContainer(corpseId); if(corpseInv == null) { return false; } obj_id [] contents = getContents(corpseInv); if(contents == null) { return false; } int numContents = contents.length; if(lootType == 0) // FREE FOR ALL { setGroupLootRule (team, group.FREE_FOR_ALL); return true; } if(lootType == 1) // MASTER LOOTER { obj_id master = getGroupMasterLooterId(team); string masterName = getEncodedName (master); if(master == player) { return true; } else if( !isIdValid(master) ) { return true; // master not on planet, go ahead and FFA it. } else { prose_package pp = new prose_package(); string_id masterMsg = new string_id (group.GROUP_STF, "master_only"); pp = prose.setStringId(pp, masterMsg); pp = prose.setTO(pp, masterName); sendSystemMessageProse (player, pp); return false; } } else if(lootType == 2) // LOTTERY { if(hasObjVar (corpseId, "autoLootComplete" )) { return true; } if(hasObjVar (corpseId, "numWindowsOpen" )) { return false; } for(int i = 0; i < objMembersWhoExist.length; i++) { // Do NOT open a dang window if it's just a cred stick. if(numContents > 0 && !(numContents == 1 && isCashLootItem(contents[0]))) { openLotteryWindow(objMembersWhoExist[i], corpseInv); } // There's just a credit stick. Bypass the window, corpse clicker loots normally and credits split. People rejoice. else if(numContents == 1 && isCashLootItem(contents[0])) { return true; } else { string_id emptyCorpse = new string_id (group.GROUP_STF, "corpse_empty"); sendSystemMessage (objMembersWhoExist[i], emptyCorpse); lootAiCorpse(player, corpseId); return false; } } setObjVar (corpseId, "numWindowsOpen", objMembersWhoExist.length); dictionary lotto = new dictionary(); lotto.put ("player", player); lotto.put ("corpseInv", corpseInv); messageTo (corpseId, "fireLotteryPulse", lotto, 2, true); return false; } else if(lootType == 3) // RANDOM { return true; } else // Get Group Loot Rule returned something that wasn't between 0 and 3 { string_id cantLoot = new string_id (group.GROUP_STF, "no_loot_group"); sendSystemMessage (player, cantLoot); return false; } } return false; } boolean pickRandomLooter(obj_id player, obj_id team, obj_id corpse) { if(team != null && corpse != null) { obj_id[] objMembersWhoExist = utils.getLocalGroupMemberIds(team); if(objMembersWhoExist == null) { return false; } obj_id corpsePack = utils.getInventoryContainer(corpse); obj_id[] corpseContents = getContents(corpsePack); if(corpseContents != null && corpseContents.length > 0) { int numTeam = objMembersWhoExist.length; int numItems = corpseContents.length; if(numItems > 0) { for (int i = 0; i < numItems; i ++) { int choice = rand (0, numTeam-1); obj_id chosenPlayer = objMembersWhoExist[choice]; if(chosenPlayer != null) { return true; } else { return false; } } } } } return true; } void sendGroupLootSystemMessage (obj_id item, obj_id winner, string stf, string message) { sendGroupLootSystemMessage (item, winner, stf, message, false); return; } void sendGroupLootSystemMessage (obj_id item, obj_id winner, string stf, string message, boolean skipWinner) { if(!group.isGrouped (winner)) { return; } else { obj_id team = getGroupObject (winner); obj_id[] objMembersWhoExist = utils.getLocalGroupMemberIds(team); prose_package pp = new prose_package(); string_id lootMsg = new string_id (stf, message); //"%TT loots %TO from the corpse." pp = prose.setStringId(pp, lootMsg); pp = prose.setTO(pp, item); pp = prose.setTT(pp, winner); if(!skipWinner) { for(int intI = 0; intI < objMembersWhoExist.length; intI++) { sendSystemMessageProse(objMembersWhoExist[intI], pp); } } else { for(int intI = 0; intI < objMembersWhoExist.length; intI++) { if(objMembersWhoExist[intI] != winner) { sendSystemMessageProse(objMembersWhoExist[intI], pp); } } } } return; } void setAutoLootComplete (obj_id winner, obj_id corpse, obj_id item) { setObjVar (corpse, "autoLootComplete", 1); setObjVar (item, "pickupable", winner); return; } obj_id chooseRandomLootPlayerFromGroup (obj_id corpse, obj_id player) { obj_id team = getGroupObject(player); int groupLootRule = getGroupLootRule (team); obj_id[] objMembersWhoExist = utils.getLocalGroupMemberIds(team); int teamNumber = objMembersWhoExist.length; if(hasObjVar (corpse, "autoLootComplete" )) { obj_id corpseInv = utils.getInventoryContainer(corpse); queueCommand(player, ##"openContainer", corpseInv, "", COMMAND_PRIORITY_DEFAULT); } if(teamNumber > 0) { int which = rand(0, teamNumber-1); player = objMembersWhoExist[which]; } return player; } boolean isMasterLooter (obj_id toTest, boolean sendMessage) { obj_id team = getGroupObject(toTest); if(team != null) { obj_id masterLooter = getGroupMasterLooterId(team); string masterName = getEncodedName (masterLooter); if(toTest == masterLooter) { return true; } else if(sendMessage) { prose_package pp = new prose_package(); string_id masterMsg = new string_id (group.GROUP_STF, "master_only"); pp = prose.setStringId(pp, masterMsg); pp = prose.setTO(pp, masterName); sendSystemMessageProse (toTest, pp); } } return false; } void lootAiCorpse(obj_id self, obj_id target) { if( !isIdValid(target) ) { return; } if( !isMob(target) ) { return; } if( !isIncapacitated(target) ) { return; } if( isIncapacitated(self)) { return; } if( pet_lib.isPet (target)) { return; } if( !hasObjVar(target, "readyToLoot") ) { return; } if (!corpse.hasLootPermissions(target, self)) { return; } if (corpse.lootAICorpse(self, target)) messageTo(target, "handleCorpseEmpty", null, 0, true); return; } obj_id findItemToStack(obj_id item) { obj_id container = getContainedBy(item); if(!isIdValid(container)) return null; // Store static item name or "" for non-static item string itemStaticName = getStaticItemName(item); if(itemStaticName == null) itemStaticName = ""; obj_id[] allItems = utils.getAllItemsInContainerByTemplate( container, getTemplateName(item), false ); if( (allItems !=null) && (allItems.length>0) ) { for ( int i = 0; i < allItems.length; ++i ) { // check static item name or "" for non-static item string curItemStaticName = getStaticItemName(allItems[i]); if(curItemStaticName == null) curItemStaticName = ""; // return item if it's not the one passed in and has the same static item name or both are not static items. if( item != allItems[i] && curItemStaticName.equals(itemStaticName)) return allItems[i]; } } return null; } void stackItem(obj_id objSourceItem, obj_id objDestinationItem) { // source is the incoming item(s) int sourceCount = getCount(objSourceItem); if(sourceCount==0) { sourceCount = 1; // when we start with 1, we make sure to increment to 2. } // dest is the item to increment int intCount = getCount(objDestinationItem); if(intCount==0) { intCount = 1; // when we start with 1, we make sure to increment to 2. } intCount = intCount + sourceCount; setCount(objDestinationItem, intCount); destroyObject(objSourceItem); obj_id container = getContainedBy(objDestinationItem); if(utils.hasScriptVar(container, "theft_in_progress") ) { notifyThiefOfItemStolen(container, objDestinationItem); } return; } // loot boolean setupLootItems(obj_id objCreature) { obj_id objContainer = utils.getInventoryContainer(objCreature); if(!hasObjVar(objCreature, "loot.lootTable")) { LOG("npe", "No Loot Table on "+objCreature+" of template "+getTemplateName(objCreature)); return false; } string strTable = getStringObjVar(objCreature, "loot.lootTable"); int intLevel = getIntObjVar(objCreature, "intCombatDifficulty"); int intItems = getIntObjVar(objCreature, "loot.numItems"); return makeLootInContainer(objContainer, strTable, intItems, intLevel); } boolean setupLootItems(obj_id objCreature, int intItems) { obj_id objContainer = utils.getInventoryContainer(objCreature); if(!hasObjVar(objCreature, "loot.lootTable")) { LOG("npe", "No Loot Table on "+objCreature+" of template "+getTemplateName(objCreature)); return false; } string strTable = getStringObjVar(objCreature, "loot.lootTable"); int intLevel = getIntObjVar(objCreature, "intCombatDifficulty"); return makeLootInContainer(objContainer, strTable, intItems, intLevel); } boolean makeLootInContainer(obj_id objContainer, string strTable, int intItems, int intLevel) { boolean boolMadeLoot = false; string strRootItems= "datatables/loot/loot_items/"; string[] parse = split(strTable, ':'); //LOG("loot", "Parse is " +parse); strTable = parse[0]; strTable = "datatables/loot/loot_types/"+strTable+".iff"; string strItemsHeader = "strItems"; //look for anything in creatures tab after the : if(parse.length == 2) { strItemsHeader = parse[1]; } string[] strLootTypes = dataTableGetStringColumnNoDefaults(strTable, strItemsHeader); string[] strRequiredItems = null; //if the datatable has this required items column it will always drop the items in there in addition to the other items //if the datatable has multiple items in the required column it will insert all of them into the loot if (dataTableHasColumn(strTable, "strRequiredItems")) strRequiredItems = dataTableGetStringColumnNoDefaults(strTable, "strRequiredItems"); if((strLootTypes==null)||(strLootTypes.length==0)) { //LOG("loot", "bad type of "+strTable+" passed in"); return boolMadeLoot; } for(int intI = 0; intI < intItems; intI++) { string strItemTable= strLootTypes[rand(0, strLootTypes.length-1)]; string[] parseItem = split(strItemTable, ':'); strItemTable = parseItem[0]; strItemTable = strRootItems+strItemTable+".iff"; string strItemTypeHeader = "strItemType"; //look for anything in the loot list after the : if(parseItem.length == 2) { strItemTypeHeader = parseItem[1]; } if(!dataTableOpen(strItemTable)) { //LOG("loot", "BAD TABLE "+strItemTable+" in "+strTable); } else { //we are in the bottom table at this point string[] strItems = dataTableGetStringColumnNoDefaults(strItemTable, strItemTypeHeader); if((strItems!=null)||(strItems.length>0)) { string strLootToMake = strItems[rand(0, strItems.length-1)]; //LOG("loot", "Making item "+strLootToMake+" pulled from table "+strItemTable); createLootItem(objContainer, strLootToMake, intLevel); boolMadeLoot = true; } else { //LOG("loot", "BAD DATA IN TABLE "+strTable); } } } if(strRequiredItems != null && strRequiredItems.length > 0) { for(int intI = 0; intI-1) { // template, overload, then return obj_id lootItem = createObject(strLootToMake, objContainer, ""); int got = getGameObjectType(lootItem); if( isGameObjectTypeOf(got, GOT_clothing) || (got == GOT_armor_foot) || (got == GOT_armor_hand)) { hue.hueObject(lootItem); } if( isGameObjectTypeOf (got, GOT_weapon)) { // Randomize weapon values randomizeWeapon(lootItem, intLevel); } if( got >= GOT_armor && got <= GOT_armor_arm) { // Randomize armor values randomizeArmor(lootItem, intLevel); } if(got == GOT_misc_pharmaceutical) { // Randomize medicine values randomizeMedicine(lootItem, intLevel); } if((got >= GOT_component && got <= GOT_component_weapon_ranged) || (got >= GOT_armor_layer && got <= GOT_armor_core) ||(got == GOT_component_new_armor)) { randomizeComponent(lootItem, intLevel, objContainer); } if(utils.hasScriptVar(objContainer, "theft_in_progress") ) notifyThiefOfItemStolen(objContainer, lootItem); return lootItem; // Yeah this sucks, but we need to maintain this shit :( } else if(strLootToMake.startsWith("loot_items/")) { string[] parseItem = split(strLootToMake, ':'); if ( parseItem.length == 2 ) { string itemDatatable = "datatables/loot/"+parseItem[0]+".iff"; string lootColumnHeader = parseItem[1]; if(!dataTableOpen(itemDatatable)) { //LOG("loot", "BAD TABLE "+strItemTable+" in "+strTable); } else { string[] lootList = dataTableGetStringColumnNoDefaults(itemDatatable, lootColumnHeader); if( lootList != null && lootList.length > 0 ) { string lootItemsLoot = lootList[rand(0, lootList.length-1)]; return createLootItem(objContainer, lootItemsLoot, intLevel); } else { //LOG("loot", "BAD DATA IN TABLE "+strTable); } } } } else if(strLootToMake.startsWith("dynamic_")) { // magic item // make me a magical item! static_item.makeDynamicObject(strLootToMake, objContainer, intLevel); } else if(strLootToMake.startsWith("resource")) { utils.removeScriptVar(objContainer, "theft_in_progress"); } else { // static item obj_id lootItem = static_item.createNewItemFunction(strLootToMake, objContainer); if(utils.hasScriptVar(objContainer, "theft_in_progress") ) notifyThiefOfItemStolen(objContainer, lootItem); return lootItem; } return null; } boolean addMilkOrEgg(obj_id objCreature) { if (rand(1,100) > 20) return false; // Only NPCs go shopping for groceries. string mobType = ai_lib.getCreatureName(objCreature); int niche = ai_lib.aiGetNiche(mobType); if (niche != NICHE_NPC) return false; boolean groceries = true; string [] groceryItems = {"milk_domesticated", "milk_wild", "meat_egg", "meat_egg"}; obj_id objContainer = utils.getInventoryContainer(objCreature); int randItem = rand(0, 3); location here = getLocation(objCreature); int level = getLevel(objCreature); int amount = (int)(rand(1, 20) + (level*rand(1.0f, 2.0f) ) ); resource.createRandom(groceryItems[randItem], amount, here, objContainer); return groceries; } void notifyThiefOfItemStolen(obj_id objContainer, obj_id loot) { // LOG("mine", "arrived in notifyThiefofItemStolen"); if(utils.hasScriptVar(objContainer, "theft_in_progress" ) ) { // LOG("mine", "Has script var"); obj_id thief = getContainedBy(objContainer); if(isPlayer(thief) ) { // LOG("mine", "if thief: " + thief); prose_package pp = new prose_package(); pp = prose.setStringId(pp, new string_id("spam", "stolen_item")); pp = prose.setTT(pp, loot); sendSystemMessageProse(thief, pp); } utils.removeScriptVar(objContainer, "theft_in_progress"); } return; } boolean addBeastEnzymes (obj_id creature) { if ( storyteller.isStorytellerNpc(creature) ) { return false; } //determine if creature will drop enzyme int chanceSpawn = rand(1,100); if(chanceSpawn >= BASE_CHANCE_FOR_ENZYME && !hasObjVar(creature, "qa.makeEnzyme")) { // you dont get one. return false; } //get random enzyme type int type = 1; //right now you can only harvest one type of Enzyme from looting //leaving this in so we can add enzyme types later. //we would just make a randome roll for the type. //createEnzyme obj_id inv = utils.getInventoryContainer(creature); obj_id enzyme = createObject("object/tangible/loot/beast/enzyme_"+type+".iff", inv, ""); int level = ai_lib.getLevel(creature); if(isIdValid(enzyme) && exists(enzyme)) { //enzyme made. setObjVar(enzyme, "beast.enzyme.level", level); return true; } else { //enzyme wasnt made, error somewhere return false; } } boolean addChronicleLoot(obj_id target) { string creatureName = ai_lib.getCreatureName(target); if( creatureName == null || creatureName.length() < 1) { return false; } dictionary creatureRow = dataTableGetRow(CREATURES_TABLE, creatureName); int mobLevel = getIntObjVar(target, "intCombatDifficulty"); obj_id mobInv = utils.getInventoryContainer(target); int chronicleLootChance = creatureRow.getInt("chronicleLootChance"); string configChance_string = getConfigSetting("GameServer", "chroniclesLootChanceOverride"); if ( configChance_string != null && configChance_string.length() > 0 ) { int configChance = utils.stringToInt(configChance_string); if ( configChance > 0 ) { chronicleLootChance = configChance; } } int chronicleLootRoll = rand(1, 100); if ( chronicleLootRoll <= chronicleLootChance ) { string lootCatergoryChoice = ""; int relicCount = rand(1, 3); string chronicleLootData = creatureRow.getString("chronicleLootCategory"); if ( chronicleLootData != null && chronicleLootData.length() > 0 && !chronicleLootData.equals("no_loot") ) { string[] rawChronicleLootCategories = split(chronicleLootData, ','); resizeable string[] chronicleLootCategories = new string[0]; for ( int i = 0; i < rawChronicleLootCategories.length; i++ ) { if ( !rawChronicleLootCategories[i].equals("rare") ) { utils.addElement(chronicleLootCategories, rawChronicleLootCategories[i]); } } int lootOptions = chronicleLootCategories.length; int rareLootRoll = rand(1,100); if ( rareLootRoll == 1 ) { lootCatergoryChoice = "rare"; } else if ( rareLootRoll > 1 && rareLootRoll < 6 ) { lootCatergoryChoice = "comm_message"; relicCount = rand(6, 9); } else if ( rareLootRoll > 6 && rareLootRoll < 10 ) { lootCatergoryChoice = "goto_task"; relicCount = rand(6, 9); } else { int lootCategoryChoiceRoll = rand(0, lootOptions-1); lootCatergoryChoice = chronicleLootCategories[lootCategoryChoiceRoll]; } } else { //location here = getLocation(target); //lootCatergoryChoice = here.area; } if ( lootCatergoryChoice.length() > 0 ) { string relicName = getChronicleRelicLootOfCategory(lootCatergoryChoice); if ( relicName != null && relicName.length() > 0 ) { obj_id relic = static_item.createNewItemFunction(relicName, mobInv, relicCount); // CS Log pgc_quests.logRelic(obj_id.NULL_ID, obj_id.NULL_ID, "Chronicles Relic loot created on creature=("+target+","+creatureName+","+getName(target)+"); relic=("+relicName+", count:"+relicCount+")."); return true; } } } // Fragment Loot int fragmentLootChance = pgc_quests.PGC_CHRONICLE_BASE_FRAGEMENT_CHANCE; string configFragChance_string = getConfigSetting("GameServer", "fragmentLootChanceOverride"); if ( configFragChance_string != null && configFragChance_string.length() > 0 ) { int configFragChance = utils.stringToInt(configFragChance_string); if ( configFragChance > 0 ) { fragmentLootChance = configFragChance; } } int fragmentLootRoll = rand(1, 100); if ( fragmentLootRoll <= fragmentLootChance ) { int fragmentCount = 1 + rand(0, mobLevel/10); obj_id fragment = static_item.createNewItemFunction(pgc_quests.PGC_CHRONICLES_RELIC_FRAGMENT, mobInv, fragmentCount); // CS Log pgc_quests.logRelic(obj_id.NULL_ID, obj_id.NULL_ID, "Chronicles Relic Fragment loot created on creature=("+target+","+creatureName+","+getName(target)+"); fragment="+pgc_quests.PGC_CHRONICLES_RELIC_FRAGMENT+", count:"+fragmentCount+")."); return true; } return false; } string getChronicleRelicLootOfCategory(string lootCatergoryChoice) { string relic = ""; resizeable string[] tokenReferences = new string[0]; int num_items = dataTableGetNumRows(storyteller.STORYTELLER_DATATABLE); for (int i = 0; i < num_items; i++) { dictionary row = dataTableGetRow(storyteller.STORYTELLER_DATATABLE, i); if ( row != null && !row.isEmpty() ) { string row_lootCategory = row.getString("relicLootCatergory"); string[] relicLootCategories = split(row_lootCategory, ','); for ( int j = 0; j < relicLootCategories.length; j++ ) { string relicCategory = relicLootCategories[j]; if ( relicCategory.equals(lootCatergoryChoice) ) { string token_reference = row.getString("name"); tokenReferences = utils.addElement(tokenReferences, token_reference); } } } } if ( tokenReferences!= null && tokenReferences.length > 0 ) { relic = tokenReferences[rand(0, tokenReferences.length-1)]; } // giving generic goto a nudge if ( lootCatergoryChoice.equals("goto_task") ) { int genericChance = rand(1,3); if ( genericChance == 3 ) { relic = pgc_quests.PGC_RELIC_OBJ_GOTO_GENERIC_LOC; } } return relic; } obj_id chroniclesCraftingLootDrop(obj_id player) { if ( hasToggledChroniclesLootOff(player) ) { return obj_id.NULL_ID; } return chroniclesNonCorpseLootDrop(player, "crafting", pgc_quests.PGC_CHRONICLE_BASE_CRAFTING_LOOT_CHANCE, "crafting"); } obj_id chroniclesPvpLootDrop(obj_id player) { if ( hasToggledChroniclesLootOff(player) ) { return obj_id.NULL_ID; } return chroniclesNonCorpseLootDrop(player, "pvp", pgc_quests.PGC_CHRONICLE_BASE_PVP_LOOT_CHANCE, "pvping"); } obj_id chroniclesNonCorpseLootDrop(obj_id player, string relicCategory, int relicChance, string activityType) { string configChance_string = getConfigSetting("GameServer", "chroniclesLootChanceOverride"); if ( configChance_string != null && configChance_string.length() > 0 ) { int configChance = utils.stringToInt(configChance_string); if ( configChance > 0 ) { relicChance = configChance; } } int lootRoll = rand(1, 100); if ( lootRoll <= relicChance ) { string relicName = getChronicleRelicLootOfCategory(relicCategory); if ( relicName != null && relicName.length() > 0 ) { int relicCount = rand(1, 3); obj_id playerInv = utils.getInventoryContainer(player); obj_id relic = static_item.createNewItemFunction(relicName, playerInv, relicCount); if ( isIdValid(relic) ) { pgc_quests.sendPlacedInInventorySystemMessage(player, relic); // CS Log pgc_quests.logRelic(player, obj_id.NULL_ID, "Player looted a Chronicles Relic while "+activityType+": relic name:"+relicName+" and count: "+relicCount); } return relic; } } return obj_id.NULL_ID; } boolean hasToggledChroniclesLootOff(obj_id player) { return hasObjVar(player, CHRONICLES_LOOT_TOGGLE_OBJVAR); } void disableChroniclesLoot(obj_id player) { setObjVar(player, CHRONICLES_LOOT_TOGGLE_OBJVAR, true); return; } void enableChroniclesLoot(obj_id player) { removeObjVar(player, CHRONICLES_LOOT_TOGGLE_OBJVAR); return; } boolean addCollectionLoot (obj_id target) { return addCollectionLoot(target, false, null); } /*Determines if the player will get a collection item after killing a mob*/ boolean addCollectionLoot (obj_id target, boolean theftBool, obj_id thief) // target == creature killed { string creatureName = ai_lib.getCreatureName(target); // get creatureName //LOG("collectionLoot", "this is the creatureName "+creatureName); int collectionLootChance = rand(1, 100); // roll for collection loot //int rollBaseChance = dataTableGetInt(CREATURES_TABLE, creatureName, "collectionRoll"); dictionary creatureRow = dataTableGetRow(CREATURES_TABLE, creatureName); // get creature Row from creatures.tab int rollBaseChance = creatureRow.getInt("collectionRoll"); // get roll base chance from table /**** rollBaseChance can be altered via **** **** COL_LOOT_MULTIPLIER_ON to increase **** ****** drop rate percentages globally *****/ rollBaseChance *= COL_LOOT_MULTIPLIER_ON; if(creatureRow == null || creatureRow.isEmpty()) { //LOG("collectionLoot", "creatureRow == null || creatureRow.isEmpty()"); return false; } if(collectionLootChance <= rollBaseChance) { //LOG("collectionLoot", "you got collectionLoot, you rolled " + collectionLootChance + "."); string myCollectionLoot = creatureRow.getString("collectionLoot"); if (myCollectionLoot.equals("no_loot") || myCollectionLoot == null) { // LOG("collectionLoot", "bah - this mob had no collection loot available. " + myCollectionLoot + "."); return false; } else // Yay! I get collection loot! { //test here for multiple collections in "collectionLoot" and choose 1 string[] collectionsInColumn = split(myCollectionLoot, ','); int collectionIndex = rand(0, (collectionsInColumn.length - 1)); myCollectionLoot = collectionsInColumn[collectionIndex]; string[] lootArray = dataTableGetStringColumnNoDefaults(COLLECTIONS_LOOT_TABLE, myCollectionLoot); // get loot list from column //LOG("collectionLoot", "arrayLength = " + lootArray.length); int max = lootArray.length -1; //LOG("collectionLoot", "maxLogged = " + max); int idx = rand(0, max); //LOG("collectionLoot", "INDEX = " + idx); string lootToGrant = lootArray[idx]; // pick the random item to create //LOG("collectionLoot", "This item was pulled from the collectionLoot Datatable -- " + lootToGrant + " -- " + myCollectionLoot); //Added to allow spies a chance to steal colleciton items. if(theftBool) { if(isIdValid(thief)) { obj_id thiefInv = utils.getInventoryContainer(thief); utils.setScriptVar(thiefInv, "theft_in_progress", 1); obj_id lootItem = static_item.createNewItemFunction(lootToGrant, thiefInv); notifyThiefOfItemStolen(thiefInv, lootItem); return true; } return false; } obj_id mobInv = utils.getInventoryContainer(target); //inventory of the creature obj_id lootItem = static_item.createNewItemFunction(lootToGrant, mobInv); // create item and place in mob's inventory if(isIdValid(lootItem) && exists(lootItem)) { //LOG("collectionLoot", "Loot isValid - " + lootItem + " - logging"); CustomerServiceLog("CollectionLootChannel: ", "LootSuccessful: " + creatureName + "(" + target + ")" + " successfully dropped: " + lootToGrant + "(" + lootItem + ")"); return true; } else { CustomerServiceLog("CollectionLootChannel: ", "BrokenLoot: " + creatureName + "(" + target + ")" + " is having an issue dropping: " + lootToGrant + " Invalid ID or Does not exist." ); //LOG("collectionLoot", "Item not created - " + lootItem + " - logging"); return false; } } } else if(collectionLootChance > rollBaseChance) { // LOG("collectionLoot", "you didn't receive collectionLoot, you rolled " + collectionLootChance + "."); // get collectionLoot Column from creatures.tab for 'name' of creature return false; } else { // LOG("collectionLoot", "roll failure - something bad happened."); return false; } } //Finds all columns prefixed with the faction passed. Selects one //of the columns at random and then selects one of the reward static //items in said column. Returns the static item to calling function. string getFactionalCollectionItem(string faction) { blog("getFactionalCollectionItem - arrival in function. Faction of winner is: "+faction); if(faction == null || faction.equals("")) return null; blog("getFactionalCollectionItem - Faction check 1: "+faction+"*"); if(!faction.equals("Rebel") && !faction.equals("Imperial")) return null; blog("getFactionalCollectionItem - Faction check 2"); resizeable string[] factionCols = new string[0]; int randColInt = 0; string randColStr = ""; string[] allLootColumns = dataTableGetColumnNames(COLLECTIONS_PVP_LOOT_TABLE); if(allLootColumns == null || allLootColumns.length <= 0 ) return null; blog("getFactionalCollectionItem - allLootColumns.length: "+allLootColumns.length); for(int i = 0; i < allLootColumns.length; i++) { if(allLootColumns[i].startsWith(toLower(faction))) { blog("getFactionalCollectionItem - found a column I am adding to col list"); utils.addElement(factionCols, allLootColumns[i]); } } blog("getFactionalCollectionItem - factionCols.length: "+factionCols.length); if(factionCols.length <= 0) return null; if(factionCols.length == 1) { blog("getFactionalCollectionItem - randColStr: "+factionCols[randColInt]); randColStr = factionCols[randColInt]; } else { randColInt = rand(0, factionCols.length-1); blog("getFactionalCollectionItem - randColInt: "+randColInt); randColStr = factionCols[randColInt]; blog("getFactionalCollectionItem - randColStr: "+randColStr); } if(randColStr == null || randColStr.equals("")) return null; string[] colLoot = dataTableGetStringColumnNoDefaults(COLLECTIONS_PVP_LOOT_TABLE, randColStr); if(colLoot == null || colLoot.length <= 0 ) return null; blog("getFactionalCollectionItem - colLoot.length: "+colLoot.length); if(colLoot.length == 1) { return colLoot[0]; } int randLootInt = rand(0, colLoot.length-1); blog("getFactionalCollectionItem - randLootInt: "+randLootInt); blog("getFactionalCollectionItem - RETURNING: "+colLoot[randLootInt]); return colLoot[randLootInt]; } //Finds all columns prefixed with the specified pvp faction col prefix. Selects one //of the columns at random and then selects the appropriate row based on the rank passed. string getAppropriatePvpRankCollectible(string faction, int rank) { blog("getAppropriatePvpRankCollectible - arrival in function. Faction: "+faction+" rank: "+rank); if(faction == null || faction.equals("")) return null; blog("getAppropriatePvpRankCollectible - Faction checked 1: "+faction+"*"); if(!faction.equals("Rebel") && !faction.equals("Imperial")) return null; blog("getAppropriatePvpRankCollectible - Faction checked 2"); //faction lieutenant = rank 6 and general = 12 if(rank < 6 || rank > 12) return null; blog("getFactionalCollectionItem - faction: "+faction); blog("getFactionalCollectionItem - faction: "+rank); resizeable string[] pvpCols = new string[0]; int randColInt = 0; string randColStr = ""; string[] allLootColumns = dataTableGetColumnNames(COLLECTIONS_PVP_RANK_TABLE); if(allLootColumns == null || allLootColumns.length <= 0 ) return null; blog("getFactionalCollectionItem - allLootColumns.length: "+allLootColumns.length); for(int i = 0; i < allLootColumns.length; i++) { if(allLootColumns[i].startsWith(toLower(faction))) { blog("getFactionalCollectionItem - found a column I wanted: "); utils.addElement(pvpCols, allLootColumns[i]); } } if(pvpCols.length <= 0) return null; blog("getFactionalCollectionItem - pvpCols.length: "+pvpCols.length); if(pvpCols.length == 1) { randColStr = pvpCols[randColInt]; } else { randColInt = rand(0, pvpCols.length-1); randColStr = pvpCols[randColInt]; } blog("getFactionalCollectionItem - pvpCols[randColInt]: "+pvpCols[randColInt]); blog("getFactionalCollectionItem - randColStr: "+randColStr); //figure out which row, return static item in that row switch(rank) { //in the event that enlisted rank is added, the cases will need to be expanded here. //The table will need to have enlisted rank in order before the officer for this to //work. case 7: //Lieutenant return dataTableGetString(COLLECTIONS_PVP_RANK_TABLE, 0, randColStr); case 8: //Captain return dataTableGetString(COLLECTIONS_PVP_RANK_TABLE, 1, randColStr); case 9: //Major return dataTableGetString(COLLECTIONS_PVP_RANK_TABLE, 2, randColStr); case 10: //Lt. Colonel return dataTableGetString(COLLECTIONS_PVP_RANK_TABLE, 3, randColStr); case 11: //Colonel return dataTableGetString(COLLECTIONS_PVP_RANK_TABLE, 4, randColStr); case 12: //General return dataTableGetString(COLLECTIONS_PVP_RANK_TABLE, 5, randColStr); default: return null; } } //This is called directly from the gcw library distributeIndividualContribution function. //At this point we know the winning player has killed an opposing faction victim and that //winning player gave X% of the damage. boolean rollRandomFactionalCollectible(obj_id victim, obj_id winner, int victimRank) { blog("rollRandomFactionalCollectible - arrival in rollRandomFactionalCollectible function"); if(isIdNull(winner) || isIdNull(victim)) return false; if(victimRank <= 0) return false; blog("rollRandomFactionalCollectible - about to roll."); int roll = rand(1,100); int rollNeeded = COLLECTION_PVP_ROLL; if(isGod(winner)) rollNeeded = 101; if(roll <= rollNeeded) return false; blog("rollRandomFactionalCollectible - won roll."); //player has won roll decide which function the winner //is going to pull reward from. Default is function 1. int rewardFunction = 1; if(victimRank > 6) { //currently there are 2 different functions //one for rank, and one for general factional //rewards. This decides between the 2. rewardFunction = rand(1,2); blog("rollRandomFactionalCollectible - victim is officer."); } string faction = factions.getFaction(victim); string rewardItem = ""; switch(rewardFunction) { case 1: blog("rollRandomFactionalCollectible - send to getFactionalCollectionItem."); rewardItem = getFactionalCollectionItem(faction); break; case 2: blog("rollRandomFactionalCollectible - send to getAppropriatePvpRankCollectible."); rewardItem = getAppropriatePvpRankCollectible(faction, victimRank); break; } if(rewardItem == null || rewardItem.equals("")) { blog("rollRandomFactionalCollectible - reward function returned a NULL."); return false; } blog("rollRandomFactionalCollectible - grant reward."); grantFactionalCollectible(winner, rewardItem); return true; } boolean grantFactionalCollectible(obj_id winner, string staticItem) { blog("grantFactionalCollectible - arrival in grantFactionalCollectible function"); if(isIdNull(winner)) return false; if(staticItem == null || staticItem.equals("")) return false; if(!static_item.isStaticItem(staticItem)) return false; obj_id pInv = utils.getInventoryContainer(winner); obj_id itemId = static_item.createNewItemFunction(staticItem, pInv); if(!isValidId(itemId) || !exists(itemId)) { blog("grantFactionalCollectible - FAILED TO CREATE ITEM IN INVENTORY."); CustomerServiceLog("CollectionLootChannel", "Player " + getFirstName(winner) + "(" + winner + ") has failed to receive item: " + staticItem + "."); return false; } //just in case we want to add multiple calls for more than //one loot item resizeable obj_id[] items = new string[0]; utils.addElement(items, itemId); blog("grantFactionalCollectible - SUCCESS, creating item in inventory."); prose_package pp = new prose_package(); prose.setStringId(pp, SID_REWARD_ITEM); prose.setTT(pp, new string_id("static_item_n", getStaticItemName(itemId))); sendSystemMessageProse(winner, pp); CustomerServiceLog("CollectionLootChannel", "Player " + getFirstName(winner) + "(" + winner + ") has received item: " + staticItem + "."); //players don't want a popup in PvP /* obj_id[] finalLootList = new obj_id[items.size()]; items.toArray(finalLootList); showLootBox(winner, finalLootList); */ return true; } //Adds lumps to player invetory obj_id addMeatlumpLumpsAsLoot(obj_id target, obj_id targetInventory, int cash) { if(!isValidId(target) || !exists(target)) return null; else if(!isValidId(targetInventory) || !exists(targetInventory)) return null; else if (cash < 1) return null; obj_id inv = utils.getInventoryContainer(target); if(inv == null) return null; obj_id cashItem = static_item.createNewItemFunction(MEATLUMP_LUMP, inv); if (!isIdValid(cashItem)) return null; incrementCount(cashItem, cash); return cashItem; } //Gives Random Loot from the Meatlump Loot Table //Rare loot is attained by being lucky, having a luck modifier, having buffs and completing the puzzle without errors. boolean giveMeatlumpPuzzleLoot(obj_id player, boolean puzzleThreshold, boolean puzzleBuff) { if(!isValidId(player) || !exists(player)) return false; obj_id pInv = utils.getInventoryContainer(player); if(!isValidId(pInv) || !exists(pInv)) return false; //throw luck into the mix boolean luckyPlayer = luck.isLucky(player, 0.01f); //give meatlump lumps int lumpAmount = rand(0,2); blog("giveMeatlumpPuzzleLoot - lumpAmount: "+lumpAmount); if(luckyPlayer) { int lumpRoll = rand(0,100); if(lumpRoll < 51) lumpAmount++; else if(lumpRoll > 51 && lumpRoll < 90) lumpAmount += 2; else lumpAmount += 3; } resizeable obj_id[] items = new string[0]; blog("giveMeatlumpPuzzleLoot - lucky player lumpAmount: "+lumpAmount); if(lumpAmount > 0) { obj_id lumpsReceived = addMeatlumpLumpsAsLoot(player, pInv, lumpAmount); blog("giveMeatlumpPuzzleLoot - lumpsReceived: "+lumpsReceived); if(lumpsReceived == null) return false; utils.addElement(items, lumpsReceived); } int lootItemAmount = rand(1,3); if(rand(0,100) > 99) //if the player rolls good they have a chance to up their score. lootItemAmount += rand(0,1); if(luckyPlayer)// if the player is lucky they have a chance at modifying the loot score. lootItemAmount += rand(0,1); else if(puzzleThreshold) if(rand(0,100) > 95) //if the player finishes the puzzle with an exceptional score, they have a slight chance at modifying the loot. lootItemAmount += rand(0,1); else if(puzzleBuff) lootItemAmount += rand(0,1); for(int i = 0; i < lootItemAmount; i++) { int cols = 1; int randCol = rand(0,100); if(randCol > 71 && randCol < 90) cols = 3; else if(randCol > 91) cols = 5; blog("giveMeatlumpPuzzleLoot - randCols: "+randCol); blog("giveMeatlumpPuzzleLoot - TOTAL cols: "+cols); string[] strLootItems = dataTableGetStringColumnNoDefaults(MEATLUMP_LOOT_TABLE, rand(0,cols)); if((strLootItems == null)||(strLootItems.length < 0)) return false; blog("giveMeatlumpPuzzleLoot - strLootItems.length: "+strLootItems.length); blog("giveMeatlumpPuzzleLoot - strLootItems.length: "+strLootItems.length); //get a random loot item from the random col string strLootToMake = strLootItems[rand(0, strLootItems.length-1)]; if(strLootToMake == null || strLootToMake.equals("")) return false; blog("giveMeatlumpPuzzleLoot - strLootToMake: "+strLootToMake); obj_id lootItem = createLootItem(pInv, strLootToMake, 0); if(!isValidId(lootItem)) return false; utils.addElement(items, lootItem); blog("giveMeatlumpPuzzleLoot - createLootItem success"); } blog("giveMeatlumpPuzzleLoot - items.size(): "+items.size()); obj_id[] finalLootList = new obj_id[items.size()]; items.toArray(finalLootList); showLootBox(player, finalLootList); return true; } boolean playerForaging(obj_id player) { if(!isValidId(player) || !exists(player)) { CustomerServiceLog("foraging", "Foraging System could not complete because player OID: "+player+ " is invalid or no longer exists."); return false; } CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " is about to start foraging rolls."); obj_id pInv = utils.getInventoryContainer(player); if(!isValidId(pInv) || !exists(pInv)) { CustomerServiceLog("foraging", "Foraging System could not find inventory for player OID: "+player+ ". Inventory container is invalid or no longer exists."); return false; } if(getVolumeFree(pInv) <= 0) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has a full inventory, foraging rolls aborted."); sendSystemMessage(player, SID_FULL_INVENTORY); return false; } //get players current location location curLoc = getLocation(player); string scene = getCurrentSceneName(); forage_blog("scene: "+ scene); //create a resizable array of locations resizeable location[] newListOfLocs = new location[0]; //THERE IS A 50% CHANCE OF GETTING SOMETHING BY DEFAULT int something = 50; int nothing = 50; int somethingMod = 0; int lootMod = 0; string bonusMessage = ""; //looking for the initial roll modifiers if(buff.hasBuff(player, "bm_truffle_pig")) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has buff bm_truffle_pig and is receiving bonus to avoid getting nothing during forage session."); float trufflePigVal = buff.getEffectValue("bm_truffle_pig", 2); bonusMessage += " Pet"; forage_blog("playerForaging: bm_truffle_pig increased somethingMod from: "+somethingMod+ " to: " + (somethingMod+trufflePigVal)); somethingMod += (int)trufflePigVal; } if(buff.hasBuff(player, "ice_cream_forage_buff")) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has buff ice_cream_forage_buff and is receiving bonus to avoid getting nothing during forage session."); float iceCreamVal = buff.getEffectValue("ice_cream_forage_buff", 2); if(bonusMessage.equals("")) bonusMessage += " Ice Cream"; else bonusMessage += ", Ice Cream"; forage_blog("playerForaging: ice_cream_forage_buff increased somethingMod from: "+somethingMod+ " to: " + (somethingMod+iceCreamVal)); somethingMod += (int)iceCreamVal; } if(buff.hasBuff(player, "treasure_forage")) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has buff treasure_forage and is receiving bonus to avoid getting nothing during forage session."); float treasureVal = buff.getEffectValue("treasure_forage", 1); if(bonusMessage.equals("")) bonusMessage += " Kommerken Steak"; else bonusMessage += ", Kommerken Steak"; forage_blog("playerForaging: treasure_forage increased somethingMod from: "+somethingMod+ " to: " + (somethingMod+treasureVal)); somethingMod += (int)treasureVal; } boolean isLuckyPlayer = luck.isLucky(player, 0.10f); if(isLuckyPlayer) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " got really lucking and is receiving bonus to avoid getting nothing during forage session."); if(bonusMessage.equals("")) bonusMessage += " Luck"; else bonusMessage += ", Luck"; forage_blog("playerForaging: isLuckyPlayer increased somethingMod from: "+somethingMod+ " to: " + (somethingMod+LUCKY_FIND_MODIFIER)); somethingMod += (int)LUCKY_FIND_MODIFIER; } if(!bonusMessage.equals("")) { prose_package pp = new prose_package(); string_id msg = new string_id (FORAGING_STF, "forage_bonus_mesage"); pp = prose.setStringId(pp, msg); pp = prose.setTO(pp, bonusMessage); sendSystemMessageProse (player, pp); } if(somethingMod > 0) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has one or more buffs and/or luck modifiers. The forage system is modifying the players chance of receiving nothing."); nothing -= somethingMod; if(nothing < 0) nothing = 0; something += somethingMod; if(something > 100) something = 100; forage_blog("playerForaging: Nothing: "+nothing); forage_blog("playerForaging: Something: "+something); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has a "+nothing+"% chance of receiving nothing within the foraging system."); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has a "+something+"% chance of receiving something within the foraging system."); } int initialRoll = rand(1,100); if(initialRoll <= 0) initialRoll = 0; forage_blog("playerForaging: initialRoll: "+initialRoll); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has rolled a "+initialRoll+". If this number is less than "+nothing+" the player gets nothing from the foraging system."); //THE PLAYER GETS NOTHING...END OF FORAGING SESSION if(initialRoll <= nothing) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has rolled a "+initialRoll+" and gets nothing from the foraging system."); forage_blog("YOU ROLLED: " + initialRoll + " on initial roll and get nothing. You needed to get higher than: "+nothing); //didnt find anything, let player know. sendSystemMessage(player, FOUND_NOTHING); //set this location as my last place to attempt a forage. saveForageLocationOnPlayer(player, newListOfLocs, curLoc); return true; } CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has rolled a "+initialRoll+" and gets some yet to be determined item from the foraging system."); forage_blog("playerForaging: YOU GET SOMETHING"); int enzymeMod = 0; int treasMod = 0; string buffMsg = ""; //looking for the Buffs if(buff.hasBuff(player,"bm_truffle_pig")) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has the bm_truffle_pig buff and will receive an extra roll for ENZYMES within the foraging system."); float enzymeBuff = buff.getEffectValue("bm_truffle_pig", 3); //value is 25 forage_blog("playerForaging: bm_truffle_pig increased enzymeMod score from: "+enzymeMod+ " to: " + (enzymeMod+enzymeBuff)); enzymeMod += (int)enzymeBuff; //the player now has 55% chance of getting enzyme } if(buff.hasBuff(player, "treasure_forage")) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has the treasure_forage buff and will receive an extra roll for TREASURE MAPS within the foraging system."); //forage_blog("playerForaging: ice_cream_forage_buff increased treasMod from: "+treasMod+ " to: " + (treasMod+treasureVal)); float treasureValMap = buff.getEffectValue("treasure_forage", 2); //value is 10 treasMod += (int)treasureValMap; //the player now has 12% chance of getting treasure map } boolean buffRoll = false; if(enzymeMod > 0) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " is NOW receiving their extra roll for ENZYMES within the foraging system."); forage_blog("Player receives extra roll because of buffs for: enzyme"); buffMsg += " Plant Enzymes"; int enzymRoll = rand(1,100); if(enzymRoll <= 0) enzymRoll = 0; CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+enzymRoll+". If this number is less than, or equal to "+enzymeMod+", the player will get an enzyme."); //THE PLAYER GETS ENZYMES...THE END if(enzymRoll <= enzymeMod) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+enzymRoll+" and received an enzyme."); forage_blog("rareObject reward: enzyme"); obj_id enzyme = createObject("object/tangible/loot/beast/enzyme_2.iff", pInv, ""); sendSystemMessage(player, FOUND_ENZYME); saveForageLocationOnPlayer(player, newListOfLocs, curLoc); buffRoll = true; } } if(treasMod > 0) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " is NOW receiving their extra roll for TREASURE MAPS within the foraging system."); forage_blog("Player receives extra roll because of buffs for: treasure map"); if(buffMsg.equals("")) buffMsg += " Treasure Map"; else buffMsg += ", Treasure Map"; int treasureRoll = rand(1,100); if(treasureRoll <= 0) treasureRoll = 0; CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+treasureRoll+". If this number is less than, or equal to "+treasMod+", the player will get an enzyme."); //THE PLAYER GETS TREASURE...THE END if(treasureRoll <= treasMod) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+treasureRoll+" and received a treasure map."); forage_blog("rareObject reward: treasure"); boolean objectReceived = loot.getRareForagedTreasureMap(player, pInv); sendSystemMessage(player, FOUND_TREASURE); saveForageLocationOnPlayer(player, newListOfLocs, curLoc); buffRoll = true; } } if(!buffMsg.equals("")) { prose_package pp = new prose_package(); string_id msg = new string_id (FORAGING_STF, "forage_buff_roll_mesage"); pp = prose.setStringId(pp, msg); pp = prose.setTO(pp, buffMsg); sendSystemMessageProse (player, pp); } if(buffRoll) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has received either an ENZYME AND/OR a TREASURE MAP because of their buffs. The forage system is now exiting."); return true; } CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has not received an enzyme or treasure map as a result of a buf so we will continue through the forage system."); forage_blog("Player moves on to get basic rolls"); int enzLow = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_ENZYME, LOOT_LOW_COL); int enzHigh = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_ENZYME, LOOT_HIGH_COL); int compLow = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_COMPONENT, LOOT_LOW_COL); int compHigh = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_COMPONENT, LOOT_HIGH_COL); int wormLow = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_WORM, LOOT_LOW_COL); int wormHigh = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_WORM, LOOT_HIGH_COL); int baitLow = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_BAIT, LOOT_LOW_COL); int baitHigh = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_BAIT, LOOT_HIGH_COL); int treasLow = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_TREASURE, LOOT_LOW_COL); int treasHigh = dataTableGetInt(FORAGING_LOOT_ROLL_TABLE, LOOT_TREASURE, LOOT_HIGH_COL); if(enzLow < 0 || enzHigh < 0 || compLow < 0 || compHigh < 0 || wormLow < 0 || wormHigh < 0 || baitLow < 0 || baitHigh < 0 || treasLow < 0 || treasHigh < 0) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has corrupted data for one or more of the foraging data. Contact development."); return false; } CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " will receive an enzyme if they roll between "+enzLow+" and "+enzHigh+"."); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " will receive a component (ice cream fryer food) if they roll between "+compLow+" and "+compHigh+"."); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " will receive a worm or theif enemy if they roll between "+wormLow+" and "+wormHigh+"."); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " will receive a bait or bugs if they roll between "+baitLow+" and "+baitHigh+"."); CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " will receive a treasure map if they roll between "+treasLow+" and "+treasHigh+"."); int lootRoll = rand(1,100); if(lootRoll <= 0) lootRoll = 0; CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+lootRoll+". The system will now attempt to reward the player with the specific item won."); forage_blog("YOU ROLLED: "+lootRoll); if(lootRoll >= enzLow && lootRoll <= enzHigh) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+lootRoll+" and has received an ENZYME."); forage_blog("rareObject reward: enzyme"); obj_id enzyme = createObject("object/tangible/loot/beast/enzyme_2.iff", pInv, ""); sendSystemMessage(player, FOUND_ENZYME); } else if(lootRoll >= compLow && lootRoll <= compHigh) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+lootRoll+" and has received a COMPONENT."); forage_blog("rareObject reward: component"); if(giveForagedCollectionObject(player, pInv, scene)) { sendSystemMessage(player, FOUND_COMPONENT); } } else if(lootRoll >= wormLow && lootRoll <= wormHigh) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+lootRoll+" and has received a WORM/THEIF ENEMY."); forage_blog("rareObject reward: worm"); //enemy level is the same level as player int mobLevel = getLevel(player); string invis = stealth.getInvisBuff(player); if(invis != null) stealth.checkForAndMakeVisibleNoRecourse(player); string[] enemyList = dataTableGetStringColumnNoDefaults(FORAGING_ENEMY_TABLE, "enemy"); int idx = rand(0, (enemyList.length-1)); obj_id mob = create.object(enemyList[idx], curLoc, mobLevel); setObjVar(mob, "player", player); attachScript(mob, FORAGE_ENEMY_SCRIPT); sendSystemMessage(player, FOUND_WORM); } else if(lootRoll >= treasLow && lootRoll <= treasHigh) { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+lootRoll+" and has received a TREASURE MAP."); forage_blog("rareObject reward: treasure map"); loot.getRareForagedTreasureMap(player, pInv); sendSystemMessage(player, FOUND_TREASURE); } else { CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " rolled "+lootRoll+" and has received BAIT."); forage_blog("reward: bait"); string[] bait = dataTableGetStringColumnNoDefaults(FORAGING_RARE_TABLE, "ITEM_TANGIBLE"); //pick the bait from datatable int itemIndex = rand(0, (bait.length-1)); //create the bait in thier inventory forage_blog("Won BAIT: object/tangible/bait"+itemIndex); createObjectOverloaded("object/tangible/"+bait[itemIndex], pInv); //let player know they got bait sendSystemMessage(player, FOUND_SOMETHING); } CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " should have received a reward object. The forage location will be stored and the forage session will be completed."); saveForageLocationOnPlayer(player, newListOfLocs, curLoc); return true; } //This function creates a list of rare foraged loot based on the int values passed. For instance, if enzymeChance = 90 and //RARE_COMPONENT_DIVISOR = 10 then 90/10 = 9 and the loop will add the enzyme category number to the resizeable int array 9 times. //This same process is done for all the rare foraged loot creating a long list of category numbers. A row in the list is selected //at random and returned as the rare loot category received by the player. int getRandomRareForagedLoot(obj_id self, int enzymeChance, int wormChance, int treasureMapChance, int componentChance) { forage_blog("getRandomRareForagedLoot init"); forage_blog("getRandomRareForagedLoot:treasureMapChance: "+treasureMapChance); resizeable int[] rareItemList = new int[0]; utils.addElement(rareItemList, ENZYME); utils.addElement(rareItemList, WORM); utils.addElement(rareItemList, TREASURE); utils.addElement(rareItemList, COMPONENT); forage_blog("getRandomRareForagedLoot:treasureMapChance: "+treasureMapChance); if(enzymeChance > 0) { int division = enzymeChance / RARE_COMPONENT_DIVISOR; for(int i = 0; i < division; ++i) utils.addElement(rareItemList, ENZYME); } if(wormChance > 0) { int division = wormChance / RARE_COMPONENT_DIVISOR; for(int i = 0; i < division; ++i) utils.addElement(rareItemList, WORM); } if(treasureMapChance > 0) { int division = treasureMapChance / SUPER_RARE_COMPONENT_DIVISOR; for(int i = 0; i < division; ++i) utils.addElement(rareItemList, TREASURE); } if(componentChance > 0) { int division = componentChance / RARE_COMPONENT_DIVISOR; for(int i = 0; i < division; ++i) utils.addElement(rareItemList, COMPONENT); } int listSize = rareItemList.size(); forage_blog("rareItemList.size(): "+ listSize); int randNum = rand(0,listSize-1); forage_blog("randNum: "+ randNum); forage_blog("Random return: "+ rareItemList[randNum]); CustomerServiceLog("foraging", "Player: "+getName(self)+" OID: "+self+ "rolled against a foraging rare item list and received rare loot item category: "+rareItemList[randNum]); forage_blog("getRandomRareForagedLoot do we have the scriptvar: "+utils.hasScriptVar(self, "qa.give_forage_data")); if(utils.hasScriptVar(self, "qa.give_forage_data") && isGod(self)) { string strForageFeedBack = utils.getStringScriptVar(self, "qa.strForageFeedBack"); strForageFeedBack += "Categories: Enzyme = 0, Worm/Thief = 1, Component = 2, Treasure Map = 3\n\r"; strForageFeedBack += "Rare Item List Length: " + listSize+"\n\r"; for(int i =0; i < listSize; ++i) { strForageFeedBack += "Rare Item Number "+i+" is category: " + rareItemList[i]+"\n\r"; } strForageFeedBack += "Winning Item Category: " + rareItemList[randNum]+"\n\r\n\r"; //check constants: strForageFeedBack += "Check Constants - The values should never change.\n\r"; strForageFeedBack += "DEFAULT_ENZYME_CHANCE: (40) "+DEFAULT_ENZYME_CHANCE+"\n\r"; strForageFeedBack += "DEFAULT_WORM_CHANCE: (40) "+DEFAULT_WORM_CHANCE+"\n\r"; strForageFeedBack += "DEFAULT_TREASURE_CHANCE: (0) "+DEFAULT_TREASURE_CHANCE+"\n\r"; strForageFeedBack += "DEFAULT_COMPONENT_CHANCE: (40) "+DEFAULT_COMPONENT_CHANCE+"\n\r"; strForageFeedBack += "ENZYME_MODIFIER: (0) "+ENZYME_MODIFIER+"\n\r"; strForageFeedBack += "TREASURE_MODIFIER: (0) "+TREASURE_MODIFIER+"\n\r"; strForageFeedBack += "PLAYER_DEFAULT_FIND_MODIFIER: (0) "+PLAYER_DEFAULT_FIND_MODIFIER+"\n\r"; strForageFeedBack += "LUCKY_FIND_MODIFIER: (6) "+LUCKY_FIND_MODIFIER+"\n\r"; utils.setScriptVar(self, "qa.strForageFeedBack", strForageFeedBack); utils.setScriptVar(self, "qa.strForageFeedBackCategory", rareItemList[randNum]); } return rareItemList[randNum]; } boolean getRareForagedTreasureMap(obj_id player, obj_id pInv) { forage_blog("getRareForagedTreasureMap init"); if(!isIdValid(player) || !exists(player)) return false; else if(!isIdValid(pInv) || !exists(pInv)) return false; CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " is rolling for a treasure map."); forage_blog("getRareForagedTreasureMap - rareObject reward: treasure map"); string treasureMap = getPlayerTreasureMapString(player); if(treasureMap != null && treasureMap != "") static_item.createNewItemFunction(treasureMap, pInv); else { forage_blog("getRareForagedTreasureMap - rareObject reward ERROR: treasure map level not found"); static_item.createNewItemFunction("item_treasure_map_1_10", pInv); } CustomerServiceLog("foraging", "Player: "+getName(player)+" OID: "+player+ " has won treasure map: "+treasureMap); forage_blog("getRareForagedTreasureMap do we have the scriptvar: "+utils.hasScriptVar(player, "qa.give_forage_data")); return true; } //Gets the appropriate treasure map based on player level string getPlayerTreasureMapString(obj_id player) { if (!isValidId(player)) return null; //the player combat level dictates the level of map they receive int playerLvl = getLevel(player); //return null means the player gets a level 1-10 if (playerLvl <= 0) return null; string mapPrefix = "item_treasure_map_"; string mapAppend = "1_10"; //if the player is not 1-10th lvl, overwrite the mapAppend //if the player is 1-10 or some failure happens, give them 1_10 if (playerLvl >= 81 && playerLvl <= 90) mapAppend = "81_90"; else if (playerLvl >= 71 && playerLvl <= 80) mapAppend = "71_80"; else if (playerLvl >= 61 && playerLvl <= 70) mapAppend = "61_70"; else if (playerLvl >= 51 && playerLvl <= 60) mapAppend = "51_60"; else if (playerLvl >= 41 && playerLvl <= 50) mapAppend = "41_50"; else if (playerLvl >= 31 && playerLvl <= 40) mapAppend = "31_40"; else if (playerLvl >= 21 && playerLvl <= 30) mapAppend = "21_30"; else if (playerLvl >= 11 && playerLvl <= 20) mapAppend = "11_20"; return mapPrefix + mapAppend; } //Fryer Rare Component Loot Drops from foraging boolean giveForagedCollectionObject(obj_id self, obj_id pInv, string scene) { if(!isIdValid(self)) return false; if((scene == null) || scene.equals("")) return false; forage_blog("rareObject reward: IN giveCollectionObject"); forage_blog("rareObject reward: scene: "+scene); boolean componentPlanet = false; for(int i =0; i < COMPONENT_PLANETS.length; ++i) { if(scene.startsWith(COMPONENT_PLANETS[i])) { componentPlanet = true; break; } } if(!componentPlanet) { sendSystemMessage(self, FOUND_NOTHING); return false; } string[] component = dataTableGetStringColumnNoDefaults(FORAGING_RARE_TABLE, scene); if((component == null) || (component.length < 0)) return false; forage_blog("rareObject reward: IN giveCollectionObject"); int itemIndex = rand(0, (component.length-1)); forage_blog("rareObject reward: IN giveCollectionObject - itemIndex: "+itemIndex); string objTemplate = dataTableGetString(FORAGING_RARE_TABLE, itemIndex, scene); forage_blog("rareObject reward: IN giveCollectionObject - objTemplate: "+objTemplate); createObjectOverloaded(objTemplate, pInv); return true; } boolean saveForageLocationOnPlayer(obj_id self, Vector newListOfLocs, location curLoc) { if(utils.hasScriptVar(self, "forage.listOfAlreadyForagedLocs")) { location[] oldListOfLocs = utils.getLocationArrayScriptVar(self, "forage.listOfAlreadyForagedLocs"); //get list of all previous successfull locations for(int i =0; i < oldListOfLocs.length; ++i) { utils.addElement(newListOfLocs, oldListOfLocs[i]); } } //add this successful location to list utils.addElement(newListOfLocs, curLoc); //save list of successes on player utils.setScriptVar(self, "forage.listOfAlreadyForagedLocs", newListOfLocs); //set this location as my last place to attempt a forage. utils.setScriptVar(self, "forage.lastLocation", curLoc); return true; } boolean logForageData(obj_id player) { if(!isValidId(player) || !exists(player)) return false; int loopSeed = 1; if(utils.hasScriptVar(player, "qa.loopseed")) loopSeed = utils.getIntScriptVar(player, "qa.loopseed"); string strForageFeedBackLog = utils.getStringScriptVar(player, "qa.strForageFeedBack"); if(strForageFeedBackLog == null || strForageFeedBackLog.equals("")) { sendSystemMessageTestingOnly(player, "The log was blank"); return false; } int strForageFeedBackCategoryLog = utils.getIntScriptVar(player, "qa.strForageFeedBackCategory"); if(strForageFeedBackCategoryLog < 0) { sendSystemMessageTestingOnly(player, "The Category Number was invalid"); return false; } string categoryName = ""; //the beginnings of the dynamic file name switch(strForageFeedBackCategoryLog) { case loot.ENZYME: categoryName = "ENZYME"; break; case loot.WORM: categoryName = "WORMTHIEF"; break; case loot.COMPONENT: categoryName = "COMPONENT"; break; case loot.TREASURE: categoryName = "TREASURE"+getPlayerTreasureMapString(player); //gives the map level break; case loot.BAIT: categoryName = "BAIT"; break; case loot.NOTHING: categoryName = "NOTHING"; break; default: categoryName = "ERROR"; break; } if(categoryName == null || categoryName.equals("")) { sendSystemMessageTestingOnly(player, "The Category Name was blank"); return false; } int number = getGameTime() + loopSeed+ rand(1,100) + rand(1,100);//rand tries to help avoid overwriting the same doc more than one in the same frame. //this is sketchy dynamic naming that may or may not work string title = ""; title = "foraged" + categoryName + number; if(title == null || title.equals("")) { sendSystemMessageTestingOnly(player, "The title was blank"); return false; } //keeping the name random, maybe. loopSeed++; utils.setScriptVar(player, "qa.loopseed", loopSeed); saveTextOnClient(player, title+".txt", strForageFeedBackLog); return true; } boolean blog(string msg) { LOG("minigame", msg); return true; } boolean forage_blog(string msg) { if(FORAGE_LOGGING_ON) LOG(FORAGE_LOGGING_CATEGORY, msg); return true; }