Fixed Cone of Effect attacks working in 360 degrees and errors when using flamethrower in free-target mode #77

This commit is contained in:
Ziggy
2021-03-19 16:57:06 +01:00
parent 2c8fabe50b
commit 1e1b8a9228
2 changed files with 67 additions and 18 deletions

View File

@@ -80,11 +80,15 @@ enum CombatCommandAttack implements CombatCommandHitType {
// Same as AREA, but the target is the destination for the AoE and can take damage
doCombatArea(source, delayEgg != null ? delayEgg : target, info, weapon, command, true);
} else {
// TODO AoE based on Location instead of delay egg
// TODO AoE based on Location instead of delay egg (free-targeting with heavy weapons)
}
break;
case CONE:
doCombatCone(source, target, info, weapon, command);
if (target != null) {
doCombatCone(source, target, info, weapon, command);
} else {
// TODO CoE based on Location (free-targeting with flamethrowers)
}
break;
default:
break;
@@ -92,12 +96,11 @@ enum CombatCommandAttack implements CombatCommandHitType {
}
}
private void doCombatCone(CreatureObject source, SWGObject target, AttackInfo info, WeaponObject weapon, CombatCommand command) {
private void doCombatCone(CreatureObject source, Location targetWorldLocation, AttackInfo info, WeaponObject weapon, CombatCommand command) {
double coneLength = command.getConeLength();
double coneWidth = command.getConeWidth();
Location sourceWorldLocation = source.getWorldLocation();
Location targetWorldLocation = target.getWorldLocation();
double dirX = targetWorldLocation.getX() - sourceWorldLocation.getX();
double dirZ = targetWorldLocation.getZ() - sourceWorldLocation.getZ();
@@ -108,37 +111,33 @@ enum CombatCommandAttack implements CombatCommandHitType {
Set<CreatureObject> targets = objectsToCheck.stream()
.filter(CreatureObject.class::isInstance)
.map(CreatureObject.class::cast)
.filter(candidate -> !target.equals(source)) // Make sure the attacker can't damage themselves
.filter(candidate -> !candidate.equals(source)) // Make sure the attacker can't damage themselves
.filter(source::isAttackable)
.filter(candidate -> canPerform(source, target, command) == CombatStatus.SUCCESS)
.filter(candidate -> canPerform(source, candidate, command) == CombatStatus.SUCCESS)
.filter(candidate -> sourceWorldLocation.distanceTo(candidate.getLocation()) <= coneLength)
.filter(candidate -> {
Location candidateWorldLocation = candidate.getWorldLocation();
return isInConeAngle(sourceWorldLocation, candidateWorldLocation, coneLength, coneWidth, dirX, dirZ);
return isInConeAngle(sourceWorldLocation, candidateWorldLocation, coneWidth, dirX, dirZ);
})
.collect(Collectors.toSet());
doCombat(source, targets, weapon, info, command);
}
private boolean isInConeAngle(Location attackerLocation, Location targetLocation, double coneLength, double coneWidth, double directionX, double directionZ) {
double radius = coneWidth / 2d;
double angle = 2 * Math.atan(coneLength / radius);
private void doCombatCone(CreatureObject source, SWGObject target, AttackInfo info, WeaponObject weapon, CombatCommand command) {
doCombatCone(source, target.getWorldLocation(), info, weapon, command);
}
boolean isInConeAngle(Location attackerLocation, Location targetLocation, double coneWidth, double directionX, double directionZ) {
double targetX = targetLocation.getX() - attackerLocation.getX();
double targetZ = targetLocation.getZ() - attackerLocation.getZ();
double targetAngle = Math.atan2(targetZ, targetX) - Math.atan2(directionZ, directionX);
double degrees = targetAngle * 180 / Math.PI;
double coneAngle = angle / 2;
if (degrees > coneAngle || degrees < -coneAngle) {
return false;
}
return true;
return !(Math.abs(degrees) > coneWidth);
}
private static void doCombatSingle(CreatureObject source, SWGObject target, AttackInfo info, WeaponObject weapon, CombatCommand command) {

View File

@@ -0,0 +1,50 @@
package com.projectswg.holocore.services.gameplay.combat.command;
import com.projectswg.common.data.location.Location;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class TestCombatCommandAttack {
@Test
public void testConeRange() {
Location attackerLocation = new Location.LocationBuilder()
.setX(0)
.setY(0)
.setZ(0)
.build();
Location targetLocation = new Location.LocationBuilder()
.setX(20)
.setY(0)
.setZ(10)
.build();
Location collateralInsideCone1 = new Location.LocationBuilder()
.setX(10)
.setY(0)
.setZ(5)
.build();
Location collateralInsideCone2 = new Location.LocationBuilder()
.setX(25)
.setY(0)
.setZ(10)
.build();
Location collateralOutsideCone = new Location.LocationBuilder()
.setX(-20)
.setY(0)
.setZ(-15)
.build();
double dirX = targetLocation.getX() - attackerLocation.getX();
double dirZ = targetLocation.getZ() - attackerLocation.getZ();
CombatCommandAttack instance = CombatCommandAttack.INSTANCE;
assertTrue(instance.isInConeAngle(attackerLocation, collateralInsideCone1, 30, dirX, dirZ));
assertTrue(instance.isInConeAngle(attackerLocation, collateralInsideCone2, 30, dirX, dirZ));
assertFalse(instance.isInConeAngle(attackerLocation, collateralOutsideCone, 30, dirX, dirZ));
}
}