/** * Copyright (c)2000-2002 Sony Online Entertainment Inc. * All Rights Reserved * * Title: consumable.scriptlib * Description: consumable effects library * @author $Author:$ * @version $Revision:$ */ /***** INCLUDES ********************************************************/ include library.utils; include library.healing; include library.player_stomach; include library.prose; include library.food; include library.metrics; include library.doctor_bag; /***** CONSTANTS *******************************************************/ const float MAX_AFFECT_DISTANCE = 6.0f; const string[] STAT_NAME = { "HEALTH", "CONSTITUTION", "ACTION", "STAMINA", "MIND", "WILLPOWER", "POISON", "DISEASE" }; const string VAR_CONSUMABLE_BASE = "consumable"; const string VAR_CONSUMABLE_TARGET = "consumable.target"; //int const string VAR_CONSUMABLE_MODS = "consumable.mods"; //attrib_mod[] const string VAR_CONSUMABLE_MEDICINE = "consumable.medicine"; //boolean const string VAR_CONSUMABLE_PET_MED = "consumable.pet_med"; //boolean const string VAR_CONSUMABLE_DROID_MED = "consumable.droid_med"; //boolean const string VAR_CONSUMABLE_MED_TYPE = "consumable.med_type"; //int const string VAR_CONSUMABLE_CHARGES = "consumable.charges"; //int const string VAR_CONSUMABLE_STOMACH_VALUES = "consumable.stomachValues"; //int[] const string VAR_BEEN_CONSUMED = "beenConsumed"; const string VAR_SKILL_MOD_REQUIRED = "consumable.skillModRequired"; //string[] const string VAR_SKILL_MOD_MIN = "consumable.skillModMin"; //int[] const string MSG_INSUFFICIENT_SKILL = "@error_message:insufficient_skill"; const string MSG_TARGET_OUT_OF_RANGE = "@error_message:target_out_of_range"; const string MSG_CANNOT_TARGET = "@error_message:cannot_target"; const string_id SID_ATTRIBMOD_APPLY = new string_id("base_player","attribmod_apply"); const string_id SID_INSUFFICIENT_SKILL = new string_id("error_message","insufficient_skill"); const string_id SID_TARGET_OUT_OF_RANGE = new string_id("error_message","target_out_of_range"); const string_id SID_CANNOT_TARGET = new string_id("error_message","cannot_target"); const string_id SID_TOO_MUCH_SHOCK = new string_id("error_message","too_much_shock"); const string_id SID_YOU_HAVE_GREATER_ENHANCEMENT = new string_id("error_message","you_have_greater_enchancement"); const string_id SID_THEY_HAVE_GREATER_ENHANCEMENT = new string_id("error_message","they_have_greater_enchancement"); const string_id SID_SHOCK_EFFECT_LOW = new string_id("healing","shock_effect_low"); const string_id SID_SHOCK_EFFECT_MEDIUM = new string_id("healing","shock_effect_medium"); const string_id SID_SHOCK_EFFECT_HIGH = new string_id("healing","shock_effect_high"); const string_id SID_SHOCK_EFFECT_LOW_TARGET = new string_id("healing","shock_effect_low_target"); const string_id SID_SHOCK_EFFECT_MEDIUM_TARGET = new string_id("healing","shock_effect_medium_target"); const string_id SID_SHOCK_EFFECT_HIGH_TARGET = new string_id("healing","shock_effect_high_target"); const string_id SID_INSUFFICIENT_SKILL_HEAL = new string_id("healing","insufficient_skill_heal"); const string_id PROSE_CONSUME_ITEM = new string_id("base_player", "prose_consume_item"); //MEDPACK TYPES const int MT_NONE = 0; const int MT_HEAL_DAMAGE = 1; const int MT_HEAL_WOUND = 2; const int MT_HEAL_STATE = 3; const int MT_HEAL_ENHANCE = 4; const int MT_REVIVE_PLAYER = 5; // Foraged stats datatable. const string FORAGED_DATA = "datatables/foraging/forage_global.iff"; /***** FUNCTIONS ********************************************************/ /***** BASE FUNCTIONS ***************************************************/ /***************************************************************** * @brief attempts to use the consumable in the appropriate fashion * * @param obj_id player * @param obj_id target * @param obj_id item * * @return boolean; false on error *****************************************************************/ boolean consumeItem( obj_id player, obj_id target, obj_id item ) { return consumeItem (player, target, item, true); } boolean consumeItem( obj_id player, obj_id target, obj_id item , boolean checkPvpStatus) { if ( !isIdValid(player) || !isIdValid(target) || !isIdValid(item) ) return false; if ( !isMob(player) || !isMob(target) ) return false; if ( !player.isLoaded() || !target.isLoaded() || !item.isLoaded() ) { sendSystemMessage(player, SID_TARGET_OUT_OF_RANGE); return false; } obj_id owner = utils.getContainingPlayer(item); if(player == owner) { // Here we do nothing, this is OK } else { if(isIdValid(owner)) { CustomerServiceLog("VendorCreditDupeExploit", "%TU tried to consume an item (" + item + "), but is no longer in possession of it! %TT currently has the item.", player, owner); } else { CustomerServiceLog("VendorCreditDupeExploit", "%TU tried to consume an item (" + item + "), but is no longer in possession of it!", player); } return false; } string medicinebuff = healing.SCRIPT_VAR_MEDICINE_BUFF; string medicinerebuff = healing.SCRIPT_VAR_MEDICINE_REBUFF; if ( healing.isRangedMedicine(item) ) { // If the item is a ranged medicing, its maximum range varies based on its objvar float healing_range = (float)healing.getHealingRange(item); float healing_range_mod = (float)getSkillStatMod(player, "healing_range"); healing_range = healing_range + ((16 * healing_range_mod) / 100.0f); //LOG("LOG_CHANNEL", "healing_range ->" + healing_range); if ( getDistance(player, target) > healing_range ) { sendSystemMessage(player, SID_TARGET_OUT_OF_RANGE); return false; } } else { if ( getDistance(player, target) > MAX_AFFECT_DISTANCE ) { sendSystemMessage(player, SID_TARGET_OUT_OF_RANGE); return false; } } // Predeclare objvar data. boolean isConsumable = false; int[] vol = { 60, 0 }; attrib_mod[] am = null; string[] skillReq = null; int[] skillMin = null; // If the item is foraged food, create our data. if ( hasScript( item, "item.comestible.foraged" ) ) { isConsumable = true; am = getForagedFoodMods( item ); if ( am == null ) isConsumable = false; else isConsumable = true; } else { // Get objvar data for food items that still have them. isConsumable = hasObjVar( item, VAR_CONSUMABLE_BASE ); if ( isConsumable ) { vol = getIntArrayObjVar( item, VAR_CONSUMABLE_STOMACH_VALUES ); am = getAttribModArrayObjVar( item, VAR_CONSUMABLE_MODS ); skillReq = getStringArrayObjVar(item, VAR_SKILL_MOD_REQUIRED); skillMin = getIntArrayObjVar(item, VAR_SKILL_MOD_MIN); } else { // This is temporary support for old style food items that don't have obj vars. if ( hasScript( item, "item.comestible.crafted" ) ) isConsumable = true; } } // Don't let us eat food we already have a mod for. if ( !canEatFood( target, item ) ) return false; if ( isConsumable ) { boolean canTarget = false; if ( player == target ) { canTarget = true; } else { if(checkPvpStatus) { if ( factions.pvpDoAllowedHelpCheck(player, target) ) { canTarget = true; } } else { canTarget = true; } } if ( !canTarget) { sendSystemMessage(player, SID_CANNOT_TARGET); return false; } // Check for a race restriction. if ( hasObjVar( item, "race_restriction" ) ) { int race = getIntObjVar( item, "race_restriction" ); int target_species = getSpecies( target ); if ( target_species == -2 ) { sendSystemMessage( target, new string_id( "error_message", "pets_only" ) ); return false; } else if ( race != target_species ) { sendSystemMessage( target, new string_id( "error_message", "race_restriction" ) ); return false; } } if ( (vol == null) || (vol.length == 0) ) return false; // Convert old buff values to % based // Convert stim packs that dont have mind attribute to heal mind as well float dcy = am[0].getDecay(); if (am[0].getDuration() > 0) { attrib_mod[] modifyBuffValue = modifyBuffMods(player, target, item, am); if ( modifyBuffValue != null && modifyBuffValue.length > 0 ) { am = modifyBuffValue; } } else if ( (am.length == 2) && ( (int)dcy == (int)MOD_POOL ) ) { //TODO LOG("buffs", "Roger Dodger"); am = utils.addMindAttribToStim(am[0].getValue()); setObjVar(getSelf(), consumable.VAR_CONSUMABLE_MODS, am); } //LOG("buffs", "Roger Dodger"); //LOG("buffs", "am length" + am.length); //LOG("buffs", "dcy" + (int)dcy); if ( (skillReq != null) && (skillReq.length != 0) && (skillMin != null) && (skillMin.length != 0) ) { if ( skillReq.length == skillMin.length ) { boolean canUse = true; for ( int i = 0; i < skillReq.length; i++ ) { int skillMod = getSkillStatMod(player, skillReq[i]); if ( skillMod < skillMin[i] ) canUse = false; } if ( !canUse ) { string[] reqs = new string[skillReq.length]; for (int n = 0; n < skillReq.length; n++ ) { reqs[n] = skillReq[n] + "[" + getSkillStatMod(player, skillReq[n]) + "/" + skillMin[n] + "]"; } if (skillReq.length > 1) { sui.listbox(player, player, MSG_INSUFFICIENT_SKILL, reqs); sendSystemMessage(player, SID_INSUFFICIENT_SKILL); } else { string skill_mod_name = "@stat_n:" + skillReq[0]; prose_package pp = prose.getPackage(SID_INSUFFICIENT_SKILL_HEAL, skill_mod_name, skillMin[0]); sendSystemMessageProse(player, pp); } return false; } if ( healing.isMedicine(item) ) { // Perform success algorithms for medicinal healing. float multiplier = healing.getHealingMultiplier(player, item); float final_multiplier = healing.applyShockWoundModifier(multiplier, target); // The player has too many shock wounds. Warn the doctor. if (final_multiplier <= 0.0f) { sendSystemMessage(player, SID_TOO_MUCH_SHOCK); return false; } float shock_effect = final_multiplier / multiplier; if (shock_effect < .3f) { sendSystemMessage(player, SID_SHOCK_EFFECT_HIGH); if (player != target) sendSystemMessage(target, SID_SHOCK_EFFECT_HIGH_TARGET); } else if (shock_effect < .5f) { sendSystemMessage(player, SID_SHOCK_EFFECT_MEDIUM); if (player != target) sendSystemMessage(target, SID_SHOCK_EFFECT_MEDIUM_TARGET); } else if (shock_effect < .8f) { sendSystemMessage(player, SID_SHOCK_EFFECT_LOW); if (player != target) sendSystemMessage(target, SID_SHOCK_EFFECT_LOW_TARGET); } // Force Disease & Poison Resist buffs to always have multiplier of 1. if (am[0].getAttribute() > 8) final_multiplier = 1f; //apply the multiplier to the medicine's attrib mods by building a modified attrib_mod array attrib_mod[] am_new = healing.modifyMedicineAttributes(am, final_multiplier); am = (attrib_mod[]) am_new.clone(); // Check to see if we should replace the old buff. if (healing.isBuffMedicine(item)) { string buff_name =""; int current_value = 0; int failed = 0; for ( int x = 0; x < am.length; x++ ) { if (healing.hasEnhancement(target, am[x].getAttribute())) { // If the player already has an enhancement check to see if the new one is better. buff_name = "medical_enhance_" + healing.attributeToString(am[x].getAttribute()).toLowerCase(); current_value = healing.getHealEnhanceValue(target, am[x].getAttribute()); if (am[x].getValue() >= current_value) { // Replace the current buff with the new one. removeAttribOrSkillModModifier(target, buff_name); // We store the fact that this is a re-buff for the healing system. utils.setScriptVar(target, medicinerebuff +"."+x, current_value); } else { failed ++; if (player == target) { sendSystemMessage(player, SID_YOU_HAVE_GREATER_ENHANCEMENT); } else { prose_package ppGreaterEnhancement = prose.getPackage(SID_THEY_HAVE_GREATER_ENHANCEMENT, target); sendSystemMessageProse(player, ppGreaterEnhancement); } } } } if (failed == am.length) // Failed in all the cases { return false; } } } } else { debugSpeakMsg(player, "consumable::consumeItem(): skill mod arrays are not synchronized"); } } else if ( (skillReq == null) && (skillMin == null) ) { // NO SKILL ITEMS // Check to see if the item is a droid medpack, which requires no skill to use if(hasObjVar(item, consumable.VAR_CONSUMABLE_DROID_MED)) { if(hasObjVar(item, "consumable.energy")) { // The item is a droid damage medpack int totalEnergy = getIntObjVar(item, "consumable.energy"); int toHeal[] = healing.distributeHAMDamageHealingPoints(target, totalEnergy); attrib_mod[] tempAm = new attrib_mod[3]; // This should never happen if(toHeal.length != 3) return false; for(int y = 0; y < 3; y++) { // tempAm[y] = utils.createHealDamageAttribMod(y*3, toHeal[y]); tempAm[y] = new attrib_mod(y*3, toHeal[y], 0.0f, 0.0f, MOD_POOL); } am = tempAm; } else { // The item is a droid wound medpack int totalPower = am[0].getValue(); int toHeal[] = healing.distributeHAMWoundHealingPoints(target, totalPower); attrib_mod[] tempAm = new attrib_mod[3]; // This should never happen if(toHeal.length != 3) return false; for(int z = 0; z < 3; z++) { // tempAm[z] = utils.createHealWoundAttribMod(z*3, toHeal[z]); tempAm[z] = new attrib_mod(z*3, toHeal[z], 0.0f, healing.AM_HEAL_WOUND, 0.0f); } am = tempAm; } } } else { debugSpeakMsg(player, "consumable::consumeItem(): skill mod arrays are fubar!"); } boolean wasConsumed = player_stomach.addToStomach( player, target, vol ); if ( wasConsumed ) { if(checkPvpStatus) { if ( target != player ) pvpHelpPerformed( player, target ); } if ( healing.isRevivePack(item) ) { pclib.clearEffectsForDeath(target); setAttrib(target, HEALTH, 1); } //modify attribmods as necessary attrib_mod[] modifiedAm = modifyConsumableMods(player, target, item, am); if ( modifiedAm != null && modifiedAm.length > 0 ) am = modifiedAm; // Store amount buffed for experience and messaging purposes if a buff medicine. if ( healing.isBuffMedicine(item) && (am != null) ) { for ( int x = 0; x < am.length; x++ ) { string buff_name = "medical_enhance_" + healing.attributeToString(am[x].getAttribute()).toLowerCase(); utils.setScriptVar( target, medicinebuff +"."+x, am[x].getValue() ); utils.setScriptVar( target, "healing." + buff_name, am[x].getValue() ); int buff_type = am[x].getAttribute(); if (buff_type <= 5) // ATTRIB { addAttribModifier(target, buff_name, buff_type, am[x].getValue(), am[x].getDuration(), am[x].getAttack(), am[x].getDecay(), true, false, true); } else if (buff_type == 6) // POISON { addSkillModModifier(target, buff_name, "resistance_poison", am[x].getValue(), am[x].getDuration(), false, true); if (hasObjVar(item, healing.VAR_HEALING_ABSORPTION)) { addSkillModModifier(target, buff_name+".absorption", "absorption_poison", getIntObjVar(item, healing.VAR_HEALING_ABSORPTION), am[x].getDuration(), false, false); } } else if (buff_type == 7) // DISEASE { addSkillModModifier(target, buff_name, "resistance_disease", am[x].getValue(), am[x].getDuration(), false, true); if (hasObjVar(item, healing.VAR_HEALING_ABSORPTION)) { addSkillModModifier(target, buff_name+".absorption", "absorption_disease", getIntObjVar(item, healing.VAR_HEALING_ABSORPTION), am[x].getDuration(), false, false); } } } // Log buff info metrics.logBuffStatus(target); } else { // Apply attribute mods. if ( am != null ) { // If item was a buff log info boolean isBuff = false; for (int i = 0; i < am.length; i++) { debugServerConsoleMsg(target, ">>>>>>>>>>>> PREPARING TO APPLY MOD TO ATTRIBUTE: " + am[i].getAttribute()); if(am[i].getAttribute() == HEALTH) { debugServerConsoleMsg(target, ">>>>>>>>>>>> ATTEMPTING TO APPLY MOD -- " + am[i].getAttribute() + " LOOKS LIKE HEALTH TO ME!"); utils.addAttribMod(target, am[i]); } if (am[i].getDuration() > 0) isBuff = true; } if (isBuff) metrics.logBuffStatus(target); } } // Apply new food effects. 12/17/03 applyFoodEffects( target, item, am ); // Don't send standard consumable results message for medicine since the healing system // generates its own. if ( !healing.isMedicine(item) ) { //play eat sound here... string snd = "clienteffect/"; switch ( getSpecies(player) ) { case SPECIES_HUMAN: case SPECIES_BOTHAN: case SPECIES_TWILEK: case SPECIES_ZABRAK: snd += "human_"; break; case SPECIES_MON_CALAMARI: case SPECIES_RODIAN: case SPECIES_TRANDOSHAN: snd += "reptile_"; break; case SPECIES_WOOKIEE: snd += "wookiee_"; break; } int gender = getGender(player); switch ( gender ) { case GENDER_FEMALE: snd += "female_eat.cef"; break; case GENDER_MALE: default: snd += "male_eat.cef"; break; } if ( !snd.equals("") ) playClientEffectLoc( player, snd, getLocation(player), getScale(player) ); prose_package pp = prose.getPackage(PROSE_CONSUME_ITEM, player, item); sendSystemMessageProse(target, pp); } removeObjVar( item, VAR_BEEN_CONSUMED ); return decrementCharges(item, player); } else { removeObjVar( item, VAR_BEEN_CONSUMED ); } return false; } return false; } /***************************************************************** * @brief overload function for consumeItem() * * @param obj_id player * @param obj_id item * * @return boolean; false on error *****************************************************************/ boolean consumeItem(obj_id player, obj_id item) { return consumeItem(player, player, item); } /***************************************************************** * @brief modifies consumable mods based on specific criteria * * @param obj_id player * @param obj_id target * @param obj_id item * @param attrib_mod[] am * * @return attrib_mod[]; null on error *****************************************************************/ attrib_mod[] modifyConsumableMods(obj_id player, obj_id target, obj_id item, attrib_mod[] am) { if ( !isIdValid(player) || !isIdValid(target) || !isIdValid(item) ) return null; if ( am == null || am.length == 0 ) return null; float modval = 1f; float moddur = 1f; int species = getSpecies(target); if ( (species == SPECIES_TWILEK) && (am != null) ) //twilek buffs last 10% longer! moddur *= 1.1f; //wound & antidote healing in factional facilities while registered = +25% effect bonus if ( utils.hasScriptVar(player, "registerWithLocation") && factions.isDeclared(player) && healing.isMedicine(item) ) { obj_id regLoc = utils.getObjIdScriptVar(player, "registerWithLocation"); if ( isIdValid(regLoc) ) { string lFac = factions.getFaction(regLoc); string pFac = factions.getFaction(player); if ( lFac != null && pFac != null && lFac.equals(pFac) ) modval *= 1.25f; } } if ( modval != 1f || moddur != 1f ) { for ( int x = 0; x < am.length; x++ ) { float atk = am[x].getAttack(); float decay = am[x].getDecay(); if ( moddur != 1f && atk >= 0f && decay >= 0f ) { attrib_mod tmp = new attrib_mod(am[x].getAttribute(), am[x].getValue(), am[x].getDuration() * moddur, atk, decay); if ( tmp != null ) am[x] = tmp; } if ( modval != 1f && (atk == healing.AM_HEAL_WOUND || atk == healing.AM_HEAL_SHOCK || decay == MOD_ANTIDOTE) ) { //debugSpeakMsg(player, "modifying attrib_mod val (" + am[x].getValue() + ") by " + modval); attrib_mod tmp = new attrib_mod(am[x].getAttribute(), (int)(am[x].getValue() * modval), am[x].getDuration(), atk, decay); if ( tmp != null ) am[x] = tmp; } } } return am; } attrib_mod[] modifyBuffMods(obj_id player, obj_id target, obj_id item, attrib_mod[] am) { if ( !isIdValid(player) || !isIdValid(target) || !isIdValid(item) ) { return null; } if ( am == null || am.length == 0 ) { return null; } for ( int x = 0; x < am.length; x++ ) { float atk = am[x].getAttack(); float decay = am[x].getDecay(); int buffValue = 0; // int buffValue = healing.getBuffAmount(target, am[x].getAttribute(), am[x].getValue()); attrib_mod tmp = new attrib_mod(am[x].getAttribute(), buffValue, am[x].getDuration(), atk, decay); if ( tmp != null ) { am[x] = tmp; } } return am; } /***************************************************************** * @brief create consumable * * @param obj_id item * @param attrib_mod[] am * @param int[] stomach * @param dictionary skillReq * * @return boolean; false on error *****************************************************************/ boolean createConsumable(obj_id item, attrib_mod[] am, int[] stomach, dictionary skillReq) { if ( (item == null) || (am == null) || (stomach == null) || (stomach.length != player_stomach.STOMACH_MAX) ) return false; boolean litmus = true; if(am.length == 0) { // Zero length array check return false; } litmus &= setObjVar(item, VAR_CONSUMABLE_MODS, am); litmus &= setObjVar(item, VAR_CONSUMABLE_STOMACH_VALUES, stomach); if ( (skillReq != null) && (!skillReq.isEmpty()) ) { java.util.Enumeration keys = skillReq.keys(); string[] skillModReq = new string[skillReq.size()]; int[] skillModMin = new int[skillReq.size()]; for ( int i = 0; i < skillReq.size(); i++ ) { skillModReq[i] = (string)keys.nextElement(); skillModMin[i] = skillReq.getInt(skillModReq[i]); } if(skillModReq.length == 0 || skillModMin.length == 0) { // Zero length array check return false; } litmus &= setObjVar(item, VAR_SKILL_MOD_REQUIRED, skillModReq); litmus &= setObjVar(item, VAR_SKILL_MOD_MIN, skillModMin); } return litmus; } /***************************************************************** * @brief create consumable * * @param obj_id item * @param attrib_mod[] am * @param int[] stomach * @param dictionary skillReq * * @return boolean; false on error *****************************************************************/ //Does not go through containers nested in player inventory boolean decrementCharges(obj_id item, obj_id player) { if (item == null) return false; // If we're dealing with a doctor bag, decrement from the index and the item itself. if ( doctor_bag.isDoctorBag( item ) ) doctor_bag.decrementSurrogateCharge( item ); int charges = getCount(item); if ( charges > 1000 ) setCount( item, 1000 ); else if (charges > 1) incrementCount(item, -1); else { if ( !doctor_bag.isDoctorBag( item ) ) destroyObject(item); } return true; } //Will not work if player has it in a backpack and the backpack is equipped. boolean decrementSpecificObjectRecursive(obj_id player, string template, string objvar) { obj_id inventory = utils.getInventoryContainer(player); obj_id[] device = utils.getAllItemsInContainerByTemplate(inventory, template, true); if(device == null) return false; for(int i = 0; i < device.length; i++) { if(getTemplateName(device[i]).equals(template) && hasObjVar(device[i], objvar)) { decrementCount(device[i]); return true; } } return false; } //loops thorough all items on the player including items equipped. boolean decrementObjectInventoryOrEquipped(obj_id player, string template, string objvar) { obj_id inventory = utils.getInventoryContainer(player); obj_id[] inventoryContents = getInventoryAndEquipment(player); if(inventoryContents == null) return false; for(int i = 0; i < inventoryContents.length; i++) { if(getTemplateName(inventoryContents[i]).equals(template) && hasObjVar(inventoryContents[i], objvar)) { decrementCount(inventoryContents[i]); return true; } } return false; } boolean decrementSpecificObject(obj_id player, string template, string objvar) { obj_id inventory = utils.getInventoryContainer(player); obj_id[] device = utils.getContainedObjectsWithObjVar(inventory, objvar); if(device == null) return false; for(int i = 0; i < device.length; i++) { if(getTemplateName(device[i]).equals(template)) { decrementCount(device[i]); return true; } } return false; } attrib_mod[] getForagedFoodMods( obj_id item ) { int mod_time = 0; int health_mod = 0; int action_mod = 0; int mind_mod = 0; if(item == null || !isIdValid(item)) return null; // Modify our template name. string mytemplate = getTemplateName(item); string prefix = "object/tangible/"; mytemplate = mytemplate.substring( prefix.length() ); if(mytemplate == null || mytemplate.equals("")) { // Not a foraged food! detachScript( item, "item.comestible.foraged" ); return null; } // Find us in the forage table. int index = -1; string tangibles[] = dataTableGetStringColumn(FORAGED_DATA, "ITEM_TANGIBLE"); if(tangibles == null || tangibles.length == 0) { // Not a foraged food! detachScript( item, "item.comestible.foraged" ); return null; } for(int i = 0; i < tangibles.length; i++) { if(mytemplate.equals(tangibles[i])) { // Found us. index = i; } } if(index == -1) { // Not a foraged food! detachScript( item, "item.comestible.foraged" ); return null; } // Get our stats. mod_time = dataTableGetInt(FORAGED_DATA, index, "MOD_TIME"); health_mod = dataTableGetInt(FORAGED_DATA, index, "MOD_HEALTH"); action_mod = dataTableGetInt(FORAGED_DATA, index, "MOD_ACTION"); mind_mod = dataTableGetInt(FORAGED_DATA, index, "MOD_MIND"); // Assemble an attribute mod. int numItems = 0; if ( health_mod > 0 ) numItems++; if ( action_mod > 0 ) numItems++; if ( mind_mod > 0 ) numItems++; attrib_mod[] am = new attrib_mod[numItems]; numItems = 0; if ( health_mod > 0 ) am[numItems++] = new attrib_mod( HEALTH, health_mod, mod_time, 0.0f, 0.0f ); if ( action_mod > 0 ) am[numItems++] = new attrib_mod( ACTION, action_mod, mod_time, 0.0f, 0.0f ); if ( mind_mod > 0 ) am[numItems++] = new attrib_mod( MIND, mind_mod, mod_time, 0.0f, 0.0f ); return am; } boolean canEatFood( obj_id target, obj_id item ) { // Duration? if ( hasObjVar( item, "duration" ) ) { string type = getStringObjVar( item, "duration.type" ); // Ignore if we already have this effect. if ( utils.hasScriptVar( target, "food."+type+".dur" ) ) { sendSystemMessage( target, new string_id( "combat_effects", "already_affected" ) ); return false; } } // Delayed? if ( hasObjVar( item, "delayed" ) ) { string type = getStringObjVar( item, "delayed.type" ); // Ignore if we already have this effect. if ( utils.hasScriptVar( target, "food."+type+".dur" ) ) { sendSystemMessage( target, new string_id( "combat_effects", "already_affected" ) ); return false; } } if ( hasObjVar( item, "skill_mod" ) ) { // This is a temporary skill mod food effect. string mod_name = getStringObjVar( item, "skill_mod.name" ); string mod_id = "food_" + mod_name; // Ignore if we already have this effect. if ( utils.hasScriptVar( target, mod_id ) ) { sendSystemMessage( target, new string_id( "combat_effects", "already_affected" ) ); return false; } } return true; } void applyFoodEffects( obj_id target, obj_id item, attrib_mod[] am ) { // Strip the food name. string templateName = getTemplateName(item); int lastslash = templateName.lastIndexOf( '/' ); int lastdot = templateName.lastIndexOf( '.' ); templateName = templateName.substring( lastslash+1, lastdot ); // Stat mod? if ( hasObjVar( item, "stat_mod" ) ) { if ( am == null ) return; // Add a buff icon. float dur = am[0].getDuration(); addBuffIcon( target, "food."+templateName, dur ); } // Delayed? else if ( hasObjVar( item, "delayed" ) ) { // This is a delayed food effect. // Copy item obj vars to target script vars. string type = getStringObjVar( item, "delayed.type" ); utils.setScriptVar( target, "food."+type+".dur", getIntObjVar( item, "delayed.dur" ) ); utils.setScriptVar( target, "food."+type+".eff", getIntObjVar( item, "delayed.eff" ) ); utils.setScriptVar( target, "food."+type+".time", getGameTime() ); // Add an icon. addBuffIcon( target, "food."+type, 3600 ); // Wears off after a half hour. dictionary outparams = new dictionary(); outparams.put( "type", type ); messageTo( target, "removeDelayedFoodEffect", outparams, 3600f, false ); } // Duration? else if ( hasObjVar( item, "duration" ) ) { string type = getStringObjVar( item, "duration.type" ); // Ignore if we already have this effect. if ( utils.hasScriptVar( target, "food."+type+".dur" ) ) { sendSystemMessage( target, new string_id( "combat_effects", "no_additional_effect" ) ); return; } // This a duration food effect. // Copy item obj vars to target script vars. int duration = getIntObjVar( item, "duration.dur" ); utils.setScriptVar( target, "food."+type+".dur", duration ); utils.setScriptVar( target, "food."+type+".eff", getIntObjVar( item, "duration.eff" ) ); // Add an icon. addBuffIcon( target, "food."+type, duration ); // Send a messageto to remove this buff. dictionary outparams = new dictionary(); outparams.put( "type", type ); messageTo( target, "removeDurationFoodEffect", outparams, duration, false ); } // Instant? else if ( hasObjVar( item, "instant" ) ) { // This is an instant food effect. food.applyInstantEffect( target, item ); } // Skill Mod? else if ( hasObjVar( item, "skill_mod" ) ) { // This is a temporary skill mod food effect. string mod_name = getStringObjVar( item, "skill_mod.name" ); int amount = (int) getFloatObjVar( item, "skill_mod.amount" ); int duration = (int) getFloatObjVar( item, "skill_mod.dur" ); string mod_id = "food_" + mod_name; // Check if we already have this mod. if ( utils.hasScriptVar( target, mod_id ) ) { // We already have this skill mod. // Notify the player. prose_package pp = prose.getPackage( new string_id( "combat_effects", "dupe_skill_mod" ), localize( new string_id( "stat_n", mod_name ) ) ); sendSystemMessageProse( target, pp ); return; } // Apply a temporary (non-persistent) skill modifier. utils.setScriptVar( target, mod_id, getGameTime() ); addSkillModModifier( target, mod_id, mod_name, amount, duration, true, true ); // Add an icon. addBuffIcon( target, "food."+templateName, duration ); // Message the player. prose_package pp = prose.getPackage( new string_id( "combat_effects", "skill_mod_buffed" ), localize( new string_id( "stat_n", mod_name ) ) ); sendSystemMessageProse( target, pp ); } // Mind Heal? else if ( hasObjVar( item, "mind_heal" ) ) { // This is a mind heal food effect. int mind_heal = getIntObjVar( item, "mind_heal" ); int mind = getAttrib( target, MIND ); int wounds = getAttribWound( target, MIND ); int max_mind = getMaxAttrib( target, MIND ) - wounds; int damage = max_mind - mind; if ( mind_heal > damage ) mind_heal = damage; setAttrib( target, MIND, getUnmodifiedAttrib( target, MIND ) + mind_heal ); prose_package pp = prose.getPackage( new string_id( "combat_effects", "food_mind_heal" ), mind_heal ); sendSystemMessageProse( target, pp ); } }