mirror of
https://github.com/SWG-Source/dsrc.git
synced 2026-07-13 22:01:04 -04:00
4137 lines
179 KiB
Java
Executable File
4137 lines
179 KiB
Java
Executable File
package script.systems.combat;
|
|
|
|
import script.*;
|
|
import script.combat_engine.*;
|
|
import script.library.*;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Vector;
|
|
|
|
public class combat_base extends script.base_script
|
|
{
|
|
public combat_base()
|
|
{
|
|
}
|
|
public static final float MAX_THROWING_DISTANCE = 30;
|
|
public static final float PVP_DAMAGE_REDUCTION_VALUE = .75f;
|
|
public static final float UNIVERSAL_ACTION_COST_MULTIPLIER = 1.0f;
|
|
public static final String JEDI_COMBAT_DATATABLE = "datatables/jedi/jedi_combat_data.iff";
|
|
public static final int MAX_TARGET_ARRAY_SIZE = 10;
|
|
public static final int ALLOWED_LOS_FAILURES = 10;
|
|
public static final int TARGET_COMMAND = -2;
|
|
public static final int PRECHECK_SUCCESS = -1;
|
|
public static final int PRECHECK_OVERRIDE = 0;
|
|
public static final int PRECHECK_CONTINUE = 1;
|
|
public static final int HIT_RESULT_MISS = 0;
|
|
public static final int HIT_RESULT_DODGE = 1;
|
|
public static final int HIT_RESULT_PARRY = 2;
|
|
public static final int HIT_RESULT_GLANCING = 3;
|
|
public static final int HIT_RESULT_BLOCK = 4;
|
|
public static final int HIT_RESULT_CRITICAL = 5;
|
|
public static final int HIT_RESULT_PUNISHING = 6;
|
|
public static final int HIT_RESULT_HIT = 7;
|
|
public static final int HIT_RESULT_EVADE = 8;
|
|
public static final int HIT_RESULT_STRIKETHROUGH = 9;
|
|
public static final String PROGRESSIVE_DAMAGE_COUNTER = "hitData.progressive.counter";
|
|
public static final string_id SID_NONE = new string_id();
|
|
public boolean combatStandardAction(String actionName, obj_id self, obj_id target, String params, String successHandler, String failHandler) throws InterruptedException
|
|
{
|
|
return combatStandardAction(actionName, self, target, getCurrentWeapon(self), params, null, false, false, 0);
|
|
}
|
|
public boolean combatStandardAction(String actionName, obj_id self, obj_id target, String params, String successHandler, String failHandler, int overloadDamage) throws InterruptedException
|
|
{
|
|
return combatStandardAction(actionName, self, target, getCurrentWeapon(self), params, null, false, false, overloadDamage);
|
|
}
|
|
public boolean combatStandardAction(String actionName, obj_id self, obj_id target, String params, String successHandler, String failHandler, boolean testPetBar) throws InterruptedException
|
|
{
|
|
return combatStandardAction(actionName, self, target, getCurrentWeapon(self), params, null, false, testPetBar, 0);
|
|
}
|
|
public boolean combatStandardAction(String actionName, obj_id self, obj_id target, String params) throws InterruptedException
|
|
{
|
|
return combatStandardAction(actionName, self, target, getCurrentWeapon(self), params, null, false, false, 0);
|
|
}
|
|
public void sendPerformSpam(obj_id actor, obj_id target, string_id message, String actionName, int spam_type) throws InterruptedException
|
|
{
|
|
string_id actionStringId = new string_id("cmd_n", actionName);
|
|
prose_package pp = new prose_package();
|
|
pp = prose.setStringId(pp, message);
|
|
pp = prose.setTU(pp, actor);
|
|
pp = prose.setTT(pp, target);
|
|
pp = prose.setTO(pp, actionStringId);
|
|
sendCombatSpamMessageProse(actor, target, pp, true, true, true, spam_type);
|
|
}
|
|
public boolean combatStandardAction(String actionName, obj_id self, obj_id target, obj_id objWeapon, String params, combat_data actionData, boolean isTangibleAttacking) throws InterruptedException
|
|
{
|
|
return combatStandardAction(actionName, self, target, objWeapon, params, actionData, isTangibleAttacking, false, 0);
|
|
}
|
|
public boolean combatStandardAction(String actionName, obj_id self, obj_id target, obj_id objWeapon, String params, combat_data actionData, boolean isTangibleAttacking, boolean testPetBar, int overloadDamage) throws InterruptedException
|
|
{
|
|
if (combat.isStunned(self))
|
|
{
|
|
return false;
|
|
}
|
|
boolean triggerPetBar = false;
|
|
obj_id player = self;
|
|
if (testPetBar && isPlayer(self))
|
|
{
|
|
obj_id beast = beast_lib.getBeastOnPlayer(self);
|
|
if (!isIdValid(beast) || !exists(beast) || !beast_lib.canPerformCommand(self, beast, actionName))
|
|
{
|
|
return false;
|
|
}
|
|
target = isIdValid(getTarget(beast)) ? getTarget(beast) : getIntendedTarget(self);
|
|
self = beast;
|
|
objWeapon = getCurrentWeapon(self);
|
|
triggerPetBar = true;
|
|
}
|
|
if (weapons.checkForIllegalStorytellerWeapon(self, objWeapon))
|
|
{
|
|
weapons.handleIllegalStorytellerWeapon(self, objWeapon, "combatStandardAction");
|
|
utils.setScriptVar(objWeapon, "illegalStorytellerWeapon", true);
|
|
}
|
|
if (!storyteller.storytellerCombatCheck(self, target))
|
|
{
|
|
target = obj_id.NULL_ID;
|
|
}
|
|
boolean verbose = false;
|
|
boolean isEggWeapon = utils.hasScriptVar(objWeapon, "objOwner");
|
|
if (actionData == null)
|
|
{
|
|
actionData = combat_engine.getCombatData(actionName);
|
|
}
|
|
actionData = attackOverrideByBuff(self, actionData);
|
|
if (actionData == null)
|
|
{
|
|
combat.combatLog(self, target, "combatStandardAction", "Attack Aborted - Not a valid combat action - " + actionName);
|
|
return false;
|
|
}
|
|
if (isEggWeapon && actionName.equals("forceThrow"))
|
|
{
|
|
self = utils.getObjIdScriptVar(objWeapon, "objOwner");
|
|
objWeapon = getCurrentWeapon(self);
|
|
}
|
|
combat.combatLog(self, target, "combatStandardAction", "who=" + getName(self) + ", actionName=" + actionName + ", target = " + target + ", params.length=" + params.length() + ", params=" + params + ", weapon=" + getName(objWeapon));
|
|
boolean isGroundTarget = getWeaponType(objWeapon) == WEAPON_TYPE_GROUND_TARGETTING;
|
|
boolean isDirectionTarget = getWeaponType(objWeapon) == WEAPON_TYPE_DIRECTIONAL;
|
|
boolean isLocationTargetOk = actionData.delayAttackEggPosition == 3;
|
|
float xTarget = -1;
|
|
float yTarget = -1;
|
|
float zTarget = -1;
|
|
obj_id cellTarget = null;
|
|
location targetLoc = null;
|
|
boolean isAutoAiming = false;
|
|
if (params != null && !params.equals(""))
|
|
{
|
|
actionData.params = params;
|
|
java.util.StringTokenizer st = new java.util.StringTokenizer(params);
|
|
int paramCount = st.countTokens();
|
|
long cellId = 0;
|
|
if (paramCount >= 7)
|
|
{
|
|
xTarget = Float.parseFloat(st.nextToken());
|
|
yTarget = Float.parseFloat(st.nextToken());
|
|
zTarget = Float.parseFloat(st.nextToken());
|
|
cellId = Long.parseLong(st.nextToken());
|
|
if (cellId != 0)
|
|
{
|
|
cellTarget = obj_id.getObjId(cellId);
|
|
xTarget = Float.parseFloat(st.nextToken());
|
|
yTarget = Float.parseFloat(st.nextToken());
|
|
zTarget = Float.parseFloat(st.nextToken());
|
|
}
|
|
targetLoc = new location(xTarget, yTarget, zTarget, getCurrentSceneName(), cellTarget);
|
|
}
|
|
String lastArg = "";
|
|
while (st.hasMoreTokens())
|
|
{
|
|
lastArg = st.nextToken();
|
|
}
|
|
if (lastArg.equals("a"))
|
|
{
|
|
isAutoAiming = true;
|
|
}
|
|
}
|
|
if (params.equals("") && isLocationTargetOk)
|
|
{
|
|
targetLoc = getLocation(objWeapon);
|
|
}
|
|
if (utils.hasScriptVar(objWeapon, "isAutoAimed"))
|
|
{
|
|
isAutoAiming = utils.getBooleanScriptVar(objWeapon, "isAutoAimed");
|
|
}
|
|
boolean isSpecialAttack = actionData.commandType == combat.RIGHT_CLICK_SPECIAL;
|
|
int hitType = actionData.hitType;
|
|
boolean isDelayedAttack = false;
|
|
if (!isTangibleAttacking)
|
|
{
|
|
isDelayedAttack = (hitType == combat.DELAY_ATTACK);
|
|
}
|
|
weapon_data weaponData = getOverloadedWeaponData(self, objWeapon, actionData);
|
|
if (weaponData == null)
|
|
{
|
|
String logString = "getOverloadedWeaponData failed to return data for self: " + self + " " + " actionData: " + actionData;
|
|
if (isIdValid(self))
|
|
{
|
|
logString += (isPlayer(self) ? " a player object" : " a non-player object");
|
|
}
|
|
if (isIdValid(objWeapon))
|
|
{
|
|
logString += " objWeapon: " + objWeapon + " getName(objWeapon): " + getName(objWeapon);
|
|
if (!utils.hasScriptVar(objWeapon, "dctWeaponStats"))
|
|
{
|
|
logString += " dctWeaponStats: null";
|
|
}
|
|
else
|
|
{
|
|
logString += " dctWeaponStats: exists";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logString += " objWeapon: null dctWeaponStats: null";
|
|
}
|
|
CustomerServiceLog("combat_errors: ", logString);
|
|
return false;
|
|
}
|
|
if (!isTangibleAttacking)
|
|
{
|
|
if (!combat.canUseWeaponWithAbility(self, weaponData, actionData, verbose))
|
|
{
|
|
combat.combatLog(self, null, "combatStandardAction", "Aborting Attack - Not able to use this action (" + actionName + ") with the current weapon");
|
|
return false;
|
|
}
|
|
}
|
|
obj_id combatTarget = target;
|
|
if (isTangibleAttacking)
|
|
{
|
|
actionData.validTarget = actionData.validEggTarget;
|
|
}
|
|
if (isSpecialAttack && !isTangibleAttacking)
|
|
{
|
|
combatTarget = getCorrectCombatTarget(self, target, actionData, verbose);
|
|
if (!isIdValid(combatTarget))
|
|
{
|
|
combat.combatLog(self, target, "combatStandardAction", "Attack Failed - No valid combat target");
|
|
return false;
|
|
}
|
|
}
|
|
else if (!isTangibleAttacking && !isGroundTarget && !isDirectionTarget)
|
|
{
|
|
combatTarget = getCorrectCombatTarget(self, target, actionData, verbose);
|
|
if (!isIdValid(combatTarget))
|
|
{
|
|
combat.combatLog(self, target, "combatStandardAction", "Attack Failed - No valid combat target");
|
|
return false;
|
|
}
|
|
}
|
|
attacker_data attackerData = new attacker_data();
|
|
defender_data[] defenderData = null;
|
|
if (isGroundTarget || isDirectionTarget)
|
|
{
|
|
actionData.targetLoc = targetLoc;
|
|
}
|
|
if (isLocationTargetOk && targetLoc != null)
|
|
{
|
|
actionData.targetLoc = targetLoc;
|
|
}
|
|
if (!isTangibleAttacking)
|
|
{
|
|
actionData = modifyActionDataByExpertise(self, actionData);
|
|
}
|
|
stealth.clearPreviousInvis(self);
|
|
stealth.testInvisCombatAction(self, target, actionData);
|
|
if (!isDelayedAttack)
|
|
{
|
|
obj_id[] defenders = getCombatDefenders(self, combatTarget, weaponData, actionData, isSpecialAttack);
|
|
if (defenders == null || defenders.length == 0)
|
|
{
|
|
combat.combatLog(self, target, "combatStandardAction", "Attack Failed - No valid targets");
|
|
if (weaponData.weaponType == WEAPON_TYPE_DIRECTIONAL && !isEggWeapon)
|
|
{
|
|
attacker_results atkRslt = new attacker_results();
|
|
defender_results[] dfndRslt = new defender_results[0];
|
|
atkRslt.id = self;
|
|
atkRslt.weapon = weaponData.id;
|
|
atkRslt.actionName = getStringCrc(toLower(actionName));
|
|
atkRslt.useLocation = true;
|
|
if(targetLoc != null && isValidLocation(targetLoc)) {
|
|
atkRslt.targetLocation = new vector(targetLoc.x, targetLoc.y, targetLoc.z);
|
|
atkRslt.targetCell = targetLoc.cell;
|
|
}
|
|
else {
|
|
location tl = getLocation(target);
|
|
if(!isValidLocation(tl)){
|
|
LOG("combat","Could not identify the target's (" + target + ":" + getPlayerFullName(target) + ") location (" + targetLoc + ") to attack it.");
|
|
}
|
|
atkRslt.targetLocation = new vector(tl.x, tl.y, tl.z);
|
|
atkRslt.targetCell = tl.cell;
|
|
}
|
|
atkRslt.endPosture = (combat.isMeleeWeapon(weaponData.id) || combat.isLightsaberWeapon(weaponData.id)) ? POSTURE_UPRIGHT : getPosture(self);
|
|
String anim = actionData.animDefault;
|
|
stealth.testInvisCombatAction(self, target, actionData);
|
|
doCombatResults(anim, atkRslt, dfndRslt);
|
|
return true;
|
|
}
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
weapon_data junkData = new weapon_data();
|
|
defenderData = new defender_data[defenders.length];
|
|
if (!getCombatData(self, defenders, attackerData, defenderData, junkData))
|
|
{
|
|
String msg = "ERROR::combatStandardAction:" + actionData.actionName + ") call to getCombatData returned false";
|
|
CustomerServiceLog("combat_errors", msg);
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
attackerData.id = self;
|
|
defender_data dat = new defender_data();
|
|
dat.id = target;
|
|
defenderData = new defender_data[]
|
|
{
|
|
dat
|
|
};
|
|
}
|
|
int[] actionCost = null;
|
|
if (!isTangibleAttacking)
|
|
{
|
|
actionCost = combat.getActionCost(self, weaponData, actionData);
|
|
if (!combat.canDrainCombatActionAttributes(self, actionCost))
|
|
{
|
|
showFlyTextPrivate(self, self, new string_id("combat_effects", "action_too_tired"), 1.5f, colors.GOLDENROD);
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "pool_drain_fail"));
|
|
}
|
|
combat.combatLog(self, self, "combatStandardAction", "Aborting Attack - Insufficient ability points for action");
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
int killMeterCost = (int)actionData.vigorCost;
|
|
if (killMeterCost > 0)
|
|
{
|
|
if (!beast_lib.isBeast(attackerData.id) && isPlayer(attackerData.id))
|
|
{
|
|
if (!combat.canDrainKillMeter(attackerData.id, killMeterCost))
|
|
{
|
|
showFlyTextPrivate(self, self, new string_id("combat_effects", "need_more_kills"), 1.5f, colors.FIREBRICK);
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
combat.drainKillMeter(attackerData.id, killMeterCost);
|
|
}
|
|
}
|
|
}
|
|
boolean isHeal = hitType == combat.HEAL;
|
|
boolean isRevive = hitType == combat.REVIVE;
|
|
if (isHeal || isRevive)
|
|
{
|
|
combat.combatLog(self, combatTarget, "combatStandardAction", "Heal Action - Branching off to healing functionality");
|
|
if (!doHealingPreCheck(actionData, attackerData, defenderData, verbose))
|
|
{
|
|
combat.combatLog(self, combatTarget, "combatStandardAction", "Attack Failed - Healing precheck failed");
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
if (!combat.canDrainCombatActionAttributes(self, actionCost))
|
|
{
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
if (actionData.performanceSpamStrId.isValid() && !isTangibleAttacking)
|
|
{
|
|
sendPerformSpam(self, combatTarget, actionData.performanceSpamStrId, actionName, COMBAT_RESULT_GENERIC);
|
|
}
|
|
boolean success = false;
|
|
if (isHeal)
|
|
{
|
|
success = healing.performHealDamage(attackerData, defenderData, actionData);
|
|
}
|
|
if (isRevive)
|
|
{
|
|
success = healing.performRevivePlayer(attackerData, defenderData, actionData, actionData.addedDamage > 0);
|
|
}
|
|
if (success)
|
|
{
|
|
stealth.testInvisNonCombatAction(self, target, actionData);
|
|
}
|
|
if (success)
|
|
{
|
|
LOG("combat_base", "combatStandardAction 7 success " + actionName);
|
|
if (!combat.drainCombatActionAttributes(self, actionCost))
|
|
{
|
|
combat.combatLog(self, combatTarget, "combatStandardAction", "ERROR:Insufficient action not caught in canDrainCombatActionAttributes()");
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
String anim = actionData.animDefault;
|
|
attacker_results cbtAttackerResults = new attacker_results();
|
|
obj_id defender = self;
|
|
if (isIdValid(target) && target != self && !isDead(target))
|
|
{
|
|
defender = target;
|
|
}
|
|
defender_results[] cbtDefenderResults = new defender_results[1];
|
|
cbtDefenderResults[0] = new defender_results();
|
|
cbtDefenderResults[0].id = defender;
|
|
cbtDefenderResults[0].endPosture = getPosture(defender);
|
|
cbtDefenderResults[0].result = COMBAT_RESULT_HIT;
|
|
cbtAttackerResults.id = self;
|
|
cbtAttackerResults.weapon = null;
|
|
cbtAttackerResults.actionName = getStringCrc(toLower(actionName));
|
|
cbtAttackerResults.endPosture = getPosture(self);
|
|
doCombatResults(anim, cbtAttackerResults, cbtDefenderResults);
|
|
}
|
|
return success;
|
|
}
|
|
else if (!isTangibleAttacking)
|
|
{
|
|
if (!doCombatPreCheck(actionData, attackerData, weaponData, defenderData, verbose))
|
|
{
|
|
combat.combatLog(self, combatTarget, "combatStandardAction", "Attack Failed - Combat precheck failed");
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
boolean isSingleTargetAttack = actionData.hitType == -1 && actionData.attackType == combat.SINGLE_TARGET;
|
|
if (!isSingleTargetAttack && !combat.drainCombatActionAttributes(self, actionCost))
|
|
{
|
|
showFlyTextPrivate(self, self, new string_id("combat_effects", "action_too_tired"), 1.5f, colors.GOLDENROD);
|
|
combat.combatLog(self, combatTarget, "combatStandardAction", "ERROR:Insufficient action not caught in canDrainCombatActionAttributes()");
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
if (actionData.performanceSpamStrId.isValid())
|
|
{
|
|
sendPerformSpam(self, combatTarget, actionData.performanceSpamStrId, actionName, COMBAT_RESULT_GENERIC);
|
|
}
|
|
}
|
|
attacker_results attackerResults = new attacker_results();
|
|
defender_results[] defenderResults = new defender_results[defenderData.length];
|
|
if (!isDelayedAttack)
|
|
{
|
|
hit_result[] hitData = null;
|
|
hitData = runHitEngine(attackerData, weaponData, defenderData, attackerResults, defenderResults, actionData, isTangibleAttacking, isAutoAiming, overloadDamage);
|
|
if (hitData == null)
|
|
{
|
|
String msg = " Attack Failed - No hit data returned";
|
|
CustomerServiceLog("combat_errors", "ERROR::combatStandardAction:" + actionData.actionName + " " + msg);
|
|
combat.combatLog(self, combatTarget, "combatStandardAction", msg);
|
|
stealth.reinstateInvisFromCombat(self);
|
|
return false;
|
|
}
|
|
}
|
|
else if (isGroundTarget && !isSpecialAttack)
|
|
{
|
|
attacker_results atkRslt = new attacker_results();
|
|
defender_results[] dfndRslt = new defender_results[0];
|
|
atkRslt.id = self;
|
|
atkRslt.weapon = weaponData.id;
|
|
atkRslt.actionName = getStringCrc(toLower(actionName));
|
|
atkRslt.useLocation = true;
|
|
atkRslt.targetLocation = new vector(targetLoc.x, targetLoc.y, targetLoc.z);
|
|
atkRslt.targetCell = targetLoc.cell;
|
|
atkRslt.endPosture = (combat.isMeleeWeapon(weaponData.id) || combat.isLightsaberWeapon(weaponData.id)) ? POSTURE_UPRIGHT : getPosture(self);
|
|
String anim = actionData.animDefault;
|
|
doCombatResults(anim, atkRslt, dfndRslt);
|
|
}
|
|
if ((isDelayedAttack) && (!isTangibleAttacking))
|
|
{
|
|
if (utils.hasScriptVar(self, combat.DIED_RECENTLY))
|
|
{
|
|
utils.removeScriptVar(self, combat.DIED_RECENTLY);
|
|
}
|
|
LOG("combat_base", "startDelayedAttack");
|
|
startDelayedAttack(self, combatTarget, objWeapon, actionName, actionData, isGroundTarget, isAutoAiming);
|
|
}
|
|
if (beast_lib.isChargeAttack(actionName))
|
|
{
|
|
setLocation(self, getLocation(target));
|
|
}
|
|
if (!isPlayer(self) && !beast_lib.isBeast(self))
|
|
{
|
|
setCommandTimerValue(self, TIMER_COOLDOWN, 0f);
|
|
}
|
|
if (triggerPetBar)
|
|
{
|
|
combat_data cd = combat_engine.getCombatData(actionName);
|
|
String cooldownGroup = cd.cooldownGroup;
|
|
int groupCrc = getStringCrc(cooldownGroup);
|
|
sendCooldownGroupTimingOnly(player, groupCrc, cd.cooldownTime);
|
|
}
|
|
return true;
|
|
}
|
|
public void startDelayedAttack(obj_id attacker, obj_id target, obj_id objWeapon, String attackName, combat_data actionData, boolean instantHit, boolean isAutoAimed) throws InterruptedException
|
|
{
|
|
int eggPosition = actionData.delayAttackEggPosition;
|
|
location where = getLocation(attacker);
|
|
if (eggPosition == 2)
|
|
{
|
|
where = getLocation(target);
|
|
}
|
|
else if (eggPosition == 3)
|
|
{
|
|
where = (location)actionData.targetLoc.clone();
|
|
}
|
|
obj_id egg = combat.makeTrackerEgg(attacker, where, actionData);
|
|
if (!isIdValid(egg) || !exists(egg))
|
|
{
|
|
return;
|
|
}
|
|
dictionary dctWeaponStats = utils.getDictionaryScriptVar(objWeapon, "dctWeaponStats");
|
|
if(dctWeaponStats != null)
|
|
utils.setScriptVar(egg, "dctWeaponStats", dctWeaponStats);
|
|
utils.setScriptVar(egg, "isAutoAimed", isAutoAimed);
|
|
doStandardDelayedAction(egg, target, attackName, actionData, instantHit);
|
|
}
|
|
public void doStandardDelayedAction(obj_id objEgg, obj_id target, String attackName, combat_data actionData, boolean instantHit) throws InterruptedException
|
|
{
|
|
float delay = actionData.delayAttackInterval;
|
|
String strEffect = actionData.delayAttackParticle;
|
|
int intLoops = actionData.delayAttackLoops;
|
|
boolean boolDestroy = true;
|
|
if (intLoops > 1)
|
|
{
|
|
if (!utils.hasScriptVar(objEgg, "intLoopsLeft"))
|
|
{
|
|
utils.setScriptVar(objEgg, "intLoopsLeft", intLoops);
|
|
boolDestroy = false;
|
|
}
|
|
else
|
|
{
|
|
intLoops = utils.getIntScriptVar(objEgg, "intLoopsLeft");
|
|
if (intLoops > 1)
|
|
{
|
|
boolDestroy = false;
|
|
}
|
|
}
|
|
}
|
|
if (instantHit)
|
|
{
|
|
float initDelay = actionData.initialDelayAttackInterval;
|
|
if (initDelay >= 0)
|
|
{
|
|
delay = initDelay;
|
|
}
|
|
}
|
|
obj_id objOwner = utils.getObjIdScriptVar(objEgg, "objOwner");
|
|
if (isIdValid(objOwner))
|
|
{
|
|
delay += (int)getSkillStatisticModifier(objOwner, "expertise_delay_line_" + actionData.specialLine);
|
|
delay -= (int)getSkillStatisticModifier(objOwner, "expertise_delay_reduce_line_" + actionData.specialLine);
|
|
}
|
|
if (delay < 0)
|
|
{
|
|
delay = 0;
|
|
}
|
|
doDelayedParticle(objEgg, strEffect, delay, false);
|
|
doDelayedAttack(objEgg, attackName, target, delay, "", boolDestroy);
|
|
return;
|
|
}
|
|
public void doDelayedParticle(obj_id objEgg, String strEffect, float fltDelay, boolean boolCleanup) throws InterruptedException
|
|
{
|
|
dictionary dctParams = new dictionary();
|
|
dctParams.put("strEffect", strEffect);
|
|
dctParams.put("boolCleanup", boolCleanup);
|
|
utils.setScriptVar(objEgg, "handleDelayedParticle", dctParams);
|
|
messageTo(objEgg, "handleDelayedParticle", dctParams, fltDelay, false);
|
|
return;
|
|
}
|
|
public void doDelayedAttack(obj_id objEgg, String strAttack, obj_id objTarget, float fltDelay, String strEffect, boolean boolCleanup) throws InterruptedException
|
|
{
|
|
dictionary dctParams = new dictionary();
|
|
dctParams.put("strCommand", strAttack);
|
|
dctParams.put("objTarget", objTarget);
|
|
dctParams.put("boolCleanup", boolCleanup);
|
|
dctParams.put("strEffect", strEffect);
|
|
utils.setScriptVar(objEgg, "handleDelayedAttack", dctParams);
|
|
messageTo(objEgg, "handleDelayedAttack", dctParams, fltDelay, false);
|
|
}
|
|
public obj_id getCorrectCombatTarget(obj_id self, obj_id curTarget, boolean verbose) throws InterruptedException
|
|
{
|
|
return getCorrectCombatTarget(self, curTarget, "", new combat_data(), verbose);
|
|
}
|
|
public obj_id getCorrectCombatTarget(obj_id self, obj_id curTarget, combat_data actionData, boolean verbose) throws InterruptedException
|
|
{
|
|
return getCorrectCombatTarget(self, curTarget, "", actionData, verbose);
|
|
}
|
|
public obj_id getCorrectCombatTarget(obj_id self, obj_id curTarget, String params, combat_data actionData, boolean verbose) throws InterruptedException
|
|
{
|
|
obj_id newTarget = curTarget;
|
|
int validTargetType = actionData.validTarget;
|
|
boolean isOffensive = (validTargetType != combat.VALID_TARGET_FRIEND && validTargetType != combat.VALID_TARGET_DEAD);
|
|
if (actionData.actionName.equals("me_stasis_1") || actionData.actionName.startsWith("bm_mend_pet") || actionData.actionName.equals("bm_soothing_comfort_1") || actionData.actionName.startsWith("bm_revive"))
|
|
{
|
|
return newTarget;
|
|
}
|
|
if (validTargetType == -1)
|
|
{
|
|
return self;
|
|
}
|
|
if (newTarget == self)
|
|
{
|
|
int intHitType = actionData.hitType;
|
|
if (isOffensive)
|
|
{
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "shoot_self"));
|
|
}
|
|
combat.combatLog(self, curTarget, "getCorrectCombatTarget", "Cannot attack self - returning null target");
|
|
return null;
|
|
}
|
|
}
|
|
if (isOffensive && isDead(newTarget))
|
|
{
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "invalid_target"));
|
|
}
|
|
combat.combatLog(self, newTarget, "getCorrectCombatTarget", "Invalid Target - Target is Dead");
|
|
return null;
|
|
}
|
|
if (isOffensive && isIncapacitated(newTarget))
|
|
{
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "invalid_target"));
|
|
}
|
|
combat.combatLog(self, newTarget, "getCorrectCombatTarget", "Invalid Target - Target is Incapped");
|
|
return null;
|
|
}
|
|
if (!isOffensive && (newTarget != self) && (!isIdValid(newTarget) || pvpCanAttack(self, newTarget) || !pvpCanHelp(self, newTarget)))
|
|
{
|
|
newTarget = self;
|
|
}
|
|
if (!combat.validateTarget(newTarget, actionData.validTarget))
|
|
{
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "invalid_target"));
|
|
}
|
|
combat.combatLog(self, curTarget, "getCorrectCombatTarget", "Invalid target as defined in datatable - returning null target");
|
|
return null;
|
|
}
|
|
obj_id container = getContainedBy(newTarget);
|
|
if (isPlayer(container))
|
|
{
|
|
String msg = "Cannot attack item contained by player - returning null target";
|
|
CustomerServiceLog("combat_errors", "ERROR::getCorrectCombatTarget:" + msg);
|
|
return null;
|
|
}
|
|
if ((!isOffensive && !pvpCanHelp(self, newTarget)) || (isOffensive && !pvpCanAttack(self, newTarget)))
|
|
{
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "invalid_pvp_target"));
|
|
}
|
|
combat.combatLog(self, curTarget, "getCorrectCombatTarget", "Not a valid PvP target - returning null target");
|
|
return null;
|
|
}
|
|
if (isOffensive && stealth.hasInvisibleBuff(newTarget))
|
|
{
|
|
int range = getPassiveRevealRange(newTarget, self);
|
|
if (range >= 0)
|
|
{
|
|
stealth.checkForAndMakeVisible(newTarget);
|
|
}
|
|
else
|
|
{
|
|
if (verbose)
|
|
{
|
|
combat.sendCombatSpamMessage(self, new string_id("cbt_spam", "invalid_target"));
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
return newTarget;
|
|
}
|
|
public boolean isInAttackRange(obj_id attacker, obj_id target, String actionName, boolean verbose) throws InterruptedException
|
|
{
|
|
if (!isIdValid(attacker) || !exists(attacker) || !isIdValid(target) || !exists(target))
|
|
{
|
|
return false;
|
|
}
|
|
combat_data actionData = combat_engine.getCombatData(actionName);
|
|
if (actionData == null)
|
|
{
|
|
return false;
|
|
}
|
|
obj_id weapon = getCurrentWeapon(attacker);
|
|
if (!isIdValid(weapon) || !exists(weapon))
|
|
{
|
|
return false;
|
|
}
|
|
weapon_data weaponData = getOverloadedWeaponData(attacker, weapon, actionData);
|
|
if (weaponData == null)
|
|
{
|
|
return false;
|
|
}
|
|
return isInAttackRange(attacker, target, actionData, weaponData, verbose);
|
|
}
|
|
public boolean isInAttackRange(obj_id attacker, obj_id target, combat_data actionData, weapon_data weaponData, boolean verbose) throws InterruptedException
|
|
{
|
|
float dist = getDistance(attacker, target);
|
|
float adjustMin = 0.0f;
|
|
float adjustMax = 0.0f;
|
|
if (dist > combat_engine.getMaxCombatRange() && actionData.ignore_distance == 0)
|
|
{
|
|
if (verbose)
|
|
{
|
|
showFlyTextPrivate(attacker, attacker, new string_id("combat_effects", "range_too_far"), 1.5f, colors.MEDIUMTURQUOISE);
|
|
combat.sendCombatSpamMessage(attacker, new string_id("cbt_spam", "out_of_range_far"), COMBAT_RESULT_OUT_OF_RANGE);
|
|
}
|
|
combat.combatLog(attacker, target, "doCombatPreCheck", "Aborting Attack - Target is further than max combat range");
|
|
return false;
|
|
}
|
|
if (actionData.overloadWeaponType == WEAPON_TYPE_THROWN)
|
|
{
|
|
weaponData.maxRange = cybernetic.getThrowRangeMod(attacker, weaponData.maxRange);
|
|
}
|
|
else
|
|
{
|
|
int intWeaponCategory = combat.getWeaponCategory(weaponData.weaponType);
|
|
if (intWeaponCategory == combat.RANGED_WEAPON)
|
|
{
|
|
weaponData.maxRange = cybernetic.getRangedRangeMod(attacker, weaponData.maxRange);
|
|
}
|
|
else
|
|
{
|
|
adjustMax = 6.0f;
|
|
}
|
|
}
|
|
float rangeMod = getSkillStatisticModifier(attacker, "expertise_range_bonus_" + weaponData.weaponType);
|
|
weaponData.maxRange += rangeMod;
|
|
float actionMaxRange = actionData.maxRange;
|
|
if (actionMaxRange > 0)
|
|
{
|
|
weaponData.maxRange = actionMaxRange;
|
|
}
|
|
rangeMod = getSkillStatisticModifier(attacker, "expertise_range_line_" + actionData.specialLine);
|
|
if (rangeMod != 0)
|
|
{
|
|
weaponData.maxRange += rangeMod;
|
|
}
|
|
if (dist > (weaponData.maxRange + adjustMax) && actionData.ignore_distance == 0)
|
|
{
|
|
if (verbose)
|
|
{
|
|
showFlyTextPrivate(attacker, attacker, new string_id("combat_effects", "range_too_far"), 1.5f, colors.MEDIUMTURQUOISE);
|
|
combat.sendCombatSpamMessage(attacker, new string_id("cbt_spam", "out_of_range_far"), COMBAT_RESULT_OUT_OF_RANGE);
|
|
}
|
|
combat.combatLog(attacker, target, "doCombatPreCheck", "Aborting Attak - Target is further than max range modified");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
public boolean doCombatPreCheck(combat_data actionData, attacker_data attackerData, weapon_data weaponData, defender_data[] defenderData, boolean verbose) throws InterruptedException
|
|
{
|
|
verbose = false;
|
|
if ((isPlayer(attackerData.id) && isPlayer(defenderData[0].id)) && (attackerData.id != defenderData[0].id))
|
|
{
|
|
String setting = getConfigSetting("GameServer", "disablePvP");
|
|
if (setting != null && (setting.equals("1") || setting.equals("true")))
|
|
{
|
|
if (verbose)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (isDead(attackerData.id))
|
|
{
|
|
if (verbose)
|
|
{
|
|
showFlyTextPrivate(attackerData.id, attackerData.id, new string_id("combat_effects", "cant_attack_fly"), 1.5f, colors.WHITE);
|
|
combat.sendCombatSpamMessage(attackerData.id, new string_id("cbt_spam", "attack_fail_dead"));
|
|
}
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "doCombatPreCheck", "Aborting Attack - Attacker is dead");
|
|
return false;
|
|
}
|
|
if (isIncapacitated(attackerData.id))
|
|
{
|
|
if (verbose)
|
|
{
|
|
showFlyTextPrivate(attackerData.id, attackerData.id, new string_id("combat_effects", "cant_attack_fly"), 1.5f, colors.WHITE);
|
|
combat.sendCombatSpamMessage(attackerData.id, new string_id("cbt_spam", "attack_fail_incap"));
|
|
}
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "doCombatPreCheck", "Aborting Attack - Attacker is incapped");
|
|
return false;
|
|
}
|
|
if (!isInAttackRange(attackerData.id, defenderData[0].id, actionData, weaponData, verbose))
|
|
{
|
|
return false;
|
|
}
|
|
boolean cansee = false;
|
|
if (actionData.specialLine.equals("ignore_los") || actionData.specialLine.equals("co_remote_detonator"))
|
|
{
|
|
cansee = true;
|
|
}
|
|
else if (weaponData.weaponType == WEAPON_TYPE_GROUND_TARGETTING)
|
|
{
|
|
int isSpecialAttack = actionData.commandType;
|
|
if (actionData.specialLine.equals("no_proc"))
|
|
{
|
|
cansee = true;
|
|
}
|
|
else if (isSpecialAttack == combat.RIGHT_CLICK_SPECIAL && combat.isHeavyWeapon(weaponData))
|
|
{
|
|
cansee = true;
|
|
}
|
|
else
|
|
{
|
|
location where = (location)actionData.targetLoc.clone();
|
|
where.y += 0.5f;
|
|
cansee = canSee(attackerData.id, where);
|
|
}
|
|
}
|
|
else if (weaponData.weaponType == WEAPON_TYPE_DIRECTIONAL)
|
|
{
|
|
cansee = true;
|
|
}
|
|
else
|
|
{
|
|
cansee = canSee(attackerData.id, defenderData[0].id);
|
|
}
|
|
if (!cansee)
|
|
{
|
|
if (verbose)
|
|
{
|
|
showFlyTextPrivate(attackerData.id, attackerData.id, new string_id("combat_effects", "cant_see"), 1.5f, colors.MEDIUMTURQUOISE);
|
|
combat.sendCombatSpamMessage(attackerData.id, new string_id("cbt_spam", "out_of_range_close"), COMBAT_RESULT_OUT_OF_RANGE);
|
|
}
|
|
return false;
|
|
}
|
|
setState(attackerData.id, STATE_PEACE, false);
|
|
return true;
|
|
}
|
|
public obj_id getNextHatedTarget(obj_id self, obj_id[] tempDefenders, obj_id currentTarget) throws InterruptedException
|
|
{
|
|
if (tempDefenders == null || tempDefenders.length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
for (int i = 0; i < tempDefenders.length; i++)
|
|
{
|
|
if (tempDefenders[i] != currentTarget && isOnHateList(self, tempDefenders[i]))
|
|
{
|
|
return tempDefenders[i];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
public obj_id[] getCombatDefenders(obj_id self, obj_id target, weapon_data weaponData, combat_data actionData, boolean isSpecialAttack) throws InterruptedException
|
|
{
|
|
int validTargetType = actionData.validTarget;
|
|
int attackType = actionData.attackType;
|
|
int hitType = actionData.hitType;
|
|
boolean isOffensive = true;
|
|
int pvpOnly = actionData.pvp_only;
|
|
if (validTargetType == combat.VALID_TARGET_FRIEND || validTargetType == combat.VALID_TARGET_DEAD || hitType == combat.HEAL || hitType == combat.REVIVE)
|
|
{
|
|
isOffensive = false;
|
|
}
|
|
boolean fromTrap = hitType == combat.DELAY_ATTACK;
|
|
obj_id source = self;
|
|
obj_id[] defenders = null;
|
|
if (actionData.actionName.equals("me_stasis_1") || actionData.actionName.startsWith("bm_mend_pet") || actionData.actionName.equals("bm_soothing_comfort_1") || actionData.actionName.startsWith("bm_revive"))
|
|
{
|
|
isOffensive = false;
|
|
}
|
|
if ((!isOffensive || hitType == combat.NON_ATTACK) && validTargetType == combat.VALID_TARGET_NONE)
|
|
{
|
|
target = self;
|
|
}
|
|
if (validTargetType == -1 && attackType == combat.SINGLE_TARGET)
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
return defenders;
|
|
}
|
|
if (attackType == combat.SINGLE_TARGET)
|
|
{
|
|
if (defenders == null || defenders.length == 0)
|
|
{
|
|
int rolls = actionData.attack_rolls == 1 ? 1 : actionData.attack_rolls - 1;
|
|
defenders = new obj_id[rolls];
|
|
for (int q = 0; q < defenders.length; q++)
|
|
{
|
|
defenders[q] = target;
|
|
}
|
|
}
|
|
}
|
|
else if (attackType == combat.CONE)
|
|
{
|
|
float length = actionData.coneLength;
|
|
float width = actionData.coneWidth;
|
|
String actionName = actionData.actionName;
|
|
String specialLine = actionData.specialLine;
|
|
float expertiseConeLengthBonus = 0.0f;
|
|
float expertiseConeWidthBonus = 0.0f;
|
|
if (actionName != null && !actionName.equals(""))
|
|
{
|
|
expertiseConeLengthBonus = getSkillStatisticModifier(self, "expertise_cone_length_single_" + actionName);
|
|
}
|
|
if (specialLine != null && !specialLine.equals(""))
|
|
{
|
|
expertiseConeLengthBonus += getSkillStatisticModifier(self, "expertise_cone_length_line_" + specialLine);
|
|
}
|
|
if (specialLine != null && !actionName.equals(""))
|
|
{
|
|
expertiseConeWidthBonus += (float)getSkillStatisticModifier(self, "expertise_cone_width_line_" + specialLine);
|
|
}
|
|
length += expertiseConeLengthBonus;
|
|
width += (expertiseConeWidthBonus / 2.0f);
|
|
if (actionData.maxRange == 0)
|
|
{
|
|
length = weaponData.maxRange;
|
|
}
|
|
if (!isOffensive)
|
|
{
|
|
defenders = getObjectsInCone(fromTrap ? weaponData.id : self, target, length, width);
|
|
}
|
|
else
|
|
{
|
|
if (weaponData.weaponType == WEAPON_TYPE_DIRECTIONAL)
|
|
{
|
|
if(actionData != null && isValidLocation(actionData.targetLoc)) {
|
|
defenders = pvpGetTargetsInCone(self, self, actionData.targetLoc, length, width);
|
|
}
|
|
else{
|
|
defenders = pvpGetTargetsInCone(self, self, target, length, width);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
defenders = pvpGetTargetsInCone(self, fromTrap ? weaponData.id : self, target, length, width);
|
|
}
|
|
}
|
|
if (defenders == null || defenders.length < 1)
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
}
|
|
combat.combatLog(self, target, "getCombatDefenders", "CONE TARGET: Defender count = " + defenders.length);
|
|
}
|
|
else if (attackType == combat.AREA || attackType == combat.TARGET_AREA)
|
|
{
|
|
float area = actionData.coneLength;
|
|
String actionName = actionData.actionName;
|
|
String specialLine = actionData.specialLine;
|
|
float expertiseConeLengthBonus = 0.0f;
|
|
if (actionName != null && !actionName.equals(""))
|
|
{
|
|
expertiseConeLengthBonus = getSkillStatisticModifier(self, "expertise_area_size_single_" + actionName);
|
|
}
|
|
if (specialLine != null && !specialLine.equals(""))
|
|
{
|
|
expertiseConeLengthBonus += getSkillStatisticModifier(self, "expertise_area_size_line_" + specialLine);
|
|
}
|
|
area += expertiseConeLengthBonus;
|
|
if (area <= 0)
|
|
{
|
|
area = weaponData.maxRange;
|
|
}
|
|
if (attackType == combat.AREA)
|
|
{
|
|
combat.combatLog(self, target, "getCombatDefenders", "Attack Type = combat.AREA");
|
|
combat.combatLog(self, target, "getCombatDefenders", "Area Effect = " + area);
|
|
if (!isOffensive)
|
|
{
|
|
defenders = getCreaturesInRange(fromTrap ? weaponData.id : self, area);
|
|
}
|
|
else
|
|
{
|
|
defenders = pvpGetTargetsInRange(self, fromTrap ? weaponData.id : self, area);
|
|
}
|
|
combat.combatLog(self, target, "getCombatDefenders", "AREA SELF: self = " + self);
|
|
}
|
|
else
|
|
{
|
|
combat.combatLog(self, target, "getCombatDefenders", "Attack Type = combat.TARGET_AREA");
|
|
combat.combatLog(self, target, "getCombatDefenders", "Area Effect = " + area);
|
|
if (!isOffensive)
|
|
{
|
|
defenders = getCreaturesInRange(target, area);
|
|
}
|
|
else
|
|
{
|
|
defenders = pvpGetTargetsInRange(self, fromTrap ? weaponData.id : target, area);
|
|
}
|
|
source = target;
|
|
}
|
|
if (defenders == null || defenders.length < 1 && actionData.commandType == combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
}
|
|
combat.combatLog(self, target, "getCombatDefenders", "AREA EFFECT: Defender count = " + defenders.length);
|
|
}
|
|
else if (attackType == combat.RAMPAGE)
|
|
{
|
|
float area = actionData.coneLength;
|
|
obj_id[] areaTargets = pvpGetTargetsInRange(self, self, area);
|
|
float maxHate = 0;
|
|
obj_id rampageTarget = null;
|
|
for (int i = 0; i < areaTargets.length; i++)
|
|
{
|
|
if (areaTargets[i] == target)
|
|
{
|
|
continue;
|
|
}
|
|
if (isOnHateList(self, areaTargets[i]))
|
|
{
|
|
float targetHate = getHate(self, areaTargets[i]);
|
|
if (targetHate > maxHate && combat.cachedCanSee(self, areaTargets[i]))
|
|
{
|
|
rampageTarget = areaTargets[i];
|
|
maxHate = targetHate;
|
|
}
|
|
}
|
|
}
|
|
if (isIdValid(rampageTarget))
|
|
{
|
|
defenders = new obj_id[1];
|
|
target = rampageTarget;
|
|
defenders[0] = rampageTarget;
|
|
}
|
|
else
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
}
|
|
combat.combatLog(self, target, "getCombatDefenders", "RAMPAGE: Defender count = " + defenders.length);
|
|
}
|
|
else if (attackType == combat.RANDOM_HATE_TARGET)
|
|
{
|
|
obj_id[] potentialTargets = getHateList(self);
|
|
float max_range = 64.0f;
|
|
Vector validTargets = new Vector();
|
|
validTargets.setSize(0);
|
|
for (int i = 0; i < potentialTargets.length; i++)
|
|
{
|
|
if (potentialTargets[i] != target && combat.cachedCanSee(self, potentialTargets[i]) && !isDead(potentialTargets[i]))
|
|
{
|
|
if (getDistance(self, potentialTargets[i]) < max_range)
|
|
{
|
|
utils.addElement(validTargets, potentialTargets[i]);
|
|
}
|
|
}
|
|
}
|
|
if (validTargets == null || validTargets.size() == 0)
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
}
|
|
else
|
|
{
|
|
target = ((obj_id)validTargets.get(rand(0, validTargets.size() - 1)));
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
}
|
|
}
|
|
else if (attackType == combat.RANDOM_HATE_TARGET_CONE)
|
|
{
|
|
float length = actionData.coneLength;
|
|
float width = actionData.coneWidth;
|
|
float max_range = length;
|
|
obj_id[] potentialTargets = getHateList(self);
|
|
Vector validTargets = new Vector();
|
|
validTargets.setSize(0);
|
|
for (int i = 0; i < potentialTargets.length; i++)
|
|
{
|
|
if (potentialTargets[i] != target && combat.cachedCanSee(self, potentialTargets[i]) && !isDead(potentialTargets[i]))
|
|
{
|
|
if (getDistance(self, potentialTargets[i]) < max_range)
|
|
{
|
|
utils.addElement(validTargets, potentialTargets[i]);
|
|
}
|
|
}
|
|
}
|
|
if (validTargets == null || validTargets.size() == 0)
|
|
{
|
|
defenders = pvpGetTargetsInCone(self, self, target, length, width);
|
|
}
|
|
else
|
|
{
|
|
target = ((obj_id)validTargets.get(rand(0, validTargets.size() - 1)));
|
|
defenders = pvpGetTargetsInCone(self, self, target, length, width);
|
|
}
|
|
}
|
|
else if (attackType == combat.RANDOM_HATE_TARGET_CONE_TERMINUS)
|
|
{
|
|
float width = actionData.coneWidth;
|
|
float max_range = 64.0f;
|
|
obj_id[] potentialTargets = getHateList(self);
|
|
Vector validTargets = new Vector();
|
|
validTargets.setSize(0);
|
|
for (int i = 0; i < potentialTargets.length; i++)
|
|
{
|
|
if (potentialTargets[i] != target && combat.cachedCanSee(self, potentialTargets[i]))
|
|
{
|
|
if (getDistance(self, potentialTargets[i]) < max_range)
|
|
{
|
|
utils.addElement(validTargets, potentialTargets[i]);
|
|
}
|
|
}
|
|
}
|
|
if (validTargets == null || validTargets.size() == 0)
|
|
{
|
|
defenders = pvpGetTargetsInCone(self, self, target, getDistance(self, target) + 5.0f, width);
|
|
}
|
|
else
|
|
{
|
|
target = ((obj_id)validTargets.get(rand(0, validTargets.size() - 1)));
|
|
max_range = getDistance(self, target) + 5.0f;
|
|
defenders = pvpGetTargetsInCone(self, self, target, max_range, width);
|
|
}
|
|
}
|
|
else if (attackType == combat.HATE_LIST)
|
|
{
|
|
obj_id[] hateTargets = getHateList(self);
|
|
Vector validTargets = new Vector();
|
|
validTargets.setSize(0);
|
|
int max_range = 90;
|
|
for (int i = 0; i < hateTargets.length; i++)
|
|
{
|
|
if (hateTargets[i] != target && canSee(self, hateTargets[i]))
|
|
{
|
|
if (getDistance(self, hateTargets[i]) < max_range)
|
|
{
|
|
utils.addElement(validTargets, hateTargets[i]);
|
|
}
|
|
}
|
|
}
|
|
if (validTargets != null)
|
|
{
|
|
defenders = new obj_id[validTargets.size()];
|
|
validTargets.toArray(defenders);
|
|
|
|
}
|
|
}
|
|
else if (attackType == combat.RANDOM_HATE_MULTI)
|
|
{
|
|
obj_id[] hateTargets = getHateList(self);
|
|
int max_targets = (int)actionData.coneLength;
|
|
if (hateTargets.length == 1)
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
}
|
|
else
|
|
{
|
|
obj_id[] randomTargets = new obj_id[max_targets];
|
|
Vector targetList = new Vector(Arrays.asList((obj_id[])hateTargets.clone()));
|
|
targetList = utils.shuffleArray(targetList);
|
|
if (max_targets > hateTargets.length)
|
|
{
|
|
max_targets = hateTargets.length;
|
|
}
|
|
for (int i = 0; i < max_targets; i++)
|
|
{
|
|
randomTargets[i] = ((obj_id)targetList.get(i));
|
|
}
|
|
defenders = randomTargets;
|
|
}
|
|
}
|
|
else if (attackType == combat.AREA_PROGRESSIVE)
|
|
{
|
|
float area = actionData.coneLength;
|
|
if (area <= 0)
|
|
{
|
|
area = weaponData.maxRange;
|
|
}
|
|
defenders = pvpGetTargetsInRange(self, self, area);
|
|
}
|
|
else if (attackType == combat.SPLIT_DAMAGE_TARGET_AREA)
|
|
{
|
|
float area = actionData.coneLength;
|
|
float targets = actionData.coneWidth;
|
|
obj_id[] targetsInArea = pvpGetTargetsInRange(self, fromTrap ? weaponData.id : target, area);
|
|
defenders = new obj_id[2];
|
|
defenders[0] = target;
|
|
for (int i = 0; i < targetsInArea.length; i++)
|
|
{
|
|
if (targetsInArea[i] != target)
|
|
{
|
|
defenders[1] = targetsInArea[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (attackType == combat.DISTANCE_FARTHEST)
|
|
{
|
|
obj_id[] hateList = getHateList(self);
|
|
obj_id farthest = getHateTarget(self);
|
|
float farDist = 0.0f;
|
|
for (int i = 0; i < hateList.length; i++)
|
|
{
|
|
if (getDistance(self, hateList[i]) >= farDist)
|
|
{
|
|
farthest = hateList[i];
|
|
farDist = getDistance(self, hateList[i]);
|
|
}
|
|
}
|
|
defenders = new obj_id[1];
|
|
defenders[0] = farthest;
|
|
target = farthest;
|
|
}
|
|
else
|
|
{
|
|
defenders = new obj_id[1];
|
|
defenders[0] = target;
|
|
combat.combatLog(self, target, "getCombatDefenders", "DEFAULT: Defender count = " + defenders.length);
|
|
}
|
|
defenders = validateDefenders(self, target, defenders, source, MAX_TARGET_ARRAY_SIZE, validTargetType, isOffensive, fromTrap ? weaponData.id : null, combat.isHeavyWeapon(weaponData), isSpecialAttack, actionData.specialLine.equals("ignore_los") ? true : false, pvpOnly, actionData);
|
|
utils.setScriptVar(self, combat.CHARGE_TARGET, target);
|
|
return defenders;
|
|
}
|
|
public hit_result[] runHitEngine(attacker_data attackerData, weapon_data weaponData, defender_data[] defenderData, attacker_results attackerResults, defender_results[] defenderResults, combat_data actionData, boolean isTangibleAttacking) throws InterruptedException
|
|
{
|
|
return runHitEngine(attackerData, weaponData, defenderData, attackerResults, defenderResults, actionData, isTangibleAttacking, true, 0);
|
|
}
|
|
public hit_result[] runHitEngine(attacker_data attackerData, weapon_data weaponData, defender_data[] defenderData, attacker_results attackerResults, defender_results[] defenderResults, combat_data actionData, boolean isTangibleAttacking, boolean isAutoAiming, int overloadDamage) throws InterruptedException
|
|
{
|
|
hit_result[] hitData = new hit_result[defenderData.length];
|
|
obj_id[] defenders = new obj_id[defenderData.length];
|
|
int[] results = new int[hitData.length];
|
|
String[] playbackNames = new String[defenderData.length];
|
|
String strAttack = actionData.actionName;
|
|
attackerResults.id = attackerData.id;
|
|
attackerResults.weapon = weaponData.id;
|
|
attackerResults.endPosture = (!isTangibleAttacking && (combat.isMeleeWeapon(weaponData.id) || combat.isLightsaberWeapon(weaponData.id))) ? POSTURE_UPRIGHT : getPosture(attackerData.id);
|
|
combat.applyAttackerCombatBuffs(attackerData.id, actionData);
|
|
int hitType = actionData.hitType;
|
|
if (hitType == combat.NON_ATTACK)
|
|
{
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "runHitEngine", "Non-Combat action - Skipping hitEngine");
|
|
attackerResults.endPosture = getPosture(attackerData.id);
|
|
for (int i = 0; i < defenderData.length; i++)
|
|
{
|
|
hitData[i] = new hit_result();
|
|
if (i == 0)
|
|
{
|
|
hitData[i].success = true;
|
|
}
|
|
else
|
|
{
|
|
hitData[i].success = false;
|
|
}
|
|
if (actionData.validTarget == combat.VALID_TARGET_FRIEND && pvpCanHelp(attackerData.id, defenderData[i].id))
|
|
{
|
|
pvpHelpPerformed(attackerData.id, defenderData[i].id);
|
|
combat.applyDefenderCombatBuffs(attackerData.id, defenderData[i].id, weaponData, actionData);
|
|
}
|
|
defenderResults[i] = new defender_results();
|
|
defenderResults[i].id = defenderData[i].id;
|
|
defenderResults[i].endPosture = getPosture(defenderData[i].id);
|
|
}
|
|
return hitData;
|
|
}
|
|
boolean isHeavyWeapon = combat.isHeavyWeapon(weaponData);
|
|
int attackerLevel = getLevel(attackerData.id);
|
|
boolean criticalHit = false;
|
|
boolean seriesStrikethrough = false;
|
|
attackerData = combat.fillAttackerCombatAttributes(attackerData);
|
|
if (utils.hasScriptVar(attackerData.id, "nextCritHit") && actionData.commandType != combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
actionData.increaseCritical += 100.0f;
|
|
actionData.reduceParry += 100;
|
|
actionData.reduceDodge += 100;
|
|
actionData.reduceBlock += 100;
|
|
actionData.reduceGlancing += 100;
|
|
actionData.increaseStrikethrough -= 100;
|
|
}
|
|
utils.removeScriptVar(attackerData.id, PROGRESSIVE_DAMAGE_COUNTER);
|
|
if (combat.isCommandoBonus(attackerData.id, weaponData, actionData.commandType))
|
|
{
|
|
float devastationBonus = combat.getDevastationChance(attackerData.id);
|
|
if (rand(0.0f, 99.0f) < devastationBonus)
|
|
{
|
|
weaponData.minDamage = weaponData.maxDamage;
|
|
weaponData.maxDamage *= 1.5f;
|
|
showFlyTextPrivate(attackerData.id, attackerData.id, new string_id("combat_effects", "devastation"), 1.5f, colors.VIOLETRED);
|
|
}
|
|
if (actionData.buffNameTarget.length() == 0 || actionData.buffNameTarget.equals(""))
|
|
{
|
|
int passiveDotBonus = 10;
|
|
passiveDotBonus += getEnhancedSkillStatisticModifierUncapped(attackerData.id, "commando_passive_dot");
|
|
if (rand(0, 99) < passiveDotBonus)
|
|
{
|
|
String commandoDotName = heavyweapons.getHeavyWeaponDotName(attackerData.id, weaponData, true);
|
|
if (commandoDotName != null && !commandoDotName.equals(""))
|
|
{
|
|
actionData.buffNameTarget = commandoDotName;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
boolean isPlayerAttacker = isPlayer(attackerData.id);
|
|
int intWeaponAttackType = getWeaponAttackType(weaponData.id);
|
|
boolean isRanged = (intWeaponAttackType == ATTACK_TYPE_RANGED);
|
|
boolean specialAttack = actionData.commandType == combat.RIGHT_CLICK_SPECIAL;
|
|
int critDamageIncrease = getEnhancedSkillStatisticModifierUncapped(attackerData.id, "expertise_critical_damage_increase");
|
|
for (int i = 0; i < defenderData.length; i++)
|
|
{
|
|
float minDamage = (float)weaponData.minDamage;
|
|
float maxDamage = (float)weaponData.maxDamage;
|
|
hitData[i] = new hit_result();
|
|
float distance = 0;
|
|
boolean isPlayerDefender = isPlayer(defenderData[i].id);
|
|
obj_id oldDefender = defenderData[i].id;
|
|
if (hitType != combat.NON_DAMAGE_ATTACK)
|
|
{
|
|
obj_id newDefender = combat.directDamageToDifferentTarget(attackerData.id, defenderData[i].id);
|
|
if (oldDefender != newDefender)
|
|
{
|
|
defenderData[i].id = newDefender;
|
|
}
|
|
}
|
|
defenderData[i] = combat.fillDefenderCombatAttributes(defenderData[i]);
|
|
int defenderLevel = getLevel(defenderData[i].id);
|
|
if (hitType == combat.NON_DAMAGE_ATTACK)
|
|
{
|
|
hitData[i].success = true;
|
|
}
|
|
else
|
|
{
|
|
int defResult = getDefenderResult(attackerData, defenderData[i], actionData, isAutoAiming);
|
|
int atkResult = getAttackerResult(attackerData, defenderData[i], actionData, isAutoAiming);
|
|
switch (defResult)
|
|
{
|
|
case HIT_RESULT_DODGE:
|
|
hitData[i].success = false;
|
|
hitData[i].dodge = true;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
hitData[i].success = false;
|
|
hitData[i].parry = true;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
hitData[i].success = true;
|
|
hitData[i].glancing = true;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
hitData[i].success = true;
|
|
hitData[i].blockResult = true;
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
hitData[i].success = true;
|
|
hitData[i].evadeResult = true;
|
|
break;
|
|
case HIT_RESULT_HIT:
|
|
hitData[i].success = true;
|
|
break;
|
|
default:
|
|
hitData[i].success = true;
|
|
break;
|
|
}
|
|
if (hitData[i].success == true)
|
|
{
|
|
switch (atkResult)
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
hitData[i].success = false;
|
|
hitData[i].miss = true;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
hitData[i].success = true;
|
|
hitData[i].critical = true;
|
|
hitData[i].glancing = false;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
hitData[i].success = true;
|
|
hitData[i].crushing = true;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
hitData[i].success = true;
|
|
hitData[i].strikethrough = true;
|
|
hitData[i].glancing = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
int[] actionCost = combat.getSuccessBasedSingleTargetActionCost(hitData[i], actionData, attackerData.id, weaponData);
|
|
combat.drainCombatActionAttributes(attackerData.id, actionCost);
|
|
int validTargetType = actionData.validTarget;
|
|
if (validTargetType == combat.VALID_TARGET_FRIEND && pvpCanHelp(attackerData.id, defenderData[i].id))
|
|
{
|
|
pvpHelpPerformed(attackerData.id, defenderData[i].id);
|
|
}
|
|
else if (pvpCanAttack(attackerData.id, defenderData[i].id))
|
|
{
|
|
pvpAttackPerformed(attackerData.id, defenderData[i].id);
|
|
}
|
|
defenderResults[i] = new defender_results();
|
|
defenderResults[i].id = defenderData[i].id;
|
|
defenderResults[i].endPosture = getPosture(defenderData[i].id);
|
|
if (validTargetType != combat.VALID_TARGET_FRIEND && defenderData[i].id != attackerData.id && hitData[i].success)
|
|
{
|
|
startCombat(defenderData[i].id, attackerData.id);
|
|
if (oldDefender != defenderData[i].id)
|
|
{
|
|
startCombat(oldDefender, attackerData.id);
|
|
}
|
|
}
|
|
if (combat.isDamageImmune(attackerData.id, defenderData[i].id, actionData))
|
|
{
|
|
if (defenderData.length == 1)
|
|
{
|
|
actionData.hitSpam = combat.NO_HIT_SPAM;
|
|
}
|
|
continue;
|
|
}
|
|
if (combat.hasPrescience(attackerData.id, defenderData[i].id, actionData))
|
|
{
|
|
if (defenderData.length == 1)
|
|
{
|
|
actionData.hitSpam = combat.NO_HIT_SPAM;
|
|
}
|
|
continue;
|
|
}
|
|
if (!hitData[i].success)
|
|
{
|
|
target_dummy.sendTargetDummyCombatData(attackerData.id, defenderData[i].id, actionData, hitData[i]);
|
|
}
|
|
if (hitData[i].success)
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_HIT;
|
|
if (beast_lib.isBeast(attackerData.id))
|
|
{
|
|
addHate(defenderData[i].id, getMaster(attackerData.id), 0);
|
|
}
|
|
if (hitData[i].strikethrough)
|
|
{
|
|
hitData[i].strikethroughAmmount = combat.getStrikethroughValue(attackerData.id, defenderData[i].id);
|
|
seriesStrikethrough |= true;
|
|
}
|
|
if (hitData[i].evadeResult)
|
|
{
|
|
hitData[i].evadeAmmount = combat.getEvasionRoll(defenderData[i].id);
|
|
}
|
|
if (hitData[i].blockResult)
|
|
{
|
|
hitData[i].block = combat.getBlockAmmount(defenderData[i].id);
|
|
}
|
|
if (hitData[i].crushing)
|
|
{
|
|
minDamage = maxDamage * 1.5f;
|
|
maxDamage = (maxDamage * 1.5f) + 1.0f;
|
|
}
|
|
dictionary rawDict = getRawDamage(attackerData.id, weaponData, actionData, hitData[i], minDamage, maxDamage, defenderData.length);
|
|
minDamage = rawDict.getFloat("minDamage");
|
|
maxDamage = rawDict.getFloat("maxDamage");
|
|
if (hitData[i].glancing)
|
|
{
|
|
minDamage *= 0.35f;
|
|
maxDamage *= 0.35f;
|
|
weaponData.elementalValue *= 0.35f;
|
|
hitData[i].critDamage = 1;
|
|
string_id critSpam = new string_id("combat_effects", "glancing_blow");
|
|
prose_package ppc = new prose_package();
|
|
ppc = prose.setStringId(ppc, critSpam);
|
|
showFlyTextPrivateProseWithFlags(defenderData[i].id, defenderData[i].id, ppc, 1.5f, colors.LIMEGREEN, FLY_TEXT_FLAG_IS_GLANCING_BLOW);
|
|
hasSkill(defenderData[i].id, "expertise_fs_general_alacrity_1");
|
|
playClientEffectObj(defenderData[i].id, "appearance/pt_jedi_alacrity.prt", defenderData[i].id, "");
|
|
}
|
|
if (hitData[i].critical)
|
|
{
|
|
float damageMod = combat.doCriticalHitEffect(attackerData, defenderData[i], weaponData, hitData[i], actionData);
|
|
float critBonus = 1.0f + ((float)critDamageIncrease / 100.0f);
|
|
damageMod += (float)critDamageIncrease / 100.0f;
|
|
minDamage = maxDamage * critBonus;
|
|
maxDamage = maxDamage * damageMod;
|
|
criticalHit = true;
|
|
}
|
|
hitData[i].damage = rand((int)minDamage, (int)maxDamage);
|
|
if (hitType != combat.NON_DAMAGE_ATTACK)
|
|
{
|
|
hitData[i] = applyDamage(attackerData.id, defenderData[i].id, weaponData, hitData[i], actionData, (i == 0), overloadDamage);
|
|
}
|
|
else
|
|
{
|
|
hitData[i].damage = 0;
|
|
}
|
|
effect_data defenderEffect = new effect_data();
|
|
combat.applyDefenderCombatBuffs(attackerData.id, defenderData[i].id, weaponData, actionData);
|
|
beast_lib.checkForSkillAcquisition(attackerData.id, defenderData[i].id, actionData.actionName);
|
|
if (!actionData.specialLine.equals("no_proc"))
|
|
{
|
|
proc.executeProcEffects(attackerData.id, defenderData[i].id, actionData);
|
|
}
|
|
else
|
|
{
|
|
hitData[i].proc = true;
|
|
}
|
|
if (actionData.effectOnTarget != null && actionData.effectOnTarget != "")
|
|
{
|
|
playClientEffectObj(defenderData[i].id, actionData.effectOnTarget, defenderData[i].id, "");
|
|
}
|
|
int luckyBreakChance = getSkillStatisticModifier(attackerData.id, "expertise_lucky_break_chance");
|
|
if (luckyBreakChance > 0 && buff.hasBuff(attackerData.id, "sm_feeling_lucky") && !buff.hasBuff(attackerData.id, "sm_lucky_break") && !buff.hasBuff(attackerData.id, "sm_lucky_break_recourse") && rand(0, 99) < luckyBreakChance)
|
|
{
|
|
buff.applyBuff(attackerData.id, "sm_lucky_break");
|
|
string_id luckySID = new string_id("cbt_spam", "sm_lucky_break_proc");
|
|
sendCombatSpamMessage(attackerData.id, defenderData[i].id, luckySID, true, true, true, COMBAT_RESULT_BUFF);
|
|
}
|
|
if (buff.hasBuff(attackerData.id, "sm_lucky_break"))
|
|
{
|
|
int doubleHitChance = (int)getSkillStatisticModifier(attackerData.id, "expertise_double_hit_chance");
|
|
if (doubleHitChance > 0 && !buff.hasBuff(attackerData.id, "sm_double_hit") && !buff.hasBuff(attackerData.id, "sm_double_hit_recourse") && rand(0, 99) < doubleHitChance)
|
|
{
|
|
buff.applyBuff(attackerData.id, "sm_double_hit");
|
|
string_id doubleHitSID = new string_id("cbt_spam", "sm_double_hit_proc");
|
|
sendCombatSpamMessage(attackerData.id, defenderData[i].id, doubleHitSID, true, true, true, COMBAT_RESULT_BUFF);
|
|
}
|
|
}
|
|
}
|
|
else if (hitData[i].dodge)
|
|
{
|
|
if (!hasScript(defenderData[i].id, "player.player_countdown") && !movement.hasMovementModifier(defenderData[i].id))
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_EVADE;
|
|
}
|
|
else
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_MISS;
|
|
}
|
|
}
|
|
else if (hitData[i].parry)
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_BLOCK;
|
|
weapon_data attackerWeapon = getWeaponData(getCurrentWeapon(attackerData.id));
|
|
weapon_data defenderWeapon = getWeaponData(getCurrentWeapon(defenderData[i].id));
|
|
boolean isRangedAttacker = false;
|
|
boolean isLightSaber = false;
|
|
if (attackerWeapon != null)
|
|
{
|
|
isRangedAttacker = combat.isRangedWeapon(attackerWeapon.weaponType);
|
|
}
|
|
if (defenderWeapon != null)
|
|
{
|
|
isLightSaber = jedi.isLightsaber(defenderWeapon.id);
|
|
}
|
|
if (isRangedAttacker && isLightSaber)
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_LIGHTSABER_BLOCK;
|
|
}
|
|
if (!actionData.specialLine.equals("no_proc"))
|
|
{
|
|
proc.executeProcEffects(attackerData.id, defenderData[i].id, actionData);
|
|
}
|
|
else
|
|
{
|
|
hitData[i].proc = true;
|
|
}
|
|
if (isLightSaber && buff.hasBuff(defenderData[i].id, "fs_saber_reflect") && !strAttack.equals("fs_saber_reflect"))
|
|
{
|
|
queueCommand(defenderData[i].id, (1345072218), attackerData.id, "", COMMAND_PRIORITY_DEFAULT);
|
|
}
|
|
int addDefenderDamageToAction = getSkillStatisticModifier(defenderData[i].id, "expertise_damage_add_to_action");
|
|
if (addDefenderDamageToAction > 0)
|
|
{
|
|
float halfOfMod = addDefenderDamageToAction / 2.0f;
|
|
int actionToAdd = Math.round((halfOfMod / 100.0f) * (float)getMaxAction(defenderData[i].id));
|
|
combat.gainCombatActionAttribute(defenderData[i].id, actionToAdd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_MISS;
|
|
}
|
|
if (defenderResults[i].result != COMBAT_RESULT_TETHERED)
|
|
{
|
|
combat.addHateProcess(attackerData.id, defenderData[i].id, hitData[i], actionData);
|
|
}
|
|
playbackNames[i] = combat.getActionAnimation(actionData, combat.getWeaponStringType(weaponData.weaponType));
|
|
if (hitType == combat.NON_DAMAGE_ATTACK && hasScript(defenderData[i].id, "player.player_logout"))
|
|
{
|
|
defenders[i] = null;
|
|
}
|
|
else
|
|
{
|
|
defenders[i] = defenderData[i].id;
|
|
}
|
|
results[i] = defenderResults[i].result;
|
|
}
|
|
if (criticalHit && utils.hasScriptVar(attackerData.id, "critRemoveBuffNames") && actionData.commandType != combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
utils.removeScriptVar(attackerData.id, "nextCritHit");
|
|
Vector buffNames = utils.getResizeableStringArrayScriptVar(attackerData.id, "critRemoveBuffNames");
|
|
buff.removeBuffs(attackerData.id, buffNames);
|
|
}
|
|
if (seriesStrikethrough)
|
|
{
|
|
int stproc = getEnhancedSkillStatisticModifierUncapped(attackerData.id, "expertise_onstrikethrough_proc_sp_resonance");
|
|
if (stproc > 0)
|
|
{
|
|
buff.partyBuff(attackerData.id, "sp_critbuff_resonance");
|
|
}
|
|
}
|
|
if (criticalHit)
|
|
{
|
|
int critproc = getEnhancedSkillStatisticModifierUncapped(attackerData.id, "expertise_oncrit_proc_sp_savagery");
|
|
if (critproc > 0)
|
|
{
|
|
buff.partyBuff(attackerData.id, "sp_critbuff_savagery");
|
|
}
|
|
}
|
|
if (utils.hasScriptVar(attackerData.id, buff.ON_ATTACK_REMOVE) && actionData.commandType != combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
Vector buffList = utils.getResizeableStringArrayScriptVar(attackerData.id, buff.ON_ATTACK_REMOVE);
|
|
for (int i = 0; i < buffList.size(); i++)
|
|
{
|
|
if (buff.hasBuff(attackerData.id, ((String)buffList.get(i))))
|
|
{
|
|
buff.removeBuff(attackerData.id, ((String)buffList.get(i)));
|
|
}
|
|
}
|
|
}
|
|
if (actionData.hitSpam != combat.NO_HIT_SPAM)
|
|
{
|
|
obj_id currentWeapon = null;
|
|
if (actionData.hitSpam == combat.ACTION_NAME)
|
|
{
|
|
string_id attackId = new string_id("cmd_n", actionData.actionName);
|
|
combat.doBasicCombatSpam(attackId, SID_NONE, currentWeapon, attackerResults, defenderResults, hitData);
|
|
}
|
|
else if (actionData.hitSpam == combat.WEAPON_NAME)
|
|
{
|
|
if (weaponData.weaponName != null)
|
|
{
|
|
combat.doBasicCombatSpam(SID_NONE, weaponData.weaponName, currentWeapon, attackerResults, defenderResults, hitData);
|
|
}
|
|
else if (isValidId(weaponData.id))
|
|
{
|
|
combat.doBasicCombatSpam(SID_NONE, weaponData.weaponName, weaponData.id, attackerResults, defenderResults, hitData);
|
|
}
|
|
else if (isValidId(attackerResults.weapon))
|
|
{
|
|
combat.doBasicCombatSpam(SID_NONE, weaponData.weaponName, attackerResults.weapon, attackerResults, defenderResults, hitData);
|
|
}
|
|
else if (isValidId(getCurrentWeapon(attackerResults.id)))
|
|
{
|
|
combat.doBasicCombatSpam(SID_NONE, weaponData.weaponName, getCurrentWeapon(attackerResults.id), attackerResults, defenderResults, hitData);
|
|
}
|
|
else
|
|
{
|
|
combat.doBasicCombatSpam(SID_NONE, SID_NONE, currentWeapon, attackerResults, defenderResults, hitData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
combat.doBasicCombatSpam(SID_NONE, SID_NONE, currentWeapon, attackerResults, defenderResults, hitData);
|
|
}
|
|
}
|
|
if (actionData.hitType != 6 || actionData.delayAttackLoopsDone == 0)
|
|
{
|
|
doMultiDefenderCombatResults(playbackNames, attackerResults, defenderResults, weaponData, actionData, hitData);
|
|
}
|
|
int rampageAttacks = getEnhancedSkillStatisticModifierUncapped(attackerData.id, "expertise_co_killing_spree_target");
|
|
if (actionData.commandType == combat.LEFT_CLICK_DEFAULT && buff.hasBuff(attackerData.id, "co_killing_spree") && rampageAttacks > 0)
|
|
{
|
|
obj_id[] hateList = getHateList(attackerData.id);
|
|
for (int q = 0; q < rampageAttacks; q++)
|
|
{
|
|
if (hateList != null && hateList.length > 0)
|
|
{
|
|
queueCommand(attackerData.id, (-410474913), hateList[rand(0, hateList.length - 1)], "", COMMAND_PRIORITY_IMMEDIATE);
|
|
}
|
|
}
|
|
}
|
|
callDefenderCombatAction(defenders, results, attackerData.id, weaponData.id);
|
|
return hitData;
|
|
}
|
|
public int getDefenderResult(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException
|
|
{
|
|
if (combat.isAreaAttack(actionData.attackType))
|
|
{
|
|
return getAreaDefenderResult(attackerData, defenderData, actionData, autoAim);
|
|
}
|
|
else
|
|
{
|
|
return getSingleTargetDefenderResult(attackerData, defenderData, actionData, autoAim);
|
|
}
|
|
}
|
|
public int getAttackerResult(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException
|
|
{
|
|
if (combat.isAreaAttack(actionData.attackType))
|
|
{
|
|
return getAreaAttackResult(attackerData, defenderData, actionData, autoAim);
|
|
}
|
|
else
|
|
{
|
|
return getSingleTargetAttackResult(attackerData, defenderData, actionData, autoAim);
|
|
}
|
|
}
|
|
public int getAreaDefenderResult(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException
|
|
{
|
|
obj_id attacker = attackerData.id;
|
|
obj_id defender = defenderData.id;
|
|
if (movement.hasStunEffect(defenderData.id))
|
|
{
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
int glancingBlow = Math.round(10.0f * combat.getGlancingBlowChance(attackerData, defenderData, actionData));
|
|
int evasionChance = Math.round(10.0f * combat.getEvasionChance(attackerData, defenderData));
|
|
int blockChance = Math.round(10.0f * combat.getBlockChance(attackerData, defenderData, actionData));
|
|
int loaded_dice = getEnhancedSkillStatisticModifierUncapped(defenderData.id, "hit_table_add_defender_position");
|
|
int impossible_odds = getEnhancedSkillStatisticModifierUncapped(defenderData.id, "hit_table_defender_add_hit");
|
|
glancingBlow = glancingBlow < 0 ? 0 : glancingBlow;
|
|
evasionChance = evasionChance < 0 ? 0 : evasionChance;
|
|
blockChance = blockChance < 0 ? 0 : blockChance;
|
|
loaded_dice = loaded_dice < 0 ? 0 : loaded_dice;
|
|
impossible_odds = impossible_odds < 0 ? 0 : impossible_odds;
|
|
evasionChance = evasionChance >= 1000 ? 1000 : evasionChance;
|
|
blockChance = blockChance + evasionChance < 1000 ? blockChance + evasionChance : 1000;
|
|
glancingBlow = glancingBlow + blockChance < 1000 ? glancingBlow + blockChance : 1000;
|
|
impossible_odds = blockChance + impossible_odds < 1000 + impossible_odds ? blockChance + impossible_odds : 1000 + impossible_odds;
|
|
int defRollResult = rand(1, 1000) + loaded_dice;
|
|
if (defRollResult <= evasionChance)
|
|
{
|
|
return HIT_RESULT_EVADE;
|
|
}
|
|
if (defRollResult <= blockChance)
|
|
{
|
|
return HIT_RESULT_BLOCK;
|
|
}
|
|
if (defRollResult <= glancingBlow)
|
|
{
|
|
return HIT_RESULT_GLANCING;
|
|
}
|
|
if (defRollResult <= impossible_odds)
|
|
{
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
public int getAreaAttackResult(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException
|
|
{
|
|
obj_id attacker = attackerData.id;
|
|
obj_id defender = defenderData.id;
|
|
boolean attackCanPunish = actionData.canBePunishing == 1;
|
|
boolean mobCanPunish = utils.getIntScriptVar(attackerData.id, "combat.immune.no_punish") == 0 ? true : false;
|
|
int punishingBlow = 0;
|
|
if (attackCanPunish && mobCanPunish)
|
|
{
|
|
punishingBlow = Math.round(10.0f * combat.getPunishingBlowChance(attackerData, defenderData));
|
|
}
|
|
punishingBlow = punishingBlow < 0 ? 0 : punishingBlow;
|
|
int criticalChance = Math.round(10.0f * combat.getCriticalHitChance(attackerData, defenderData, actionData, autoAim));
|
|
criticalChance = criticalChance < 0 ? 0 : criticalChance;
|
|
int length = 1000 - punishingBlow < 0 ? 0 : 1000 - punishingBlow;
|
|
criticalChance = criticalChance >= length ? length : criticalChance;
|
|
punishingBlow += criticalChance;
|
|
int attackRollResult = rand(1, 1000);
|
|
if (attackRollResult <= criticalChance)
|
|
{
|
|
return HIT_RESULT_CRITICAL;
|
|
}
|
|
if (attackRollResult <= punishingBlow)
|
|
{
|
|
return HIT_RESULT_PUNISHING;
|
|
}
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
public int getSingleTargetDefenderResult(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException
|
|
{
|
|
obj_id attacker = attackerData.id;
|
|
obj_id defender = defenderData.id;
|
|
int loaded_dice = getEnhancedSkillStatisticModifierUncapped(defenderData.id, "hit_table_add_defender_position");
|
|
int impossible_odds = getEnhancedSkillStatisticModifierUncapped(defenderData.id, "hit_table_defender_add_hit");
|
|
int hit_always = getEnhancedSkillStatisticModifierUncapped(attacker, "hit_always");
|
|
if (hit_always > 0)
|
|
{
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
if (movement.hasStunEffect(defenderData.id))
|
|
{
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
weapon_data attackerWeapon = getWeaponData(getCurrentWeapon(attacker));
|
|
weapon_data defenderWeapon = getWeaponData(getCurrentWeapon(defender));
|
|
boolean isRangedAttacker = false;
|
|
boolean isRangedDefender = false;
|
|
boolean isLightSaber = false;
|
|
if (attackerWeapon != null)
|
|
{
|
|
isRangedAttacker = combat.isRangedWeapon(attackerWeapon.weaponType);
|
|
}
|
|
if (defenderWeapon != null)
|
|
{
|
|
isRangedDefender = combat.isRangedWeapon(defenderWeapon.weaponType);
|
|
isLightSaber = jedi.isLightsaber(defenderWeapon.id);
|
|
}
|
|
int glancingBlow = Math.round(10.0f * combat.getGlancingBlowChance(attackerData, defenderData, actionData));
|
|
glancingBlow = glancingBlow < 0 ? 0 : glancingBlow;
|
|
int dodgeChance = Math.round(10.0f * combat.getDodgeChance(attackerData, defenderData, actionData));
|
|
dodgeChance = dodgeChance < 0 ? 0 : dodgeChance;
|
|
int parryChance = Math.round(10.0f * combat.getParryChance(attackerData, defenderData, actionData));
|
|
parryChance = parryChance < 0 ? 0 : parryChance;
|
|
int blockChance = Math.round(10.0f * combat.getBlockChance(attackerData, defenderData, actionData));
|
|
blockChance = blockChance < 0 ? 0 : blockChance;
|
|
if (isRangedDefender || (isRangedAttacker && !isLightSaber))
|
|
{
|
|
parryChance = 0;
|
|
}
|
|
dodgeChance = dodgeChance >= 1000 ? 1000 : dodgeChance;
|
|
parryChance = dodgeChance + parryChance < 1000 ? dodgeChance + parryChance : 1000;
|
|
blockChance = blockChance + parryChance < 1000 ? blockChance + parryChance : 1000;
|
|
glancingBlow = glancingBlow + blockChance < 1000 ? glancingBlow + blockChance : 1000;
|
|
impossible_odds = blockChance + impossible_odds < 1000 + impossible_odds ? blockChance + impossible_odds : 1000 + impossible_odds;
|
|
int defRollResult = rand(1, 1000) + loaded_dice;
|
|
if (defRollResult <= dodgeChance)
|
|
{
|
|
return HIT_RESULT_DODGE;
|
|
}
|
|
if (defRollResult <= parryChance)
|
|
{
|
|
return HIT_RESULT_PARRY;
|
|
}
|
|
if (defRollResult <= blockChance)
|
|
{
|
|
return HIT_RESULT_BLOCK;
|
|
}
|
|
if (defRollResult <= glancingBlow)
|
|
{
|
|
return HIT_RESULT_GLANCING;
|
|
}
|
|
if (defRollResult <= impossible_odds)
|
|
{
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
public int getSingleTargetAttackResult(attacker_data attackerData, defender_data defenderData, combat_data actionData, boolean autoAim) throws InterruptedException
|
|
{
|
|
obj_id attacker = attackerData.id;
|
|
obj_id defender = defenderData.id;
|
|
int crit_always = getEnhancedSkillStatisticModifierUncapped(attacker, "crit_always");
|
|
if (crit_always > 0)
|
|
{
|
|
return HIT_RESULT_CRITICAL;
|
|
}
|
|
weapon_data attackerWeapon = getWeaponData(getCurrentWeapon(attacker));
|
|
weapon_data defenderWeapon = getWeaponData(getCurrentWeapon(defender));
|
|
boolean isRangedAttacker = false;
|
|
boolean isRangedDefender = false;
|
|
boolean isLightSaber = false;
|
|
if (attackerWeapon != null)
|
|
{
|
|
isRangedAttacker = combat.isRangedWeapon(attackerWeapon.weaponType);
|
|
}
|
|
if (defenderWeapon != null)
|
|
{
|
|
isRangedDefender = combat.isRangedWeapon(defenderWeapon.weaponType);
|
|
isLightSaber = jedi.isLightsaber(defenderWeapon.id);
|
|
}
|
|
int missChance = Math.round(10.0f * combat.getMissChance(attackerData, defenderData, actionData, autoAim));
|
|
missChance = missChance < 0 ? 0 : missChance;
|
|
int punishingBlow = Math.round(10.0f * combat.getPunishingBlowChance(attackerData, defenderData));
|
|
punishingBlow = punishingBlow < 0 ? 0 : punishingBlow;
|
|
int criticalChance = Math.round(10.0f * combat.getCriticalHitChance(attackerData, defenderData, actionData, autoAim));
|
|
criticalChance = criticalChance < 0 ? 0 : criticalChance;
|
|
int strikethroughChance = Math.round(10.0f * combat.getStrikethroughChance(attackerData, defenderData, actionData));
|
|
strikethroughChance = strikethroughChance < 0 ? 0 : strikethroughChance;
|
|
int length = 1000 - punishingBlow < 0 ? 0 : 1000 - punishingBlow;
|
|
missChance = missChance > length ? length : missChance;
|
|
strikethroughChance = strikethroughChance + missChance < length ? strikethroughChance + missChance : length;
|
|
criticalChance = criticalChance + strikethroughChance < length ? criticalChance + strikethroughChance : length;
|
|
punishingBlow += criticalChance;
|
|
int attackRollResult = rand(1, 1000);
|
|
int stvalue = strikethroughChance - missChance;
|
|
int critvalue = criticalChance - strikethroughChance;
|
|
if (attackRollResult <= missChance)
|
|
{
|
|
return HIT_RESULT_MISS;
|
|
}
|
|
if (attackRollResult <= strikethroughChance)
|
|
{
|
|
return HIT_RESULT_STRIKETHROUGH;
|
|
}
|
|
if (attackRollResult <= criticalChance)
|
|
{
|
|
return HIT_RESULT_CRITICAL;
|
|
}
|
|
if (attackRollResult <= punishingBlow)
|
|
{
|
|
return HIT_RESULT_PUNISHING;
|
|
}
|
|
return HIT_RESULT_HIT;
|
|
}
|
|
public void displayHitTable(obj_id attacker, obj_id defender, int[] defTable, int[] atkTable, int defResult, int atkResult) throws InterruptedException
|
|
{
|
|
if (!isGod(attacker) && !isGod(defender))
|
|
{
|
|
return;
|
|
}
|
|
int miss = 0;
|
|
int dodge = 0;
|
|
int parry = 0;
|
|
int glancing = 0;
|
|
int block = 0;
|
|
int critical = 0;
|
|
int punishing = 0;
|
|
int hit = 0;
|
|
int evade = 0;
|
|
int strikethrough = 0;
|
|
for (int i = 0; i < defTable.length; i++)
|
|
{
|
|
switch (defTable[i])
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
miss++;
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
dodge++;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
parry++;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
glancing++;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
block++;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
critical++;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
punishing++;
|
|
break;
|
|
case HIT_RESULT_HIT:
|
|
hit++;
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
evade++;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
strikethrough++;
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 0; i < atkTable.length; i++)
|
|
{
|
|
switch (atkTable[i])
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
miss++;
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
dodge++;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
parry++;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
glancing++;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
block++;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
critical++;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
punishing++;
|
|
break;
|
|
case HIT_RESULT_HIT:
|
|
hit++;
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
evade++;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
strikethrough++;
|
|
break;
|
|
}
|
|
}
|
|
if (isPlayer(attacker))
|
|
{
|
|
LOG("atkHitTable", "Forming Hit Table");
|
|
LOG("atkHitTable", getName(attacker) + " Vs. " + getName(defender));
|
|
LOG("atkHitTable", "Miss " + miss);
|
|
LOG("atkHitTable", "Dodge " + dodge);
|
|
LOG("atkHitTable", "Parry " + parry);
|
|
LOG("atkHitTable", "Glancing " + glancing);
|
|
LOG("atkHitTable", "Block " + block);
|
|
LOG("atkHitTable", "Critical " + critical);
|
|
LOG("atkHitTable", "Punishing " + punishing);
|
|
LOG("atkHitTable", "Hit " + hit);
|
|
LOG("atkHitTable", "Evade " + evade);
|
|
LOG("atkHitTable", "Strikethrough " + strikethrough);
|
|
LOG("atkHitTable", "Defender Result was " + getHitResultByName(defResult));
|
|
LOG("atkHitTable", "Attacker Results was " + getHitResultByName(atkResult));
|
|
LOG("atkHitTable", "xx");
|
|
}
|
|
if (!isPlayer(attacker))
|
|
{
|
|
LOG("defHitTabel", "Forming Hit Table");
|
|
LOG("defHitTable", getName(attacker) + " Vs. " + getName(defender));
|
|
LOG("defHitTable", "Miss " + miss);
|
|
LOG("defHitTable", "Dodge " + dodge);
|
|
LOG("defHitTable", "Parry " + parry);
|
|
LOG("defHitTable", "Glancing " + glancing);
|
|
LOG("defHitTable", "Block " + block);
|
|
LOG("defHitTable", "Critical " + critical);
|
|
LOG("defHitTable", "Punishing " + punishing);
|
|
LOG("defHitTable", "Hit " + hit);
|
|
LOG("defHitTable", "Evade " + evade);
|
|
LOG("defHitTable", "Strikethrough " + strikethrough);
|
|
LOG("defHitTable", "Defender Result was " + getHitResultByName(defResult));
|
|
LOG("defHitTable", "Attacker Results was " + getHitResultByName(atkResult));
|
|
LOG("defHitTable", "xx");
|
|
}
|
|
if (hasObjVar(attacker, "viewHitTable"))
|
|
{
|
|
sendSystemMessageTestingOnly(attacker, "Forming Hit Table");
|
|
sendSystemMessageTestingOnly(attacker, getName(attacker) + " Attacking. " + getName(defender));
|
|
sendSystemMessageTestingOnly(attacker, "Miss " + miss);
|
|
sendSystemMessageTestingOnly(attacker, "Dodge " + dodge);
|
|
sendSystemMessageTestingOnly(attacker, "Parry " + parry);
|
|
sendSystemMessageTestingOnly(attacker, "Glancing " + glancing);
|
|
sendSystemMessageTestingOnly(attacker, "Block " + block);
|
|
sendSystemMessageTestingOnly(attacker, "Critical " + critical);
|
|
sendSystemMessageTestingOnly(attacker, "Punishing " + punishing);
|
|
sendSystemMessageTestingOnly(attacker, "Hit " + hit);
|
|
sendSystemMessageTestingOnly(attacker, "Evade " + evade);
|
|
sendSystemMessageTestingOnly(attacker, "Strikethrough " + strikethrough);
|
|
sendSystemMessageTestingOnly(attacker, "" + getHitResultByName(defResult));
|
|
sendSystemMessageTestingOnly(attacker, "" + getHitResultByName(atkResult));
|
|
sendSystemMessageTestingOnly(attacker, "xx");
|
|
}
|
|
if (hasObjVar(defender, "viewHitTable"))
|
|
{
|
|
sendSystemMessageTestingOnly(defender, "Forming Hit Table");
|
|
sendSystemMessageTestingOnly(defender, getName(attacker) + " Attacking. " + getName(defender));
|
|
sendSystemMessageTestingOnly(defender, "Miss " + miss);
|
|
sendSystemMessageTestingOnly(defender, "Dodge " + dodge);
|
|
sendSystemMessageTestingOnly(defender, "Parry " + parry);
|
|
sendSystemMessageTestingOnly(defender, "Glancing " + glancing);
|
|
sendSystemMessageTestingOnly(defender, "Block " + block);
|
|
sendSystemMessageTestingOnly(defender, "Critical " + critical);
|
|
sendSystemMessageTestingOnly(defender, "Punishing " + punishing);
|
|
sendSystemMessageTestingOnly(defender, "Hit " + hit);
|
|
sendSystemMessageTestingOnly(defender, "Evade " + evade);
|
|
sendSystemMessageTestingOnly(defender, "Strikethrough " + strikethrough);
|
|
sendSystemMessageTestingOnly(defender, "" + getHitResultByName(defResult));
|
|
sendSystemMessageTestingOnly(defender, "" + getHitResultByName(atkResult));
|
|
sendSystemMessageTestingOnly(defender, "xx");
|
|
}
|
|
}
|
|
public String getHitResultByName(int hitResult) throws InterruptedException
|
|
{
|
|
String hitType = "None";
|
|
switch (hitResult)
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
hitType = "Miss";
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
hitType = "Dodge";
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
hitType = "Parry";
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
hitType = "Glancing";
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
hitType = "Block";
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
hitType = "Critical";
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
hitType = "Punishing";
|
|
break;
|
|
case HIT_RESULT_HIT:
|
|
hitType = "Hit";
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
hitType = "Evade";
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
hitType = "Strikethrough";
|
|
break;
|
|
}
|
|
return hitType;
|
|
}
|
|
public hit_result applyDamage(obj_id attacker, obj_id defender, weapon_data weaponData, hit_result hitData, combat_data actionData, boolean primaryTarget) throws InterruptedException
|
|
{
|
|
return applyDamage(attacker, defender, weaponData, hitData, actionData, primaryTarget, 0);
|
|
}
|
|
public hit_result applyDamage(obj_id attacker, obj_id defender, weapon_data weaponData, hit_result hitData, combat_data actionData, boolean primaryTarget, int overloadDamage) throws InterruptedException
|
|
{
|
|
doWrappedDamage(attacker, defender, weaponData, hitData, actionData, overloadDamage);
|
|
return hitData;
|
|
}
|
|
public weapon_data getOverloadedWeaponData(obj_id self, obj_id objWeapon, combat_data actionData) throws InterruptedException
|
|
{
|
|
if (!isIdValid(self))
|
|
{
|
|
return null;
|
|
}
|
|
if (!isIdValid(objWeapon))
|
|
{
|
|
return getWeaponData(getCurrentWeapon(self));
|
|
}
|
|
if (actionData.overloadWeapon > 0)
|
|
{
|
|
return weapons.fillWeaponData(objWeapon, actionData);
|
|
}
|
|
weapon_data weaponData = weapons.getNewWeaponData(objWeapon);
|
|
if (weaponData == null)
|
|
{
|
|
CustomerServiceLog("combat_errors: ", "getOverloadedWeaponData weaponData: null for objWeapon: " + objWeapon + " getName(objWeapon): " + getName(objWeapon));
|
|
weaponData = getWeaponData(getCurrentWeapon(self));
|
|
}
|
|
else
|
|
{
|
|
weaponData.elementalValue = isPlayer(self) ? weaponData.elementalValue * 2 : weaponData.elementalValue;
|
|
}
|
|
return weaponData;
|
|
}
|
|
public dictionary getRawDamage(obj_id attacker, weapon_data weaponData, combat_data actionData, hit_result hitData, float minDamage, float maxDamage, int numTargets) throws InterruptedException
|
|
{
|
|
int addedDamage = actionData.addedDamage;
|
|
if (actionData.attackType == combat.AREA_PROGRESSIVE)
|
|
{
|
|
addedDamage += utils.getIntScriptVar(attacker, PROGRESSIVE_DAMAGE_COUNTER);
|
|
utils.setScriptVar(attacker, PROGRESSIVE_DAMAGE_COUNTER, addedDamage);
|
|
}
|
|
if (actionData.attackType == combat.SPLIT_DAMAGE_TARGET_AREA)
|
|
{
|
|
addedDamage = Math.round((float)addedDamage / (float)numTargets);
|
|
}
|
|
float percentAddFromWeapon = actionData.percentAddFromWeapon;
|
|
if (!combat.checkWeaponCerts(attacker, weaponData, false))
|
|
{
|
|
combat.combatLog(attacker, null, "getRawDamage", "Weapon Certification Failed. Modifying damage to 5 min, 10 max.");
|
|
minDamage = 5.0f;
|
|
maxDamage = 10.0f;
|
|
}
|
|
if (beast_lib.isBeastMaster(attacker) || beast_lib.isBeast(attacker))
|
|
{
|
|
minDamage = minDamage + (minDamage * (beast_lib.getBeastModDamageBonus(attacker) / 100.0f));
|
|
maxDamage = maxDamage + (maxDamage * (beast_lib.getBeastModDamageBonus(attacker) / 100.0f));
|
|
}
|
|
combat.combatLog(attacker, null, "getRawDamage", "RAW DAMAGE Damage = " + minDamage + "-" + maxDamage);
|
|
float speed = weaponData.attackSpeed;
|
|
if (speed == 0 || (isMob(attacker) && beast_lib.isBeast(attacker)))
|
|
{
|
|
speed = 1.0f;
|
|
}
|
|
minDamage *= percentAddFromWeapon;
|
|
maxDamage *= percentAddFromWeapon;
|
|
if (addedDamage > 0)
|
|
{
|
|
float dps = ((minDamage + maxDamage) / 2.0f) / speed;
|
|
float totalAddedDamage = dps + addedDamage;
|
|
float normalizedSpecialDmg = totalAddedDamage - ((minDamage + maxDamage) / 2);
|
|
minDamage += normalizedSpecialDmg;
|
|
maxDamage += normalizedSpecialDmg;
|
|
}
|
|
float damageAddFromLevel = (float)getEnhancedSkillStatisticModifierUncapped(attacker, "level_add_to_damage");
|
|
if (damageAddFromLevel > 0.0f)
|
|
{
|
|
float levelDamageMod = getLevel(attacker) * (damageAddFromLevel / 100.0f);
|
|
minDamage += levelDamageMod * speed;
|
|
maxDamage += levelDamageMod * speed;
|
|
}
|
|
if (getWeaponType(weaponData.id) >= 0)
|
|
{
|
|
if (combat.isMeleeWeapon(weaponData.id) || combat.isLightsaberWeapon(weaponData.id))
|
|
{
|
|
int strengthBonus = 0;
|
|
strengthBonus += Math.round((float)getEnhancedSkillStatisticModifierUncapped(attacker, "strength_modified") / 3.0f);
|
|
strengthBonus += Math.round((float)getSkillStatisticModifier(attacker, "strength") / 3.0f);
|
|
minDamage += strengthBonus;
|
|
maxDamage += strengthBonus;
|
|
}
|
|
}
|
|
float enragedMod = utils.getFloatScriptVar(attacker, "damageDealtMod.value");
|
|
if (enragedMod > 0)
|
|
{
|
|
minDamage *= enragedMod;
|
|
maxDamage *= enragedMod;
|
|
}
|
|
combat.combatLog(attacker, null, "getRawDamage", "Final Modified Damage = " + minDamage + "-" + maxDamage);
|
|
if (minDamage > maxDamage - 1)
|
|
{
|
|
minDamage = maxDamage - 1;
|
|
}
|
|
if (minDamage < 1)
|
|
{
|
|
minDamage = 1;
|
|
}
|
|
if (percentAddFromWeapon == 0.0)
|
|
{
|
|
weaponData.elementalValue = 0;
|
|
}
|
|
if (utils.hasScriptVar(attacker, "private_damage_bonus"))
|
|
{
|
|
minDamage = minDamage * (1 + utils.getFloatScriptVar(attacker, "private_damage_bonus"));
|
|
maxDamage = maxDamage * (1 + utils.getFloatScriptVar(attacker, "private_damage_bonus"));
|
|
}
|
|
dictionary dict = new dictionary();
|
|
dict.put("minDamage", minDamage);
|
|
dict.put("maxDamage", maxDamage);
|
|
return dict;
|
|
}
|
|
public int expertiseDamageModify(obj_id attacker, obj_id defender, hit_result hitData, combat_data actionData) throws InterruptedException
|
|
{
|
|
int newDamage = hitData.damage;
|
|
int damageDecrease = getSkillStatisticModifier(attacker, "expertise_damage_decrease_chance");
|
|
if (damageDecrease > 0)
|
|
{
|
|
float spotASuckerDecreaseChance = rand(0, damageDecrease) * 0.01f;
|
|
if (spotASuckerDecreaseChance > 1.0f)
|
|
{
|
|
spotASuckerDecreaseChance = 1.0f;
|
|
}
|
|
newDamage *= 1.0f - spotASuckerDecreaseChance;
|
|
}
|
|
float smugglerRankBonus = (float)getSkillStatisticModifier(attacker, "expertise_sm_rank_damage_bonus");
|
|
if (smugglerRankBonus > 0)
|
|
{
|
|
smugglerRankBonus *= 0.01f;
|
|
newDamage += newDamage * smugglerRankBonus;
|
|
}
|
|
int jediAnticipateAggression = getSkillStatisticModifier(defender, "expertise_damage_reduce_anticipate_aggression");
|
|
if (jediAnticipateAggression > 0)
|
|
{
|
|
float conversionPercentage = jediAnticipateAggression / 100.0f;
|
|
int oldDamage = hitData.damage;
|
|
newDamage -= (int)(oldDamage * conversionPercentage);
|
|
if (rand(0, 9) < 1)
|
|
{
|
|
playClientEffectObj(defender, "appearance/pt_jedi_anticipate_aggression.prt", defender, "root");
|
|
}
|
|
}
|
|
damageDecrease = getSkillStatisticModifier(defender, "damage_decrease_percentage");
|
|
if (damageDecrease > 0)
|
|
{
|
|
newDamage -= (int)(hitData.damage * (float)((float)damageDecrease / 100.0f));
|
|
}
|
|
int areaDamageDecrease = getSkillStatisticModifier(defender, "area_damage_decrease_percentage");
|
|
if (areaDamageDecrease > 0 && combat.isAreaAttack(actionData.attackType))
|
|
{
|
|
newDamage -= (int)(hitData.damage * (float)((float)areaDamageDecrease / 100.0f));
|
|
}
|
|
int areaDamageResist = getSkillStatisticModifier(defender, "area_damage_resist_full_percentage");
|
|
if (areaDamageResist > 0 && combat.isAreaAttack(actionData.attackType) && rand(0, 99) < areaDamageResist)
|
|
{
|
|
newDamage = 0;
|
|
}
|
|
if (hitData.critDamage > 0)
|
|
{
|
|
if (utils.hasScriptVar(attacker, "critDoubleDamage") && actionData.commandType != combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
newDamage *= 2;
|
|
playClientEffectObj(defender, "appearance/pt_smuggler_end_line.prt", defender, "");
|
|
}
|
|
if (utils.hasScriptVar(attacker, "critRoot") && actionData.commandType != combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
buff.applyBuff(defender, attacker, "sm_nerf_herder_root");
|
|
}
|
|
}
|
|
newDamage = ai_lib.triggerSharedHealthLink(defender, newDamage);
|
|
return newDamage;
|
|
}
|
|
public void doWrappedDamage(obj_id attacker, obj_id defender, weapon_data weaponData, hit_result hitData, combat_data actionData) throws InterruptedException
|
|
{
|
|
doWrappedDamage(attacker, defender, weaponData, hitData, actionData, 0);
|
|
}
|
|
public void clog(String message) throws InterruptedException
|
|
{
|
|
debugSpeakMsg(getSelf(), message);
|
|
}
|
|
public void doWrappedDamage(obj_id attacker, obj_id defender, weapon_data weaponData, hit_result hitData, combat_data actionData, int overloadDamage) throws InterruptedException
|
|
{
|
|
boolean dmgSpecial = false;
|
|
boolean evade = hitData.evadeResult;
|
|
boolean block = hitData.blockResult;
|
|
float armorBypass = actionData.bypassArmor;
|
|
float siphonLife = actionData.convertDamageToHealth;
|
|
hitData.rawDamage = hitData.damage;
|
|
int maxDamage = hitData.rawDamage + weaponData.elementalValue;
|
|
String dotType = actionData.dotType;
|
|
if (utils.hasScriptVar(defender, "elemental_vulnerability.type_heat"))
|
|
{
|
|
float value = utils.getFloatScriptVar(defender, "elemental_vulnerability.type_heat.value");
|
|
boolean exclusive = utils.getBooleanScriptVar(defender, "elemental_vulnerability.type_heat.exclusive");
|
|
if (exclusive)
|
|
{
|
|
hitData.rawDamage = 0;
|
|
maxDamage = 0;
|
|
}
|
|
if (weaponData.elementalType == DAMAGE_ELEMENTAL_HEAT)
|
|
{
|
|
weaponData.elementalValue *= value;
|
|
}
|
|
else if (exclusive)
|
|
{
|
|
weaponData.elementalValue *= 0.0f;
|
|
}
|
|
}
|
|
if (utils.hasScriptVar(defender, "elemental_vulnerability.type_electrical"))
|
|
{
|
|
float value = utils.getFloatScriptVar(defender, "elemental_vulnerability.type_electrical.value");
|
|
boolean exclusive = utils.getBooleanScriptVar(defender, "elemental_vulnerability.type_electrical.exclusive");
|
|
if (exclusive)
|
|
{
|
|
hitData.rawDamage = 0;
|
|
maxDamage = 0;
|
|
}
|
|
if (weaponData.elementalType == DAMAGE_ELEMENTAL_ELECTRICAL)
|
|
{
|
|
weaponData.elementalValue *= value;
|
|
}
|
|
else if (exclusive)
|
|
{
|
|
weaponData.elementalValue *= 0.0f;
|
|
}
|
|
}
|
|
if (utils.hasScriptVar(defender, "elemental_vulnerability.type_cold"))
|
|
{
|
|
float value = utils.getFloatScriptVar(defender, "elemental_vulnerability.type_cold.value");
|
|
boolean exclusive = utils.getBooleanScriptVar(defender, "elemental_vulnerability.type_cold.exclusive");
|
|
if (exclusive)
|
|
{
|
|
hitData.rawDamage = 0;
|
|
maxDamage = 0;
|
|
}
|
|
if (weaponData.elementalType == DAMAGE_ELEMENTAL_COLD)
|
|
{
|
|
weaponData.elementalValue *= value;
|
|
}
|
|
else if (exclusive)
|
|
{
|
|
weaponData.elementalValue *= 0.0f;
|
|
}
|
|
}
|
|
if (utils.hasScriptVar(defender, "elemental_vulnerability.type_acid"))
|
|
{
|
|
float value = utils.getFloatScriptVar(defender, "elemental_vulnerability.type_acid.value");
|
|
boolean exclusive = utils.getBooleanScriptVar(defender, "elemental_vulnerability.type_acid.exclusive");
|
|
if (exclusive)
|
|
{
|
|
hitData.rawDamage = 0;
|
|
maxDamage = 0;
|
|
}
|
|
if (weaponData.elementalType == DAMAGE_ELEMENTAL_ACID)
|
|
{
|
|
weaponData.elementalValue *= value;
|
|
}
|
|
else if (exclusive)
|
|
{
|
|
weaponData.elementalValue *= 0.0f;
|
|
}
|
|
}
|
|
if (beast_lib.isBeastMaster(defender) || beast_lib.isBeast(defender))
|
|
{
|
|
hitData.rawDamage = hitData.rawDamage - (int)(hitData.rawDamage * (beast_lib.getBeastModDamageReduction(defender) / 100.0f));
|
|
}
|
|
int evadeReduction = 0;
|
|
if (hitData.evadeResult)
|
|
{
|
|
float evadeValue = hitData.evadeAmmount;
|
|
if (evadeValue == -1)
|
|
{
|
|
hitData.rawDamage = 0;
|
|
weaponData.elementalValue = 0;
|
|
hitData.evadeAmmount = 100.0f;
|
|
dotType = null;
|
|
}
|
|
else
|
|
{
|
|
float percentReduction = rand(evadeValue / 2.0f, evadeValue);
|
|
evadeReduction = (int)(hitData.rawDamage * (percentReduction / 100.0f));
|
|
weaponData.elementalValue = (int)(weaponData.elementalValue * (percentReduction / 100.0f));
|
|
hitData.rawDamage -= evadeReduction;
|
|
hitData.evadeAmmount = percentReduction;
|
|
}
|
|
}
|
|
if (dotType != null && dotType.length() > 0)
|
|
{
|
|
int dotIntensity = actionData.dotIntensity;
|
|
int dotDuration = actionData.dotDuration;
|
|
if (dotIntensity == 0)
|
|
{
|
|
dotIntensity = (int)(hitData.rawDamage * actionData.percentAddFromWeapon);
|
|
}
|
|
String specialName = actionData.actionName;
|
|
String specialLine = actionData.specialLine;
|
|
int expertiseDotDurationBonus = 0;
|
|
float expertiseDotDamageBonus = getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_damage_all");
|
|
if (isMob(defender) && !isPlayer(defender) && !isIdValid(getMaster(defender)))
|
|
{
|
|
expertiseDotDamageBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_damage_npc");
|
|
}
|
|
if (specialLine != null && specialLine.length() > 0)
|
|
{
|
|
expertiseDotDurationBonus = getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_dot_duration_line_" + specialLine);
|
|
expertiseDotDurationBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_dot_duration_single_" + specialName);
|
|
expertiseDotDamageBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_dot_damage_line_" + specialLine);
|
|
}
|
|
float dotIntensityFloat = (float)dotIntensity * (1.0f + (expertiseDotDamageBonus / 100.0f));
|
|
dotIntensity = (int)dotIntensityFloat;
|
|
dotDuration += expertiseDotDurationBonus;
|
|
int targetAttrib = dotType.equals("disease") ? ACTION : HEALTH;
|
|
boolean rslt = dot.applyDotEffect(defender, attacker, dotType, dotType + "" + attacker, targetAttrib, 100, dotIntensity, dotDuration);
|
|
}
|
|
hitData.damage = (hitData.rawDamage);
|
|
hitData.damageType = weaponData.damageType;
|
|
if (actionData.overloadWeapon > 0)
|
|
{
|
|
hitData.damageType = actionData.overloadWeaponDamageType;
|
|
}
|
|
hitData.damage = expertiseDamageModify(attacker, defender, hitData, actionData);
|
|
float expertiseDamageBonus = 0;
|
|
obj_id expert = attacker;
|
|
if (!isPlayer(attacker) && utils.hasScriptVar(attacker, "objOwner"))
|
|
{
|
|
expert = utils.getObjIdObjVar(attacker, "objOwner");
|
|
}
|
|
int killMeter = getKillMeter(expert);
|
|
if (actionData.percentAddFromWeapon > 0.0f)
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_weapon_all");
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_weapon_" + combat.getCorrectedWeaponType(weaponData.weaponType));
|
|
expertiseDamageBonus += killMeter * getSkillStatisticModifier(expert, "kill_meter_damage_weapon_all");
|
|
}
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_all");
|
|
if (isMob(defender) && !isPlayer(defender) && !isIdValid(getMaster(defender)))
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "expertise_damage_npc");
|
|
}
|
|
if (getWeaponType(weaponData.id) >= 0)
|
|
{
|
|
if ((combat.isMeleeWeapon(weaponData.id)) || (combat.isLightsaberWeapon(weaponData.id)))
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_melee");
|
|
}
|
|
if (combat.isRangedWeapon(weaponData.id))
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_ranged");
|
|
}
|
|
}
|
|
String specialLine = actionData.specialLine;
|
|
String actionName = actionData.actionName;
|
|
if (specialLine != null && !specialLine.equals(""))
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(defender, "expertise_damage_line_vulnerability_" + specialLine);
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_line_" + specialLine);
|
|
expertiseDamageBonus += killMeter * getSkillStatisticModifier(expert, "kill_meter_damage_line_" + specialLine);
|
|
}
|
|
if (actionName != null && !actionName.equals(""))
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_single_" + actionName);
|
|
}
|
|
if (actionName != null && !actionName.equals(""))
|
|
{
|
|
if (combat.isAreaAttack(actionData.attackType))
|
|
{
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "expertise_damage_area_effect");
|
|
}
|
|
}
|
|
if (hasObjVar(attacker, "specialNicheType"))
|
|
{
|
|
String subType = getStringObjVar(attacker, "specialNicheType");
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(defender, "increase_damage_taken_from_" + subType);
|
|
expertiseDamageBonus -= getEnhancedSkillStatisticModifierUncapped(defender, "reduce_damage_taken_from_" + subType);
|
|
}
|
|
if (hasObjVar(defender, "specialNicheType"))
|
|
{
|
|
String subType = getStringObjVar(defender, "specialNicheType");
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(attacker, "increase_damage_dealt_to_" + subType);
|
|
expertiseDamageBonus -= getEnhancedSkillStatisticModifierUncapped(attacker, "reduce_damage_dealt_to_" + subType);
|
|
}
|
|
if (overloadDamage > 0)
|
|
{
|
|
hitData.damage = overloadDamage;
|
|
armorBypass = 100.0f;
|
|
}
|
|
if (actionData.scaleByDefenderWeaponSpeed > 0)
|
|
{
|
|
obj_id defenderWeapon = getCurrentWeapon(defender);
|
|
if (isIdValid(defenderWeapon))
|
|
{
|
|
float defenderWeaponSpeed = getWeaponAttackSpeed(getCurrentWeapon(defender));
|
|
hitData.damage *= actionData.scaleByDefenderWeaponSpeed * defenderWeaponSpeed;
|
|
}
|
|
}
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(expert, "combat_multiply_damage_dealt");
|
|
expertiseDamageBonus += getEnhancedSkillStatisticModifierUncapped(defender, "combat_multiply_damage_taken");
|
|
float tempDamageFloat = (float)hitData.damage;
|
|
tempDamageFloat = tempDamageFloat * (1.0f + ((float)expertiseDamageBonus / 100.0f));
|
|
int expertiseDamageReduction = 0;
|
|
expertiseDamageReduction += getEnhancedSkillStatisticModifierUncapped(expert, "combat_divide_damage_dealt");
|
|
expertiseDamageReduction += getEnhancedSkillStatisticModifierUncapped(defender, "combat_divide_damage_taken");
|
|
expertiseDamageReduction = expertiseDamageReduction > 100 ? 100 : expertiseDamageReduction;
|
|
tempDamageFloat = tempDamageFloat * (1.0f - ((float)expertiseDamageReduction / 100.0f));
|
|
hitData.damage = (int)tempDamageFloat;
|
|
hitData.damage += getEnhancedSkillStatisticModifierUncapped(expert, "combat_add_damage_dealt");
|
|
hitData.damage -= getEnhancedSkillStatisticModifierUncapped(expert, "combat_subtract_damage_dealt");
|
|
hitData.damage += getEnhancedSkillStatisticModifierUncapped(defender, "combat_add_damage_taken");
|
|
hitData.damage -= getEnhancedSkillStatisticModifierUncapped(defender, "combat_subtract_damage_taken");
|
|
if (isPlayer(attacker))
|
|
{
|
|
session.logActivity(attacker, isPlayer(defender) ? session.ACTIVITY_PVP : session.ACTIVITY_PVE);
|
|
}
|
|
hitData.blockedDamage += combat.applyArmorProtection(attacker, defender, weaponData, hitData, armorBypass, expertiseDamageBonus);
|
|
if (hitData.damage < 0)
|
|
{
|
|
hitData.damage = 0;
|
|
}
|
|
healing.applyLifeSiphonHeal(attacker, defender, hitData.damage, siphonLife, actionData.actionName);
|
|
int addDefenderDamageToAction = getSkillStatisticModifier(defender, "expertise_damage_add_to_action");
|
|
if (addDefenderDamageToAction > 0)
|
|
{
|
|
int actionToAdd = (int)(hitData.damage * (float)(addDefenderDamageToAction / 100.0f));
|
|
combat.gainCombatActionAttribute(defender, actionToAdd);
|
|
if (rand(0, 9) < 1)
|
|
{
|
|
playClientEffectObj(defender, "appearance/pt_jedi_reactive_response.prt", defender, "root");
|
|
}
|
|
}
|
|
int addAttackerDamageToAction = getSkillStatisticModifier(attacker, "expertise_attacker_action_gain");
|
|
if (addAttackerDamageToAction > 0)
|
|
{
|
|
int actionToAdd = (int)(hitData.damage * (float)(addAttackerDamageToAction / 100.0f));
|
|
combat.gainCombatActionAttribute(attacker, actionToAdd);
|
|
if (rand(0, 9) < 1)
|
|
{
|
|
playClientEffectObj(attacker, "appearance/pt_jedi_tempt_hatred.prt", attacker, "");
|
|
}
|
|
}
|
|
target_dummy.sendTargetDummyCombatData(attacker, defender, actionData, hitData);
|
|
doDamage(attacker, defender, hitData);
|
|
int expertiseActionDamage = 0;
|
|
if (specialLine != null && specialLine.length() > 0)
|
|
{
|
|
expertiseActionDamage = getSkillStatisticModifier(attacker, "expertise_action_damage_line_" + specialLine);
|
|
}
|
|
int flatActionDamage = getAction(defender) < actionData.flatActionDamage + expertiseActionDamage ? getAction(defender) : actionData.flatActionDamage + expertiseActionDamage;
|
|
if (flatActionDamage > 0)
|
|
{
|
|
drainAttributes(defender, flatActionDamage, 0);
|
|
}
|
|
doKillMeterUpdate(attacker, defender, hitData.damage);
|
|
if (isPlayer(attacker))
|
|
{
|
|
string_id strSpam = new string_id("combat_effects", "damage_fly");
|
|
prose_package pp = new prose_package();
|
|
pp = prose.setStringId(pp, strSpam);
|
|
pp = prose.setDI(pp, -(hitData.damage + hitData.elementalDamage));
|
|
String strName = actionData.actionName;
|
|
float textSize = 1.5f;
|
|
color combatColor = colors.RED;
|
|
if ((strName.equals("meleeHit")) || (strName.equals("rangedShot")) || (strName.equals("saberHit")))
|
|
{
|
|
if (hitData.critDamage > 0 && hitData.critDamage < 7)
|
|
{
|
|
string_id critSpam = new string_id("combat_effects", "critical_hit_" + hitData.critDamage);
|
|
prose_package ppc = new prose_package();
|
|
ppc = prose.setStringId(ppc, critSpam);
|
|
ppc = prose.setDI(ppc, -(hitData.damage + hitData.elementalDamage));
|
|
showFlyTextPrivateProseWithFlags(defender, attacker, ppc, 1.5f, combat.getCriticalColor(hitData.critDamage), FLY_TEXT_FLAG_IS_DAMAGE_FROM_PLAYER);
|
|
}
|
|
else
|
|
{
|
|
showFlyTextPrivateProseWithFlags(defender, attacker, pp, textSize, combatColor, FLY_TEXT_FLAG_IS_DAMAGE_FROM_PLAYER);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hitData.critDamage > 0 && hitData.critDamage < 7)
|
|
{
|
|
string_id critSpam = new string_id("combat_effects", "critical_hit_" + hitData.critDamage);
|
|
prose_package ppc = new prose_package();
|
|
ppc = prose.setStringId(ppc, critSpam);
|
|
ppc = prose.setDI(ppc, -(hitData.damage + hitData.elementalDamage));
|
|
showFlyTextPrivateProseWithFlags(defender, attacker, ppc, 1.5f, combat.getCriticalColor(hitData.critDamage), FLY_TEXT_FLAG_IS_DAMAGE_FROM_PLAYER);
|
|
}
|
|
else
|
|
{
|
|
showFlyTextPrivateProseWithFlags(defender, attacker, pp, 1.5f, colors.DARKORANGE, FLY_TEXT_FLAG_IS_DAMAGE_FROM_PLAYER);
|
|
}
|
|
}
|
|
}
|
|
if (beast_lib.isBeast(defender) || pet_lib.isPet(defender))
|
|
{
|
|
obj_id master = getMaster(defender);
|
|
if (isIdValid(master) && exists(master))
|
|
{
|
|
string_id strSpam = null;
|
|
color combatHue = colors.YELLOW;
|
|
if (hitData.critDamage > 0 && hitData.critDamage < 7)
|
|
{
|
|
strSpam = new string_id("combat_effects", "critical_hit_" + hitData.critDamage);
|
|
combatHue = combat.getCriticalColor(hitData.critDamage);
|
|
}
|
|
else
|
|
{
|
|
strSpam = new string_id("combat_effects", "damage_fly");
|
|
}
|
|
prose_package pp = new prose_package();
|
|
pp = prose.setStringId(pp, strSpam);
|
|
pp = prose.setDI(pp, -(hitData.damage + hitData.elementalDamage));
|
|
showFlyTextPrivate(defender, master, pp, 1.5f, combatHue);
|
|
}
|
|
}
|
|
if (beast_lib.isBeast(attacker) || pet_lib.isPet(attacker))
|
|
{
|
|
obj_id master = getMaster(attacker);
|
|
if (isIdValid(master) && exists(master))
|
|
{
|
|
string_id strSpam = null;
|
|
color combatHue = colors.RED;
|
|
if (hitData.critDamage > 0 && hitData.critDamage < 7)
|
|
{
|
|
strSpam = new string_id("combat_effects", "critical_hit_" + hitData.critDamage);
|
|
combatHue = combat.getCriticalColor(hitData.critDamage);
|
|
}
|
|
else
|
|
{
|
|
strSpam = new string_id("combat_effects", "damage_fly");
|
|
}
|
|
prose_package pp = new prose_package();
|
|
pp = prose.setStringId(pp, strSpam);
|
|
pp = prose.setDI(pp, -(hitData.damage + hitData.elementalDamage));
|
|
showFlyTextPrivate(defender, master, pp, 1.5f, combatHue);
|
|
}
|
|
}
|
|
combat.assignDamageCredit(attacker, defender, weaponData, hitData.damage);
|
|
}
|
|
public obj_id[] truncateTargetArray(obj_id[] array, obj_id target, int size) throws InterruptedException
|
|
{
|
|
if (array.length <= size + 1)
|
|
{
|
|
return array;
|
|
}
|
|
obj_id[] truncArray = new obj_id[size];
|
|
truncArray[0] = target;
|
|
int source = 0;
|
|
int dest = 1;
|
|
while (source < size)
|
|
{
|
|
if (array[source] != target)
|
|
{
|
|
truncArray[dest] = array[source];
|
|
dest++;
|
|
}
|
|
source++;
|
|
}
|
|
return truncArray;
|
|
}
|
|
public void sortDefendersByPlayer(obj_id[] defenders) throws InterruptedException
|
|
{
|
|
Vector playerList = new Vector();
|
|
playerList.setSize(0);
|
|
Vector objectList = new Vector();
|
|
objectList.setSize(0);
|
|
for (int i = 0; i < defenders.length; i++)
|
|
{
|
|
if (isPlayer(defenders[i]))
|
|
{
|
|
utils.addElement(playerList, defenders[i]);
|
|
}
|
|
else
|
|
{
|
|
utils.addElement(objectList, defenders[i]);
|
|
}
|
|
}
|
|
if ((playerList.size() + objectList.size()) == defenders.length)
|
|
{
|
|
obj_id[] tempPlayerList = new obj_id[0];
|
|
if (playerList != null)
|
|
{
|
|
tempPlayerList = new obj_id[playerList.size()];
|
|
playerList.toArray(tempPlayerList);
|
|
}
|
|
obj_id[] tempObjectList = new obj_id[0];
|
|
if (objectList != null)
|
|
{
|
|
tempObjectList = new obj_id[objectList.size()];
|
|
objectList.toArray(tempObjectList);
|
|
}
|
|
defenders = utils.concatArrays(tempPlayerList, tempObjectList);
|
|
}
|
|
}
|
|
public obj_id[] validateDefenders(obj_id attacker, obj_id target, obj_id[] defenders, obj_id source, int maxDefenders, int validTargetType, boolean isOffensiveAction, obj_id losSourceObj, boolean isHeavyWeapon, boolean isSpecialAttack, boolean ignoreLOS, int pvpOnly, combat_data actionData) throws InterruptedException
|
|
{
|
|
Vector validDefenders = new Vector();
|
|
validDefenders.setSize(0);
|
|
if (defenders == null || defenders.length == 0)
|
|
{
|
|
obj_id[] _validDefenders = new obj_id[0];
|
|
if (validDefenders != null)
|
|
{
|
|
_validDefenders = new obj_id[validDefenders.size()];
|
|
validDefenders.toArray(_validDefenders);
|
|
}
|
|
return _validDefenders;
|
|
}
|
|
combat.combatLog(attacker, target, "validateDefenders", "Attempting to put target at start of list");
|
|
if (isIdValid(target) && (!isOffensiveAction || attacker != target))
|
|
{
|
|
if (!factions.ignorePlayer(attacker, target))
|
|
{
|
|
obj_id pvpAttacker = attacker;
|
|
obj_id pvpTarget = target;
|
|
if (isMob(attacker) && !isPlayer(attacker) && isIdValid(getMaster(attacker)))
|
|
{
|
|
pvpAttacker = getMaster(attacker);
|
|
}
|
|
if (isMob(target) && !isPlayer(target) && isIdValid(getMaster(target)))
|
|
{
|
|
pvpTarget = getMaster(target);
|
|
}
|
|
if (!isOffensiveAction || pvpCanAttack(pvpAttacker, pvpTarget))
|
|
{
|
|
validDefenders = utils.addElement(validDefenders, target);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
combat.combatLog(attacker, target, "validateDefenders", "Invalid Target - Attacker or Target is flagged as playerIgnore");
|
|
}
|
|
}
|
|
if (!isOffensiveAction)
|
|
{
|
|
sortDefendersByPlayer(defenders);
|
|
}
|
|
losSourceObj = losSourceObj != null ? losSourceObj : attacker;
|
|
if (isIdValid(target) && target != attacker && (!combat.cachedCanSee(losSourceObj, target) && !ignoreLOS))
|
|
{
|
|
showFlyTextPrivate(attacker, attacker, new string_id("combat_effects", "cant_see"), 1.5f, colors.MEDIUMTURQUOISE);
|
|
combat.combatLog(attacker, target, "validateDefenders", "Invalid Target - Fail LOS check");
|
|
return null;
|
|
}
|
|
String attackerSocialGroup = null;
|
|
if (!isPlayer(attacker) && !pet_lib.isPet(attacker))
|
|
{
|
|
attackerSocialGroup = ai_lib.getSocialGroup(attacker);
|
|
}
|
|
for (int i = 0; i < defenders.length; i++)
|
|
{
|
|
if (isInvulnerable(defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Defender is invulnerable");
|
|
continue;
|
|
}
|
|
if (defenders[i] == target && actionData.attack_rolls == 1)
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Skipping target");
|
|
continue;
|
|
}
|
|
if (pvpOnly == 1 && !isPlayer(defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Skipping target - is not a Player");
|
|
continue;
|
|
}
|
|
if (!isIdValid(defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Invalid ObjId");
|
|
continue;
|
|
}
|
|
if (isGameObjectTypeOf(defenders[i], GOT_building))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Is a building");
|
|
continue;
|
|
}
|
|
if (!isOffensiveAction && !isMob(defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Not a mobile object");
|
|
continue;
|
|
}
|
|
if (!isOffensiveAction && vehicle.isVehicle(defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Is a vehicle");
|
|
continue;
|
|
}
|
|
if (isOffensiveAction && isDead(defenders[i]) || getState(defenders[i], STATE_FEIGN_DEATH) > 0)
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Target is Dead");
|
|
continue;
|
|
}
|
|
if (isOffensiveAction && isIncapacitated(defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Target is Incapped");
|
|
continue;
|
|
}
|
|
if (isOffensiveAction)
|
|
{
|
|
obj_id pvpAttacker = attacker;
|
|
obj_id pvpTarget = defenders[i];
|
|
if (isMob(attacker) && !isPlayer(attacker) && isIdValid(getMaster(attacker)))
|
|
{
|
|
pvpAttacker = getMaster(attacker);
|
|
}
|
|
if (isMob(defenders[i]) && !isPlayer(defenders[i]) && isIdValid(getMaster(defenders[i])))
|
|
{
|
|
pvpTarget = getMaster(defenders[i]);
|
|
}
|
|
if (!pvpCanAttack(pvpAttacker, pvpTarget))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Failed PvP check");
|
|
continue;
|
|
}
|
|
}
|
|
if (!isOffensiveAction && (!pvpCanHelp(attacker, defenders[i]) || pvpCanAttack(attacker, defenders[i])))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Failed PvP check");
|
|
continue;
|
|
}
|
|
if (isOffensiveAction && attackerSocialGroup != null && attackerSocialGroup.equals(ai_lib.getSocialGroup(defenders[i])))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Same AI social group");
|
|
continue;
|
|
}
|
|
if (!combat.cachedCanSee(losSourceObj, defenders[i]) && !ignoreLOS)
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Fail LOS check");
|
|
continue;
|
|
}
|
|
if (isOffensiveAction && factions.ignorePlayer(attacker, defenders[i]))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - Attacker or Defender is flagged as playerIgnore");
|
|
continue;
|
|
}
|
|
if (!combat.validateTarget(defenders[i], validTargetType))
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - failed valid target check");
|
|
continue;
|
|
}
|
|
if (pet_lib.getPetType(defenders[i]) == pet_lib.PET_TYPE_FAMILIAR)
|
|
{
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Invalid Target - familiar");
|
|
continue;
|
|
}
|
|
combat.combatLog(attacker, defenders[i], "validateDefenders", "Valid Target - Adding Defender: " + defenders[i]);
|
|
validDefenders = utils.addElement(validDefenders, defenders[i]);
|
|
if (validDefenders.size() == maxDefenders)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
combat.combatLog(attacker, defenders[0], "validateDefenders", "Valid defender count = " + validDefenders.size());
|
|
obj_id[] _validDefenders = new obj_id[0];
|
|
if (validDefenders != null)
|
|
{
|
|
_validDefenders = new obj_id[validDefenders.size()];
|
|
validDefenders.toArray(_validDefenders);
|
|
}
|
|
return _validDefenders;
|
|
}
|
|
public obj_id[] validateDefenders(obj_id attacker, obj_id target, obj_id[] defenders, obj_id source, int maxDefenders, int validTargetType, boolean isOffensiveAction, obj_id losSourceObj, boolean isHeavyWeapon, boolean isSpecialAttack) throws InterruptedException
|
|
{
|
|
return validateDefenders(attacker, target, defenders, source, maxDefenders, validTargetType, isOffensiveAction, null, false, false, false, 0, null);
|
|
}
|
|
public obj_id[] validateDefenders(obj_id attacker, obj_id target, obj_id[] defenders, obj_id source, int maxDefenders, int validTargetType, boolean isOffensiveAction) throws InterruptedException
|
|
{
|
|
return validateDefenders(attacker, target, defenders, source, maxDefenders, validTargetType, isOffensiveAction, null, false, false);
|
|
}
|
|
public defender_results[] createDefenderResultsArray(int size) throws InterruptedException
|
|
{
|
|
defender_results[] defenderResults = new defender_results[size];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
defenderResults[i] = new defender_results();
|
|
}
|
|
return defenderResults;
|
|
}
|
|
public hit_result[] createHitResultsArray(int size) throws InterruptedException
|
|
{
|
|
hit_result[] hitData = new hit_result[size];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
hitData[i] = new hit_result();
|
|
}
|
|
return hitData;
|
|
}
|
|
public attacker_results[] createAttackerResultsArray(int size) throws InterruptedException
|
|
{
|
|
attacker_results[] attackerResults = new attacker_results[size];
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
attackerResults[i] = new attacker_results();
|
|
}
|
|
return attackerResults;
|
|
}
|
|
public boolean shouldPlayAnimation(obj_id objAttacker, combat_data actionData) throws InterruptedException
|
|
{
|
|
return true;
|
|
}
|
|
public void doMultiDefenderCombatResults(String[] playbackNames, attacker_results attackerResults, defender_results[] defenderResults, weapon_data weaponData, combat_data actionData, hit_result[] cbtHitData) throws InterruptedException
|
|
{
|
|
int weaponTrail = actionData.intWeapon;
|
|
int leftHandTrail = actionData.intLeftHand;
|
|
int rightHandTrail = actionData.intRightHand;
|
|
int leftFootTrail = actionData.intLeftFoot;
|
|
int rightFootTrail = actionData.intRightFoot;
|
|
if (playbackNames == null || playbackNames.length == 0 || attackerResults == null || defenderResults == null || defenderResults.length == 0 || weaponData == null || actionData == null || cbtHitData == null || cbtHitData.length == 0)
|
|
{
|
|
boolean pbnull = playbackNames == null;
|
|
boolean pbzero = playbackNames.length == 0;
|
|
boolean atkres = attackerResults == null;
|
|
boolean defres = defenderResults == null;
|
|
boolean deflen = defenderResults.length == 0;
|
|
boolean weadat = weaponData == null;
|
|
boolean actdat = actionData == null;
|
|
boolean cbtdat = cbtHitData == null;
|
|
CustomerServiceLog("COMBAT_exception", "Error in doMultiDefenderCombatResult: playbackNames == null : " + pbnull + ", playbackNames.length == 0: " + pbzero + ", attackerResults == null: " + atkres + ", defenderResults == null : " + defres + ", defenderResults.length == 0 : " + deflen + ", weaponData == null : " + weadat + ", actionData == null : " + actdat + ", cbtHitData = null : " + cbtdat);
|
|
return;
|
|
}
|
|
boolean addDamageAmount = (cbtHitData.length == defenderResults.length);
|
|
boolean boolPlayAnim = shouldPlayAnimation(attackerResults.id, actionData);
|
|
if (weaponData.weaponType == WEAPON_TYPE_UNARMED)
|
|
{
|
|
if (leftHandTrail > 0)
|
|
{
|
|
attackerResults.setTrail(attacker_results.TRAIL_LHAND, true);
|
|
}
|
|
if (rightHandTrail > 0)
|
|
{
|
|
attackerResults.setTrail(attacker_results.TRAIL_RHAND, true);
|
|
}
|
|
if (leftFootTrail > 0)
|
|
{
|
|
attackerResults.setTrail(attacker_results.TRAIL_LFOOT, true);
|
|
}
|
|
if (rightFootTrail > 0)
|
|
{
|
|
attackerResults.setTrail(attacker_results.TRAIL_RFOOT, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (weaponTrail > 0 || combat.isLightsaberWeapon(weaponData.weaponType))
|
|
{
|
|
attackerResults.setTrail(attacker_results.TRAIL_WEAPON, true);
|
|
}
|
|
}
|
|
if (actionData.actionName == "" || actionData.actionName == null)
|
|
{
|
|
return;
|
|
}
|
|
attackerResults.actionName = getStringCrc(actionData.actionName.toLowerCase());
|
|
if (defenderResults.length > 1)
|
|
{
|
|
if (defenderResults[0] == null || cbtHitData[0] == null)
|
|
{
|
|
return;
|
|
}
|
|
defender_results[] defenderResult = new defender_results[1];
|
|
defenderResult[0] = defenderResults[0];
|
|
if (addDamageAmount)
|
|
{
|
|
defenderResult[0].damageAmount = cbtHitData[0].damage;
|
|
}
|
|
if (boolPlayAnim)
|
|
{
|
|
if (playbackNames[0] != null && !playbackNames[0].equals(""))
|
|
{
|
|
doCombatResults(playbackNames[0], attackerResults, defenderResult);
|
|
}
|
|
}
|
|
attacker_results animationResults = new attacker_results();
|
|
for (int i = 1; i < defenderResults.length; i++)
|
|
{
|
|
if (playbackNames[i] == null || playbackNames[i].equals("") || defenderResults[i] == null)
|
|
{
|
|
continue;
|
|
}
|
|
defenderResult[0].endPosture = defenderResults[i].endPosture;
|
|
defenderResult[0].id = defenderResults[i].id;
|
|
if (defenderResults[i].result == COMBAT_RESULT_HIT)
|
|
{
|
|
doCombatResults(playbackNames[i], null, defenderResult);
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_EVADE)
|
|
{
|
|
doCombatResults(playbackNames[i], null, defenderResult);
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_BLOCK)
|
|
{
|
|
doCombatResults(playbackNames[i], null, defenderResult);
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_MISS)
|
|
{
|
|
doCombatResults(playbackNames[i], null, defenderResult);
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_TETHERED)
|
|
{
|
|
defenderResults[i].result = COMBAT_RESULT_MISS;
|
|
doCombatResults(playbackNames[i], null, defenderResult);
|
|
defenderResults[i].result = COMBAT_RESULT_TETHERED;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (playbackNames[0] != null && !playbackNames[0].equals("") && defenderResults[0] != null)
|
|
{
|
|
if (addDamageAmount)
|
|
{
|
|
defenderResults[0].damageAmount = cbtHitData[0].damage + cbtHitData[0].elementalDamage;
|
|
}
|
|
if (defenderResults[0].result == COMBAT_RESULT_TETHERED)
|
|
{
|
|
defenderResults[0].result = COMBAT_RESULT_MISS;
|
|
if (boolPlayAnim)
|
|
{
|
|
doCombatResults(playbackNames[0], attackerResults, defenderResults);
|
|
}
|
|
defenderResults[0].result = COMBAT_RESULT_TETHERED;
|
|
}
|
|
if (boolPlayAnim)
|
|
{
|
|
doCombatResults(playbackNames[0], attackerResults, defenderResults);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public void doBasicGrenadeCombatResults(String[] playbackNames, attacker_results attackerResults, defender_results[] defenderResults, weapon_data weaponData, combat_data actionData, hit_result[] hitData) throws InterruptedException
|
|
{
|
|
String attackAnim = "throw_grenade";
|
|
String distance = "_near";
|
|
float range = getDistance(attackerResults.id, defenderResults[0].id);
|
|
String hitStrength = "_light";
|
|
int avgDamage = (weaponData.minDamage + weaponData.maxDamage) / 2;
|
|
String grenadeName = getGrenadeType(weaponData.id);
|
|
attackAnim += distance + "_" + grenadeName;
|
|
attackerResults.weapon = null;
|
|
boolean addDamageAmount = (hitData.length == defenderResults.length);
|
|
if (defenderResults.length > 1)
|
|
{
|
|
defender_results[] defenderResult = new defender_results[1];
|
|
defenderResult[0] = defenderResults[0];
|
|
if (addDamageAmount)
|
|
{
|
|
defenderResult[0].damageAmount = hitData[0].damage;
|
|
}
|
|
if (!playbackNames[0].equals(""))
|
|
{
|
|
doCombatResults(attackAnim, attackerResults, defenderResult);
|
|
}
|
|
attacker_results animationResults = new attacker_results();
|
|
for (int i = 1; i < defenderResults.length; i++)
|
|
{
|
|
animationResults.endPosture = defenderResults[i].endPosture;
|
|
animationResults.id = defenderResults[i].id;
|
|
if (defenderResults[i].result == COMBAT_RESULT_HIT)
|
|
{
|
|
if (hitData[i].damage > avgDamage)
|
|
{
|
|
hitStrength = "_medium";
|
|
}
|
|
else
|
|
{
|
|
hitStrength = "_light";
|
|
}
|
|
doCombatResults("get_hit_grenade" + distance + hitStrength, animationResults, null);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (addDamageAmount)
|
|
{
|
|
defenderResults[0].damageAmount = hitData[0].damage;
|
|
}
|
|
doCombatResults(attackAnim, attackerResults, defenderResults);
|
|
}
|
|
return;
|
|
}
|
|
public boolean checkPosture(obj_id objAttacker, obj_id objDefender) throws InterruptedException
|
|
{
|
|
if (isDead(objDefender))
|
|
{
|
|
return false;
|
|
}
|
|
if (isIncapacitated(objDefender))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
public boolean didHit(hit_result[] cbtHitData) throws InterruptedException
|
|
{
|
|
int intI = 0;
|
|
while (intI < cbtHitData.length)
|
|
{
|
|
if (cbtHitData[intI].success == true)
|
|
{
|
|
return true;
|
|
}
|
|
intI = intI + 1;
|
|
}
|
|
return false;
|
|
}
|
|
public void finalizeMineDamage(obj_id objAttacker, weapon_data cbtWeaponData, defender_data[] cbtDefenderData, hit_result[] cbtHitData, defender_results[] cbtDefenderResults) throws InterruptedException
|
|
{
|
|
obj_id[] objDefenders = new obj_id[cbtDefenderData.length];
|
|
int[] intResults = new int[cbtHitData.length];
|
|
int intI = 0;
|
|
while (intI < cbtDefenderData.length)
|
|
{
|
|
if (cbtHitData[intI].success)
|
|
{
|
|
if (damage(cbtDefenderData[intI].id, cbtWeaponData.damageType, cbtHitData[intI].hitLocation, cbtHitData[intI].damage))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
}
|
|
}
|
|
else
|
|
{
|
|
}
|
|
objDefenders[intI] = cbtDefenderData[intI].id;
|
|
intResults[intI] = cbtDefenderResults[intI].result;
|
|
intI = intI + 1;
|
|
}
|
|
}
|
|
public color getHitColor(int intHitLocation) throws InterruptedException
|
|
{
|
|
switch (intHitLocation)
|
|
{
|
|
case HIT_LOCATION_BODY:
|
|
return colors.RED;
|
|
case HIT_LOCATION_HEAD:
|
|
return colors.BLUE;
|
|
case HIT_LOCATION_R_ARM:
|
|
return colors.RED;
|
|
case HIT_LOCATION_L_ARM:
|
|
return colors.RED;
|
|
case HIT_LOCATION_R_LEG:
|
|
return colors.GREEN;
|
|
case HIT_LOCATION_L_LEG:
|
|
return colors.GREEN;
|
|
}
|
|
return colors.WHITE;
|
|
}
|
|
public void doCombatFlyText(defender_data[] defenderData, obj_id attacker, hit_result[] hitData, defender_results[] defenderResults) throws InterruptedException
|
|
{
|
|
float textSize = 1.5f;
|
|
for (int i = 0; i < hitData.length; i++)
|
|
{
|
|
string_id strSpam = null;
|
|
color textColor = colors.WHITE;
|
|
if (defenderResults[i].result == COMBAT_RESULT_BLOCK || defenderResults[i].result == COMBAT_RESULT_LIGHTSABER_BLOCK)
|
|
{
|
|
strSpam = new string_id("combat_effects", "block");
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_EVADE)
|
|
{
|
|
strSpam = new string_id("combat_effects", "dodge");
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_COUNTER)
|
|
{
|
|
strSpam = new string_id("combat_effects", "counterattack");
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_MISS)
|
|
{
|
|
strSpam = new string_id("combat_effects", "miss");
|
|
}
|
|
else if (defenderResults[i].result == COMBAT_RESULT_TETHERED)
|
|
{
|
|
strSpam = new string_id("combat_effects", "tethered");
|
|
textSize = 5f;
|
|
}
|
|
if (strSpam != null)
|
|
{
|
|
if (isPlayer(attacker) && isMob(defenderData[i].id) && (!vehicle.isDriveableVehicle(defenderData[i].id)))
|
|
{
|
|
showCombatTextPrivate(defenderData[i].id, attacker, attacker, strSpam, textSize, textColor);
|
|
}
|
|
if (isPlayer(defenderData[i].id) && isMob(defenderData[i].id) && (!vehicle.isDriveableVehicle(defenderData[i].id)))
|
|
{
|
|
showCombatTextPrivate(defenderData[i].id, attacker, defenderData[i].id, strSpam, textSize, textColor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public String getHitSpam(hit_result cbtHitData) throws InterruptedException
|
|
{
|
|
int intHitLocation = cbtHitData.hitLocation;
|
|
String strId = "";
|
|
switch (intHitLocation)
|
|
{
|
|
case HIT_LOCATION_BODY:
|
|
return "body";
|
|
case HIT_LOCATION_HEAD:
|
|
return "head";
|
|
case HIT_LOCATION_R_ARM:
|
|
return "rarm";
|
|
case HIT_LOCATION_L_ARM:
|
|
return "larm";
|
|
case HIT_LOCATION_R_LEG:
|
|
return "rleg";
|
|
case HIT_LOCATION_L_LEG:
|
|
return "lleg";
|
|
}
|
|
return strId;
|
|
}
|
|
public boolean checkForFumbles(hit_result[] hitData) throws InterruptedException
|
|
{
|
|
return false;
|
|
}
|
|
public void doFumble(attacker_data cbtAttackerData, weapon_data cbtWeaponData, int intAttackerPosture, String strAnimationAction) throws InterruptedException
|
|
{
|
|
string_id strSpam = new string_id("cbt_spam", "fumble");
|
|
int intFumbleDamage = rand(cbtWeaponData.minDamage, cbtWeaponData.maxDamage);
|
|
return;
|
|
}
|
|
public boolean isImmuneToStateChange(obj_id target) throws InterruptedException
|
|
{
|
|
if (vehicle.isDriveableVehicle(target))
|
|
{
|
|
return true;
|
|
}
|
|
if (ai_lib.aiGetNiche(target) == NICHE_DROID)
|
|
{
|
|
return true;
|
|
}
|
|
if (ai_lib.aiGetNiche(target) == NICHE_VEHICLE)
|
|
{
|
|
return true;
|
|
}
|
|
if (ai_lib.isAndroid(target))
|
|
{
|
|
return true;
|
|
}
|
|
if (ai_lib.isTurret(target))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public void doWarcryCombatResults(String strAnimationAction, attacker_results cbtAttackerResults, defender_results[] cbtDefenderResults) throws InterruptedException
|
|
{
|
|
if (cbtDefenderResults.length > 1)
|
|
{
|
|
attacker_results cbtAnimationResults = new attacker_results();
|
|
defender_results[] cbtDefenderResult = new defender_results[1];
|
|
cbtDefenderResult[0] = cbtDefenderResults[0];
|
|
doCombatResults(strAnimationAction, cbtAttackerResults, cbtDefenderResult);
|
|
for (int i = 0; i < cbtDefenderResults.length; i++)
|
|
{
|
|
cbtAnimationResults.endPosture = cbtDefenderResults[i].endPosture;
|
|
cbtAnimationResults.id = cbtDefenderResults[i].id;
|
|
if (cbtDefenderResults[i].result == COMBAT_RESULT_HIT)
|
|
{
|
|
doCombatResults("get_hit_light", cbtAnimationResults, null);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doCombatResults(strAnimationAction, cbtAttackerResults, cbtDefenderResults);
|
|
}
|
|
}
|
|
public void doSuppressionFireResults(String strPlaybackName, attacker_results cbtAttackerResults, defender_results[] cbtDefenderResults, combat_data dctCombatInfo) throws InterruptedException
|
|
{
|
|
int intLeftFoot = dctCombatInfo.intLeftFoot;
|
|
int intRightFoot = dctCombatInfo.intRightFoot;
|
|
int intWeapon = dctCombatInfo.intWeapon;
|
|
int intRightHand = dctCombatInfo.intRightHand;
|
|
int intLeftHand = dctCombatInfo.intLeftHand;
|
|
if (jedi.isLightsaber(cbtAttackerResults.weapon))
|
|
{
|
|
intWeapon = 1;
|
|
}
|
|
if (intLeftFoot > 0)
|
|
{
|
|
cbtAttackerResults.setTrail(attacker_results.TRAIL_LFOOT, true);
|
|
}
|
|
if (intRightFoot > 0)
|
|
{
|
|
cbtAttackerResults.setTrail(attacker_results.TRAIL_RFOOT, true);
|
|
}
|
|
if (intLeftHand > 0)
|
|
{
|
|
cbtAttackerResults.setTrail(attacker_results.TRAIL_LHAND, true);
|
|
}
|
|
if (intRightHand > 0)
|
|
{
|
|
cbtAttackerResults.setTrail(attacker_results.TRAIL_RHAND, true);
|
|
}
|
|
if (intWeapon > 0)
|
|
{
|
|
cbtAttackerResults.setTrail(attacker_results.TRAIL_WEAPON, true);
|
|
}
|
|
if (cbtDefenderResults.length > 1)
|
|
{
|
|
int intI = 1;
|
|
attacker_results cbtAnimationResults = new attacker_results();
|
|
defender_results[] cbtDefenderResult = new defender_results[1];
|
|
cbtDefenderResult[0] = cbtDefenderResults[0];
|
|
doCombatResults(strPlaybackName, cbtAttackerResults, cbtDefenderResult);
|
|
while (intI < cbtDefenderResults.length)
|
|
{
|
|
cbtAnimationResults.endPosture = cbtDefenderResults[intI].endPosture;
|
|
cbtAnimationResults.id = cbtDefenderResults[intI].id;
|
|
if (cbtDefenderResults[intI].result == COMBAT_RESULT_HIT)
|
|
{
|
|
doCombatResults("posture_scramble", cbtAnimationResults, null);
|
|
}
|
|
else if (cbtDefenderResults[intI].result == COMBAT_RESULT_EVADE)
|
|
{
|
|
doCombatResults("dodge", cbtAnimationResults, null);
|
|
}
|
|
else if (cbtDefenderResults[intI].result == COMBAT_RESULT_BLOCK)
|
|
{
|
|
doCombatResults("dodge", cbtAnimationResults, null);
|
|
}
|
|
else if (cbtDefenderResults[intI].result == COMBAT_RESULT_MISS)
|
|
{
|
|
doCombatResults("dodge", cbtAnimationResults, null);
|
|
}
|
|
else
|
|
{
|
|
}
|
|
intI = intI + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doCombatResults(strPlaybackName, cbtAttackerResults, cbtDefenderResults);
|
|
}
|
|
}
|
|
public void doChargeResults(String strPlaybackName, attacker_results cbtAttackerResults, defender_results[] cbtDefenderResults) throws InterruptedException
|
|
{
|
|
if (cbtDefenderResults.length > 1)
|
|
{
|
|
int intI = 1;
|
|
attacker_results cbtAnimationResults = new attacker_results();
|
|
defender_results[] cbtDefenderResult = new defender_results[1];
|
|
cbtDefenderResult[0] = cbtDefenderResults[0];
|
|
doCombatResults(strPlaybackName, cbtAttackerResults, cbtDefenderResult);
|
|
while (intI < cbtDefenderResults.length)
|
|
{
|
|
cbtAnimationResults.endPosture = cbtDefenderResults[intI].endPosture;
|
|
cbtAnimationResults.id = cbtDefenderResults[intI].id;
|
|
if (cbtDefenderResults[intI].result == COMBAT_RESULT_HIT)
|
|
{
|
|
doCombatResults("change_posture", cbtAnimationResults, null);
|
|
}
|
|
else if (cbtDefenderResults[intI].result == COMBAT_RESULT_EVADE)
|
|
{
|
|
doCombatResults("dodge", cbtAnimationResults, null);
|
|
}
|
|
else if (cbtDefenderResults[intI].result == COMBAT_RESULT_BLOCK)
|
|
{
|
|
doCombatResults("dodge", cbtAnimationResults, null);
|
|
}
|
|
else if (cbtDefenderResults[intI].result == COMBAT_RESULT_MISS)
|
|
{
|
|
doCombatResults("dodge", cbtAnimationResults, null);
|
|
}
|
|
intI = intI + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
doCombatResults(strPlaybackName, cbtAttackerResults, cbtDefenderResults);
|
|
}
|
|
}
|
|
public String getPlaybackIntensity(String strPlaybackName, hit_result[] cbtHitData, weapon_data cbtWeaponData) throws InterruptedException
|
|
{
|
|
String strNewAction = new String();
|
|
strNewAction = strPlaybackName;
|
|
int intMidDamage = (cbtWeaponData.minDamage + cbtWeaponData.maxDamage) / 2;
|
|
if (cbtHitData[0].damage > intMidDamage)
|
|
{
|
|
strNewAction = strNewAction + "_medium";
|
|
}
|
|
else
|
|
{
|
|
strNewAction = strNewAction + "_light";
|
|
}
|
|
return strNewAction;
|
|
}
|
|
public String getThrownWeaponStringType(obj_id objWeapon) throws InterruptedException
|
|
{
|
|
String strType = "";
|
|
String strTemplate = getGrenadeType(objWeapon);
|
|
if (strTemplate == null)
|
|
{
|
|
strType = "";
|
|
}
|
|
strType = strType + strTemplate;
|
|
return strType;
|
|
}
|
|
public String getHeavyWeaponStringType(obj_id objWeapon) throws InterruptedException
|
|
{
|
|
String strType = "heavy_";
|
|
String strTemplate = getHeavyWeaponType(objWeapon);
|
|
if (strTemplate == null)
|
|
{
|
|
strType = "";
|
|
}
|
|
strType = strType + strTemplate;
|
|
return strTemplate;
|
|
}
|
|
public String[] makeStringArray(int intLength) throws InterruptedException
|
|
{
|
|
int intI = 0;
|
|
String[] strArray = new String[intLength];
|
|
while (intI < intLength)
|
|
{
|
|
strArray[intI] = "";
|
|
intI = intI + 1;
|
|
}
|
|
return strArray;
|
|
}
|
|
public boolean checkWeaponData(obj_id objPlayer, obj_id objTarget, int intAttackCategory, weapon_data cbtWeaponData, String strCommand, String strWeaponType) throws InterruptedException
|
|
{
|
|
float fltDistance = getDistance(objPlayer, objTarget);
|
|
if (fltDistance > cbtWeaponData.maxRange)
|
|
{
|
|
combat.sendCombatSpamMessage(objPlayer, new string_id("cbt_spam", "out_of_range"), COMBAT_RESULT_OUT_OF_RANGE);
|
|
sendSystemMessage(objPlayer, new string_id("cbt_spam", "out_of_range_single"));
|
|
return false;
|
|
}
|
|
if (intAttackCategory != combat.ALL_WEAPONS)
|
|
{
|
|
if ((intAttackCategory == combat.RANGED_WEAPON) || (intAttackCategory == combat.MELEE_WEAPON))
|
|
{
|
|
int intWeaponCategory = combat.getWeaponCategory(cbtWeaponData.weaponType);
|
|
if (intWeaponCategory != intAttackCategory)
|
|
{
|
|
string_id strSpam = new string_id("cbt_spam", "no_attack_wrong_weapon");
|
|
sendSystemMessage(objPlayer, strSpam);
|
|
return false;
|
|
}
|
|
}
|
|
else if (intAttackCategory == combat.ALL_LIGHTSABERS)
|
|
{
|
|
if (!jedi.isLightsaber(cbtWeaponData.weaponType))
|
|
{
|
|
string_id strSpam = new string_id("cbt_spam", "no_attack_wrong_weapon");
|
|
sendSystemMessage(objPlayer, strSpam);
|
|
return false;
|
|
}
|
|
}
|
|
else if (intAttackCategory == combat.FORCE_POWER)
|
|
{
|
|
if (!jedi.isLightsaber(cbtWeaponData.weaponType))
|
|
{
|
|
if (cbtWeaponData.weaponType != WEAPON_TYPE_UNARMED)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
else if (intAttackCategory != cbtWeaponData.weaponType)
|
|
{
|
|
string_id strSpam = new string_id("cbt_spam", "no_attack_wrong_weapon");
|
|
sendSystemMessage(objPlayer, strSpam);
|
|
return false;
|
|
}
|
|
}
|
|
if (!strCommand.equals(""))
|
|
{
|
|
String strAbility = strWeaponType + strCommand;
|
|
if (!hasCommand(objPlayer, strAbility))
|
|
{
|
|
string_id strSpam = new string_id("cbt_spam", "no_ability");
|
|
sendSystemMessage(objPlayer, strSpam);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
public boolean checkCommands(obj_id objPlayer, String strCommand, String strWeaponType) throws InterruptedException
|
|
{
|
|
if (!strCommand.equals(""))
|
|
{
|
|
String strAbility = strWeaponType + strCommand;
|
|
if (!hasCommand(objPlayer, strAbility))
|
|
{
|
|
string_id strSpam = new string_id("cbt_spam", "no_ability");
|
|
sendSystemMessage(objPlayer, strSpam);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
public boolean checkForCombatActions(obj_id objPlayer) throws InterruptedException
|
|
{
|
|
if (queueHasCommandFromGroup(objPlayer, (-1170591580)))
|
|
{
|
|
return true;
|
|
}
|
|
if (queueHasCommandFromGroup(objPlayer, (391413347)))
|
|
{
|
|
return true;
|
|
}
|
|
if (queueHasCommandFromGroup(objPlayer, (-506878646)))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public String getGrenadeType(obj_id objGrenade) throws InterruptedException
|
|
{
|
|
String strTemplate = getTemplateName(objGrenade);
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_cryoban.iff"))
|
|
{
|
|
return "cryoban";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_fragmentation_light.iff"))
|
|
{
|
|
return "fragmentation";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_fragmentation.iff"))
|
|
{
|
|
return "fragmentation";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_glop.iff"))
|
|
{
|
|
return "glop";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_imperial_detonator.iff"))
|
|
{
|
|
return "imperial_detonator";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_proton.iff"))
|
|
{
|
|
return "proton";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_thermal_detonator.iff"))
|
|
{
|
|
return "thermal_detonator";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/grenade/grenade_bug_bomb.iff"))
|
|
{
|
|
return "bug_bomb";
|
|
}
|
|
return "fragmentation";
|
|
}
|
|
public String getHeavyWeaponType(obj_id objHeavyWeapon) throws InterruptedException
|
|
{
|
|
String strTemplate = getTemplateName(objHeavyWeapon);
|
|
String hw = "heavyweapon";
|
|
if (strTemplate.equals("object/weapon/ranged/heavy/heavy_rocket_laucher.iff"))
|
|
{
|
|
return hw;
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/heavy/heavy_lightning_beam.iff"))
|
|
{
|
|
return hw;
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/heavy/heavy_particle_beam.iff"))
|
|
{
|
|
return hw;
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/heavy/heavy_acid_beam.iff"))
|
|
{
|
|
return hw;
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/rifle/rifle_flame_thrower.iff"))
|
|
{
|
|
return "rifle";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/rifle/rifle_beam.iff"))
|
|
{
|
|
return "rifle_beam";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/rifle/rifle_acid_beam.iff"))
|
|
{
|
|
return "rifle";
|
|
}
|
|
if (strTemplate.equals("object/weapon/ranged/rifle/rifle_lightning.iff"))
|
|
{
|
|
return "carbine";
|
|
}
|
|
return hw;
|
|
}
|
|
public void clearQueue(obj_id objPlayer) throws InterruptedException
|
|
{
|
|
queueClearCommandsFromGroup(objPlayer, (-506878646));
|
|
queueClearCommandsFromGroup(objPlayer, (-1170591580));
|
|
queueClearCommandsFromGroup(objPlayer, (391413347));
|
|
}
|
|
public boolean callSupplyDrop(obj_id self, int supplyId) throws InterruptedException
|
|
{
|
|
location loc = getLocation(self);
|
|
if (isIdValid(loc.cell))
|
|
{
|
|
sendSystemMessage(self, new string_id("combat_effects", "not_indoors"));
|
|
return false;
|
|
}
|
|
obj_id shuttle = createObject("object/creature/npc/theme_park/lambda_shuttle.iff", loc);
|
|
if (!isIdValid(shuttle))
|
|
{
|
|
return false;
|
|
}
|
|
dictionary d = new dictionary();
|
|
d.put("owner", self);
|
|
d.put("supplyId", supplyId);
|
|
attachScript(shuttle, "systems.combat.combat_supply_drop_controller");
|
|
messageTo(shuttle, "startLandingSequence", d, 5.0f, false);
|
|
if (supplyId < 13)
|
|
{
|
|
prose_package pp = new prose_package();
|
|
prose.setStringId(pp, new string_id("combat_effects", "supply_drop_comm"));
|
|
commPlayers(self, "object/mobile/npe/npe_hutt_minion.iff", "sound/sys_comm_other.snd", 5f, self, pp);
|
|
}
|
|
else
|
|
{
|
|
prose_package pp = new prose_package();
|
|
prose.setStringId(pp, new string_id("spam", "officer_reinforcement_com"));
|
|
commPlayers(self, "object/mobile/npe/npe_hutt_minion.iff", "sound/sys_comm_other.snd", 5f, self, pp);
|
|
}
|
|
return true;
|
|
}
|
|
public boolean canCallSupplyDrop(obj_id self) throws InterruptedException
|
|
{
|
|
location loc = getLocation(self);
|
|
if (isIdValid(loc.cell))
|
|
{
|
|
sendSystemMessage(self, new string_id("combat_effects", "not_indoors"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
public boolean callFavor(obj_id self, int which) throws InterruptedException
|
|
{
|
|
location loc = getLocation(self);
|
|
String buddyObj = "summon_smuggler_buddy_";
|
|
String buddyScript = "ai.smuggler_buddy";
|
|
String buddyName = "smuggler_buddy_name";
|
|
if (which == 1)
|
|
{
|
|
buddyObj = "summon_smuggler_medic_";
|
|
buddyScript = "ai.smuggler_medic";
|
|
buddyName = "smuggler_medic_name";
|
|
}
|
|
if (!isIdValid(loc.cell))
|
|
{
|
|
int x = rand(-3, 3);
|
|
int z = rand(-3, 3);
|
|
loc.x += x;
|
|
loc.z += z;
|
|
}
|
|
int playerLevel = getLevel(self);
|
|
int summonLevel = playerLevel / 10;
|
|
if (summonLevel < 1)
|
|
{
|
|
summonLevel = 1;
|
|
}
|
|
else
|
|
{
|
|
if (summonLevel > 9)
|
|
{
|
|
summonLevel = 9;
|
|
}
|
|
}
|
|
buddyObj += summonLevel;
|
|
obj_id target = getHateTarget(self);
|
|
if (!isIdValid(target))
|
|
{
|
|
sendSystemMessage(self, new string_id("combat_effects", "no_target_for_buddy"));
|
|
return false;
|
|
}
|
|
obj_id buddy = create.object(buddyObj, loc, getLevel(self));
|
|
if (!isIdValid(buddy))
|
|
{
|
|
return false;
|
|
}
|
|
setName(buddy, new string_id("combat_effects", buddyName));
|
|
attachScript(buddy, buddyScript);
|
|
if (!isIdValid(loc.cell))
|
|
{
|
|
loc.x += 30;
|
|
setHomeLocation(buddy, loc);
|
|
}
|
|
setMaster(buddy, self);
|
|
startCombat(buddy, target);
|
|
return true;
|
|
}
|
|
public boolean doHealingPreCheck(combat_data actionData, attacker_data attackerData, defender_data[] defenderData, boolean verbose) throws InterruptedException
|
|
{
|
|
if (vehicle.isRidingVehicle(attackerData.id))
|
|
{
|
|
if (verbose)
|
|
{
|
|
sendSystemMessage(attackerData.id, new string_id("cbt_spam", "attack_fail_vehicle_single"));
|
|
}
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "doHealingPreCheck", "Aborting Action - Riding vehicle");
|
|
return false;
|
|
}
|
|
obj_id mount = getMountId(attackerData.id);
|
|
if (isIdValid(mount))
|
|
{
|
|
if (pet_lib.isGalloping(mount))
|
|
{
|
|
if (verbose)
|
|
{
|
|
sendSystemMessage(attackerData.id, new string_id("cbt_spam", "attack_fail_gallop_single"));
|
|
}
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "doHealingPreCheck", "Aborting Action - Riding galloping mount");
|
|
return false;
|
|
}
|
|
}
|
|
if (attackerData.posture == POSTURE_KNOCKED_DOWN)
|
|
{
|
|
if (verbose)
|
|
{
|
|
sendSystemMessage(attackerData.id, new string_id("cbt_spam", "attack_fail_knockdown_single"));
|
|
}
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "doHealingPreCheck", "Aborting Action - Attacker id knocked down");
|
|
return false;
|
|
}
|
|
float actionMaxRange = actionData.maxRange;
|
|
float dist = getDistance(attackerData.id, defenderData[0].id);
|
|
if (dist > actionMaxRange)
|
|
{
|
|
if (verbose)
|
|
{
|
|
sendSystemMessage(attackerData.id, new string_id("cbt_spam", "out_of_range_far_single"));
|
|
showFlyTextPrivate(attackerData.id, attackerData.id, new string_id("combat_effects", "range_too_far"), 1.0f, colors.MEDIUMTURQUOISE);
|
|
}
|
|
combat.combatLog(attackerData.id, defenderData[0].id, "doHealingPreCheck", "Aborting Action - Target is further than max range");
|
|
return false;
|
|
}
|
|
if ((ai_lib.isDroid(defenderData[0].id) && !ai_lib.isDroid(attackerData.id)) || (ai_lib.isAndroid(defenderData[0].id) && !ai_lib.isAndroid(attackerData.id)))
|
|
{
|
|
if (verbose)
|
|
{
|
|
sendSystemMessage(attackerData.id, new string_id("cbt_spam", "cannot_heal_droid"));
|
|
showFlyTextPrivate(attackerData.id, attackerData.id, new string_id("cbt_spam", "cannot_heal_droid"), 1.0f, colors.MEDIUMTURQUOISE);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
public boolean hasFullActionPool(obj_id target) throws InterruptedException
|
|
{
|
|
int currentAction = getAction(target);
|
|
int maxAction = getMaxAction(target);
|
|
if (currentAction == maxAction)
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
public boolean healthToActionConversion(obj_id target, int healthCost, int actionGain) throws InterruptedException
|
|
{
|
|
int currentHealth = getHealth(target);
|
|
int modifiedHealth = currentHealth - healthCost;
|
|
attrib_mod[] healthMods = getHealthModifiers(target);
|
|
int healthModsValue = 0;
|
|
for (int i = 0; i < healthMods.length; i++)
|
|
{
|
|
healthModsValue += healthMods[i].getValue();
|
|
}
|
|
if (healthModsValue < 0)
|
|
{
|
|
sendSystemMessage(target, new string_id("spam", "not_well_enough"));
|
|
return false;
|
|
}
|
|
if (modifiedHealth < 1)
|
|
{
|
|
showFlyTextPrivate(target, target, new string_id("spam", "low_health"), 1.0f, colors.RED);
|
|
return false;
|
|
}
|
|
int currentAction = getAction(target);
|
|
int maxAction = getMaxAction(target);
|
|
int modifiedAction = currentAction + actionGain;
|
|
int trueActionGained = 0;
|
|
if (modifiedAction > maxAction)
|
|
{
|
|
setAction(target, maxAction);
|
|
trueActionGained = maxAction - currentAction;
|
|
}
|
|
else
|
|
{
|
|
setAction(target, modifiedAction);
|
|
trueActionGained = actionGain;
|
|
}
|
|
setHealth(target, modifiedHealth);
|
|
string_id actionStringId = new string_id("spam", "plus_action");
|
|
prose_package pp = new prose_package();
|
|
pp = prose.setStringId(pp, actionStringId);
|
|
pp = prose.setDI(pp, trueActionGained);
|
|
showFlyTextPrivate(target, target, pp, 1.1f, colors.YELLOW);
|
|
playClientEffectObj(target, "clienteffect/medic_reckless_stimulation.cef", target, "root");
|
|
return true;
|
|
}
|
|
public void doMedicEvasion(obj_id medic) throws InterruptedException
|
|
{
|
|
obj_id[] thoseWhoHateMe = getHateList(medic);
|
|
int medicLevel = getLevel(medic);
|
|
String evasionScriptVarName = "me_evasion." + medic;
|
|
if (thoseWhoHateMe.length != 0 && thoseWhoHateMe != null)
|
|
{
|
|
for (int i = 0; i < thoseWhoHateMe.length; i++)
|
|
{
|
|
int haterLevel = getLevel(thoseWhoHateMe[i]);
|
|
int levelSpread = medicLevel - haterLevel;
|
|
int baseChance = 93;
|
|
int difficultyClass = getIntObjVar(thoseWhoHateMe[i], "difficultyClass");
|
|
if (difficultyClass > 1)
|
|
{
|
|
baseChance = -45;
|
|
}
|
|
if (levelSpread < -24)
|
|
{
|
|
levelSpread = -24;
|
|
}
|
|
int roll = rand(1, 100);
|
|
if (roll + baseChance + (levelSpread * 3) > 100)
|
|
{
|
|
removeHateTarget(medic, thoseWhoHateMe[i]);
|
|
removeHateTarget(thoseWhoHateMe[i], medic);
|
|
utils.setScriptVar(thoseWhoHateMe[i], evasionScriptVarName, 1);
|
|
}
|
|
else
|
|
{
|
|
float hate = getHate(medic, thoseWhoHateMe[i]);
|
|
float oldHate = hate;
|
|
hate /= 2;
|
|
setHate(medic, thoseWhoHateMe[i], hate);
|
|
utils.removeScriptVar(thoseWhoHateMe[i], evasionScriptVarName);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
public combat_data modifyActionDataByExpertise(obj_id self, combat_data actionData) throws InterruptedException
|
|
{
|
|
if (stealth.hasInvisibleBuff(self) || buff.hasBuff(self, "sp_smoke_mirrors"))
|
|
{
|
|
float expertise_defense_overpower = (float)getEnhancedSkillStatisticModifierUncapped(self, "expertise_avoidance_overpower");
|
|
actionData.reduceParry += expertise_defense_overpower;
|
|
actionData.reduceDodge += expertise_defense_overpower;
|
|
actionData.reduceBlock += expertise_defense_overpower;
|
|
actionData.reduceGlancing += expertise_defense_overpower;
|
|
}
|
|
float armorBypass = (float)getEnhancedSkillStatisticModifierUncapped(self, "expertise_armor_bypass_" + actionData.specialLine);
|
|
if (armorBypass > 0.0f)
|
|
{
|
|
armorBypass = armorBypass / 100.0f;
|
|
armorBypass += actionData.bypassArmor;
|
|
armorBypass = armorBypass > 1.0f ? 1.0f : armorBypass;
|
|
actionData.increaseStrikethrough = -1000.0f;
|
|
actionData.bypassArmor = armorBypass;
|
|
}
|
|
float damToHeal = (float)getEnhancedSkillStatisticModifierUncapped(self, "expertise_damage_to_healing_" + actionData.specialLine);
|
|
if (damToHeal > 0.0f && actionData.convertDamageToHealth > 0.0f)
|
|
{
|
|
damToHeal = damToHeal / 100.0f;
|
|
actionData.convertDamageToHealth += damToHeal;
|
|
}
|
|
float flawlessBead = (float)getEnhancedSkillStatisticModifierUncapped(self, "flawless_bead");
|
|
if (flawlessBead > 0 && actionData.commandType != combat.LEFT_CLICK_DEFAULT)
|
|
{
|
|
actionData.reduceParry += flawlessBead;
|
|
actionData.reduceDodge += flawlessBead;
|
|
actionData.reduceBlock += flawlessBead;
|
|
actionData.reduceGlancing += flawlessBead;
|
|
}
|
|
return actionData;
|
|
}
|
|
public void runHitSimulator(obj_id self, int[] atkTable, int[] defTable) throws InterruptedException
|
|
{
|
|
if (!isGod(self) && !utils.hasObjVar(self, "runHitSimulator"))
|
|
{
|
|
return;
|
|
}
|
|
int hitLoops = 0;
|
|
if (hasObjVar(self, "runHitSimulator"))
|
|
{
|
|
hitLoops = getIntObjVar(self, "runHitSimulator");
|
|
}
|
|
if (hitLoops < 1)
|
|
{
|
|
utils.removeObjVar(self, "runHitSimulator");
|
|
return;
|
|
}
|
|
hitLoops--;
|
|
utils.setObjVar(self, "runHitSimulator", hitLoops);
|
|
if (utils.hasScriptVar(self, "hitpid"))
|
|
{
|
|
int bellpid = utils.getIntScriptVar(self, "hitpid");
|
|
sui.forceCloseSUIPage(bellpid);
|
|
}
|
|
int hitNumber = 1000;
|
|
int miss = 0;
|
|
int dodge = 0;
|
|
int parry = 0;
|
|
int glancing = 0;
|
|
int block = 0;
|
|
int critical = 0;
|
|
int punishing = 0;
|
|
int dhit = 0;
|
|
int ahit = 0;
|
|
int hit = 0;
|
|
int evade = 0;
|
|
int strikethrough = 0;
|
|
for (int i = 0; i < defTable.length; i++)
|
|
{
|
|
switch (defTable[i])
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
miss++;
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
dodge++;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
parry++;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
glancing++;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
block++;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
critical++;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
punishing++;
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
evade++;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
strikethrough++;
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 0; i < atkTable.length; i++)
|
|
{
|
|
switch (atkTable[i])
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
miss++;
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
dodge++;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
parry++;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
glancing++;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
block++;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
critical++;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
punishing++;
|
|
break;
|
|
case HIT_RESULT_HIT:
|
|
if (defTable[i] == HIT_RESULT_HIT)
|
|
{
|
|
hit++;
|
|
}
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
evade++;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
strikethrough++;
|
|
break;
|
|
}
|
|
}
|
|
int test_miss = 0;
|
|
int test_dodge = 0;
|
|
int test_parry = 0;
|
|
int test_glancing = 0;
|
|
int test_block = 0;
|
|
int test_critical = 0;
|
|
int test_punishing = 0;
|
|
int test_hit = 0;
|
|
int test_evade = 0;
|
|
int test_strikethrough = 0;
|
|
int[] atkResult = new int[hitNumber];
|
|
int[] defResult = new int[hitNumber];
|
|
for (int i = 0; i < hitNumber; i++)
|
|
{
|
|
defResult[i] = defTable[rand(0, defTable.length - 1)];
|
|
atkResult[i] = atkTable[rand(0, atkTable.length - 1)];
|
|
}
|
|
for (int i = 0; i < defResult.length; i++)
|
|
{
|
|
switch (defResult[i])
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
test_miss++;
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
test_dodge++;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
test_parry++;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
test_glancing++;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
test_block++;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
test_critical++;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
test_punishing++;
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
test_evade++;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
test_strikethrough++;
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 0; i < atkResult.length; i++)
|
|
{
|
|
switch (atkResult[i])
|
|
{
|
|
case HIT_RESULT_MISS:
|
|
test_miss++;
|
|
break;
|
|
case HIT_RESULT_DODGE:
|
|
test_dodge++;
|
|
break;
|
|
case HIT_RESULT_PARRY:
|
|
test_parry++;
|
|
break;
|
|
case HIT_RESULT_GLANCING:
|
|
test_glancing++;
|
|
break;
|
|
case HIT_RESULT_BLOCK:
|
|
test_block++;
|
|
break;
|
|
case HIT_RESULT_CRITICAL:
|
|
test_critical++;
|
|
break;
|
|
case HIT_RESULT_PUNISHING:
|
|
test_punishing++;
|
|
break;
|
|
case HIT_RESULT_HIT:
|
|
if (defResult[i] == HIT_RESULT_HIT)
|
|
{
|
|
test_hit++;
|
|
}
|
|
break;
|
|
case HIT_RESULT_EVADE:
|
|
test_evade++;
|
|
break;
|
|
case HIT_RESULT_STRIKETHROUGH:
|
|
test_strikethrough++;
|
|
break;
|
|
}
|
|
}
|
|
int pid = createSUIPage(sui.SUI_MSGBOX, self, self, "noHandler");
|
|
setSUIProperty(pid, "", "Size", "650,375");
|
|
setSUIProperty(pid, sui.LISTBOX_TITLE, sui.PROP_TEXT, "Hit Result Analysis");
|
|
sui.listboxButtonSetup(pid, sui.OK_ONLY);
|
|
setSUIProperty(pid, sui.LISTBOX_BTN_OK, sui.PROP_TEXT, "@cancel");
|
|
int passedTotal = miss + dodge + parry + glancing + block + critical + punishing + evade + strikethrough;
|
|
hit = 1000 - passedTotal > -1 ? 1000 - passedTotal : 0;
|
|
int simTotal = test_miss + test_dodge + test_parry + test_glancing + test_block + test_critical + test_punishing + test_evade + test_strikethrough;
|
|
test_hit = 1000 - simTotal > -1 ? 1000 - simTotal : 0;
|
|
String resultString = "Defender and Attacker Hit Table Values \n ";
|
|
resultString += "Passed hit_table Values \n ";
|
|
resultString += "Defender Dodge: " + (float)dodge / 10.0f + " \n ";
|
|
resultString += "Defender Parry: " + (float)parry / 10.0f + " \n ";
|
|
resultString += "Defender Evade: " + (float)evade / 10.0f + " \n ";
|
|
resultString += "Defender Block: " + (float)block / 10.0f + " \n ";
|
|
resultString += "Defender Glance: " + (float)glancing / 10.0f + " \n\n ";
|
|
resultString += "Attacker Miss: " + (float)miss / 10.0f + " \n ";
|
|
resultString += "Attacker Strikethrough: " + (float)strikethrough / 10.0f + " \n ";
|
|
resultString += "Attacker Critical: " + (float)critical / 10.0f + " \n ";
|
|
resultString += "Attacker Punishing: " + (float)punishing / 10.0f + " \n ";
|
|
resultString += "Attacker Hit: " + (float)hit / 10.0f + " \n\n\n";
|
|
resultString += "Simulated hit_table rolls (" + hitNumber + ") itterations \n ";
|
|
resultString += "Defender Dodge: " + (float)test_dodge / 10.0f + " \n ";
|
|
resultString += "Defender Parry: " + (float)test_parry / 10.0f + " \n ";
|
|
resultString += "Defender Evade: " + (float)test_evade / 10.0f + " \n ";
|
|
resultString += "Defender Block: " + (float)test_block / 10.0f + " \n ";
|
|
resultString += "Defender Glance: " + (float)test_glancing / 10.0f + " \n\n ";
|
|
resultString += "Attacker Miss: " + (float)test_miss / 10.0f + " \n ";
|
|
resultString += "Attacker Strikethrough: " + (float)test_strikethrough / 10.0f + " \n ";
|
|
resultString += "Attacker Critical: " + (float)test_critical / 10.0f + " \n ";
|
|
resultString += "Attacker Punishing: " + (float)test_punishing / 10.0f + " \n ";
|
|
resultString += "Attacker Hit: " + (float)test_hit / 10.0f + " \n ";
|
|
setSUIProperty(pid, sui.MSGBOX_PROMPT, sui.PROP_TEXT, resultString);
|
|
showSUIPage(pid);
|
|
flushSUIPage(pid);
|
|
utils.setScriptVar(self, "hitpid", pid);
|
|
}
|
|
public combat_data attackOverrideByBuff(obj_id self, combat_data actionData) throws InterruptedException
|
|
{
|
|
int hasAO = getEnhancedSkillStatisticModifierUncapped(self, "attack_override_by_buff");
|
|
if (hasAO < 1)
|
|
{
|
|
return actionData;
|
|
}
|
|
int[] allBuffs = buff.getAllBuffs(self);
|
|
if (allBuffs == null || allBuffs.length == 0)
|
|
{
|
|
return actionData;
|
|
}
|
|
for (int i = 0; i < allBuffs.length; i++)
|
|
{
|
|
String buffName = buff.getBuffNameFromCrc(allBuffs[i]);
|
|
if (buffName.length() != 0 && !buffName.equals("") && buffName.startsWith("attack_override_" + actionData.actionName))
|
|
{
|
|
String[] parse = split(buffName, '|');
|
|
String newAttack = parse[1];
|
|
return combat_engine.getCombatData(newAttack);
|
|
}
|
|
}
|
|
return actionData;
|
|
}
|
|
public void doKillMeterUpdate(obj_id attacker, obj_id defender, int damage) throws InterruptedException
|
|
{
|
|
if (!utils.isProfession(attacker, utils.COMMANDO) && !utils.isProfession(defender, utils.COMMANDO) || damage < 1)
|
|
{
|
|
return;
|
|
}
|
|
if (utils.isProfession(attacker, utils.COMMANDO))
|
|
{
|
|
int damageInterval = utils.getIntScriptVar(attacker, "km.damage_done");
|
|
damageInterval += damage;
|
|
if (damageInterval >= getLevel(attacker) * (15 - Math.round(getKillMeter(attacker) / 5.0f)))
|
|
{
|
|
utils.setScriptVar(attacker, "km.damage_done", 0);
|
|
combat.modifyKillMeter(attacker, 1);
|
|
}
|
|
else
|
|
{
|
|
utils.setScriptVar(attacker, "km.damage_done", damageInterval);
|
|
}
|
|
}
|
|
if (utils.isProfession(defender, utils.COMMANDO))
|
|
{
|
|
int damageInterval = utils.getIntScriptVar(defender, "km.damage_taken");
|
|
damageInterval += damage;
|
|
if (damageInterval >= getLevel(defender) * (25 - Math.round(getKillMeter(attacker) / 5.0f)))
|
|
{
|
|
utils.setScriptVar(defender, "km.damage_taken", 0);
|
|
combat.modifyKillMeter(defender, 1);
|
|
}
|
|
else
|
|
{
|
|
utils.setScriptVar(defender, "km.damage_taken", damageInterval);
|
|
}
|
|
}
|
|
}
|
|
}
|