diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java index f70433c..f794c1b 100644 --- a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java @@ -98,7 +98,7 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit { public static Cubot deserialize(JSONObject json) { Cubot cubot = new Cubot(); - cubot.setObjectId((int) (long) json.get("i")); + cubot.setObjectId((long) json.get("i")); cubot.setX((int) (long) json.get("x")); cubot.setY((int) (long) json.get("y")); cubot.hp = (int) (long) json.get("hp"); diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java index 5e5dedd..85deb75 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java @@ -1,14 +1,40 @@ package net.simon987.npcplugin; +import net.simon987.server.GameServer; import net.simon987.server.game.GameObject; import net.simon987.server.game.Updatable; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import java.awt.*; +import java.util.ArrayList; + public class Factory extends GameObject implements Updatable { private static final int MAP_INFO = 0x0200; + static final int ID = 3; - public static final int ID = 3; + private static final int MAX_NPC_COUNT = GameServer.INSTANCE.getConfig().getInt("factory_max_npc_count"); + + private static final int NPC_CREATION_COOLDOWN = NonPlayerCharacter.LIFETIME / MAX_NPC_COUNT; + + private ArrayList npcs = new ArrayList<>(); + + /** + * Number of ticks to wait until the Factory can spawn a new NPC + */ + private int cooldown = 0; + + /** + * Temporary NPC objectId array. The Factory links the NPCs to itself when initialised, + * at the first call of update(). + */ + private Object[] tmpNpcArray = new Object[0]; + + /** + * Factory are uninitialised until the first update() call + */ + private boolean initialised = false; @Override public char getMapInfo() { @@ -17,7 +43,45 @@ public class Factory extends GameObject implements Updatable { @Override public void update() { - System.out.println("Updating Factory..."); + + if (!initialised) { + + initialised = true; + + for (Object id : tmpNpcArray) { + + NonPlayerCharacter npc = (NonPlayerCharacter) GameServer.INSTANCE.getGameUniverse().getObject((int) (long) id); + npc.setFactory(this); + + npcs.add(npc); + } + + } else { + + if (cooldown == 0) { + if (npcs.size() < MAX_NPC_COUNT) { + Point p = getAdjacentTile(); + + if (p != null) { + NonPlayerCharacter npc = new HarvesterNPC(); + npc.setWorld(getWorld()); + npc.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId()); + npc.setX(p.x); + npc.setY(p.y); + getWorld().getGameObjects().add(npc); + getWorld().incUpdatable(); + npc.setFactory(this); + + npcs.add(npc); + } + } + + cooldown += NPC_CREATION_COOLDOWN; + + } else { + cooldown--; + } + } } @Override @@ -42,16 +106,79 @@ public class Factory extends GameObject implements Updatable { json.put("y", getY()); json.put("t", ID); + JSONArray tmpNpcArray = new JSONArray(); + + for (NonPlayerCharacter npc : npcs) { + tmpNpcArray.add(npc.getObjectId()); + } + + json.put("n", tmpNpcArray); + return json; } public static Factory deserialise(JSONObject json) { Factory factory = new Factory(); - factory.setObjectId((int) (long) json.get("i")); + factory.setObjectId((long) json.get("i")); factory.setX((int) (long) json.get("x")); factory.setY((int) (long) json.get("y")); + factory.tmpNpcArray = (Object[]) ((JSONArray) json.get("n")).toArray(); + return factory; } + + /** + * Get the first non-blocked tile that is directly adjacent to the factory, starting from the north-east corner + * going clockwise. + * + * @return The coordinates of the first non-blocked tile, null otherwise. + */ + public Point getAdjacentTile() { + + /* + * (2,0) + * (2,1) + * (1,2) + * (0,2) + * (-1,1) + * (-1,0) + * (0,-1) + * (1,-1) + */ + + if (!getWorld().isTileBlocked(getX() + 2, getY())) { + return new Point(getX() + 2, getY()); + + } else if (!getWorld().isTileBlocked(getX() + 2, getY() + 1)) { + return new Point(getX() + 2, getY() + 1); + + } else if (!getWorld().isTileBlocked(getX() + 1, getY() + 2)) { + return new Point(getX() + 1, getY() + 2); + + } else if (!getWorld().isTileBlocked(getX(), getY() + 2)) { + return new Point(getX(), getY() + 2); + + } else if (!getWorld().isTileBlocked(getX() + -1, getY() + 1)) { + return new Point(getX() + -1, getY() + 1); + + } else if (!getWorld().isTileBlocked(getX() + -1, getY())) { + return new Point(getX() + -1, getY()); + + } else if (!getWorld().isTileBlocked(getX(), getY() + -1)) { + return new Point(getX(), getY() + -1); + + } else if (!getWorld().isTileBlocked(getX() + 1, getY() + -1)) { + return new Point(getX() + 1, getY() + -1); + + } else { + return null; + } + + } + + ArrayList getNpcs() { + return npcs; + } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvestTask.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvestTask.java index 00cee21..5c12479 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvestTask.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvestTask.java @@ -84,11 +84,14 @@ public class HarvestTask extends NPCTask { if (nextWorldDirection == null) { - nextWorldDirection = Direction.getDirection(random.nextInt(3)); + while (!npc.gotoWorld(nextWorldDirection)) { + nextWorldDirection = Direction.getDirection(random.nextInt(4)); + } pause += 6; + } else { + npc.gotoWorld(nextWorldDirection); } - npc.gotoWorld(nextWorldDirection); } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java index 89931b4..cccef1f 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java @@ -17,19 +17,22 @@ public class HarvesterNPC extends NonPlayerCharacter { @Override public void update() { - if (hp <= 0) { - setDead(true); - //TODO: drop biomass + super.update(); - } + if (getFactory() != null) { + if (getTask().checkCompleted()) { + setTask(new HarvestTask()); - if (getTask().checkCompleted()) { + } else { + getTask().tick(this); + } - setTask(new HarvestTask()); - - } else { - getTask().tick(this); + //Self-destroy when age limit is reached + if (getAge() >= NonPlayerCharacter.LIFETIME) { + setDead(true); + getFactory().getNpcs().remove(this); + } } } @@ -52,7 +55,7 @@ public class HarvesterNPC extends NonPlayerCharacter { public static HarvesterNPC deserialize(JSONObject json) { HarvesterNPC npc = new HarvesterNPC(); - npc.setObjectId((int) (long) json.get("i")); + npc.setObjectId((long) json.get("i")); npc.setX((int) (long) json.get("x")); npc.setY((int) (long) json.get("y")); npc.hp = (int) (long) json.get("hp"); diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java index 1cd5f64..e833f49 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java @@ -1,5 +1,7 @@ package net.simon987.npcplugin; +import net.simon987.server.GameServer; +import net.simon987.server.assembly.Util; import net.simon987.server.game.Action; import net.simon987.server.game.Direction; import net.simon987.server.game.GameObject; @@ -14,22 +16,65 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable private static final int MAP_INFO = 0x0040; - protected int hp; + private static final int MAX_FACTORY_DISTANCE = GameServer.INSTANCE.getConfig().getInt("npc_max_factory_distance"); - protected int energy; - protected int maxEnergy; + public static final int LIFETIME = GameServer.INSTANCE.getConfig().getInt("npc_lifetime"); + //Unused + int hp; + int energy; + int maxEnergy; + + /** + * Current task + */ private NPCTask task; private Action lastAction = Action.IDLE; + /** + * Factory that created this NPC + */ + private Factory factory; + + /** + * If set to true, the NPC will be destroyed next tick if it is + * not linked to a Factory + */ + private boolean selfDestroyNextTick = false; + + /** + * Age of the npc, in ticks + */ + private int age = 0; + @Override public char getMapInfo() { return MAP_INFO; } + @Override + public void update() { - public boolean moveTo(int x, int y, int range) { + age++; + + //Destroy NPCs that are not linked with a Factory + if (factory == null) { + if (selfDestroyNextTick) { + setDead(true); + } + + selfDestroyNextTick = true; + } + } + + /** + * Attempt to move the NPC to the specified coordinates + * + * @param range distance to the desired coordinates, in tiles + * @return true if the path is passable + */ + boolean moveTo(int x, int y, int range) { ArrayList path = Pathfinder.findPath(getWorld(), getX(), getY(), x, y, range); @@ -55,33 +100,62 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable return false; } - public boolean gotoWorld(Direction direction) { + /** + * Go to the next World in the specified Direction. + * + * @return true if the World in the specified Direction is within the max. distance from the Factory + */ + boolean gotoWorld(Direction direction) { if (direction == Direction.NORTH) { - if (!moveTo(8, 0, 0)) { - setDirection(Direction.NORTH); - return incrementLocation(); + + if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + getWorld().getX(), getWorld().getY() - 1) <= MAX_FACTORY_DISTANCE) { + if (!moveTo(8, 0, 0)) { + setDirection(Direction.NORTH); + incrementLocation(); + } + return true; + } else { + return false; } + } else if (direction == Direction.EAST) { - if (!moveTo(15, 8, 0)) { - setDirection(Direction.EAST); - return incrementLocation(); + if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + getWorld().getX() + 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) { + if (!moveTo(15, 7, 0)) { + setDirection(Direction.EAST); + incrementLocation(); + } + return true; + } else { + return false; } } else if (direction == Direction.SOUTH) { - if (!moveTo(7, 15, 0)) { - setDirection(Direction.SOUTH); - return incrementLocation(); + if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + getWorld().getX(), getWorld().getY() + 1) <= MAX_FACTORY_DISTANCE) { + if (!moveTo(8, 15, 0)) { + setDirection(Direction.SOUTH); + incrementLocation(); + } + return true; + } else { + return false; } } else if (direction == Direction.WEST) { - if (!moveTo(0, 7, 0)) { - setDirection(Direction.WEST); - return incrementLocation(); + if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + getWorld().getX() - 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) { + if (!moveTo(0, 7, 0)) { + setDirection(Direction.WEST); + incrementLocation(); + } + return true; + } else { + return false; } } else { return false; } - - return true; } public NPCTask getTask() { @@ -95,4 +169,16 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable public Action getAction() { return lastAction; } + + public Factory getFactory() { + return factory; + } + + public void setFactory(Factory factory) { + this.factory = factory; + } + + public int getAge() { + return age; + } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java index 2ee01fc..36e7da6 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java @@ -1,6 +1,6 @@ package net.simon987.npcplugin; -import net.simon987.npcplugin.event.WorldUpdateListener; +import net.simon987.npcplugin.event.WorldCreationListener; import net.simon987.server.ServerConfiguration; import net.simon987.server.game.GameObject; import net.simon987.server.io.GameObjectDeserializer; @@ -14,7 +14,7 @@ public class NpcPlugin extends ServerPlugin implements GameObjectDeserializer { @Override public void init(ServerConfiguration configuration) { - listeners.add(new WorldUpdateListener()); + listeners.add(new WorldCreationListener()); LogManager.LOGGER.info("Initialised NPC plugin"); } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java new file mode 100644 index 0000000..6601bbf --- /dev/null +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java @@ -0,0 +1,64 @@ +package net.simon987.npcplugin.event; + +import net.simon987.npcplugin.Factory; +import net.simon987.server.GameServer; +import net.simon987.server.event.GameEvent; +import net.simon987.server.event.GameEventListener; +import net.simon987.server.event.WorldGenerationEvent; +import net.simon987.server.game.World; +import net.simon987.server.logging.LogManager; + +import java.util.Random; + +public class WorldCreationListener implements GameEventListener { + + /** + * Spawn rate. Higher = rarer: A factory will be spawn about every FACTORY_SPAWN_RATE generated Worlds + */ + private static final int FACTORY_SPAWN_RATE = 35; + + private Random random = new Random(); + + @Override + public Class getListenedEventType() { + return WorldGenerationEvent.class; + } + + @Override + public void handle(GameEvent event) { + + if (random.nextInt(FACTORY_SPAWN_RATE) == 0) { + + World world = ((WorldGenerationEvent) event).getWorld(); + + for (int x = 2; x < 12; x++) { + for (int y = 2; y < 12; y++) { + + if ((!world.isTileBlocked(x, y) && !world.isTileBlocked(x + 1, y) && + !world.isTileBlocked(x, y + 1) && !world.isTileBlocked(x + 1, y + 1))) { + + Factory factory = new Factory(); + + factory.setWorld(world); + factory.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId()); + factory.setX(x); + factory.setY(y); + + if (factory.getAdjacentTile() == null) { + //Factory has no non-blocked adjacent tiles + continue; + } + + world.getGameObjects().add(factory); + world.incUpdatable(); + + LogManager.LOGGER.info("Spawned Factory at (" + world.getX() + ", " + world.getY() + + ") (" + x + ", " + y + ")"); + + return; + } + } + } + } + } +} diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldUpdateListener.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldUpdateListener.java deleted file mode 100644 index e99b0c8..0000000 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldUpdateListener.java +++ /dev/null @@ -1,67 +0,0 @@ -package net.simon987.npcplugin.event; - -import net.simon987.npcplugin.Factory; -import net.simon987.npcplugin.HarvesterNPC; -import net.simon987.npcplugin.NonPlayerCharacter; -import net.simon987.server.GameServer; -import net.simon987.server.event.GameEvent; -import net.simon987.server.event.GameEventListener; -import net.simon987.server.event.WorldUpdateEvent; -import net.simon987.server.game.World; -import net.simon987.server.logging.LogManager; - -import java.awt.*; - -public class WorldUpdateListener implements GameEventListener { - - private boolean ok = true; - - @Override - public Class getListenedEventType() { - return WorldUpdateEvent.class; - } - - @Override - public void handle(GameEvent event) { - - //LogManager.LOGGER.info("Time is " + ); - - World world = ((WorldUpdateEvent) event).getWorld(); - - if (GameServer.INSTANCE.getGameUniverse().getTime() % 10 == 0) { - - if (ok) { - ok = false; - LogManager.LOGGER.info("Spawning Harvester + Factory\n--------------------------------------"); - - NonPlayerCharacter npc = new HarvesterNPC(); - - Point p = world.getRandomPassableTile(); - - if (p != null) { - npc.setWorld(world); - npc.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId()); - npc.setX(p.x); - npc.setY(p.y); - world.getGameObjects().add(npc); - world.incUpdatable(); - } - - //Factory test - Factory factory = new Factory(); - - factory.setWorld(world); - factory.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId()); - factory.setX(7); - factory.setY(11); - world.getGameObjects().add(factory); - world.incUpdatable(); - - } - - - } - } - - -} diff --git a/Plugin Plant/src/main/java/net/simon987/biomassplugin/BiomassBlob.java b/Plugin Plant/src/main/java/net/simon987/biomassplugin/BiomassBlob.java index 80fa1b4..dba3c04 100644 --- a/Plugin Plant/src/main/java/net/simon987/biomassplugin/BiomassBlob.java +++ b/Plugin Plant/src/main/java/net/simon987/biomassplugin/BiomassBlob.java @@ -61,7 +61,7 @@ public class BiomassBlob extends GameObject implements InventoryHolder { BiomassBlob biomassBlob = new BiomassBlob(); - biomassBlob.setObjectId((int) (long) json.get("i")); + biomassBlob.setObjectId((long) json.get("i")); biomassBlob.setX((int) (long) json.get("x")); biomassBlob.setY((int) (long) json.get("y")); // biomassBlob.style = (int) (long) json.get("style"); diff --git a/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java b/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java index f0217f9..fda2516 100644 --- a/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java +++ b/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java @@ -15,8 +15,6 @@ public class WorldUtils { */ public static ArrayList generateBlobs(World world, int minCount, int maxCount, int yield) { - System.out.println("Generating blobs..."); - Random random = new Random(); int blobCount = random.nextInt(maxCount - minCount) + minCount; ArrayList biomassBlobs = new ArrayList<>(maxCount); diff --git a/Server/src/main/java/net/simon987/server/game/ControllableUnit.java b/Server/src/main/java/net/simon987/server/game/ControllableUnit.java index 296cc65..51a97f1 100644 --- a/Server/src/main/java/net/simon987/server/game/ControllableUnit.java +++ b/Server/src/main/java/net/simon987/server/game/ControllableUnit.java @@ -7,7 +7,7 @@ import java.util.ArrayList; public interface ControllableUnit { - int getObjectId(); + long getObjectId(); void setKeyboardBuffer(ArrayList kbBuffer); diff --git a/Server/src/main/java/net/simon987/server/game/GameObject.java b/Server/src/main/java/net/simon987/server/game/GameObject.java index c64bcd1..20679dc 100755 --- a/Server/src/main/java/net/simon987/server/game/GameObject.java +++ b/Server/src/main/java/net/simon987/server/game/GameObject.java @@ -18,7 +18,7 @@ public abstract class GameObject implements JSONSerialisable { /** * Object's unique identifier */ - private int objectId; + private long objectId; /** * X coordinate of the object in its World @@ -178,11 +178,11 @@ public abstract class GameObject implements JSONSerialisable { } - public int getObjectId() { + public long getObjectId() { return objectId; } - public void setObjectId(int objectId) { + public void setObjectId(long objectId) { this.objectId = objectId; } diff --git a/Server/src/main/java/net/simon987/server/game/GameUniverse.java b/Server/src/main/java/net/simon987/server/game/GameUniverse.java index 4c850e0..77cb620 100644 --- a/Server/src/main/java/net/simon987/server/game/GameUniverse.java +++ b/Server/src/main/java/net/simon987/server/game/GameUniverse.java @@ -241,7 +241,7 @@ public class GameUniverse implements JSONSerialisable { } - public int getNextObjectId() { + public long getNextObjectId() { return ++nextObjectId; } diff --git a/config.properties b/config.properties index 7b0cf1b..87bc1c8 100644 --- a/config.properties +++ b/config.properties @@ -50,6 +50,12 @@ battery_max_energy=60000 biomassRespawnTime=64 # Respawn timer will start when biomass count is below this number biomassRespawnThreshold=1 +# NPC lifetime in ticks +npc_lifetime=1024 +# Maximum travel distance from the Factory in Worlds +npc_max_factory_distance=3 +# Maximum NPC per Factory +factory_max_npc_count=16 # ---------------------------------------------- # Minimum center point count for the WorldGenerator wg_centerPointCountMin=5 @@ -65,6 +71,8 @@ wg_maxIronCount=2 wg_minCopperCount=0 # Maximum copper tile count for the WorldGenerator wg_maxCopperCount=2 + + # ---------------------------------------------- # Maximum execution time of user code in ms user_timeout=100 \ No newline at end of file diff --git a/plugins/Cubot.jar b/plugins/Cubot.jar index a030401..6865b39 100644 Binary files a/plugins/Cubot.jar and b/plugins/Cubot.jar differ diff --git a/plugins/Plant.jar b/plugins/Plant.jar index 4c76786..7c885cb 100644 Binary files a/plugins/Plant.jar and b/plugins/Plant.jar differ diff --git a/plugins/Plugin NPC.jar b/plugins/Plugin NPC.jar index e74e8f4..d3be2a9 100644 Binary files a/plugins/Plugin NPC.jar and b/plugins/Plugin NPC.jar differ