package script.library; import script.*; import script.combat_engine.*; import java.util.Vector; public class combat extends script.base_script { public combat() { } public static final float IN_COMBAT_REGEN_MULT = 1.0f; public static final float OUT_COMBAT_REGEN_MULT = 4.0f; public static final int PLAYER_COMBAT_BASE_DAMAGE = 60; public static final int PLAYER_ATTACKER_DAMAGE_LEVEL_MULTIPLIER = 5; public static final int RANGED_WEAPON = -1; public static final int MELEE_WEAPON = -2; public static final int ALL_WEAPONS = -3; public static final int ALL_LIGHTSABERS = -4; public static final int FORCE_POWER = -5; public static final int ALL_BUT_HEAVY = -6; public static final int WEAPON_TYPE_FORCE_POWER = -1; public static final int LEFT_CLICK_DEFAULT = 1; public static final int RIGHT_CLICK_SPECIAL = 2; public static final int B_RIFLE = 0x00000001; public static final int B_CARBINE = 0x00000002; public static final int B_PISTOL = 0x00000004; public static final int B_HEAVY = 0x00000008; public static final int B_1HAND_MELEE = 0x00000010; public static final int B_2HAND_MELEE = 0x00000020; public static final int B_UNARMED = 0x00000040; public static final int B_POLEARM = 0x00000080; public static final int B_THROWN = 0x00000100; public static final int B_1HAND_LIGHTSABER = 0x00000200; public static final int B_2HAND_LIGHTSABER = 0x00000400; public static final int B_POLEARM_LIGHTSABER = 0x00000800; public static final int B_GROUND_TARGETTING = 0x00001000; public static final int B_DIRECTIONAL = 0x00002000; public static final int B_RANGED = 0x08000000; public static final int B_MELEE = 0x10000000; public static final int B_ALL = 0x20000000; public static final int B_ALL_LIGHTSABERS = 0x40000000; public static final int[] weaponTypeToBitValueMap = { B_ALL | B_RANGED | B_RIFLE, B_ALL | B_RANGED | B_CARBINE, B_ALL | B_RANGED | B_PISTOL, B_ALL | B_RANGED | B_HEAVY, B_ALL | B_MELEE | B_1HAND_MELEE, B_ALL | B_MELEE | B_2HAND_MELEE, B_ALL | B_MELEE | B_UNARMED, B_ALL | B_MELEE | B_POLEARM, B_ALL | B_RANGED | B_THROWN, B_ALL | B_ALL_LIGHTSABERS | B_1HAND_LIGHTSABER, B_ALL | B_ALL_LIGHTSABERS | B_2HAND_LIGHTSABER, B_ALL | B_ALL_LIGHTSABERS | B_POLEARM_LIGHTSABER, B_ALL | B_RANGED | B_HEAVY | B_GROUND_TARGETTING, B_ALL | B_RANGED | B_HEAVY | B_DIRECTIONAL }; public static final int EFFECT_MAX_DURATION = 180; public static final float DELAY_EFFECT_TIMER = 30.0f; public static final int TKA_JEDI_ARMOR_HARD_CAP = 9000; public static final int MAX_RE_EXOTIC_ELEMENTAL_PENETRATION_SKILLMOD = 10; public static final int MOVEMENT_MODIFIER_REUSE_TIME = 30; public static final int AI_LEVEL_ACCURACY_BONUS = 65; public static final int AI_LEVEL_DEFENSE_BONUS = 65; public static final int AI_LEVEL_DAMAGE_BONUS = 40; public static final int AI_LEVEL_DAMAGE_PENALTY = 25; public static final int AI_LEVEL_MIN_DAMAGE_CAP = 50; public static final int AI_LEVEL_MAX_DAMAGE_CAP = 75; public static final float AI_CON_SCALE_MULT = 8.0f; public static final int VALID_TARGET_NONE = -1; public static final int VALID_TARGET_STANDARD = 0; public static final int VALID_TARGET_MOB = 1; public static final int VALID_TARGET_CREATURE = 2; public static final int VALID_TARGET_NPC = 3; public static final int VALID_TARGET_DROID = 4; public static final int VALID_TARGET_PVP = 5; public static final int VALID_TARGET_JEDI = 6; public static final int VALID_TARGET_DEAD = 7; public static final int VALID_TARGET_FRIEND = 8; public static final int NON_ATTACK = 0; public static final int NON_DAMAGE_ATTACK = 4; public static final int HEAL = 5; public static final int DELAY_ATTACK = 6; public static final int REVIVE = 7; public static final int ACTION_NAME = 0; public static final int WEAPON_NAME = 1; public static final int NO_ATTACK_NAME = 2; public static final int NO_HIT_SPAM = 3; public static final float ARMOR_BREAK_REDUCTION = 0.5f; public static final String ATTACKER_DATA = "cached_attacker_data"; public static final String ATTACKER_BASE_CRIT = ATTACKER_DATA + ".base_crit"; public static final String ATTACKER_PVP_CRIT = ATTACKER_DATA + ".pvp_crit"; public static final String ATTACKER_DROID_CRIT = ATTACKER_DATA + ".droid_crit"; public static final String ATTACKER_NPC_CRIT = ATTACKER_DATA + ".npc_crit"; public static final String ATTACKER_CREATURE_CRIT = ATTACKER_DATA + ".creature_crit"; public static final String ATTACKER_HIT_BONUS = ATTACKER_DATA + ".hit_bonus"; public static final String ATTACKER_STRIKETHROUGH = ATTACKER_DATA + ".strikethrough_chance"; public static final String ATTACKER_REDUCE_PARRY = ATTACKER_DATA + ".reduce_parry"; public static final String ATTACKER_REDUCE_BLOCK = ATTACKER_DATA + ".reudce_block"; public static final String ATTACKER_REDUCE_DODGE = ATTACKER_DATA + ".reduce_dodge"; public static final String ATTACKER_REDUCE_GLANCING = ATTACKER_DATA + ".reduce_glancing"; public static final String ATTACKER_REDUCE_EVADE = ATTACKER_DATA + ".reduce_evade"; public static final String DEFENDER_DATA = "cached_defender_data"; public static final String DEFENDER_REDUCE_CRIT = DEFENDER_DATA + ".crit_reduce"; public static final String DEFENDER_INCREASE_MISS = DEFENDER_DATA + ".increase_miss"; public static final String DEFENDER_REDUCE_STRIKETHROUGH = DEFENDER_DATA + ".reduce_strikethrough"; public static final String DEFENDER_REDUCE_PUNISH = DEFENDER_DATA + ".reduce_punish"; public static final String DEFENDER_GLANCING = DEFENDER_DATA + ".increase_glancing"; public static final String DEFENDER_PARRY = DEFENDER_DATA + ".parry_chance"; public static final String DEFENDER_DODGE = DEFENDER_DATA + ".dodge_chance"; public static final String DEFENDER_EVADE = DEFENDER_DATA + ".evade_chance"; public static final String DEFENDER_BLOCK = DEFENDER_DATA + ".block_chance"; public static final String[][] ARMOR_SLOT_LOCATIONS = { { "chest2", "chest2" }, { "hat", "hat" }, { "bicep_r", "bracer_upper_r" }, { "bicep_l", "bracer_upper_l" }, { "pants2", "pants2" }, { "pants2", "pants2" } }; public static final String PSG_SLOT_LOCATION = "utility_belt"; public static final float PSG_BASE_DMG_PER_HIT = 50.0f; public static final float PSG_DAMAGE_SCALE = 200.0f; public static final int CONE = 0; public static final int SINGLE_TARGET = 1; public static final int AREA = 2; public static final int TARGET_AREA = 3; public static final int DUAL_WIELD = 4; public static final int RAMPAGE = 5; public static final int RANDOM_HATE_TARGET = 6; public static final int RANDOM_HATE_TARGET_CONE = 7; public static final int RANDOM_HATE_TARGET_CONE_TERMINUS = 8; public static final int HATE_LIST = 9; public static final int RANDOM_HATE_MULTI = 10; public static final int AREA_PROGRESSIVE = 11; public static final int SPLIT_DAMAGE_TARGET_AREA = 12; public static final int DISTANCE_FARTHEST = 13; public static final String NONCOMBAT_DATATABLE = "datatables/combat/non_combat_data.iff"; public static final String WEAPON_LEVEL_TABLE = "datatables/combat/weapon_level.iff"; public static final int[] RANGED_WEAPONS = { WEAPON_TYPE_RIFLE, WEAPON_TYPE_LIGHT_RIFLE, WEAPON_TYPE_PISTOL, WEAPON_TYPE_HEAVY }; public static final int[] MELEE_WEAPONS = { WEAPON_TYPE_1HAND_MELEE, WEAPON_TYPE_2HAND_MELEE, WEAPON_TYPE_UNARMED, WEAPON_TYPE_POLEARM, WEAPON_TYPE_WT_1HAND_LIGHTSABER, WEAPON_TYPE_WT_2HAND_LIGHTSABER, WEAPON_TYPE_WT_POLEARM_LIGHTSABER }; public static final String VAR_VOLLEY_GROUPS = "squadleader.volleyGroups"; public static final String ID_VOLLEY_FIRE_PARTICLE = "squadleader.volleyFire"; public static final int VOLLEY_INTERVAL = 30; public static final String VAR_GROUP_VOLLEY_TARGET = "squadleader.curVolleyTarget"; public static final String VAR_GROUP_LAST_VOLLEY_TIME = "squadleader.lastVolleyTime"; public static final string_id SID_NONE = new string_id(); public static final String CHARGE_TARGET = "charge_target"; public static final String DIED_RECENTLY = "combat.died_recently"; public static final float RECENT_DEATH_EXPIRATION = 60.0f; public static final String DAMAGE_REDIRECT = "damage_redirect"; public static final String DO_ONCE_ATTACK_OVERRIDE = "default.do_once_attack_override"; public static final String DEFAULT_ATTACK_OVERRIDE = "default.default_attack_override"; public static final int FS_TAUNT_HATE_INCREASE_LOW = 750; public static final int FS_TAUNT_HATE_INCREASE_MID = 1200; public static final int FS_TAUNT_HATE_INCREASE_HIGH = 2000; public static final int FS_TAUNT_LOW_LEVEL = 64; public static final int FS_TAUNT_MID_LEVEL = 77; public static final int BH_TAUNT_HATE_INCREASE_LOW = 300; public static final int BH_TAUNT_HATE_INCREASE_MID = 750; public static final int BH_TAUNT_HATE_INCREASE_HIGH = 1200; public static final int BH_TAUNT_LOW_LEVEL = 43; public static final int BH_TAUNT_MID_LEVEL = 66; public static final String TEMP_COMBAT_BLOCK = "combat_debuff.all_actions_blocked"; public static final String PERSIST_COMBAT = "persist_combat"; public static final String BUFF_HATE_XFER = "aggroChannelself"; public static final int EGG_AURA_TRIGGER_MIN_RANGE = 15; public static final int EGG_AURA_TRIGGER_MAX_RANGE = 100; public static final int EGG_AURA_DURATION = 20; public static final int DR_ATTACKER_CRITICAL = 0; public static final int DR_ATTACKER_STRIKETHROUGH = 1; public static final int DR_DEFENDER_BLOCK = 2; public static final int DR_DEFENDER_DODGE = 3; public static final int DR_DEFENDER_PARRY = 4; public static final int DR_DEFENDER_GLANCE = 5; public static final int DR_DEFENDER_EVADE = 6; public static final int DR_ATTACKER_CRITICAL_2 = 7; public static void sendCombatSpam(obj_id attacker, obj_id defender, obj_id weapon, combat_engine.hit_result result, string_id attackName, boolean sendToAttacker, boolean sendToDefender, boolean sendToBystanders) throws InterruptedException { sendCombatSpam(attacker, defender, weapon, result, attackName, sendToAttacker, sendToDefender, sendToBystanders, COMBAT_RESULT_GENERIC); } public static void sendCombatSpam(obj_id attacker, obj_id defender, string_id weapon, combat_engine.hit_result result, string_id attackName, boolean sendToAttacker, boolean sendToDefender, boolean sendToBystanders) throws InterruptedException { sendCombatSpam(attacker, defender, weapon, result, attackName, sendToAttacker, sendToDefender, sendToBystanders, COMBAT_RESULT_GENERIC); } public static void sendCombatSpamMessage(obj_id player, string_id message) throws InterruptedException { sendCombatSpamMessage(player, message, COMBAT_RESULT_GENERIC); } public static void sendCombatSpamMessage(obj_id attacker, obj_id defender, string_id message, boolean sendToAttacker, boolean sendToDefender, boolean sendToBystanders) throws InterruptedException { sendCombatSpamMessage(attacker, defender, message, sendToAttacker, sendToDefender, sendToBystanders, COMBAT_RESULT_GENERIC); } public static void sendCombatSpamMessageProse(obj_id player, prose_package pp) throws InterruptedException { sendCombatSpamMessageProse(player, pp, COMBAT_RESULT_GENERIC); } public static void sendCombatSpamMessageProse(obj_id attacker, obj_id defender, prose_package pp, boolean sendToAttacker, boolean sendToDefender, boolean sendToBystanders) throws InterruptedException { sendCombatSpamMessageProse(attacker, defender, pp, sendToAttacker, sendToDefender, sendToBystanders, COMBAT_RESULT_GENERIC); } public static void doBasicCombatSpam(String attackName, attacker_results attackerResults, defender_results[] defenderResults, hit_result[] hitData) throws InterruptedException { string_id attackId = new string_id("cmd_n", attackName); doBasicCombatSpam(attackId, SID_NONE, null, attackerResults, defenderResults, hitData); } public static void doBasicCombatSpam(string_id attackId, string_id weaponId, obj_id weapon, attacker_results attackerResults, defender_results[] defenderResults, hit_result[] hitData) throws InterruptedException { if (isIdValid(weapon)) { for (int i = 0; i < defenderResults.length; ++i) { sendCombatSpam(attackerResults.id, defenderResults[i].id, weapon, hitData[i], attackId, true, true, true, defenderResults[i].result); } } else { for (int i = 0; i < defenderResults.length; ++i) { sendCombatSpam(attackerResults.id, defenderResults[i].id, weaponId, hitData[i], attackId, true, true, true, defenderResults[i].result); } } } public static boolean isAreaAttack(int attackType) throws InterruptedException { if (attackType == CONE || attackType == AREA || attackType == TARGET_AREA) { return true; } return false; } public static boolean breakMez(obj_id defender) throws InterruptedException { int[] buffs = buff.getAllBuffs(defender); if (buffs == null || buffs.length == 0) { return true; } for (int b : buffs) { for (int j = 1; j <= buff.MAX_EFFECTS; j++) { String effect = buff.getEffectParam(b, j); if (effect.equals("delay_attack")) { buff.removeBuff(defender, b); } } } return true; } public static int getAiLevelDiff(obj_id attacker, obj_id defender) throws InterruptedException { obj_id master = null; if (isPlayer(attacker)) { return 0; } if (!isPlayer(defender) && !isMob(defender)) { return 0; } if (isMob(attacker)) { master = getMaster(attacker); if (!isPlayer(attacker) && isIdValid(master)) { return 0; } } if (isMob(defender)) { master = getMaster(defender); if (!isPlayer(defender) && !isIdValid(master)) { return 0; } } int aiLevel = getLevel(attacker); int defenderLevel = getLevel(defender); if (!isMob(attacker)) { aiLevel = getIntObjVar(attacker, "intCombatDifficulty"); } obj_id defenderGroup = getGroupObject(defender); if (group.isGroupObject(defenderGroup)) { defenderLevel = getGroupObjectLevel(defenderGroup); } int buffBonus = getEnhancedSkillStatisticModifier(defender, "private_relative_level_mod"); defenderLevel += buffBonus; return (aiLevel - defenderLevel); } public static float getConBalanceScalar(int levelDiff) throws InterruptedException { float scale = (Math.abs(levelDiff) + AI_CON_SCALE_MULT) / (10.0f + AI_CON_SCALE_MULT); return scale; } public static int getWeaponDamage(obj_id objAttacker, obj_id objDefender, weapon_data cbtWeaponData, int intAttackerRoll, int intDefenderRoll) throws InterruptedException { int intFinalDamage = 0; float fltMinDamage = cbtWeaponData.minDamage; float fltMaxDamage = cbtWeaponData.maxDamage; if (!isPlayer(objAttacker)) { if (intAttackerRoll > intDefenderRoll) { return getAiDamage(objAttacker, objDefender, fltMinDamage, fltMaxDamage); } else { return (int)rand(fltMinDamage, fltMinDamage + (fltMaxDamage - fltMinDamage)); } } if (intAttackerRoll > intDefenderRoll) { int intDamage = (int)rand(fltMinDamage + (fltMaxDamage - fltMinDamage), fltMaxDamage); return intDamage; } else { int intDamage = (int)(rand(fltMinDamage, fltMinDamage + (fltMaxDamage - fltMinDamage))); return intDamage; } } public static int getAiDamage(obj_id attacker, obj_id defender, float minDamage, float maxDamage) throws InterruptedException { if (!isPlayer(attacker)) { int levelDiff = getAiLevelDiff(attacker, defender); combatLog(attacker, defender, "modifyAiRawDamage", "AI Level diff = " + levelDiff); float levelDiffMod = levelDiff * getConBalanceScalar(levelDiff); if (levelDiffMod < 0) { float damageMod = 1.0f + (levelDiffMod / 10.0f); combatLog(attacker, defender, "modifyAiRawDamage", "AI Damage Mod = " + damageMod); float minCap = minDamage / 2.0f; float maxCap = maxDamage / 2.0f; combatLog(attacker, defender, "modifyAiRawDamage", "Min Cap = " + minCap); combatLog(attacker, defender, "modifyAiRawDamage", "Max Cap = " + maxCap); minDamage *= damageMod; maxDamage *= damageMod; if (minDamage < minCap) { minDamage = minCap; } if (maxDamage < maxCap) { maxDamage = maxCap; } } else { float maxDmgMod = 1.0f + (levelDiffMod / 10.0f); combatLog(attacker, defender, "modifyAiRawDamage", "AI Max Damage Mod = " + maxDmgMod); maxDamage *= maxDmgMod; maxDamage += levelDiff; float minDmgMod = 1.0f + (levelDiffMod / (10.0f / levelDiff)); combatLog(attacker, defender, "modifyAiRawDamage", "AI Min Damage Mod = " + minDmgMod); minDamage *= minDmgMod; float minCap = maxDamage / 2.0f; if (minDamage > minCap) { minDamage = minCap; } } combatLog(attacker, defender, "modifyAiRawDamage", "New Min Damage = " + minDamage); combatLog(attacker, defender, "modifyAiRawDamage", "New Max Damage = " + maxDamage); } if (utils.checkConfigFlag("ScriptFlags", "e3Demo")) { if (isPlayer(defender)) { int intCurrentHP = getHealth(defender); int intMaxHP = getMaxHealth(defender); if ((minDamage > intMaxHP) || (maxDamage > intMaxHP) || (intCurrentHP < (intMaxHP / 2))) { minDamage = 1; maxDamage = 5; } } } int intDamage = (int)rand(minDamage, maxDamage); return intDamage; } public static void assignDamageCredit(obj_id attacker, obj_id defender, weapon_data weaponData, int damage) throws InterruptedException { String xpType = xp.getWeaponXpType(weaponData.weaponType); if (!isPlayer(attacker)) { if (pet_lib.isPet(attacker)) { obj_id master = getMaster(attacker); if (ai_lib.isMonster(attacker) && isIdValid(master) && hasSkill(master, "outdoors_creaturehandler_novice")) { xp.updateCombatXpList(defender, master, xp.CREATUREHANDLER, damage); xp.updateCombatXpList(defender, attacker, xp.CREATUREHANDLER, damage); return; } else if (isIdValid(master) && !beast_lib.isBeast(attacker)) { xp.updateCombatXpList(defender, master, xp.PET_DAMAGE, damage); xp.updateCombatXpList(defender, attacker, xpType, damage); return; } } } xp.updateCombatXpList(defender, attacker, xpType, damage); } public static boolean expertiseRandomBuffChance(obj_id attacker, combat_data actionData) throws InterruptedException { String specialLine = actionData.specialLine; if (getSkillStatisticModifier(attacker, "expertise_use_buff_chance_line_" + specialLine) > 0 || getSkillStatisticModifier(attacker, "private_use_buff_chance_line_" + specialLine) > 0) { int chance = getSkillStatisticModifier(attacker, "expertise_buff_chance_line_" + specialLine); if (rand(0, 99) > chance) { return false; } } return true; } public static boolean applyDefenderCombatBuffs(obj_id attacker, obj_id defender, weapon_data weaponData, combat_data actionData) throws InterruptedException { String actionName = actionData.actionName; String buffName = actionData.buffNameTarget; float buffStrength = actionData.buffStrengthTarget; float buffDuration = actionData.buffDurationTarget; if (buffName == null || buffName.equals("")) { return false; } if (!expertiseRandomBuffChance(attacker, actionData)) { return false; } combatLog(attacker, defender, "applyDefenderCombatBuffs", "Applying defender buff - " + buffName); String effectName = buff.getEffectParam(buffName, 2); if (effectName.equals("dot")) { effectName = buff.getEffectParam(buffName, 1); String type = effectName.substring((effectName.lastIndexOf("_") + 1), effectName.length()); if (dot.attemptDotResist(defender, type, 100, true)) { return false; } if (!dot.canApplyDotType(defender, type)) { return false; } } buffDuration += getExpertiseBuffDurationMods(attacker, actionData, buffName, buffDuration); return buff.applyBuff(defender, attacker, buffName, buffDuration, buffStrength); } public static boolean applyAttackerCombatBuffs(obj_id attacker, combat_data actionData) throws InterruptedException { String actionName = actionData.actionName; String buffName = actionData.buffNameSelf; float buffStrength = actionData.buffStrengthSelf; float buffDuration = actionData.buffDurationSelf; if (buffName == null || buffName.equals("")) { return false; } if (!expertiseRandomBuffChance(attacker, actionData)) { return false; } combatLog(attacker, null, "applyAttackerCombatBuffs", "Applying attacker buff - " + buffName); buffDuration += getExpertiseBuffDurationMods(attacker, actionData, buffName, buffDuration); return buff.applyBuff(attacker, attacker, buffName, buffDuration, buffStrength); } public static float getExpertiseBuffDurationMods(obj_id buffCaster, combat_data actionData, String buffName, float buffDuration) throws InterruptedException { float expertiseBuffDurationMod = 0.0f; dictionary dic = dataTableGetRow(buff.BUFF_TABLE, buffName); if (dic == null) { return expertiseBuffDurationMod; } String buffGroupName = dic.getString("GROUP1"); if (buffDuration == 0) { buffDuration = dic.getFloat("DURATION"); } String specialLine = actionData.specialLine; expertiseBuffDurationMod = buffDuration + getEnhancedSkillStatisticModifierUncapped(buffCaster, "expertise_buff_duration_line_" + specialLine); expertiseBuffDurationMod += getEnhancedSkillStatisticModifierUncapped(buffCaster, "expertise_buff_duration_group_" + toLower(buffGroupName)); expertiseBuffDurationMod += getEnhancedSkillStatisticModifierUncapped(buffCaster, "expertise_buff_duration_single_" + toLower(buffName)); return expertiseBuffDurationMod; } public static boolean applyCombatMovementModifier(obj_id attacker, obj_id defender, String actionName) throws InterruptedException { boolean isRoot = movement.isRoot(actionName); boolean isSnare = movement.isSnare(actionName); boolean isStun = movement.isStunEffect(actionName); prose_package pp = new prose_package(); String movementType = ""; if (isRoot) { movementType = "root"; } if (isSnare) { movementType = "snare"; } int lastMovementMod; if (isPlayer(defender)) { lastMovementMod = utils.getIntScriptVar(defender, "combat.movement." + movementType + ".time"); } else { lastMovementMod = utils.getIntScriptVar(defender, "combat.movement." + movementType + "." + attacker + ".time"); } if (lastMovementMod + MOVEMENT_MODIFIER_REUSE_TIME > getGameTime()) { showCombatText(defender, attacker, new string_id("combat_effects", "no_effect"), 1.5f, colors.WHITE); return false; } if (buff.hasBuff(defender, actionName) || !buff.canApplyBuff(defender, actionName)) { showCombatText(defender, attacker, new string_id("combat_effects", "no_effect"), 1.5f, colors.WHITE); return false; } if (isStun) { pp = prose.setStringId(pp, new string_id("combat_effects", "go_stunned")); showFlyTextPrivateProseWithFlags(defender, defender, pp, 1.5f, colors.LAWNGREEN, FLY_TEXT_FLAG_IS_SNARE); playClientEffectObj(defender, "appearance/pt_state_cantmove.prt", defender, "", null, "state_stunned"); playClientEffectObj(defender, "sound/sta_rooted_on.snd", defender, ""); } else if (isRoot) { pp = prose.setStringId(pp, new string_id("combat_effects", "go_rooted")); showFlyTextPrivateProseWithFlags(defender, defender, pp, 1.5f, colors.LAWNGREEN, FLY_TEXT_FLAG_IS_SNARE); playClientEffectObj(defender, "appearance/pt_state_cantmove.prt", defender, "", null, "state_rooted"); playClientEffectObj(defender, "sound/sta_rooted_on.snd", defender, ""); } else if (isSnare) { pp = prose.setStringId(pp, new string_id("combat_effects", "go_snare")); showFlyTextPrivateProseWithFlags(defender, defender, pp, 1.5f, colors.LAWNGREEN, FLY_TEXT_FLAG_IS_SNARE); playClientEffectObj(defender, "appearance/pt_state_slowedmove.prt", defender, "", null, "state_snared"); playClientEffectObj(defender, "sound/sta_snared_on.snd", defender, ""); } if (isPlayer(defender)) { utils.setScriptVar(defender, "combat.movement." + movementType + ".time", getGameTime()); } else { utils.setScriptVar(defender, "combat.movement." + movementType + "." + attacker + ".time", getGameTime()); } return true; } public static void removeCombatMovementModifierEffect(obj_id self, String buffName) throws InterruptedException { if (buffName == null || buffName.equals("")) { return; } boolean isVisible = true; dictionary buffData = dataTableGetRow("datatables/buff/buff.iff", buffName); if (buffData != null) { isVisible = buffData.getInt("VISIBLE") == 1; } prose_package pp = new prose_package(); if (movement.isStunEffect(buffName)) { if (isVisible) { pp = prose.setStringId(pp, new string_id("combat_effects", "no_stunned")); showFlyTextPrivateProseWithFlags(self, self, pp, 1.5f, colors.TOMATO, FLY_TEXT_FLAG_IS_SNARE); } stopClientEffectObjByLabel(self, "state_stunned"); playClientEffectObj(self, "sound/sta_rooted_off.snd", self, ""); } else if (movement.isRoot(buffName)) { if (isVisible) { pp = prose.setStringId(pp, new string_id("combat_effects", "no_rooted")); showFlyTextPrivateProseWithFlags(self, self, pp, 1.5f, colors.TOMATO, FLY_TEXT_FLAG_IS_SNARE); } stopClientEffectObjByLabel(self, "state_rooted"); playClientEffectObj(self, "sound/sta_rooted_off.snd", self, ""); } else if (movement.isSnare(buffName) && buff.getDuration(buffName) > 0.0f) { if (isVisible) { pp = prose.setStringId(pp, new string_id("combat_effects", "no_snare")); showFlyTextPrivateProseWithFlags(self, self, pp, 1.5f, colors.TOMATO, FLY_TEXT_FLAG_IS_SNARE); } stopClientEffectObjByLabel(self, "state_snared"); playClientEffectObj(self, "sound/sta_snared_off.snd", self, ""); } } public static void removeCombatBuffEffect(obj_id self, String buffName) throws InterruptedException { for (int i = 1; i <= buff.MAX_EFFECTS; i++) { int effectType = getStringCrc(buff.getEffectParam(buffName, i)); float effectValue = buff.getEffectValue(buffName, i); if (effectType == (-1161461455)) { stopClientEffectObjByLabel(self, "state_slowattack"); playClientEffectObj(self, "sound/sta_slow_off.snd", self, ""); } } } public static boolean gainCombatActionAttribute(obj_id self, int action) throws InterruptedException { if (getMaxAction(self) == getAction(self)) { return false; } int newAction = getAction(self) + action; if (newAction > getMaxAction(self)) { newAction = getMaxAction(self); } setAction(self, newAction); return true; } public static boolean drainCombatActionAttributes(obj_id self, int[] actionCost) throws InterruptedException { if (!(drainAttributes(self, actionCost[1], actionCost[2]))) { return false; } return true; } public static boolean canDrainCombatActionAttributes(obj_id self, int actionCost) throws InterruptedException { int[] tempArray = { 0, actionCost, 0 }; return canDrainCombatActionAttributes(self, tempArray); } public static boolean canDrainCombatActionAttributes(obj_id self, int[] actionCost) throws InterruptedException { if (actionCost[1] > 0) { if (testDrainAttribute(self, ACTION, actionCost[1]) < 0) { return false; } } return true; } public static final int ACTION_SUCCESS = 0; public static final int ACTION_INVALID_DATA = 1; public static final int ACTION_INVALID_WEAPON = 2; public static final int ACTION_TOO_TIRED = 3; public static int canPerformAction(String actionName, obj_id self) throws InterruptedException { actionName = getBestAction(self, actionName); combat_data actionData = combat_engine.getCombatData(actionName); if (actionData == null) { return ACTION_INVALID_DATA; } weapon_data weaponData = new weapon_data(); weaponData = weapons.getNewWeaponData(getCurrentWeapon(self)); if (!canUseWeaponWithAbility(self, weaponData, actionData, true)) { return ACTION_INVALID_WEAPON; } int[] actionCost = getActionCost(self, weaponData, actionData); if (!canDrainCombatActionAttributes(self, actionCost)) { return ACTION_TOO_TIRED; } return ACTION_SUCCESS; } public static boolean isCommandoBonus(obj_id self, weapon_data weaponData, int commandType) throws InterruptedException { return isPlayer(self) && utils.isProfession(self, utils.COMMANDO) && isHeavyWeapon(weaponData) && (commandType == LEFT_CLICK_DEFAULT); } public static int[] getActionCost(obj_id self, weapon_data weaponData, dictionary actionData) throws InterruptedException { int[] cost = new int[3]; float healthCost = 0; float actionCost; float mindCost; actionCost = actionData.getFloat("actionCost"); if (isCommandoBonus(self, weaponData, actionData.getInt("commandType"))) { cost[0] = (int)(0); cost[1] = (int)(0); cost[2] = (int)(0); return cost; } String specialLine = actionData.getString("specialLine"); String actionName = actionData.getString("actionName"); int freeshotChance = getEnhancedSkillStatisticModifierUncapped(self, "expertise_freeshot_" + specialLine); prose_package pp = new prose_package(); if (freeshotChance > 0) { int roll = rand(1, 100) + freeshotChance; if (roll > 100) { actionCost = 0; pp = prose.setStringId(pp, new string_id("spam", "freeshot")); showFlyTextPrivateProseWithFlags(self, self, pp, 1.5f, colors.LEMONCHIFFON, FLY_TEXT_FLAG_IS_FREESHOT); pp = prose.setStringId(pp, new string_id("cbt_spam", "freeshot")); pp = prose.setTU(pp, self); sendCombatSpamMessageProse(self, self, pp, true, true, true, COMBAT_RESULT_GENERIC); cost[0] = (int)(0); cost[1] = (int)(0); cost[2] = (int)(0); return cost; } } if (actionCost > 0) { float expertiseActionCostMod = 0; float percentAddFromWeapon = actionData.getFloat("percentAddFromWeapon"); if (percentAddFromWeapon > 0.0f) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_weapon_" + weaponData.weaponType); if (isMeleeWeapon(weaponData.id)) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_weapon_melee"); } if (isRangedWeapon(weaponData.id)) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_weapon_ranged"); } } expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_all"); if (specialLine != null && !specialLine.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_line_" + specialLine); } if (actionName != null && !actionName.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_single_" + actionName); } switch (weaponData.weaponType) { case WEAPON_TYPE_PISTOL: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_pistol"); break; case WEAPON_TYPE_RIFLE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_rifle"); break; case WEAPON_TYPE_LIGHT_RIFLE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_carbine"); break; case WEAPON_TYPE_HEAVY: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_heavy"); break; case WEAPON_TYPE_GROUND_TARGETTING: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_heavy"); break; case WEAPON_TYPE_DIRECTIONAL: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_heavy"); break; case WEAPON_TYPE_1HAND_MELEE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_1h"); break; case WEAPON_TYPE_2HAND_MELEE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_2h"); break; case WEAPON_TYPE_UNARMED: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_unarmed"); break; case WEAPON_TYPE_POLEARM: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_polearm"); break; case WEAPON_TYPE_WT_1HAND_LIGHTSABER: case WEAPON_TYPE_WT_2HAND_LIGHTSABER: case WEAPON_TYPE_WT_POLEARM_LIGHTSABER: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_lightsaber"); break; } actionCost = actionCost - (actionCost * (expertiseActionCostMod / 100.0f)); if (actionCost < 0) { actionCost = 0; } } if (actionCost < 0) { float expertiseActionCostMod = 0; int actionTotal = getLevel(self) * 100; actionCost = actionTotal * (float)(-1.0f * ((float)actionCost / 100.0f)); expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_all"); if (specialLine != null && !specialLine.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_line_" + specialLine); } if (actionName != null && !actionName.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_single_" + actionName); } actionCost = actionCost * (1 - (expertiseActionCostMod / 100)); } if (utils.hasScriptVar(self, "buff.action_burn.value") && actionCost > 0) { int rightNow = getGameTime(); int timestamp = utils.getIntScriptVar(self, "buff.action_burn.timestamp"); if (rightNow > timestamp) { utils.setScriptVar(self, "buff.action_burn.timestamp", rightNow); float burnRatio = utils.getFloatScriptVar(self, "buff.action_burn.value"); float actionBurn = (float)actionCost * (burnRatio / 100); int currentHealth = getHealth(self); int modifiedHealth = currentHealth - (int)actionBurn; if (modifiedHealth < 1) { modifiedHealth = 1; } int[] burnBuffs = buff.getAllBuffsByEffect(self, "action_burn"); obj_id burnBuffOwner = self; if (burnBuffs.length > 0 && utils.hasScriptVar(self, "buffOwner." + burnBuffs[0])) { burnBuffOwner = utils.getObjIdScriptVar(self, "buffOwner." + burnBuffs[0]); } setHealth(self, modifiedHealth); pp = prose.setStringId(pp, new string_id("spam", "crippling_pain")); showFlyTextPrivateProseWithFlags(self, burnBuffOwner, pp, 1.5f, colors.ORANGERED, FLY_TEXT_FLAG_IS_CRITICAL_HIT); pp = prose.setStringId(pp, new string_id("cbt_spam", "crippling_pain")); pp = prose.setTU(pp, self); pp = prose.setDI(pp, (int)actionBurn); sendCombatSpamMessageProse(self, burnBuffOwner, pp, true, true, true, COMBAT_RESULT_DEBUFF); } } cost[0] = (int)(0); cost[1] = (int)(actionCost); cost[2] = (int)(0); return cost; } public static int[] getActionCost(obj_id self, weapon_data weaponData, combat_data actionData) throws InterruptedException { int[] cost = new int[3]; float healthCost = 0; float actionCost; float mindCost; actionCost = actionData.actionCost; if (isCommandoBonus(self, weaponData, actionData.commandType)) { cost[0] = (int)(0); cost[1] = (int)(0); cost[2] = (int)(0); return cost; } if (beast_lib.isBeast(self)) { cost[0] = 0; cost[1] = (int)actionData.vigorCost; cost[2] = 0; return cost; } String specialLine = actionData.specialLine; String actionName = actionData.actionName; int freeshotChance = getEnhancedSkillStatisticModifierUncapped(self, "expertise_freeshot_" + specialLine); prose_package pp = new prose_package(); if (freeshotChance > 0) { int roll = rand(1, 100) + freeshotChance; if (roll > 100) { actionCost = 0; pp = prose.setStringId(pp, new string_id("spam", "freeshot")); showFlyTextPrivateProseWithFlags(self, self, pp, 1.5f, colors.LEMONCHIFFON, FLY_TEXT_FLAG_IS_FREESHOT); pp = prose.setStringId(pp, new string_id("cbt_spam", "freeshot")); pp = prose.setTU(pp, self); sendCombatSpamMessageProse(self, self, pp, true, true, true, COMBAT_RESULT_GENERIC); cost[0] = (int)(0); cost[1] = (int)(0); cost[2] = (int)(0); return cost; } } if (actionCost > 0) { float expertiseActionCostMod = 0; if (actionData.percentAddFromWeapon > 0.0f) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_weapon_" + weaponData.weaponType); if (isMeleeWeapon(weaponData.id)) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_weapon_melee"); } if (isRangedWeapon(weaponData.id)) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_weapon_ranged"); } } if (actionData.hitType == HEAL) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "exotic_heal_action_reduction"); } expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_all"); if (specialLine != null && !specialLine.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_line_" + specialLine); } if (actionName != null && !actionName.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_single_" + actionName); } switch (weaponData.weaponType) { case WEAPON_TYPE_PISTOL: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_pistol"); break; case WEAPON_TYPE_RIFLE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_rifle"); break; case WEAPON_TYPE_LIGHT_RIFLE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_carbine"); break; case WEAPON_TYPE_HEAVY: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_heavy"); break; case WEAPON_TYPE_GROUND_TARGETTING: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_heavy"); break; case WEAPON_TYPE_DIRECTIONAL: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_heavy"); break; case WEAPON_TYPE_1HAND_MELEE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_1h"); break; case WEAPON_TYPE_2HAND_MELEE: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_2h"); break; case WEAPON_TYPE_UNARMED: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_unarmed"); break; case WEAPON_TYPE_POLEARM: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_polearm"); break; case WEAPON_TYPE_WT_1HAND_LIGHTSABER: case WEAPON_TYPE_WT_2HAND_LIGHTSABER: case WEAPON_TYPE_WT_POLEARM_LIGHTSABER: expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_lightsaber"); break; } actionCost = actionCost - (actionCost * (expertiseActionCostMod / 100.0f)); if (actionCost < 0) { actionCost = 0; } } if (actionCost < 0) { float expertiseActionCostMod = 0; int actionTotal = getLevel(self) * 100; actionCost = actionTotal * (float)(-1.0f * ((float)actionCost / 100.0f)); expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_all"); if (specialLine != null && !specialLine.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_line_" + specialLine); } if (actionName != null && !actionName.equals("")) { expertiseActionCostMod += getEnhancedSkillStatisticModifierUncapped(self, "expertise_action_single_" + actionName); } actionCost = actionCost * (1 - (expertiseActionCostMod / 100)); } if (utils.hasScriptVar(self, "buff.action_burn.value") && actionCost > 0) { int rightNow = getGameTime(); int timestamp = utils.getIntScriptVar(self, "buff.action_burn.timestamp"); if (rightNow > timestamp) { utils.setScriptVar(self, "buff.action_burn.timestamp", rightNow); float burnRatio = utils.getFloatScriptVar(self, "buff.action_burn.value"); float actionBurn = (float)actionCost * (burnRatio / 100); int currentHealth = getHealth(self); int modifiedHealth = currentHealth - (int)actionBurn; if (modifiedHealth < 1) { modifiedHealth = 1; } setHealth(self, modifiedHealth); int[] burnBuffs = buff.getAllBuffsByEffect(self, "action_burn"); obj_id burnBuffOwner = self; if (burnBuffs.length > 0 && utils.hasScriptVar(self, "buffOwner." + burnBuffs[0])) { burnBuffOwner = utils.getObjIdScriptVar(self, "buffOwner." + burnBuffs[0]); } pp = prose.setStringId(pp, new string_id("spam", "crippling_pain")); showFlyTextPrivateProseWithFlags(self, burnBuffOwner, pp, 1.5f, colors.ORANGERED, FLY_TEXT_FLAG_IS_CRITICAL_HIT); pp = prose.setStringId(pp, new string_id("cbt_spam", "crippling_pain")); pp = prose.setTU(pp, self); pp = prose.setDI(pp, (int)actionBurn); sendCombatSpamMessageProse(self, burnBuffOwner, pp, true, true, true, COMBAT_RESULT_DEBUFF); } } cost[0] = (int)(0); cost[1] = (int)(actionCost); cost[2] = (int)(0); combatLog(self, null, "getActionCost", "Final Action cost = [" + cost[0] + ", " + cost[1] + ", " + cost[2] + "]"); return cost; } public static int getForceCost(obj_id self, weapon_data weaponData, dictionary actionData) throws InterruptedException { return 0; } public static int getForceCost(obj_id self, weapon_data weaponData, combat_data actionData) throws InterruptedException { return 0; } public static boolean validateTarget(obj_id target, int targetCheck) throws InterruptedException { obj_id self = getSelf(); if (!storyteller.storytellerCombatCheck(self, target)) { return false; } switch (targetCheck) { case VALID_TARGET_MOB: return isMob(target) && !vehicle.isVehicle(target); case VALID_TARGET_CREATURE: return ai_lib.isMonster(target); case VALID_TARGET_NPC: return ai_lib.isNpc(target); case VALID_TARGET_DROID: return ai_lib.isDroid(target) || ai_lib.isAndroid(target); case VALID_TARGET_PVP: return pvpCanAttack(self, target); case VALID_TARGET_JEDI: if (isPlayer(target)) { return isJedi(target); } else { return jedi.isLightsaber(getCurrentWeapon(target)); } case VALID_TARGET_DEAD: return isDead(target); case VALID_TARGET_FRIEND: return pvpCanHelp(self, target); } return true; } public static String getCreatureAnimationName(obj_id self, String playbackName, hit_result hitData, weapon_data weaponData) throws InterruptedException { String animName = ""; int niche = ai_lib.aiGetNiche(self); if (niche == NICHE_VEHICLE) { animName = "droid_attack"; } else { if (playbackName.endsWith("ranged")) { animName = "creature_attack_ranged"; } else if (playbackName.endsWith("melee")) { animName = "creature_attack"; } else { animName = "creature_attack_special_" + rand(1, 2); } } int avgDamage = (weaponData.minDamage + weaponData.maxDamage) / 2; if (hitData.damage > avgDamage) { animName += "_medium"; } else { animName += "_light"; } return animName; } public static boolean checkWeaponCerts(obj_id player, weapon_data weaponData) throws InterruptedException { return checkWeaponCerts(player, weaponData, true); } public static boolean checkWeaponCerts(obj_id player, weapon_data weaponData, boolean verbose) throws InterruptedException { if (!isPlayer(player)) { return true; } if (!combat.hasCertification(player, weaponData.id, verbose)) { return false; } if (utils.isProfession(player, utils.FORCE_SENSITIVE)) { if (jedi.isLightsaber(weaponData.weaponType)) { if (!jedi.hasColorCrystal(weaponData.id)) { return false; } if (!jedi.validateCrystalCount(weaponData.id)) { return false; } } if (buff.hasBuff(player, "forceRun_1") || buff.hasBuff(player, "forceRun_2")) { return false; } } return true; } public static int applyArmorProtection(obj_id attacker, obj_id defender, weapon_data weaponData, hit_result hitData, float bypassArmor) throws InterruptedException { return applyArmorProtection(attacker, defender, weaponData, hitData, bypassArmor, 0.0f); } public static int applyArmorProtection(obj_id attacker, obj_id defender, weapon_data weaponData, hit_result hitData, float bypassArmor, float expertiseDamageBonus) throws InterruptedException { float baseDamage = hitData.damage; float elementalDamage = weaponData.elementalValue; float baseProtection = 0.0f; float elementalProtection = 0.0f; float basePsgProtection = 0.0f; float elementalPsgProtection = 0.0f; boolean strikethrough = hitData.strikethrough; int armor_neglect = getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_armor_neglect"); int player_armor_reduction = getEnhancedSkillStatisticModifierUncapped(defender, "expertise_innate_reduction_all_player"); float neglectMod = 1.0f - (armor_neglect / 100.0f); float playerMod = 1.0f - (player_armor_reduction / 100.0f); obj_id armorPiece = null; obj_id psg = null; if (isPlayer(defender)) { boolean isRanged = isRangedWeapon(weaponData.weaponType); psg = getPsgArmor(defender); if (isIdValid(defender) || isIdValid(psg)) { float[] psgProtectionReference = new float[1]; baseProtection = getArmorProtection(defender, defender, psg, weaponData.damageType, psgProtectionReference); baseProtection *= neglectMod; baseProtection *= playerMod; if (isRanged) { basePsgProtection = psgProtectionReference[0]; } elementalProtection = getArmorProtection(defender, defender, psg, weaponData.elementalType, psgProtectionReference, attacker); if (isRanged) { elementalPsgProtection = psgProtectionReference[0]; } if (isRanged) { if (basePsgProtection > 0 || elementalPsgProtection > 0) { int psgLevel = armor.getArmorLevel(psg); if (psgLevel < 0 || psgLevel >= armor.PSG_HIT_EFFECTS.length) { psgLevel = 0; } playClientEffectObj(defender, armor.PSG_HIT_EFFECTS[psgLevel], defender, "", transform.identity); } } } } else { baseProtection = getNpcArmorProtection(defender, weaponData.damageType); baseProtection *= neglectMod; elementalProtection = getNpcArmorProtection(defender, weaponData.elementalType); } if (bypassArmor == 0 && strikethrough) { float strikethroughRating = (float)(getStrikethroughValue(attacker, defender) / 100.0f); bypassArmor = rand(strikethroughRating / 2.0f, strikethroughRating); hitData.strikethroughAmmount = bypassArmor * 100.0f; } if (bypassArmor > 0) { if (bypassArmor > 1.0) { bypassArmor = 1.0f; } if (bypassArmor < 0.0) { bypassArmor = 0.0f; } } baseProtection *= 1.0 - bypassArmor; elementalProtection *= 1.0 - bypassArmor; if (basePsgProtection > 0) { basePsgProtection = scalePsgProtectionByBaseProtection(basePsgProtection, baseProtection); } int baseDmgAbsorbed = (int)(baseDamage * (baseProtection + basePsgProtection)); baseDamage -= baseDmgAbsorbed; int blockValue = 0; if (hitData.blockResult) { blockValue = combat.getBlockAmmount(defender); if (baseDamage <= blockValue) { hitData.block = (int)baseDamage; baseDamage = 0.0f; } else { baseDamage -= blockValue; hitData.block = blockValue; } string_id critSpam = new string_id("combat_effects", "block"); prose_package pp = new prose_package(); pp = prose.setStringId(pp, critSpam); pp = prose.setDI(pp, hitData.block); showFlyTextPrivateProseWithFlags(defender, defender, pp, 1.5f, colors.LIMEGREEN, FLY_TEXT_FLAG_IS_GLANCING_BLOW); showFlyTextPrivateProseWithFlags(defender, attacker, pp, 1.5f, colors.LIMEGREEN, FLY_TEXT_FLAG_IS_GLANCING_BLOW); } if (baseDamage < 0) { baseDmgAbsorbed += baseDamage; baseDamage = 0; } if (expertiseDamageBonus != 0) { elementalDamage = elementalDamage * (1.0f + (expertiseDamageBonus / 100.0f)); } int elementalDmgAbsorbed = (int)(elementalDamage * (elementalProtection)); elementalDamage -= elementalDmgAbsorbed; if (elementalDamage < 0) { elementalDmgAbsorbed += elementalDamage; elementalDamage = 0; } if ((basePsgProtection > 0 || elementalPsgProtection > 0) && isIdValid(psg)) { decayArmorPiece(psg, baseDamage * basePsgProtection + elementalDamage * elementalPsgProtection); } hitData.elementalDamage = (int)elementalDamage; hitData.elementalDamageType = weaponData.elementalType; hitData.damage = (int)baseDamage; if ((baseProtection > 0 || elementalProtection > 0) && isIdValid(armorPiece)) { hitData.blockingArmor = armorPiece; } else if ((basePsgProtection > 0 || elementalPsgProtection > 0) && isIdValid(psg)) { hitData.blockingArmor = psg; } return (baseDmgAbsorbed + elementalDmgAbsorbed); } public static float scalePsgProtectionByBaseProtection(float basePsgProtection, float baseProtection) throws InterruptedException { if (baseProtection < 0.2f) { baseProtection = 0.2f; } basePsgProtection *= 10000; basePsgProtection = (basePsgProtection / 100) / (100 + ((basePsgProtection / 100))) / (baseProtection * 4); return basePsgProtection; } public static obj_id getArmorPieceHit(obj_id defender, int hitLocation) throws InterruptedException { String[] armorSlots = ARMOR_SLOT_LOCATIONS[hitLocation]; String slotHit = armorSlots[rand(0, armorSlots.length - 1)]; obj_id armorPiece = getObjectInSlot(defender, slotHit); int armorType = getGameObjectType(armorPiece); if (isGameObjectTypeOf(armorType, GOT_armor)) { return armorPiece; } return null; } public static obj_id getPsgArmor(obj_id defender) throws InterruptedException { obj_id psg = getObjectInSlot(defender, PSG_SLOT_LOCATION); if (armor.isPsg(psg)) { return psg; } return null; } public static float getNpcArmorProtection(obj_id defender, int damageType) throws InterruptedException { int generalProtection = 0; dictionary protections = null; generalProtection = armor.getArmorGeneralProtection(defender); generalProtection += getEnhancedSkillStatisticModifier(defender, "private_armor_bonus"); generalProtection -= getEnhancedSkillStatisticModifierUncapped(defender, "expertise_innate_reduction_all_mob"); if (generalProtection == 0 && protections == null) { return 0.0f; } if (utils.hasScriptVar(defender, "ai.combat.genProtectionBonus")) { generalProtection += (int)utils.getFloatScriptVar(defender, "ai.combat.genProtectionBonus"); } float armorProtection = convertProtectionToPercent(generalProtection); return armorProtection; } public static float getArmorProtection(obj_id defender, obj_id armorPiece, obj_id psg, int damageType, float[] psgProtectionResult) throws InterruptedException { return getArmorProtection(defender, armorPiece, psg, damageType, psgProtectionResult, obj_id.NULL_ID); } public static float getArmorProtection(obj_id defender, obj_id armorPiece, obj_id psg, int damageType, float[] psgProtectionResult, obj_id attacker) throws InterruptedException { int generalProtection = 0; int armorBonus = getEnhancedSkillStatisticModifier(defender, "private_armor_bonus"); generalProtection += armorBonus; dictionary protections = null; dictionary psgProtections = null; if (isIdValid(armorPiece)) { generalProtection = armor.getCombatArmorGeneralProtection(armorPiece); protections = armor.getCombatArmorSpecialProtections(armorPiece); } if (isIdValid(psg)) { psgProtections = armor.getPsgSpecialProtections(psg); } if (generalProtection == 0 && protections == null && psgProtections == null) { return 0.0f; } String damageString = null; switch (damageType) { case DAMAGE_KINETIC: damageString = "kinetic"; break; case DAMAGE_ENERGY: damageString = "energy"; break; case DAMAGE_BLAST: damageString = "blast"; break; case DAMAGE_STUN: damageString = "stun"; break; case DAMAGE_RESTRAINT: damageString = "lightsaber"; break; case DAMAGE_ELEMENTAL_HEAT: damageString = "heat"; break; case DAMAGE_ELEMENTAL_COLD: damageString = "cold"; break; case DAMAGE_ELEMENTAL_ACID: damageString = "acid"; break; case DAMAGE_ELEMENTAL_ELECTRICAL: damageString = "electricity"; break; case DAMAGE_ENVIRONMENTAL_ELECTRICAL: damageString = "electricity"; break; default: break; } float value = generalProtection; float psgValue = 0; if (damageString != null) { if (protections != null) { value += protections.getFloat(damageString); } if (psgProtections != null) { psgValue = psgProtections.getFloat(damageString); if (psgValue != 0) { if (psgProtectionResult != null && psgProtectionResult.length > 0) { psgProtectionResult[0] = psgValue / 10000.0f; } } } } if (isIdValid(attacker) && damageString != null && damageString.length() > 0) { float elementalPenetration = getElementalPenetration(attacker, damageString); if (elementalPenetration > 0) { value *= elementalPenetration; } } value = convertProtectionToPercent(value); return value; } public static float getTerasKasiProtection(obj_id defender) throws InterruptedException { if (utils.getIntScriptVar(defender, armor.SCRIPTVAR_ARMOR_COUNT) > 0) { return 0.0f; } float protection = 0.0f; float tkaArmor = 0.0f; float jediArmor = 0.0f; protection = getEnhancedSkillStatisticModifier(defender, "clothing_armor"); if (hasSkill(defender, "combat_unarmed_novice")) { tkaArmor = getSkillStatisticModifier(defender, "tka_armor"); } if (tkaArmor > protection) { protection = tkaArmor; } if (hasSkill(defender, "force_title_jedi_novice")) { jediArmor = getSkillStatisticModifier(defender, "jedi_armor"); } if (jediArmor > protection) { protection = jediArmor; } protection *= 100.0f; int armorBonus = getEnhancedSkillStatisticModifier(defender, "private_armor_bonus"); protection += armorBonus; return protection; } public static float convertProtectionToPercent(float protection) throws InterruptedException { final int reductionThreshold = 2000; if (protection <= reductionThreshold && protection >= (-1 * reductionThreshold)) { return protection / 10000.0f; } if (protection < 0) { protection += reductionThreshold; protection = protection / (100.0f - (protection / 100.0f)); protection = -0.2f + (protection / 100.0f); } else { protection -= reductionThreshold; protection = protection / (100.0f + (protection / 100.0f)); protection = 0.2f + (protection / 100.0f); } return protection; } public static float getArmorDecayPercentage(obj_id armorPiece) throws InterruptedException { float ARMOR_REDUCTION_THRESHOLD = 0.5f; float reduction = 1.0f; float hp = getHitpoints(armorPiece); float maxhp = getMaxHitpoints(armorPiece); float decayLevel = hp / maxhp; if (hasObjVar(armorPiece, "slice.resilience")) { int resilience = getIntObjVar(armorPiece, "slice.resilience"); ARMOR_REDUCTION_THRESHOLD -= resilience / 100.0f; } if (decayLevel < ARMOR_REDUCTION_THRESHOLD) { reduction = 1.0f - (ARMOR_REDUCTION_THRESHOLD - decayLevel); } return reduction; } public static float reduceArmorProtectionFromDecay(obj_id armorPiece, float value) throws InterruptedException { float ARMOR_REDUCTION_THRESHOLD = 0.5f; float hp = getHitpoints(armorPiece); float maxhp = getMaxHitpoints(armorPiece); float decayLevel = hp / maxhp; if (hasObjVar(armorPiece, "slice.resilience")) { int resilience = getIntObjVar(armorPiece, "slice.resilience"); ARMOR_REDUCTION_THRESHOLD -= resilience / 100.0f; } if (decayLevel < ARMOR_REDUCTION_THRESHOLD) { float reduction = 1.0f - (ARMOR_REDUCTION_THRESHOLD - decayLevel); value *= reduction; } return value; } public static void decayArmorPiece(obj_id armorPiece, float newDamage) throws InterruptedException { if (armor.isPsg(armorPiece)) { dictionary protections = armor.getArmorSpecialProtections(armorPiece); float maxEffectiveness = (Float) (((protections.values()).iterator()).next()); if (maxEffectiveness > 0) { float effectivenessReduction = PSG_BASE_DMG_PER_HIT + (newDamage / PSG_DAMAGE_SCALE); float efficiencyReduction = effectivenessReduction / maxEffectiveness; float currentEfficiency = armor.getPsgEfficiency(armorPiece); currentEfficiency -= efficiencyReduction; armor.setPsgEfficiency(armorPiece, currentEfficiency); } } if (utils.isAntiDecay(armorPiece)) { return; } if (static_item.isStaticItem(armorPiece)) { return; } } public static boolean getGrenadeData(obj_id player, weapon_data weaponData, String params, boolean verbose) throws InterruptedException { Long lngId; try { lngId = Long.valueOf(params); } catch(NumberFormatException err) { if (verbose) { sendSystemMessage(player, new string_id("cbt_spam", "grenade_bad_param")); } return false; } obj_id grenade = obj_id.getObjId(lngId.longValue()); if (!isIdValid(grenade)) { if (verbose) { sendSystemMessage(player, new string_id("cbt_spam", "grenade_invalid")); } return false; } if (getFirstParentInWorld(grenade) != player) { if (verbose) { sendSystemMessage(player, new string_id("cbt_spam", "grenade_not_owned")); } removeObjVar(grenade, "intUsed"); return false; } weaponData.id = grenade; weaponData.weaponName = getNameStringId(grenade); weaponData.minDamage = getWeaponMinDamage(grenade); weaponData.maxDamage = getWeaponMaxDamage(grenade); weaponData.weaponType = WEAPON_TYPE_THROWN; weaponData.attackType = ATTACK_TYPE_THROWN; weaponData.damageType = getWeaponDamageType(grenade); weaponData.elementalType = getWeaponElementalType(grenade); weaponData.elementalValue = getWeaponElementalValue(grenade); weaponData.attackSpeed = getWeaponAttackSpeed(grenade); weaponData.woundChance = getWeaponWoundChance(grenade); range_info rng = getWeaponRangeInfo(grenade); weaponData.minRange = rng.minRange; weaponData.maxRange = rng.maxRange; weaponData.accuracy = getWeaponAccuracy(grenade); weaponData.damageRadius = getWeaponDamageRadius(grenade); weaponData.attackCost = getWeaponAttackCost(grenade); return true; } public static boolean canPaintTarget(obj_id squadLeader, obj_id target) throws InterruptedException { if (!isValidId(target)) { return false; } if (!pvpCanAttack(squadLeader, target)) { return false; } obj_id groupId = getGroupObject(squadLeader); if (!isIdValid(groupId)) { return false; } if (utils.hasScriptVar(groupId, VAR_GROUP_LAST_VOLLEY_TIME)) { int lastVolley = utils.getIntScriptVar(groupId, VAR_GROUP_LAST_VOLLEY_TIME); int now = getGameTime(); int interval = now - lastVolley; if (interval < VOLLEY_INTERVAL) { return false; } } obj_id[] groupMembers = getGroupMemberIds(groupId); if (groupMembers == null || groupMembers.length < 2) { return false; } return true; } public static void doPaintTarget(obj_id squadLeader, obj_id target) throws InterruptedException { obj_id groupId = getGroupObject(squadLeader); obj_id[] groupMembers = getGroupMemberIds(groupId); if (!hasScript(target, "systems.combat.volleytarget")) { attachScript(target, "systems.combat.volleytarget"); } utils.setScriptVar(groupId, VAR_GROUP_VOLLEY_TARGET, target); utils.setScriptVar(groupId, VAR_GROUP_LAST_VOLLEY_TIME, getGameTime()); dictionary parms = new dictionary(); parms.put("objGroup", groupId); messageTo(target, "msgMarkedByGroup", parms, 0, false); messageTo(target, "msgUnmarkedByGroup", parms, VOLLEY_INTERVAL * 0.7f, false); playClientEffectObj(groupMembers, "appearance/pt_special_attack_volley_fire.prt", target, "", new transform(), ID_VOLLEY_FIRE_PARTICLE); } public static boolean canUseWeaponWithAbility(obj_id self, weapon_data weaponData, combat_data actionData, boolean verbose) throws InterruptedException { if (!isIdValid(weaponData.id)) { return false; } if (((actionData.invalidWeapon & weaponTypeToBitValueMap[weaponData.weaponType]) == 0) && ((actionData.validWeapon & weaponTypeToBitValueMap[weaponData.weaponType]) != 0)) { return true; } if (verbose) { sendCombatSpamMessage(self, new string_id("cbt_spam", "invalid_weapon")); sendSystemMessage(self, new string_id("cbt_spam", "invalid_weapon_single")); } return false; } public static boolean canUseWeaponWithAbility(obj_id self, weapon_data weaponData, String actionName, boolean verbose) throws InterruptedException { return canUseWeaponWithAbility(self, weaponData, combat_engine.getCombatData(actionName), verbose); } public static void addHateProcess(obj_id attacker, obj_id defender, hit_result hitData, combat_data actionData) throws InterruptedException { if (defender == attacker) { return; } float hateMod = actionData.hateDamageModifier; int maxHate = actionData.maxHate; int hateAdd = actionData.hateAdd; int hateAddTime = actionData.hateAddTime; int hateReduction = actionData.hateReduce; if (hitData.success) { if (hateReduction < 1) { if (maxHate > 0) { final float maxHateDamage = getMaxHate(defender); setHate(defender, attacker, maxHateDamage + maxHate); } if (hateAdd > 0 && hateAddTime > 0) { addHateDot(defender, attacker, hateAdd, hateAddTime); } float totalHateDamage = hitData.damage * hateMod; if (totalHateDamage > 0.0f) { int skillBonus = getEnhancedSkillStatisticModifier(attacker, "private_taunt_bonus"); totalHateDamage += totalHateDamage * (skillBonus / 100.0f); if (utils.hasScriptVar(attacker, "ai.combat.aggroBonus")) { totalHateDamage += totalHateDamage * utils.getFloatScriptVar(attacker, "ai.combat.aggroBonus"); } if (utils.hasScriptVar(attacker, "ai.combat.aggroBonusFlat")) { totalHateDamage += utils.getFloatScriptVar(attacker, "ai.combat.aggroBonusFlat"); } if (utils.hasScriptVar(attacker, "ai.combat.aggroReduction")) { totalHateDamage -= totalHateDamage * utils.getFloatScriptVar(attacker, "ai.combat.aggroReduction"); } if (buff.hasBuff(attacker, BUFF_HATE_XFER)) { int hateTransferBonus = getEnhancedSkillStatisticModifier(attacker, "expertise_aggro_channel"); float hateTransfered = totalHateDamage * (hateTransferBonus / 100.0f); totalHateDamage -= hateTransfered; obj_id transferTo = utils.getObjIdScriptVar(attacker, buff.AGGRO_TRANSFER_TO); addHate(defender, transferTo, hateTransfered); } addHate(defender, attacker, totalHateDamage); } } else { if ((isPlayer(attacker) || beast_lib.isBeast(attacker)) && (isMob(defender) && !beast_lib.isBeast(defender))) { float currentHate = getHate(defender, attacker); currentHate -= hateReduction; currentHate = currentHate < 1.0f ? 1.0f : currentHate; setHate(defender, attacker, currentHate); } else if ((isMob(attacker) && !beast_lib.isBeast(attacker)) && (isPlayer(defender) || beast_lib.isBeast(defender))) { float currentHate = getHate(attacker, defender); currentHate -= hateReduction; currentHate = currentHate < 1.0f ? 1.0f : currentHate; setHate(attacker, defender, currentHate); } } } else { addHate(defender, attacker, 1.0f); } if (isPlayer(attacker)) { addHate(attacker, defender, 0.0f); { obj_id[] hateList = getHateList(attacker); for (obj_id hateTarget : hateList) { if (!isPlayer(hateTarget) && isTangible(hateTarget)) { if (getHateTarget(hateTarget) == attacker) { resetHateTimer(hateTarget); } } } } } } public static boolean cachedIsRidingVehicle(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.ridingVehicle"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueBool(attacker, cacheId); } boolean ridingVehicle = vehicle.isRidingVehicle(attacker); setCachedCombatValue(attacker, cacheId, ridingVehicle); return ridingVehicle; } public static boolean cachedIsGalloping(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.galloping"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueBool(attacker, cacheId); } obj_id mount = getMountId(attacker); boolean galloping = false; if (isIdValid(mount)) { galloping = pet_lib.isGalloping(mount); } setCachedCombatValue(attacker, cacheId, galloping); return galloping; } public static boolean cachedIsDead(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.isDead"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueBool(attacker, cacheId); } boolean dead = isDead(attacker); setCachedCombatValue(attacker, cacheId, dead); return dead; } public static boolean cachedIsIncapacitated(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.isIncapped"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueBool(attacker, cacheId); } boolean incapped = isIncapacitated(attacker); setCachedCombatValue(attacker, cacheId, incapped); return incapped; } public static int cachedGetLocomotion(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.locomotion"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueInt(attacker, cacheId); } int locomotion = getLocomotion(attacker); setCachedCombatValue(attacker, cacheId, locomotion); return locomotion; } public static float cachedGetMovementSpeed(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.moveSpeed"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueFloat(attacker, cacheId); } float moveSpeed = getMovementSpeed(attacker); setCachedCombatValue(attacker, cacheId, moveSpeed); return moveSpeed; } public static float cachedGetRunSpeed(obj_id attacker) throws InterruptedException { String cacheId = "combat.cache.runSpeed"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueFloat(attacker, cacheId); } float runSpeed = getRunSpeed(attacker); setCachedCombatValue(attacker, cacheId, runSpeed); return runSpeed; } public static float cachedGetDistance(obj_id attacker, obj_id defender) throws InterruptedException { String cacheId = "combat.cache." + defender + ".distance"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueFloat(attacker, cacheId); } float dist = getDistance(attacker, defender); setCachedCombatValue(attacker, cacheId, dist); return dist; } public static float cachedGetSpeedAdjustMin(obj_id attacker, obj_id defender) throws InterruptedException { String cacheId = "combat.cache." + defender + ".speedAdjustMin"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueFloat(attacker, cacheId); } float adjustMin = 0.0f; if (cachedGetLocomotion(attacker) == LOCOMOTION_RUNNING) { location defenderLocation = getLocation(defender); defenderLocation.y = getLocation(attacker).y; if (!isFacing(attacker, defenderLocation)) { adjustMin -= cachedGetMovementSpeed(attacker) * 0.6f; } } if (cachedGetLocomotion(defender) == LOCOMOTION_RUNNING) { location attackerLocation = getLocation(attacker); attackerLocation.y = getLocation(defender).y; if (isFacing(defender, attackerLocation)) { adjustMin -= cachedGetMovementSpeed(defender) * 0.6f; } } float maxAdjustValue = (cachedGetRunSpeed(attacker) + cachedGetRunSpeed(defender)) * 0.75f; if (adjustMin < -maxAdjustValue) { adjustMin = -maxAdjustValue; } setCachedCombatValue(attacker, cacheId, adjustMin); return adjustMin; } public static float cachedGetSpeedAdjustMax(obj_id attacker, obj_id defender) throws InterruptedException { String cacheId = "combat.cache." + defender + ".speedAdjustMax"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueFloat(attacker, cacheId); } float adjustMax = 0.0f; if (cachedGetLocomotion(attacker) == LOCOMOTION_RUNNING) { location defenderLocation = getLocation(defender); defenderLocation.y = getLocation(attacker).y; if (isFacing(attacker, defenderLocation)) { adjustMax += cachedGetMovementSpeed(attacker) * 0.6f; } } if (cachedGetLocomotion(defender) == LOCOMOTION_RUNNING) { location attackerLocation = getLocation(attacker); attackerLocation.y = getLocation(defender).y; if (!isFacing(defender, attackerLocation)) { adjustMax += cachedGetMovementSpeed(defender) * 0.6f; } } float maxAdjustValue = (cachedGetRunSpeed(attacker) + cachedGetRunSpeed(defender)) * 0.75f; if (adjustMax > maxAdjustValue) { adjustMax = maxAdjustValue; } setCachedCombatValue(attacker, cacheId, adjustMax); return adjustMax; } public static boolean cachedCanSee(obj_id attacker, obj_id defender) throws InterruptedException { return canSee(attacker, defender); } public static boolean cachedPvpCanAttack(obj_id attacker, obj_id defender) throws InterruptedException { String cacheId = "combat.cache." + defender + ".pvpCanAttack"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueBool(attacker, cacheId); } boolean attackerCanAttack = pvpCanAttack(attacker, defender); setCachedCombatValue(attacker, cacheId, attackerCanAttack); return attackerCanAttack; } public static boolean cachedIsSameSocialGroup(obj_id attacker, obj_id defender) throws InterruptedException { String cacheId = "combat.cache." + defender + ".sameSocialGroup"; if (hasCachedCombatValue(attacker, cacheId)) { return getCachedCombatValueBool(attacker, cacheId); } String attackerSocialGroup = null; if (!isPlayer(attacker) && !pet_lib.isPet(attacker) && !pet_lib.isPet(defender)) { attackerSocialGroup = ai_lib.getSocialGroup(attacker); } boolean sameSocialGroup = (attackerSocialGroup != null && attackerSocialGroup.equals(ai_lib.getSocialGroup(defender))); setCachedCombatValue(attacker, cacheId, sameSocialGroup); return sameSocialGroup; } public static boolean isCachedServerFrame(obj_id attacker) throws InterruptedException { int serverFrame = utils.getIntScriptVar(attacker, "combat.cache.serverFrame"); int currentFrame = getServerFrame(); return (serverFrame == currentFrame); } public static void setCachedServerFrame(obj_id attacker) throws InterruptedException { if (!isCachedServerFrame(attacker)) { utils.removeScriptVarTree(attacker, "combat.cache"); int currentFrame = getServerFrame(); utils.setScriptVar(attacker, "combat.cache.serverFrame", currentFrame); } } public static boolean hasCachedCombatValue(obj_id attacker, String cacheId) throws InterruptedException { if (isCachedServerFrame(attacker)) { return (utils.hasScriptVar(attacker, "combat.cache." + cacheId)); } return false; } public static int getCachedCombatValueInt(obj_id attacker, String cacheId) throws InterruptedException { if (isCachedServerFrame(attacker)) { return (utils.getIntScriptVar(attacker, "combat.cache." + cacheId)); } return 0; } public static float getCachedCombatValueFloat(obj_id attacker, String cacheId) throws InterruptedException { if (isCachedServerFrame(attacker)) { return (utils.getFloatScriptVar(attacker, "combat.cache." + cacheId)); } return 0.0f; } public static boolean getCachedCombatValueBool(obj_id attacker, String cacheId) throws InterruptedException { if (isCachedServerFrame(attacker)) { return (utils.getBooleanScriptVar(attacker, "combat.cache." + cacheId)); } return false; } public static void setCachedCombatValue(obj_id attacker, String cacheId, int value) throws InterruptedException { setCachedServerFrame(attacker); utils.setScriptVar(attacker, "combat.cache." + cacheId, value); } public static void setCachedCombatValue(obj_id attacker, String cacheId, float value) throws InterruptedException { setCachedServerFrame(attacker); utils.setScriptVar(attacker, "combat.cache." + cacheId, value); } public static void setCachedCombatValue(obj_id attacker, String cacheId, boolean value) throws InterruptedException { setCachedServerFrame(attacker); utils.setScriptVar(attacker, "combat.cache." + cacheId, value); } public static void combatLog(obj_id attacker, obj_id defender, String logName, String logMsg) throws InterruptedException { } public static int getWeaponCategory(String weaponType) throws InterruptedException { if (weaponType.equals("rifle")) { return RANGED_WEAPON; } if (weaponType.equals("carbine")) { return RANGED_WEAPON; } if (weaponType.equals("pistol")) { return RANGED_WEAPON; } if (weaponType.equals("heavyweapon")) { return RANGED_WEAPON; } if (weaponType.equals("onehandmelee")) { return MELEE_WEAPON; } if (weaponType.equals("twohandmelee")) { return MELEE_WEAPON; } if (weaponType.equals("unarmed")) { return MELEE_WEAPON; } if (weaponType.equals("polearm")) { return MELEE_WEAPON; } if (weaponType.equals("thrown")) { return RANGED_WEAPON; } if (weaponType.equals("onehandlightsaber")) { return MELEE_WEAPON; } if (weaponType.equals("twohandlightsaber")) { return MELEE_WEAPON; } if (weaponType.equals("polearmlightsaber")) { return MELEE_WEAPON; } if (weaponType.equals("force")) { return FORCE_POWER; } return 0; } public static int getWeaponCategory(int weaponType) throws InterruptedException { if (weaponType == WEAPON_TYPE_RIFLE) { return RANGED_WEAPON; } if (weaponType == WEAPON_TYPE_LIGHT_RIFLE) { return RANGED_WEAPON; } if (weaponType == WEAPON_TYPE_PISTOL) { return RANGED_WEAPON; } if (weaponType == WEAPON_TYPE_HEAVY) { return RANGED_WEAPON; } if (weaponType == WEAPON_TYPE_1HAND_MELEE) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_2HAND_MELEE) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_UNARMED) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_POLEARM) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_THROWN) { return RANGED_WEAPON; } if (weaponType == WEAPON_TYPE_WT_1HAND_LIGHTSABER) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_WT_2HAND_LIGHTSABER) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_WT_POLEARM_LIGHTSABER) { return MELEE_WEAPON; } if (weaponType == WEAPON_TYPE_FORCE_POWER) { return FORCE_POWER; } return 0; } public static String getWeaponStringCategory(int weaponType) throws InterruptedException { int weaponCategory = getWeaponCategory(weaponType); if (weaponCategory == RANGED_WEAPON) { return "ranged"; } if (weaponCategory == MELEE_WEAPON) { return "melee"; } if (weaponCategory == FORCE_POWER) { return "ranged"; } return null; } public static String getWeaponStringCategory(String weaponType) throws InterruptedException { int weaponCategory = getWeaponCategory(weaponType); if (weaponCategory == RANGED_WEAPON) { return "ranged"; } if (weaponCategory == MELEE_WEAPON) { return "melee"; } if (weaponCategory == FORCE_POWER) { return "ranged"; } return null; } public static int getWeaponType(String weaponType) throws InterruptedException { if (weaponType.equals("rifle")) { return WEAPON_TYPE_RIFLE; } if (weaponType.equals("carbine")) { return WEAPON_TYPE_LIGHT_RIFLE; } if (weaponType.equals("pistol")) { return WEAPON_TYPE_PISTOL; } if (weaponType.equals("heavyweapon")) { return WEAPON_TYPE_HEAVY; } if (weaponType.equals("onehandmelee")) { return WEAPON_TYPE_1HAND_MELEE; } if (weaponType.equals("twohandmelee")) { return WEAPON_TYPE_2HAND_MELEE; } if (weaponType.equals("unarmed")) { return WEAPON_TYPE_UNARMED; } if (weaponType.equals("polearm")) { return WEAPON_TYPE_POLEARM; } if (weaponType.equals("thrown")) { return WEAPON_TYPE_THROWN; } if (weaponType.equals("onehandlightsaber")) { return WEAPON_TYPE_WT_1HAND_LIGHTSABER; } if (weaponType.equals("twohandlightsaber")) { return WEAPON_TYPE_WT_2HAND_LIGHTSABER; } if (weaponType.equals("polearmlightsaber")) { return WEAPON_TYPE_WT_POLEARM_LIGHTSABER; } return 0; } public static int getCorrectedWeaponType(int passedType) throws InterruptedException { switch (passedType) { case WEAPON_TYPE_GROUND_TARGETTING: return WEAPON_TYPE_HEAVY; case WEAPON_TYPE_DIRECTIONAL: return WEAPON_TYPE_HEAVY; } return passedType; } public static String getWeaponStringType(int weaponType) throws InterruptedException { switch (weaponType) { case WEAPON_TYPE_RIFLE: return "rifle"; case WEAPON_TYPE_LIGHT_RIFLE: return "carbine"; case WEAPON_TYPE_PISTOL: return "pistol"; case WEAPON_TYPE_HEAVY: return "heavyweapon"; case WEAPON_TYPE_1HAND_MELEE: return "onehandmelee"; case WEAPON_TYPE_2HAND_MELEE: return "twohandmelee"; case WEAPON_TYPE_UNARMED: return "unarmed"; case WEAPON_TYPE_POLEARM: return "polearm"; case WEAPON_TYPE_THROWN: return "thrown"; case WEAPON_TYPE_WT_1HAND_LIGHTSABER: return "onehandlightsaber"; case WEAPON_TYPE_WT_2HAND_LIGHTSABER: return "twohandlightsaber"; case WEAPON_TYPE_WT_POLEARM_LIGHTSABER: return "polearmlightsaber"; case WEAPON_TYPE_FORCE_POWER: return "force"; } return ""; } public static boolean isMeleeWeapon(obj_id weapon) throws InterruptedException { int weaponType = getWeaponType(weapon); return isMeleeWeapon(weaponType); } public static boolean isMeleeWeapon(int weaponType) throws InterruptedException { return hasBit(weaponType, B_MELEE); } public static boolean isRangedWeapon(obj_id weapon) throws InterruptedException { int weaponType = getWeaponType(weapon); return isRangedWeapon(weaponType); } public static boolean isHeavyWeapon(int weaponType) throws InterruptedException { return hasBit(weaponType, B_HEAVY); } public static boolean isHeavyWeapon(obj_id weapon) throws InterruptedException { int weaponType = getWeaponType(weapon); return hasBit(weaponType, B_HEAVY); } public static boolean isRangedWeapon(int weaponType) throws InterruptedException { return hasBit(weaponType, B_RANGED); } public static boolean isLightsaberWeapon(obj_id weapon) throws InterruptedException { int weaponType = getWeaponType(weapon); return isLightsaberWeapon(weaponType); } public static boolean isLightsaberWeapon(int weaponType) throws InterruptedException { return hasBit(weaponType, B_ALL_LIGHTSABERS); } public static boolean isGrenade(weapon_data weaponData) throws InterruptedException { return hasBit(weaponData.weaponType, B_THROWN); } public static boolean isHeavyWeapon(weapon_data weaponData) throws InterruptedException { return hasBit(weaponData.weaponType, B_HEAVY); } public static boolean canApplyCombatEffect(obj_id objPlayer) throws InterruptedException { if (!isIdValid(objPlayer)) { return false; } if (ai_lib.aiGetNiche(objPlayer) == NICHE_DROID || vehicle.isVehicle(objPlayer) || ai_lib.isTurret(objPlayer) || ai_lib.isAndroid(objPlayer)) { return false; } return true; } public static boolean hasCertification(obj_id objPlayer, obj_id objWeapon) throws InterruptedException { return hasCertification(objPlayer, objWeapon, true); } public static boolean hasCertification(obj_id objPlayer, obj_id objWeapon, boolean verbose) throws InterruptedException { boolean hasCert = true; if (utils.hasScriptVar(objPlayer, "combat.weaponCertified")) { obj_id lastCertified = utils.getObjIdScriptVar(objPlayer, "combat.weaponCertified"); if (lastCertified == objWeapon) { return true; } } String template = getTemplateName(objWeapon); if (template == null) { return false; } if (template.equals("object/weapon/melee/unarmed/unarmed_default_player.iff")) { return true; } int speciesRequired = dataTableGetInt(combat.WEAPON_LEVEL_TABLE, template, "species_restriction"); if (speciesRequired >= 0) { int playerSpecies = getSpecies(objPlayer); if (playerSpecies != speciesRequired) { hasCert = false; } } if (hasObjVar(objWeapon, "factionrestricted.rebel")) { if (!factions.isRebel(objPlayer)) { hasCert = false; } } if (hasObjVar(objWeapon, "factionrestricted.imperial")) { if (!factions.isImperial(objPlayer)) { hasCert = false; } } String classTemplate = getSkillTemplate(objPlayer); if (isLightsaberWeapon(objWeapon) && !utils.isProfession(objPlayer, utils.FORCE_SENSITIVE)) { hasCert = false; } if (static_item.isDynamicItem(objWeapon)) { int levelRequired = getIntObjVar(objWeapon, "dynamic_item.intLevelRequired"); int playerLevel = getLevel(objPlayer); if (playerLevel < levelRequired) { hasCert = false; } } else if (static_item.isStaticItem(objWeapon)) { dictionary itemData = static_item.getMasterItemDictionary(objWeapon); String skillRequired = itemData.getString("required_skill"); if (skillRequired != null && !skillRequired.equals("")) { if (classTemplate != null && !classTemplate.equals("")) { if (!classTemplate.startsWith(skillRequired)) { hasCert = false; } } } int levelRequired = itemData.getInt("required_level"); int playerLevel = getLevel(objPlayer); if (playerLevel < levelRequired) { hasCert = false; } } else { String skillRequired = dataTableGetString(WEAPON_LEVEL_TABLE, template, "secondary_restriction"); if (skillRequired != null && !skillRequired.equals("")) { if (classTemplate != null && !classTemplate.equals("")) { if (!classTemplate.startsWith(skillRequired)) { hasCert = false; } } } int levelRequired = -1; int playerLevel = getLevel(objPlayer); if (hasObjVar(objWeapon, weapons.OBJVAR_WP_LEVEL)) { levelRequired = getIntObjVar(objWeapon, weapons.OBJVAR_WP_LEVEL); } else { levelRequired = dataTableGetInt(WEAPON_LEVEL_TABLE, template, "weapon_level"); } if (playerLevel < levelRequired) { hasCert = false; } } if (!hasCert && isGod(objPlayer)) { hasCert = true; } if (hasCert) { utils.setScriptVar(objPlayer, "combat.weaponCertified", objWeapon); } return hasCert; } public static void applyCombatSpeedDelay(obj_id target, float delay, int duration) throws InterruptedException { int delayStampStart = getGameTime(); int delayStampEnd = delayStampStart + duration; utils.setScriptVar(target, "speedDelayEffectStart", delayStampStart); utils.setScriptVar(target, "speedDelayEffectEnd", delayStampEnd); utils.setScriptVar(target, "speedDelayPotency", delay); return; } public static void doCombatDebuffs(obj_id self) throws InterruptedException { if (!utils.hasScriptVar(self, "fltNonCombatHealthRegen")) { float fltHealthRegen = getHealthRegenRate(self); utils.setScriptVar(self, "fltNonCombatHealthRegen", fltHealthRegen); setRegenRate(self, HEALTH, 0); } } public static boolean clearCombatDebuffs(obj_id self) throws InterruptedException { if (getGameTime() > utils.getIntScriptVar(self, "incap.timeStamp")) { if (utils.hasScriptVar(self, "fltNonCombatHealthRegen")) { float fltHealthRegen = utils.getFloatScriptVar(self, "fltNonCombatHealthRegen"); setRegenRate(self, HEALTH, fltHealthRegen); utils.removeScriptVar(self, "fltNonCombatHealthRegen"); return true; } } return false; } public static boolean isInCombat(obj_id objTarget) throws InterruptedException { return getState(objTarget, STATE_COMBAT) > 0 ? true : false; } public static String getAttackName(obj_id self) throws InterruptedException { String actionName = null; obj_id weapon = getCurrentWeapon(self); int weaponType = getWeaponType(weapon); if (utils.hasScriptVar(self, DO_ONCE_ATTACK_OVERRIDE)) { actionName = utils.getStringScriptVar(self, DO_ONCE_ATTACK_OVERRIDE); utils.removeScriptVar(self, DO_ONCE_ATTACK_OVERRIDE); return actionName; } if (utils.hasScriptVar(self, DEFAULT_ATTACK_OVERRIDE)) { return utils.getStringScriptVar(self, DEFAULT_ATTACK_OVERRIDE); } if (hasObjVar(self, DEFAULT_ATTACK_OVERRIDE)) { LOG("combat.scriptlib", "gcw getStringObjVar(self, DEFAULT_ATTACK_OVERRIDE): " + getStringObjVar(self, DEFAULT_ATTACK_OVERRIDE)); return getStringObjVar(self, DEFAULT_ATTACK_OVERRIDE); } if ((getTemplateName(weapon)).equals("object/weapon/ranged/droid/droid_flamethrower.iff")) { actionName = "ig88_flame_thrower"; } else if ((getTemplateName(weapon)).equals("object/weapon/ranged/droid/droid_flamethrower_nomuzzle.iff")) { actionName = "ig88_flame_thrower_vertical"; } else if ((getTemplateName(weapon)).equals("object/weapon/ranged/droid/droid_rocket_launcher.iff")) { actionName = "ig88_rocket_launcher"; } else if (combat.isRangedWeapon(weaponType)) { if (ai_lib.isHumanoid(self)) { actionName = "rangedShot"; } else { actionName = "creatureRangedAttack"; } } else if (combat.isMeleeWeapon(weaponType)) { if (ai_lib.isHumanoid(self)) { actionName = "meleeHit"; } else { actionName = "creatureMeleeAttack"; } } else if (combat.isLightsaberWeapon(weaponType)) { if (ai_lib.isHumanoid(self)) { actionName = "saberHit"; } else { actionName = "creatureMeleeAttack"; } } return actionName; } public static String getBestAction(obj_id player, String actionName) throws InterruptedException { for (int i = 2; i > 0; i--) { String testCmd = actionName + "_" + i; if (hasCommand(player, testCmd)) { return testCmd; } } return actionName; } public static String getActionAnimation(combat_data actionData, String weaponType) throws InterruptedException { String animation = ""; switch (weaponType) { case "unarmed": animation = actionData.anim_unarmed; break; case "onehandmelee": animation = actionData.anim_onehandmelee; break; case "twohandmelee": animation = actionData.anim_twohandmelee; break; case "polearm": animation = actionData.anim_polearm; break; case "lightRifle": animation = actionData.anim_lightRifle; break; case "pistol": animation = actionData.anim_pistol; break; case "carbine": animation = actionData.anim_carbine; break; case "rifle": animation = actionData.anim_rifle; break; case "heavyweapon": animation = actionData.anim_heavyweapon; break; case "thrown": animation = actionData.anim_thrown; break; case "onehandlightsaber": animation = actionData.anim_onehandlightsaber; break; case "twohandlightsaber": animation = actionData.anim_twohandlightsaber; break; case "polearmlightsaber": animation = actionData.anim_polearmlightsaber; break; } if (animation.length() < 1) { animation = actionData.animDefault; } if (animation.startsWith("*creature_attack")) { return getCreatureAnimationName(animation); } String[] anims = split(animation, ','); if (anims.length > 1) { animation = anims[rand(0, anims.length - 1)]; } return animation; } public static String getCreatureAnimationName(String playbackName) throws InterruptedException { obj_id self = getSelf(); String animName = ""; int niche = ai_lib.aiGetNiche(self); if (niche == NICHE_VEHICLE) { animName = "droid_attack"; } else { if (playbackName.endsWith("ranged")) { animName = "creature_attack_ranged"; } else if (playbackName.endsWith("melee")) { if (rand(1, 2) == 1) { animName = "creature_attack"; } else { animName = "creature_attack_special_1"; } if (rand(1, 2) == 1) { animName += "_light"; } else { animName += "_heavy"; } return animName; } else { animName = "creature_attack_special_" + rand(1, 2); } } animName += "_medium"; return animName; } public static obj_id makeTrackerEgg(obj_id objPlayer, location locTest, combat_data actionData) throws InterruptedException { obj_id objEgg = createObject(actionData.delayAttackEggTemplate, locTest); if (!isIdValid(objEgg)) { return null; } dictionary combatDict = combat_engine.getCombatDataDictionary(actionData); utils.setScriptVar(objEgg, "combatDataDict", combatDict); utils.setScriptVar(objEgg, "objOwner", objPlayer); utils.setScriptVar(objPlayer, "objEgg", objEgg); attachScript(objEgg, "systems.combat.combat_delayed_tracker"); return objEgg; } public static int makeBit(int bit) throws InterruptedException { return (1 << (bit - 1)); } public static boolean hasBit(int weaponType, int bitValue) throws InterruptedException { if (weaponType == -1) { return false; } return ((weaponTypeToBitValueMap[weaponType] & bitValue) != 0); } public static float doCriticalHitEffect(attacker_data attackerData, defender_data defenderData, weapon_data weaponData, hit_result hitData, combat_data actionData) throws InterruptedException { int elementalType = weaponData.elementalType; float elementalDamage = weaponData.elementalValue; float damageMod = 1.5f; hitData.critDamage = 2; int critResist = 0; float heatResist = 0; float coldResist = 0; float acidResist = 0; float electricResist = 0; int resistRoll = rand(1, 10000); if (actionData.percentAddFromWeapon > 0.0f) { if (utils.hasScriptVar(defenderData.id, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION)) { critResist += utils.getIntScriptVar(defenderData.id, armor.SCRIPTVAR_CACHED_GENERAL_PROTECTION); } if (utils.hasScriptVar(defenderData.id, armor.SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS)) { dictionary protections = utils.getDictionaryScriptVar(defenderData.id, armor.SCRIPTVAR_CACHED_SPECIAL_PROTECTIONS); heatResist = protections.getFloat("heat"); coldResist = protections.getFloat("cold"); acidResist = protections.getFloat("acid"); electricResist = protections.getFloat("electricity"); } switch (elementalType) { case DAMAGE_ELEMENTAL_HEAT: if (resistRoll > critResist + heatResist) { int level = getLevel(attackerData.id); if (level == 90) { buff.applyBuff(defenderData.id, attackerData.id, "crit_fire_dot_4"); } else if (level >= 75) { buff.applyBuff(defenderData.id, attackerData.id, "crit_fire_dot_3"); } else if (level >= 50) { buff.applyBuff(defenderData.id, attackerData.id, "crit_fire_dot_2"); } else if (level >= 25) { buff.applyBuff(defenderData.id, attackerData.id, "crit_fire_dot_1"); } damageMod = 1.5f; hitData.critDamage = 3; return damageMod; } break; case DAMAGE_ELEMENTAL_COLD: if (resistRoll > critResist + coldResist && !buff.hasBuff(defenderData.id, "criticalImmunity")) { if (!buff.hasBuff(defenderData.id, "criticalColdEffect")) { buff.applyBuff(defenderData.id, "criticalColdEffect", 6, (float)elementalDamage / 10.0f); } damageMod = 1.5f; hitData.critDamage = 4; return damageMod; } break; case DAMAGE_ELEMENTAL_ACID: if (resistRoll > critResist + acidResist && !buff.hasBuff(defenderData.id, "criticalAcidEffect") && !buff.hasBuff(defenderData.id, "criticalImmunity")) { buff.applyBuff(defenderData.id, "criticalAcidEffect"); damageMod = 1.5f; hitData.critDamage = 5; return damageMod; } break; case DAMAGE_ELEMENTAL_ELECTRICAL: if (resistRoll > critResist + electricResist && !buff.hasBuff(defenderData.id, "criticalElectricEffect") && !buff.hasBuff(defenderData.id, "criticalImmunity")) { buff.applyBuff(defenderData.id, "criticalElectricEffect"); damageMod = 1.5f; hitData.critDamage = 6; return damageMod; } break; } } return damageMod; } public static color getCriticalColor(int hitDataCritDamage) throws InterruptedException { color criticalColor = colors.MEDIUMVIOLETRED; switch (hitDataCritDamage) { case 1: criticalColor = colors.SLATEGREY; break; case 3: criticalColor = colors.ORANGERED; break; case 4: criticalColor = colors.LIGHTSKYBLUE; break; case 5: criticalColor = colors.DARKOLIVEGREEN; break; case 6: criticalColor = colors.SILVER; break; } return criticalColor; } public static obj_id directDamageToDifferentTarget(obj_id attacker, obj_id defender) throws InterruptedException { if (buff.hasBuff(defender, "bm_shield_master_player")) { obj_id guardian = beast_lib.getBeastOnPlayer(defender); if (isIdValid(guardian) && exists(guardian) && !isDead(guardian) && !isIncapacitated(guardian) && getEnhancedSkillStatisticModifierUncapped(defender, "damage_immune") <= 0) { return guardian; } } if (utils.hasScriptVar(defender, DAMAGE_REDIRECT)) { obj_id guardian = utils.getObjIdScriptVar(defender, DAMAGE_REDIRECT); if (isIdValid(guardian) && exists(guardian) && !isMob(guardian) && getEnhancedSkillStatisticModifierUncapped(defender, "damage_immune") <= 0) { return guardian; } if (isIdValid(guardian) && exists(guardian) && isMob(guardian) && !isDead(guardian) && !isIncapacitated(guardian) && getEnhancedSkillStatisticModifierUncapped(defender, "damage_immune") <= 0 && pvpCanAttack(attacker, guardian)) { return guardian; } else { utils.removeScriptVar(defender, DAMAGE_REDIRECT); } } return defender; } public static float getMissChance(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException { obj_id attacker = attackerData.id; obj_id defender = defenderData.id; float missChance = isPlayer(attacker) ? 0.0f : 5.0f; if (autoAim && isRangedWeapon(getCurrentWeapon(attacker))) { missChance += 5.0f; boolean attackerMovePenalty = (getLocomotion(attacker) == LOCOMOTION_RUNNING && isRangedWeapon(getCurrentWeapon(attacker))); boolean defenderMovePenalty = (getLocomotion(defender) == LOCOMOTION_RUNNING && isRangedWeapon(getCurrentWeapon(defender))); if (isPlayer(attacker) && attackerMovePenalty) { missChance += 5.0f; } if (defenderMovePenalty) { missChance += 3.0f; } } if (isRangedWeapon(getCurrentWeapon(attackerData.id))) { missChance += (getEnhancedSkillStatisticModifierUncapped(defenderData.id, "combat_ranged_attack_avoidance") / 10.0f); missChance -= (getEnhancedSkillStatisticModifierUncapped(defenderData.id, "combat_ranged_attack_vulnerability") / 10.0f); } if (isMeleeWeapon(getCurrentWeapon(attackerData.id)) || isLightsaberWeapon(getCurrentWeapon(attackerData.id))) { missChance += (getEnhancedSkillStatisticModifierUncapped(defenderData.id, "combat_melee_attack_avoidance") / 10.0f); missChance -= (getEnhancedSkillStatisticModifierUncapped(defenderData.id, "combat_melee_attack_vulnerability") / 10.0f); } missChance += getAttackerMissChance(attackerData, actionData, autoAim); missChance += getDefenderMissChance(defenderData, actionData, autoAim); missChance -= attackerData.hitChance; missChance += defenderData.increaseMiss; return missChance; } public static float getAttackerMissChance(attacker_data attackerData, combat_data actionData, boolean autoAim) throws InterruptedException { float attackerMissChance = 0.0f; if (isRangedWeapon(getCurrentWeapon(attackerData.id))) { attackerMissChance += (getEnhancedSkillStatisticModifierUncapped(attackerData.id, "combat_ranged_attack_miss") / 10.0f); attackerMissChance -= (getEnhancedSkillStatisticModifierUncapped(attackerData.id, "combat_ranged_attack_miss_reduction") / 10.0f); } if (isMeleeWeapon(getCurrentWeapon(attackerData.id)) || isLightsaberWeapon(getCurrentWeapon(attackerData.id))) { attackerMissChance += (getEnhancedSkillStatisticModifierUncapped(attackerData.id, "combat_melee_attack_miss") / 10.0f); attackerMissChance -= (getEnhancedSkillStatisticModifierUncapped(attackerData.id, "combat_melee_attack_miss_reduction") / 10.0f); } attackerMissChance += (getEnhancedSkillStatisticModifierUncapped(attackerData.id, "combat_all_attack_miss") / 10.0f); attackerMissChance -= (getEnhancedSkillStatisticModifierUncapped(attackerData.id, "combat_all_attack_miss_reduction") / 10.0f); return attackerMissChance; } public static float getDefenderMissChance(defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException { float defenderMissChance = 0.0f; defenderMissChance += (getEnhancedSkillStatisticModifierUncapped(defenderData.id, "combat_all_attack_avoidance") / 10.0f); defenderMissChance -= (getEnhancedSkillStatisticModifierUncapped(defenderData.id, "combat_all_attack_miss_vulnerability") / 10.0f); return defenderMissChance; } public static float getGlancingBlowChance(attacker_data attackerData, defender_data defenderData, combat_data actionData) throws InterruptedException { obj_id attacker = attackerData.id; obj_id defender = defenderData.id; float glancingChance = 0.0f; if (isMob(defender) && beast_lib.isBeast(defender)) { glancingChance += (int)beast_lib.getBeastGlanceChance(defender); } if ((isPlayer(attacker) || beast_lib.isBeast(attacker)) && (!isPlayer(defender) && !beast_lib.isBeast(defender))) { int attackerLevel = getLevel(attacker); int defenderLevel = getLevel(defender); int difficultyClass = getIntObjVar(defender, "difficultyClass"); if (attackerLevel < defenderLevel) { glancingChance = 5.0f * (defenderLevel - attackerLevel); } switch (difficultyClass) { case 1: if (glancingChance < 5.0f) { glancingChance = 5.0f; } break; case 2: if (glancingChance < 15.0f) { glancingChance = 15.0f; } } } if ((!isPlayer(attacker) && !beast_lib.isBeast(attacker)) && (isPlayer(defender) || beast_lib.isBeast(defender))) { int attackerLevel = getLevel(attacker); int defenderLevel = getLevel(defender); defenderLevel = Math.round(defenderLevel * 0.75f); if (attackerLevel < defenderLevel) { glancingChance = 2.0f * (defenderLevel - attackerLevel); } } glancingChance += defenderData.glancingChance; glancingChance -= attackerData.reduceGlancing; boolean isRangedAttacker = isRangedWeapon(getCurrentWeapon(attacker)); boolean isRangedDefender = isRangedWeapon(getCurrentWeapon(defender)); if (isRangedAttacker) { glancingChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_glancing_blow_ranged"); if (armor.hasExpertiseArmorSetBonus(defender)) { glancingChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_armorset_glancing_blow_ranged_" + armor.getExpertiseArmorSetId(defender)); } } if (!isRangedAttacker) { glancingChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_glancing_blow_melee"); if (armor.hasExpertiseArmorSetBonus(defender)) { glancingChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_armorset_glancing_blow_melee_" + armor.getExpertiseArmorSetId(defender)); } } glancingChance -= actionData.reduceGlancing; if (beast_lib.isBeast(defender)) { LOG("beastDamage", "glancingChance: " + glancingChance); } return glancingChance; } public static float getDefenderGlancingBlowChance(obj_id defender) throws InterruptedException { float glancingChance = 0.0f; boolean isRangedDefender = isRangedWeapon(getCurrentWeapon(defender)); if (!isRangedDefender) { glancingChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_glancing_blow_melee_defense"); } glancingChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_glancing_blow_all"); glancingChance += utils.getIntScriptVar(defender, "expertise_stance_glance"); glancingChance += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(defender, "combat_glancing"), DR_DEFENDER_GLANCE) / 10.0f); return glancingChance; } public static float getAttackerGlancingReduction(obj_id attacker) throws InterruptedException { float glancingReduce = 0.0f; if (beast_lib.isBeastMaster(attacker) || beast_lib.isBeast(attacker)) { glancingReduce += beast_lib.getBeastModGlancingReduction(attacker); } glancingReduce += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "combat_glancing_blow_reduction") / 7.0f); glancingReduce += (getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_glancing_blow_reduction")); glancingReduce -= getEnhancedSkillStatisticModifierUncapped(attacker, "glancing_blow_vulnerable"); return glancingReduce; } public static float getPunishingBlowChance(attacker_data attackerData, defender_data defenderData) throws InterruptedException { obj_id attacker = attackerData.id; obj_id defender = defenderData.id; float punishingChance = 0.0f; if ((!isPlayer(attacker) && !beast_lib.isBeast(attacker)) && (isPlayer(defender) || beast_lib.isBeast(defender))) { int attackerLevel = getLevel(attacker); int defenderLevel = getLevel(defender); if (attackerLevel > defenderLevel) { punishingChance = 5.0f * (attackerLevel - defenderLevel); } int difficultyClass = getIntObjVar(attacker, "difficultyClass"); switch (difficultyClass) { case 1: if (punishingChance < 5.0f) { punishingChance = 5.0f; } break; case 2: if (punishingChance < 15.0f) { punishingChance = 15.0f; } } } punishingChance -= defenderData.reducePunishing; return punishingChance; } public static float getDefenderPunishingBlowReduction(obj_id defender) throws InterruptedException { float punishingChance = 0.0f; if (beast_lib.isBeastMaster(defender) || beast_lib.isBeast(defender)) { punishingChance += beast_lib.getBeastModPunishingReduction(defender); } return punishingChance; } public static float getDodgeChance(attacker_data attackerData, defender_data defenderData, combat_data actionData) throws InterruptedException { obj_id attacker = attackerData.id; obj_id defender = defenderData.id; float dodgeChance = 0.0f; dodgeChance += defenderData.dodgeChance; dodgeChance -= attackerData.reduceDodge; dodgeChance -= actionData.reduceDodge; boolean isRangedAttacker = isRangedWeapon(getCurrentWeapon(attacker)); if (isRangedAttacker) { if (armor.hasExpertiseArmorSetBonus(defender)) { dodgeChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_armorset_ranged_dodge_" + armor.getExpertiseArmorSetId(defender)); } } if (!isRangedAttacker) { if (armor.hasExpertiseArmorSetBonus(defender)) { dodgeChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_armorset_melee_dodge_" + armor.getExpertiseArmorSetId(defender)); } } return dodgeChance; } public static float getDefenderDodgeChance(obj_id player) throws InterruptedException { float dodgeChance = isPlayer(player) || beast_lib.isBeast(player) ? 5.0f : getLevel(player) * 0.10f; dodgeChance += (getEnhancedSkillStatisticModifierUncapped(player, "agility_modified") / 100.0f); dodgeChance += (getEnhancedSkillStatisticModifierUncapped(player, "agility") / 100.0f); dodgeChance += (getEnhancedSkillStatisticModifierUncapped(player, "luck_modified") / 300.0f); dodgeChance += (getEnhancedSkillStatisticModifierUncapped(player, "luck") / 300.0f); dodgeChance += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(player, "combat_dodge"), DR_DEFENDER_DODGE) / 10.0f); dodgeChance += getEnhancedSkillStatisticModifierUncapped(player, "expertise_dodge"); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { dodgeChance += beast_lib.getBeastDodgeChance(player); } return dodgeChance; } public static float getAttackerDodgeReduction(obj_id attacker) throws InterruptedException { float dodgeReduction = 0.0f; dodgeReduction += (getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_dodge_reduction")); dodgeReduction += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "exotic_dodge_reduction") / 7.0f); dodgeReduction += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "combat_dodge_reduction") / 7.0f); return dodgeReduction; } public static float getParryChance(attacker_data attackerData, defender_data defenderData, combat_data actionData) throws InterruptedException { float parryChance = 0.0f; parryChance += defenderData.parryChance; parryChance -= attackerData.reduceParry; parryChance -= actionData.reduceParry; return parryChance; } public static float getDefenderParryChance(obj_id player) throws InterruptedException { float parrySkill = isPlayer(player) || beast_lib.isBeast(player) ? 5.0f : getLevel(player) * 0.09f; parrySkill += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(player, "combat_parry"), DR_DEFENDER_PARRY) / 10.0f); parrySkill += (getEnhancedSkillStatisticModifierUncapped(player, "precision_modified") / 200.0f); parrySkill += (getSkillStatisticModifier(player, "precision") / 200.0f); parrySkill += (getEnhancedSkillStatisticModifierUncapped(player, "agility_modified") / 200.0f); parrySkill += (getSkillStatisticModifier(player, "agility") / 200.0f); parrySkill += getEnhancedSkillStatisticModifierUncapped(player, "expertise_parry"); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { parrySkill += beast_lib.getBeastParryChance(player); } parrySkill += getSkillStatisticModifier(player, "expertise_stance_saber_block"); boolean isSaberBlocking = buff.hasBuff(player, "saberBlock"); if (isSaberBlocking) { float enhancedSaberBlockSkillMod = getSkillStatisticModifier(player, "expertise_saber_block"); parrySkill += enhancedSaberBlockSkillMod; } parrySkill = parrySkill > 75.0f ? 75.0f : parrySkill; return parrySkill; } public static float getAttackerParryReduction(obj_id attacker) throws InterruptedException { float parryReduction = 0.0f; parryReduction += (getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_parry_reduction")); parryReduction += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "exotic_parry_reduction") / 7.0f); parryReduction += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "combat_parry_reduction") / 7.0f); return parryReduction; } public static float getBlockChance(attacker_data attackerData, defender_data defenderData, combat_data actionData) throws InterruptedException { float blockChance = 0.0f; obj_id attacker = attackerData.id; obj_id defender = defenderData.id; blockChance += defenderData.blockChance; blockChance -= attackerData.reduceBlock; blockChance -= actionData.reduceBlock; boolean isRangedAttacker = isRangedWeapon(getCurrentWeapon(attacker)); if (isRangedAttacker) { if (armor.hasExpertiseArmorSetBonus(defender)) { blockChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_armorset_ranged_block_" + armor.getExpertiseArmorSetId(defender)); } } if (!isRangedAttacker) { if (armor.hasExpertiseArmorSetBonus(defender)) { blockChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_armorset_melee_block_" + armor.getExpertiseArmorSetId(defender)); } } return blockChance; } public static float getDefenderBlockChance(obj_id player) throws InterruptedException { float blockChance = isPlayer(player) ? 5.0f : getLevel(player) * 0.14f; blockChance += (getEnhancedSkillStatisticModifierUncapped(player, "precision_modified") / 200.0f); blockChance += (getEnhancedSkillStatisticModifierUncapped(player, "precision") / 200.0f); blockChance += (getEnhancedSkillStatisticModifierUncapped(player, "strength_modified") / 200.0f); blockChance += (getEnhancedSkillStatisticModifierUncapped(player, "strength") / 200.0f); blockChance += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(player, "combat_block_chance"), DR_DEFENDER_BLOCK) / 5.0f); blockChance += getEnhancedSkillStatisticModifierUncapped(player, "expertise_block_chance"); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { blockChance += beast_lib.getBeastBlockChance(player); } if (blockChance > 30.0f) { int nextBlockLog = utils.getIntScriptVar(player, "nextBlockLog"); if (getGameTime() > nextBlockLog) { float fromBlockChance = getEnhancedSkillStatisticModifierUncapped(player, "combat_block_chance") / 5.0f; float fromBlockChanceDiminished = (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(player, "combat_block_chance"), DR_DEFENDER_BLOCK) / 5.0f); float fromPreMod = getEnhancedSkillStatisticModifierUncapped(player, "precision_modified") / 200.0f; float fromPre = getEnhancedSkillStatisticModifierUncapped(player, "precision") / 200.0f; float fromStrMod = getEnhancedSkillStatisticModifierUncapped(player, "strength_modified") / 200.0f; float fromStr = getEnhancedSkillStatisticModifierUncapped(player, "strength") / 200.0f; float fromExChance = getEnhancedSkillStatisticModifierUncapped(player, "expertise_block_chance"); float fromBeastMod = 0.0f; if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { fromBeastMod += beast_lib.getBeastBlockChance(player); } utils.setScriptVar(player, "nextBlockLog", getGameTime() + 600); CustomerServiceLog("BLOCK_CHANCE", "Player %TU total block chance: 5% + " + blockChance + " = combat_block_chance(" + fromBlockChance + "/ DRF: " + fromBlockChanceDiminished + ") + precision_modified(" + fromPreMod + ") + precision(" + fromPre + ") + strength_modified(" + fromStrMod + ") + strength(" + fromStr + ") + Expertise Block(" + fromExChance + ") + beast_mod(" + fromBeastMod + ")", player); } } return blockChance; } public static float getAttackerBlockReduction(obj_id attacker) throws InterruptedException { float blockReduction = 0.0f; blockReduction += (getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_block_reduction")); blockReduction += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "exotic_block_reduction") / 5.0f); blockReduction += (float)(getEnhancedSkillStatisticModifierUncapped(attacker, "combat_block_reduction") / 5.0f); return blockReduction; } public static int getBlockAmmount(obj_id player) throws InterruptedException { int blockAmmount = isPlayer(player) || beast_lib.isBeast(player) ? 25 : Math.round((getLevel(player) * 2)); float blockStat = 0.0f; blockStat += getEnhancedSkillStatisticModifierUncapped(player, "strength_modified"); blockStat += getSkillStatisticModifier(player, "strength"); blockStat += getEnhancedSkillStatisticModifierUncapped(player, "combat_block_value"); blockStat = Math.round((float)blockStat * 0.5f); blockAmmount += blockStat; if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { blockAmmount += beast_lib.getBeastBlockValue(player); } return blockAmmount; } public static float getEvasionChance(attacker_data attackerData, defender_data defenderData) throws InterruptedException { float evasionChance = 0.0f; evasionChance += defenderData.evadeChance; evasionChance -= attackerData.reduceEvade; return evasionChance; } public static float getDefenderEvasionChance(obj_id player) throws InterruptedException { float evasionChance = isPlayer(player) || beast_lib.isBeast(player) ? 5.0f : (getLevel(player) * 0.2f); evasionChance += (getEnhancedSkillStatisticModifierUncapped(player, "agility_modified") / 100.0f); evasionChance += (getSkillStatisticModifier(player, "agility") / 100.0f); evasionChance += (getEnhancedSkillStatisticModifierUncapped(player, "luck_modified") / 300.0f); evasionChance += (getSkillStatisticModifier(player, "luck") / 300.0f); evasionChance += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(player, "combat_evasion_chance"), DR_DEFENDER_EVADE) / 10.0f); evasionChance += getEnhancedSkillStatisticModifierUncapped(player, "expertise_evasion_chance"); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { evasionChance += beast_lib.getBeastEvadeChance(player); } return evasionChance; } public static float getAttackerEvasionReduction(obj_id attacker) throws InterruptedException { return 0.0f; } public static float getHitReductionChance(obj_id player) throws InterruptedException { return getEnhancedSkillStatisticModifierUncapped(player, "expertise_critical_hit_reduction"); } public static float getPvPHitReductionChance(obj_id player) throws InterruptedException { return getEnhancedSkillStatisticModifierUncapped(player, "expertise_critical_hit_pvp_reduction"); } public static float getEvasionRoll(obj_id player) throws InterruptedException { float evasionRoll = 0.0f; evasionRoll += (getEnhancedSkillStatisticModifierUncapped(player, "combat_evasion_value") / 4.0f); if (evasionRoll < 0) { return -1.0f; } evasionRoll += 10.0f; evasionRoll += (getEnhancedSkillStatisticModifierUncapped(player, "luck_modified") / 10.0f); evasionRoll += (getSkillStatisticModifier(player, "luck") / 10.0f); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { evasionRoll += beast_lib.getBeastEvadeValue(player); } return evasionRoll > 99.0f ? 99.0f : evasionRoll; } public static float getCriticalHitChance(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAiming) throws InterruptedException { obj_id attacker = attackerData.id; obj_id defender = defenderData.id; float critChance = attackerData.critChance; float altCritChance = 0.0f; String specialLine = actionData.specialLine; critChance += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_line_" + specialLine); if (!autoAiming && isPlayer(attacker)) { critChance += 3.0f; } float specificDefenderMod = 0.0f; if (isPlayer(defender)) { altCritChance += attackerData.pvpCrit; } else if (ai_lib.isDroid(defender) || ai_lib.isAndroid(defender)) { altCritChance += attackerData.droidCrit; } else if (ai_lib.isNpc(defender)) { altCritChance += attackerData.npcCrit; } else if (ai_lib.isMonster(defender)) { altCritChance += attackerData.creatureCrit; } altCritChance = getDiminishedReturnValue(altCritChance, DR_ATTACKER_CRITICAL_2); critChance -= defenderData.reduceCritical; if (isPlayer(defender) && (isPlayer(attacker) || (isMob(attacker) && isIdValid(getMaster(attacker)) && isPlayer(getMaster(attacker))))) { critChance -= getEnhancedSkillStatisticModifierUncapped(defender, "expertise_critical_hit_pvp_reduction"); } critChance += actionData.increaseCritical; critChance += altCritChance; return critChance; } public static float getAttackerCritMod(obj_id attacker) throws InterruptedException { float critMod = isPlayer(attacker) ? 5.0f : getLevel(attacker) * 0.15f; float dr_critMod = 0.0f; float dr_critMod2 = 0.0f; critMod += utils.getIntScriptVar(attacker, "expertise_stance_critical"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_niche_all"); critMod += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(attacker, "combat_critical_hit"), DR_ATTACKER_CRITICAL) / 10.0f); if (beast_lib.isBeastMaster(attacker) || beast_lib.isBeast(attacker)) { critMod += beast_lib.getBeastCritChance(attacker); } critMod += (getEnhancedSkillStatisticModifierUncapped(attacker, "precision_modified") / 100.0f); critMod += (getSkillStatisticModifier(attacker, "precision") / 100.0f); critMod += (getEnhancedSkillStatisticModifierUncapped(attacker, "luck_modified") / 300.0f); critMod += (getSkillStatisticModifier(attacker, "luck") / 300.0f); obj_id weapon = getCurrentWeapon(attacker); if (!isIdValid(weapon)) { return critMod; } weapon_data weaponData = weapons.getNewWeaponData(weapon); if (weaponData == null) { CustomerServiceLog("COMBAT_exception", "getAttackerCritMod: Player %TU caused an exception with bad cached data on weapon " + weapon + "/" + getTemplateName(weapon), attacker); } boolean isRangedAttacker = isRangedWeapon(weapon); if (isRangedAttacker) { dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_ranged"); dr_critMod += (getEnhancedSkillStatisticModifierUncapped(attacker, "combat_critical_ranged") / 10.0f); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_ranged"); } else { dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_melee"); dr_critMod += (getEnhancedSkillStatisticModifierUncapped(attacker, "combat_critical_melee") / 10.0f); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_melee"); } if(weaponData != null) { switch (weaponData.weaponType) { case WEAPON_TYPE_PISTOL: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_pistol"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_pistol"); break; case WEAPON_TYPE_RIFLE: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_rifle"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_rifle"); break; case WEAPON_TYPE_LIGHT_RIFLE: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_carbine"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_carbine"); break; case WEAPON_TYPE_HEAVY: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_heavy"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_heavy"); break; case WEAPON_TYPE_GROUND_TARGETTING: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_heavy"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_heavy"); break; case WEAPON_TYPE_DIRECTIONAL: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_heavy"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_heavy"); break; case WEAPON_TYPE_1HAND_MELEE: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_1h"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_1h"); break; case WEAPON_TYPE_2HAND_MELEE: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_2h"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_2h"); break; case WEAPON_TYPE_UNARMED: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_unarmed"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_unarmed"); break; case WEAPON_TYPE_POLEARM: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_polearm"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_polearm"); break; case WEAPON_TYPE_WT_1HAND_LIGHTSABER: case WEAPON_TYPE_WT_2HAND_LIGHTSABER: case WEAPON_TYPE_WT_POLEARM_LIGHTSABER: dr_critMod2 += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_lightsaber"); critMod += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_undiminished_critical_lightsaber"); break; } } dr_critMod = getDiminishedReturnValue(dr_critMod, DR_ATTACKER_CRITICAL); dr_critMod2 = getDiminishedReturnValue(dr_critMod2, DR_ATTACKER_CRITICAL_2); critMod += dr_critMod; critMod += dr_critMod2; return critMod; } public static float getDefenderCriticalChance(obj_id defender) throws InterruptedException { float critChance = 0.0f; critChance -= getEnhancedSkillStatisticModifierUncapped(defender, "critical_hit_vulnerable"); critChance += (getEnhancedSkillStatisticModifierUncapped(defender, "combat_critical_hit_reduction") / 3.0f); critChance += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_critical_hit_reduction"); return critChance; } public static float getStrikethroughChance(attacker_data attackerData, defender_data defenderData, combat_data actionData) throws InterruptedException { float strikethroughChance = 0.0f; strikethroughChance += attackerData.strikethroughChance; strikethroughChance -= defenderData.reduceStrikethrough; strikethroughChance += actionData.increaseStrikethrough; return strikethroughChance; } public static float getAttackerStrikethroughChance(obj_id player) throws InterruptedException { float strikethrough = isPlayer(player) || beast_lib.isBeast(player) ? 5.0f : getLevel(player) * 0.08f; strikethrough += (getEnhancedSkillStatisticModifierUncapped(player, "precision_modified") / 200.0f); strikethrough += (getSkillStatisticModifier(player, "precision") / 200.0f); strikethrough += (getEnhancedSkillStatisticModifierUncapped(player, "luck_modified") / 200.0f); strikethrough += (getSkillStatisticModifier(player, "luck") / 200.0f); strikethrough += (getDiminishedReturnValue(getEnhancedSkillStatisticModifierUncapped(player, "combat_strikethrough_chance"), DR_ATTACKER_STRIKETHROUGH) / 10.0f); strikethrough += getEnhancedSkillStatisticModifierUncapped(player, "expertise_strikethrough_chance"); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { strikethrough += beast_lib.getBeastStrikethroughChance(player); } return strikethrough; } public static float getDefenderStrikethroughReduction(obj_id player) throws InterruptedException { float strikethroughChance = 0.0f; strikethroughChance -= getEnhancedSkillStatisticModifierUncapped(player, "strikethrough_vulnerable"); return strikethroughChance; } public static float getStrikethroughValue(obj_id attacker, obj_id defender) throws InterruptedException { return getAttackerStrikethroughValue(attacker); } public static float getAttackerStrikethroughValue(obj_id player) throws InterruptedException { float strikethroughValue = isPlayer(player) ? 10.0f : 35.0f; strikethroughValue += (getEnhancedSkillStatisticModifierUncapped(player, "luck_modified") / 10.0f); strikethroughValue += (getSkillStatisticModifier(player, "luck") / 10.0f); strikethroughValue += (getEnhancedSkillStatisticModifierUncapped(player, "combat_strikethrough_value") / 2.0f); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { strikethroughValue += beast_lib.getBeastStrikethroughValue(player); } return strikethroughValue > 99.0f ? 99.0f : strikethroughValue; } public static float getToHitBonus(obj_id player) throws InterruptedException { float hitBonus = 0.0f; hitBonus += (getEnhancedSkillStatisticModifierUncapped(player, "strength_modified") / 100.0f); hitBonus += (getSkillStatisticModifier(player, "strength") / 100.0f); hitBonus += getEnhancedSkillStatisticModifierUncapped(player, "expertise_to_hit"); if (beast_lib.isBeastMaster(player) || beast_lib.isBeast(player)) { hitBonus += beast_lib.getBeastHitChance(player); } return hitBonus; } public static float getDefenderHitReduction(obj_id player) throws InterruptedException { float increaseMiss = 0.0f; return increaseMiss; } public static attacker_data fillAttackerCombatAttributes(attacker_data attackerData) throws InterruptedException { if (!utils.hasScriptVar(attackerData.id, ATTACKER_DATA)) { cacheAttackerData(attackerData.id); } attackerData.critChance = utils.getFloatScriptVar(attackerData.id, ATTACKER_BASE_CRIT); attackerData.pvpCrit = utils.getFloatScriptVar(attackerData.id, ATTACKER_PVP_CRIT); attackerData.npcCrit = utils.getFloatScriptVar(attackerData.id, ATTACKER_NPC_CRIT); attackerData.droidCrit = utils.getFloatScriptVar(attackerData.id, ATTACKER_DROID_CRIT); attackerData.creatureCrit = utils.getFloatScriptVar(attackerData.id, ATTACKER_CREATURE_CRIT); attackerData.hitChance = utils.getFloatScriptVar(attackerData.id, ATTACKER_HIT_BONUS); attackerData.strikethroughChance = utils.getFloatScriptVar(attackerData.id, ATTACKER_STRIKETHROUGH); attackerData.reduceParry = utils.getFloatScriptVar(attackerData.id, ATTACKER_REDUCE_PARRY); attackerData.reduceBlock = utils.getFloatScriptVar(attackerData.id, ATTACKER_REDUCE_BLOCK); attackerData.reduceDodge = utils.getFloatScriptVar(attackerData.id, ATTACKER_REDUCE_DODGE); attackerData.reduceGlancing = utils.getFloatScriptVar(attackerData.id, ATTACKER_REDUCE_GLANCING); attackerData.reduceEvade = utils.getFloatScriptVar(attackerData.id, ATTACKER_REDUCE_EVADE); return attackerData; } public static defender_data fillDefenderCombatAttributes(defender_data defenderData) throws InterruptedException { if (!utils.hasScriptVar(defenderData.id, DEFENDER_DATA)) { cacheDefenderData(defenderData.id); } defenderData.reduceCritical = utils.getFloatScriptVar(defenderData.id, DEFENDER_REDUCE_CRIT); defenderData.increaseMiss = utils.getFloatScriptVar(defenderData.id, DEFENDER_INCREASE_MISS); defenderData.reduceStrikethrough = utils.getFloatScriptVar(defenderData.id, DEFENDER_REDUCE_STRIKETHROUGH); defenderData.reducePunishing = utils.getFloatScriptVar(defenderData.id, DEFENDER_REDUCE_PUNISH); defenderData.glancingChance = utils.getFloatScriptVar(defenderData.id, DEFENDER_GLANCING); defenderData.parryChance = utils.getFloatScriptVar(defenderData.id, DEFENDER_PARRY); defenderData.dodgeChance = utils.getFloatScriptVar(defenderData.id, DEFENDER_DODGE); defenderData.evadeChance = utils.getFloatScriptVar(defenderData.id, DEFENDER_EVADE); defenderData.blockChance = utils.getFloatScriptVar(defenderData.id, DEFENDER_BLOCK); return defenderData; } public static void cacheCombatData(obj_id self) throws InterruptedException { cacheAttackerData(self); cacheDefenderData(self); } public static void clearCachedCombatData(obj_id self) throws InterruptedException { utils.removeScriptVarTree(self, ATTACKER_DATA); utils.removeScriptVarTree(self, DEFENDER_DATA); } public static void cacheAttackerData(obj_id attacker) throws InterruptedException { utils.setScriptVar(attacker, ATTACKER_BASE_CRIT, getAttackerCritMod(attacker)); utils.setScriptVar(attacker, ATTACKER_PVP_CRIT, (float)getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_niche_pvp")); utils.setScriptVar(attacker, ATTACKER_DROID_CRIT, (float)getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_niche_droid")); utils.setScriptVar(attacker, ATTACKER_NPC_CRIT, (float)getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_niche_npc")); utils.setScriptVar(attacker, ATTACKER_CREATURE_CRIT, (float)getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_critical_niche_creature")); utils.setScriptVar(attacker, ATTACKER_HIT_BONUS, getToHitBonus(attacker)); utils.setScriptVar(attacker, ATTACKER_STRIKETHROUGH, getAttackerStrikethroughChance(attacker)); utils.setScriptVar(attacker, ATTACKER_REDUCE_PARRY, getAttackerParryReduction(attacker)); utils.setScriptVar(attacker, ATTACKER_REDUCE_BLOCK, getAttackerBlockReduction(attacker)); utils.setScriptVar(attacker, ATTACKER_REDUCE_DODGE, getAttackerDodgeReduction(attacker)); utils.setScriptVar(attacker, ATTACKER_REDUCE_GLANCING, getAttackerGlancingReduction(attacker)); utils.setScriptVar(attacker, ATTACKER_REDUCE_EVADE, getAttackerEvasionReduction(attacker)); } public static void cacheDefenderData(obj_id defender) throws InterruptedException { utils.setScriptVar(defender, DEFENDER_REDUCE_CRIT, getDefenderCriticalChance(defender)); utils.setScriptVar(defender, DEFENDER_INCREASE_MISS, getDefenderHitReduction(defender)); utils.setScriptVar(defender, DEFENDER_REDUCE_STRIKETHROUGH, getDefenderStrikethroughReduction(defender)); utils.setScriptVar(defender, DEFENDER_REDUCE_PUNISH, getDefenderPunishingBlowReduction(defender)); utils.setScriptVar(defender, DEFENDER_GLANCING, getDefenderGlancingBlowChance(defender)); utils.setScriptVar(defender, DEFENDER_PARRY, getDefenderParryChance(defender)); utils.setScriptVar(defender, DEFENDER_DODGE, getDefenderDodgeChance(defender)); utils.setScriptVar(defender, DEFENDER_EVADE, getDefenderEvasionChance(defender)); utils.setScriptVar(defender, DEFENDER_BLOCK, getDefenderBlockChance(defender)); } public static obj_id getRandomHateTarget(obj_id self) throws InterruptedException { obj_id[] potentialTargets = getHateList(self); if (potentialTargets == null || potentialTargets.length == 0) { return null; } float max_range = 64.0f; Vector validTargets = new Vector(); validTargets.setSize(0); for (obj_id potentialTarget : potentialTargets) { if (isIdValid(potentialTarget) && exists(potentialTarget) && canSee(self, potentialTarget) && !stealth.hasInvisibleBuff(potentialTarget) && getMaster(potentialTarget) == null && !isDead(potentialTarget)) { if (getDistance(self, potentialTarget) < max_range) { utils.addElement(validTargets, potentialTarget); } } } if (validTargets == null || validTargets.size() == 0) { return null; } return ((obj_id)validTargets.get(rand(0, validTargets.size() - 1))); } public static boolean doBhTaunt(obj_id player, obj_id target) throws InterruptedException { if (!isIdValid(player) || !isIdValid(target)) { return false; } if (!exists(player) || !exists(target)) { return false; } obj_id[] enemies = getHateList(target); if (enemies == null || enemies.length == 0) { return false; } obj_id topHateTarget = getHateTarget(target); float topHateAmount = getHate(target, topHateTarget); int level = getLevel(player); float increasedHate = topHateAmount; if (level <= BH_TAUNT_LOW_LEVEL) { increasedHate += BH_TAUNT_HATE_INCREASE_LOW; } else if (level <= BH_TAUNT_MID_LEVEL) { increasedHate += BH_TAUNT_HATE_INCREASE_MID; } else { increasedHate += BH_TAUNT_HATE_INCREASE_HIGH; } addHate(target, player, increasedHate); return true; } public static boolean dsFsTaunt(obj_id player, obj_id target) throws InterruptedException { int stanceBuff = buff.getBuffOnTargetFromGroup(player, "fsStance"); if (stanceBuff == 0) { return false; } if (!isIdValid(player) || !isIdValid(target)) { return false; } if (!exists(player) || !exists(target)) { return false; } obj_id[] enemies = getHateList(target); if (enemies == null || enemies.length == 0) { return false; } obj_id topHateTarget = getHateTarget(target); float topHateAmount = getHate(target, topHateTarget); int level = getLevel(player); float increasedHate = topHateAmount; if (level <= FS_TAUNT_LOW_LEVEL) { increasedHate += FS_TAUNT_HATE_INCREASE_LOW; } else if (level <= FS_TAUNT_MID_LEVEL) { increasedHate += FS_TAUNT_HATE_INCREASE_MID; } else { increasedHate += FS_TAUNT_HATE_INCREASE_HIGH; } addHate(target, player, increasedHate); return true; } public static boolean isStunned(obj_id target) throws InterruptedException { return utils.hasScriptVar(target, TEMP_COMBAT_BLOCK); } public static int[] getSuccessBasedSingleTargetActionCost(hit_result hitData, combat_data actionData, obj_id attacker, weapon_data weaponData) throws InterruptedException { boolean isSingleTargetAttack = actionData.hitType == -1 && actionData.attackType == combat.SINGLE_TARGET && actionData.commandType != combat.LEFT_CLICK_DEFAULT; int[] freeShot = { 0, 0, 0 }; if (!isSingleTargetAttack) { return freeShot; } int freeMiss = getEnhancedSkillStatisticModifierUncapped(attacker, "freeshot_case_miss"); int freeDodge = getEnhancedSkillStatisticModifierUncapped(attacker, "freeshot_case_dodge"); int freeParry = getEnhancedSkillStatisticModifierUncapped(attacker, "freeshot_case_parry"); int freeCrit = getEnhancedSkillStatisticModifierUncapped(attacker, "freeshot_case_crit"); int freeStrike = getEnhancedSkillStatisticModifierUncapped(attacker, "freeshot_case_strikethrough"); boolean isFreeshot = false; if (hitData.success == false) { if (freeMiss > 0) { isFreeshot = true; } if (hitData.dodge == true && freeDodge > 0) { isFreeshot = true; } if (hitData.parry == true && freeParry > 0) { isFreeshot = true; } } if (hitData.success == true) { if (hitData.critical == true && freeCrit > 0) { isFreeshot = true; } if (hitData.strikethrough == true && freeStrike > 0) { isFreeshot = true; } } if (isFreeshot) { prose_package pp = new prose_package(); pp = prose.setStringId(pp, new string_id("spam", "freeshot")); showFlyTextPrivateProseWithFlags(attacker, attacker, pp, 1.5f, colors.LEMONCHIFFON, FLY_TEXT_FLAG_IS_FREESHOT); return freeShot; } int[] shotCost = getActionCost(attacker, weaponData, actionData); return shotCost; } public static void setPersistCombatMode(obj_id target, boolean state) throws InterruptedException { if (!isMob(target)) { return; } if (!state) { if (utils.hasScriptVar(target, combat.PERSIST_COMBAT)) { utils.removeScriptVar(target, combat.PERSIST_COMBAT); } return; } utils.setScriptVar(target, combat.PERSIST_COMBAT, 1); return; } public static boolean isPersistCombatMode(obj_id self) throws InterruptedException { return utils.hasScriptVar(self, combat.PERSIST_COMBAT); } public static boolean isValidPersistCombat(obj_id self) throws InterruptedException { if (isInvulnerable(self)) { return false; } if (!getCreatureCoverVisibility(self)) { return false; } if (isDead(self)) { return false; } obj_id[] hateList = getHateList(self); if (hateList == null || hateList.length == 0) { return false; } boolean hasLos = true; for (obj_id obj_id : hateList) { hasLos |= canSee(self, obj_id); } if (!hasLos) { return false; } return true; } public static boolean isDamageImmune(obj_id attacker, obj_id defender, combat_data actionData) throws InterruptedException { boolean attackerImmune = getEnhancedSkillStatisticModifierUncapped(attacker, "damage_immune") > 0; boolean defenderImmune = getEnhancedSkillStatisticModifierUncapped(defender, "damage_immune") > 0; prose_package pp = new prose_package(); boolean isBeneficial = (actionData.hitType == NON_ATTACK || actionData.hitType == NON_DAMAGE_ATTACK) && pvpCanHelp(attacker, defender); if (attackerImmune && !isBeneficial) { pp.stringId = new string_id("cbt_spam", "immune_hit_other"); pp.target.set(defender); pp.other.set(new string_id("cmd_n", actionData.actionName)); sendCombatSpamMessageProse(attacker, pp); pp.target.set(attacker); pp.stringId = new string_id("cbt_spam", "immune_target_hit_other"); sendCombatSpamMessageProse(defender, pp); } if (defenderImmune && !isBeneficial) { pp.stringId = new string_id("cbt_spam", "immune_get_hit"); pp.target.set(attacker); pp.other.set(new string_id("cmd_n", actionData.actionName)); sendCombatSpamMessageProse(defender, pp); pp.target.set(defender); pp.stringId = new string_id("cbt_spam", "immune_target_hit"); sendCombatSpamMessageProse(attacker, pp); } return (attackerImmune || defenderImmune) && !isBeneficial; } public static float getDiminishedReturnValue(float value, int modType) throws InterruptedException { if (value < 0.0f) { return value; } float[] seedList = { 500.0f, 500.0f, 500.0f, 500.0f, 500.0f, 500.0f, 500.0f, 50.0f }; switch (modType) { case DR_ATTACKER_CRITICAL: return (value * (1.0f - (value / (value + seedList[DR_ATTACKER_CRITICAL])))); case DR_ATTACKER_STRIKETHROUGH: return (value * (1.0f - (value / (value + seedList[DR_ATTACKER_STRIKETHROUGH])))); case DR_DEFENDER_BLOCK: return (value * (1.0f - (value / (value + seedList[DR_DEFENDER_BLOCK])))); case DR_DEFENDER_DODGE: return (value * (1.0f - (value / (value + seedList[DR_DEFENDER_DODGE])))); case DR_DEFENDER_PARRY: return (value * (1.0f - (value / (value + seedList[DR_DEFENDER_PARRY])))); case DR_DEFENDER_GLANCE: return (value * (1.0f - (value / (value + seedList[DR_DEFENDER_GLANCE])))); case DR_DEFENDER_EVADE: return (value * (1.0f - (value / (value + seedList[DR_DEFENDER_EVADE])))); case DR_ATTACKER_CRITICAL_2: return (value * (1.0f - (value / (value + seedList[DR_ATTACKER_CRITICAL_2])))); } return value; } public static void mirrorArmor(obj_id commando) throws InterruptedException { obj_id[] haters = getHateList(commando); if (haters != null || haters.length > 0) { for (obj_id hater : haters) { float myHate = getHate(hater, commando); if (myHate <= 0.0f) { continue; } myHate /= 2.0f; setHate(hater, commando, myHate); } } } public static boolean setKillMeter(obj_id player, int value) throws InterruptedException { int current = getKillMeter(player); int delta = value - current; incrementKillMeter(player, delta); return true; } public static boolean modifyKillMeter(obj_id player, int value) throws InterruptedException { int current = getKillMeter(player); int delta = current + value; if (delta < 0) { delta = 0; } if (delta > 50) { delta = 50; } setKillMeter(player, delta); return true; } public static boolean canDrainKillMeter(obj_id player, int value) throws InterruptedException { if (!isPlayer(player)) { return true; } int current = getKillMeter(player); int delta = current - value; return delta < 0 ? false : true; } public static boolean drainKillMeter(obj_id player, int value) throws InterruptedException { if (!isPlayer(player)) { return true; } if (!canDrainKillMeter(player, value)) { return false; } return modifyKillMeter(player, -1 * value); } public static location getCommandGroundTargetLocation(String params) throws InterruptedException { String[] stringLocationParams = split(params, ' '); if (stringLocationParams == null || stringLocationParams.length <= 0) { return null; } location eggLoc = new location(); boolean inCell = false; if (isIdValid(utils.stringToObjId(stringLocationParams[3]))) { inCell = true; } if (!inCell) { eggLoc.x = utils.stringToFloat(stringLocationParams[0]); eggLoc.y = utils.stringToFloat(stringLocationParams[1]); eggLoc.z = utils.stringToFloat(stringLocationParams[2]); } else { eggLoc.cell = utils.stringToObjId(stringLocationParams[3]); eggLoc.x = utils.stringToFloat(stringLocationParams[4]); eggLoc.y = utils.stringToFloat(stringLocationParams[5]); eggLoc.z = utils.stringToFloat(stringLocationParams[6]); } return eggLoc; } public static float getDevastationChance(obj_id attacker) throws InterruptedException { float devastationBonus = 2.0f; devastationBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "commando_devastation"); devastationBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_devastation_bonus") / 10.0f; return devastationBonus; } public static float getElementalPenetration(obj_id attacker, String damageString) throws InterruptedException { float elementalPenetrationAmount = 0.0f; if (isIdValid(attacker) && damageString != null && damageString.length() > 0) { String elementalPenetrationSkillModName = "exotic_" + damageString + "_penetration"; float elementalPenetrationSkillMod = getEnhancedSkillStatisticModifierUncapped(attacker, elementalPenetrationSkillModName); if (elementalPenetrationSkillMod > MAX_RE_EXOTIC_ELEMENTAL_PENETRATION_SKILLMOD) { elementalPenetrationSkillMod = MAX_RE_EXOTIC_ELEMENTAL_PENETRATION_SKILLMOD; } elementalPenetrationAmount = 100.0f - (elementalPenetrationSkillMod * 2.5f); elementalPenetrationAmount *= 0.01f; } return elementalPenetrationAmount; } public static boolean hasPrescience(obj_id attacker, obj_id defender, combat_data actionData) throws InterruptedException { if (!buff.hasBuff(defender, "bh_prescience")) { return false; } obj_id owner = buff.getBuffCaster(defender, "bh_prescience"); if (attacker == owner) { prose_package pp = new prose_package(); pp.stringId = new string_id("cbt_spam", "bh_prescience_attacker"); pp.target.set(defender); pp.other.set(new string_id("cmd_n", actionData.actionName)); sendCombatSpamMessageProse(attacker, pp); pp.target.set(attacker); pp.stringId = new string_id("cbt_spam", "bh_prescience_defender"); sendCombatSpamMessageProse(defender, pp); return true; } return false; } }