238 lines
10 KiB
Java
238 lines
10 KiB
Java
/*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package fr.neatmonster.nocheatplus.checks.fight;
|
|
|
|
import org.bukkit.Location;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.util.Vector;
|
|
|
|
import fr.neatmonster.nocheatplus.checks.Check;
|
|
import fr.neatmonster.nocheatplus.checks.CheckType;
|
|
import fr.neatmonster.nocheatplus.checks.moving.location.tracking.LocationTrace.ITraceEntry;
|
|
import fr.neatmonster.nocheatplus.compat.MCAccess;
|
|
import fr.neatmonster.nocheatplus.utilities.collision.CollisionUtil;
|
|
import fr.neatmonster.nocheatplus.utilities.location.TrigUtil;
|
|
|
|
/**
|
|
* The Direction check will find out if a player tried to interact with something that's not in their field of view.
|
|
*/
|
|
public class Direction extends Check {
|
|
|
|
/**
|
|
* Instantiates a new direction check.
|
|
*/
|
|
public Direction() {
|
|
super(CheckType.FIGHT_DIRECTION);
|
|
}
|
|
|
|
/**
|
|
* "Classic" check.
|
|
*
|
|
* @param player
|
|
* the player
|
|
* @param damaged
|
|
* the damaged
|
|
* @return true, if successful
|
|
*/
|
|
public boolean check(final Player player, final Location loc,
|
|
final Entity damaged, final boolean damagedIsFake, final Location dLoc,
|
|
final FightData data, final FightConfig cc) {
|
|
boolean cancel = false;
|
|
|
|
final MCAccess mcAccess = this.mcAccess.getHandle();
|
|
|
|
// Safeguard, if entity is complex, this check will fail due to giant and hard to define hitboxes.
|
|
// if (damaged instanceof EntityComplex || damaged instanceof EntityComplexPart)
|
|
if (!damagedIsFake && mcAccess.isComplexPart(damaged)) {
|
|
return false;
|
|
}
|
|
|
|
// Find out how wide the entity is.
|
|
final double width = damagedIsFake ? 0.6 : mcAccess.getWidth(damaged);
|
|
|
|
// entity.height is broken and will always be 0, therefore. Calculate height instead based on boundingBox.
|
|
final double height = damagedIsFake ? (damaged instanceof LivingEntity ? ((LivingEntity) damaged).getEyeHeight() : 1.75) : mcAccess.getHeight(damaged);
|
|
|
|
// TODO: allow any hit on the y axis (might just adapt interface to use foot position + height)!
|
|
|
|
// How far "off" is the player with their aim. We calculate from the players eye location and view direction to
|
|
// the center of the target entity. If the line of sight is more too far off, "off" will be bigger than 0.
|
|
final Vector direction = loc.getDirection();
|
|
|
|
final double off;
|
|
if (cc.directionStrict){
|
|
off = CollisionUtil.combinedDirectionCheck(loc, player.getEyeHeight(), direction, dLoc.getX(), dLoc.getY() + height / 2D, dLoc.getZ(), width, height, TrigUtil.DIRECTION_PRECISION, 80.0);
|
|
}
|
|
else{
|
|
// Also take into account the angle.
|
|
off = CollisionUtil.directionCheck(loc, player.getEyeHeight(), direction, dLoc.getX(), dLoc.getY() + height / 2D, dLoc.getZ(), width, height, TrigUtil.DIRECTION_PRECISION);
|
|
}
|
|
|
|
if (off > 0.1) {
|
|
// Player failed the check. Let's try to guess how far they were from looking directly to the entity...
|
|
final Vector blockEyes = new Vector(dLoc.getX() - loc.getX(), dLoc.getY() + height / 2D - loc.getY() - player.getEyeHeight(), dLoc.getZ() - loc.getZ());
|
|
final double distance = blockEyes.crossProduct(direction).length() / direction.length();
|
|
|
|
// Add the overall violation level of the check.
|
|
data.directionVL += distance;
|
|
|
|
// Execute whatever actions are associated with this check and the violation level and find out if we should
|
|
// cancel the event.
|
|
cancel = executeActions(player, data.directionVL, distance, cc.directionActions).willCancel();
|
|
|
|
if (cancel) {
|
|
// Deal an attack penalty time.
|
|
data.attackPenalty.applyPenalty(cc.directionPenalty);
|
|
}
|
|
} else {
|
|
// Reward the player by lowering their violation level.
|
|
data.directionVL *= 0.8D;
|
|
}
|
|
|
|
return cancel;
|
|
}
|
|
|
|
/**
|
|
* Data context for iterating over TraceEntry instances.
|
|
* @param player
|
|
* @param loc
|
|
* @param damaged
|
|
* @param damagedLoc
|
|
* @param data
|
|
* @param cc
|
|
* @return
|
|
*/
|
|
public DirectionContext getContext(final Player player, final Location loc,
|
|
final Entity damaged, final boolean damagedIsFake, final Location damagedLoc,
|
|
final FightData data, final FightConfig cc) {
|
|
final DirectionContext context = new DirectionContext();
|
|
// Find out how wide the entity is.
|
|
if (damagedIsFake) {
|
|
// Assume player / default.
|
|
context.damagedComplex = false; // Later prefer bukkit based provider.
|
|
}
|
|
else {
|
|
final MCAccess mcAccess = this.mcAccess.getHandle();
|
|
context.damagedComplex = mcAccess.isComplexPart(damaged);
|
|
}
|
|
context.direction = loc.getDirection();
|
|
context.lengthDirection = context.direction.length();
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* Check if the player fails the direction check, no change of FightData.
|
|
* @param player
|
|
* @param loc
|
|
* @param damaged
|
|
* @param dLoc
|
|
* @param context
|
|
* @param data
|
|
* @param cc
|
|
* @return
|
|
*/
|
|
public boolean loopCheck(final Player player, final Location loc,
|
|
final Entity damaged, final ITraceEntry dLoc,
|
|
final DirectionContext context,
|
|
final FightData data, final FightConfig cc) {
|
|
|
|
// Ignore complex entities for the moment.
|
|
if (context.damagedComplex) {
|
|
// TODO: Revise :p
|
|
return false;
|
|
}
|
|
boolean cancel = false;
|
|
|
|
// TODO: allow any hit on the y axis (might just adapt interface to use foot position + height)!
|
|
|
|
// How far "off" is the player with their aim. We calculate from the players eye location and view direction to
|
|
// the center of the target entity. If the line of sight is more too far off, "off" will be bigger than 0.
|
|
final double damagedBoxMarginHorizontal = dLoc.getBoxMarginHorizontal();
|
|
final double damagedBoxMarginVertical = dLoc.getBoxMarginVertical();
|
|
final double off;
|
|
if (cc.directionStrict){
|
|
off = CollisionUtil.combinedDirectionCheck(loc, player.getEyeHeight(), context.direction, dLoc.getX(), dLoc.getY() + damagedBoxMarginVertical / 2D, dLoc.getZ(), damagedBoxMarginHorizontal * 2.0, damagedBoxMarginVertical, TrigUtil.DIRECTION_LOOP_PRECISION, 80.0);
|
|
}
|
|
else{
|
|
// Also take into account the angle.
|
|
off = CollisionUtil.directionCheck(loc, player.getEyeHeight(),
|
|
context.direction, dLoc.getX(),
|
|
dLoc.getY() + damagedBoxMarginVertical / 2D, dLoc.getZ(),
|
|
damagedBoxMarginHorizontal * 2.0, damagedBoxMarginVertical,
|
|
TrigUtil.DIRECTION_LOOP_PRECISION);
|
|
}
|
|
|
|
if (off > 0.0) {
|
|
if (dLoc.isInside(loc.getX(), loc.getY() + player.getEyeHeight(),
|
|
loc.getZ())) { // Inside box.
|
|
context.minResult = 0.0;
|
|
}
|
|
else {
|
|
if (off > 0.11) {
|
|
// Player failed the check. Let's try to guess how far they were from looking directly to the entity...
|
|
final Vector blockEyes = new Vector(dLoc.getX() - loc.getX(), dLoc.getY() + damagedBoxMarginVertical / 2D - loc.getY() - player.getEyeHeight(), dLoc.getZ() - loc.getZ());
|
|
final double distance = blockEyes.crossProduct(context.direction).length() / context.lengthDirection;
|
|
context.minViolation = Math.min(context.minViolation, distance);
|
|
cancel = true;
|
|
}
|
|
context.minResult = Math.min(context.minResult, off);
|
|
}
|
|
}
|
|
|
|
return cancel;
|
|
}
|
|
|
|
/**
|
|
* Apply changes to FightData according to check results (context), trigger violations.
|
|
* @param player
|
|
* @param loc
|
|
* @param damaged
|
|
* @param context
|
|
* @param forceViolation
|
|
* @param data
|
|
* @param cc
|
|
* @return
|
|
*/
|
|
public boolean loopFinish(final Player player, final Location loc, final Entity damaged, final DirectionContext context, final boolean forceViolation, final FightData data, final FightConfig cc) {
|
|
boolean cancel = false;
|
|
final double off = forceViolation && context.minViolation != Double.MAX_VALUE ? context.minViolation : context.minResult;
|
|
if (off == Double.MAX_VALUE) {
|
|
return false;
|
|
}
|
|
else if (off > 0.1) {
|
|
// Add the overall violation level of the check.
|
|
data.directionVL += off;
|
|
|
|
// Execute whatever actions are associated with this check and the violation level and find out if we should
|
|
// cancel the event.
|
|
cancel = executeActions(player, data.directionVL, off, cc.directionActions).willCancel();
|
|
|
|
if (cancel) {
|
|
// Deal an attack penalty time.
|
|
data.attackPenalty.applyPenalty(cc.directionPenalty);
|
|
}
|
|
}
|
|
else {
|
|
// Reward the player by lowering their violation level.
|
|
data.directionVL *= 0.8D;
|
|
}
|
|
|
|
return cancel;
|
|
}
|
|
|
|
}
|