/*
* This file is part of Skript.
*
* Skript 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.
*
* Skript 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 Skript. If not, see <http://www.gnu.org/licenses/>.
*
*
* Copyright 2011-2014 Peter Güttinger
*
*/
package ch.njol.skript.events;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.Event;
import org.bukkit.event.EventException;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.plugin.EventExecutor;
import org.eclipse.jdt.annotation.Nullable;
import ch.njol.skript.Skript;
import ch.njol.skript.SkriptConfig;
import ch.njol.skript.SkriptEventHandler;
import ch.njol.skript.aliases.ItemData;
import ch.njol.skript.aliases.ItemType;
import ch.njol.skript.lang.Literal;
import ch.njol.skript.lang.SelfRegisteringSkriptEvent;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.Trigger;
import ch.njol.skript.registrations.Classes;
/**
* @author Peter Güttinger
*/
@SuppressWarnings("deprecation")
public class EvtMoveOn extends SelfRegisteringSkriptEvent { // TODO on jump
// private final static class BlockLocation {
// final World world;
// final int x, y, z;
//
// BlockLocation(final World world, final int x, final int y, final int z) {
// this.world = world;
// this.x = x;
// this.y = y;
// this.z = z;
// }
//
// @Override
// public boolean equals(final Object obj) {
// if (obj == this)
// return true;
// if (!(obj instanceof BlockLocation))
// return false;
// final BlockLocation other = (BlockLocation) obj;
// return world == other.world && x == other.x && y == other.y && z == other.z;
// }
//
// @Override
// public int hashCode() {
// return world.hashCode() + 29 * (x + 17 * (y + 31 * z));
// }
// }
static {
// Skript.registerEvent(EvtMoveOn.class, PlayerMoveEvent.class, "(step|walk) on <.+>");
Skript.registerEvent("Move On", EvtMoveOn.class, PlayerMoveEvent.class, "(step|walk)[ing] (on|over) %*itemtypes%")
.description("Called when a player moves onto a certain type of block. Please note that using this event can cause lag if there are many players online.")
.examples("on walking on dirt or grass", "on stepping on stone")
.since("2.0");
}
// private final static HashMap<BlockLocation, List<Trigger>> blockTriggers = new HashMap<BlockLocation, List<Trigger>>();
final static HashMap<Integer, List<Trigger>> itemTypeTriggers = new HashMap<Integer, List<Trigger>>();
@SuppressWarnings("null")
ItemType[] types = null;
// private World world;
// private int x, y, z;
private static boolean registeredExecutor = false;
private final static EventExecutor executor = new EventExecutor() {
@SuppressWarnings("null")
@Override
public void execute(final @Nullable Listener l, final @Nullable Event event) throws EventException {
if (event == null)
return;
final PlayerMoveEvent e = (PlayerMoveEvent) event;
final Location from = e.getFrom(), to = e.getTo();
// if (!blockTriggers.isEmpty()) {
// final List<Trigger> ts = blockTriggers.get(new BlockLocation(to.getWorld(), to.getBlockX(), to.getBlockY(), to.getBlockZ()));
// if (ts != null) {
// for (final Trigger t : ts) {
// SkriptEventHandler.logTriggerStart(t);
// t.start(e);
// SkriptEventHandler.logTriggerEnd(t);
// }
// }
// }
if (!itemTypeTriggers.isEmpty()) {
final int id = getOnBlock(to);
if (id == 0)
return;
final List<Trigger> ts = itemTypeTriggers.get(id);
if (ts == null)
return;
final int y = getBlockY(to.getY(), id);
if (to.getWorld().equals(from.getWorld()) && to.getBlockX() == from.getBlockX() && to.getBlockZ() == from.getBlockZ() && y == getBlockY(from.getY(), getOnBlock(from)) && getOnBlock(from) == id)
return;
SkriptEventHandler.logEventStart(e);
final byte data = to.getWorld().getBlockAt(to.getBlockX(), y, to.getBlockZ()).getData();
triggersLoop: for (final Trigger t : ts) {
final EvtMoveOn se = (EvtMoveOn) t.getEvent();
for (final ItemType i : se.types) {
if (i.isOfType(id, data)) {
SkriptEventHandler.logTriggerStart(t);
t.execute(e);
SkriptEventHandler.logTriggerEnd(t);
continue triggersLoop;
}
}
}
SkriptEventHandler.logEventEnd();
}
}
};
final static int getOnBlock(final Location l) {
int id = l.getWorld().getBlockTypeIdAt(l.getBlockX(), (int) Math.ceil(l.getY()) - 1, l.getBlockZ());
if (id == 0 && Math.abs((l.getY() - l.getBlockY()) - 0.5) < Skript.EPSILON) { // fences
id = l.getWorld().getBlockTypeIdAt(l.getBlockX(), l.getBlockY() - 1, l.getBlockZ());
if (id != Material.FENCE.getId() && id != 107 && id != 113) // fence gate // nether fence
return 0;
}
return id;
}
final static int getBlockY(final double y, final int id) {
if ((id == Material.FENCE.getId() || id == 107 || id == 113) && Math.abs((y - Math.floor(y)) - 0.5) < Skript.EPSILON) // fence gate // nether fence
return (int) Math.floor(y) - 1;
return (int) Math.ceil(y) - 1;
}
@SuppressWarnings("null")
public final static Block getBlock(final PlayerMoveEvent e) {
return e.getTo().subtract(0, 0.5, 0).getBlock();
}
@Override
public boolean init(final Literal<?>[] args, final int matchedPattern, final ParseResult parser) {
// if (parser.regexes.get(0).group().equalsIgnoreCase("<block>")/* && isValidatingInput*/)
// return true;
// final Matcher m = Pattern.compile("<block:(.+)>").matcher(parser.regexes.get(0).group());
// if (m.matches()) {
// final Block b = (Block) Skript.deserialize("block", m.group(1));
// if (b == null)
// return false;
// world = b.getWorld();
// x = b.getX();
// y = b.getY();
// z = b.getZ();
// } else {
@SuppressWarnings("unchecked")
final Literal<? extends ItemType> l = (Literal<? extends ItemType>) args[0];//SkriptParser.parseLiteral(parser.regexes.get(0).group(), ItemType.class, ParseContext.EVENT);
if (l == null)
return false;
types = l.getAll();
for (final ItemType t : types) {
boolean hasBlock = false;
for (final ItemData d : t) {
if (d.getId() == -1) {
Skript.error("Can't use an 'on walk' event with an alias that matches all blocks");
return false;
}
if (d.getId() <= Skript.MAXBLOCKID && d.getId() != 0) // don't allow air
hasBlock = true;
}
if (!hasBlock) {
Skript.error(t + " is not a block and can thus not be walked on");
return false;
}
}
// }
return true;
}
@Override
public String toString(final @Nullable Event e, final boolean debug) {
return "walk on " + Classes.toString(types, false);
// return "walk on " + (types != null ? Skript.toString(types, false) : "<block:" + world.getName() + ":" + x + "," + y + "," + z + ">");
}
@Override
public void register(final Trigger trigger) {
// if (types == null) {
// final BlockLocation l = new BlockLocation(world, x, y, z);
// List<Trigger> ts = blockTriggers.get(l);
// if (ts == null)
// blockTriggers.put(l, ts = new ArrayList<Trigger>());
// ts.add(trigger);
// } else {
for (final ItemType t : types) {
for (final ItemData d : t) {
if (d.getId() > Skript.MAXBLOCKID)
continue;
List<Trigger> ts = itemTypeTriggers.get(d.getId());
if (ts == null)
itemTypeTriggers.put(d.getId(), ts = new ArrayList<Trigger>());
ts.add(trigger);
}
}
// }
if (!registeredExecutor) {
Bukkit.getPluginManager().registerEvent(PlayerMoveEvent.class, new Listener() {}, SkriptConfig.defaultEventPriority.value(), executor, Skript.getInstance(), true);
registeredExecutor = true;
}
}
@Override
public void unregister(final Trigger t) {
// final Iterator<Entry<BlockLocation, List<Trigger>>> i = blockTriggers.entrySet().iterator();
// while (i.hasNext()) {
// final List<Trigger> ts = i.next().getValue();
// ts.remove(t);
// if (ts.isEmpty())
// i.remove();
// }
final Iterator<Entry<Integer, List<Trigger>>> i2 = itemTypeTriggers.entrySet().iterator();
while (i2.hasNext()) {
final List<Trigger> ts = i2.next().getValue();
ts.remove(t);
if (ts.isEmpty())
i2.remove();
}
}
@Override
public void unregisterAll() {
// blockTriggers.clear();
itemTypeTriggers.clear();
}
}