From 8c287154c8d1e9f8dce2071305c4df95cde8e25a Mon Sep 17 00:00:00 2001 From: FatBoy-DOTC Date: Sat, 12 Aug 2023 19:07:50 -0500 Subject: [PATCH 1/8] players to use marksmanship again --- src/engine/gameManager/CombatManager.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java index 843d7824..dc20465c 100644 --- a/src/engine/gameManager/CombatManager.java +++ b/src/engine/gameManager/CombatManager.java @@ -730,6 +730,13 @@ public enum CombatManager { dpj = ((PlayerCharacter) ac).getWeaponPower(); if (dpj != null) { float attackRange = getWeaponRange(wb); + PlayerBonuses bonus = ac.getBonuses(); + if (bonus != null) { + float buffRange = 1; + buffRange += bonus.getFloat(ModType.WeaponRange, SourceType.None) * .01f; + attackRange *= buffRange; + + } dpj.attack(target, attackRange); @@ -931,6 +938,14 @@ public enum CombatManager { PowersBase wp = dpj.getPower(); if (wp.requiresHitRoll() == false) { float attackRange = getWeaponRange(wb); + PlayerBonuses bonus = ac.getBonuses(); + if (bonus != null) { + float buffRange = 1; + buffRange += bonus.getFloat(ModType.WeaponRange, SourceType.None) * .01f; + attackRange *= buffRange; + + } + dpj.attack(target, attackRange); } else { ((PlayerCharacter) ac).setWeaponPower(null); From a278f8bc79a7d83e8dd5ee50634fd0c53dad6bbd Mon Sep 17 00:00:00 2001 From: FatBoy-DOTC Date: Sat, 12 Aug 2023 19:41:44 -0500 Subject: [PATCH 2/8] range bonus confined to 1 spot --- src/engine/gameManager/CombatManager.java | 36 +++++++---------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java index dc20465c..372ef3fb 100644 --- a/src/engine/gameManager/CombatManager.java +++ b/src/engine/gameManager/CombatManager.java @@ -451,12 +451,7 @@ public enum CombatManager { if (hasNoWeapon) { range = MBServerStatics.NO_WEAPON_RANGE; } else { - range = getWeaponRange(wb); - if (bonus != null) { - float buffRange = 1; - buffRange += bonus.getFloat(ModType.WeaponRange, SourceType.None) * .01f; - range *= buffRange; - } + range = getWeaponRange(wb, bonus); } if (abstractCharacter.getObjectType() == GameObjectType.Mob) { @@ -729,15 +724,8 @@ public enum CombatManager { if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { dpj = ((PlayerCharacter) ac).getWeaponPower(); if (dpj != null) { - float attackRange = getWeaponRange(wb); PlayerBonuses bonus = ac.getBonuses(); - if (bonus != null) { - float buffRange = 1; - buffRange += bonus.getFloat(ModType.WeaponRange, SourceType.None) * .01f; - attackRange *= buffRange; - - } - + float attackRange = getWeaponRange(wb, bonus); dpj.attack(target, attackRange); if (dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) @@ -748,7 +736,7 @@ public enum CombatManager { if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && !mainHand) { dpj = ((PlayerCharacter) ac).getWeaponPower(); if (dpj != null && dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) { - float attackRange = getWeaponRange(wb); + float attackRange = getWeaponRange(wb,null); dpj.attack(target, attackRange); } } @@ -937,15 +925,8 @@ public enum CombatManager { if (dpj != null) { PowersBase wp = dpj.getPower(); if (wp.requiresHitRoll() == false) { - float attackRange = getWeaponRange(wb); PlayerBonuses bonus = ac.getBonuses(); - if (bonus != null) { - float buffRange = 1; - buffRange += bonus.getFloat(ModType.WeaponRange, SourceType.None) * .01f; - attackRange *= buffRange; - - } - + float attackRange = getWeaponRange(wb,bonus); dpj.attack(target, attackRange); } else { ((PlayerCharacter) ac).setWeaponPower(null); @@ -1230,11 +1211,14 @@ public enum CombatManager { pc.setLastMobAttackTime(); } - public static float getWeaponRange(ItemBase weapon) { + public static float getWeaponRange(ItemBase weapon, PlayerBonuses bonus) { if (weapon == null) return 0f; - - return weapon.getRange(); + float rangeMod = 1.0f; + if (bonus != null) { + rangeMod += bonus.getFloat(ModType.WeaponRange, SourceType.None); + } + return weapon.getRange() * rangeMod; } public static void toggleCombat(ToggleCombatMsg msg, ClientConnection origin) { From 58fe2d93b789086681ea7733a9cea27b2f33c598 Mon Sep 17 00:00:00 2001 From: FatBoy-DOTC Date: Sat, 12 Aug 2023 19:58:27 -0500 Subject: [PATCH 3/8] weapon range bonus works again --- src/engine/gameManager/CombatManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java index 372ef3fb..cb2e39e7 100644 --- a/src/engine/gameManager/CombatManager.java +++ b/src/engine/gameManager/CombatManager.java @@ -736,7 +736,7 @@ public enum CombatManager { if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && !mainHand) { dpj = ((PlayerCharacter) ac).getWeaponPower(); if (dpj != null && dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) { - float attackRange = getWeaponRange(wb,null); + float attackRange = getWeaponRange(wb,bonuses); dpj.attack(target, attackRange); } } @@ -1216,7 +1216,8 @@ public enum CombatManager { return 0f; float rangeMod = 1.0f; if (bonus != null) { - rangeMod += bonus.getFloat(ModType.WeaponRange, SourceType.None); + //rangeMod += bonus.getFloat(ModType.WeaponRange, SourceType.None); + rangeMod += bonus.getFloatPercentAll(ModType.WeaponRange, SourceType.None); } return weapon.getRange() * rangeMod; } From 1c9b7594d35d5dfaabdb9d81424567e238e0ce8f Mon Sep 17 00:00:00 2001 From: MagicBot Date: Sun, 13 Aug 2023 08:13:27 -0400 Subject: [PATCH 4/8] Class cleanup. Formatting, squiggles and unused code/variables. --- src/engine/gameManager/CombatManager.java | 2610 +++++++++++---------- 1 file changed, 1317 insertions(+), 1293 deletions(-) diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java index cb2e39e7..29c37fc0 100644 --- a/src/engine/gameManager/CombatManager.java +++ b/src/engine/gameManager/CombatManager.java @@ -34,1390 +34,1414 @@ import static engine.math.FastMath.sqr; public enum CombatManager { - COMBATMANAGER; - - public static int animation = 0; - - /** - * Message sent by player to attack something. - */ - public static void setAttackTarget(AttackCmdMsg msg, ClientConnection origin) throws MsgSendException { - - PlayerCharacter player; - int targetType; - AbstractWorldObject target; - - if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) { - return; - } - - player = SessionManager.getPlayerCharacter(origin); - - if (player == null) { - - return; - } - - //source must match player this account belongs to - if (player.getObjectUUID() != msg.getSourceID() || player.getObjectType().ordinal() != msg.getSourceType()) { - Logger.error("Msg Source ID " + msg.getSourceID() + " Does not Match Player ID " + player.getObjectUUID()); - - return; - } - - targetType = msg.getTargetType(); - - if (targetType == GameObjectType.PlayerCharacter.ordinal()) { - target = PlayerCharacter.getFromCache(msg.getTargetID()); - } else if (targetType == GameObjectType.Building.ordinal()) { - target = BuildingManager.getBuildingFromCache(msg.getTargetID()); - } else if (targetType == GameObjectType.Mob.ordinal()) { - target = Mob.getFromCache(msg.getTargetID()); - } else { - player.setCombatTarget(null); - return; //not valid type to attack - } - // quit of the combat target is already the current combat target - // or there is no combat target - if (target == null) { - return; - } - - //set sources target - player.setCombatTarget(target); - - //put in combat if not already - if (!player.isCombat()) { - toggleCombat(true, origin); - } - - //make character stand if sitting - if (player.isSit()) { - toggleSit(false, origin); - } - - AttackTarget(player, target); - - } - - public static void AttackTarget(PlayerCharacter playerCharacter, AbstractWorldObject target) { - - boolean swingOffhand = false; - - //check my weapon can I do an offhand attack - Item weaponOff = playerCharacter.getCharItemManager().getEquipped().get(MBServerStatics.SLOT_OFFHAND); - Item weaponMain = playerCharacter.getCharItemManager().getEquipped().get(MBServerStatics.SLOT_MAINHAND); - - // if you carry something in the offhand thats a weapon you get to swing it - if (weaponOff != null) { - if (weaponOff.getItemBase().getType().equals(ItemType.WEAPON)) { - swingOffhand = true; - } - } - // if you carry nothing in either hand you get to swing your offhand - if (weaponOff == null && weaponMain == null) { - swingOffhand = true; - } - - //we always swing our mainhand if we are not on timer - JobContainer main = playerCharacter.getTimers().get("Attack" + MBServerStatics.SLOT_MAINHAND); - if (main == null) { - // no timers on the mainhand, lets submit a job to swing - CombatManager.createTimer(playerCharacter, MBServerStatics.SLOT_MAINHAND, 1, true); // attack in 0.1 of a second - } - - if (swingOffhand) { + COMBATMANAGER; + + public static int animation = 0; + + /** + * Message sent by player to attack something. + */ + public static void setAttackTarget(AttackCmdMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + int targetType; + AbstractWorldObject target; + + if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) + return; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return; + + //source must match player this account belongs to + + if (player.getObjectUUID() != msg.getSourceID() || player.getObjectType().ordinal() != msg.getSourceType()) { + Logger.error("Msg Source ID " + msg.getSourceID() + " Does not Match Player ID " + player.getObjectUUID()); + return; + } + + targetType = msg.getTargetType(); + + if (targetType == GameObjectType.PlayerCharacter.ordinal()) { + target = PlayerCharacter.getFromCache(msg.getTargetID()); + } else if (targetType == GameObjectType.Building.ordinal()) { + target = BuildingManager.getBuildingFromCache(msg.getTargetID()); + } else if (targetType == GameObjectType.Mob.ordinal()) { + target = Mob.getFromCache(msg.getTargetID()); + } else { + player.setCombatTarget(null); + return; //not valid type to attack + } + + // quit of the combat target is already the current combat target + // or there is no combat target + + if (target == null) + return; + + //set sources target + + player.setCombatTarget(target); + + //put in combat if not already + + if (!player.isCombat()) + toggleCombat(true, origin); + + //make character stand if sitting + + if (player.isSit()) + toggleSit(false, origin); + + AttackTarget(player, target); + + } + + public static void AttackTarget(PlayerCharacter playerCharacter, AbstractWorldObject target) { + + boolean swingOffhand = false; + + //check my weapon can I do an offhand attack + + Item weaponOff = playerCharacter.getCharItemManager().getEquipped().get(MBServerStatics.SLOT_OFFHAND); + Item weaponMain = playerCharacter.getCharItemManager().getEquipped().get(MBServerStatics.SLOT_MAINHAND); + + // if you carry something in the offhand thats a weapon you get to swing it + + if (weaponOff != null) + if (weaponOff.getItemBase().getType().equals(ItemType.WEAPON)) + swingOffhand = true; + + // if you carry nothing in either hand you get to swing your offhand + + if (weaponOff == null && weaponMain == null) + swingOffhand = true; + + + //we always swing our mainhand if we are not on timer + + JobContainer main = playerCharacter.getTimers().get("Attack" + MBServerStatics.SLOT_MAINHAND); + + // no timers on the mainhand, lets submit a job to swing + + if (main == null) + CombatManager.createTimer(playerCharacter, MBServerStatics.SLOT_MAINHAND, 1, true); // attack in 0.1 of a second + /* only swing offhand if we have a weapon in it or are unarmed in both hands and no timers running */ - JobContainer off = playerCharacter.getTimers().get("Attack" + MBServerStatics.SLOT_OFFHAND); - if (off == null) { - CombatManager.createTimer(playerCharacter, MBServerStatics.SLOT_OFFHAND, 1, true); // attack in 0.1 of a second - } - } - City playerCity = ZoneManager.getCityAtLocation(playerCharacter.getLoc()); - if (playerCity != null && playerCity.getGuild().getNation().equals(playerCharacter.getGuild().getNation()) == false && playerCity.cityOutlaws.contains(playerCharacter.getObjectUUID()) == false) - playerCity.cityOutlaws.add(playerCharacter.getObjectUUID()); - } - public static void setAttackTarget(PetAttackMsg msg, ClientConnection origin) throws MsgSendException { + if (swingOffhand) { + + JobContainer off = playerCharacter.getTimers().get("Attack" + MBServerStatics.SLOT_OFFHAND); + + if (off == null) + CombatManager.createTimer(playerCharacter, MBServerStatics.SLOT_OFFHAND, 1, true); // attack in 0.1 of a second + } + + City playerCity = ZoneManager.getCityAtLocation(playerCharacter.getLoc()); + + if (playerCity != null && playerCity.getGuild().getNation().equals(playerCharacter.getGuild().getNation()) == false && playerCity.cityOutlaws.contains(playerCharacter.getObjectUUID()) == false) + playerCity.cityOutlaws.add(playerCharacter.getObjectUUID()); + } + + public static void setAttackTarget(PetAttackMsg msg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter player; + Mob pet; + int targetType; + AbstractWorldObject target; + + if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) + return; + + player = SessionManager.getPlayerCharacter(origin); + + if (player == null) + return; + + pet = player.getPet(); + + if (pet == null) + return; + + targetType = msg.getTargetType(); + + if (targetType == GameObjectType.PlayerCharacter.ordinal()) + target = PlayerCharacter.getFromCache(msg.getTargetID()); + else if (targetType == GameObjectType.Building.ordinal()) + target = BuildingManager.getBuildingFromCache(msg.getTargetID()); + else if (targetType == GameObjectType.Mob.ordinal()) + target = Mob.getFromCache(msg.getTargetID()); + else { + pet.setCombatTarget(null); + return; //not valid type to attack + } + + if (pet.equals(target)) + return; + + // quit of the combat target is already the current combat target + // or there is no combat target + + if (target == null || target == pet.getCombatTarget()) + return; + + //set sources target + + pet.setCombatTarget(target); + + //put in combat if not already + + if (!pet.isCombat()) + pet.setCombat(true); + + //make character stand if sitting + + if (pet.isSit()) + toggleSit(false, origin); + + } + + private static void removeAttackTimers(AbstractCharacter ac) { + + JobContainer main; + JobContainer off; + + if (ac == null) + return; + + main = ac.getTimers().get("Attack" + MBServerStatics.SLOT_MAINHAND); + off = ac.getTimers().get("Attack" + MBServerStatics.SLOT_OFFHAND); + + if (main != null) + JobScheduler.getInstance().cancelScheduledJob(main); + + ac.getTimers().remove("Attack" + MBServerStatics.SLOT_MAINHAND); + + if (off != null) + JobScheduler.getInstance().cancelScheduledJob(off); + + ac.getTimers().remove("Attack" + MBServerStatics.SLOT_OFFHAND); + + ac.setCombatTarget(null); + + } + + /** + * Begin Attacking + */ + public static void doCombat(AbstractCharacter ac, int slot) { + + int ret = 0; + + if (ac == null) + return; + + // Attempt to eat null targets until we can clean + // up this unholy mess and refactor it into a thread. + + ret = attemptCombat(ac, slot); + + //handle pets + if (ret < 2 && ac.getObjectType().equals(GameObjectType.Mob)) { + + Mob mob = (Mob) ac; + + if (mob.isPet()) + return; + } + + //ret values + //0: not valid attack, fail attack + //1: cannot attack, wrong hand + //2: valid attack + //3: cannot attack currently, continue checking + + if (ret == 0 || ret == 1) { + + //Could not attack, clear timer + + ConcurrentHashMap timers = ac.getTimers(); + + if (timers != null) + timers.remove("Attack" + slot); + + //clear combat target if not valid attack + + if (ret == 0) + ac.setCombatTarget(null); + + } else if (ret == 3) //Failed but continue checking. reset timer + createTimer(ac, slot, 5, false); + } + + /** + * Verify can attack target + */ + private static int attemptCombat(AbstractCharacter abstractCharacter, int slot) { + + if (abstractCharacter == null) + return 0; + + try { + + //Make sure player can attack + + PlayerBonuses bonus = abstractCharacter.getBonuses(); + + if (bonus != null && bonus.getBool(ModType.ImmuneToAttack, SourceType.None)) + return 0; + + AbstractWorldObject target = abstractCharacter.getCombatTarget(); + + if (target == null) + return 0; + + //target must be valid type + + if (AbstractWorldObject.IsAbstractCharacter(target)) { + + AbstractCharacter tar = (AbstractCharacter) target; + + //must be alive, attackable and in World + + if (!tar.isAlive()) + return 0; + else if (tar.isSafeMode()) + return 0; + else if (!tar.isActive()) + return 0; + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getTimers().get("Attack" + slot) == null) + if (!((PlayerCharacter) abstractCharacter).canSee((PlayerCharacter) target)) + return 0; + + //must not be immune to all or immune to attack + + Resists res = tar.getResists(); + bonus = tar.getBonuses(); + + if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.ImmuneToAttack)) + if (res != null) + if (res.immuneToAll() || res.immuneToAttacks()) + return 0; + } else if (target.getObjectType().equals(GameObjectType.Building)) { + Building tar = (Building) target; + + // Cannot attack an invuln building + + if (tar.isVulnerable() == false) + return 0; + + } else + return 0; //only characters and buildings may be attacked + + //source must be in world and alive + + if (!abstractCharacter.isActive()) + return 0; + else if (!abstractCharacter.isAlive()) + return 0; + + //make sure source is in combat mode + + if (!abstractCharacter.isCombat()) + return 0; + + //See if either target is in safe zone + + if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (((PlayerCharacter) abstractCharacter).inSafeZone() || ((PlayerCharacter) target).inSafeZone()) + return 0; + + if (!(slot == MBServerStatics.SLOT_MAINHAND || slot == MBServerStatics.SLOT_OFFHAND)) + return 0; + + if (abstractCharacter.getCharItemManager() == null) + return 0; + + //get equippment + + ConcurrentHashMap equipped = abstractCharacter.getCharItemManager().getEquipped(); + boolean hasNoWeapon = false; + + if (equipped == null) + return 0; + + //get Weapon + + boolean isWeapon = true; + Item weapon = equipped.get(slot); + ItemBase wb = null; + + if (weapon == null) + isWeapon = false; + else { + ItemBase ib = weapon.getItemBase(); + + if (ib == null || !ib.getType().equals(ItemType.WEAPON)) + isWeapon = false; + else + wb = ib; + } + + //no weapon, see if other hand has a weapon + + if (!isWeapon) + if (slot == MBServerStatics.SLOT_MAINHAND) { + + //make sure offhand has weapon, not shield + + Item weaponOff = equipped.get(MBServerStatics.SLOT_OFFHAND); + + if (weaponOff != null) { + ItemBase ib = weaponOff.getItemBase(); + + if (ib == null || !ib.getType().equals(ItemType.WEAPON)) + hasNoWeapon = true; + else + return 1; //no need to attack with this hand + + } else + hasNoWeapon = true; + + } else if (equipped.get(MBServerStatics.SLOT_MAINHAND) == null) + return 1; //no need to attack with this hand + + //Source can attack. + //NOTE Don't 'return;' beyond this point until timer created + + boolean attackFailure = (wb != null) && (wb.getRange() > 35f) && abstractCharacter.isMoving(); + + //Target can't attack on move with ranged weapons. + //if not enough stamina, then skip attack + + if (wb == null) { + if (abstractCharacter.getStamina() < 1) + attackFailure = true; + } else if (abstractCharacter.getStamina() < wb.getWeight()) + attackFailure = true; + + //see if attacker is stunned. If so, stop here + + bonus = abstractCharacter.getBonuses(); + + if (bonus != null && bonus.getBool(ModType.Stunned, SourceType.None)) + attackFailure = true; + + //Get Range of weapon + + float range; + + if (hasNoWeapon) + range = MBServerStatics.NO_WEAPON_RANGE; + else + range = getWeaponRange(wb, bonus); + + if (abstractCharacter.getObjectType() == GameObjectType.Mob) { + + Mob minion = (Mob) abstractCharacter; + + if (minion.isSiege()) + range = 300f; + } + + //Range check. + + if (NotInRange(abstractCharacter, target, range)) { + + //target is in stealth and can't be seen by source + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) + if (!((PlayerCharacter) abstractCharacter).canSee((PlayerCharacter) target)) + return 0; + + attackFailure = true; + } + + //handle pet, skip timers (handled by AI) + + if (abstractCharacter.getObjectType().equals(GameObjectType.Mob)) { + + Mob mob = (Mob) abstractCharacter; + + if (mob.isPet()) { + attack(abstractCharacter, target, weapon, wb, slot == MBServerStatics.SLOT_MAINHAND); + return 2; + } + } + + //TODO Verify attacker has los (if not ranged weapon). + + if (!attackFailure) { + + if (hasNoWeapon || abstractCharacter.getObjectType().equals(GameObjectType.Mob)) + createTimer(abstractCharacter, slot, 20, true); //2 second for no weapon + else { + int wepSpeed = (int) (wb.getSpeed()); + + if (weapon != null && weapon.getBonusPercent(ModType.WeaponSpeed, SourceType.None) != 0f) //add weapon speed bonus + wepSpeed *= (1 + weapon.getBonus(ModType.WeaponSpeed, SourceType.None)); + + if (abstractCharacter.getBonuses() != null && abstractCharacter.getBonuses().getFloatPercentAll(ModType.AttackDelay, SourceType.None) != 0f) //add effects speed bonus + wepSpeed *= (1 + abstractCharacter.getBonuses().getFloatPercentAll(ModType.AttackDelay, SourceType.None)); + + if (wepSpeed < 10) + wepSpeed = 10; //Old was 10, but it can be reached lower with legit buffs,effects. + + createTimer(abstractCharacter, slot, wepSpeed, true); + } + + attack(abstractCharacter, target, weapon, wb, slot == MBServerStatics.SLOT_MAINHAND); + } else + createTimer(abstractCharacter, slot, 5, false); // changed this to half a second to make combat attempts more aggressive than movement sync + + } catch (Exception e) { + return 0; + } + return 2; + } + + private static void createTimer(AbstractCharacter ac, int slot, int time, boolean success) { + + ConcurrentHashMap timers = ac.getTimers(); + + if (timers != null) { + AttackJob aj = new AttackJob(ac, slot, success); + JobContainer job; + job = JobScheduler.getInstance().scheduleJob(aj, (time * 100)); + timers.put("Attack" + slot, job); + } else { + Logger.error("Unable to find Timers for Character " + ac.getObjectUUID()); + } + } + + /** + * Attempt to attack target + */ + private static void attack(AbstractCharacter ac, AbstractWorldObject target, Item weapon, ItemBase wb, boolean mainHand) { + + float atr; + int minDamage, maxDamage; + int errorTrack = 0; + + try { + + if (ac == null) + return; + + if (target == null) + return; + + if (mainHand) { + atr = ac.getAtrHandOne(); + minDamage = ac.getMinDamageHandOne(); + maxDamage = ac.getMaxDamageHandOne(); + } else { + atr = ac.getAtrHandTwo(); + minDamage = ac.getMinDamageHandTwo(); + maxDamage = ac.getMaxDamageHandTwo(); + } + + boolean tarIsRat = false; + + if (target.getObjectTypeMask() == MBServerStatics.MASK_RAT) + tarIsRat = true; + else if (target.getObjectType() == GameObjectType.PlayerCharacter) { + + PlayerCharacter pTar = (PlayerCharacter) target; + + for (Effect eff : pTar.getEffects().values()) + if (eff.getPowerToken() == 429513599 || eff.getPowerToken() == 429415295) + tarIsRat = true; + } + + //Dont think we need to do this anymore. + + if (tarIsRat) + if (ac.getBonuses().getFloatPercentAll(ModType.Slay, SourceType.Rat) != 0) { //strip away current % dmg buffs then add with rat % + + float percent = 1 + ac.getBonuses().getFloatPercentAll(ModType.Slay, SourceType.Rat); + + minDamage *= percent; + maxDamage *= percent; + } + + errorTrack = 1; + + //subtract stamina + + if (wb == null) + ac.modifyStamina(-0.5f, ac, true); + else { + float stam = wb.getWeight() / 3; + stam = (stam < 1) ? 1 : stam; + ac.modifyStamina(-(stam), ac, true); + } + + ac.cancelOnAttackSwing(); + + errorTrack = 2; + + //set last time this player has attacked something. + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectUUID() != ac.getObjectUUID() && ac.getObjectType() == GameObjectType.PlayerCharacter) { + ac.setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + ((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); + } else + ac.setTimeStamp("LastCombatMob", System.currentTimeMillis()); + + errorTrack = 3; + + //Get defense for target + + float defense; + + if (target.getObjectType().equals(GameObjectType.Building)) { + + if (BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null) { + ac.setCombatTarget(null); + return; + } + + defense = 0; + + Building building = (Building) target; + + if (building.getParentZone() != null && building.getParentZone().isPlayerCity()) { + + if (System.currentTimeMillis() > building.getTimeStamp("CallForHelp")) { + + building.getTimestamps().put("CallForHelp", System.currentTimeMillis() + 15000); + + for (Mob mob : building.getParentZone().zoneMobSet) { + if (!mob.isPlayerGuard()) + continue; + + if (mob.getCombatTarget() != null) + continue; + + if (mob.getGuild() != null && building.getGuild() != null) + if (!Guild.sameGuild(mob.getGuild().getNation(), building.getGuild().getNation())) + continue; + + if (mob.getLoc().distanceSquared2D(building.getLoc()) > sqr(300)) + continue; + + mob.setCombatTarget(ac); + } + } + } + } else { + AbstractCharacter tar = (AbstractCharacter) target; + defense = tar.getDefenseRating(); + handleRetaliate(tar, ac); //Handle target attacking back if in combat and has no other target + } + + errorTrack = 4; + + //Get hit chance + + int chance; + float dif = atr - defense; + + if (dif > 100) + chance = 94; + else if (dif < -100) + chance = 4; + else + chance = (int) ((0.45 * dif) + 49); + + errorTrack = 5; + + //calculate hit/miss + + int roll = ThreadLocalRandom.current().nextInt(100); + DeferredPowerJob dpj = null; + + if (roll < chance) { + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + updateAttackTimers((PlayerCharacter) ac, target, true); + + boolean skipPassives = false; + PlayerBonuses bonuses = ac.getBonuses(); + + if (bonuses != null && bonuses.getBool(ModType.IgnorePassiveDefense, SourceType.None)) + skipPassives = true; + + AbstractCharacter tarAc = null; + + if (AbstractWorldObject.IsAbstractCharacter(target)) + tarAc = (AbstractCharacter) target; + + errorTrack = 6; + + // Apply Weapon power effect if any. don't try to apply twice if + // dual wielding. Perform after passive test for sync purposes. + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { + + dpj = ((PlayerCharacter) ac).getWeaponPower(); + + if (dpj != null) { + + PlayerBonuses bonus = ac.getBonuses(); + float attackRange = getWeaponRange(wb, bonus); + dpj.attack(target, attackRange); + + if (dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) + ((PlayerCharacter) ac).setWeaponPower(dpj); + } + } + + //check to apply second backstab. + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && !mainHand) { + + dpj = ((PlayerCharacter) ac).getWeaponPower(); + + if (dpj != null && dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) { + float attackRange = getWeaponRange(wb, bonuses); + dpj.attack(target, attackRange); + } + } + + errorTrack = 7; + + //Hit, check if passive kicked in + + boolean passiveFired = false; + + if (!skipPassives && tarAc != null) { + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { + + //Handle Block passive + + if (testPassive(ac, tarAc, "Block") && canTestBlock(ac, target)) { + + if (!target.isAlive()) + return; + + sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_BLOCK, dpj, mainHand); + passiveFired = true; + } + + //Handle Parry passive + + if (!passiveFired) + if (canTestParry(ac, target) && testPassive(ac, tarAc, "Parry")) { + + if (!target.isAlive()) + return; + + sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_PARRY, dpj, mainHand); + passiveFired = true; + } + + } + + errorTrack = 8; + + //Handle Dodge passive + + if (!passiveFired) { + if (testPassive(ac, tarAc, "Dodge")) { + + if (!target.isAlive()) + return; + + sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_DODGE, dpj, mainHand); + passiveFired = true; + } + } + } + + //return if passive (Block, Parry, Dodge) fired + + if (passiveFired) + return; + + errorTrack = 9; + + //Hit and no passives + //if target is player, set last attack timestamp + + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) + updateAttackTimers((PlayerCharacter) target, ac, false); + + //Get damage Type + + DamageType damageType; + + if (wb != null) + damageType = wb.getDamageType(); + else if (ac.getObjectType().equals(GameObjectType.Mob) && ((Mob) ac).isSiege()) + damageType = DamageType.Siege; + else + damageType = DamageType.Crush; + + errorTrack = 10; + + //Get target resists + + Resists resists = null; + + if (tarAc != null) + resists = tarAc.getResists(); + else if (target.getObjectType().equals(GameObjectType.Building)) + resists = ((Building) target).getResists(); + + //make sure target is not immune to damage type; + + if (resists != null && resists.immuneTo(damageType)) { + sendCombatMessage(ac, target, 0f, wb, dpj, mainHand); + return; + } + + errorTrack = 11; + + //Calculate Damage done + + float damage; + + if (wb != null) + damage = calculateDamage(ac, tarAc, minDamage, maxDamage, damageType, resists); + else + damage = calculateDamage(ac, tarAc, minDamage, maxDamage, damageType, resists); + + float d = 0f; + + errorTrack = 12; + + //Subtract Damage from target's health + + if (tarAc != null) { + + if (tarAc.isSit()) + damage *= 2.5f; //increase damage if sitting + + if (tarAc.getObjectType() == GameObjectType.Mob) { + ac.setHateValue(damage * MBServerStatics.PLAYER_COMBAT_HATE_MODIFIER); + ((Mob) tarAc).handleDirectAggro(ac); + } + + if (tarAc.getHealth() > 0) + d = tarAc.modifyHealth(-damage, ac, false); + + } else if (target.getObjectType().equals(GameObjectType.Building)) { + + if (BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null) { + ac.setCombatTarget(null); + return; + } + + if (target.getHealth() > 0) + d = ((Building) target).modifyHealth(-damage, ac); + } + + errorTrack = 13; + + //Test to see if any damage needs done to weapon or armor + + testItemDamage(ac, target, weapon, wb); + + // if target is dead, we got the killing blow, remove attack timers on our weapons + + if (tarAc != null && !tarAc.isAlive()) + removeAttackTimers(ac); + + //test double death fix + + if (d != 0) + sendCombatMessage(ac, target, damage, wb, dpj, mainHand); //send damage message + + errorTrack = 14; + + //handle procs + + if (weapon != null && tarAc != null && tarAc.isAlive()) { + + ConcurrentHashMap effects = weapon.getEffects(); + + for (Effect eff : effects.values()) { + if (eff == null) + continue; + + HashSet aems = eff.getEffectModifiers(); + + if (aems != null) { + for (AbstractEffectModifier aem : aems) { + + if (!tarAc.isAlive()) + break; + + if (aem instanceof WeaponProcEffectModifier) { + + int procChance = ThreadLocalRandom.current().nextInt(100); + + if (procChance < MBServerStatics.PROC_CHANCE) + ((WeaponProcEffectModifier) aem).applyProc(ac, target); + + } + } + } + } + } + + errorTrack = 15; + + //handle damage shields + + if (ac.isAlive() && tarAc != null && tarAc.isAlive()) + handleDamageShields(ac, tarAc, damage); + + } else { + + // Apply Weapon power effect if any. + // don't try to apply twice if dual wielding. + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { + dpj = ((PlayerCharacter) ac).getWeaponPower(); + + if (dpj != null) { + + PowersBase wp = dpj.getPower(); + + if (wp.requiresHitRoll() == false) { + PlayerBonuses bonus = ac.getBonuses(); + float attackRange = getWeaponRange(wb, bonus); + dpj.attack(target, attackRange); + } else + ((PlayerCharacter) ac).setWeaponPower(null); + } + } + + if (target.getObjectType() == GameObjectType.Mob) + ((Mob) target).handleDirectAggro(ac); + + errorTrack = 17; + + //miss, Send miss message + + sendCombatMessage(ac, target, 0f, wb, dpj, mainHand); + + //if attacker is player, set last attack timestamp + + if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) + updateAttackTimers((PlayerCharacter) ac, target, true); + } + + errorTrack = 18; + + //cancel effects that break on attack or attackSwing + ac.cancelOnAttack(); + + } catch (Exception e) { + Logger.error(ac.getName() + ' ' + errorTrack + ' ' + e); + } + } + + public static boolean canTestParry(AbstractCharacter ac, AbstractWorldObject target) { + + if (ac == null || target == null || !AbstractWorldObject.IsAbstractCharacter(target)) + return false; + + AbstractCharacter tar = (AbstractCharacter) target; + + CharacterItemManager acItem = ac.getCharItemManager(); + CharacterItemManager tarItem = tar.getCharItemManager(); + + if (acItem == null || tarItem == null) + return false; + + Item acMain = acItem.getItemFromEquipped(1); + Item acOff = acItem.getItemFromEquipped(2); + Item tarMain = tarItem.getItemFromEquipped(1); + Item tarOff = tarItem.getItemFromEquipped(2); + + return !isRanged(acMain) && !isRanged(acOff) && !isRanged(tarMain) && !isRanged(tarOff); + } + + public static boolean canTestBlock(AbstractCharacter ac, AbstractWorldObject target) { + + if (ac == null || target == null || !AbstractWorldObject.IsAbstractCharacter(target)) + return false; + + AbstractCharacter tar = (AbstractCharacter) target; + + CharacterItemManager acItem = ac.getCharItemManager(); + CharacterItemManager tarItem = tar.getCharItemManager(); + + if (acItem == null || tarItem == null) + return false; + + + Item tarOff = tarItem.getItemFromEquipped(2); + + + if (tarOff == null) + return false; + + return tarOff.getItemBase().isShield() != false; + } + + private static boolean isRanged(Item item) { + + if (item == null) + return false; + + ItemBase ib = item.getItemBase(); + + if (ib == null) + return false; + + if (ib.getType().equals(ItemType.WEAPON) == false) + return false; + + return ib.getRange() > MBServerStatics.RANGED_WEAPON_RANGE; + + + } + + private static float calculateDamage(AbstractCharacter source, AbstractCharacter target, float minDamage, float maxDamage, DamageType damageType, Resists resists) { + + //get range between min and max + + float range = maxDamage - minDamage; + + //Damage is calculated twice to average a more central point + + float damage = ThreadLocalRandom.current().nextFloat() * range; + damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) * .5f; + + //put it back between min and max + + damage += minDamage; + + //calculate resists in if any + + if (resists != null) + return resists.getResistedDamage(source, target, damageType, damage, 0); + else + return damage; + } + + private static void sendPassiveDefenseMessage(AbstractCharacter source, ItemBase wb, AbstractWorldObject target, int passiveType, DeferredPowerJob dpj, boolean mainHand) { + + int swingAnimation = getSwingAnimation(wb, dpj, mainHand); + + if (dpj != null) + if (PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) + swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); + + TargetedActionMsg cmm = new TargetedActionMsg(source, swingAnimation, target, passiveType); + DispatchMessage.sendToAllInRange(target, cmm); + + } + + private static void sendCombatMessage(AbstractCharacter source, AbstractWorldObject target, float damage, ItemBase wb, DeferredPowerJob dpj, boolean mainHand) { + + int swingAnimation = getSwingAnimation(wb, dpj, mainHand); + + if (dpj != null) + if (PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) + swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); + + if (source.getObjectType() == GameObjectType.PlayerCharacter) + for (Effect eff : source.getEffects().values()) + if (eff.getPower() != null && (eff.getPower().getToken() == 429506943 || eff.getPower().getToken() == 429408639 || eff.getPower().getToken() == 429513599 || eff.getPower().getToken() == 429415295)) + swingAnimation = 0; + + TargetedActionMsg cmm = new TargetedActionMsg(source, target, damage, swingAnimation); + DispatchMessage.sendToAllInRange(target, cmm); + } + + public static int getSwingAnimation(ItemBase wb, DeferredPowerJob dpj, boolean mainHand) { + int token = 0; + + if (dpj != null) + token = (dpj.getPower() != null) ? dpj.getPower().getToken() : 0; + + if (token == 563721004) //kick animation + return 79; + + if (CombatManager.animation != 0) + return CombatManager.animation; + + if (wb == null) + return 75; + + if (mainHand) { + if (wb.getAnimations().size() > 0) { + + int animation; + + int random = ThreadLocalRandom.current().nextInt(wb.getAnimations().size()); + + try { + animation = wb.getAnimations().get(random); + return animation; + } catch (Exception e) { + Logger.error(e.getMessage()); + return wb.getAnimations().get(0); + } + + } else if (wb.getOffHandAnimations().size() > 0) { + + int animation; + int random = ThreadLocalRandom.current().nextInt(wb.getOffHandAnimations().size()); + + try { + animation = wb.getOffHandAnimations().get(random); + return animation; + } catch (Exception e) { + Logger.error(e.getMessage()); + return wb.getOffHandAnimations().get(0); + } + } + } else { + if (wb.getOffHandAnimations().size() > 0) { + int animation; + int random = ThreadLocalRandom.current().nextInt(wb.getOffHandAnimations().size()); + + try { + animation = wb.getOffHandAnimations().get(random); + return animation; + } catch (Exception e) { + Logger.error(e.getMessage()); + return wb.getOffHandAnimations().get(0); + + } + } else if (wb.getAnimations().size() > 0) { + + int animation; + int random = ThreadLocalRandom.current().nextInt(wb.getAnimations().size()); + + try { + animation = wb.getAnimations().get(random); + return animation; + } catch (Exception e) { + Logger.error(e.getMessage()); + return wb.getAnimations().get(0); + + } + + } + } + + + String required = wb.getSkillRequired(); + String mastery = wb.getMastery(); + + if (required.equals("Unarmed Combat")) + return 75; + else if (required.equals("Sword")) { + + if (wb.isTwoHanded()) + return 105; + else + return 98; + + } else if (required.equals("Staff") || required.equals("Pole Arm")) { + return 85; + } else if (required.equals("Spear")) { + return 92; + } else if (required.equals("Hammer") || required.equals("Axe")) { + if (wb.isTwoHanded()) { + return 105; + } else if (mastery.equals("Throwing")) { + return 115; + } else { + return 100; + } + } else if (required.equals("Dagger")) { + if (mastery.equals("Throwing")) { + return 117; + } else { + return 81; + } + } else if (required.equals("Crossbow")) { + return 110; + } else if (required.equals("Bow")) { + return 109; + } else if (wb.isTwoHanded()) { + return 105; + } else { + return 100; + } + } + + private static boolean testPassive(AbstractCharacter source, AbstractCharacter target, String type) { + + float chance = target.getPassiveChance(type, source.getLevel(), true); + + if (chance == 0f) + return false; + + //max 75% chance of passive to fire + + if (chance > 75f) + chance = 75f; + + int roll = ThreadLocalRandom.current().nextInt(100); + + return roll < chance; + + } - PlayerCharacter player; - Mob pet; - int targetType; - AbstractWorldObject target; + private static void updateAttackTimers(PlayerCharacter pc, AbstractWorldObject target, boolean attack) { - if (TargetedActionMsg.un2cnt == 60 || TargetedActionMsg.un2cnt == 70) - return; + //Set Attack Timers - player = SessionManager.getPlayerCharacter(origin); + if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) + pc.setLastPlayerAttackTime(); + else + pc.setLastMobAttackTime(); + } - if (player == null) - return; + public static float getWeaponRange(ItemBase weapon, PlayerBonuses bonus) { - pet = player.getPet(); + float rangeMod = 1.0f; - if (pet == null) - return; + if (weapon == null) + return 0f; - targetType = msg.getTargetType(); + if (bonus != null) + rangeMod += bonus.getFloatPercentAll(ModType.WeaponRange, SourceType.None); - if (targetType == GameObjectType.PlayerCharacter.ordinal()) - target = PlayerCharacter.getFromCache(msg.getTargetID()); - else if (targetType == GameObjectType.Building.ordinal()) - target = BuildingManager.getBuildingFromCache(msg.getTargetID()); - else if (targetType == GameObjectType.Mob.ordinal()) - target = Mob.getFromCache(msg.getTargetID()); - else { - pet.setCombatTarget(null); - return; //not valid type to attack - } + return weapon.getRange() * rangeMod; + } - if (pet.equals(target)) - return; + public static void toggleCombat(ToggleCombatMsg msg, ClientConnection origin) { + toggleCombat(msg.toggleCombat(), origin); + } - // quit of the combat target is already the current combat target - // or there is no combat target + public static void toggleCombat(SetCombatModeMsg msg, ClientConnection origin) { + toggleCombat(msg.getToggle(), origin); + } - if (target == null || target == pet.getCombatTarget()) - return; + private static void toggleCombat(boolean toggle, ClientConnection origin) { + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); - //set sources target - pet.setCombatTarget(target); - // setFirstHitCombatTarget(player,target); + if (pc == null) + return; - //put in combat if not already - if (!pet.isCombat()) - pet.setCombat(true); + pc.setCombat(toggle); - //make character stand if sitting - if (pet.isSit()) - toggleSit(false, origin); + if (!toggle) // toggle is move it to false so clear combat target + pc.setCombatTarget(null); //clear last combat target - } + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); + } - private static void removeAttackTimers(AbstractCharacter ac) { + private static void toggleSit(boolean toggle, ClientConnection origin) { - JobContainer main; - JobContainer off; + PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); - if (ac == null) - return; + if (pc == null) + return; - main = ac.getTimers().get("Attack" + MBServerStatics.SLOT_MAINHAND); - off = ac.getTimers().get("Attack" + MBServerStatics.SLOT_OFFHAND); + pc.setSit(toggle); - if (main != null) - JobScheduler.getInstance().cancelScheduledJob(main); + UpdateStateMsg rwss = new UpdateStateMsg(); + rwss.setPlayer(pc); + DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); + } - ac.getTimers().remove("Attack" + MBServerStatics.SLOT_MAINHAND); + public static boolean NotInRange(AbstractCharacter ac, AbstractWorldObject target, float range) { - if (off != null) - JobScheduler.getInstance().cancelScheduledJob(off); + Vector3fImmutable sl = ac.getLoc(); + Vector3fImmutable tl = target.getLoc(); - ac.getTimers().remove("Attack" + MBServerStatics.SLOT_OFFHAND); + //add Hitbox to range. - ac.setCombatTarget(null); + range += (calcHitBox(ac) + calcHitBox(target)); - } + float magnitudeSquared = tl.distanceSquared(sl); - /** - * Begin Attacking - */ - public static void doCombat(AbstractCharacter ac, int slot) { + return magnitudeSquared > range * range; - int ret = 0; + } - if (ac == null) - return; + //Called when character takes damage. + public static void handleRetaliate(AbstractCharacter tarAc, AbstractCharacter ac) { - // Attempt to eat null targets until we can clean - // up this unholy mess and refactor it into a thread. + if (ac == null || tarAc == null) + return; + if (ac.equals(tarAc)) + return; - ret = attemptCombat(ac, slot); + if (tarAc.isMoving() && tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) + return; - //handle pets - if (ret < 2 && ac.getObjectType().equals(GameObjectType.Mob)) { - Mob mob = (Mob) ac; - if (mob.isPet()) { - return; - } - } + if (!tarAc.isAlive() || !ac.isAlive()) + return; - //ret values - //0: not valid attack, fail attack - //1: cannot attack, wrong hand - //2: valid attack - //3: cannot attack currently, continue checking + boolean isCombat = tarAc.isCombat(); - if (ret == 0 || ret == 1) { + //If target in combat and has no target, then attack back - //Could not attack, clear timer + AbstractWorldObject awoCombTar = tarAc.getCombatTarget(); - ConcurrentHashMap timers = ac.getTimers(); + if ((tarAc.isCombat() && awoCombTar == null) || (isCombat && awoCombTar != null && (!awoCombTar.isAlive() || tarAc.isCombat() && NotInRange(tarAc, awoCombTar, tarAc.getRange()))) || (tarAc != null && tarAc.getObjectType() == GameObjectType.Mob && ((Mob) tarAc).isSiege())) + if (tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) { // we are in combat with no valid target - if (timers != null && timers.containsKey("Attack" + slot)) - timers.remove("Attack" + slot); + PlayerCharacter pc = (PlayerCharacter) tarAc; + tarAc.setCombatTarget(ac); + pc.setLastTarget(ac.getObjectType(), ac.getObjectUUID()); - //clear combat target if not valid attack - if (ret == 0) - ac.setCombatTarget(null); + if (tarAc.getTimers() != null) + if (!tarAc.getTimers().containsKey("Attack" + MBServerStatics.SLOT_MAINHAND)) + CombatManager.AttackTarget((PlayerCharacter) tarAc, tarAc.getCombatTarget()); + } - } else if (ret == 3) { - //Failed but continue checking. reset timer - createTimer(ac, slot, 5, false); - } - } + //Handle pet retaliate if assist is on and pet doesn't have a target. - /** - * Verify can attack target - */ - private static int attemptCombat(AbstractCharacter abstractCharacter, int slot) { + if (tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) { - if (abstractCharacter == null) { - // debugCombat(ac, "Source is null"); - return 0; - } + Mob pet = ((PlayerCharacter) tarAc).getPet(); - try { - //Make sure player can attack - PlayerBonuses bonus = abstractCharacter.getBonuses(); - - if (bonus != null && bonus.getBool(ModType.ImmuneToAttack, SourceType.None)) - return 0; - - AbstractWorldObject target = abstractCharacter.getCombatTarget(); - - if (target == null) { - return 0; - } - - - //target must be valid type - if (AbstractWorldObject.IsAbstractCharacter(target)) { - AbstractCharacter tar = (AbstractCharacter) target; - //must be alive, attackable and in World - if (!tar.isAlive()) { - return 0; - } else if (tar.isSafeMode()) { - return 0; - } else if (!tar.isActive()) { - return 0; - } - - if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getTimers().get("Attack" + slot) == null) { - if (!((PlayerCharacter) abstractCharacter).canSee((PlayerCharacter) target)) { - return 0; - } - } - - //must not be immune to all or immune to attack - Resists res = tar.getResists(); - bonus = tar.getBonuses(); - if (bonus != null && !bonus.getBool(ModType.NoMod, SourceType.ImmuneToAttack)) { - if (res != null) { - if (res.immuneToAll() || res.immuneToAttacks()) { - return 0; - } - } - } - } else if (target.getObjectType().equals(GameObjectType.Building)) { - Building tar = (Building) target; - - // Cannot attack an invuln building - - if (tar.isVulnerable() == false) { - return 0; - } - - } else { - return 0; //only characters and buildings may be attacked - } - - //source must be in world and alive - if (!abstractCharacter.isActive()) { - return 0; - } else if (!abstractCharacter.isAlive()) { - return 0; - } - - //make sure source is in combat mode - if (!abstractCharacter.isCombat()) { - return 0; - } - - //See if either target is in safe zone - if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectType().equals(GameObjectType.PlayerCharacter)) { - if (((PlayerCharacter) abstractCharacter).inSafeZone() || ((PlayerCharacter) target).inSafeZone()) { - return 0; - } - } - - if (!(slot == MBServerStatics.SLOT_MAINHAND || slot == MBServerStatics.SLOT_OFFHAND)) { - return 0; - } - - if (abstractCharacter.getCharItemManager() == null) { - return 0; - } - - //get equippment - ConcurrentHashMap equipped = abstractCharacter.getCharItemManager().getEquipped(); - boolean hasNoWeapon = false; - - if (equipped == null) { - return 0; - } - - //get Weapon - boolean isWeapon = true; - Item weapon = equipped.get(slot); - ItemBase wb = null; - if (weapon == null) { - isWeapon = false; - } else { - ItemBase ib = weapon.getItemBase(); - if (ib == null || !ib.getType().equals(ItemType.WEAPON)) { - isWeapon = false; - } else { - wb = ib; - } - } - - //if no weapon, see if other hand has a weapon - if (!isWeapon) { - //no weapon, see if other hand has a weapon - if (slot == MBServerStatics.SLOT_MAINHAND) { - //make sure offhand has weapon, not shield - Item weaponOff = equipped.get(MBServerStatics.SLOT_OFFHAND); - if (weaponOff != null) { - ItemBase ib = weaponOff.getItemBase(); - if (ib == null || !ib.getType().equals(ItemType.WEAPON)) { - hasNoWeapon = true; - } else { - // debugCombat(ac, "mainhand, weapon in other hand"); - return 1; //no need to attack with this hand - } - } else { - hasNoWeapon = true; - } - } else { - if (equipped.get(MBServerStatics.SLOT_MAINHAND) == null) { - // debgCombat(ac, "offhand, weapon in other hand"); - return 1; //no need to attack with this hand - } - } - } - - //Source can attack. - //NOTE Don't 'return;' beyond this point until timer created - boolean attackFailure = false; - - //Target can't attack on move with ranged weapons. - if ((wb != null) && (wb.getRange() > 35f) && abstractCharacter.isMoving()) { - // debugCombat(ac, "Cannot attack with throwing weapon while moving"); - attackFailure = true; - } - - //if not enough stamina, then skip attack - if (wb == null) { - if (abstractCharacter.getStamina() < 1) { - // debugCombat(ac, "Not enough stamina to attack"); - attackFailure = true; - } - } else if (abstractCharacter.getStamina() < wb.getWeight()) { - // debugCombat(ac, "Not enough stamina to attack"); - attackFailure = true; - } - - //skipping for now to test out mask casting. - // //if attacker is casting, then skip this attack - // if (ac.getLastPower() != null) { - // debugCombat(ac, "Cannot attack, curently casting"); - // attackFailure = true; - // } - //see if attacker is stunned. If so, stop here - bonus = abstractCharacter.getBonuses(); - if (bonus != null && bonus.getBool(ModType.Stunned, SourceType.None)) { - // debugCombat(ac, "Cannot attack while stunned"); - attackFailure = true; - } - - //Get Range of weapon - float range; - if (hasNoWeapon) { - range = MBServerStatics.NO_WEAPON_RANGE; - } else { - range = getWeaponRange(wb, bonus); - } - - if (abstractCharacter.getObjectType() == GameObjectType.Mob) { - Mob minion = (Mob) abstractCharacter; - if (minion.isSiege()) { - range = 300f; - } - } - - //Range check. - if (NotInRange(abstractCharacter, target, range)) { - //target is in stealth and can't be seen by source - if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) { - if (!((PlayerCharacter) abstractCharacter).canSee((PlayerCharacter) target)) { - // debugCombat(ac, "cannot see target."); - return 0; - } - } - attackFailure = true; - } - - //handle pet, skip timers (handled by AI) - if (abstractCharacter.getObjectType().equals(GameObjectType.Mob)) { - Mob mob = (Mob) abstractCharacter; - if (mob.isPet()) { - attack(abstractCharacter, target, weapon, wb, (slot == MBServerStatics.SLOT_MAINHAND) ? true : false); - return 2; - } - } - - //TODO Verify attacker has los (if not ranged weapon). - if (!attackFailure) { - if (hasNoWeapon || abstractCharacter.getObjectType().equals(GameObjectType.Mob)) { - createTimer(abstractCharacter, slot, 20, true); //2 second for no weapon - } else { - int wepSpeed = (int) (wb.getSpeed()); - if (weapon != null && weapon.getBonusPercent(ModType.WeaponSpeed, SourceType.None) != 0f) //add weapon speed bonus - { - wepSpeed *= (1 + weapon.getBonus(ModType.WeaponSpeed, SourceType.None)); - } - if (abstractCharacter.getBonuses() != null && abstractCharacter.getBonuses().getFloatPercentAll(ModType.AttackDelay, SourceType.None) != 0f) //add effects speed bonus - { - wepSpeed *= (1 + abstractCharacter.getBonuses().getFloatPercentAll(ModType.AttackDelay, SourceType.None)); - } - if (wepSpeed < 10) { - wepSpeed = 10; //Old was 10, but it can be reached lower with legit buffs,effects. - } - createTimer(abstractCharacter, slot, wepSpeed, true); - } - - if (target == null) - return 0; - - attack(abstractCharacter, target, weapon, wb, (slot == MBServerStatics.SLOT_MAINHAND) ? true : false); - } else { - // changed this to half a second to make combat attempts more aggressive than movement sync - createTimer(abstractCharacter, slot, 5, false); //0.5 second timer if attack fails - //System.out.println("Attack attempt failed"); - } - - } catch (Exception e) { - return 0; - } - return 2; - } - - private static void debugCombat(AbstractCharacter ac, String reason) { - if (ac == null) { - return; - } - - //if DebugMeleeSync is on, then debug reason for melee failure - if (ac.getDebug(64)) { - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { - String out = "Attack Failure: " + reason; - ChatManager.chatSystemInfo((PlayerCharacter) ac, out); - } - } - } - - private static void debugCombatRange(AbstractCharacter ac, Vector3fImmutable sl, Vector3fImmutable tl, float range, float distance) { - if (ac == null || sl == null || tl == null) { - return; - } - - //if DebugMeleeSync is on, then debug reason for melee failure - if (ac.getDebug(64)) { - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { - String out = "Attack Failure: Out of Range: Range: " + distance + ", weaponRange: " + range; - out += ", sourceLoc: " + sl.x + ", " + sl.y + ", " + sl.z; - out += ", targetLoc: " + tl.x + ", " + tl.y + ", " + tl.z; - ChatManager.chatSystemInfo((PlayerCharacter) ac, out); - } - } - } - - private static void createTimer(AbstractCharacter ac, int slot, int time, boolean success) { - ConcurrentHashMap timers = ac.getTimers(); - if (timers != null) { - AttackJob aj = new AttackJob(ac, slot, success); - JobContainer job; - job = JobScheduler.getInstance().scheduleJob(aj, (time * 100)); - timers.put("Attack" + slot, job); - } else { - Logger.error("Unable to find Timers for Character " + ac.getObjectUUID()); - } - } - - /** - * Attempt to attack target - */ - private static void attack(AbstractCharacter ac, AbstractWorldObject target, Item weapon, ItemBase wb, boolean mainHand) { - - float atr; - int minDamage, maxDamage; - int errorTrack = 0; - - try { - - if (ac == null) - return; - - if (target == null) - return; - - if (mainHand) { - atr = ac.getAtrHandOne(); - minDamage = ac.getMinDamageHandOne(); - maxDamage = ac.getMaxDamageHandOne(); - } else { - atr = ac.getAtrHandTwo(); - minDamage = ac.getMinDamageHandTwo(); - maxDamage = ac.getMaxDamageHandTwo(); - } - - boolean tarIsRat = false; - - if (target.getObjectTypeMask() == MBServerStatics.MASK_RAT) - tarIsRat = true; - else if (target.getObjectType() == GameObjectType.PlayerCharacter) { - PlayerCharacter pTar = (PlayerCharacter) target; - for (Effect eff : pTar.getEffects().values()) { - if (eff.getPowerToken() == 429513599 || eff.getPowerToken() == 429415295) { - tarIsRat = true; - } - } - } - - //Dont think we need to do this anymore. - if (tarIsRat) { - //strip away current % dmg buffs then add with rat % - if (ac.getBonuses().getFloatPercentAll(ModType.Slay, SourceType.Rat) != 0) { - - - float percent = 1 + ac.getBonuses().getFloatPercentAll(ModType.Slay, SourceType.Rat); - - minDamage *= percent; - maxDamage *= percent; - } - - } - - errorTrack = 1; - - //subtract stamina - if (wb == null) { - ac.modifyStamina(-0.5f, ac, true); - } else { - float stam = wb.getWeight() / 3; - stam = (stam < 1) ? 1 : stam; - ac.modifyStamina(-(stam), ac, true); - } - - ac.cancelOnAttackSwing(); - - errorTrack = 2; - - //set last time this player has attacked something. - if (target.getObjectType().equals(GameObjectType.PlayerCharacter) && target.getObjectUUID() != ac.getObjectUUID() && ac.getObjectType() == GameObjectType.PlayerCharacter) { - ac.setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); - ((PlayerCharacter) target).setTimeStamp("LastCombatPlayer", System.currentTimeMillis()); - } else { - ac.setTimeStamp("LastCombatMob", System.currentTimeMillis()); - } - - errorTrack = 3; - - //Get defense for target - float defense; - if (target.getObjectType().equals(GameObjectType.Building)) { - - if (BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null) { - ac.setCombatTarget(null); - return; - } - defense = 0; - - Building building = (Building) target; - if (building.getParentZone() != null && building.getParentZone().isPlayerCity()) { - - if (System.currentTimeMillis() > building.getTimeStamp("CallForHelp")) { - building.getTimestamps().put("CallForHelp", System.currentTimeMillis() + 15000); - int count = 0; - for (Mob mob : building.getParentZone().zoneMobSet) { - if (!mob.isPlayerGuard()) - continue; - if (mob.getCombatTarget() != null) - continue; - if (mob.getGuild() != null && building.getGuild() != null) - if (!Guild.sameGuild(mob.getGuild().getNation(), building.getGuild().getNation())) - continue; - - if (mob.getLoc().distanceSquared2D(building.getLoc()) > sqr(300)) - continue; - - if (count == 5) - count++; - - mob.setCombatTarget(ac); - } - } - } - } else { - AbstractCharacter tar = (AbstractCharacter) target; - defense = tar.getDefenseRating(); - //Handle target attacking back if in combat and has no other target - handleRetaliate(tar, ac); - } - - errorTrack = 4; - - //Get hit chance - int chance; - float dif = atr - defense; - if (dif > 100) { - chance = 94; - } else if (dif < -100) { - chance = 4; - } else { - chance = (int) ((0.45 * dif) + 49); - } - - errorTrack = 5; - - //calculate hit/miss - int roll = ThreadLocalRandom.current().nextInt(100); - DeferredPowerJob dpj = null; - if (roll < chance) { - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { - updateAttackTimers((PlayerCharacter) ac, target, true); - } - - boolean skipPassives = false; - PlayerBonuses bonuses = ac.getBonuses(); - if (bonuses != null && bonuses.getBool(ModType.IgnorePassiveDefense, SourceType.None)) { - skipPassives = true; - } - - AbstractCharacter tarAc = null; - if (AbstractWorldObject.IsAbstractCharacter(target)) { - tarAc = (AbstractCharacter) target; - } - - errorTrack = 6; - - // Apply Weapon power effect if any. don't try to apply twice if - // dual wielding. Perform after passive test for sync purposes. - - - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { - dpj = ((PlayerCharacter) ac).getWeaponPower(); - if (dpj != null) { - PlayerBonuses bonus = ac.getBonuses(); - float attackRange = getWeaponRange(wb, bonus); - dpj.attack(target, attackRange); - - if (dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) - ((PlayerCharacter) ac).setWeaponPower(dpj); - } - } - //check to apply second backstab. - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && !mainHand) { - dpj = ((PlayerCharacter) ac).getWeaponPower(); - if (dpj != null && dpj.getPower() != null && (dpj.getPowerToken() == -1851459567 || dpj.getPowerToken() == -1851489518)) { - float attackRange = getWeaponRange(wb,bonuses); - dpj.attack(target, attackRange); - } - } - - errorTrack = 7; - - //Hit, check if passive kicked in - boolean passiveFired = false; - if (!skipPassives && tarAc != null) { - if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { - - //Handle Block passive - if (testPassive(ac, tarAc, "Block") && canTestBlock(ac, target)) { - - if (!target.isAlive()) - return; - - sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_BLOCK, dpj, mainHand); - passiveFired = true; - } - - //Handle Parry passive - if (!passiveFired) { - if (canTestParry(ac, target) && testPassive(ac, tarAc, "Parry")) { - if (!target.isAlive()) - return; - sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_PARRY, dpj, mainHand); - passiveFired = true; - } - } - } - - errorTrack = 8; - - //Handle Dodge passive - if (!passiveFired) { - if (testPassive(ac, tarAc, "Dodge")) { - - if (!target.isAlive()) - return; - - sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_DODGE, dpj, mainHand); - passiveFired = true; - } - } - } - - //return if passive (Block, Parry, Dodge) fired - - if (passiveFired) - return; - - errorTrack = 9; - - //Hit and no passives - //if target is player, set last attack timestamp - if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) { - updateAttackTimers((PlayerCharacter) target, ac, false); - } - - //Get damage Type - DamageType damageType; - if (wb != null) { - damageType = wb.getDamageType(); - } else if (ac.getObjectType().equals(GameObjectType.Mob) && ((Mob) ac).isSiege()) { - damageType = DamageType.Siege; - } else { - damageType = DamageType.Crush; - } - - errorTrack = 10; - - //Get target resists - Resists resists = null; - - if (tarAc != null) { - resists = tarAc.getResists(); - } else if (target.getObjectType().equals(GameObjectType.Building)) { - resists = ((Building) target).getResists(); - } - - //make sure target is not immune to damage type; - if (resists != null && resists.immuneTo(damageType)) { - sendCombatMessage(ac, target, 0f, wb, dpj, mainHand); - return; - } - - // PowerProjectileMsg ppm = new PowerProjectileMsg(ac,tarAc); - // DispatchMessage.dispatchMsgToInterestArea(ac, ppm, DispatchChannel.SECONDARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); - // - - errorTrack = 11; - - //Calculate Damage done - - float damage; - - if (wb != null) { - damage = calculateDamage(ac, tarAc, minDamage, maxDamage, damageType, resists); - } else { - damage = calculateDamage(ac, tarAc, minDamage, maxDamage, damageType, resists); - } - - float d = 0f; - - errorTrack = 12; - - //Subtract Damage from target's health - if (tarAc != null) { - if (tarAc.isSit()) { - damage *= 2.5f; //increase damage if sitting - } - if (tarAc.getObjectType() == GameObjectType.Mob) { - ac.setHateValue(damage * MBServerStatics.PLAYER_COMBAT_HATE_MODIFIER); - ((Mob) tarAc).handleDirectAggro(ac); - } - - if (tarAc.getHealth() > 0) - d = tarAc.modifyHealth(-damage, ac, false); - - } else if (target.getObjectType().equals(GameObjectType.Building)) { - - if (BuildingManager.getBuildingFromCache(target.getObjectUUID()) == null) { - ac.setCombatTarget(null); - return; - } - if (target.getHealth() > 0) - d = ((Building) target).modifyHealth(-damage, ac); - } - - errorTrack = 13; - - //Test to see if any damage needs done to weapon or armor - testItemDamage(ac, target, weapon, wb); - - // if target is dead, we got the killing blow, remove attack timers on our weapons - if (tarAc != null && !tarAc.isAlive()) { - removeAttackTimers(ac); - } - - //test double death fix - if (d != 0) { - sendCombatMessage(ac, target, damage, wb, dpj, mainHand); //send damage message - } - - errorTrack = 14; - - //handle procs - if (weapon != null && tarAc != null && tarAc.isAlive()) { - ConcurrentHashMap effects = weapon.getEffects(); - for (Effect eff : effects.values()) { - if (eff == null) { - continue; - } - HashSet aems = eff.getEffectModifiers(); - if (aems != null) { - for (AbstractEffectModifier aem : aems) { - if (!tarAc.isAlive()) { - break; - } - if (aem instanceof WeaponProcEffectModifier) { - int procChance = ThreadLocalRandom.current().nextInt(100); - if (procChance < MBServerStatics.PROC_CHANCE) { - ((WeaponProcEffectModifier) aem).applyProc(ac, target); - } - } - } - } - } - } + if (pet != null && pet.assist() && pet.getCombatTarget() == null) + pet.setCombatTarget(ac); + } - errorTrack = 15; + //Handle Mob Retaliate. - //handle damage shields - if (ac.isAlive() && tarAc != null && tarAc.isAlive()) { - handleDamageShields(ac, tarAc, damage); - } - } else { - int animationOverride = 0; - // Apply Weapon power effect if any. - // don't try to apply twice if dual wielding. - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter) && (mainHand || wb.isTwoHanded())) { - dpj = null; - dpj = ((PlayerCharacter) ac).getWeaponPower(); + if (tarAc.getObjectType() == GameObjectType.Mob) { - if (dpj != null) { - PowersBase wp = dpj.getPower(); - if (wp.requiresHitRoll() == false) { - PlayerBonuses bonus = ac.getBonuses(); - float attackRange = getWeaponRange(wb,bonus); - dpj.attack(target, attackRange); - } else { - ((PlayerCharacter) ac).setWeaponPower(null); - } + Mob retaliater = (Mob) tarAc; - } - } - if (target.getObjectType() == GameObjectType.Mob) { - ((Mob) target).handleDirectAggro(ac); - } + if (retaliater.getCombatTarget() != null && !retaliater.isSiege()) + return; - errorTrack = 17; + if (ac.getObjectType() == GameObjectType.Mob && retaliater.isSiege()) + return; - //miss, Send miss message - sendCombatMessage(ac, target, 0f, wb, dpj, mainHand); + retaliater.setCombatTarget(ac); - //if attacker is player, set last attack timestamp - if (ac.getObjectType().equals(GameObjectType.PlayerCharacter)) { - updateAttackTimers((PlayerCharacter) ac, target, true); - } - } + } + } - errorTrack = 18; + public static void handleDamageShields(AbstractCharacter ac, AbstractCharacter target, float damage) { - //cancel effects that break on attack or attackSwing - ac.cancelOnAttack(); + if (ac == null || target == null) + return; - } catch (Exception e) { - Logger.error(ac.getName() + ' ' + errorTrack + ' ' + e.toString()); - } - } + PlayerBonuses bonuses = target.getBonuses(); - public static boolean canTestParry(AbstractCharacter ac, AbstractWorldObject target) { + if (bonuses != null) { - if (ac == null || target == null || !AbstractWorldObject.IsAbstractCharacter(target)) - return false; + ConcurrentHashMap damageShields = bonuses.getDamageShields(); + float total = 0; - AbstractCharacter tar = (AbstractCharacter) target; + for (DamageShield ds : damageShields.values()) { - CharacterItemManager acItem = ac.getCharItemManager(); - CharacterItemManager tarItem = tar.getCharItemManager(); + //get amount to damage back - if (acItem == null || tarItem == null) - return false; + float amount; - Item acMain = acItem.getItemFromEquipped(1); - Item acOff = acItem.getItemFromEquipped(2); - Item tarMain = tarItem.getItemFromEquipped(1); - Item tarOff = tarItem.getItemFromEquipped(2); + if (ds.usePercent()) + amount = damage * ds.getAmount() / 100; + else + amount = ds.getAmount(); - return !isRanged(acMain) && !isRanged(acOff) && !isRanged(tarMain) && !isRanged(tarOff); - } + //get resisted damage for damagetype - public static boolean canTestBlock(AbstractCharacter ac, AbstractWorldObject target) { + Resists resists = ac.getResists(); - if (ac == null || target == null || !AbstractWorldObject.IsAbstractCharacter(target)) - return false; + if (resists != null) + amount = resists.getResistedDamage(target, ac, ds.getDamageType(), amount, 0); - AbstractCharacter tar = (AbstractCharacter) target; + total += amount; + } - CharacterItemManager acItem = ac.getCharItemManager(); - CharacterItemManager tarItem = tar.getCharItemManager(); + if (total > 0) { - if (acItem == null || tarItem == null) - return false; + //apply Damage back + ac.modifyHealth(-total, target, true); - Item tarOff = tarItem.getItemFromEquipped(2); + TargetedActionMsg cmm = new TargetedActionMsg(ac, ac, total, 0); + DispatchMessage.sendToAllInRange(target, cmm); + } + } + } - if (tarOff == null) - return false; + public static float calcHitBox(AbstractWorldObject ac) { - return tarOff.getItemBase().isShield() != false; - } + //TODO Figure out how Str Affects HitBox - private static boolean isRanged(Item item) { + float hitBox = 1; - if (item == null) - return false; + switch (ac.getObjectType()) { + case PlayerCharacter: + PlayerCharacter pc = (PlayerCharacter) ac; + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) { + Logger.info("Hit box radius for " + pc.getFirstName() + " is " + ((int) pc.statStrBase / 20f)); + } + hitBox = 1.5f + (int) ((PlayerCharacter) ac).statStrBase / 20f; + break; - ItemBase ib = item.getItemBase(); + case Mob: + Mob mob = (Mob) ac; + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info("Hit box radius for " + mob.getFirstName() + + " is " + ((Mob) ac).getMobBase().getHitBoxRadius()); - if (ib == null) - return false; + hitBox = ((Mob) ac).getMobBase().getHitBoxRadius(); + break; + case Building: + Building building = (Building) ac; + if (building.getBlueprint() == null) + return 32; + hitBox = Math.max(building.getBlueprint().getBuildingGroup().getExtents().x, + building.getBlueprint().getBuildingGroup().getExtents().y); + if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) + Logger.info("Hit box radius for " + building.getName() + " is " + hitBox); + break; - if (ib.getType().equals(ItemType.WEAPON) == false) - return false; + } + return hitBox; + } - return ib.getRange() > MBServerStatics.RANGED_WEAPON_RANGE; + private static void testItemDamage(AbstractCharacter ac, AbstractWorldObject awo, Item weapon, ItemBase wb) { + if (ac == null) + return; - } + //get chance to damage - private static float calculateDamage(AbstractCharacter source, AbstractCharacter target, float minDamage, float maxDamage, DamageType damageType, Resists resists) { - //get range between min and max - float range = maxDamage - minDamage; + int chance = 4500; - //Damage is calculated twice to average a more central point - float damage = ThreadLocalRandom.current().nextFloat() * range; - damage = (damage + (ThreadLocalRandom.current().nextFloat() * range)) * .5f; + if (wb != null) + if (wb.isGlass()) //glass used weighted so fast weapons don't break faster + chance = 9000 / wb.getWeight(); - //put it back between min and max - damage += minDamage; + //test damaging attackers weapon - //calculate resists in if any - if (resists != null) { - return resists.getResistedDamage(source, target, damageType, damage, 0); - } else { - return damage; - } - } + int takeDamage = ThreadLocalRandom.current().nextInt(chance); - private static void sendPassiveDefenseMessage(AbstractCharacter source, ItemBase wb, AbstractWorldObject target, int passiveType, DeferredPowerJob dpj, boolean mainHand) { + if (takeDamage == 0 && wb != null && (ac.getObjectType().equals(GameObjectType.PlayerCharacter))) + ac.getCharItemManager().damageItem(weapon, 1); - int swingAnimation = getSwingAnimation(wb, dpj, mainHand); - if (dpj != null) { - if (PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) - swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); - } - TargetedActionMsg cmm = new TargetedActionMsg(source, swingAnimation, target, passiveType); - DispatchMessage.sendToAllInRange(target, cmm); + //test damaging targets gear - } + takeDamage = ThreadLocalRandom.current().nextInt(chance); - private static void sendCombatMessage(AbstractCharacter source, AbstractWorldObject target, float damage, ItemBase wb, DeferredPowerJob dpj, boolean mainHand) { - - int swingAnimation = getSwingAnimation(wb, dpj, mainHand); - - if (dpj != null) { - if (PowersManager.AnimationOverrides.containsKey(dpj.getAction().getEffectID())) - swingAnimation = PowersManager.AnimationOverrides.get(dpj.getAction().getEffectID()); - } - - if (source.getObjectType() == GameObjectType.PlayerCharacter) { - for (Effect eff : source.getEffects().values()) { - if (eff.getPower() != null && (eff.getPower().getToken() == 429506943 || eff.getPower().getToken() == 429408639 || eff.getPower().getToken() == 429513599 || eff.getPower().getToken() == 429415295)) - swingAnimation = 0; - } - } - TargetedActionMsg cmm = new TargetedActionMsg(source, target, damage, swingAnimation); - DispatchMessage.sendToAllInRange(target, cmm); - } - - public static int getSwingAnimation(ItemBase wb, DeferredPowerJob dpj, boolean mainHand) { - int token = 0; - if (dpj != null) { - token = (dpj.getPower() != null) ? dpj.getPower().getToken() : 0; - } - - if (token == 563721004) //kick animation - { - return 79; - } - - if (CombatManager.animation != 0) { - return CombatManager.animation; - } - - if (wb == null) { - return 75; - } - if (mainHand) { - if (wb.getAnimations().size() > 0) { - int animation = wb.getAnimations().get(0); - int random = ThreadLocalRandom.current().nextInt(wb.getAnimations().size()); - try { - animation = wb.getAnimations().get(random); - return animation; - } catch (Exception e) { - Logger.error(e.getMessage()); - return wb.getAnimations().get(0); - - } - - } else if (wb.getOffHandAnimations().size() > 0) { - int animation = wb.getOffHandAnimations().get(0); - int random = ThreadLocalRandom.current().nextInt(wb.getOffHandAnimations().size()); - try { - animation = wb.getOffHandAnimations().get(random); - return animation; - } catch (Exception e) { - Logger.error(e.getMessage()); - return wb.getOffHandAnimations().get(0); - - } - } - } else { - if (wb.getOffHandAnimations().size() > 0) { - int animation = wb.getOffHandAnimations().get(0); - int random = ThreadLocalRandom.current().nextInt(wb.getOffHandAnimations().size()); - try { - animation = wb.getOffHandAnimations().get(random); - return animation; - } catch (Exception e) { - Logger.error(e.getMessage()); - return wb.getOffHandAnimations().get(0); - - } - } else if (wb.getAnimations().size() > 0) { - int animation = wb.getAnimations().get(0); - int random = ThreadLocalRandom.current().nextInt(wb.getAnimations().size()); - try { - animation = wb.getAnimations().get(random); - return animation; - } catch (Exception e) { - Logger.error(e.getMessage()); - return wb.getAnimations().get(0); - - } - - } - } - - - String required = wb.getSkillRequired(); - String mastery = wb.getMastery(); - if (required.equals("Unarmed Combat")) { - return 75; - } else if (required.equals("Sword")) { - if (wb.isTwoHanded()) { - return 105; - } else { - return 98; - } - } else if (required.equals("Staff") || required.equals("Pole Arm")) { - return 85; - } else if (required.equals("Spear")) { - return 92; - } else if (required.equals("Hammer") || required.equals("Axe")) { - if (wb.isTwoHanded()) { - return 105; - } else if (mastery.equals("Throwing")) { - return 115; - } else { - return 100; - } - } else if (required.equals("Dagger")) { - if (mastery.equals("Throwing")) { - return 117; - } else { - return 81; - } - } else if (required.equals("Crossbow")) { - return 110; - } else if (required.equals("Bow")) { - return 109; - } else if (wb.isTwoHanded()) { - return 105; - } else { - return 100; - } - } - - private static boolean testPassive(AbstractCharacter source, AbstractCharacter target, String type) { - - float chance = target.getPassiveChance(type, source.getLevel(), true); - - if (chance == 0f) - return false; - - - //max 75% chance of passive to fire - if (chance > 75f) - chance = 75f; - - int roll = ThreadLocalRandom.current().nextInt(100); - - //Passive fired - //Passive did not fire - return roll < chance; - - } - - private static void updateAttackTimers(PlayerCharacter pc, AbstractWorldObject target, boolean attack) { - - //Set Attack Timers - if (target.getObjectType().equals(GameObjectType.PlayerCharacter)) - pc.setLastPlayerAttackTime(); - else - pc.setLastMobAttackTime(); - } - - public static float getWeaponRange(ItemBase weapon, PlayerBonuses bonus) { - if (weapon == null) - return 0f; - float rangeMod = 1.0f; - if (bonus != null) { - //rangeMod += bonus.getFloat(ModType.WeaponRange, SourceType.None); - rangeMod += bonus.getFloatPercentAll(ModType.WeaponRange, SourceType.None); - } - return weapon.getRange() * rangeMod; - } - - public static void toggleCombat(ToggleCombatMsg msg, ClientConnection origin) { - toggleCombat(msg.toggleCombat(), origin); - } - - public static void toggleCombat(SetCombatModeMsg msg, ClientConnection origin) { - toggleCombat(msg.getToggle(), origin); - } - - private static void toggleCombat(boolean toggle, ClientConnection origin) { - - PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); - - if (pc == null) - return; - - pc.setCombat(toggle); - - if (!toggle) // toggle is move it to false so clear combat target - pc.setCombatTarget(null); //clear last combat target - - UpdateStateMsg rwss = new UpdateStateMsg(); - rwss.setPlayer(pc); - DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, false, false); - } - - private static void toggleSit(boolean toggle, ClientConnection origin) { - - PlayerCharacter pc = SessionManager.getPlayerCharacter(origin); - - if (pc == null) - return; - - pc.setSit(toggle); - - UpdateStateMsg rwss = new UpdateStateMsg(); - rwss.setPlayer(pc); - DispatchMessage.dispatchMsgToInterestArea(pc, rwss, DispatchChannel.PRIMARY, MBServerStatics.CHARACTER_LOAD_RANGE, true, false); - } - - public static boolean NotInRange(AbstractCharacter ac, AbstractWorldObject target, float range) { - Vector3fImmutable sl = ac.getLoc(); - Vector3fImmutable tl = target.getLoc(); - //add Hitbox's to range. - range += (calcHitBox(ac) + calcHitBox(target)); - - float magnitudeSquared = tl.distanceSquared(sl); - - return magnitudeSquared > range * range; - - } - - //Called when character takes damage. - public static void handleRetaliate(AbstractCharacter tarAc, AbstractCharacter ac) { - if (ac == null || tarAc == null) { - return; - } - if (ac.equals(tarAc)) { - return; - } - - if (tarAc.isMoving() && tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) - return; - - if (!tarAc.isAlive() || !ac.isAlive()) - return; - boolean isCombat = tarAc.isCombat(); - //If target in combat and has no target, then attack back - AbstractWorldObject awoCombTar = tarAc.getCombatTarget(); - if ((tarAc.isCombat() && awoCombTar == null) || (isCombat && awoCombTar != null && (!awoCombTar.isAlive() || tarAc.isCombat() && NotInRange(tarAc, awoCombTar, tarAc.getRange()))) || (tarAc != null && tarAc.getObjectType() == GameObjectType.Mob && ((Mob) tarAc).isSiege())) { - // we are in combat with no valid target - if (tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) { - PlayerCharacter pc = (PlayerCharacter) tarAc; - tarAc.setCombatTarget(ac); - pc.setLastTarget(ac.getObjectType(), ac.getObjectUUID()); - if (tarAc.getTimers() != null) { - if (!tarAc.getTimers().containsKey("Attack" + MBServerStatics.SLOT_MAINHAND)) { - CombatManager.AttackTarget((PlayerCharacter) tarAc, tarAc.getCombatTarget()); - } - } - } - } - - //Handle pet retaliate if assist is on and pet doesn't have a target. - if (tarAc.getObjectType().equals(GameObjectType.PlayerCharacter)) { - Mob pet = ((PlayerCharacter) tarAc).getPet(); - if (pet != null && pet.assist() && pet.getCombatTarget() == null) { - pet.setCombatTarget(ac); - } - } - - //Handle Mob Retaliate. - if (tarAc.getObjectType() == GameObjectType.Mob) { - Mob retaliater = (Mob) tarAc; - if (retaliater.getCombatTarget() != null && !retaliater.isSiege()) - return; - if (ac.getObjectType() == GameObjectType.Mob && retaliater.isSiege()) - return; - retaliater.setCombatTarget(ac); - - } - } - - public static void handleDamageShields(AbstractCharacter ac, AbstractCharacter target, float damage) { - if (ac == null || target == null) { - return; - } - PlayerBonuses bonuses = target.getBonuses(); - if (bonuses != null) { - ConcurrentHashMap damageShields = bonuses.getDamageShields(); - float total = 0; - for (DamageShield ds : damageShields.values()) { - //get amount to damage back - float amount; - if (ds.usePercent()) { - amount = damage * ds.getAmount() / 100; - } else { - amount = ds.getAmount(); - } - - //get resisted damage for damagetype - Resists resists = ac.getResists(); - if (resists != null) { - amount = resists.getResistedDamage(target, ac, ds.getDamageType(), amount, 0); - } - - total += amount; - } - if (total > 0) { - //apply Damage back - ac.modifyHealth(-total, target, true); - - TargetedActionMsg cmm = new TargetedActionMsg(ac, ac, total, 0); - DispatchMessage.sendToAllInRange(target, cmm); - - } - } - } - - public static float calcHitBox(AbstractWorldObject ac) { - //TODO Figure out how Str Affects HitBox - float hitBox = 1; - switch (ac.getObjectType()) { - case PlayerCharacter: - PlayerCharacter pc = (PlayerCharacter) ac; - if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) { - Logger.info("Hit box radius for " + pc.getFirstName() + " is " + ((int) pc.statStrBase / 20f)); - } - hitBox = 1.5f + (int) ((PlayerCharacter) ac).statStrBase / 20f; - break; - - case Mob: - Mob mob = (Mob) ac; - if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) - Logger.info("Hit box radius for " + mob.getFirstName() - + " is " + ((Mob) ac).getMobBase().getHitBoxRadius()); - - hitBox = ((Mob) ac).getMobBase().getHitBoxRadius(); - break; - case Building: - Building building = (Building) ac; - if (building.getBlueprint() == null) - return 32; - hitBox = Math.max(building.getBlueprint().getBuildingGroup().getExtents().x, - building.getBlueprint().getBuildingGroup().getExtents().y); - if (MBServerStatics.COMBAT_TARGET_HITBOX_DEBUG) - Logger.info("Hit box radius for " + building.getName() + " is " + hitBox); - break; - - } - return hitBox; - } - - private static void testItemDamage(AbstractCharacter ac, AbstractWorldObject awo, Item weapon, ItemBase wb) { - if (ac == null) { - return; - } - - //get chance to damage - int chance = 4500; - if (wb != null) { - if (wb.isGlass()) //glass used weighted so fast weapons don't break faster - { - chance = 9000 / wb.getWeight(); - } - } - //test damaging attackers weapon - int takeDamage = ThreadLocalRandom.current().nextInt(chance); - if (takeDamage == 0 && wb != null && (ac.getObjectType().equals(GameObjectType.PlayerCharacter))) { - ac.getCharItemManager().damageItem(weapon, 1); - } - - //test damaging targets gear - takeDamage = ThreadLocalRandom.current().nextInt(chance); - if (takeDamage == 0 && awo != null && (awo.getObjectType().equals(GameObjectType.PlayerCharacter))) { - ((AbstractCharacter) awo).getCharItemManager().damageRandomArmor(1); - } - } + if (takeDamage == 0 && awo != null && (awo.getObjectType().equals(GameObjectType.PlayerCharacter))) + ((AbstractCharacter) awo).getCharItemManager().damageRandomArmor(1); + } } From 895b87ae051bdbde3569595a0c54872c5558787c Mon Sep 17 00:00:00 2001 From: MagicBot Date: Sun, 13 Aug 2023 08:16:02 -0400 Subject: [PATCH 5/8] Class cleanup. Formatting, squiggles and unused code/variables. --- src/engine/gameManager/CombatManager.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/engine/gameManager/CombatManager.java b/src/engine/gameManager/CombatManager.java index 29c37fc0..7bccb83b 100644 --- a/src/engine/gameManager/CombatManager.java +++ b/src/engine/gameManager/CombatManager.java @@ -750,7 +750,7 @@ public enum CombatManager { //Handle Dodge passive - if (!passiveFired) { + if (!passiveFired) if (testPassive(ac, tarAc, "Dodge")) { if (!target.isAlive()) @@ -759,7 +759,6 @@ public enum CombatManager { sendPassiveDefenseMessage(ac, wb, target, MBServerStatics.COMBAT_SEND_DODGE, dpj, mainHand); passiveFired = true; } - } } //return if passive (Block, Parry, Dodge) fired @@ -981,10 +980,8 @@ public enum CombatManager { if (acItem == null || tarItem == null) return false; - Item tarOff = tarItem.getItemFromEquipped(2); - if (tarOff == null) return false; @@ -1006,7 +1003,6 @@ public enum CombatManager { return ib.getRange() > MBServerStatics.RANGED_WEAPON_RANGE; - } private static float calculateDamage(AbstractCharacter source, AbstractCharacter target, float minDamage, float maxDamage, DamageType damageType, Resists resists) { From 4cd180141c9112ce26a3419fdc3ef321597d7377 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Sun, 13 Aug 2023 09:39:16 -0400 Subject: [PATCH 6/8] Read current repo branch using MB Devops script. --- src/engine/gameManager/ConfigManager.java | 34 ++++++++++++++++++- .../handlers/ArcLoginNotifyMsgHandler.java | 5 +++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/engine/gameManager/ConfigManager.java b/src/engine/gameManager/ConfigManager.java index 07aa3206..9ca6b598 100644 --- a/src/engine/gameManager/ConfigManager.java +++ b/src/engine/gameManager/ConfigManager.java @@ -18,6 +18,10 @@ import engine.server.login.LoginServer; import engine.server.world.WorldServer; import org.pmw.tinylog.Logger; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; @@ -107,11 +111,13 @@ public enum ConfigManager { public static LoginServer loginServer; public static Map regex = new HashMap<>(); + public static String currentRepoBranch = ""; + // Called at bootstrap: ensures that all config values are loaded. public static boolean init() { - Logger.info("Loading config from environment..."); + Logger.info("Loading config from environment."); for (ConfigManager configSetting : ConfigManager.values()) if (configMap.containsKey(configSetting.name())) @@ -123,6 +129,32 @@ public enum ConfigManager { return false; } + Logger.info("Determine current Repo branch"); + + File file = new File("mbbranch.sh"); + + if (file.exists() && !file.isDirectory()) { + + String[] command = {"./mbbranch"}; + + try { + + Process process = Runtime.getRuntime().exec(command); + BufferedReader reader = new BufferedReader(new InputStreamReader( + process.getInputStream())); + String string; + + while (true) { + if ((string = reader.readLine()) == null) + break; + currentRepoBranch += string; + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + // compile regex here Logger.info("Compiling regex"); diff --git a/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java b/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java index b9986be9..02b0518c 100644 --- a/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java +++ b/src/engine/net/client/handlers/ArcLoginNotifyMsgHandler.java @@ -67,6 +67,11 @@ public class ArcLoginNotifyMsgHandler extends AbstractClientMsgHandler { GuildManager.enterWorldMOTD(player); ChatManager.sendSystemMessage(player, ConfigManager.MB_WORLD_GREETING.getValue()); + // Send branch string if available from ConfigManager. + + if (ConfigManager.currentRepoBranch != "") + ChatManager.sendSystemMessage(player, ConfigManager.currentRepoBranch); + // Set player mask for QT if (player.getRace() != null && player.getRace().getToken() == -524731385) player.setObjectTypeMask(MBServerStatics.MASK_PLAYER | MBServerStatics.MASK_UNDEAD); From 04534e2ca6e36610740ad665da9021fedabae0fc Mon Sep 17 00:00:00 2001 From: MagicBot Date: Sun, 13 Aug 2023 09:43:06 -0400 Subject: [PATCH 7/8] Current repo branch reported during bootstrap. --- src/engine/gameManager/ConfigManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/gameManager/ConfigManager.java b/src/engine/gameManager/ConfigManager.java index 9ca6b598..9d61fcce 100644 --- a/src/engine/gameManager/ConfigManager.java +++ b/src/engine/gameManager/ConfigManager.java @@ -150,6 +150,7 @@ public enum ConfigManager { currentRepoBranch += string; } + Logger.info(currentRepoBranch); } catch (IOException e) { throw new RuntimeException(e); } From d21921cb6db28d159fd4aaeb3fb13bf5b35f2b81 Mon Sep 17 00:00:00 2001 From: MagicBot Date: Sun, 13 Aug 2023 09:44:28 -0400 Subject: [PATCH 8/8] Typo in command string. --- src/engine/gameManager/ConfigManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/gameManager/ConfigManager.java b/src/engine/gameManager/ConfigManager.java index 9d61fcce..7541a4e1 100644 --- a/src/engine/gameManager/ConfigManager.java +++ b/src/engine/gameManager/ConfigManager.java @@ -135,7 +135,7 @@ public enum ConfigManager { if (file.exists() && !file.isDirectory()) { - String[] command = {"./mbbranch"}; + String[] command = {"./mbbranch.sh"}; try {