Files
NGECore2/src/services/combat/CombatService.java
T

2010 lines
71 KiB
Java

/*******************************************************************************
* Copyright (c) 2013 <Project SWG>
*
* This File is part of NGECore2.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Using NGEngine to work with NGECore2 is making a combined work based on NGEngine.
* Therefore all terms and conditions of the GNU Lesser General Public License cover the combination.
******************************************************************************/
package services.combat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import protocol.swg.ObjControllerMessage;
import protocol.swg.PlayClientEffectLocMessage;
import protocol.swg.objectControllerObjects.CombatAction;
import protocol.swg.objectControllerObjects.CombatSpam;
import protocol.swg.objectControllerObjects.CommandEnqueueRemove;
import protocol.swg.objectControllerObjects.StartTask;
import resources.buffs.Buff;
import resources.buffs.DamageOverTime;
import resources.common.FileUtilities;
import resources.common.OutOfBand;
import resources.datatables.DisplayType;
import resources.datatables.FactionStatus;
import resources.datatables.GcwType;
import resources.datatables.Options;
import resources.datatables.Elemental;
import resources.datatables.Posture;
import resources.datatables.WeaponType;
import resources.objects.creature.CreatureObject;
import resources.objects.installation.InstallationObject;
import resources.objects.mission.MissionObject;
import resources.objects.player.PlayerObject;
import resources.objects.tangible.TangibleObject;
import resources.objects.waypoint.WaypointObject;
import resources.objects.weapon.WeaponObject;
import services.ai.AIActor;
import services.ai.TurretAIActor;
import services.combat.CombatEvents.DamageTaken;
import services.command.CombatCommand;
import services.sui.SUIService.MessageBoxType;
import services.sui.SUIWindow;
import services.sui.SUIWindow.SUICallback;
import services.sui.SUIWindow.Trigger;
import tools.DevLog;
import main.NGECore;
import engine.clients.Client;
import engine.resources.common.CRC;
import engine.resources.objects.SWGObject;
import engine.resources.scene.Point3D;
import engine.resources.service.INetworkDispatch;
import engine.resources.service.INetworkRemoteEvent;
import resources.datatables.HitType;
public class CombatService implements INetworkDispatch {
private NGECore core;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private CombatEvents events = new CombatEvents();
public CombatService(NGECore core) {
this.core = core;
CombatCommands.registerCommands(core);
}
@Override
public void insertOpcodes(Map<Integer, INetworkRemoteEvent> arg0, Map<Integer, INetworkRemoteEvent> arg1) {
}
@Override
public void shutdown() {
// TODO Auto-generated method stub
}
public void doCombat(final CreatureObject attacker, final TangibleObject target, final WeaponObject weapon, final CombatCommand command, final int actionCounter) {
if(target instanceof CreatureObject) if(((CreatureObject) target).getPosture() == Posture.Incapacitated || ((CreatureObject) target).getPosture() == Posture.Dead) return;
boolean success = true;
if((command.getAttackType() == 0 || command.getAttackType() == 1 || command.getAttackType() == 3) && !attemptCombat(attacker, target))
success = false;
if(success && !applySpecialCost(attacker, weapon, command))
success = false;
if(!success) {
IoSession session = attacker.getClient().getSession();
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(attacker.getObjectId(), actionCounter);
session.write(new ObjControllerMessage(0x0B, commandRemove).serialize());
StartTask startTask = new StartTask(actionCounter, attacker.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), -1);
session.write(new ObjControllerMessage(0x0B, startTask).serialize());
return;
}
final Point3D targetPos = target.getPosition();
final SWGObject targetParent = target.getContainer();
if(command.getDelayAttackParticle().length() > 0 || command.getInitialAttackDelay() > -1) {
if(command.getInitialAttackDelay() > 0) {
if(command.getDelayAttackParticle().length() > 0)
target.notifyObservers(new PlayClientEffectLocMessage(command.getDelayAttackParticle(), target.getPlanet().getName(), target.getWorldPosition()), true);
try {
Thread.sleep((long) command.getInitialAttackDelay() * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if(command.getInitialAttackDelay() <= 0 && command.getDelayAttackInterval() > 0 && command.getDelayAttackLoops() <= 1) {
try {
Thread.sleep((long) command.getDelayAttackInterval() * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(command.getDelayAttackParticle().length() > 0)
target.notifyObservers(new PlayClientEffectLocMessage(command.getDelayAttackParticle(), target.getPlanet().getName(), target.getWorldPosition()), true);
}
if(command.getDelayAttackLoops() > 1) {
final ScheduledFuture<?> task = scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
if(command.getDelayAttackParticle().length() > 0)
target.notifyObservers(new PlayClientEffectLocMessage(command.getDelayAttackParticle(), target.getPlanet().getName(), target.getWorldPosition()), true);
if(command.getAttackType() == 1)
doSingleTargetCombat(attacker, target, weapon, command, actionCounter);
else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
doAreaCombat(attacker, targetPos, weapon, command, actionCounter, targetParent);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 0, (long) (command.getDelayAttackInterval() * 1000), TimeUnit.MILLISECONDS);
scheduler.schedule(new Runnable() {
@Override
public void run() {
try {
task.cancel(true);
} catch (Exception e) {
e.printStackTrace();
}
}
}, (long) ((command.getDelayAttackInterval() * 1000) * command.getDelayAttackLoops()), TimeUnit.MILLISECONDS);
return;
} else {
if(command.getAttackType() == 1)
doSingleTargetCombat(attacker, target, weapon, command, actionCounter);
else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
doAreaCombat(attacker, targetPos, weapon, command, actionCounter, targetParent);
}
} else {
if(command.getAttackType() == 1)
doSingleTargetCombat(attacker, target, weapon, command, actionCounter);
else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
doAreaCombat(attacker, target, weapon, command, actionCounter);
}
if(target instanceof CreatureObject)
{
CreatureObject creature = (CreatureObject)target;
if(creature.getPosture() == Posture.Incapacitated || creature.getPosture() == Posture.Dead)
{
for (TangibleObject defender : new ArrayList<TangibleObject>(creature.getDefendersList()))
{
defender.removeDefender(creature);
creature.removeDefender(defender);
}
if(((CreatureObject) target).getPlayerObject() == null)
{
target.setKiller(attacker);
scheduler.schedule(new Runnable()
{
@Override
public void run()
{
try {
core.objectService.destroyObject(target);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 2, TimeUnit.MINUTES);
}
}
}
else if(target instanceof TangibleObject)
{
if(target.getConditionDamage() == target.getMaximumCondition())
{
for(TangibleObject defender : target.getDefendersList()) defender.removeDefender(target);
core.objectService.destroyObject(target);
}
}
}
public void doCombatForInstallation(final InstallationObject attacker, final TangibleObject target, final WeaponObject weapon, final CombatCommand command, final int actionCounter) {
if(target instanceof CreatureObject) if(((CreatureObject) target).getPosture() == Posture.Incapacitated || ((CreatureObject) target).getPosture() == Posture.Dead) return;
boolean success = true;
// if((command.getAttackType() == 0 || command.getAttackType() == 1 || command.getAttackType() == 3) && !attemptCombat(attacker, target))
// success = false;
// if(success && !applySpecialCost(attacker, weapon, command))
// success = false;
if(!success) {
IoSession session = attacker.getClient().getSession();
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(attacker.getObjectId(), actionCounter);
session.write(new ObjControllerMessage(0x0B, commandRemove).serialize());
StartTask startTask = new StartTask(actionCounter, attacker.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), -1);
session.write(new ObjControllerMessage(0x0B, startTask).serialize());
return;
}
//final Point3D targetPos = target.getPosition();
//final SWGObject targetParent = target.getContainer();
if(command.getDelayAttackParticle().length() > 0 || command.getInitialAttackDelay() > -1) {
if(command.getInitialAttackDelay() > 0) {
if(command.getDelayAttackParticle().length() > 0)
target.notifyObservers(new PlayClientEffectLocMessage(command.getDelayAttackParticle(), target.getPlanet().getName(), target.getWorldPosition()), true);
try {
Thread.sleep((long) command.getInitialAttackDelay() * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if(command.getInitialAttackDelay() <= 0 && command.getDelayAttackInterval() > 0 && command.getDelayAttackLoops() <= 1) {
try {
Thread.sleep((long) command.getDelayAttackInterval() * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(command.getDelayAttackParticle().length() > 0)
target.notifyObservers(new PlayClientEffectLocMessage(command.getDelayAttackParticle(), target.getPlanet().getName(), target.getWorldPosition()), true);
}
if(command.getDelayAttackLoops() > 1) {
final ScheduledFuture<?> task = scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
if(command.getDelayAttackParticle().length() > 0)
target.notifyObservers(new PlayClientEffectLocMessage(command.getDelayAttackParticle(), target.getPlanet().getName(), target.getWorldPosition()), true);
if(command.getAttackType() == 1)
doSingleTargetCombatForInstallation(attacker, target, weapon, command, actionCounter);
//else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
//doAreaCombat(attacker, targetPos, weapon, command, actionCounter, targetParent);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 0, (long) (command.getDelayAttackInterval() * 1000), TimeUnit.MILLISECONDS);
scheduler.schedule(new Runnable() {
@Override
public void run() {
try {
task.cancel(true);
} catch (Exception e) {
e.printStackTrace();
}
}
}, (long) ((command.getDelayAttackInterval() * 1000) * command.getDelayAttackLoops()), TimeUnit.MILLISECONDS);
return;
} else {
if(command.getAttackType() == 1)
doSingleTargetCombatForInstallation(attacker, target, weapon, command, actionCounter);
//else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
//doAreaCombat(attacker, targetPos, weapon, command, actionCounter, targetParent);
}
} else {
if(command.getAttackType() == 1)
doSingleTargetCombatForInstallation(attacker, target, weapon, command, actionCounter);
//else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
//doAreaCombat(attacker, target, weapon, command, actionCounter);
}
if(target instanceof CreatureObject)
{
CreatureObject creature = (CreatureObject)target;
if(creature.getPosture() == Posture.Incapacitated || creature.getPosture() == Posture.Dead)
{
for (TangibleObject defender : new ArrayList<TangibleObject>(creature.getDefendersList()))
{
defender.removeDefender(creature);
creature.removeDefender(defender);
}
if(((CreatureObject) target).getPlayerObject() == null)
{
target.setKiller(attacker);
scheduler.schedule(new Runnable()
{
@Override
public void run()
{
try {
core.objectService.destroyObject(target);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 2, TimeUnit.MINUTES);
}
}
}
else if(target instanceof TangibleObject)
{
if(target.getConditionDamage() == target.getMaximumCondition())
{
for(TangibleObject defender : target.getDefendersList()) defender.removeDefender(target);
core.objectService.destroyObject(target);
}
}
}
private void doAreaCombat(CreatureObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
if(target instanceof CreatureObject) {
doAreaCombat(attacker, (CreatureObject) target, weapon, command, actionCounter);
return;
}
}
private void doAreaCombat(CreatureObject attacker, Point3D targetPos, WeaponObject weapon, CombatCommand command, int actionCounter, SWGObject targetCell) {
float x = attacker.getWorldPosition().x;
float z = attacker.getWorldPosition().z;
SWGObject fakeTargetObject = new WaypointObject(0, attacker.getPlanet(), targetPos);
if(targetCell != null)
targetCell._add(fakeTargetObject);
float dirX = fakeTargetObject.getWorldPosition().x - x;
float dirZ = fakeTargetObject.getWorldPosition().z - z;
float range = command.getConeLength();
List<SWGObject> inRangeObjects = core.simulationService.get(attacker.getPlanet(), fakeTargetObject.getWorldPosition().x, fakeTargetObject.getWorldPosition().z, (int) range);
for(SWGObject obj : inRangeObjects) {
if(!(obj instanceof TangibleObject) || obj == attacker)
continue;
if(obj instanceof CreatureObject && (((CreatureObject) obj).getPosture() == Posture.Incapacitated || ((CreatureObject) obj).getPosture() == Posture.Dead))
continue;
if(command.getAttackType() == 0 && !isInConeAngle(attacker, obj, (int) command.getConeLength(), (int) command.getConeWidth(), dirX, dirZ))
continue;
if(!core.simulationService.checkLineOfSight(fakeTargetObject, obj))
continue;
if(!attemptCombat(attacker, (TangibleObject) obj))
continue;
doSingleTargetCombat(attacker, (TangibleObject) obj, weapon, command, actionCounter);
}
if(targetCell != null)
targetCell._remove(fakeTargetObject);
core.objectService.destroyObject(fakeTargetObject);
}
private void doSingleTargetCombat(CreatureObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
if(target instanceof CreatureObject) {
doSingleTargetCombat(attacker, (CreatureObject) target, weapon, command, actionCounter);
return;
}
float damage = calculateDamage(attacker, target, weapon, command);
if(damage > 0)
applyDamage(attacker, target, (int) damage);
sendCombatPackets(attacker, target, weapon, command, actionCounter, damage, 0, HitType.HIT);
if(FileUtilities.doesFileExist("scripts/commands/combat/" + command.getCommandName() + ".py"))
core.scriptService.callScript("scripts/commands/combat/", command.getCommandName(), "run", core, attacker, target, damage);
}
private void doSingleTargetCombatForInstallation(InstallationObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
if(target instanceof CreatureObject) {
doSingleTargetCombatForInstallation(attacker, (CreatureObject) target, weapon, command, actionCounter);
return;
}
float damage = calculateDamageForInstallation(attacker, target, weapon, command);
if(damage > 0)
applyDamageForInstallation(attacker, target, (int) damage);
sendCombatPacketsForInstallation(attacker, target, weapon, command, actionCounter, damage, 0, HitType.HIT);
if(FileUtilities.doesFileExist("scripts/commands/combat/" + command.getCommandName() + ".py"))
core.scriptService.callScript("scripts/commands/combat/", command.getCommandName(), "run", core, attacker, target, damage);
}
private void applyDamage(CreatureObject attacker, TangibleObject target, int damage) {
target.setConditionDamage(target.getConditionDamage() + damage);
if (target.getOption(Options.MOUNT)) {
core.mountService.damage((CreatureObject) target);
}
if (target instanceof CreatureObject){
CreatureObject targetCreature = (CreatureObject)target;
if (targetCreature.getCalledPet()!=null) {
AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI");
if (petActor!=null){
petActor.addDefender(attacker);
DevLog.debugoutai(petActor, "Charon", "Pet AI", "applyDamage addDefender");
}
}
if (!targetCreature.isInCombat()){
//targetCreature.setInCombat(true);
AIActor targetActor = (AIActor) targetCreature.getAttachment("AI");
targetActor.addDefender(attacker);
DevLog.debugoutai(targetActor, "Charon", "CombatService AI", "applyDamage addDefender 2");
}
}
DamageTaken event = events.new DamageTaken();
event.attacker = attacker;
event.damage = damage;
target.getEventBus().publish(event);
attacker.setTefTime(300000);
}
private void applyDamageForInstallation(InstallationObject attacker, TangibleObject target, int damage) {
target.setConditionDamage(target.getConditionDamage() + damage);
if (target.getOption(Options.MOUNT)) {
core.mountService.damage((CreatureObject) target);
}
if (target instanceof CreatureObject){
CreatureObject targetCreature = (CreatureObject)target;
if (targetCreature.getCalledPet()!=null) {
AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI");
if (petActor!=null){
petActor.addDefender(attacker);
DevLog.debugout("Charon", "Pet AI", "applyDamage addDefender");
}
}
if (!targetCreature.isInCombat()){
//targetCreature.setInCombat(true);
AIActor targetActor = (AIActor) targetCreature.getAttachment("AI");
targetActor.addDefender(attacker);
DevLog.debugout("Charon", "CombatService AI", "applyDamage addDefender 2");
}
}
DamageTaken event = events.new DamageTaken();
event.attacker = attacker;
event.damage = damage;
target.getEventBus().publish(event);
//attacker.setTefTime(300000);
}
private void applyDamageForInstallation(InstallationObject attacker, CreatureObject target, int damage) {
if(target.getHealth() - damage <= 0 && target.getSlottedObject("ghost") != null) {
if(target.hasBuff("incapWeaken")) {
//AIActor aiActor = (AIActor)attacker.getAttachment("AI");
deathblowPlayerFromInstallation(attacker, target);
return;
}
if (attacker.getAttachment("AI") instanceof AIActor){
AIActor aiActor = (AIActor)attacker.getAttachment("AI");
if (aiActor.getMobileTemplate().isDeathblow()){
deathblowPlayerFromInstallation(attacker, target);
return;
}
}
if (attacker.getAttachment("AI") instanceof TurretAIActor){
deathblowPlayerFromInstallation(attacker, target); // turrets always db
return;
}
synchronized(target.getMutex()) {
if (core.mountService.isMounted(target)) {
core.mountService.dismount(target, (CreatureObject) target.getContainer());
}
target.setHealth(1);
target.setPosture(Posture.Incapacitated);
target.setTurnRadius(0);
target.setSpeedMultiplierBase(0);
}
ScheduledFuture<?> incapTask = scheduler.schedule(() -> {
synchronized(target.getMutex()) {
try {
if(target.getPosture() != 13)
return;
target.setPosture(Posture.Upright);
target.setTurnRadius(1);
target.setSpeedMultiplierBase(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}, target.getIncapTimer(), TimeUnit.SECONDS);
target.setIncapTask(incapTask);
core.buffService.addBuffToCreature(target, "incapWeaken", target);
if(target.getSlottedObject("ghost") != null && attacker.getSlottedObject("ghost") != null) {
target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_incap", "TT", attacker.getCustomName()), DisplayType.Broadcast);
//attacker.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_target_incap", "TT", target.getCustomName()), DisplayType.Broadcast);
} else {target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_incap", "TT", "@" + attacker.getStfFilename() + ":" + attacker.getStfName()), DisplayType.Broadcast); }
return;
} else if(target.getHealth() - damage <= 0 && target.getAttachment("AI") != null) {
synchronized(target.getMutex()) {
target.setHealth(0);
target.setPosture(Posture.Dead);
}
attacker.removeDefender(target);
target.removeDefender(attacker);
return;
}
synchronized(target.getMutex()) {
target.setHealth(target.getHealth() - damage);
}
if (target instanceof CreatureObject){
CreatureObject targetCreature = (CreatureObject)target;
if (targetCreature.getCalledPet()!=null) {
AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI");
if (petActor!=null){
petActor.addDefender(attacker);
DevLog.debugout("Charon", "Pet AI", "applyDamage addDefender");
}
}
if (!targetCreature.isInCombat()){
//targetCreature.setInCombat(true);
if (targetCreature.getAttachment("AI") instanceof AIActor){
AIActor targetActor = (AIActor) targetCreature.getAttachment("AI");
targetActor.addDefender(attacker);
DevLog.debugoutai(targetActor, "Charon", "CombatService AI", "applyDamage addDefender 2");
}
if (targetCreature.getAttachment("AI") instanceof TurretAIActor){
TurretAIActor targetActor = (TurretAIActor) targetCreature.getAttachment("AI");
targetActor.addDefender(attacker);
DevLog.debugoutai(targetActor, "Charon", "CombatService AI", "applyDamage addDefender 2");
}
}
}
if (target.getPosture()!=13){
DamageTaken event = events.new DamageTaken();
event.attacker = attacker;
event.damage = damage;
target.getEventBus().publish(event);
}
}
private void doAreaCombat(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
float x = attacker.getWorldPosition().x;
float z = attacker.getWorldPosition().z;
float dirX = target.getWorldPosition().x - x;
float dirZ = target.getWorldPosition().z - z;
float range = command.getConeLength();
List<SWGObject> inRangeObjects = core.simulationService.get(attacker.getPlanet(), target.getWorldPosition().x, target.getWorldPosition().z, (int) range);
for(SWGObject obj : inRangeObjects) {
if(!(obj instanceof TangibleObject) || obj == attacker)
continue;
if(obj instanceof CreatureObject && (((CreatureObject) obj).getPosture() == Posture.Incapacitated || ((CreatureObject) obj).getPosture() == Posture.Dead))
continue;
if(command.getAttackType() == 0 && !isInConeAngle(attacker, obj, (int) command.getConeLength(), (int) command.getConeWidth(), dirX, dirZ))
continue;
if(!core.simulationService.checkLineOfSight(target, obj))
continue;
if(!attemptCombat(attacker, (TangibleObject) obj))
continue;
doSingleTargetCombat(attacker, (TangibleObject) obj, weapon, command, actionCounter);
}
}
private void doSingleTargetCombat(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
float damage = calculateDamage(attacker, target, weapon, command);
if(damage > 0) {
byte hitType = getHitType(attacker, target, weapon, command);
switch(hitType) {
case HitType.MISS:
damage = 0;
break;
case HitType.DODGE:
damage = 0;
break;
case HitType.PARRY:
damage = 0;
break;
case HitType.CRITICAL:
damage *= 1.5f;
break;
}
byte mitigationType = -1;
if(hitType == HitType.CRITICAL || hitType == HitType.HIT || hitType == HitType.STRIKETHROUGH) {
mitigationType = doMitigationRolls(attacker, target, weapon, command, hitType);
if(mitigationType == HitType.GLANCE) {
damage *= 0.4f;
} else if(mitigationType == HitType.EVASION && attacker.getSkillMod("combat_evasion_value") != null) {
float evasionValue = (attacker.getSkillMod("combat_evasion_value").getBase() / 4) / 100;
damage *= (1 - evasionValue);
}
}
int damageBeforeArmor = (int) damage;
damage *= (1 - getArmorReduction(attacker, target, weapon, command, hitType));
int armorAbsorbed = (int) (damageBeforeArmor - damage);
if(mitigationType == HitType.BLOCK) {
float blockValue = (attacker.getStrength() + attacker.getSkillModBase("combat_block_value")) / 2 + 25;
damage -= blockValue;
}
if(command.getAttackType() != 1 && target.getSkillMod("area_damage_resist_full_percentage") != null) {
if(new Random().nextFloat() <= target.getSkillModBase("area_damage_resist_full_percentage"))
damage = 0;
}
if(damage > 0)
applyDamage(attacker, target, (int) damage);
sendCombatPackets(attacker, target, weapon, command, actionCounter, damage, armorAbsorbed, hitType);
if(hitType != HitType.MISS && hitType != HitType.DODGE && hitType != HitType.PARRY && command.getBuffNameTarget().length() > 0) {
core.buffService.addBuffToCreature(target, command.getBuffNameTarget(), attacker);
}
if(command.getDotIntensity() > 0) {
addDotToCreature(attacker, target, command, target.getBuffByName(command.getBuffNameTarget()));
}
if(FileUtilities.doesFileExist("scripts/commands/combat/" + command.getCommandName() + ".py"))
core.scriptService.callScript("scripts/commands/combat/", command.getCommandName(), "run", core, attacker, target, damage);
return;
}
if(command.getAddedDamage() == 0 && command.getPercentFromWeapon() == 0 && command.getBuffNameTarget().length() > 0)
core.buffService.addBuffToCreature(target, command.getBuffNameTarget(), attacker);
sendCombatPackets(attacker, target, weapon, command, actionCounter, damage, 0, HitType.HIT);
}
private void doSingleTargetCombatForInstallation(InstallationObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
float damage = calculateDamageForInstallation(attacker, target, weapon, command);
if(damage > 0) {
damage *= 1.5f;
int damageBeforeArmor = (int) damage;
damage *= 0.99;
int armorAbsorbed = (int) (damageBeforeArmor - damage);
if(damage > 0)
applyDamageForInstallation(attacker, target, (int) damage);
sendCombatPacketsForInstallation(attacker, target, weapon, command, actionCounter, damage, armorAbsorbed, HitType.CRITICAL);
if(FileUtilities.doesFileExist("scripts/commands/combat/" + command.getCommandName() + ".py"))
core.scriptService.callScript("scripts/commands/combat/", command.getCommandName(), "run", core, attacker, target, damage);
return;
}
}
private void sendCombatPackets(CreatureObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command, int actionCounter, float damage, int armorAbsorbed, int hitType) {
String animationStr = command.getRandomAnimation(weapon); // choosing turretShot as default attack, results in a zero length string
if (weapon.getTemplate().contains("atst_ranged"))
animationStr="fire_1_single_light";
CombatAction combatAction = new CombatAction(CRC.StringtoCRC(animationStr), attacker.getObjectID(), weapon.getObjectID(), target.getObjectID(), command.getCommandCRC());
// int turret_fire = 0xD334C0B8; // 0xA7595B4E
// CombatAction combatAction = new CombatAction(turret_fire, attacker.getObjectID(), weapon.getObjectID(), target.getObjectID(), command.getCommandCRC());
//
ObjControllerMessage objController = new ObjControllerMessage(0x1B, combatAction);
attacker.notifyObserversInRange(objController, true, 125);
CombatSpam combatSpam = new CombatSpam(attacker.getObjectID(), target.getObjectID(), weapon.getObjectID(), (int) damage, armorAbsorbed, hitType);
ObjControllerMessage objController4 = new ObjControllerMessage(0x1B, combatSpam);
IoBuffer spam = objController4.serialize();
if(attacker.getClient() != null) {
StartTask startTask = new StartTask(actionCounter, attacker.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), command.getCooldown());
ObjControllerMessage objController2 = new ObjControllerMessage(0x0B, startTask);
attacker.getClient().getSession().write(objController2.serialize());
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(attacker.getObjectID(), actionCounter);
ObjControllerMessage objController3 = new ObjControllerMessage(0x0B, commandRemove);
attacker.getClient().getSession().write(objController3.serialize());
attacker.getClient().getSession().write(spam);
}
if(target.getClient() != null)
target.getClient().getSession().write(spam);
}
private void sendCombatPacketsForInstallation(InstallationObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command, int actionCounter, float damage, int armorAbsorbed, int hitType) {
//String animationStr = command.getRandomAnimation(weapon);
int turret_fire = 0xD334C0B8; // 0xA7595B4E
//CombatAction combatAction = new CombatAction(CRC.StringtoCRC(animationStr), attacker.getObjectID(), weapon.getObjectID(), target.getObjectID(), command.getCommandCRC());
CombatAction combatAction = new CombatAction(turret_fire, attacker.getObjectID(), weapon.getObjectID(), target.getObjectID(), command.getCommandCRC());
ObjControllerMessage objController = new ObjControllerMessage(0x1B, combatAction);
attacker.notifyObserversInRange(objController, true, 125);
CombatSpam combatSpam = new CombatSpam(attacker.getObjectID(), target.getObjectID(), weapon.getObjectID(), (int) damage, armorAbsorbed, hitType);
ObjControllerMessage objController4 = new ObjControllerMessage(0x1B, combatSpam);
IoBuffer spam = objController4.serialize();
if(target.getClient() != null)
target.getClient().getSession().write(spam);
}
private void sendHealPackets(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
CombatAction combatAction = new CombatAction(CRC.StringtoCRC(command.getDefaultAnimations()[0]), attacker.getObjectID(), weapon.getObjectID(), target.getObjectID(), command.getCommandCRC());
ObjControllerMessage objController = new ObjControllerMessage(0x1B, combatAction);
attacker.notifyObserversInRange(objController, true, 125);
if(attacker.getClient() != null) {
StartTask startTask = new StartTask(actionCounter, attacker.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), command.getCooldown());
ObjControllerMessage objController2 = new ObjControllerMessage(0x0B, startTask);
attacker.getClient().getSession().write(objController2.serialize());
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(attacker.getObjectID(), actionCounter);
ObjControllerMessage objController3 = new ObjControllerMessage(0x0B, commandRemove);
attacker.getClient().getSession().write(objController3.serialize());
}
}
private float getArmorReduction(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command, byte hitType) {
String elementalName = "";
if(command.getPercentFromWeapon() > 0) {
// TODO: elemental mitigation and damage
elementalName = weapon.getDamageType();
} else {
elementalName = Elemental.getElementalName(command.getElementalType());
}
int baseArmor = 0;
baseArmor = target.getSkillModBase(elementalName);
if(target.getSkillMod("expertise_innate_reduction_all_player") != null)
baseArmor *= (100 - target.getSkillModBase("expertise_innate_reduction_all_player")) / 100;
if(command.getBypassArmor() > 0)
baseArmor *= (100 - command.getBypassArmor()) / 100;
float mitigation = (float) (90 * (1 - Math.exp(-0.000125 * baseArmor))) + baseArmor / 9000;
if(hitType == HitType.STRIKETHROUGH) {
float stMaxValue = attacker.getSkillModBase("combat_strikethrough_value") / 2 + attacker.getLuck() / 10;
if(stMaxValue > 99)
stMaxValue = 99;
float stMinValue = stMaxValue / 2;
float stValue = new Random().nextInt((int) (stMaxValue - stMinValue + 1)) + stMinValue;
stValue /= 100;
stValue = 1 - stValue;
mitigation *= stValue;
}
return mitigation / 100;
}
private boolean attemptCombat(CreatureObject attacker, TangibleObject target) {
if (attacker.isCloaked()) {
attacker.setCloaked(false);
if (attacker.getPlayerObject() != null && attacker.getPlayerObject().getProfession().equals("spy_1a")) {
attacker.setVisibleOnRadar(true);
}
}
if (attacker.getSlottedObject("ghost") != null) {
PlayerObject ghost = attacker.getPlayerObject();
if (ghost.getBountyMissionId() != 0) {
MissionObject mission = (MissionObject) core.objectService.getObject(ghost.getBountyMissionId());
if (mission != null && mission.getBountyMarkId() == target.getObjectID()) {
target.addDefender(attacker);
target.updatePvpStatus();
}
}
}
if(target.getDefendersList().contains(attacker) && attacker.getDefendersList().contains(target))
return true;
if(attacker.getStateBitmask() == 0x8000000)
return false;
if(!target.isAttackableBy(attacker))
return false;
if(target.getAttachment("AI") instanceof AIActor && attacker instanceof CreatureObject) {
((AIActor) target.getAttachment("AI")).addDefender((CreatureObject) attacker);
} else {
target.addDefender(attacker);
}
if(attacker.getAttachment("AI") instanceof AIActor && target instanceof CreatureObject) {
((AIActor) attacker.getAttachment("AI")).addDefender((CreatureObject) target);
} else {
attacker.addDefender(target); // See below comment
}
//attacker.addDefender(target); // Why do we need to add target to defender list twice?
return true;
}
public boolean applySpecialCost(CreatureObject attacker, WeaponObject weapon, CombatCommand command) {
float actionCost = command.getActionCost();
float healthCost = command.getHealthCost();
if(actionCost == 0 && healthCost == 0)
return true;
actionCost *= getWeaponActionCostReduction(attacker, weapon);
float newAction = attacker.getAction() - actionCost;
if(newAction <= 0)
return false;
float newHealth = attacker.getHealth() - healthCost;
if(newHealth <= 0)
return false;
if(newAction != attacker.getAction())
attacker.setAction((int) newAction);
if(newHealth != attacker.getHealth())
attacker.setHealth((int) newHealth);
return true;
}
private float calculateDamage(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command) {
if(target.getBuffByName("me_stasis_self_1") != null || target.getBuffByName("me_stasis_1") != null)
return 0;
if(attacker.getBuffByName("me_stasis_self_1") != null || attacker.getBuffByName("me_stasis_1") != null)
return 0;
float rawDamage = command.getAddedDamage();
if(command.getPercentFromWeapon() > 0) {
float weaponMinDmg = weapon.getMinDamage();
float weaponMaxDmg = weapon.getMaxDamage();
if(attacker.getSlottedObject("ghost") != null) {
PlayerObject ghost = (PlayerObject) attacker.getSlottedObject("ghost");
if(ghost.getProfession().equals("commando_1a") && weapon.getWeaponType() == 12 && attacker.getSkillMod("expertise_devastation_bonus") != null) {
if(new Random().nextFloat() <= attacker.getSkillMod("expertise_devastation_bonus").getBase() / 1000) {
weaponMinDmg = weaponMaxDmg;
weaponMaxDmg *= 1.1f;
}
}
}
float weaponDmg = new Random().nextInt((int) (weaponMaxDmg - weaponMinDmg + 1)) + weaponMinDmg;
weaponDmg *= command.getPercentFromWeapon();
rawDamage += weaponDmg;
if(weapon.isMelee()) {
if(attacker.getStrength() > 0) {
rawDamage += ((attacker.getStrength() / 100) * 33);
}
}
}
rawDamage *= getWeaponDamageIncrease(attacker, weapon);
if(target.getSkillMod("damage_decrease_percentage") != null) {
rawDamage *= (1 - (target.getSkillMod("damage_decrease_percentage").getBase() / 100));
}
if(target.getSkillMod("combat_divide_damage_dealt") != null) {
rawDamage *= (1 - (target.getSkillMod("combat_divide_damage_dealt").getBase() / 100));
}
//Hook in here to agitate called pet
return rawDamage;
}
private float calculateDamageForInstallation(InstallationObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command) {
float rawDamage = command.getAddedDamage();
if(command.getPercentFromWeapon() > 0) {
float weaponMinDmg = weapon.getMinDamage();
float weaponMaxDmg = weapon.getMaxDamage();
float weaponDmg = new Random().nextInt((int) (weaponMaxDmg - weaponMinDmg + 1)) + weaponMinDmg;
weaponDmg *= command.getPercentFromWeapon();
rawDamage += weaponDmg;
}
return rawDamage;
}
private float calculateDamage(CreatureObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command) {
float rawDamage = command.getAddedDamage();
if(command.getPercentFromWeapon() > 0 && weapon != attacker.getSlottedObject("default_weapon")) {
float weaponMinDmg = weapon.getMinDamage();
float weaponMaxDmg = weapon.getMaxDamage();
if(attacker.getSlottedObject("ghost") != null) {
PlayerObject ghost = (PlayerObject) attacker.getSlottedObject("ghost");
if(ghost.getProfession().equals("commando_1a") && weapon.getWeaponType() == 12 && attacker.getSkillMod("expertise_devastation_bonus") != null) {
if(new Random().nextFloat() <= attacker.getSkillMod("expertise_devastation_bonus").getBase() / 1000) {
weaponMinDmg = weaponMaxDmg;
weaponMaxDmg *= 1.1f;
}
}
}
float weaponDmg = new Random().nextInt((int) (weaponMaxDmg - weaponMinDmg + 1)) + weaponMinDmg;
weaponDmg *= command.getPercentFromWeapon();
rawDamage += weaponDmg;
if(weapon.isMelee()) {
if(attacker.getStrength() > 0) {
rawDamage += ((attacker.getStrength() / 100) * 33);
}
}
if (!attacker.isPlayer()){
if (attacker.hasBuff("expertise_damage_npc")){
rawDamage *=2; // tower_defense buff
}
}
}
return rawDamage;
}
private float calculateDamageForInstallation(InstallationObject attacker, TangibleObject target, WeaponObject weapon, CombatCommand command) {
float rawDamage = command.getAddedDamage();
if(command.getPercentFromWeapon() > 0 && weapon != attacker.getSlottedObject("default_weapon")) {
float weaponMinDmg = weapon.getMinDamage();
float weaponMaxDmg = weapon.getMaxDamage();
float weaponDmg = new Random().nextInt((int) (weaponMaxDmg - weaponMinDmg + 1)) + weaponMinDmg;
weaponDmg *= command.getPercentFromWeapon();
rawDamage += weaponDmg;
}
return rawDamage;
}
public byte getHitType(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command) {
float r;
Random random = new Random();
// negation rolls(parry, miss and dodge) can only roll on single target attacks, strikethrough also only rolls on single target attacks
if(command.getAttackType() == 1) {
if(weapon.isRanged()) {
float missChance = 0.05f;
if(attacker.getStrength() > 0) {
float missNegation = (float) ((attacker.getStrength() / 100) * 0.1);
if(missNegation > 0.04f)
missNegation = 0.04f;
missChance -= missNegation;
}
r = random.nextFloat();
if(r <= missChance)
return HitType.MISS;
}
float dodgeChance = (float) (target.getSkillModBase("display_only_dodge") - attacker.getSkillModBase("display_only_opp_dodge_reduction")) / 10000;
r = random.nextFloat();
if(r <= dodgeChance)
return HitType.DODGE;
WeaponObject weapon2 = (WeaponObject) core.objectService.getObject(((CreatureObject) target).getWeaponId());
if(weapon2 != null && weapon2.isMelee()) {
float parryChance = (float) (target.getSkillModBase("display_only_parry") - attacker.getSkillModBase("display_only_parry_reduction"))/ 10000;
r = random.nextFloat();
if(r <= parryChance)
return HitType.PARRY;
}
float stChance = (float) attacker.getSkillModBase("display_only_strikethrough") / 10000;
r = random.nextFloat();
if(r <= stChance)
return HitType.STRIKETHROUGH;
}
float critChance = 0;
critChance += (command.getCriticalChance());
critChance += ((float) attacker.getSkillModBase("display_only_critical") / 100);
critChance -= ((float) target.getSkillModBase("display_only_expertise_critical_hit_reduction") / 100);
critChance += ((float) target.getSkillModBase("critical_hit_vulnerable") / 100);
if(target.getSlottedObject("ghost") != null)
if(target.isPlayer()) {
critChance += attacker.getSkillModBase("expertise_critical_niche_pvp");
critChance -= ((float) target.getSkillModBase("display_only_expertise_critical_hit_pvp_reduction") / 100);
}
r = random.nextFloat();
if(r <= ((float) critChance / 100))
return HitType.CRITICAL;
// TODO: Punishing blow once AI is implemented
return HitType.HIT;
}
public byte doMitigationRolls(CreatureObject attacker, CreatureObject target, WeaponObject weapon, CombatCommand command, byte hitType) {
float r;
Random random = new Random();
float blockChance = (float) (target.getSkillModBase("display_only_block") - attacker.getSkillModBase("display_only_opp_block_reduction"))/ 10000;
r = random.nextFloat();
if(r <= blockChance)
return HitType.BLOCK;
if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3) {
float evasionChance = (float) target.getSkillModBase("display_only_evasion") / 10000;
r = random.nextFloat();
if(r <= evasionChance)
return HitType.EVASION;
}
if(hitType == HitType.HIT && target.getSkillMod("display_only_glancing_blow") != null) {
float glanceChance = (float) target.getSkillModBase("display_only_glancing_blow") / 10000;
if(weapon.isRanged())
glanceChance += target.getSkillModBase("expertise_glancing_blow_ranged");
if(weapon.isMelee())
glanceChance += target.getSkillModBase("expertise_glancing_blow_melee");
r = random.nextFloat();
if(r <= glanceChance)
return HitType.GLANCE;
}
return -1;
}
public void applyDamage(CreatureObject attacker, final CreatureObject target, int damage) {
if(target.getHealth() - damage <= 0 && target.getSlottedObject("ghost") != null) {
if(target.hasBuff("incapWeaken")) {
if (!attacker.isPlayer()){
AIActor aiActor = (AIActor)attacker.getAttachment("AI");
if (aiActor.getMobileTemplate().isDeathblow())
deathblowPlayer(attacker, target);
} else
{
deathblowPlayer(attacker, target);
}
return;
}
if (!attacker.isPlayer()){
AIActor aiActor = (AIActor)attacker.getAttachment("AI");
if (aiActor.getMobileTemplate().isDeathblow()){
deathblowPlayer(attacker, target);
return;
}
}
synchronized(target.getMutex()) {
if (core.mountService.isMounted(target)) {
core.mountService.dismount(target, (CreatureObject) target.getContainer());
}
target.setHealth(1);
target.setPosture(Posture.Incapacitated);
target.setTurnRadius(0);
target.setSpeedMultiplierBase(0);
}
ScheduledFuture<?> incapTask = scheduler.schedule(() -> {
synchronized(target.getMutex()) {
try {
if(target.getPosture() != 13)
return;
target.setPosture(Posture.Upright);
target.setTurnRadius(1);
target.setSpeedMultiplierBase(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}, target.getIncapTimer(), TimeUnit.SECONDS);
target.setIncapTask(incapTask);
core.buffService.addBuffToCreature(target, "incapWeaken", target);
if(target.getSlottedObject("ghost") != null && attacker.getSlottedObject("ghost") != null) {
target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_incap", "TT", attacker.getCustomName()), DisplayType.Broadcast);
attacker.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_target_incap", "TT", target.getCustomName()), DisplayType.Broadcast);
} else {target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_incap", "TT", "@" + attacker.getStfFilename() + ":" + attacker.getStfName()), DisplayType.Broadcast); }
return;
} else if(target.getHealth() - damage <= 0 && target.getAttachment("AI") != null) {
synchronized(target.getMutex()) {
target.setHealth(0);
target.setPosture(Posture.Dead);
}
attacker.removeDefender(target);
target.removeDefender(attacker);
return;
}
synchronized(target.getMutex()) {
System.out.println("THIS SHOULD BE RUN!!! " + (target.getHealth() - damage));
target.setHealth(target.getHealth() - damage);
}
if (target instanceof CreatureObject){
CreatureObject targetCreature = (CreatureObject)target;
if (targetCreature.getCalledPet()!=null) {
AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI");
if (petActor!=null){
petActor.addDefender(attacker);
//DevLog.debugout("Charon", "Pet AI", "applyDamage addDefender");
}
}
if (!targetCreature.isInCombat()){
AIActor targetActor = (AIActor) targetCreature.getAttachment("AI");
targetActor.addDefender(attacker);
//DevLog.debugout("Charon", "CombatService AI", "applyDamage addDefender 2");
}
}
if (target.getPosture()!=13){
DamageTaken event = events.new DamageTaken();
event.attacker = attacker;
event.damage = damage;
target.getEventBus().publish(event);
}
}
public void doDrainHeal(CreatureObject receiver, int drainAmount) {
synchronized(receiver.getMutex()) {
receiver.setHealth(receiver.getHealth() + drainAmount);
}
}
private boolean isInConeAngle(CreatureObject attacker, SWGObject target, int coneLength, int coneWidth, float directionX, float directionZ) {
float radius = coneWidth / 2;
float angle = (float) (2 * Math.atan(coneLength / radius));
float targetX = target.getWorldPosition().x - attacker.getWorldPosition().x;
float targetZ = target.getWorldPosition().z - attacker.getWorldPosition().z;
float targetAngle = (float) (Math.atan2(targetZ, targetX) - Math.atan2(directionZ, directionX));
float degrees = (float) (targetAngle * 180 / Math.PI);
float coneAngle = angle / 2;
if(degrees > coneAngle || degrees < -coneAngle)
return false;
return true;
}
public boolean attemptHeal(CreatureObject healer, CreatureObject target) {
if(healer == target)
return true;
PlayerObject healerPo = (PlayerObject) healer.getSlottedObject("ghost");
if (healerPo != null && !healerPo.getProfession().startsWith("medic")) {
return false;
}
if (areInDuel(healer, target)) {
return false;
}
if(target.getAttachment("AI") != null) return false;
if(healer.getFaction().equals(target.getFaction())) {
if(healer.getFactionStatus() < target.getFactionStatus())
return false;
return true;
} else {
if(target.getFactionStatus() == FactionStatus.OnLeave)
return true;
return false;
}
}
public void doHeal(CreatureObject healer, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
boolean success = true;
if((command.getAttackType() == 0 || command.getAttackType() == 1 || command.getAttackType() == 3) && !attemptHeal(healer, target))
target = healer;
if(target.getMaxHealth() == target.getHealth())
success = command.getAttackType() != 1;
if(success && !applySpecialCost(healer, weapon, command))
success = false;
if(!success && healer.getClient() != null) {
IoSession session = healer.getClient().getSession();
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(healer.getObjectId(), actionCounter);
session.write(new ObjControllerMessage(0x0B, commandRemove).serialize());
StartTask startTask = new StartTask(actionCounter, healer.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), -1);
session.write(new ObjControllerMessage(0x0B, startTask).serialize());
return;
}
if(command.getAttackType() == 1)
doSingleTargetHeal(healer, target, weapon, command, actionCounter);
else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
doAreaHeal(healer, target, weapon, command, actionCounter);
sendHealPackets(healer, target, weapon, command, actionCounter);
}
private void doSingleTargetHeal(CreatureObject healer, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
if(command.getDotDuration() > 0) {
addHealOverTimeToCreature(healer, target, command);
return;
}
int healAmount = command.getAddedDamage();
int healPotency = 0;
if(healer.getSkillMod("expertise_healing_all") != null)
healPotency += healer.getSkillMod("expertise_healing_all").getBase();
if(healer.getSkillMod("expertise_healing_line_me_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_me_heal").getBase();
if(healer.getSkillMod("expertise_healing_line_of_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_of_heal").getBase();
if(healer.getSkillMod("expertise_healing_line_sm_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_sm_heal").getBase();
if(healer.getSkillMod("expertise_healing_line_sp_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_sp_heal").getBase();
if(healer.hasBuff("fs_buff_def_1_1"))
if(healer.getSkillMod("expertise_stance_healing_line_fs_heal") != null)
healPotency += healer.getSkillMod("expertise_stance_healing_line_fs_heal").getBase();
if(healPotency > 0)
healAmount += ((healAmount * healPotency) / 100);
if(target.getSkillMod("expertise_healing_reduction") != null)
healAmount *= (1 - target.getSkillMod("expertise_healing_reduction").getBase() / 100);
synchronized(target.getMutex()) {
target.setHealth(target.getHealth() + healAmount);
}
if(FileUtilities.doesFileExist("scripts/commands/combat" + command.getCommandName() + ".py"))
core.scriptService.callScript("scripts/commands/combat", command.getCommandName(), "run", core, healer, target, null);
}
private void doAreaHeal(CreatureObject healer, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
float range = command.getConeLength();
List<SWGObject> inRangeObjects = core.simulationService.get(healer.getPlanet(), target.getWorldPosition().x, target.getWorldPosition().z, (int) range);
for(SWGObject obj : inRangeObjects) {
if(!(obj instanceof CreatureObject))
continue;
if(obj instanceof CreatureObject && (((CreatureObject) obj).getPosture() == Posture.Dead))
continue;
if(!core.simulationService.checkLineOfSight(target, obj))
continue;
if(!attemptHeal(healer, (CreatureObject) obj))
continue;
doSingleTargetHeal(healer, (CreatureObject) obj, weapon, command, actionCounter);
}
}
public void deathblowPlayer(CreatureObject attacker, CreatureObject target) {
target.stopIncapTask();
target.setIncapTask(null);
target.setPosture(Posture.Dead);
if (target.getSlottedObject("ghost") != null && attacker.getSlottedObject("ghost") != null) {
boolean bountyWindow = true;
if (attacker.getPlayerObject().getBountyMissionId() != 0) { // Bounty Hunter is deathblowing (attacker)
bountyWindow = false;
MissionObject mission = (MissionObject) core.objectService.getObject(attacker.getPlayerObject().getBountyMissionId());
if (mission != null && mission.getBountyMarkId() == target.getObjectID()) {
attacker.sendSystemMessage(OutOfBand.ProsePackage("@bounty_hunter:bounty_success_hunter", mission.getCreditReward(), "TT", target.getCustomName(), "TO", target.getObjectID()), DisplayType.Broadcast);
target.sendSystemMessage(OutOfBand.ProsePackage("@bounty_hunter:bounty_success_target", mission.getCreditReward(), "TT", attacker.getCustomName()), DisplayType.Broadcast);
core.missionService.handleMissionComplete(attacker, mission);
}
} else if (target.getPlayerObject().getBountyMissionId() != 0) { // Bounty Hunter is being deathblown (They're the target)
bountyWindow = false;
MissionObject mission = (MissionObject) core.objectService.getObject(target.getPlayerObject().getBountyMissionId());
if (mission != null && mission.getBountyMarkId() == attacker.getObjectID()) {
attacker.sendSystemMessage(OutOfBand.ProsePackage("@bounty_hunter:bounty_failed_target", mission.getCreditReward(), "TT", target.getCustomName(), "TO", target.getObjectID()), DisplayType.Broadcast);
target.sendSystemMessage(OutOfBand.ProsePackage("@bounty_hunter:bounty_failed_hunter", "TT", attacker.getCustomName()), DisplayType.Broadcast);
core.missionService.handleMissionAbort(target, mission);
}
}
if (target.getDuelList().contains(attacker)) {
bountyWindow = false;
handleEndDuel(target, attacker, false);
} else {
awardGcw(attacker, target);
}
if (bountyWindow) {
core.playerService.sendSetBountyWindow(target, attacker);
}
} else {
attacker.sendSystemMessage("You have killed " + ((target.getCustomName() == null) ? target.getObjectName().getStfValue() : target.getCustomName()) + ".", DisplayType.Broadcast);
}
attacker.removeDefender(target);
target.removeDefender(attacker);
target.setSpeedMultiplierBase(0);
target.setTurnRadius(0);
if (attacker.getSlottedObject("ghost") != null)
target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_dead", "TT", attacker.getCustomName()), DisplayType.Broadcast);
else
target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_dead", "TT", "@" + attacker.getStfFilename() + ":" + attacker.getStfName()), DisplayType.Broadcast);
core.playerService.sendCloningWindow(target, attacker.getSlottedObject("ghost") != null);
}
public void deathblowPlayerFromInstallation(InstallationObject attacker, CreatureObject target) {
target.stopIncapTask();
target.setIncapTask(null);
target.setPosture(Posture.Dead);
attacker.removeDefender(target);
target.removeDefender(attacker);
target.setSpeedMultiplierBase(0);
target.setTurnRadius(0);
if (attacker.getSlottedObject("ghost") != null)
target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_dead", "TT", attacker.getCustomName()), DisplayType.Broadcast);
else
target.sendSystemMessage(OutOfBand.ProsePackage("@base_player:prose_victim_dead", "TT", "@" + attacker.getStfFilename() + ":" + attacker.getStfName()), DisplayType.Broadcast);
core.playerService.sendCloningWindow(target, attacker.getSlottedObject("ghost") != null);
}
public boolean areInDuel(CreatureObject creature1, CreatureObject creature2) {
if(creature1.getDuelList().contains(creature2) && creature2.getDuelList().contains(creature1))
return true;
return false;
}
public void handleDuel(CreatureObject requester, CreatureObject target) {
if(!target.getDuelList().contains(requester)) {
requester.getDuelList().add(target);
requester.sendSystemMessage("You challenge " + target.getCustomName() + " to a duel.", (byte) 0);
target.sendSystemMessage(requester.getCustomName() + " challenges you to a duel.", (byte) 0);
return;
} else {
requester.getDuelList().add(target);
requester.sendSystemMessage("You accept " + target.getCustomName() + "'s challenge.", (byte) 0);
target.sendSystemMessage(requester.getCustomName() + " accepts your challenge.", (byte) 0);
target.updatePvpStatus();
requester.updatePvpStatus();
}
}
public void handleEndDuel(CreatureObject requester, CreatureObject target, boolean announce) {
requester.getDuelList().remove(target);
target.getDuelList().remove(requester);
target.removeDefender(requester);
requester.removeDefender(target);
target.updatePvpStatus();
requester.updatePvpStatus();
if(announce)
{
requester.sendSystemMessage("You end your duel with " + target.getCustomName() + ".", (byte) 0);
target.sendSystemMessage(requester.getCustomName() + " ends your duel.", (byte) 0);
}
}
public void doRevive(CreatureObject medic, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
boolean success = true;
if(!applySpecialCost(medic, weapon, command))
success = false;
if((command.getAttackType() == 0 || command.getAttackType() == 1 || command.getAttackType() == 3) && !attemptHeal(medic, target))
success = false;
if(!success) {
IoSession session = medic.getClient().getSession();
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(medic.getObjectId(), actionCounter);
session.write(new ObjControllerMessage(0x0B, commandRemove).serialize());
StartTask startTask = new StartTask(actionCounter, medic.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), -1);
session.write(new ObjControllerMessage(0x0B, startTask).serialize());
return;
}
if(command.getAttackType() == 1)
doSingleTargetRevive(medic, target, weapon, command, actionCounter);
else if(command.getAttackType() == 0 || command.getAttackType() == 2 || command.getAttackType() == 3)
doAreaRevive(medic, target, weapon, command, actionCounter);
//sendHealPackets(medic, target, weapon, command, actionCounter); // Is there a reason this is here (combat log...?) - it causes a player to come back from the dead die again.
}
private void doSingleTargetRevive(CreatureObject medic, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
SUIWindow rezWindow = core.suiService.createMessageBox(MessageBoxType.MESSAGE_BOX_YES_NO, "@spam:revive_sui_title", medic.getCustomName() + " has offered to Revive you. Click YES to revive immediately at this location without cloning.", target, null, 0);
Vector<String> returnParams = new Vector<String>();
returnParams.add("btnOk:Text");
rezWindow.addHandler(0, "", Trigger.TRIGGER_OK, returnParams, new SUICallback() {
@Override
public void process(SWGObject owner, int eventType, Vector<String> returnList) {
CreatureObject creature = (CreatureObject) owner;
if(eventType != 0 || creature.getPosture() != Posture.Dead)
return;
synchronized(creature.getMutex()) {
creature.setPosture(Posture.Upright);
creature.setTurnRadius(1);
creature.setSpeedMultiplierBase(1);
creature.setHealth(creature.getHealth() + 4000);
}
}
});
core.suiService.openSUIWindow(rezWindow);
}
private void doAreaRevive(CreatureObject medic, CreatureObject target, WeaponObject weapon, CombatCommand command, int actionCounter) {
float range = command.getConeLength();
List<SWGObject> inRangeObjects = core.simulationService.get(medic.getPlanet(), target.getWorldPosition().x, target.getWorldPosition().z, (int) range);
for(SWGObject obj : inRangeObjects) {
if(!(obj instanceof CreatureObject))
continue;
if(obj instanceof CreatureObject && (((CreatureObject) obj).getPosture() != Posture.Dead))
continue;
if(!attemptHeal(medic, (CreatureObject) obj))
continue;
if(!core.simulationService.checkLineOfSight(target, obj))
continue;
doSingleTargetRevive(medic, (CreatureObject) obj, weapon, command, actionCounter);
}
}
public void addDotToCreature(final CreatureObject attacker, final CreatureObject target, final CombatCommand command, Buff debuff) {
//if(debuff == null)
// return;
if(target.getDotByName(command.getCommandName()) != null) {
DamageOverTime oldDot = target.getDotByName(command.getCommandName());
if(debuff != null && debuff.getMaxStacks() > 1)
return;
else {
oldDot.getTask().cancel(true);
target.removeDot(oldDot);
}
}
final DamageOverTime dot = new DamageOverTime(command.getCommandName(), debuff, command.getDotType(), command.getDotDuration(), command.getDotIntensity());
target.addDot(dot);
dot.setStartTime(System.currentTimeMillis());
final ScheduledFuture<?> task = scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
if(dot.getRemainingDuration() <= 0) {
target.removeDot(dot);
dot.getTask().cancel(true);
}
doDotDamageTick(attacker, target, command, dot);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 10, 2000, TimeUnit.MILLISECONDS);
dot.setTask(task);
}
public void doDotDamageTick(CreatureObject attacker, CreatureObject target, CombatCommand command, DamageOverTime dot) {
int damage = dot.getIntensity();
if(dot.getBuff() != null)
damage *= dot.getBuff().getStacks();
int baseArmor = 0;
float innateDotResist = 0.05f; // TODO: add armor dot resists chances
if(new Random().nextFloat() <= innateDotResist)
return;
if(target.getSkillMod("expertise_dot_absorption_all") != null)
damage *= (1 - target.getSkillModBase("expertise_dot_absorption_all") / 100);
switch(dot.getType()) {
case "kinetic":
baseArmor = target.getSkillModBase("electricity"); // Is this a mistake or is it legitimate?
case "poison":
baseArmor = target.getSkillModBase("acid");
case "disease": // disease damages action in nge
baseArmor = 0;
case "bleeding": // elemental type unknown
case "fire":
baseArmor = target.getSkillModBase("heat");
default:
baseArmor = target.getSkillModBase(dot.getType());
}
if(target.getSkillMod("expertise_innate_reduction_all_player") != null) {
baseArmor *= (100 - target.getSkillMod("expertise_innate_reduction_all_player").getBase()) / 100;
}
if(baseArmor > 0) {
float mitigation = (float) (90 * (1 - Math.exp(-0.000125 * baseArmor)) + baseArmor / 9000);
mitigation /= 100;
damage *= (1 - mitigation / 2);
}
if(dot.getType().equals("disease")) {
target.setAction(target.getAction() - damage);
return;
}
applyDamage(attacker, target, damage);
}
public void doSelfBuff(CreatureObject creature, WeaponObject weapon, CombatCommand command, int actionCounter) {
boolean success = true;
if(!applySpecialCost(creature, weapon, command))
success = false;
// Quick Hotfix for AI NPCs buffing themselves
Client client = null;
if(creature.getClient() == null) {
if (creature.getAttachment("AI")!=null){
AIActor aiActor = (AIActor) creature.getAttachment("AI");
TangibleObject target = aiActor.getFollowObject();
if (target.getClient()!=null)
client = target.getClient(); // Use player's client reference that is in combat with that NPC
}
} else {
client = creature.getClient();
}
if(!success && client != null) {
IoSession session = creature.getClient().getSession();
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(creature.getObjectId(), actionCounter);
session.write(new ObjControllerMessage(0x0B, commandRemove).serialize());
StartTask startTask = new StartTask(actionCounter, creature.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), -1);
session.write(new ObjControllerMessage(0x0B, startTask).serialize());
return;
}
if (client == null)
return;
if (creature.hasBuff("co_position_secured"))
core.buffService.removeBuffFromCreatureByName(creature, "co_position_secured");
else
core.buffService.addBuffToCreature(creature, command.getBuffNameSelf(), creature);
StartTask startTask = new StartTask(actionCounter, creature.getObjectID(), command.getCommandCRC(), CRC.StringtoCRC(command.getCooldownGroup()), command.getCooldown());
ObjControllerMessage objController2 = new ObjControllerMessage(0x0B, startTask);
client.getSession().write(objController2.serialize());
CommandEnqueueRemove commandRemove = new CommandEnqueueRemove(creature.getObjectID(), actionCounter);
ObjControllerMessage objController3 = new ObjControllerMessage(0x0B, commandRemove);
client.getSession().write(objController3.serialize());
}
public void addHealOverTimeToCreature(final CreatureObject healer, final CreatureObject target, final CombatCommand command) {
if(target.getDotByName(command.getCommandName()) != null) {
DamageOverTime oldDot = target.getDotByName(command.getCommandName());
oldDot.getTask().cancel(true);
target.removeDot(oldDot);
}
final DamageOverTime dot = new DamageOverTime(command.getCommandName(), null, command.getDotType(), command.getDotDuration(), command.getDotIntensity());
target.addDot(dot);
dot.setStartTime(System.currentTimeMillis());
final ScheduledFuture<?> task = scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
if(dot.getRemainingDuration() <= 0) {
target.removeDot(dot);
dot.getTask().cancel(true);
}
doHealOverTimeTick(healer, target, command, dot);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 0, command.getDotDuration() / (command.getAddedDamage() / command.getDotIntensity()), TimeUnit.SECONDS);
dot.setTask(task);
}
private void doHealOverTimeTick(CreatureObject healer, CreatureObject target, CombatCommand command, DamageOverTime dot) {
int healAmount = command.getDotIntensity();
int healPotency = 0;
if(healer.getSkillMod("expertise_healing_all") != null)
healPotency += healer.getSkillMod("expertise_healing_all").getBase();
if(healer.getSkillMod("expertise_healing_line_me_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_me_heal").getBase();
if(healer.getSkillMod("expertise_healing_line_of_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_of_heal").getBase();
if(healer.getSkillMod("expertise_healing_line_sm_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_sm_heal").getBase();
if(healer.getSkillMod("expertise_healing_line_sp_heal") != null)
healPotency += healer.getSkillMod("expertise_healing_line_sp_heal").getBase();
if(healPotency > 0)
healAmount += (healAmount * (healPotency / 100));
if(target.getSkillMod("expertise_healing_reduction") != null)
healAmount *= (1 - target.getSkillMod("expertise_healing_reduction").getBase() / 100);
synchronized(target.getMutex()) {
target.setHealth(target.getHealth() + healAmount);
}
target.notifyObservers(new PlayClientEffectLocMessage("appearance/pt_heal_2.prt", target.getPlanet().getName(), target.getWorldPosition()), true);
}
public float getWeaponActionCostReduction(CreatureObject attacker, WeaponObject weapon) {
int weaponType = weapon.getWeaponType();
int actionReduction;
if(weaponType == WeaponType.HEAVYWEAPON || weaponType == WeaponType.FLAMETHROWER)
actionReduction = attacker.getSkillModBase("expertise_action_weapon_3");
else
actionReduction = attacker.getSkillModBase("expertise_action_weapon_" + Integer.toString(weaponType));
actionReduction += attacker.getSkillModBase("expertise_action_all");
return 1 + (actionReduction / 100);
}
public float getWeaponDamageIncrease(CreatureObject attacker, WeaponObject weapon) {
int weaponType = weapon.getWeaponType();
int weaponDmgIncrease;
if(weaponType == WeaponType.HEAVYWEAPON || weaponType == WeaponType.FLAMETHROWER)
weaponDmgIncrease = attacker.getSkillModBase("expertise_damage_weapon_3");
else
weaponDmgIncrease = attacker.getSkillModBase("expertise_damage_weapon_" + Integer.toString(weaponType));
if(weapon.isRanged())
weaponDmgIncrease += attacker.getSkillModBase("expertise_damage_ranged");
if(weapon.isMelee())
weaponDmgIncrease += attacker.getSkillModBase("expertise_damage_melee");
weaponDmgIncrease += attacker.getSkillModBase("expertise_damage_all");
return 1 + (weaponDmgIncrease / 100);
}
public void endCombat(CreatureObject defender) {
Vector<TangibleObject> defenderList = new Vector<TangibleObject>(defender.getDefendersList());
if (defenderList.size() > 0) {
defenderList.stream().forEach(attacker -> defender.removeDefender(attacker));
}
}
public void awardGcw(CreatureObject actor, CreatureObject target) {
try {
if (actor.getSlottedObject("ghost") == null || target.getSlottedObject("ghost") == null) {
return;
}
PlayerObject targetPlayer = (PlayerObject) target.getSlottedObject("ghost");
if (!core.factionService.isFactionEnemy(actor, target) && target.isAttackableBy(actor)) {
return;
}
Set<CreatureObject> inRange = new HashSet<CreatureObject>();
inRange.add(actor);
actor.getObservers().stream().forEach(observer -> inRange.add((CreatureObject) observer.getParent()));
for (CreatureObject rangedPlayer : inRange) {
if (!(rangedPlayer.getFaction().equals(actor.getFaction()) && target.isAttackableBy(rangedPlayer))){
continue;
}
if (!((rangedPlayer.getLevel() / target.getLevel() * 100) < 70)) {
continue;
}
if (rangedPlayer.getSlottedObject("ghost") == null) {
continue;
}
PlayerObject player = (PlayerObject) rangedPlayer.getSlottedObject("ghost");
float gcwPoints = 37.5f;
gcwPoints *= targetPlayer.getCurrentRank();
float decay = (((float) player.getPvpKills()) / 100f * gcwPoints);
gcwPoints = (((gcwPoints - decay) < 0) ? 0 : (gcwPoints - decay));
core.gcwService.addGcwPoints(rangedPlayer, (int) gcwPoints, GcwType.Player);
actor.sendSystemMessage(OutOfBand.ProsePackage("@gcw:gcw_rank_pvp_kill_point_grant", (int) gcwPoints, "TT", target.getCustomName()), DisplayType.Broadcast);
}
} catch (Exception e) {
}
}
}