mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2025-02-16 20:41:59 +01:00
Migrate portal, entity and worldpurger to new wm
This commit is contained in:
parent
0a62081d03
commit
32fb25854f
@ -7,21 +7,15 @@
|
|||||||
|
|
||||||
package com.onarandombox.MultiverseCore.listeners;
|
package com.onarandombox.MultiverseCore.listeners;
|
||||||
|
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
|
||||||
import com.onarandombox.MultiverseCore.api.MVWorldManager;
|
|
||||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
|
||||||
import com.onarandombox.MultiverseCore.api.WorldPurger;
|
|
||||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
|
||||||
import com.onarandombox.MultiverseCore.inject.InjectableListener;
|
import com.onarandombox.MultiverseCore.inject.InjectableListener;
|
||||||
|
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
|
||||||
|
import com.onarandombox.MultiverseCore.worldnew.WorldPurger;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||||
import org.bukkit.event.entity.EntityPortalEvent;
|
|
||||||
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||||
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
|
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
|
||||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||||
@ -33,17 +27,14 @@ import org.jvnet.hk2.annotations.Service;
|
|||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class MVEntityListener implements InjectableListener {
|
public class MVEntityListener implements InjectableListener {
|
||||||
private final MVCoreConfig config;
|
private final WorldManager worldManager;
|
||||||
private final MVWorldManager worldManager;
|
|
||||||
private final WorldPurger worldPurger;
|
private final WorldPurger worldPurger;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MVEntityListener(
|
public MVEntityListener(
|
||||||
@NotNull MVCoreConfig config,
|
@NotNull WorldManager worldManager,
|
||||||
@NotNull MVWorldManager worldManager,
|
|
||||||
@NotNull WorldPurger worldPurger
|
@NotNull WorldPurger worldPurger
|
||||||
) {
|
) {
|
||||||
this.config = config;
|
|
||||||
this.worldManager = worldManager;
|
this.worldManager = worldManager;
|
||||||
this.worldPurger = worldPurger;
|
this.worldPurger = worldPurger;
|
||||||
}
|
}
|
||||||
@ -57,16 +48,15 @@ public class MVEntityListener implements InjectableListener {
|
|||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.getEntity() instanceof Player) {
|
if (!(event.getEntity() instanceof Player player)) {
|
||||||
Player p = (Player) event.getEntity();
|
return;
|
||||||
MVWorld w = this.worldManager.getMVWorld(p.getWorld().getName());
|
|
||||||
if (w != null && !w.getHunger()) {
|
|
||||||
// If the world has hunger set to false, do not let the level go down
|
|
||||||
if (event.getFoodLevel() < ((Player) event.getEntity()).getFoodLevel()) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
worldManager.getMVWorld(player.getWorld())
|
||||||
|
.peek((world) -> {
|
||||||
|
if (!world.getHunger() && event.getFoodLevel() < player.getFoodLevel()) {
|
||||||
|
event.setCancelled(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,14 +65,15 @@ public class MVEntityListener implements InjectableListener {
|
|||||||
*/
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void entityRegainHealth(EntityRegainHealthEvent event) {
|
public void entityRegainHealth(EntityRegainHealthEvent event) {
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled() || event.getRegainReason() != RegainReason.REGEN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RegainReason reason = event.getRegainReason();
|
worldManager.getMVWorld(event.getEntity().getWorld())
|
||||||
MVWorld world = this.worldManager.getMVWorld(event.getEntity().getLocation().getWorld());
|
.peek((world) -> {
|
||||||
if (world != null && reason == RegainReason.REGEN && !world.getAutoHeal()) {
|
if (!world.getAutoHeal()) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,45 +82,21 @@ public class MVEntityListener implements InjectableListener {
|
|||||||
*/
|
*/
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void creatureSpawn(CreatureSpawnEvent event) {
|
public void creatureSpawn(CreatureSpawnEvent event) {
|
||||||
|
if (event.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check to see if the Creature is spawned by a plugin, we don't want to prevent this behaviour.
|
// Check to see if the Creature is spawned by a plugin, we don't want to prevent this behaviour.
|
||||||
// TODO: Allow the egg thing to be a config param. Doubt this will be per world; seems silly.
|
// TODO: Allow the egg thing to be a config param. Doubt this will be per world; seems silly.
|
||||||
if (event.getSpawnReason() == SpawnReason.CUSTOM || event.getSpawnReason() == SpawnReason.SPAWNER_EGG
|
if (event.getSpawnReason() == SpawnReason.CUSTOM
|
||||||
|
|| event.getSpawnReason() == SpawnReason.SPAWNER_EGG
|
||||||
|| event.getSpawnReason() == SpawnReason.BREEDING) {
|
|| event.getSpawnReason() == SpawnReason.BREEDING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
World world = event.getEntity().getWorld();
|
worldManager.getMVWorld(event.getEntity().getWorld())
|
||||||
if (event.isCancelled())
|
.peek((world) -> {
|
||||||
return;
|
event.setCancelled(this.worldPurger.shouldWeKillThisCreature(world, event.getEntity()));
|
||||||
|
});
|
||||||
// Check if it's a world which we are meant to be managing.
|
|
||||||
if (!(this.worldManager.isMVWorld(world.getName())))
|
|
||||||
return;
|
|
||||||
|
|
||||||
EntityType type = event.getEntityType();
|
|
||||||
/**
|
|
||||||
* Handle people with non-standard animals: ie a patched craftbukkit.
|
|
||||||
*/
|
|
||||||
if (type == null || type.getName() == null) {
|
|
||||||
Logging.finer("Found a null typed creature.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MVWorld mvworld = this.worldManager.getMVWorld(world.getName());
|
|
||||||
event.setCancelled(this.worldPurger.shouldWeKillThisCreature(mvworld, event.getEntity()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles portal search radius adjustment.
|
|
||||||
* @param event The Event that was fired.
|
|
||||||
*/
|
|
||||||
@EventHandler
|
|
||||||
public void entityPortal(EntityPortalEvent event) {
|
|
||||||
if (event.isCancelled() || event.getTo() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!config.isUsingCustomPortalSearch()) {
|
|
||||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,17 +8,20 @@
|
|||||||
package com.onarandombox.MultiverseCore.listeners;
|
package com.onarandombox.MultiverseCore.listeners;
|
||||||
|
|
||||||
import com.dumptruckman.minecraft.util.Logging;
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||||
import com.onarandombox.MultiverseCore.api.MVWorldManager;
|
|
||||||
import com.onarandombox.MultiverseCore.inject.InjectableListener;
|
import com.onarandombox.MultiverseCore.inject.InjectableListener;
|
||||||
|
import com.onarandombox.MultiverseCore.worldnew.MVWorld;
|
||||||
|
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.PortalType;
|
import org.bukkit.PortalType;
|
||||||
import org.bukkit.block.BlockState;
|
import org.bukkit.block.BlockState;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.block.Action;
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.entity.EntityPortalEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.event.world.PortalCreateEvent;
|
import org.bukkit.event.world.PortalCreateEvent;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jvnet.hk2.annotations.Service;
|
import org.jvnet.hk2.annotations.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,10 +30,12 @@ import org.jvnet.hk2.annotations.Service;
|
|||||||
@Service
|
@Service
|
||||||
public class MVPortalListener implements InjectableListener {
|
public class MVPortalListener implements InjectableListener {
|
||||||
|
|
||||||
private MVWorldManager worldManager;
|
private final MVCoreConfig config;
|
||||||
|
private final WorldManager worldManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MVPortalListener(MVWorldManager worldManager) {
|
public MVPortalListener(@NotNull MVCoreConfig config, @NotNull WorldManager worldManager) {
|
||||||
|
this.config = config;
|
||||||
this.worldManager = worldManager;
|
this.worldManager = worldManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +47,7 @@ public class MVPortalListener implements InjectableListener {
|
|||||||
public void portalForm(PortalCreateEvent event) {
|
public void portalForm(PortalCreateEvent event) {
|
||||||
Logging.fine("Attempting to create portal at '%s' with reason: %s", event.getWorld().getName(), event.getReason());
|
Logging.fine("Attempting to create portal at '%s' with reason: %s", event.getWorld().getName(), event.getReason());
|
||||||
|
|
||||||
MVWorld world = this.worldManager.getMVWorld(event.getWorld());
|
MVWorld world = this.worldManager.getMVWorld(event.getWorld()).getOrNull();
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PortalCreateEvent.", event.getWorld().getName());
|
Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PortalCreateEvent.", event.getWorld().getName());
|
||||||
return;
|
return;
|
||||||
@ -75,7 +80,7 @@ public class MVPortalListener implements InjectableListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!world.getAllowedPortals().isPortalAllowed(targetType)) {
|
if (!world.getPortalForm().isPortalAllowed(targetType)) {
|
||||||
Logging.fine("Cancelling creation of %s portal because portalForm disallows.", targetType);
|
Logging.fine("Cancelling creation of %s portal because portalForm disallows.", targetType);
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
@ -98,16 +103,30 @@ public class MVPortalListener implements InjectableListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MVWorld world = this.worldManager.getMVWorld(event.getPlayer().getWorld());
|
MVWorld world = this.worldManager.getMVWorld(event.getPlayer().getWorld()).getOrNull();
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PlayerInteractEvent.",
|
Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PlayerInteractEvent.",
|
||||||
event.getPlayer().getWorld().getName());
|
event.getPlayer().getWorld().getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!world.getAllowedPortals().isPortalAllowed(PortalType.ENDER)) {
|
if (!world.getPortalForm().isPortalAllowed(PortalType.ENDER)) {
|
||||||
Logging.fine("Cancelling creation of ENDER portal because portalForm disallows.");
|
Logging.fine("Cancelling creation of ENDER portal because portalForm disallows.");
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles portal search radius adjustment.
|
||||||
|
* @param event The Event that was fired.
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
public void entityPortal(EntityPortalEvent event) {
|
||||||
|
if (event.isCancelled() || event.getTo() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!config.isUsingCustomPortalSearch()) {
|
||||||
|
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,162 @@
|
|||||||
|
package com.onarandombox.MultiverseCore.worldnew;
|
||||||
|
|
||||||
|
import com.dumptruckman.minecraft.util.Logging;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Animals;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Ghast;
|
||||||
|
import org.bukkit.entity.Golem;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.Monster;
|
||||||
|
import org.bukkit.entity.Phantom;
|
||||||
|
import org.bukkit.entity.Projectile;
|
||||||
|
import org.bukkit.entity.Slime;
|
||||||
|
import org.bukkit.entity.Squid;
|
||||||
|
import org.jvnet.hk2.annotations.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
// TODO: This entire class is a mess.
|
||||||
|
@Service
|
||||||
|
public class WorldPurger {
|
||||||
|
|
||||||
|
private Class<Entity> ambientClass = null;
|
||||||
|
|
||||||
|
public WorldPurger() {
|
||||||
|
try {
|
||||||
|
Class entityClass = Class.forName("org.bukkit.entity.Ambient");
|
||||||
|
if (Entity.class.isAssignableFrom(entityClass)) {
|
||||||
|
ambientClass = entityClass;
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException ignore) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purgeWorlds(List<MVWorld> worlds) {
|
||||||
|
if (worlds == null || worlds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (MVWorld world : worlds) {
|
||||||
|
this.purgeWorld(world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purgeWorld(MVWorld world) {
|
||||||
|
if (world == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<String> allMobs = new ArrayList<String>(world.getSpawningAnimalsExceptions());
|
||||||
|
allMobs.addAll(world.getSpawningMonstersExceptions());
|
||||||
|
purgeWorld(world, allMobs, !world.getSpawningAnimals(), !world.getSpawningMonsters());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldWeKillThisCreature(MVWorld world, Entity e) {
|
||||||
|
ArrayList<String> allMobs = new ArrayList<String>(world.getSpawningAnimalsExceptions());
|
||||||
|
allMobs.addAll(world.getSpawningMonstersExceptions());
|
||||||
|
return this.shouldWeKillThisCreature(e, allMobs, !world.getSpawningAnimals(), !world.getSpawningMonsters());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purgeWorld(MVWorld mvworld, List<String> thingsToKill,
|
||||||
|
boolean negateAnimals, boolean negateMonsters, CommandSender sender) {
|
||||||
|
if (mvworld == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
World world = mvworld.getBukkitWorld().getOrNull();
|
||||||
|
if (world == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int projectilesKilled = 0;
|
||||||
|
int entitiesKilled = 0;
|
||||||
|
boolean specifiedAll = thingsToKill.contains("ALL");
|
||||||
|
boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll;
|
||||||
|
boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll;
|
||||||
|
List<Entity> worldEntities = world.getEntities();
|
||||||
|
List<LivingEntity> livingEntities = new ArrayList<LivingEntity>(worldEntities.size());
|
||||||
|
List<Projectile> projectiles = new ArrayList<Projectile>(worldEntities.size());
|
||||||
|
for (final Entity e : worldEntities) {
|
||||||
|
if (e instanceof Projectile) {
|
||||||
|
final Projectile p = (Projectile) e;
|
||||||
|
if (p.getShooter() != null) {
|
||||||
|
projectiles.add((Projectile) e);
|
||||||
|
}
|
||||||
|
} else if (e instanceof LivingEntity) {
|
||||||
|
livingEntities.add((LivingEntity) e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final LivingEntity e : livingEntities) {
|
||||||
|
if (killDecision(e, thingsToKill, negateAnimals, negateMonsters, specifiedAnimals, specifiedMonsters)) {
|
||||||
|
final Iterator<Projectile> it = projectiles.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
final Projectile p = it.next();
|
||||||
|
if (p.getShooter().equals(e)) {
|
||||||
|
p.remove();
|
||||||
|
it.remove();
|
||||||
|
projectilesKilled++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.remove();
|
||||||
|
entitiesKilled++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sender != null) {
|
||||||
|
sender.sendMessage(entitiesKilled + " entities purged from the world '" + world.getName() + "' along with " + projectilesKilled + " projectiles that belonged to them.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean killDecision(Entity e, List<String> thingsToKill, boolean negateAnimals,
|
||||||
|
boolean negateMonsters, boolean specifiedAnimals, boolean specifiedMonsters) {
|
||||||
|
boolean negate = false;
|
||||||
|
boolean specified = false;
|
||||||
|
if (e instanceof Golem || e instanceof Squid || e instanceof Animals
|
||||||
|
|| (ambientClass != null && ambientClass.isInstance(e))) {
|
||||||
|
// it's an animal
|
||||||
|
if (specifiedAnimals && !negateAnimals) {
|
||||||
|
Logging.finest("Removing an entity because I was told to remove all animals in world %s: %s", e.getWorld().getName(), e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (specifiedAnimals)
|
||||||
|
specified = true;
|
||||||
|
negate = negateAnimals;
|
||||||
|
} else if (e instanceof Monster || e instanceof Ghast || e instanceof Slime || e instanceof Phantom) {
|
||||||
|
// it's a monster
|
||||||
|
if (specifiedMonsters && !negateMonsters) {
|
||||||
|
Logging.finest("Removing an entity because I was told to remove all monsters in world %s: %s", e.getWorld().getName(), e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (specifiedMonsters)
|
||||||
|
specified = true;
|
||||||
|
negate = negateMonsters;
|
||||||
|
}
|
||||||
|
for (String s : thingsToKill) {
|
||||||
|
EntityType type = EntityType.fromName(s);
|
||||||
|
if (type != null && type.equals(e.getType())) {
|
||||||
|
specified = true;
|
||||||
|
if (!negate) {
|
||||||
|
Logging.finest("Removing an entity because it WAS specified and we are NOT negating in world %s: %s", e.getWorld().getName(), e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!specified && negate) {
|
||||||
|
Logging.finest("Removing an entity because it was NOT specified and we ARE negating in world %s: %s", e.getWorld().getName(), e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldWeKillThisCreature(Entity e, List<String> thingsToKill, boolean negateAnimals, boolean negateMonsters) {
|
||||||
|
boolean specifiedAll = thingsToKill.contains("ALL");
|
||||||
|
boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll;
|
||||||
|
boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll;
|
||||||
|
return this.killDecision(e, thingsToKill, negateAnimals, negateMonsters, specifiedAnimals, specifiedMonsters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void purgeWorld(MVWorld mvworld, List<String> thingsToKill, boolean negateAnimals, boolean negateMonsters) {
|
||||||
|
purgeWorld(mvworld, thingsToKill, negateAnimals, negateMonsters, null);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user