Factory Integration, NPC improvements. #19 & #31.

This commit is contained in:
simon 2017-12-24 14:26:19 -05:00
parent 4f1342593f
commit 2e9248ea2e
17 changed files with 334 additions and 112 deletions

View File

@ -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");

View File

@ -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<NonPlayerCharacter> 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<NonPlayerCharacter> getNpcs() {
return npcs;
}
}

View File

@ -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);
}

View File

@ -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");

View File

@ -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<Node> 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;
}
}

View File

@ -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");
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -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");

View File

@ -15,8 +15,6 @@ public class WorldUtils {
*/
public static ArrayList<BiomassBlob> 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<BiomassBlob> biomassBlobs = new ArrayList<>(maxCount);

View File

@ -7,7 +7,7 @@ import java.util.ArrayList;
public interface ControllableUnit {
int getObjectId();
long getObjectId();
void setKeyboardBuffer(ArrayList<Integer> kbBuffer);

View File

@ -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;
}

View File

@ -241,7 +241,7 @@ public class GameUniverse implements JSONSerialisable {
}
public int getNextObjectId() {
public long getNextObjectId() {
return ++nextObjectId;
}

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.