package com.laytonsmith.core.events.drivers;
import com.laytonsmith.PureUtilities.Common.StringUtils;
import com.laytonsmith.PureUtilities.Version;
import com.laytonsmith.abstraction.MCEntity;
import com.laytonsmith.abstraction.MCLocation;
import com.laytonsmith.abstraction.MCPlayer;
import com.laytonsmith.abstraction.MCVehicle;
import com.laytonsmith.abstraction.MCProjectile;
import com.laytonsmith.abstraction.MCProjectileSource;
import com.laytonsmith.abstraction.blocks.MCBlockProjectileSource;
import com.laytonsmith.abstraction.enums.MCCollisionType;
import com.laytonsmith.abstraction.enums.MCEntityType;
import com.laytonsmith.abstraction.events.MCVehicleBlockCollideEvent;
import com.laytonsmith.abstraction.events.MCVehicleCollideEvent;
import com.laytonsmith.abstraction.events.MCVehicleEnitityCollideEvent;
import com.laytonsmith.abstraction.events.MCVehicleEnterExitEvent;
import com.laytonsmith.abstraction.events.MCVehicleMoveEvent;
import com.laytonsmith.abstraction.events.MCVehicleDestroyEvent;
import com.laytonsmith.annotations.api;
import com.laytonsmith.core.CHVersion;
import com.laytonsmith.core.ObjectGenerator;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.constructs.CArray;
import com.laytonsmith.core.constructs.CBoolean;
import com.laytonsmith.core.constructs.CNull;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.events.AbstractEvent;
import com.laytonsmith.core.events.BindableEvent;
import com.laytonsmith.core.events.BoundEvent;
import com.laytonsmith.core.events.Driver;
import com.laytonsmith.core.events.EventBuilder;
import com.laytonsmith.core.events.EventUtils;
import com.laytonsmith.core.events.Prefilters;
import com.laytonsmith.core.events.Prefilters.PrefilterType;
import com.laytonsmith.core.exceptions.CRE.CREBadEntityException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import com.laytonsmith.core.exceptions.EventException;
import com.laytonsmith.core.exceptions.PrefilterNonMatchException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
*
* @author jb_aero
*/
public class VehicleEvents {
@api
public static class vehicle_enter extends AbstractEvent {
@Override
public String getName() {
return "vehicle_enter";
}
@Override
public String docs() {
return "{vehicletype: <macro> the entitytype of the vehicle | passengertype: <macro>"
+ " the enitytype of the passenger} Fires when an entity enters a vehicle."
+ " {vehicletype | passengertype | vehicle: entityID | passenger: entityID"
+ " | player: player name if passenger is a player, null otherwise}"
+ " {}"
+ " {}";
}
@Override
public boolean matches(Map<String, Construct> prefilter, BindableEvent event) throws PrefilterNonMatchException {
if (event instanceof MCVehicleEnterExitEvent) {
MCVehicleEnterExitEvent e = (MCVehicleEnterExitEvent) event;
Prefilters.match(prefilter, "vehicletype", e.getVehicle().getType().name(), PrefilterType.MACRO);
Prefilters.match(prefilter, "passengertype", e.getEntity().getType().name(), PrefilterType.MACRO);
return true;
}
return false;
}
@Override
public BindableEvent convert(CArray manualObject, Target t) {
throw ConfigRuntimeException.CreateUncatchableException("Unsupported Operation", Target.UNKNOWN);
}
@Override
public Map<String, Construct> evaluate(BindableEvent event) throws EventException {
if (event instanceof MCVehicleEnterExitEvent) {
MCVehicleEnterExitEvent e = (MCVehicleEnterExitEvent) event;
Target t = Target.UNKNOWN;
Map<String, Construct> ret = evaluate_helper(e);
ret.put("vehicletype", new CString(e.getVehicle().getType().name(), t));
ret.put("passengertype", new CString(e.getEntity().getType().name(), t));
ret.put("vehicle", new CString(e.getVehicle().getUniqueId().toString(), t));
ret.put("passenger", new CString(e.getEntity().getUniqueId().toString(), t));
if (e.getEntity().getType().getAbstracted() == MCEntityType.MCVanillaEntityType.PLAYER) {
ret.put("player", new CString(((MCPlayer)e.getEntity()).getName(), t));
} else {
ret.put("player", CNull.NULL);
}
return ret;
} else {
throw new EventException("Could not convert to MCVehicleEnterExitEvent");
}
}
@Override
public Driver driver() {
return Driver.VEHICLE_ENTER;
}
@Override
public boolean modifyEvent(String key, Construct value, BindableEvent event) {
return false;
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
}
@api
public static class vehicle_leave extends AbstractEvent {
@Override
public String getName() {
return "vehicle_leave";
}
@Override
public String docs() {
return "{vehicletype: <macro> the entitytype of the vehicle | passengertype: <macro>"
+ " the enitytype of the passenger} Fires when an entity leaves a vehicle."
+ " {vehicletype | passengertype | vehicle: entityID | passenger: entityID"
+ " | player: player name if passenger is a player, null otherwise}"
+ " {}"
+ " {}";
}
@Override
public boolean matches(Map<String, Construct> prefilter, BindableEvent event) throws PrefilterNonMatchException {
if (event instanceof MCVehicleEnterExitEvent) {
MCVehicleEnterExitEvent e = (MCVehicleEnterExitEvent) event;
Prefilters.match(prefilter, "vehicletype", e.getVehicle().getType().name(), PrefilterType.MACRO);
Prefilters.match(prefilter, "passengertype", e.getEntity().getType().name(), PrefilterType.MACRO);
return true;
}
return false;
}
@Override
public BindableEvent convert(CArray manualObject, Target t) {
throw ConfigRuntimeException.CreateUncatchableException("Unsupported Operation", Target.UNKNOWN);
}
@Override
public Map<String, Construct> evaluate(BindableEvent event) throws EventException {
if (event instanceof MCVehicleEnterExitEvent) {
MCVehicleEnterExitEvent e = (MCVehicleEnterExitEvent) event;
Target t = Target.UNKNOWN;
Map<String, Construct> ret = evaluate_helper(e);
ret.put("vehicletype", new CString(e.getVehicle().getType().name(), t));
ret.put("passengertype", new CString(e.getEntity().getType().name(), t));
ret.put("vehicle", new CString(e.getVehicle().getUniqueId().toString(), t));
ret.put("passenger", new CString(e.getEntity().getUniqueId().toString(), t));
if (e.getEntity().getType().getAbstracted() == MCEntityType.MCVanillaEntityType.PLAYER) {
ret.put("player", new CString(((MCPlayer)e.getEntity()).getName(), t));
} else {
ret.put("player", CNull.NULL);
}
return ret;
} else {
throw new EventException("Could not convert to MCVehicleEnterExitEvent");
}
}
@Override
public Driver driver() {
return Driver.VEHICLE_LEAVE;
}
@Override
public boolean modifyEvent(String key, Construct value, BindableEvent event) {
return false;
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
}
@api
public static class vehicle_collide extends AbstractEvent {
@Override
public String getName() {
return "vehicle_collide";
}
@Override
public String docs() {
return "{type: <macro> The entitytype of the vehicle | collisiontype: <macro> One of "
+ StringUtils.Join(MCCollisionType.values(), ", ", ", or ", " or ")
+ " | hittype: <macro> Matches an entitytype in an enitity collision"
+ " | hittype: <item match> Matches a block in a block collision}"
+ " Fires when a vehicle runs into something. If it ran into a block,"
+ " event data will contain block info. If it ran into an entity,"
+ " event data will contain info and options relevant to hitting an entity."
+ " {type | id: The entityID of the vehicle | entity: the entityID of the entity that was hit"
+ " | block: the location of the block that was hit | collisiontype | collide | pickup}"
+ " {collide: whether the vehicle hits the entity or passes through it | pickup: whether or not the"
+ " vehicle pick up the entity | both fields can only be modified for entity collisions}"
+ " {}";
}
@Override
public boolean matches(Map<String, Construct> prefilter, BindableEvent e) throws PrefilterNonMatchException {
if (e instanceof MCVehicleCollideEvent) {
MCVehicleCollideEvent event = (MCVehicleCollideEvent) e;
Prefilters.match(prefilter, "type", event.getVehicle().getType().name(), PrefilterType.MACRO);
Prefilters.match(prefilter, "collisiontype", event.getCollisionType().name(), PrefilterType.MACRO);
switch (event.getCollisionType()) {
case BLOCK:
Prefilters.match(prefilter, "hittype", Static.ParseItemNotation(((MCVehicleBlockCollideEvent) event)
.getBlock()), PrefilterType.ITEM_MATCH);
break;
case ENTITY:
Prefilters.match(prefilter, "hittype", ((MCVehicleEnitityCollideEvent) event)
.getEntity().getType().name(), PrefilterType.MACRO);
break;
default:
throw ConfigRuntimeException.CreateUncatchableException("Greetings from the future! If you are seeing this message,"
+ " Minecraft has reached the point where vehicles can hit things that are neither"
+ " a block nor an entity. Please report this error to developers.", Target.UNKNOWN);
}
return true;
}
return false;
}
@Override
public BindableEvent convert(CArray manualObject, Target t) {
throw ConfigRuntimeException.CreateUncatchableException("Unsupported Operation", Target.UNKNOWN);
}
@Override
public Map<String, Construct> evaluate(BindableEvent event) throws EventException {
if (event instanceof MCVehicleCollideEvent) {
MCVehicleCollideEvent e = (MCVehicleCollideEvent) event;
Target t = Target.UNKNOWN;
Map<String, Construct> ret = evaluate_helper(e);
ret.put("type", new CString(e.getVehicle().getType().name(), t));
ret.put("id", new CString(e.getVehicle().getUniqueId().toString(), t));
ret.put("collisiontype", new CString(e.getCollisionType().name(), t));
Construct block = CNull.NULL;
Construct entity = CNull.NULL;
boolean collide = true;
boolean pickup = false;
switch (e.getCollisionType()) {
case BLOCK:
block = ObjectGenerator.GetGenerator().location(
((MCVehicleBlockCollideEvent) e).getBlock().getLocation());
break;
case ENTITY:
MCVehicleEnitityCollideEvent vec = (MCVehicleEnitityCollideEvent) e;
entity = new CString(vec.getEntity().getUniqueId().toString(), t);
collide = !vec.isCollisionCancelled();
pickup = !vec.isPickupCancelled();
break;
default:
throw ConfigRuntimeException.CreateUncatchableException("Greetings from the future! If you are seeing this message,"
+ " Minecraft has reached the point where vehicles can hit things that are neither"
+ " a block nor an entity. Please report this error to developers.", t);
}
ret.put("block", block);
ret.put("entity", entity);
ret.put("pickup", CBoolean.get(pickup));
ret.put("collide", CBoolean.get(collide));
return ret;
} else {
throw new EventException("The event could not be converted to MCVehicleCollideEvent.");
}
}
@Override
public Driver driver() {
return Driver.VEHICLE_COLLIDE;
}
@Override
public boolean modifyEvent(String key, Construct value, BindableEvent event) {
if (event instanceof MCVehicleEnitityCollideEvent) {
MCVehicleEnitityCollideEvent e = (MCVehicleEnitityCollideEvent) event;
if (key.equals("collide")) {
e.setCollisionCancelled(!Static.getBoolean(value));
return true;
}
if (key.equals("pickup")) {
e.setPickupCancelled(!Static.getBoolean(value));
return true;
}
}
return false;
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
}
private static final Set<Integer> thresholdList = new HashSet<>();
public static Set<Integer> GetThresholdList(){
return thresholdList;
}
private static final Map<Integer, Map<UUID, MCLocation>> lastVehicleLocations = new HashMap<>();
public static Map<UUID, MCLocation> GetLastLocations(Integer i){
if (!lastVehicleLocations.containsKey(i)) {
HashMap<UUID, MCLocation> newLocation = new HashMap<>();
lastVehicleLocations.put(i, newLocation);
return newLocation;
}
return(lastVehicleLocations.get(i));
}
@api
public static class vehicle_move extends AbstractEvent {
@Override
public String getName() {
return "vehicle_move";
}
@Override
public String docs() {
return "{vehicletype: <macro> the entitytype of the vehicle | passengertype: <macro>"
+ " the enitytype of the passenger | world: <string> the world the vehicle is in"
+ "| from: <location match> This should be a location array (x, y, z, world)."
+ "| to: <location match> The location the vehicle is now in."
+ "| threshold: <custom> The minimum distance the vehicle must have travelled before the event"
+ " will be triggered. This is based on the 3D distance, and is measured in block units.}"
+ " Fires when a vehicle is moving. Due to the high frequency of this event, prefilters are"
+ " extremely important to use -- especially threshold."
+ "{world | from: Get the previous position | to: Get the next position"
+ " | vehicletype | passengertype | id: entityID | passenger: entityID"
+ " | player: player name if passenger is a player, null otherwise}"
+ " {}"
+ " {}";
}
@Override
public void hook() {
thresholdList.clear();
lastVehicleLocations.clear();
}
@Override
public void bind(BoundEvent event) {
int threshold = 1;
Map<String, Construct> prefilters = event.getPrefilter();
if(prefilters.containsKey("threshold")) {
threshold = Static.getInt32(prefilters.get("threshold"), Target.UNKNOWN);
}
thresholdList.add(threshold);
}
@Override
public void unbind(BoundEvent event) {
int threshold = 1;
Map<String, Construct> prefilters = event.getPrefilter();
if(prefilters.containsKey("threshold")) {
threshold = Static.getInt32(prefilters.get("threshold"), Target.UNKNOWN);
}
for (BoundEvent b : EventUtils.GetEvents(event.getDriver())) {
if (b.getId().equals(event.getId())) {
continue;
}
if (b.getPrefilter().containsKey("threshold")) {
if(threshold == Static.getInt(b.getPrefilter().get("threshold"), Target.UNKNOWN)) {
return;
}
}
}
thresholdList.remove(threshold);
lastVehicleLocations.remove(threshold);
}
@Override
public void cancel(BindableEvent o, boolean state) {
if (o instanceof MCVehicleMoveEvent) {
((MCVehicleMoveEvent) o).setCancelled(state);
}
}
@Override
public boolean isCancellable(BindableEvent o) {
return true;
}
@Override
public boolean isCancelled(BindableEvent o) {
return o instanceof MCVehicleMoveEvent && ((MCVehicleMoveEvent) o).isCancelled();
}
@Override
public boolean matches(Map<String, Construct> prefilter, BindableEvent e) throws PrefilterNonMatchException {
if (e instanceof MCVehicleMoveEvent) {
MCVehicleMoveEvent event = (MCVehicleMoveEvent) e;
if(prefilter.containsKey("threshold")) {
if(Static.getInt(prefilter.get("threshold"), Target.UNKNOWN) != event.getThreshold()) {
return false;
}
} else if(event.getThreshold() != 1) {
return false;
}
if(prefilter.containsKey("world")
&& !prefilter.get("world").val().equals(event.getFrom().getWorld().getName())) {
return false;
}
if (prefilter.containsKey("from")) {
MCLocation pLoc = ObjectGenerator.GetGenerator().location(prefilter.get("from"), event.getVehicle().getVehicle().getWorld(), Target.UNKNOWN);
MCLocation loc = event.getFrom();
if (loc.getBlockX() != pLoc.getBlockX() || loc.getBlockY() != pLoc.getBlockY() || loc.getBlockZ() != pLoc.getBlockZ()) {
return false;
}
}
if (prefilter.containsKey("to")) {
MCLocation pLoc = ObjectGenerator.GetGenerator().location(prefilter.get("to"), event.getVehicle().getVehicle().getWorld(), Target.UNKNOWN);
MCLocation loc = event.getFrom();
if (loc.getBlockX() != pLoc.getBlockX() || loc.getBlockY() != pLoc.getBlockY() || loc.getBlockZ() != pLoc.getBlockZ()) {
return false;
}
}
Prefilters.match(prefilter, "vehicletype", event.getVehicle().getType().name(), PrefilterType.MACRO);
MCEntity passenger = event.getVehicle().getPassenger();
if (passenger != null) {
Prefilters.match(prefilter, "passengertype", passenger.getType().name(), PrefilterType.MACRO);
}
return true;
}
return false;
}
@Override
public BindableEvent convert(CArray manualObject, Target t) {
MCEntity e = Static.getEntity(manualObject.get("id", Target.UNKNOWN), Target.UNKNOWN);
if (!(e instanceof MCVehicle)) {
throw new CREBadEntityException("The id was not a vehicle", Target.UNKNOWN);
}
MCLocation from = ObjectGenerator.GetGenerator().location(manualObject.get("from", Target.UNKNOWN), e.getWorld(), manualObject.getTarget());
MCLocation to = ObjectGenerator.GetGenerator().location(manualObject.get("to", Target.UNKNOWN), e.getWorld(), manualObject.getTarget());
return EventBuilder.instantiate(MCVehicleMoveEvent.class, e, from, to);
}
@Override
public Map<String, Construct> evaluate(BindableEvent event) throws EventException {
if (event instanceof MCVehicleMoveEvent) {
MCVehicleMoveEvent e = (MCVehicleMoveEvent) event;
Target t = Target.UNKNOWN;
Map<String, Construct> ret = new HashMap<>();
ret.put("world", new CString(e.getFrom().getWorld().getName(), t));
ret.put("from", ObjectGenerator.GetGenerator().location(e.getFrom()));
ret.put("to", ObjectGenerator.GetGenerator().location(e.getTo()));
ret.put("vehicletype", new CString(e.getVehicle().getType().name(), t));
ret.put("id", new CString(e.getVehicle().getUniqueId().toString(), t));
MCEntity passenger = e.getVehicle().getPassenger();
if (passenger == null) {
ret.put("passenger", CNull.NULL);
ret.put("passengertype", CNull.NULL);
ret.put("player", CNull.NULL);
} else {
MCEntityType passengertype = e.getVehicle().getPassenger().getType();
ret.put("passengertype", new CString(passengertype.name(), t));
ret.put("passenger", new CString(passenger.getUniqueId().toString(), t));
if (passengertype.getAbstracted() == MCEntityType.MCVanillaEntityType.PLAYER) {
ret.put("player", new CString(((MCPlayer) e.getVehicle().getPassenger()).getName(), t));
} else {
ret.put("player", CNull.NULL);
}
}
return ret;
} else {
throw new EventException("Could not convert to MCVehicleMoveEvent");
}
}
@Override
public Driver driver() {
return Driver.VEHICLE_MOVE;
}
@Override
public boolean modifyEvent(String key, Construct value, BindableEvent event) {
//Nothing can be modified, so always return false
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
public static class vehicle_destroy extends AbstractEvent {
@Override
public String getName() {
return "vehicle_destroy";
}
@Override
public String docs() {
return "{vehicletype: <macro> the entitytype of the vehicle} "
+ "Fires when a vehicle is destroyed."
+ " {vehicletype | vehicle: entityID | passenger: entityID"
+ " | damager: If the source of damage is a player this will contain their name, otherwise it will be"
+ " the entityID of the damager (only available when an entity causes damage)"
+ " | shooter: The name of the player who shot, otherwise the entityID"
+ " (only available when damager is a projectile)}"
+ " {}"
+ " {}";
}
@Override
public boolean matches(Map<String, Construct> prefilter, BindableEvent event) throws PrefilterNonMatchException {
if (event instanceof MCVehicleDestroyEvent) {
MCVehicleDestroyEvent e = (MCVehicleDestroyEvent) event;
Prefilters.match(prefilter, "vehicletype", e.getVehicle().getType().name(), PrefilterType.MACRO);
return true;
}
return false;
}
@Override
public BindableEvent convert(CArray manualObject, Target t) {
throw ConfigRuntimeException.CreateUncatchableException("Unsupported Operation", Target.UNKNOWN);
}
@Override
public Map<String, Construct> evaluate(BindableEvent event) throws EventException {
if (event instanceof MCVehicleDestroyEvent) {
MCVehicleDestroyEvent e = (MCVehicleDestroyEvent) event;
Target t = Target.UNKNOWN;
Map<String, Construct> ret = evaluate_helper(e);
ret.put("vehicletype", new CString(e.getVehicle().getType().name(), t));
ret.put("vehicle", new CString(e.getVehicle().getUniqueId().toString(), t));
MCEntity damager = ((MCVehicleDestroyEvent) event).getAttacker();
if (damager instanceof MCPlayer) {
ret.put("damager", new CString(((MCPlayer) damager).getName(), Target.UNKNOWN));
} else {
ret.put("damager", new CString(damager.getUniqueId().toString(), Target.UNKNOWN));
}
if (damager instanceof MCProjectile) {
MCProjectileSource shooter = ((MCProjectile) damager).getShooter();
if (shooter instanceof MCPlayer) {
ret.put("shooter", new CString(((MCPlayer) shooter).getName(), Target.UNKNOWN));
} else if (shooter instanceof MCEntity) {
ret.put("shooter", new CString(((MCEntity) shooter).getUniqueId().toString(), Target.UNKNOWN));
} else if (shooter instanceof MCBlockProjectileSource) {
ret.put("shooter", ObjectGenerator.GetGenerator().location(((MCBlockProjectileSource) shooter).getBlock().getLocation()));
}
}
ret.put("location", ObjectGenerator.GetGenerator().location(e.getVehicle().getLocation()));
return ret;
} else {
throw new EventException("Could not convert to MCVehicleDestroyEvent");
}
}
@Override
public Driver driver() {
return Driver.VEHICLE_DESTROY;
}
@Override
public boolean modifyEvent(String key, Construct value, BindableEvent event) {
return false;
}
@Override
public Version since() {
return CHVersion.V3_3_2;
}
}
}