/** * Copyright (c) ©2004 Sony Online Entertainment Inc. * All Rights Reserved * * Title: library.armor.scriptlib * Description: common data and functions for the new armor system * @author $Author: Steve Jakab$ * @version $Revision: 0$ */ /***** INCLUDES ********************************************************/ include java.util.Enumeration; include java.util.Map; include library.skill; include library.trial; /***** CONSTANTS *******************************************************/ // note these strings may also be defined in TangibleObject.cpp const string DATATABLE_ARMOR = "datatables/crafting/armor.iff"; const string DATATABLE_ARMOR_PENALTY = "datatables/crafting/armor_penalty.iff"; const string DATATABLE_TYPE_COL = "type"; const string DATATABLE_ATTRIB_INC_COL = "attrib_inc"; const string DATATABLE_ATTRIB_DEC_COL = "attrib_dec"; const string DATATABLE_RES_GEN_PROT_COL = "resource_general_protection_percent"; const string DATATABLE_MIN_GEN_PROT_COL = "min_general_protection"; const string DATATABLE_MAX_GEN_PROT_COL = "max_general_protection"; const string DATATABLE_RES_SPEC_PROT_COL = "resource_special_protection_percent"; const string DATATABLE_MIN_SPEC_PROT_COL = "min_special_protection"; const string DATATABLE_MAX_SPEC_PROT_COL = "max_special_protection"; const string DATATABLE_RES_CONDITION_COL = "resource_condition_percent"; const string DATATABLE_MIN_CONDITION_COL = "min_condition"; const string DATATABLE_MAX_CONDITION_COL = "max_condition"; const string DATATABLE_TYPE_PENALTY_COL = "type"; const string DATATABLE_MOVE_PENALTY_COL = "move"; const string DATATABLE_ACCURACY_PENALTY_COL = "accuracy"; const string DATATABLE_ATTACK_PENALTY_COL = "attack"; const string DATATABLE_DEFENSE_PENALTY_COL = "defense"; const string DATATABLE_LAYER_ROW = "layer"; const string DATATABLE_LAYER_REFERENCE_ROW = "layer_reference"; const string DATATABLE_SEGMENT_ROW = "segment"; const string DATATABLE_CORE_ROW = "core"; const string DATATABLE_FINAL_ROW = "final"; const string DATATABLE_GENERIC_ROW = "generic"; const string DATATABLE_PSG_ROW = "psg"; const string DATATABLE_CATEGORY_ROW = "category"; const int DATATABLE_PSG_LAYER = 13; // layer added to personal shield generators const int DATATABLE_RECON_LAYER = 14; // layer added to recon cores const int DATATABLE_ASSAULT_LAYER = 15; // layer added to assault cores const string DATATABLE_PSG_LAYER_ROW = DATATABLE_LAYER_ROW + DATATABLE_PSG_LAYER; const string DATATABLE_RECON_LAYER_ROW = DATATABLE_LAYER_ROW + DATATABLE_RECON_LAYER; const string DATATABLE_ASSAULT_LAYER_ROW = DATATABLE_LAYER_ROW + DATATABLE_ASSAULT_LAYER; const string DATATABLE_MASTER_ARMOR = "datatables/crafting/armor_schematics.iff"; const int NUM_DATATABLE_ARMOR_LAYERS = 16; const float MIN_ARMOR_RANGE = -0.5f; const float MAX_ARMOR_RANGE = 1.5f; const string SCRIPT_ARMOR_EXAMINE = "item.armor.new_armor"; const string SID_TABLE = "@obj_attr_n:"; const string STANDARD_PROTECTION_PREFIX = "cat_armor_standard_protection.armor_eff_"; const string SPECIAL_PROTECTION_PREFIX = "cat_armor_special_protection.armor_eff_"; const string[] SID_SPECIAL_PROTECTIONS = { SPECIAL_PROTECTION_PREFIX + "elemental_acid", SPECIAL_PROTECTION_PREFIX + "blast", SPECIAL_PROTECTION_PREFIX + "elemental_cold", SPECIAL_PROTECTION_PREFIX + "elemental_electrical", STANDARD_PROTECTION_PREFIX + "energy", SPECIAL_PROTECTION_PREFIX + "elemental_heat", STANDARD_PROTECTION_PREFIX + "kinetic", SPECIAL_PROTECTION_PREFIX + "stun", SPECIAL_PROTECTION_PREFIX + "lightsaber" }; const string ARMOR_PENALTY_PREFIX = "cat_armor_penalty.armor_penalty_"; const string RAW_ARMOR_PENALTY_PREFIX = "cat_armor_penalty_raw.armor_penalty_"; const string[] SID_ARMOR_PENALTIES = { ARMOR_PENALTY_PREFIX + "move", ARMOR_PENALTY_PREFIX + "accuracy", ARMOR_PENALTY_PREFIX + "attack", ARMOR_PENALTY_PREFIX + "defense" }; const string[] SID_RAW_ARMOR_PENALTIES = { RAW_ARMOR_PENALTY_PREFIX + "move", RAW_ARMOR_PENALTY_PREFIX + "accuracy", RAW_ARMOR_PENALTY_PREFIX + "attack", RAW_ARMOR_PENALTY_PREFIX + "defense" }; // these names MUST match the names in the attrib_inc and attrib_dec columns in the armor datatable! const string[] DATATABLE_SPECIAL_PROTECTIONS = { "acid", "blast", "cold", "electricity", "energy", "heat", "kinetic", "stun", "lightsaber" }; const Map SPECIAL_PROTECTION_MAP = collections.newMap(DATATABLE_SPECIAL_PROTECTIONS, SID_SPECIAL_PROTECTIONS); const string SID_ARMOR_CATEGORY = "armor_category"; const Integer[] ARMOR_CATEGORY_INDEXES = { new Integer(AC_reconnaissance), new Integer(AC_battle), new Integer(AC_assault), }; const string[] SID_ARMOR_CATEGORIES = { SID_TABLE + "armor_reconnaissance", SID_TABLE + "armor_battle", SID_TABLE + "armor_assault" }; const Map ARMOR_CATEGORY_MAP = collections.newMap(ARMOR_CATEGORY_INDEXES, SID_ARMOR_CATEGORIES); const Integer[] ARMOR_PENALTY_DATATABLE_TYPES = { new Integer(getStringCrc("basic_recon")), new Integer(getStringCrc("std_recon")), new Integer(getStringCrc("adv_recon")), new Integer(getStringCrc("basic_battle")), new Integer(getStringCrc("std_battle")), new Integer(getStringCrc("adv_battle")), new Integer(getStringCrc("basic_assault")), new Integer(getStringCrc("std_assault")), new Integer(getStringCrc("adv_assault")) }; const Integer[] ARMOR_PENALTY_OBJECT_TYPES = { new Integer(AC_reconnaissance * 10 + AL_basic), new Integer(AC_reconnaissance * 10 + AL_standard), new Integer(AC_reconnaissance * 10 + AL_advanced), new Integer(AC_battle * 10 + AL_basic), new Integer(AC_battle * 10 + AL_standard), new Integer(AC_battle * 10 + AL_advanced), new Integer(AC_assault * 10 + AL_basic), new Integer(AC_assault * 10 + AL_standard), new Integer(AC_assault * 10 + AL_advanced) }; const Map ARMOR_PENALTY_TYPE_MAP = collections.newMap(ARMOR_PENALTY_OBJECT_TYPES, ARMOR_PENALTY_DATATABLE_TYPES); const int ARMOR_MOVE_PENALTY = 0; const int ARMOR_ACCURACY_PENALTY = 1; const int ARMOR_ATTACK_PENALTY = 2; const int ARMOR_DEFENSE_PENALTY = 3; const Integer[] ARMOR_PENALTY_INDEXES = { new Integer(ARMOR_MOVE_PENALTY), new Integer(ARMOR_ACCURACY_PENALTY), new Integer(ARMOR_ATTACK_PENALTY), new Integer(ARMOR_DEFENSE_PENALTY) }; const string[] ARMOR_PENALTY_DATATABLE_COLUMNS = { DATATABLE_MOVE_PENALTY_COL, DATATABLE_ACCURACY_PENALTY_COL, DATATABLE_ATTACK_PENALTY_COL, DATATABLE_DEFENSE_PENALTY_COL }; const Map ARMOR_PENALTY_COLUMN_MAP = collections.newMap(ARMOR_PENALTY_INDEXES, ARMOR_PENALTY_DATATABLE_COLUMNS); const int[][][] ARMOR_CERTIFICATION_REDUCTIONS = { {{ 10, 20, 0 }, // Recon Move Mitigations { 20, 30, 40 }, // Recon Accuracy Mitigations { 30, 40, 60 }}, // Recon Attack Mitigations {{ 20, 30, 40 }, // Battle Move Mitigations { 20, 30, 40 }, // Battle Accuracy Mitigations { 20, 30, 40 }}, // Battle Attack Mitigations {{ 30, 40, 60 }, // Assault Move Mitigations { 20, 30, 40 }, // Assault Accuracy Mitigations { 10, 20, 0 }} // Assault Attack Mitigations }; // note these objvars may also be defined in TangibleObject.cpp const string OBJVAR_ARMOR_BASE = "armor"; const string OBJVAR_LAYER_PREFIX = "layer"; const string OBJVAR_CORE_TYPE = "core"; const string OBJVAR_ARMOR_LEVEL = "armorLevel"; const string OBJVAR_ARMOR_CATEGORY = "armorCategory"; const string OBJVAR_GENERAL_PROTECTION = "general_protection"; const string OBJVAR_CONDITION = "condition"; const string OBJVAR_CONDITION_MULTIPLIER = "conditionMultiplier"; const string OBJVAR_PSG_CURRENT_EFFICIENCY = "efficiency"; const string OBJVAR_PSG_RECHARGE_RATE = "recharge_rate"; const string OBJVAR_SPECIES_RESTRICTIONS = "species_restrictions"; const string SCRIPTVAR_ARMOR_COUNT = "armor_count"; const string SCRIPTVAR_PSG_EFFICIENCY = OBJVAR_ARMOR_BASE + "." + OBJVAR_PSG_CURRENT_EFFICIENCY; const string SCRIPTVAR_CACHED_BASE = OBJVAR_ARMOR_BASE + ".cache"; const string SCRIPTVAR_CACHED_DATATABLE_ROW = SCRIPTVAR_CACHED_BASE + "." + "datatableRow"; const string SCRIPTVAR_CACHED_LEVEL = SCRIPTVAR_CACHED_BASE + "." + "level"; const string SCRIPTVAR_CACHED_CATEGORY = SCRIPTVAR_CACHED_BASE + "." + "category"; const string SCRIPTVAR_CACHED_GENERAL_PROTECTION = SCRIPTVAR_CACHED_BASE + "." + "generalProtection"; const string SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS = SCRIPTVAR_CACHED_BASE + "." + "specialProtection"; const string SCRIPTVAR_CACHED_PENALTIES = SCRIPTVAR_CACHED_BASE + "." + "penalties"; const string SCRIPTVAR_SPECIES_RESTRICTIONS = OBJVAR_ARMOR_BASE + "." + OBJVAR_SPECIES_RESTRICTIONS; const string[] PSG_STARTUP_EFFECTS = { "appearance/pt_psg_on_mark_1.prt", "appearance/pt_psg_on_mark_2.prt", "appearance/pt_psg_on_mark_3.prt" }; const string[] PSG_HIT_EFFECTS = { "appearance/pt_psg_hit_mark_1.prt", "appearance/pt_psg_hit_mark_2.prt", "appearance/pt_psg_hit_mark_3.prt" }; const java.text.DecimalFormat ARMOR_PENALTY_FORMAT = new java.text.DecimalFormat("-##0.0'%'"); const string POWER_1 = "pseudo_1"; const string POWER_2 = "pseudo_2"; const string POWER_3 = "pseudo_3"; const string POWER_4 = "pseudo_4"; const string POWER_5 = "pseudo_5"; const string POWER_6 = "pseudo_6"; const string POWER_7 = "pseudo_7"; const string_id SID_ARMOR_TO_SCHEM = new string_id("spam", "armor_to_schem"); const string_id SID_CONVERT_PROMPT = new string_id("spam", "armor_to_schem_prompt"); const string_id SID_CONVERT_TITLE = new string_id("spam", "armor_to_schem_title"); const string_id SID_CONVERT_CONVERT_FAIL = new string_id("spam", "armor_to_schem_failure"); const string_id SID_CONVERT_CONVERT_SUCCESS = new string_id("spam", "armor_to_schem_success"); const string_id SID_CONVERT_INVALID_RESPONSE = new string_id("spam", "armor_to_schem_invalid_response"); /***** ARMOR CRAFTING FUNCTIONS ****************************************/ /** * Removes the scriptvars that are used to cache the armor data. * * @param armor the armor to remove the data from */ void clearCachedData(obj_id armor) { if ( isIdValid(armor) ) { utils.removeScriptVarTree(armor, SCRIPTVAR_CACHED_BASE); } } /** * Tests if an object is an armor component. * * @param target the object to test * * @return true if the target is an armor component */ boolean isArmorComponent(obj_id target) { return isArmorComponent(getGameObjectType(target)); } /** * Tests if an object GOT is an armor component. * * @param armorGOT the game object type to test * * @return true if the target is an armor component */ boolean isArmorComponent(int armorGOT) { return (isGameObjectTypeOf(armorGOT, GOT_armor) && (armorGOT == GOT_armor_layer || armorGOT == GOT_armor_segment || armorGOT == GOT_armor_core)); } /** * Tests if an object is an armor layer. * * @param target the object to test * * @return true if the target is an armor layer */ boolean isArmorLayer(obj_id target) { return isGameObjectTypeOf(getGameObjectType(target), GOT_armor_layer); } /** * Tests if an object is a final armor piece. * * @param target the object to test * * @return true if the target is a final armor piece */ boolean isFinalArmor(obj_id target) { return isFinalArmor(getGameObjectType(target)); } /** * Tests if an object GOT is a final armor piece. * * @param armorGOT the game object type to test * * @return true if the target is a final armor piece */ boolean isFinalArmor(int armorGOT) { return (isGameObjectTypeOf(armorGOT, GOT_armor) && (armorGOT != GOT_armor_layer && armorGOT != GOT_armor_segment && armorGOT != GOT_armor_core && armorGOT != GOT_armor_psg)); } /** * Tests if an object is a personal shield generator. * * @param target the object to test * * @return true if the target is a psg */ boolean isPsg(obj_id target) { return isPsg(getGameObjectType(target)); } /** * Tests if an object GOT is a personal shield generator. * * @param armorGOT the game object type to test * * @return true if the target is a psg */ boolean isPsg(int armorGOT) { return armorGOT == GOT_armor_psg; } /** * Returns the armor datatable row that contains the data for a given piece of armor. * * @param armor the armor to find the row for * * @return the row index, or -1 on error */ int getArmorDatatableRow(obj_id armor) { if ( !isValidArmor(armor) ) { debugServerConsoleMsg(null, "WARNING: armor.getArmorDatatableRow called with invalid target " + armor); return -1; } if ( utils.hasScriptVar(armor, SCRIPTVAR_CACHED_DATATABLE_ROW) ) { return utils.getIntScriptVar(armor, SCRIPTVAR_CACHED_DATATABLE_ROW); } string rowName = null; int armorType = getGameObjectType(armor); if ( armorType == GOT_armor_layer ) { rowName = DATATABLE_LAYER_REFERENCE_ROW; } else if ( armorType == GOT_armor_segment ) { rowName = DATATABLE_SEGMENT_ROW; } else if ( armorType == GOT_armor_core ) { int armorLevel = getArmorLevel(armor); if ( armorLevel >= 0 ) rowName = DATATABLE_CORE_ROW + armorLevel; } else if ( armorType == GOT_armor_psg ) { int armorLevel = getArmorLevel(armor); if ( armorLevel >= 0 ) rowName = DATATABLE_PSG_ROW + armorLevel; } else { int armorLevel = getArmorLevel(armor); if ( armorLevel >= 0 ) rowName = DATATABLE_FINAL_ROW + armorLevel; else rowName = DATATABLE_GENERIC_ROW; } int armorLevelRow = dataTableSearchColumnForInt(getStringCrc(rowName), DATATABLE_TYPE_COL, DATATABLE_ARMOR); if ( armorLevelRow < 0) armorLevelRow = -1; else utils.setScriptVar(armor, SCRIPTVAR_CACHED_DATATABLE_ROW, armorLevelRow); return armorLevelRow; } /** * Returns the root objvar list name that the armor data should be saved to. * * @param armor the armor to test * * @return the objvar list name the armor's objvars should use, or null if the GOT is not an armor type */ string getArmorBaseObjvar(obj_id armor) { return getArmorBaseObjvar(getGameObjectType(armor)); } /** * Returns the root objvar list name that the armor data should be saved to. * * @param armorGOT the armor's game object type * * @return the objvar list name the armor's objvars should use, or null if the GOT is not an armor type */ string getArmorBaseObjvar(int armorGOT) { //if ( !isGameObjectTypeOf( armorGOT, GOT_armor ) && !isGameObjectTypeOf( armorGOT, GOT_creature ) ) // return null; if ( isArmorComponent(armorGOT) ) { // armor component return craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME; } // final armor return OBJVAR_ARMOR_BASE; } /** * Returns the absolute attribute value for a scaled attribute. * * @param value the value to convert * @param row the datatable row to use * @param column the datatable min value column (we assume the max value column is column + 1) * * @return the attribute value, or Float.MIN_VALUE on error */ float getAbsoluteArmorAttribute(float value, int row, string column) { int columnIndex = dataTableFindColumnNumber(DATATABLE_ARMOR, column); if ( columnIndex == -1 ) { debugServerConsoleMsg(null, "WARNING armor.getAbsoluteArmorAttribute passed bad column " + column); return Float.MIN_VALUE; } else if ( row < 0 || row >= dataTableGetNumRows(DATATABLE_ARMOR) ) { debugServerConsoleMsg(null, "WARNING armor.getAbsoluteArmorAttribute passed bad row " + row); return Float.MIN_VALUE; } float min_value = dataTableGetInt(DATATABLE_ARMOR, row, columnIndex); float max_value = dataTableGetInt(DATATABLE_ARMOR, row, columnIndex+1); return value * (max_value - min_value) + min_value; } /** * Returns the absolute attribute values for a scaled armor special protections. * * @param armor the armor we're getting the data for (may be null, just used for debug output) * @param layerName the armor datatable row name for the layer type * @param value the relative special protection value * @param row the armor datatable row to use to compute the actual protection value * * @return a dictionary with the special protection values, or null on error */ dictionary getAbsoluteArmorSpecialProtection(obj_id armor, string layerName, float value, int row) { int layerRow = dataTableSearchColumnForInt(getStringCrc(layerName), DATATABLE_TYPE_COL, DATATABLE_ARMOR); if ( layerRow < 0 ) { CustomerServiceLog("armor", "WARNING: armor/component " + armor + " has bad objvar " + craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + "." + layerName); return null; } float newValue = getAbsoluteArmorAttribute(value, row, DATATABLE_MIN_SPEC_PROT_COL); if ( newValue == Float.MIN_VALUE ) return null; string attrib_inc = dataTableGetString(DATATABLE_ARMOR, layerRow, DATATABLE_ATTRIB_INC_COL); string attrib_dec = dataTableGetString(DATATABLE_ARMOR, layerRow, DATATABLE_ATTRIB_DEC_COL); string[] attrib_incs = split(attrib_inc, ','); string[] attrib_decs = split(attrib_dec, ','); dictionary protections = new dictionary(); if ( attrib_incs != null ) { for ( int i = 0; i < attrib_incs.length; ++i ) protections.addFloat(attrib_incs[i], newValue); } if ( attrib_decs != null ) { for ( int i = 0; i < attrib_decs.length; ++i ) protections.addFloat(attrib_decs[i], -newValue); } return protections; } /** * Returns the penalties associated with a given armor level and category. * * @param armorLevel the armor level (AL_basic, AL_standard, etc) * @param armorCategory the armor category (AC_reconnaissance, AC_battle, etc) * * @return the penalty %s, or null on error */ float[] getArmorPenalties(int armorLevel, int armorCategory) { if (armorLevel == AL_none || armorCategory == AC_none) return null; Object o = ARMOR_PENALTY_TYPE_MAP.get(new Integer(armorCategory * 10 + armorLevel)); if ( o == null || !(o instanceof Integer) ) return null; int row = dataTableSearchColumnForInt(((Integer)o).intValue(), DATATABLE_TYPE_PENALTY_COL, DATATABLE_ARMOR_PENALTY); if ( row < 0 ) return null; float[] penalties = new float[ARMOR_PENALTY_INDEXES.length]; for ( int i = 0; i < penalties.length; ++i ) { o = ARMOR_PENALTY_COLUMN_MAP.get(ARMOR_PENALTY_INDEXES[i]); if ( o == null || !(o instanceof string) ) return null; penalties[i] = dataTableGetFloat(DATATABLE_ARMOR_PENALTY, row, (string)o); } return penalties; } /** * Returns the penalties associated with a given armor level, category, and general protection value. * * @param generalProtection the armor general protection amount * @param armorRow the row in the armor datatable for the armor * @param armorLevel the armor level (AL_basic, AL_standard, etc) * @param armorCategory the armor category (AC_reconnaissance, AC_battle, etc) * * @return the penalty %s, or null on error */ float[] getScaledArmorPenalties(obj_id armor, float generalProtection, int armorRow, int armorLevel, int armorCategory) { float[] penalties = null; if ( isIdValid(armor) ) { penalties = utils.getFloatArrayScriptVar(armor, SCRIPTVAR_CACHED_PENALTIES); if ( penalties != null ) return penalties; } penalties = getArmorPenalties(armorLevel, armorCategory); if ( penalties != null ) { // get the % of the max gp value for the general protection float gpMin = dataTableGetInt(DATATABLE_ARMOR, armorRow, DATATABLE_MIN_GEN_PROT_COL); float gpMax = dataTableGetInt(DATATABLE_ARMOR, armorRow, DATATABLE_MAX_GEN_PROT_COL); float gpPercent = 1.0f; if ( gpMax != gpMin ) gpPercent = (generalProtection - gpMin) / (gpMax - gpMin); for ( int i = 0; i < penalties.length; ++i ) penalties[i] *= gpPercent; } if (isIdValid(armor) && penalties != null) { utils.setScriptVar(armor, SCRIPTVAR_CACHED_PENALTIES, penalties); } return penalties; } /** * Returns the penalties for a specific piece of armor after being reduced by a players certifications * * @param player the player * @param armorCategory the armor category (AC_reconnaissance, AC_battle, etc) * @param penalties the base penalties of the armor * * @return the penalty %s, or null on error */ float[] reduceCertifiedArmorPenalties(obj_id player, int armorCategory, float[] penalties) { if (armorCategory == AC_none) return penalties; string[] armorCatNames = { "recon", "battle", "assault" }; string[] armorPenaltyNames = { "move", "accuracy", "firerate" }; float[] certPenalties = new float[penalties.length]; for (int i = 0; i < ARMOR_DEFENSE_PENALTY; i++) { string certName = armorCatNames[armorCategory]+"_"+armorPenaltyNames[i]+"_mitigate_"; certPenalties[i] = penalties[i]; for (int j = 3; j > 0; j--) { if (hasCommand(player, certName+j)) { certPenalties[i] -= ARMOR_CERTIFICATION_REDUCTIONS[armorCategory][i][j-1]; if (certPenalties[i] < 0f) certPenalties[i] = 0f; break; } } } certPenalties[ARMOR_DEFENSE_PENALTY] = penalties[ARMOR_DEFENSE_PENALTY]; return certPenalties; } /** * Cumulates all worn armor penalties and saves highest values in scriptvars on player * * @param player the player * * @return nothing */ void calculateArmorHinderances(obj_id player) { float movePenalty = 0; float toHitPenalty = 0; float fireRatePenalty = 0; obj_id[] wornItems = metrics.getWornItems(player); if ( wornItems != null ) { for (int i = 0; i < wornItems.length; i++) { //make sure it is armor and not hand or boot armor int got = getGameObjectType(wornItems[i]); if (isGameObjectTypeOf(got, GOT_armor)&& (!isGameObjectTypeOf(got, GOT_armor_foot) && !isGameObjectTypeOf(got, GOT_armor_hand))) { float[] penalties; penalties = getFinalArmorPenalties(player, wornItems[i]); if (penalties == null) continue; if (penalties[0] > movePenalty) movePenalty = penalties[0]; if (penalties[1] > toHitPenalty) toHitPenalty = penalties[1]; if (penalties[2] > fireRatePenalty) fireRatePenalty = penalties[2]; } } } utils.setScriptVar(player, "combat.armor.toHitPenalty", toHitPenalty); utils.setScriptVar(player, "combat.armor.fireRatePenalty", fireRatePenalty); // Add new debuffs, or remove the old buffs if (movePenalty > 0) buff.applyBuff(player, ##"armorhinderancemove", 0, movePenalty); else buff.removeBuff(player, ##"armorhinderancemove"); if (toHitPenalty > 0) buff.applyBuff(player, ##"armorhinderanceaccuracy"); else buff.removeBuff(player, ##"armorhinderanceaccuracy"); if (fireRatePenalty > 0) buff.applyBuff(player, ##"armorhinderancefirerate"); else buff.removeBuff(player, ##"armorhinderancefirerate"); //debug chunk /* if (buff.hasBuff(player, "armorHinderanceFireRate")) sendSystemMessageTestingOnly(player, "YES YOU HAVE fire rate hindrance. FIRE RATE PENALTY: " + fireRatePenalty); else sendSystemMessageTestingOnly(player, "NO YOU DON'T HAVE fire rate hindrance. FIRE RATE PENALTY: " + fireRatePenalty); if (buff.hasBuff(player, "armorHinderanceAccuracy")) sendSystemMessageTestingOnly(player, "YES YOU HAVE Accuracy hindrance. TO HIT PENALTY: " + toHitPenalty); else sendSystemMessageTestingOnly(player, "NO YOU DON'T HAVE Accuracy hindrance. TO HIT PENALTY: " + toHitPenalty); if (buff.hasBuff(player, "armorHinderanceMove")) sendSystemMessageTestingOnly(player, "YES YOU HAVE move hindrance. MOVE PENALTY: " + movePenalty); else sendSystemMessageTestingOnly(player, "NO YOU DON'T HAVE move hindrance. MOVE PENALTY: " + movePenalty); */ return; } /** * Calculates whether the player is wearing armor, and store the information in a scriptvar * * @param player the player * * @return nothing */ void calculateWornArmor(obj_id player) { obj_id[] wornItems = metrics.getWornItems(player); int armorCount = 0; if ( wornItems != null ) { for (int i = 0; i < wornItems.length; i++) { int got = getGameObjectType(wornItems[i]); if ( isGameObjectTypeOf(got, GOT_armor) && !isGameObjectTypeOf(got, GOT_armor_psg) ) { armorCount++; // optimization; no one currently uses the actual // value in SCRIPTVAR_ARMOR_COUNT; they only care // whether or not it's > 0, so we can stop as soon // as we have found 1 worn armor break; } } } if ( armorCount > 0 ) utils.setScriptVar(player, SCRIPTVAR_ARMOR_COUNT, armorCount); else utils.removeScriptVar(player, SCRIPTVAR_ARMOR_COUNT); return; } /** * Returns the final certified penalties for a specified piece of armor * * @param player the player * @param armorPiece a piece of armor * * @return the penalty %s, or null on error */ float[] getFinalArmorPenalties(obj_id player, obj_id armorPiece) { int armorType = getGameObjectType(armorPiece); if ( armor.isFinalArmor(armorType) ) armorType = GOT_armor; int armorLevel = -1; int armorCategory = -1; int armorRow = armor.getArmorDatatableRow(armorPiece); if ( armorRow < 0 ) { CustomerServiceLog("armor", "WARNING: could not find armor datatable row for armor " + armorPiece); return null; } string armorBaseObjVar = armor.getArmorBaseObjvar(armorType); obj_var_list armorData = getObjVarList(armorPiece, armorBaseObjVar); if ( armorData == null ) { CustomerServiceLog("armor", "WARNING: unable to get armor data for armor/component " + armorPiece); return null; } if ( armorType == GOT_armor || armorType == GOT_armor_core || armorType == GOT_armor_psg ) { // get the armor level and category if ( !armorData.hasObjVar(armor.OBJVAR_ARMOR_LEVEL) ) { CustomerServiceLog("armor", "WARNING: armor " + armorPiece + " missing objvar " + armor.OBJVAR_ARMOR_LEVEL); return null; } if ( !armorData.hasObjVar(armor.OBJVAR_ARMOR_CATEGORY) ) { CustomerServiceLog("armor", "WARNING: armor " + armorPiece + " missing objvar " + armor.OBJVAR_ARMOR_CATEGORY); return null; } armorLevel = armorData.getIntObjVar(armor.OBJVAR_ARMOR_LEVEL); armorCategory = armorData.getIntObjVar(armor.OBJVAR_ARMOR_CATEGORY); } float general_protection = armor.getArmorGeneralProtection(armorPiece); float[] penalties = null; if ( general_protection > 0 && armorLevel >= 0 && armorCategory >= 0 ) { // penalties are based on general protection, so get them now penalties = armor.getScaledArmorPenalties(armorPiece, general_protection, armorRow, armorLevel, armorCategory); } if ( penalties != null ) { penalties = armor.reduceCertifiedArmorPenalties(player, armorCategory, penalties); } return penalties; } /** * Returns true if the player has the basic certifcation for the armor category * * @param player the player * @param armorPiece a piece of armor * * @return true or false */ boolean isArmorCertified(obj_id player, obj_id armorPiece) { if ( !isValidArmor(armorPiece) ) return true; int armorType = getGameObjectType(armorPiece); int armorCategory = -1; if ( isGameObjectTypeOf(armorType, GOT_cybernetic) ) return true;//everyone is certed for this stuff, yo. if ( isPsg(armorPiece) ) return true; if ( armor.isFinalArmor(armorType) ) armorType = GOT_armor; int armorRow = armor.getArmorDatatableRow(armorPiece); if ( armorRow < 0 ) { if(hasScript(armorPiece, "item.armor.new_armor")) { CustomerServiceLog("armor", "WARNING: could not find armor datatable row for armor " + armorPiece); return false; } return true; // we assume that we tested a piece of non-armor, hence armor certs shouldn't matter. } if(hasObjVar (armorPiece, "factionrestricted.rebel")) { if(!factions.isRebel(player)) { return false; } } if(hasObjVar (armorPiece, "factionrestricted.imperial")) { if(!factions.isImperial(player)) { return false; } } string armorBaseObjVar = armor.getArmorBaseObjvar(armorType); obj_var_list armorData = getObjVarList(armorPiece, armorBaseObjVar); // figure out the armorCategory if(armorData != null) { // try to grab the armorCategory from the objvar if(!armorData.hasObjVar(armor.OBJVAR_ARMOR_CATEGORY)) { CustomerServiceLog("armor", "WARNING: armor " + armorPiece + " missing objvar " + armor.OBJVAR_ARMOR_CATEGORY); return false; } else { armorCategory = armorData.getIntObjVar(armor.OBJVAR_ARMOR_CATEGORY); } } else // armorData == null && not a foot or hand { CustomerServiceLog("armor", "WARNING: could not determine armor type for armor " + armorPiece); return false; } // sanity check if(armorCategory < 0) { CustomerServiceLog("armor", "WARNING: could not determine armor type for armor " + armorPiece); return false; } if(hasCommand(player, "wear_all_armor") ) return true; /************ Old cert check ************ string[] armorCatNames = { "recon", "battle", "assault" }; string[] armorPenaltyNames = { "move", "accuracy", "firerate" }; for (int i = 0; i < ARMOR_ATTACK_PENALTY; i++) { string certName = armorCatNames[armorCategory]+"_"+armorPenaltyNames[i]+"_mitigate_1"; if (hasCommand(player, certName)) { return true; } } ****************************************/ return false; } /** * Convert scaled armor data from a range in one row of the armor datatable to a different row. Ex: * scale a layer special protection value to a segment special protection value. * * @param value the value to convert * @param fromRow the datatable row to convert from * @param toRow the datatable row to convert to * @param column the datatable min value column (we assume the max value column is column + 1) * * @return the new scaled value */ float convertArmorData(float value, int fromRow, int toRow, string column) { int columnIndex = dataTableFindColumnNumber(DATATABLE_ARMOR, column); if ( columnIndex == -1 ) { debugServerConsoleMsg(null, "WARNING armor.convertArmorData passed bad column " + column); return value; } float min_value = dataTableGetInt(DATATABLE_ARMOR, fromRow, columnIndex); float max_value = dataTableGetInt(DATATABLE_ARMOR, fromRow, columnIndex+1); float newValue = value * (max_value - min_value) + min_value; min_value = dataTableGetInt(DATATABLE_ARMOR, toRow, columnIndex); max_value = dataTableGetInt(DATATABLE_ARMOR, toRow, columnIndex+1); if ( max_value != min_value ) newValue = (newValue - min_value) / (max_value - min_value); else newValue = 1.0f; return newValue; } /** * Takes armor component data from an objvar list, converts it to a new scale, and stores it back on the manf schematic. * * @param schematic the schematic to store the scaled data on * @param componentData the objvar list with the data to scale * @param fromRow the row in the armor datatable to scale from * @param toRow the row in the armor datatable to scale to */ void rescaleComponentData(obj_id schematic, obj_var_list componentData, int fromRow, int toRow) { if ( !hasObjVar(schematic, craftinglib.COMPONENT_ATTRIBUTE_INTERNAL_OBJVAR_NAME + ".isScaled") ) { // we want some of the component data to be re-scaled Enumeration keys = componentData.keys(); while ( keys.hasMoreElements() ) { string key = (string)(keys.nextElement()); float value = componentData.getFloatObjVar(key); float newValue = rescaleArmorData(key, fromRow, toRow, value); if ( newValue != Float.MIN_VALUE) { debugServerConsoleMsg(null, "Armor scaling component data " + key + " from " + value + " to " + newValue + "(fromRow = " + fromRow + ", toRow = " + toRow + ")"); setObjVar(schematic, craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + ".scaled." + key, newValue); removeObjVar(schematic, craftinglib.COMPONENT_ATTRIBUTE_INTERNAL_OBJVAR_NAME + "." + key); } } setObjVar(schematic, craftinglib.COMPONENT_ATTRIBUTE_INTERNAL_OBJVAR_NAME + ".isScaled", true); } } /** * Converts an armor attribute from one row of the armor datatable to another. * * @param attrib the armor attribute to scale * @param fromRow the row in the armor datatable to scale from * @param toRow the row in the armor datatable to scale to * @param value the value to convert * * @return the rescaled value, or Float.MIN_VALUE if the attribute wasn't one we cared about */ float rescaleArmorData(string attrib, int fromRow, int toRow, float value) { float newValue = Float.MIN_VALUE; if ( attrib.startsWith(OBJVAR_LAYER_PREFIX) || attrib == "special_protection" ) { newValue = convertArmorData(value, fromRow, toRow, DATATABLE_MIN_SPEC_PROT_COL); } else if ( attrib == OBJVAR_GENERAL_PROTECTION ) { newValue = convertArmorData(value, fromRow, toRow, DATATABLE_MIN_GEN_PROT_COL); } else if ( attrib == OBJVAR_CONDITION ) { newValue = convertArmorData(value, fromRow, toRow, DATATABLE_MIN_CONDITION_COL); } return newValue; } /** * Updates armor attributes (set by resources) for any component data. * * @param schematic the manf schematic the component data is stored on * @param itemAttributes the armor attributes set by the resources * @param armorRow which row in the armor datatable contains the armor info */ void updateItemAttributes(obj_id schematic, draft_schematic.attribute[] itemAttributes, int armorRow) { for ( int i = 0; i < itemAttributes.length; ++i ) { if (itemAttributes[i] == null) continue; if ( itemAttributes[i].name.equals(OBJVAR_GENERAL_PROTECTION) ) { float resourceScale = dataTableGetFloat(DATATABLE_ARMOR, armorRow, DATATABLE_RES_GEN_PROT_COL) / 100.0f; float value = itemAttributes[i].currentValue * resourceScale; string componentName = craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + ".scaled." + OBJVAR_GENERAL_PROTECTION; if ( hasObjVar(schematic, componentName) ) { float componentValue = getFloatObjVar(schematic, componentName); value += componentValue; int minProtection = dataTableGetInt(DATATABLE_ARMOR, armorRow, DATATABLE_MIN_GEN_PROT_COL); int maxProtection = dataTableGetInt(DATATABLE_ARMOR, armorRow, DATATABLE_MAX_GEN_PROT_COL); if ( maxProtection != minProtection && minProtection != 0 ) { // this is an adjustment that needs to be made if the min value is not 0 value += ((float)minProtection) / ((float)(maxProtection - minProtection)); } debugServerConsoleMsg(null, "Armor scaling general protection from resource value " + itemAttributes[i].currentValue + ", component value " + componentValue + " to " + value); } else { debugServerConsoleMsg(null, "Armor scaling general protection from resource value " + itemAttributes[i].currentValue + " to " + value); } itemAttributes[i].currentValue = value; } else if ( itemAttributes[i].name.equals(OBJVAR_CONDITION) ) { float resourceScale = dataTableGetFloat(DATATABLE_ARMOR, armorRow, DATATABLE_RES_CONDITION_COL) / 100.0f; float value = itemAttributes[i].currentValue * resourceScale; string componentName = craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + ".scaled." + OBJVAR_CONDITION; if ( hasObjVar(schematic, componentName) ) { float componentValue = getFloatObjVar(schematic, componentName); value += componentValue; int minCondition = dataTableGetInt(DATATABLE_ARMOR, armorRow, DATATABLE_MIN_CONDITION_COL); int maxCondition = dataTableGetInt(DATATABLE_ARMOR, armorRow, DATATABLE_MAX_CONDITION_COL); if ( maxCondition != minCondition && minCondition != 0 ) { // this is an adjustment that needs to be made if the min value is not 0 value += ((float)minCondition) / ((float)(maxCondition - minCondition)); } debugServerConsoleMsg(null, "Armor scaling condition from resource value " + itemAttributes[i].currentValue + ", component value " + componentValue + " to " + value); } else { debugServerConsoleMsg(null, "Armor scaling condition from resource value " + itemAttributes[i].currentValue + " to " + value); } itemAttributes[i].currentValue = value; } } } /** * Saves any scaled data on a schematic to a prototype object. * * @param schematic the schematic where the scaled data is stored * @param prototype the prototype to store the data on * @param itemAttributes attributes we have resource data for * @param objvarBase the objvar list to store the data in */ void saveScaledDataToPrototype(obj_id schematic, obj_id prototype, draft_schematic.attribute[] itemAttributes, string objvarBase) { clearCachedData(prototype); obj_var_list componentData = getObjVarList(schematic, craftinglib.COMPONENT_ATTRIBUTE_OBJVAR_NAME + ".scaled"); if ( componentData != null ) { Enumeration keys = componentData.keys(); while ( keys.hasMoreElements() ) { string key = (string)(keys.nextElement()); float value = componentData.getFloatObjVar(key); if (value < 0) value = 0; else if (value > 1.0f) value = 1.0f; boolean haveAttribute = false; for ( int i = 0; i < itemAttributes.length; ++i ) { if ( itemAttributes[i].name.equals(key) ) { haveAttribute = true; break; } } if ( !haveAttribute ) { setObjVar(prototype, objvarBase + "." + key, value); } } } } /** * Tests if an object appears to be a valid piece of armor. * * @param target the object to test * * @return true if the object is armor, false if not */ boolean isValidArmor(obj_id target) { if ( !isValidId(target) ) { /* debugServerConsoleMsg(null, "WARNING: armor.isValidArmor called with invalid target " + target); LOG("armor.scriptlib","WARNING: armor.isValidArmor called with invalid target " + target); */ return false; } int armorType = getGameObjectType(target); string baseObjvar = getArmorBaseObjvar(armorType); if (armorType != GOT_armor_layer && armorType != GOT_armor_segment) { if ( !hasObjVar(target, baseObjvar + "." + OBJVAR_ARMOR_LEVEL) ) { /* if(!hasObjVar(target, "armor.fake_armor") ) // Yes, we know we're fake armor but don't spam us for it. { LOG("armor.scriptlib","WARNING: armor.isValidArmor called with target " + target + "(" + getName(target) + ")" + " that has no armor level"); debugServerConsoleMsg(null, "WARNING: armor.isValidArmor called with target " + target + "(" + getName(target) + ")" + " that has no armor level"); } */ return false; } if ( !hasObjVar(target, baseObjvar + "." + OBJVAR_ARMOR_CATEGORY) ) // Yes, we know we're fake armor but don't spam us for it. { /* if(!hasObjVar(target, "armor.fake_armor") ) { LOG("armor.scriptlib","WARNING: armor.isValidArmor called with target " + target + "(" + getName(target) + ")" + " that has no armor category"); debugServerConsoleMsg(null, "WARNING: armor.isValidArmor called with target " + target + "(" + getName(target) + ")" + " that has no armor category"); } */ return false; } } else { if ( !hasScript(target, SCRIPT_ARMOR_EXAMINE) ) { // LOG("armor.scriptlib","WARNING: armor.isValidArmor called with target " + target + "(" + getName(target) + ")" + " that does not have the armor examine script"); // debugServerConsoleMsg(null, "WARNING: armor.isValidArmor called with target " + target + "(" + getName(target) + ")" + " that does not have the armor examine script"); return false; } } return true; } /** * Returns the level of a piece of armor or armor component. * * @param armor the armor to test * * @return the armor level, or -1 on error */ int getArmorLevel(obj_id armor) { if ( isValidId(armor) && utils.hasScriptVar(armor, SCRIPTVAR_CACHED_LEVEL) ) { return utils.getIntScriptVar(armor, SCRIPTVAR_CACHED_LEVEL); } string baseObjvar = getArmorBaseObjvar(armor); if ( baseObjvar == null ) return -1; string armorLevelObjVar = baseObjvar + "." + OBJVAR_ARMOR_LEVEL; if ( !hasObjVar(armor, armorLevelObjVar) ) { CustomerServiceLog("armor", "WARNING: armor object (" + armor + ") missing objvar " + armorLevelObjVar); return -1; } int armorLevel = getIntObjVar(armor, armorLevelObjVar); utils.setScriptVar(armor, SCRIPTVAR_CACHED_LEVEL, armorLevel); return armorLevel; } /** * Returns the category of a piece of armor or armor component. * * @param armor the armor to test * * @return the armor category, or -1 on error */ int getArmorCategory(obj_id armor) { if ( isValidId(armor) && utils.hasScriptVar(armor, SCRIPTVAR_CACHED_CATEGORY) ) { return utils.getIntScriptVar(armor, SCRIPTVAR_CACHED_CATEGORY); } string baseObjvar = getArmorBaseObjvar(armor); if ( baseObjvar == null ) return -1; string armorCategoryObjVar = baseObjvar + "." + OBJVAR_ARMOR_CATEGORY; if ( !hasObjVar(armor, armorCategoryObjVar) ) { CustomerServiceLog("armor", "WARNING: armor object (" + armor + ") missing objvar " + armorCategoryObjVar); return -1; } int armorCategory = getIntObjVar(armor, armorCategoryObjVar); utils.setScriptVar(armor, SCRIPTVAR_CACHED_CATEGORY, armorCategory); return armorCategory; } // faster getter, no recalc during combatr. int getCombatArmorGeneralProtection(obj_id objTarget) { return utils.getIntScriptVar(objTarget, SCRIPTVAR_CACHED_GENERAL_PROTECTION); } /** * Returns the general protection value for an armor or armor component. * * @param armor the armor to test * * @return the gp value, or -1 on error */ int getArmorGeneralProtection(obj_id armor) { if (isGameObjectTypeOf(getGameObjectType(armor), GOT_creature)) { return utils.getIntScriptVar(armor, SCRIPTVAR_CACHED_GENERAL_PROTECTION); } return _getArmorGeneralProtection(armor, getArmorDatatableRow(armor)); } // faster getter, no recalc during combatr. dictionary getCombatArmorSpecialProtections(obj_id objTarget) { return utils.getDictionaryScriptVar(objTarget, SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS); } /** * Returns the special protection values for an armor or armor component. * * @param armor the armor to test * * @return the protection values, in the form of a dictionary of protection type->value pairs, or null on error/no special protections */ dictionary getArmorSpecialProtections(obj_id armor) { return _getArmorSpecialProtections(armor, getArmorDatatableRow(armor)); } /** * Returns the special protection values for a psg, modified by its efficiency. * * @param armor the armor to test * * @return the protection values, in the form of a dictionary of protection type->value pairs, or null on error/no special protections */ dictionary getPsgSpecialProtections(obj_id armor) { return _getPsgSpecialProtections(armor, getArmorDatatableRow(armor)); } /** * Returns a psg's current efficiency. */ float getPsgEfficiency(obj_id psg) { if ( isPsg(psg) ) return utils.getFloatScriptVar(psg, SCRIPTVAR_PSG_EFFICIENCY); return 0; } /** * Sets a psg's current efficiency. */ void setPsgEfficiency(obj_id psg, float efficiency) { if ( isPsg(psg) ) { if ( efficiency > 1.0f ) efficiency = 1.0f; else if ( efficiency < 0 ) efficiency = 0; utils.setScriptVar(psg, SCRIPTVAR_PSG_EFFICIENCY, efficiency); int iconCount = (int)(efficiency * 100.0f + 0.5f); if ( iconCount == 0 ) { // there's a bug where a count of 0 doesn't affect the client iconCount = 1; } setCount(psg, iconCount); } } /***** ARMOR LOOT FUNCTIONS ********************************************/ /** * Sets the base data for a piece of armor. * * @param target the object we want to make armor * @param armorLevel the armor level (AL_basic, etc.) defined in base_class.java * @param armorCategory the armor category (AC_battle, ect.) defined in base_class.java * * @return true on success, false if passed bad data */ boolean initializeArmor(obj_id target, int armorLevel, int armorCategory) { if ( !isValidId(target) ) { CustomerServiceLog("armor", "WARNING: armor.initializeArmor called with invalid target " + target); return false; } if ( armorLevel < AL_none || armorLevel >= AL_max ) { //debugSpeakMsg( target, "armor level is shit: "+ armorLevel ); CustomerServiceLog("armor", "WARNING: armor.initializeArmor called for target " + target + " with invalid armor level " + armorLevel); return false; } if ( armorCategory < AC_none || armorCategory >= AC_max ) { //debugSpeakMsg( target, "armor category is shit" ); CustomerServiceLog("armor", "WARNING: armor.initializeArmor called for target " + target + " with invalid armor category " + armorCategory); return false; } clearCachedData(target); int armorType = getGameObjectType(target); string objvarBase = getArmorBaseObjvar(armorType); if ( objvarBase == null ) { //debugSpeakMsg( target, "armor base objvar is bogus" ); CustomerServiceLog("armor", "WARNING: armor.initializeArmor called for target " + target + " with bad GOT: " + armorType); return false; } else if ( armorType != GOT_armor_layer && armorType != GOT_armor_segment ) { //debugSpeakMsg( target, "s'all setup" ); setObjVar(target, objvarBase + "." + OBJVAR_ARMOR_LEVEL, armorLevel); setObjVar(target, objvarBase + "." + OBJVAR_ARMOR_CATEGORY, armorCategory); } if ( !hasScript(target, SCRIPT_ARMOR_EXAMINE) ) attachScript(target, SCRIPT_ARMOR_EXAMINE); return true; } /** * Initializes the data on a personal shield generator. * * @param target the psg to init * @param rechargeRate the recharge rate, in efficiency % per sec * @param protection the amount of protection the psg provides * @param condition the hp of the psg */ boolean initializePsg(obj_id target, float rechargeRate, int protection, int condition) { if (!isPsg(target)) return false; clearCachedData(target); setObjVar(target, OBJVAR_ARMOR_BASE + "." + OBJVAR_PSG_RECHARGE_RATE, (rechargeRate / 100.0f) * protection); setAbsoluteArmorSpecialProtection(target, DATATABLE_PSG_LAYER, protection); setMaxHitpoints(target, condition); return true; } // Gets the template row from the master table int getTemplateRowFromMasterTable(string template) { if(template.trim().equals("")) { return -1; } int row = dataTableSearchColumnForString(template, "crafted_object_template", DATATABLE_MASTER_ARMOR); return row; } // gets the armor category by template from the master table, given the string template int getArmorCategoryByTemplate(string template) { int row = getTemplateRowFromMasterTable(template); //LOG("loot", "TEMPLATE ="+ template +" ,row =" + row); if(row < 0) { return -1; } int cat = dataTableGetInt(DATATABLE_MASTER_ARMOR, row, "category"); //LOG("loot", "CATEGORY ="+ cat); return cat; } /** * Sets the base data to give an object armor attributes. * * @param target the object we want to make armor * @param armorLevel the armor level (AL_basic, etc.) defined in base_class.java * @param armorCategory the armor category (AC_battle, ect.) defined in base_class.java * @param generalProtection the general protection of the armor, should be between 0 and 1 * @param condition the condition of the armor, should be between 0 and 1 * * @return true on success, false if passed bad data */ boolean setArmorDataPercent(obj_id target, int armorLevel, int armorCategory, float generalProtection, float condition) { if ( !initializeArmor(target, armorLevel, armorCategory) ) return false; return _setArmorDataPercent(target, generalProtection, condition, false); } /** * Sets the stats for a piece of armor. * * @param target the armor we want to set stats for * @param generalProtection the general protection of the armor, should be between 0 and 1 (although we will accept values > 1) * @param condition the condition of the armor, should be between 0 and 1 (although we will accept values > 1) * * @return true on success, false if the target is bad */ boolean setArmorDataPercent(obj_id target, float generalProtection, float condition) { return _setArmorDataPercent(target, generalProtection, condition, true); } /** * Sets the base data to give an object armor attributes. The values passed in are absolute values instead of a relative 0->1 scale. * * @param target the object we want to make armor * @param armorLevel the armor level (AL_basic, etc.) defined in base_class.java * @param armorCategory the armor category (AC_battle, ect.) defined in base_class.java * @param generalProtection the general protection of the armor * @param condition the condition of the armor * * @return true on success, false if passed bad data */ boolean setAbsoluteArmorData(obj_id target, int armorLevel, int armorCategory, int generalProtection, int condition) { if ( !initializeArmor(target, armorLevel, armorCategory) ) return false; return setAbsoluteArmorData(target, generalProtection, condition); } /** * Sets the stats for a piece of armor. * * @param target the armor we want to set stats for * @param generalProtection the general protection of the armor * @param condition the condition of the armor * @param testForValidArmor flag to verify that the armor object is ok * * @return true on success, false if the target is bad */ boolean setAbsoluteArmorData(obj_id target, int generalProtection, int condition) { clearCachedData(target); // convert the absolute values to relative values, based on the armor datatable int armorLevelRow = getArmorDatatableRow(target); if ( armorLevelRow < 0) { CustomerServiceLog("armor", "WARNING: armor._setAbsoluteArmorData unable to get armor level row for armor " + target); return false; } float minGP = dataTableGetInt(DATATABLE_ARMOR, armorLevelRow, DATATABLE_MIN_GEN_PROT_COL); float maxGP = dataTableGetInt(DATATABLE_ARMOR, armorLevelRow, DATATABLE_MAX_GEN_PROT_COL); float minCondition = dataTableGetInt(DATATABLE_ARMOR, armorLevelRow, DATATABLE_MIN_CONDITION_COL); float maxCondition = dataTableGetInt(DATATABLE_ARMOR, armorLevelRow, DATATABLE_MAX_CONDITION_COL); if ( minGP == maxGP && minCondition == maxCondition ) { // we don't need to save anything return true; } float scaledGP = 0; float scaledCondition = 0; if (maxGP != minGP) scaledGP = (generalProtection - minGP) / (maxGP - minGP); if ( maxCondition != minCondition ) scaledCondition = (condition - minCondition) / (maxCondition - minCondition); // save the scaled armor data // note: we don't need to test for valid armor since we got a good armorLevelRow return _setArmorDataPercent(target, scaledGP, scaledCondition, armorLevelRow, false); } /** * Removes all the armor data on an item. * * @param target the armor to remove the data from */ void removeAllArmorData(obj_id target) { if ( isValidArmor(target) ) { string baseObjvar = getArmorBaseObjvar(target); if ( baseObjvar != null ) removeObjVar(target, baseObjvar); clearCachedData(target); } } /** * Adds the effect of a special protection layer to a piece of armor. * * @param target the armor to add the protection to * @param specialLayer the effective layer the protection will give, as defined in the armor datatable * @param protection the amount of protection given (this should be a positive value) * * @return true on success, false if bad data was given */ boolean setAbsoluteArmorSpecialProtection(obj_id target, int specialLayer, int protection) { if ( !isValidArmor(target) ) { CustomerServiceLog("armor", "WARNING: armor.setAbsoluteArmorSpecialProtection called with invalid target " + target); return false; } if ( specialLayer < 0 || specialLayer >= NUM_DATATABLE_ARMOR_LAYERS ) { CustomerServiceLog("armor", "WARNING: armor.setAbsoluteArmorSpecialProtection called for target " + target + " with bad special layer " + specialLayer); return false; } clearCachedData(target); float maxProtection = dataTableGetInt(DATATABLE_ARMOR, specialLayer, DATATABLE_MAX_SPEC_PROT_COL); if ( maxProtection <= 0 ) return true; float minProtection = dataTableGetInt(DATATABLE_ARMOR, specialLayer, DATATABLE_MIN_SPEC_PROT_COL); float scaledProtection = 1.0f; if ( maxProtection != minProtection ) scaledProtection = (protection - minProtection) / (maxProtection - minProtection); return setArmorSpecialProtectionPercent(target, specialLayer, scaledProtection); } /** * Adds the effect of a special protection layer to a piece of armor. * * @param target the armor to add the protection to * @param specialLayer the effective layer the protection will give, as defined in the armor datatable * @param protection the amount of protection given, should be between 0 and 1 (although we will accept values > 1) * * @return true on success, false if bad data was given */ boolean setArmorSpecialProtectionPercent(obj_id target, int specialLayer, float protection) { if ( !isValidArmor(target) ) { CustomerServiceLog("armor", "WARNING: armor.setArmorSpecialProtectionPercent called with invalid target " + target); return false; } if ( specialLayer < 0 || specialLayer >= NUM_DATATABLE_ARMOR_LAYERS ) { CustomerServiceLog("armor", "WARNING: armor.setArmorSpecialProtectionPercent called for target " + target + " with bad special layer " + specialLayer); return false; } clearCachedData(target); // see if the protection needs to be rescaled int armorLevelRow = getArmorDatatableRow(target); if ( armorLevelRow < 0 ) { CustomerServiceLog("armor", "WARNING: armor.setAbsoluteArmorSpecialProtection unable to get armor level row for armor " + target); return false; } if ( armorLevelRow != specialLayer ) { // rescale the protection to the armor level row protection = rescaleArmorData(OBJVAR_LAYER_PREFIX + specialLayer, specialLayer, armorLevelRow, protection); } if ( protection < MIN_ARMOR_RANGE ) protection = MIN_ARMOR_RANGE; else if ( protection > MAX_ARMOR_RANGE ) protection = MAX_ARMOR_RANGE; string baseObjvar = getArmorBaseObjvar(target); if ( baseObjvar == null ) return false; setObjVar(target, baseObjvar + "." + OBJVAR_LAYER_PREFIX + specialLayer, protection); return true; } /** * Removes the effect of a special protection layer to a piece of armor. * * @param target the armor to add the protection to * @param specialLayer the effective layer the protection will give, as defined in the armor datatable * * @return true on success, false if bad data was given */ boolean removeArmorSpecialProtection(obj_id target, int specialLayer) { if ( !isValidArmor(target) ) { CustomerServiceLog("armor", "WARNING: armor.removeArmorSpecialProtection called with invalid target " + target); return false; } if ( specialLayer < 0 || specialLayer >= NUM_DATATABLE_ARMOR_LAYERS ) { CustomerServiceLog("armor", "WARNING: armor.removeArmorSpecialProtection called for target " + target + " with bad special layer " + specialLayer); return false; } clearCachedData(target); string baseObjvar = getArmorBaseObjvar(target); if ( baseObjvar == null ) return false; removeObjVar(target, baseObjvar + "." + OBJVAR_LAYER_PREFIX + specialLayer); return true; } /***** INTERNAL FUNCTIONS - DO NOT CALL FROM A SCRIPT ******************/ /** * Returns the general protection value for an armor or armor component. * * @param armor the armor to test * * @return the gp value, or -1 on error */ int _getArmorGeneralProtection(obj_id armor, int row) { if ( !isValidArmor(armor) ) { CustomerServiceLog("armor", "WARNING: armor._getArmorGeneralProtection called with invalid target " + armor); return -1; } if (utils.hasScriptVar(armor, SCRIPTVAR_CACHED_GENERAL_PROTECTION)) { return utils.getIntScriptVar(armor, SCRIPTVAR_CACHED_GENERAL_PROTECTION); } string genProtectionObjVar = getArmorBaseObjvar(armor) + "." + OBJVAR_GENERAL_PROTECTION; if ( !hasObjVar(armor, genProtectionObjVar) ) { CustomerServiceLog("armor", "WARNING: armor object (" + armor + ") missing objvar " + genProtectionObjVar); return -1; } float value = getFloatObjVar(armor, genProtectionObjVar); float genProtection = getAbsoluteArmorAttribute(value, row, DATATABLE_MIN_GEN_PROT_COL); utils.setScriptVar(armor, SCRIPTVAR_CACHED_GENERAL_PROTECTION, (int)genProtection); return (int)genProtection; } /** * Returns the special protection values for an armor or armor component. * * @param armor the armor to test * @param row the armor datatable row to get the data from * * @return the protection values, in the form of a dictionary of protection type->value pairs, or null on error/no special protections */ dictionary _getArmorSpecialProtections(obj_id armor, int row) { if ( !isValidArmor(armor) ) { CustomerServiceLog("armor", "WARNING: armor._getArmorSpecialProtections called with invalid target " + armor); return null; } // return the cached data if we've got it dictionary protections = utils.getDictionaryScriptVar(armor, SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS); if ( isArmorLayer(armor) ) utils.removeScriptVar(armor, SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS); if ( protections != null ) return protections; string baseObjvar = getArmorBaseObjvar(armor); boolean isLayer = isArmorLayer(armor); float generalProtection = 0; if ( !isLayer ) generalProtection = getArmorGeneralProtection(armor); for (int i = 0; i < NUM_DATATABLE_ARMOR_LAYERS; i++) { obj_var layer = getObjVar(armor, baseObjvar + "." + OBJVAR_LAYER_PREFIX + i); if ( layer != null ) { dictionary layerProtections = getAbsoluteArmorSpecialProtection(armor, DATATABLE_LAYER_ROW + i, layer.getFloatData(), row); if ( layerProtections != null ) { if ( protections == null ) protections = new dictionary(); Enumeration keys = layerProtections.keys(); while ( keys.hasMoreElements() ) { Object key = keys.nextElement(); // don't let the protection go negative (unless the armor is a layer) float protection = layerProtections.getFloat(key); if ( !isLayer && protection + generalProtection < 0 ) protection = -generalProtection; protections.addFloat(key, protection); } } } } if ( protections != null ) utils.setScriptVar(armor, SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS, protections); return protections; } /** * Returns the special protection values for a psg, modified by its efficiency. * * @param armor the armor to test * @param row the armor datatable row to get the data from * * @return the protection values, in the form of a dictionary of protection type->value pairs, or null on error/no special protections */ dictionary _getPsgSpecialProtections(obj_id armor, int row) { if ( !isIdValid(armor) || !isPsg(armor) ) return null; dictionary protections = _getArmorSpecialProtections(armor, row); if ( protections == null ) return null; dictionary newProtections = new dictionary(protections.size() * 2); float generalProtection = getArmorGeneralProtection(armor); float efficiency = utils.getFloatScriptVar(armor, OBJVAR_ARMOR_BASE + "." + OBJVAR_PSG_CURRENT_EFFICIENCY); if ( efficiency > 1.0f ) efficiency = 1.0f; else if ( efficiency < 0 ) efficiency = 0; Enumeration keys = protections.keys(); while ( keys.hasMoreElements() ) { string key = (string)(keys.nextElement()); float value = protections.getFloat(key) * efficiency; if ( value + generalProtection < 0 ) value = -generalProtection; newProtections.put(key, value); } return newProtections; } /** * Sets the stats for a piece of armor. NOTE: this function is meant to be called internally from this scriptlib only. * * @param target the armor we want to set stats for * @param generalProtection the general protection of the armor, should be between 0 and 1 (although we will accept values > 1) * @param condition the condition of the armor, should be between 0 and 1 (although we will accept values > 1) * @param armorLevelRow the row in the armor datatable that is used for the armor data * @param testForValidArmor flag to verify that the armor object is ok * * @return true on success, false if the target is bad */ boolean _setArmorDataPercent(obj_id target, float generalProtection, float condition, boolean testForValidArmor) { int armorLevelRow = getArmorDatatableRow(target); if ( armorLevelRow < 0) { CustomerServiceLog("armor", "WARNING: armor._setArmorDataPercent unable to get armor level row for armor " + target); return false; } return _setArmorDataPercent(target, generalProtection, condition, armorLevelRow, testForValidArmor); } /** * Sets the stats for a piece of armor. NOTE: this function is meant to be called internally from this scriptlib only. * * @param target the armor we want to set stats for * @param generalProtection the general protection of the armor, should be between 0 and 1 (although we will accept values > 1) * @param condition the condition of the armor, should be between 0 and 1 (although we will accept values > 1) * @param armorLevelRow the row in the armor datatable that is used for the armor data * @param testForValidArmor flag to verify that the armor object is ok * * @return true on success, false if the target is bad */ boolean _setArmorDataPercent(obj_id target, float generalProtection, float condition, int armorLevelRow, boolean testForValidArmor) { if ( testForValidArmor && !isValidArmor(target) ) { CustomerServiceLog("armor", "WARNING: armor._setArmorDataPercent called with invalid target " + target); return false; } clearCachedData(target); if ( generalProtection < MIN_ARMOR_RANGE) generalProtection = MIN_ARMOR_RANGE; else if (generalProtection > MAX_ARMOR_RANGE) generalProtection = MAX_ARMOR_RANGE; if ( condition < MIN_ARMOR_RANGE ) condition = MIN_ARMOR_RANGE; else if (condition > MAX_ARMOR_RANGE) condition = MAX_ARMOR_RANGE; string objvarBase = getArmorBaseObjvar(target); if ( objvarBase == null) return false; // store the gp as an objvar setObjVar(target, objvarBase + "." + OBJVAR_GENERAL_PROTECTION, generalProtection); // store the condition as an objvar, and as the max hp value for the armor setObjVar(target, objvarBase + "." + OBJVAR_CONDITION, condition); float hp = getAbsoluteArmorAttribute(condition, armorLevelRow, DATATABLE_MIN_CONDITION_COL); if ( hp != Float.MIN_VALUE ) setMaxHitpoints(target, (int)hp); return true; } void recalculateArmorForMob(obj_id mob) { int mobGeneralArmor = (int)getFloatObjVar(mob, OBJVAR_ARMOR_BASE + "." + OBJVAR_GENERAL_PROTECTION); int innateProtectionAll = (int)getSkillStatisticModifier(mob, "expertise_innate_protection_all"); utils.setScriptVar(mob, SCRIPTVAR_CACHED_GENERAL_PROTECTION, (mobGeneralArmor + innateProtectionAll)); } void recalculateArmorForPlayer(obj_id objPlayer) { // It is possible for a debuff to call recalculate armor. For mobs, there is not much calculating to do... if(!isPlayer(objPlayer) && isGameObjectTypeOf(getGameObjectType(objPlayer), GOT_creature)) { recalculateArmorForMob(objPlayer); return; } trial.bumpSession(objPlayer, "displayDefensiveMods"); messageTo(objPlayer, "setDisplayOnlyDefensiveMods", trial.getSessionDict(objPlayer, "displayDefensiveMods") , 5, false); const string strArmor[] = { "chest2", "bicep_r", "bicep_l", "pants2", "bracer_upper_r", "bracer_upper_l", "hat" }; const float[] fltWeightings = { 5, // chest 2 (BP armor), mutually exclusive w/chest 1 1,//bicep_r 1,//bicep_l 3,//pants2 1,//bracer_upper_r 1, //bracer_upper_l 2 //head }; const float[] fltSpecialProts = new float[armor.DATATABLE_SPECIAL_PROTECTIONS.length]; // yeah i dunno why this is called datatable.. float fltGeneralProtection = 0; float fltTotal = 0; int lastArmorType = -1; int numLikePieces = 0; int armorSetWorn = -1; int numClothingPieces = 0; int[] armorTallyType = { 0, 0, 0}; for(int intI = 0; intI < strArmor.length; intI++) { obj_id objArmor = getObjectInSlot(objPlayer, strArmor[intI]); float fltWeight= fltWeightings[intI]; // If we don't have a BP take a look at the shirt slot instead. if(strArmor[intI].equals("chest2") && isIdNull(objArmor)) { objArmor = getObjectInSlot(objPlayer, "chest1"); fltWeight = 5; } // If we don't have a pants2 take a look at the pants1 slot instead. if(strArmor[intI].equals("pants2") && isIdNull(objArmor)) { objArmor = getObjectInSlot(objPlayer, "pants1"); fltWeight = 3; } // Clothing and other armor-like animals outside of the crafting system. if(hasObjVar(objArmor, "armor.fake_armor") ) { for(int i = 0; i < fltSpecialProts.length; i++) { if(hasObjVar(objArmor, "armor.fake_armor." + DATATABLE_SPECIAL_PROTECTIONS[i]) ) { float fltSpecialProt = (float)getIntObjVar(objArmor, "armor.fake_armor." + DATATABLE_SPECIAL_PROTECTIONS[i]); if(fltSpecialProt!=0) { fltSpecialProts[i] += fltWeight * fltSpecialProt; } } } } //check for worn item with Armor objvar if (hasObjVar(objArmor, "armor.general_protection_clothing")) { recalculatePseudoArmorForPlayer(objPlayer, objArmor, true); return; } // A cybernetic piece is always "correct" when determining whether a full armor set is worn if(isGameObjectTypeOf(getGameObjectType(objArmor), GOT_cybernetic) ) { numLikePieces++; } // Check for wearing full armor set or no armor for entertainers if(hasObjVar(objArmor, "armor.armorCategory")) { int thisArmorType = getIntObjVar(objArmor, "armor.armorCategory"); if(intI == 0) { lastArmorType = getIntObjVar(objArmor, "armor.armorCategory"); numLikePieces++; } else if(intI == numLikePieces && thisArmorType == lastArmorType) { numLikePieces++; if(strArmor.length == numLikePieces) { armorSetWorn = lastArmorType; } } if(thisArmorType > -1 && thisArmorType < 3) { armorTallyType[thisArmorType]++; } // sendSystemMessageTestingOnly(objPlayer, "thisArmorType = " + thisArmorType + " lastArmorType = " + lastArmorType + " numLikePieces = " + numLikePieces + " armorSetWorn = " + armorSetWorn); } // calculate armor! if(isIdValid(objArmor)&&isValidArmor(objArmor)&&!hasObjVar(objArmor, "armor.fake_armor") ) { LOG("armor.recalculateArmorForPlayer ", "checking is armor of obj_id: "+objArmor); dictionary dctSpecialProtections= getArmorSpecialProtections(objArmor); float fltArmorGeneralProtection = (float)utils.getIntScriptVar(objArmor, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION); float fltReduction = combat.getArmorDecayPercentage(objArmor); fltArmorGeneralProtection = fltArmorGeneralProtection * fltReduction; // reduce due to damage if (dctSpecialProtections != null) { for(int intJ = 0; intJ < fltSpecialProts.length; intJ++) { if (armor.DATATABLE_SPECIAL_PROTECTIONS[intJ] != null) { float fltSpecialProt = dctSpecialProtections.getFloat(armor.DATATABLE_SPECIAL_PROTECTIONS[intJ]); if(fltSpecialProt!=0) { fltSpecialProts[intJ] += fltWeight * fltSpecialProt; } } } } fltGeneralProtection += fltWeight * fltArmorGeneralProtection; } fltTotal += fltWeight; } float fltTempProtection = 0; float fltGenericProtection = getSkillStatisticModifier(objPlayer, "expertise_overridable_protection_generic"); float fltInnateProtectionAll = getSkillStatisticModifier(objPlayer, "expertise_innate_protection_all"); fltGeneralProtection = fltGeneralProtection / fltTotal; dictionary dctProtections = new dictionary(); for(int intI = 0; intI < fltSpecialProts.length; intI++) { fltSpecialProts[intI] = fltSpecialProts[intI] / fltTotal; fltTempProtection = 0; // Expertise innate protection bonus. fltSpecialProts[intI] += fltInnateProtectionAll; fltSpecialProts[intI] += getSkillStatisticModifier(objPlayer, "expertise_innate_protection_" + DATATABLE_SPECIAL_PROTECTIONS[intI]); fltSpecialProts[intI] += getSkillStatisticModifier(objPlayer, "expertise_armorset_protection_" + armorSetWorn + "_" + DATATABLE_SPECIAL_PROTECTIONS[intI]); // Generic Overridable Protection. if( (fltSpecialProts[intI] + fltGeneralProtection) > 0) { fltTempProtection = getSkillStatisticModifier(objPlayer, "expertise_overridable_protection_" + DATATABLE_SPECIAL_PROTECTIONS[intI]) + fltGenericProtection; if(((fltSpecialProts[intI] + fltGeneralProtection) * 0.5) < fltTempProtection) { fltSpecialProts[intI] += fltTempProtection - ( (fltSpecialProts[intI] + fltGeneralProtection) * 0.5); } } else { fltSpecialProts[intI] += fltTempProtection = getSkillStatisticModifier(objPlayer, "expertise_overridable_protection_" + DATATABLE_SPECIAL_PROTECTIONS[intI]) + fltGenericProtection; } int intMod = (int)(fltSpecialProts[intI] + fltGeneralProtection + 0.5f); applySkillStatisticModifier(objPlayer, DATATABLE_SPECIAL_PROTECTIONS[intI], -1*getSkillStatisticModifier(objPlayer,DATATABLE_SPECIAL_PROTECTIONS[intI]) ); applySkillStatisticModifier(objPlayer, DATATABLE_SPECIAL_PROTECTIONS[intI], intMod); dctProtections.put(armor.DATATABLE_SPECIAL_PROTECTIONS[intI], fltSpecialProts[intI]); } // now we have our mathemagical thing. // lets's set them on the player.. utils.setScriptVar(objPlayer, armor.SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS, dctProtections); int intProtection =(int) fltGeneralProtection; utils.setScriptVar(objPlayer, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION , intProtection); utils.setScriptVar(objPlayer, "armor.armor_set_worn", armorSetWorn); utils.setScriptVar(objPlayer, "armor.armor_type_tally", armorTallyType); return; } void recalculatePseudoArmorForPlayer(obj_id objPlayer, obj_id item, boolean applyArmor) { int intProtection = getIntObjVar(item, "armor.general_protection_clothing"); int generalProtectionValue = 0; const float[] fltSpecialProts = new float[armor.DATATABLE_SPECIAL_PROTECTIONS.length]; float fltInnateProtectionAll = getSkillStatisticModifier(objPlayer, "expertise_innate_protection_all"); trial.bumpSession(objPlayer, "displayDefensiveMods"); messageTo(objPlayer, "setDisplayOnlyDefensiveMods", trial.getSessionDict(objPlayer, "displayDefensiveMods") , 5, false); if(intProtection > 0) { dictionary dctProtections = new dictionary(); //create the special protections array for(int intI = 0; intI < fltSpecialProts.length; intI++) { int armorValue = 0; if(applyArmor) { armorValue = intProtection; fltSpecialProts[intI] += fltInnateProtectionAll; fltSpecialProts[intI] += getSkillStatisticModifier(objPlayer, "expertise_innate_protection_" + DATATABLE_SPECIAL_PROTECTIONS[intI]); fltSpecialProts[intI] += getSkillStatisticModifier(objPlayer, "expertise_armorset_protection_" + "_" + DATATABLE_SPECIAL_PROTECTIONS[intI]); int intMod = (int)(fltSpecialProts[intI]+ armorValue + 0.5f); //set values for character sheet applySkillStatisticModifier(objPlayer, DATATABLE_SPECIAL_PROTECTIONS[intI], -1*getSkillStatisticModifier(objPlayer,DATATABLE_SPECIAL_PROTECTIONS[intI]) ); applySkillStatisticModifier(objPlayer, DATATABLE_SPECIAL_PROTECTIONS[intI], intMod); //add special protections to dictionary dctProtections.put(armor.DATATABLE_SPECIAL_PROTECTIONS[intI], fltSpecialProts[intI]); } } //set special protections utils.setScriptVar(objPlayer, armor.SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS, dctProtections); if(applyArmor) { generalProtectionValue = intProtection; int[] armorTallyType = {7, 0, 0}; utils.setScriptVar(objPlayer, "armor.armor_type_tally", armorTallyType); } //set base protection utils.setScriptVar(objPlayer, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION , generalProtectionValue); } // If we're taking the armor off, call the normal recalculate because we could be wearing something with protections. if(!applyArmor) recalculateArmorForPlayer(objPlayer); return; } string getPseudoArmorLevel(int pseudoLevel) { // Faint if (pseudoLevel < 2000) return POWER_1; // Weak else if (pseudoLevel < 4000) return POWER_2; // Light else if (pseudoLevel < 5000) return POWER_3; // Medium else if (pseudoLevel < 6000) return POWER_4; // Strong else if (pseudoLevel < 7000) return POWER_5; // Great else if (pseudoLevel < 8000) return POWER_6; // One With Force else return POWER_7; } boolean hasExpertiseArmorSetBonus(obj_id wearer) { if(!isPlayer(wearer) ) return false; if(!utils.hasScriptVar(wearer, "armor.armor_set_worn") ) return false; else if(utils.getIntScriptVar(wearer, "armor.armor_set_worn") > -1) return true; return false; } int getExpertiseArmorSetId(obj_id wearer) { int armorSetId = -1; if(utils.hasScriptVar(wearer, "armor.armor_set_worn") ) return utils.getIntScriptVar(wearer, "armor.armor_set_worn"); return armorSetId; } //function to turn armor into new appearance schems boolean turnArmorIntoSchem(obj_id player, obj_id armor) { if(!isIdValid(armor) || !exists(armor)) return false; if(!isIdValid(player) || !exists(player)) return false; string template = getTemplateName(armor); int row = dataTableSearchColumnForString(template, "crafted_object_template", DATATABLE_MASTER_ARMOR); if(row < 0) return false; string schemName = dataTableGetString(DATATABLE_MASTER_ARMOR, row, "schematic_name"); if(schemName == null || schemName.equals("")) return false; string limitedUseTemplate = "object/tangible/loot/loot_schematic/deconstructed_armor_schematic.iff"; obj_id pInv = utils.getInventoryContainer(player); obj_id newSchem = createObjectOverloaded(limitedUseTemplate, pInv); CustomerServiceLog("new_armor_conversion", "New schematic("+schemName+") converted from armor("+armor+") for player "+getFirstName(player)+"("+player+")"); if(!isIdValid(newSchem) || !exists(newSchem)) return false; setName(newSchem, utils.packStringId(getNameFromTemplate(template))); setObjVar(newSchem, "loot_schematic.schematic", "object/draft_schematic/armor/" + schemName + ".iff"); setObjVar(newSchem, "loot_schematic.uses", 1); int gotType = getGameObjectType(armor); if(gotType == GOT_cybernetic_forearm) { setObjVar(newSchem, "loot_schematic.skill_req", "class_engineering_phase1_master"); } else { setObjVar(newSchem, "loot_schematic.skill_req", "class_munitions_phase1_master"); } //if it was a bioLinked armor piece, we need to persist that. obj_id bioLink = getBioLink(armor); if(isIdValid(bioLink)) { setBioLink(newSchem, bioLink); } //if it is a no-trade item being deconstructed, it needs to bio-link to the deconstructor if(utils.isItemNoDrop(armor)) { setBioLink(newSchem, player); } attachScript(newSchem, "item.loot_schematic.loot_schematic"); CustomerServiceLog("new_armor_conversion", "armor("+armor+") about to be destroyed on player "+getFirstName(player)+"("+player+") because it was converted into schematic "+schemName+"("+newSchem+")"); //TODO add Bio-Link stuff return true; }