/*
* This file is part of QuarterBukkit-Plugin.
* Copyright (c) 2012 QuarterCode <http://www.quartercode.com/>
*
* QuarterBukkit-Plugin 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.
*
* QuarterBukkit-Plugin 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 QuarterBukkit-Plugin. If not, see <http://www.gnu.org/licenses/>.
*/
package com.quartercode.quarterbukkit.api.objectsystem.util;
import org.apache.commons.lang.Validate;
import com.quartercode.quarterbukkit.api.objectsystem.ActiveObjectSystem;
import com.quartercode.quarterbukkit.api.objectsystem.Modifier;
import com.quartercode.quarterbukkit.api.objectsystem.ModifierWrapper;
import com.quartercode.quarterbukkit.api.objectsystem.physics.PhysicsObject;
import com.quartercode.quarterbukkit.api.shape.Shape;
/**
* A {@link Modifier} wrapper that cancels the rest of the modifier chain if the given {@link PhysicsObject} isn't located inside a given {@link Shape}.
* Different null values which should be returned when the chain is cancelled apart from {@code null} can be defined.
*
* @param <O> The type of object the shaped modifier accepts. This must extend {@link PhysicsObject}.
* @param <M> The type of the modification object that is returned by the shaped modifier and the rest of the modifier chain.
* Note that the null value must be an instance of this type.
* @see Shape
*/
public class ShapedModifier<O extends PhysicsObject, M> extends ModifierWrapper<O, M> {
private Shape shape;
private boolean inversed;
private M nullObject;
/**
* Creates a new shaped modifier that checks whether each {@link PhysicsObject} is located inside the given {@link Shape} for the modifier chain to proceed.
* In case of a canceling of the chain, a {@code null} reference is returned.
*
* @param shape The shape each object must be located in for the check to succeed.
* Note that all coordinates stored by the shape are always relative to the origin of any {@link ActiveObjectSystem} the modifier is used by.
* @param wrapped The wrapped {@link Modifier} that is only called when the check succeeds.
*/
public ShapedModifier(Shape shape, Modifier<? super O, M> wrapped) {
this(shape, false, null, wrapped);
}
/**
* Creates a new shaped modifier that checks whether each {@link PhysicsObject} is located either inside or outside the given {@link Shape} for the modifier chain to proceed.
* In case of a canceling of the chain, a {@code null} reference is returned.
*
* @param shape The shape each object must be located in for the check to succeed.
* Note that all coordinates stored by the shape are always relative to the origin of any {@link ActiveObjectSystem} the modifier is used by.
* @param inversed Whether the shape modifier should check that each {@link PhysicsObject} is <b>outside</b> the provided shape instead of inside.
* @param wrapped The wrapped {@link Modifier} that is only called when the check succeeds.
*/
public ShapedModifier(Shape shape, boolean inversed, Modifier<? super O, M> wrapped) {
this(shape, inversed, null, wrapped);
}
/**
* Creates a new shaped modifier that checks whether each {@link PhysicsObject} is located inside the given {@link Shape} for the modifier chain to proceed.
* In case of a canceling of the chain, the given null object is returned instead of a plain {@code null} reference.
*
* @param shape The shape each object must be located in for the check to succeed.
* Note that all coordinates stored by the shape are always relative to the origin of any {@link ActiveObjectSystem} the modifier is used by.
* @param nullObject The null object that is returned when the modifier chain is cancelled.
* @param wrapped The wrapped {@link Modifier} that is only called when the check succeeds.
*/
public ShapedModifier(Shape shape, M nullObject, Modifier<? super O, M> wrapped) {
this(shape, false, nullObject, wrapped);
}
/**
* Creates a new shaped modifier that checks whether each {@link PhysicsObject} is located either inside or outside the given {@link Shape} for the modifier chain to proceed.
* In case of a canceling of the chain, the given null object is returned instead of a plain {@code null} reference.
*
* @param shape The shape each object must be located in for the check to succeed.
* Note that all coordinates stored by the shape are always relative to the origin of any {@link ActiveObjectSystem} the modifier is used by.
* @param inversed Whether the shape modifier should check that each {@link PhysicsObject} is <b>outside</b> the provided shape instead of inside.
* @param nullObject The null object that is returned when the modifier chain is cancelled.
* @param wrapped The wrapped {@link Modifier} that is only called when the check succeeds.
*/
public ShapedModifier(Shape shape, boolean inversed, M nullObject, Modifier<? super O, M> wrapped) {
super(false, wrapped);
setShape(shape);
this.inversed = inversed;
this.nullObject = nullObject;
}
/**
* Returns the {@link Shape} each {@link PhysicsObject} must be located in for the modifier chain to proceed.
* Note that all coordinates stored by the shape are always relative to the origin of any {@link ActiveObjectSystem} the modifier is used by.
*
* @return The shape for the intersection check.
*/
public Shape getShape() {
return shape;
}
/**
* Sets the {@link Shape} each {@link PhysicsObject} must be located in for the modifier chain to proceed.
* Note that all coordinates stored by the shape are always relative to the origin of any {@link ActiveObjectSystem} the modifier is used by.
*
* @param shape The new shape for the intersection check.
* @return This object.
*/
public ShapedModifier<O, M> setShape(Shape shape) {
Validate.notNull(shape, "Shape of shaped modifier cannot be null");
this.shape = shape;
return this;
}
/**
* Returns whether the shape modifier checks that each {@link PhysicsObject} is <b>outside</b> the provided {@link Shape}.
*
* @return Whether the check mode is inversed.
*/
public boolean isInversed() {
return inversed;
}
/**
* Sets whether the shape modifier checks that each {@link PhysicsObject} is <b>outside</b> the provided {@link Shape}.
*
* @param inversed Whether the check mode should be inversed.
* @return This object.
*/
public ShapedModifier<O, M> setInversed(boolean inversed) {
this.inversed = inversed;
return this;
}
/**
* Returns the null object that is returned when the modifier chain is cancelled.
* By default, the null object is a {@code null} reference.
* Note that the null object is not cloned when it is returned, so be careful with mutable objects.
*
* @return The null return object for canceling the chain.
*/
public M getNullObject() {
return nullObject;
}
/**
* Sets the null object that is returned when the modifier chain is cancelled.
* By default, the null object is a {@code null} reference.
* Note that the null object is not cloned when it is returned, so be careful with mutable objects.
*
* @param nullObject The new null return object for canceling the chain.
* @return This object.
*/
public ShapedModifier<O, M> setNullObject(M nullObject) {
this.nullObject = nullObject;
return this;
}
@Override
public M getModification(O object) {
boolean intersectsShape = shape.intersects(object.getPosition());
if (intersectsShape == !inversed) {
return getWrapped().getModification(object);
} else {
return nullObject;
}
}
}