Merge AIController and Navigator

This commit is contained in:
fullwall 2012-02-04 17:58:51 +08:00
parent 01fa072112
commit ca3790fde1
7 changed files with 242 additions and 45 deletions

View File

@ -1,8 +1,8 @@
package net.citizensnpcs; package net.citizensnpcs;
import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPC;
import net.citizensnpcs.npc.CitizensNPC;
import net.citizensnpcs.npc.CitizensNPCManager; import net.citizensnpcs.npc.CitizensNPCManager;
import net.citizensnpcs.npc.ai.CitizensNavigator;
public class NPCUpdater implements Runnable { public class NPCUpdater implements Runnable {
private final CitizensNPCManager npcManager; private final CitizensNPCManager npcManager;
@ -14,6 +14,6 @@ public class NPCUpdater implements Runnable {
@Override @Override
public void run() { public void run() {
for (NPC npc : npcManager) for (NPC npc : npcManager)
((CitizensNavigator) npc.getNavigator()).update(); ((CitizensNPC) npc).update();
} }
} }

View File

@ -3,26 +3,27 @@ package net.citizensnpcs.npc;
import net.citizensnpcs.api.event.NPCDespawnEvent; import net.citizensnpcs.api.event.NPCDespawnEvent;
import net.citizensnpcs.api.event.NPCSpawnEvent; import net.citizensnpcs.api.event.NPCSpawnEvent;
import net.citizensnpcs.api.npc.AbstractNPC; import net.citizensnpcs.api.npc.AbstractNPC;
import net.citizensnpcs.api.npc.ai.Navigator;
import net.citizensnpcs.api.npc.trait.trait.SpawnLocation; import net.citizensnpcs.api.npc.trait.trait.SpawnLocation;
import net.citizensnpcs.api.npc.trait.trait.Spawned; import net.citizensnpcs.api.npc.trait.trait.Spawned;
import net.citizensnpcs.npc.ai.CitizensNavigator; import net.citizensnpcs.npc.ai.CitizensAI;
import net.citizensnpcs.util.Messaging; import net.citizensnpcs.util.Messaging;
import net.minecraft.server.EntityLiving;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity;
public abstract class CitizensNPC extends AbstractNPC { public abstract class CitizensNPC extends AbstractNPC {
protected final CitizensNPCManager manager; protected final CitizensNPCManager manager;
protected net.minecraft.server.Entity mcEntity; protected final CitizensAI ai = new CitizensAI(this);
protected EntityLiving mcEntity;
protected CitizensNPC(CitizensNPCManager manager, int id, String name) { protected CitizensNPC(CitizensNPCManager manager, int id, String name) {
super(id, name); super(id, name);
this.manager = manager; this.manager = manager;
} }
protected abstract net.minecraft.server.Entity createHandle(Location loc); protected abstract EntityLiving createHandle(Location loc);
@Override @Override
public boolean despawn() { public boolean despawn() {
@ -40,17 +41,17 @@ public abstract class CitizensNPC extends AbstractNPC {
} }
@Override @Override
public Entity getBukkitEntity() { public LivingEntity getBukkitEntity() {
return getHandle().getBukkitEntity(); return (LivingEntity) getHandle().getBukkitEntity();
} }
public net.minecraft.server.Entity getHandle() { public EntityLiving getHandle() {
return mcEntity; return mcEntity;
} }
@Override @Override
public Navigator getNavigator() { public CitizensAI getAI() {
return new CitizensNavigator(this); return new CitizensAI(this);
} }
@Override @Override
@ -86,4 +87,8 @@ public abstract class CitizensNPC extends AbstractNPC {
addTrait(new Spawned(true)); addTrait(new Spawned(true));
return true; return true;
} }
public void update() {
ai.update();
}
} }

View File

@ -1,43 +1,28 @@
package net.citizensnpcs.npc.ai; package net.citizensnpcs.npc.ai;
import net.citizensnpcs.api.npc.ai.Navigator; import net.citizensnpcs.api.npc.ai.AI;
import net.citizensnpcs.api.npc.ai.NavigatorCallback; import net.citizensnpcs.api.npc.ai.Goal;
import net.citizensnpcs.api.npc.ai.NavigationCallback;
import net.citizensnpcs.npc.CitizensNPC; import net.citizensnpcs.npc.CitizensNPC;
import net.citizensnpcs.trait.LookClose; import net.citizensnpcs.util.Messaging;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
public class CitizensNavigator implements Navigator { public class CitizensAI implements AI {
private final CitizensNPC npc;
private PathStrategy executing; private PathStrategy executing;
private Runnable ai;
private final CitizensNPC npc;
public CitizensNavigator(CitizensNPC npc) { public CitizensAI(CitizensNPC npc) {
this.npc = npc; this.npc = npc;
} }
@Override @Override
public void registerCallback(NavigatorCallback callback) { public void addGoal(int priority, Goal goal) {
} // TODO Auto-generated method stub
public void update() {
if (npc.getHandle() != null && npc.getHandle().world.findNearbyPlayer(npc.getHandle(), 5) != null)
if (npc.getTrait(LookClose.class).shouldLookClose()
&& npc.getHandle().world.findNearbyPlayer(npc.getHandle(), 5) != null)
faceEntity(npc.getHandle().world.findNearbyPlayer(npc.getHandle(), 5).getBukkitEntity());
if (executing != null)
executing.update();
}
@Override
public void setDestination(Location destination) {
executing = new MoveStrategy(npc, destination);
}
@Override
public void setTarget(LivingEntity target, boolean aggressive) {
executing = new TargetStrategy(npc, target, aggressive);
} }
private void faceEntity(Entity target) { private void faceEntity(Entity target) {
@ -61,4 +46,38 @@ public class CitizensNavigator implements Navigator {
npc.getHandle().yaw = (float) yaw - 90; npc.getHandle().yaw = (float) yaw - 90;
npc.getHandle().pitch = (float) pitch; npc.getHandle().pitch = (float) pitch;
} }
@Override
public void registerNavigationCallback(NavigationCallback callback) {
}
@Override
public void setAI(Runnable ai) {
this.ai = ai;
}
@Override
public void setDestination(Location destination) {
executing = new MoveStrategy(npc, destination);
}
@Override
public void setTarget(LivingEntity target, boolean aggressive) {
executing = new TargetStrategy(npc, target, aggressive);
}
public void update() {
if (executing != null && executing.update()) {
executing = null;
}
if (ai != null) {
try {
ai.run();
} catch (Throwable ex) {
Messaging.log("Unexpected error while running ai " + ai);
ex.printStackTrace();
}
}
}
} }

View File

@ -24,13 +24,27 @@ public class TargetStrategy implements PathStrategy {
if (target == null || target.dead) if (target == null || target.dead)
return true; return true;
current = new MoveStrategy(handle, handle.world.findPath(handle, target, 16F)); current = new MoveStrategy(handle, handle.world.findPath(handle, target, 16F));
if (aggro) if (aggro && canAttack()) {
if (handle instanceof EntityMonster) if (handle instanceof EntityMonster) {
((EntityMonster) handle).d(target); ((EntityMonster) handle).d(target);
else if (handle instanceof EntityHuman) } else if (handle instanceof EntityHuman) {
((EntityHuman) handle).attack(target); ((EntityHuman) handle).attack(target);
}
}
current.update(); current.update();
return false; return false;
} }
private boolean canAttack() {
return handle.attackTicks == 0
&& (handle.boundingBox.e > target.boundingBox.b && handle.boundingBox.b < target.boundingBox.e)
&& distanceSquared() <= ATTACK_DISTANCE && handle.g(target);
}
private static final double ATTACK_DISTANCE = 1.75 * 1.75;
private double distanceSquared() {
return handle.getBukkitEntity().getLocation().distanceSquared(target.getBukkitEntity().getLocation());
}
} }

View File

@ -5,12 +5,9 @@ import net.citizensnpcs.npc.CitizensNPCManager;
import net.citizensnpcs.resource.lib.EntityHumanNPC; import net.citizensnpcs.resource.lib.EntityHumanNPC;
import net.minecraft.server.EntityLiving; import net.minecraft.server.EntityLiving;
import net.minecraft.server.ItemInWorldManager; import net.minecraft.server.ItemInWorldManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.WorldServer; import net.minecraft.server.WorldServer;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -24,8 +21,13 @@ public class CitizensHumanNPC extends CitizensNPC {
return (Player) getHandle().getBukkitEntity(); return (Player) getHandle().getBukkitEntity();
} }
protected static MinecraftServer getMinecraftServer(Server server) { @Override
return ((CraftServer) server).getServer(); public void update() {
super.update();
if (mcEntity.noDamageTicks > 0)
mcEntity.noDamageTicks--;
if (mcEntity.attackTicks > 0)
mcEntity.attackTicks--;
} }
@Override @Override

View File

@ -0,0 +1,83 @@
package net.citizensnpcs.npc.entity;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Map;
import net.citizensnpcs.npc.CitizensNPC;
import net.citizensnpcs.npc.CitizensNPCManager;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.EntityTypes;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.LivingEntity;
public abstract class CitizensMobNPC extends CitizensNPC {
private final Constructor<? extends EntityLiving> constructor;
protected CitizensMobNPC(CitizensNPCManager manager, int id, String name, Class<? extends EntityLiving> clazz) {
super(manager, id, name);
try {
this.constructor = clazz.getConstructor(World.class);
} catch (Exception ex) {
throw new IllegalStateException("unable to find an entity constructor");
}
if (!classToInt.containsKey(clazz))
registerEntityClass(clazz);
}
private EntityLiving createEntityFromClass(net.minecraft.server.World world) {
try {
return constructor.newInstance(world);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
protected EntityLiving createHandle(Location loc) {
EntityLiving entity = createEntityFromClass(((CraftWorld) loc.getWorld()).getHandle());
mcEntity.setPositionRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
return entity;
}
@Override
public LivingEntity getBukkitEntity() {
return (LivingEntity) getHandle().getBukkitEntity();
}
private static Map<Class<? extends Entity>, Integer> classToInt;
private static Map<Integer, Class<? extends Entity>> intToClass;
private static void registerEntityClass(Class<? extends Entity> clazz) {
Class<?> search = clazz;
while ((search = search.getSuperclass()) != null && Entity.class.isAssignableFrom(search)) {
if (!classToInt.containsKey(search))
continue;
int code = classToInt.get(search);
intToClass.put(code, clazz);
classToInt.put(clazz, code);
return;
}
throw new IllegalArgumentException("unable to find valid entity superclass");
}
static {
try {
Field field = EntityTypes.class.getDeclaredField("d");
field.setAccessible(true);
intToClass = (Map<Integer, Class<? extends Entity>>) field.get(null);
field = EntityTypes.class.getDeclaredField("e");
field.setAccessible(true);
classToInt = (Map<Class<? extends Entity>, Integer>) field.get(null);
} catch (Exception ex) {
ex.printStackTrace();
throw new IllegalStateException(
"Unable to fetch entity class mapping - is Citizens updated for this version of CraftBukkit?");
}
}
}

View File

@ -0,0 +1,74 @@
package net.citizensnpcs.resource.lib.entity;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import net.citizensnpcs.resource.lib.NPCNetHandler;
import net.citizensnpcs.resource.lib.NPCNetworkManager;
import net.citizensnpcs.resource.lib.NPCSocket;
import net.citizensnpcs.util.Messaging;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ItemInWorldManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.NetHandler;
import net.minecraft.server.NetworkManager;
import net.minecraft.server.World;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
@SuppressWarnings("unchecked")
public class EntityHumanNPC extends EntityPlayer {
public EntityHumanNPC(MinecraftServer minecraftServer, World world, String string,
ItemInWorldManager itemInWorldManager) {
super(minecraftServer, world, string, itemInWorldManager);
itemInWorldManager.setGameMode(0);
NPCSocket socket = new NPCSocket();
NetworkManager netMgr = new NPCNetworkManager(socket, "npc mgr", new NetHandler() {
@Override
public boolean c() {
return false;
}
});
netServerHandler = new NPCNetHandler(minecraftServer, netMgr, this);
netMgr.a(netServerHandler);
try {
socket.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public CraftPlayer getBukkitEntity() {
if (bukkitEntity == null) {
super.getBukkitEntity();
removeFromPlayerMap(name);
// Bukkit uses a map of player names to CraftPlayer instances to
// solve a reconnect issue, so NPC names will conflict with ordinary
// player names. Workaround.
}
return super.getBukkitEntity();
}
public void removeFromPlayerMap(String name) {
if (players != null)
players.remove(name);
}
private static Map<String, CraftPlayer> players;
static {
try {
Field f = CraftEntity.class.getDeclaredField("players");
f.setAccessible(true);
players = (Map<String, CraftPlayer>) f.get(null);
} catch (Exception ex) {
Messaging.log("Unable to fetch player map from CraftEntity: " + ex.getMessage());
}
}
}