diff --git a/src/toniarts/openkeeper/common/RoomInstance.java b/src/toniarts/openkeeper/common/RoomInstance.java index caa54f31..81454e73 100644 --- a/src/toniarts/openkeeper/common/RoomInstance.java +++ b/src/toniarts/openkeeper/common/RoomInstance.java @@ -32,6 +32,8 @@ public class RoomInstance extends EntityInstance { private final Thing.Room.Direction direction; private short ownerId; private boolean destroyed = false; + private int health; + private int maxHealth; public RoomInstance(Room room) { this(room, null); @@ -83,4 +85,20 @@ public boolean isDestroyed() { return destroyed; } + public int getHealth() { + return health; + } + + public void setHealth(int health) { + this.health = health; + } + + public int getMaxHealth() { + return maxHealth; + } + + public void setMaxHealth(int maxHealth) { + this.maxHealth = maxHealth; + } + } diff --git a/src/toniarts/openkeeper/game/component/RoomComponent.java b/src/toniarts/openkeeper/game/component/RoomComponent.java index 4ce4f45c..c06df824 100644 --- a/src/toniarts/openkeeper/game/component/RoomComponent.java +++ b/src/toniarts/openkeeper/game/component/RoomComponent.java @@ -1,44 +1,52 @@ -/* - * Copyright (C) 2014-2021 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.component; - -import com.simsilica.es.EntityComponent; - -/** - * A base room component. This entity is a room - * - * @author Toni Helenius - */ -public class RoomComponent implements EntityComponent { - - public short roomId; - public boolean destroyed = false; - - public RoomComponent() { - // For serialization - } - - public RoomComponent(short roomId) { - this.roomId = roomId; - } - - public RoomComponent(short roomId, boolean destroyed) { - this.roomId = roomId; - this.destroyed = destroyed; - } - -} +/* + * Copyright (C) 2014-2021 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.component; + +import com.simsilica.es.EntityComponent; +import java.awt.Point; + +/** + * A base room component. This entity is a room + * + * @author Toni Helenius + */ +public class RoomComponent implements EntityComponent { + + public short roomId; + public boolean destroyed = false; + + /** + * Room center, for convenience + */ + public Point location; + + public RoomComponent() { + // For serialization + } + + public RoomComponent(RoomComponent roomComponent) { + roomId = roomComponent.roomId; + destroyed = roomComponent.destroyed; + location = roomComponent.location; + } + + public RoomComponent(short roomId, boolean destroyed, Point location) { + this.roomId = roomId; + this.destroyed = destroyed; + } + +} diff --git a/src/toniarts/openkeeper/game/controller/MapController.java b/src/toniarts/openkeeper/game/controller/MapController.java index c411c1f1..3e633b0a 100644 --- a/src/toniarts/openkeeper/game/controller/MapController.java +++ b/src/toniarts/openkeeper/game/controller/MapController.java @@ -34,6 +34,7 @@ import toniarts.openkeeper.game.controller.room.AbstractRoomController; import toniarts.openkeeper.game.controller.room.AbstractRoomController.ObjectType; import toniarts.openkeeper.game.controller.room.IRoomController; +import toniarts.openkeeper.game.data.Keeper; import toniarts.openkeeper.game.listener.MapListener; import toniarts.openkeeper.game.listener.RoomListener; import toniarts.openkeeper.game.map.IMapData; @@ -116,8 +117,19 @@ private void loadRoom(Point p) { // Find it RoomInstance roomInstance = new RoomInstance(kwdFile.getRoomByTerrain(mapTile.getTerrainId())); + Keeper owner = levelInfo.getPlayer(mapTile.getOwnerId()); + roomInstance.setDestroyed(roomInstance.getRoom() == kwdFile.getDungeonHeart() && (owner == null || owner.isDestroyed())); roomInstance.setOwnerId(mapTile.getOwnerId()); findRoom(p, roomInstance); + int health = 0; + int maxHealth = 0; + for (Point coordinate : roomInstance.getCoordinates()) { + IMapTileController roomTile = mapData.getTile(coordinate); + health += roomTile.getHealth(); + maxHealth += roomTile.getMaxHealth(); + } + roomInstance.setHealth(health); + roomInstance.setMaxHealth(maxHealth); // Create a controller for it IRoomController roomController = RoomControllerFactory.constructRoom(entityData, kwdFile, roomInstance, objectsController, gameSettings, gameTimer); @@ -360,7 +372,7 @@ public void removeRoomInstances(RoomInstance... instances) { // Signal the room IRoomController roomController = getRoomController(instance); - roomController.destroy(); + roomController.remove(); roomControllers.remove(instance); for (Point p : instance.getCoordinates()) { diff --git a/src/toniarts/openkeeper/game/controller/RoomControllerFactory.java b/src/toniarts/openkeeper/game/controller/RoomControllerFactory.java index ae1e6039..9e68172c 100644 --- a/src/toniarts/openkeeper/game/controller/RoomControllerFactory.java +++ b/src/toniarts/openkeeper/game/controller/RoomControllerFactory.java @@ -1,139 +1,141 @@ -/* - * Copyright (C) 2014-2015 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.controller; - -import com.simsilica.es.EntityData; -import com.simsilica.es.EntityId; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.Map; -import toniarts.openkeeper.common.RoomInstance; -import toniarts.openkeeper.game.component.Owner; -import toniarts.openkeeper.game.component.RoomComponent; -import toniarts.openkeeper.game.controller.room.CasinoController; -import toniarts.openkeeper.game.controller.room.CombatPitController; -import toniarts.openkeeper.game.controller.room.DoubleQuadController; -import toniarts.openkeeper.game.controller.room.FiveByFiveRotatedController; -import toniarts.openkeeper.game.controller.room.HatcheryController; -import toniarts.openkeeper.game.controller.room.HeroGateFrontEndController; -import toniarts.openkeeper.game.controller.room.IRoomController; -import toniarts.openkeeper.game.controller.room.LairController; -import toniarts.openkeeper.game.controller.room.LibraryController; -import toniarts.openkeeper.game.controller.room.NormalRoomController; -import toniarts.openkeeper.game.controller.room.PrisonController; -import toniarts.openkeeper.game.controller.room.TempleController; -import toniarts.openkeeper.game.controller.room.ThreeByThreeController; -import toniarts.openkeeper.game.controller.room.TortureChamberController; -import toniarts.openkeeper.game.controller.room.TrainingRoomController; -import toniarts.openkeeper.game.controller.room.TreasuryController; -import toniarts.openkeeper.game.controller.room.WorkshopController; -import toniarts.openkeeper.tools.convert.map.KwdFile; -import toniarts.openkeeper.tools.convert.map.Variable; -import toniarts.openkeeper.tools.convert.map.Variable.MiscVariable; -import toniarts.openkeeper.tools.convert.map.Variable.MiscVariable.MiscType; - -/** - * A factory class you can use to build buildings - * - * @author ArchDemon - */ -public final class RoomControllerFactory { - - private static final Logger logger = System.getLogger(RoomControllerFactory.class.getName()); - - private RoomControllerFactory() { - // Nope - } - - public static IRoomController constructRoom(EntityData entityData, KwdFile kwdFile, RoomInstance roomInstance, IObjectsController objectsController, Map gameSettings, IGameTimer gameTimer) { - String roomName = roomInstance.getRoom().getName(); - EntityId entity = entityData.createEntity(); - - setRoomComponents(entity, entityData, roomInstance); - - switch (roomInstance.getRoom().getTileConstruction()) { - case _3_BY_3 -> { - return new ThreeByThreeController(entity, entityData, kwdFile, roomInstance, objectsController); - } - case HERO_GATE, HERO_GATE_FRONT_END -> { - return new HeroGateFrontEndController(entity, entityData, kwdFile, roomInstance, objectsController); - } - case HERO_GATE_2_BY_2 -> { - return new NormalRoomController(entity, entityData, kwdFile, roomInstance, objectsController); - } - case _5_BY_5_ROTATED -> { - return new FiveByFiveRotatedController(entity, entityData, kwdFile, roomInstance, objectsController, gameSettings, gameTimer); - } - case NORMAL -> { - return constructNormal(entity, entityData, roomName, kwdFile, roomInstance, objectsController, gameTimer, gameSettings); - } - case DOUBLE_QUAD -> { - return constructDoubleQuad(entity, entityData, roomName, kwdFile, roomInstance, objectsController, gameTimer); - } - default -> { - // TODO - logger.log(Level.WARNING, "Room {0} not exist", roomName); - return new NormalRoomController(entity, entityData, kwdFile, roomInstance, objectsController); - } - - } - } - - private static void setRoomComponents(EntityId entity, EntityData entityData, RoomInstance roomInstance) { - entityData.setComponents(entity, - new RoomComponent(roomInstance.getRoom().getRoomId(), roomInstance.isDestroyed()), - new Owner(roomInstance.getOwnerId(), roomInstance.getOwnerId())); - } - - private static IRoomController constructDoubleQuad(EntityId entity, EntityData entityData, String roomName, KwdFile kwdFile, RoomInstance roomInstance, IObjectsController objectsController, IGameTimer gameTimer) { - if (roomName.equalsIgnoreCase("Prison")) { - return new PrisonController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); - } else if (roomName.equalsIgnoreCase("Combat Pit")) { - return new CombatPitController(entity, entityData, kwdFile, roomInstance, objectsController); - } else if (roomName.equalsIgnoreCase("Temple")) { - return new TempleController(entity, entityData, kwdFile, roomInstance, objectsController); - } - - return new DoubleQuadController(entity, entityData, kwdFile, roomInstance, objectsController); - } - - private static IRoomController constructNormal(EntityId entity, EntityData entityData, String roomName, KwdFile kwdFile, RoomInstance roomInstance, IObjectsController objectsController, IGameTimer gameTimer, Map gameSettings) { - if (roomName.equalsIgnoreCase("Lair")) { - return new LairController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); - } else if (roomName.equalsIgnoreCase("Library")) { - return new LibraryController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); - } else if (roomName.equalsIgnoreCase("Training Room")) { - return new TrainingRoomController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); - } else if (roomName.equalsIgnoreCase("Work Shop")) { - return new WorkshopController(entity, entityData, kwdFile, roomInstance, objectsController); -// } else if (roomName.equalsIgnoreCase("Guard Room")) { -// return new GuardRoom(assetManager, roomInstance, objectLoader, worldState, effectManager); - } else if (roomName.equalsIgnoreCase("Casino")) { - return new CasinoController(entity, entityData, kwdFile, roomInstance, objectsController); -// } else if (roomName.equalsIgnoreCase("Graveyard")) { -// return new Graveyard(assetManager, roomInstance, objectLoader, worldState, effectManager); - } else if (roomName.equalsIgnoreCase("Torture Chamber")) { - return new TortureChamberController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); - } else if (roomName.equalsIgnoreCase("Treasury")) { - return new TreasuryController(entity, entityData, kwdFile, roomInstance, objectsController, gameSettings, gameTimer); - } else if (roomName.equalsIgnoreCase("Hatchery")) { - return new HatcheryController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); - } - - return new NormalRoomController(entity, entityData, kwdFile, roomInstance, objectsController); - } -} +/* + * Copyright (C) 2014-2015 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.controller; + +import com.simsilica.es.EntityData; +import com.simsilica.es.EntityId; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.Map; +import toniarts.openkeeper.common.RoomInstance; +import toniarts.openkeeper.game.component.Health; +import toniarts.openkeeper.game.component.Owner; +import toniarts.openkeeper.game.component.RoomComponent; +import toniarts.openkeeper.game.controller.room.CasinoController; +import toniarts.openkeeper.game.controller.room.CombatPitController; +import toniarts.openkeeper.game.controller.room.DoubleQuadController; +import toniarts.openkeeper.game.controller.room.FiveByFiveRotatedController; +import toniarts.openkeeper.game.controller.room.HatcheryController; +import toniarts.openkeeper.game.controller.room.HeroGateFrontEndController; +import toniarts.openkeeper.game.controller.room.IRoomController; +import toniarts.openkeeper.game.controller.room.LairController; +import toniarts.openkeeper.game.controller.room.LibraryController; +import toniarts.openkeeper.game.controller.room.NormalRoomController; +import toniarts.openkeeper.game.controller.room.PrisonController; +import toniarts.openkeeper.game.controller.room.TempleController; +import toniarts.openkeeper.game.controller.room.ThreeByThreeController; +import toniarts.openkeeper.game.controller.room.TortureChamberController; +import toniarts.openkeeper.game.controller.room.TrainingRoomController; +import toniarts.openkeeper.game.controller.room.TreasuryController; +import toniarts.openkeeper.game.controller.room.WorkshopController; +import toniarts.openkeeper.tools.convert.map.KwdFile; +import toniarts.openkeeper.tools.convert.map.Variable; +import toniarts.openkeeper.tools.convert.map.Variable.MiscVariable; +import toniarts.openkeeper.tools.convert.map.Variable.MiscVariable.MiscType; + +/** + * A factory class you can use to build buildings + * + * @author ArchDemon + */ +public final class RoomControllerFactory { + + private static final Logger logger = System.getLogger(RoomControllerFactory.class.getName()); + + private RoomControllerFactory() { + // Nope + } + + public static IRoomController constructRoom(EntityData entityData, KwdFile kwdFile, RoomInstance roomInstance, IObjectsController objectsController, Map gameSettings, IGameTimer gameTimer) { + String roomName = roomInstance.getRoom().getName(); + EntityId entity = entityData.createEntity(); + + setRoomComponents(entity, entityData, roomInstance); + + switch (roomInstance.getRoom().getTileConstruction()) { + case _3_BY_3 -> { + return new ThreeByThreeController(entity, entityData, kwdFile, roomInstance, objectsController); + } + case HERO_GATE, HERO_GATE_FRONT_END -> { + return new HeroGateFrontEndController(entity, entityData, kwdFile, roomInstance, objectsController); + } + case HERO_GATE_2_BY_2 -> { + return new NormalRoomController(entity, entityData, kwdFile, roomInstance, objectsController); + } + case _5_BY_5_ROTATED -> { + return new FiveByFiveRotatedController(entity, entityData, kwdFile, roomInstance, objectsController, gameSettings, gameTimer); + } + case NORMAL -> { + return constructNormal(entity, entityData, roomName, kwdFile, roomInstance, objectsController, gameTimer, gameSettings); + } + case DOUBLE_QUAD -> { + return constructDoubleQuad(entity, entityData, roomName, kwdFile, roomInstance, objectsController, gameTimer); + } + default -> { + // TODO + logger.log(Level.WARNING, "Room {0} not exist", roomName); + return new NormalRoomController(entity, entityData, kwdFile, roomInstance, objectsController); + } + + } + } + + private static void setRoomComponents(EntityId entity, EntityData entityData, RoomInstance roomInstance) { + entityData.setComponents(entity, + new RoomComponent(roomInstance.getRoom().getRoomId(), roomInstance.isDestroyed(), roomInstance.getCenter()), + new Owner(roomInstance.getOwnerId(), roomInstance.getOwnerId()), + new Health(roomInstance.getHealth(), roomInstance.getMaxHealth())); + } + + private static IRoomController constructDoubleQuad(EntityId entity, EntityData entityData, String roomName, KwdFile kwdFile, RoomInstance roomInstance, IObjectsController objectsController, IGameTimer gameTimer) { + if (roomName.equalsIgnoreCase("Prison")) { + return new PrisonController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); + } else if (roomName.equalsIgnoreCase("Combat Pit")) { + return new CombatPitController(entity, entityData, kwdFile, roomInstance, objectsController); + } else if (roomName.equalsIgnoreCase("Temple")) { + return new TempleController(entity, entityData, kwdFile, roomInstance, objectsController); + } + + return new DoubleQuadController(entity, entityData, kwdFile, roomInstance, objectsController); + } + + private static IRoomController constructNormal(EntityId entity, EntityData entityData, String roomName, KwdFile kwdFile, RoomInstance roomInstance, IObjectsController objectsController, IGameTimer gameTimer, Map gameSettings) { + if (roomName.equalsIgnoreCase("Lair")) { + return new LairController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); + } else if (roomName.equalsIgnoreCase("Library")) { + return new LibraryController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); + } else if (roomName.equalsIgnoreCase("Training Room")) { + return new TrainingRoomController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); + } else if (roomName.equalsIgnoreCase("Work Shop")) { + return new WorkshopController(entity, entityData, kwdFile, roomInstance, objectsController); +// } else if (roomName.equalsIgnoreCase("Guard Room")) { +// return new GuardRoom(assetManager, roomInstance, objectLoader, worldState, effectManager); + } else if (roomName.equalsIgnoreCase("Casino")) { + return new CasinoController(entity, entityData, kwdFile, roomInstance, objectsController); +// } else if (roomName.equalsIgnoreCase("Graveyard")) { +// return new Graveyard(assetManager, roomInstance, objectLoader, worldState, effectManager); + } else if (roomName.equalsIgnoreCase("Torture Chamber")) { + return new TortureChamberController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); + } else if (roomName.equalsIgnoreCase("Treasury")) { + return new TreasuryController(entity, entityData, kwdFile, roomInstance, objectsController, gameSettings, gameTimer); + } else if (roomName.equalsIgnoreCase("Hatchery")) { + return new HatcheryController(entity, entityData, kwdFile, roomInstance, objectsController, gameTimer); + } + + return new NormalRoomController(entity, entityData, kwdFile, roomInstance, objectsController); + } +} diff --git a/src/toniarts/openkeeper/game/controller/room/AbstractRoomController.java b/src/toniarts/openkeeper/game/controller/room/AbstractRoomController.java index 934394eb..bc7f8032 100644 --- a/src/toniarts/openkeeper/game/controller/room/AbstractRoomController.java +++ b/src/toniarts/openkeeper/game/controller/room/AbstractRoomController.java @@ -1,457 +1,498 @@ -/* - * Copyright (C) 2014-2015 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.controller.room; - -import com.simsilica.es.EntityData; -import com.simsilica.es.EntityId; -import java.awt.Point; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import toniarts.openkeeper.common.RoomInstance; -import toniarts.openkeeper.game.controller.IObjectsController; -import toniarts.openkeeper.game.controller.room.storage.IRoomObjectControl; -import toniarts.openkeeper.tools.convert.map.KwdFile; -import toniarts.openkeeper.tools.convert.map.Room; - -/** - * Base class for all rooms - * - * @author Toni Helenius - */ -public abstract class AbstractRoomController implements IRoomController { - - /** - * The type of object the room houses - */ - public enum ObjectType { - - GOLD, LAIR, SPELL_BOOK, SPECIAL, RESEARCHER, PRISONER, TORTUREE, FOOD, TRAINEE; - - }; - - /** - * How the objects are laid out, there is always a 1 tile margin from the - * sides. I don't know where this information really is, so I hard coded it. - */ - public enum RoomObjectLayout { - - /** - * Allow side-to-side, every tile - */ - ALLOW_NEIGHBOUR, - /** - * Allow a neighbouring object diagonally - */ - ALLOW_DIAGONAL_NEIGHBOUR_ONLY, - /** - * No touching! - */ - ISOLATED; - } - - protected final EntityId entityId; - protected final EntityData entityData; - protected final KwdFile kwdFile; - protected final RoomInstance roomInstance; - private ObjectType defaultObjectType; - private final Map objectControls = new HashMap<>(); - protected boolean destroyed = false; - protected boolean[][] map; - protected Point start; - protected final IObjectsController objectsController; - protected final Set floorFurniture = new HashSet<>(); - protected final Set wallFurniture = new HashSet<>(); - private final Set pillars; - - public AbstractRoomController(EntityId entityId, EntityData entityData, KwdFile kwdFile, - RoomInstance roomInstance, IObjectsController objectsController) { - this.entityId = entityId; - this.entityData = entityData; - this.kwdFile = kwdFile; - this.roomInstance = roomInstance; - this.objectsController = objectsController; - if (hasPillars()) { - pillars = new HashSet<>(); - } else { - pillars = Collections.emptySet(); - } - } - - protected void setupCoordinates() { - map = roomInstance.getCoordinatesAsMatrix(); - start = roomInstance.getMatrixStartPoint(); - } - - @Override - public void construct() { - setupCoordinates(); - - // Construct the room objects - removeObjects(); - constructObjects(); - if (hasPillars()) { - pillars.addAll(constructPillars()); - } - } - - private boolean hasPillars() { - return getPillarObject(roomInstance.getEntity().getRoomId()) != null; - } - - /** - * Construct room pillars. Info in: - * https://github.com/tonihele/OpenKeeper/issues/116 - * - * @return the list of pillars constructed - */ - protected List constructPillars() { - // TODO: Maybe replace with something similar than the object placement ENUM, there are only few different scenarios of constructing the pillars - return Collections.emptyList(); - } - - /** - * Construct room objects - */ - protected void constructObjects() { - - // Floor objects 0-2 - Room room = roomInstance.getRoom(); - int index = -1; - if (room.getObjects().get(0) > 0 || room.getObjects().get(1) > 0 || room.getObjects().get(2) > 0) { - - // Object map - boolean[][] objectMap = new boolean[map.length][map[0].length]; - - for (int x = 0; x < map.length; x++) { - for (int y = 0; y < map[x].length; y++) { - - // Skip non-room tiles - if (!map[x][y]) { - continue; - } - - // See neighbouring tiles - boolean N = hasSameTile(map, x, y - 1); - boolean NE = hasSameTile(map, x + 1, y - 1); - boolean E = hasSameTile(map, x + 1, y); - boolean SE = hasSameTile(map, x + 1, y + 1); - boolean S = hasSameTile(map, x, y + 1); - boolean SW = hasSameTile(map, x - 1, y + 1); - boolean W = hasSameTile(map, x - 1, y); - boolean NW = hasSameTile(map, x - 1, y - 1); - - if (N && NE && E && SE && S && SW && W && NW) { - - // Building options - N = hasSameTile(objectMap, x, y - 1); - NE = hasSameTile(objectMap, x + 1, y - 1); - E = hasSameTile(objectMap, x + 1, y); - SE = hasSameTile(objectMap, x + 1, y + 1); - S = hasSameTile(objectMap, x, y + 1); - SW = hasSameTile(objectMap, x - 1, y + 1); - W = hasSameTile(objectMap, x - 1, y); - NW = hasSameTile(objectMap, x - 1, y - 1); - if (getRoomObjectLayout() == RoomObjectLayout.ALLOW_DIAGONAL_NEIGHBOUR_ONLY - && (N || E || S || W)) { - continue; - } - if (getRoomObjectLayout() == RoomObjectLayout.ISOLATED - && (N || E || S || W || NE || SE || SW || NW)) { - continue; - } - do { - if (index > 1) { - index = -1; - } - index++; - } while (room.getObjects().get(index) == 0); - - // Add object - objectMap[x][y] = true; - EntityId object = objectsController.loadObject(room.getObjects().get(index), (short) 0, start.x + x, start.y + y); - floorFurniture.add(object); - } - } - } - } - - // Wall objects 3-5 - if (room.getObjects().get(3) > 0 || room.getObjects().get(4) > 0 || room.getObjects().get(5) > 0) { - - } - } - - protected static boolean hasSameTile(boolean[][] map, int x, int y) { - - // Check for out of bounds - if (x < 0 || x >= map.length || y < 0 || y >= map[x].length) { - return false; - } - return map[x][y]; - } - - protected RoomObjectLayout getRoomObjectLayout() { - return RoomObjectLayout.ALLOW_NEIGHBOUR; - } - - public boolean isTileAccessible(Integer fromX, Integer fromY, int toX, int toY) { - return true; - } - - @Override - public final boolean isTileAccessible(Point from, Point to) { - return isTileAccessible(from != null ? from.x : null, (from != null ? from.y : null), to.x, to.y); - } - - protected final void addObjectControl(IRoomObjectControl control) { - objectControls.put(control.getObjectType(), control); - if (defaultObjectType == null) { - defaultObjectType = control.getObjectType(); - } - } - - @Override - public boolean hasObjectControl(ObjectType objectType) { - return objectControls.containsKey(objectType); - } - - @Override - public T getObjectControl(ObjectType objectType) { - return (T) objectControls.get(objectType); - } - - /** - * Destroy the room, marks the room as destroyed and releases all the - * controls. The room should not be used after this. - */ - @Override - public void destroy() { - destroyed = true; - roomInstance.setDestroyed(destroyed); - - // Destroy the controls - for (IRoomObjectControl control : objectControls.values()) { - control.destroy(); - } - - // Remove objects - removeObjects(); - - // Remove us - entityData.removeEntity(entityId); - } - - private void removeObjects() { - - // Clear the old ones - // TODO: recycle? - for (EntityId entityId : pillars) { - entityData.removeEntity(entityId); - } - pillars.clear(); - for (EntityId entityId : floorFurniture) { - entityData.removeEntity(entityId); - } - floorFurniture.clear(); - for (EntityId entityId : wallFurniture) { - entityData.removeEntity(entityId); - } - wallFurniture.clear(); - } - - @Override - public boolean isDestroyed() { - return destroyed; - } - - /** - * Can store gold to the room? - * - * @return can store gold - */ - @Override - public boolean canStoreGold() { - return hasObjectControl(ObjectType.GOLD); - } - - /** - * Get room max capacity - * - * @return room max capacity - */ - protected int getMaxCapacity() { - IRoomObjectControl control = getDefaultRoomObjectControl(); - if (control != null) { - return control.getMaxCapacity(); - } - return 0; - } - - /** - * Get used capacity - * - * @return the used capacity of the room - */ - protected int getUsedCapacity() { - IRoomObjectControl control = getDefaultRoomObjectControl(); - if (control != null) { - return control.getCurrentCapacity(); - } - return 0; - } - - private IRoomObjectControl getDefaultRoomObjectControl() { - if (defaultObjectType != null) { - return objectControls.get(defaultObjectType); - } - return null; - } - - @Override - public final RoomInstance getRoomInstance() { - return roomInstance; - } - - /** - * Is the room at full capacity - * - * @return max capacity used - */ - @Override - public boolean isFullCapacity() { - return getUsedCapacity() >= getMaxCapacity(); - } - - /** - * Get the room type - * - * @return the room type - */ - @Override - public Room getRoom() { - return roomInstance.getRoom(); - } - - /** - * Are we the dungeon heart? - * - * @return are we? - */ - @Override - public boolean isDungeonHeart() { - return false; - } - - @Override - public void captured(short playerId) { - - // Nothing, hmm, should we move some logic here from the MapController - roomInstance.setOwnerId(playerId); - - // Notify the controls - for (IRoomObjectControl control : objectControls.values()) { - control.captured(playerId); - } - } - - /** - * Get the total number of furniture in room - * - * @return furniture count - */ - public int getFurnitureCount() { - return wallFurniture.size() + floorFurniture.size(); - } - - /** - * Get the number of floor furniture in a room - * - * @return floor furniture count - */ - @Override - public int getFloorFurnitureCount() { - return floorFurniture.size(); - } - - /** - * Get the number of wall furniture in a room - * - * @return wall furniture count - */ - @Override - public int getWallFurnitureCount() { - return wallFurniture.size(); - } - - @Override - public Set getFloorFurniture() { - return floorFurniture; - } - - @Override - public Set getWallFurniture() { - return wallFurniture; - } - - /** - * Get the object ID for the room pillars - * - * @param roomId the room ID - * @return the object ID for the room pillar or {@code null} if not found - */ - protected static Short getPillarObject(short roomId) { - - // FIXME: Is this data available somewhere?? - switch (roomId) { - case 1: // Treasury - return 76; - case 2: // Lair - return 77; - case 4: // Hatchery - return 78; - case 10: // Workshop - return 80; - case 11: // Prison - //return 81; // Model exists, but not used by the game - return null; - case 12: // Torture - return 82; - case 13: // Temple - //return 83; // This is the actual model, but place candle sticks instead... - return 111; - case 14: // Graveyard - return 84; - case 15: // Casino - return 85; - case 16: // Pit - return 79; - case 26: // Crypt - return 141; - default: - return null; // No pillars - } - } - - @Override - public EntityId getEntityId() { - return entityId; - } -} +/* + * Copyright (C) 2014-2015 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.controller.room; + +import com.simsilica.es.EntityData; +import com.simsilica.es.EntityId; +import java.awt.Point; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import toniarts.openkeeper.common.RoomInstance; +import toniarts.openkeeper.game.component.Health; +import toniarts.openkeeper.game.component.Owner; +import toniarts.openkeeper.game.component.RoomComponent; +import toniarts.openkeeper.game.controller.IObjectsController; +import toniarts.openkeeper.game.controller.room.storage.IRoomObjectControl; +import toniarts.openkeeper.tools.convert.map.KwdFile; +import toniarts.openkeeper.tools.convert.map.Room; + +/** + * Base class for all rooms + * + * @author Toni Helenius + */ +public abstract class AbstractRoomController implements IRoomController { + + /** + * The type of object the room houses + */ + public enum ObjectType { + + GOLD, LAIR, SPELL_BOOK, SPECIAL, RESEARCHER, PRISONER, TORTUREE, FOOD, TRAINEE; + + }; + + /** + * How the objects are laid out, there is always a 1 tile margin from the + * sides. I don't know where this information really is, so I hard coded it. + */ + public enum RoomObjectLayout { + + /** + * Allow side-to-side, every tile + */ + ALLOW_NEIGHBOUR, + /** + * Allow a neighbouring object diagonally + */ + ALLOW_DIAGONAL_NEIGHBOUR_ONLY, + /** + * No touching! + */ + ISOLATED; + } + + protected final EntityId entityId; + protected final EntityData entityData; + protected final KwdFile kwdFile; + protected final RoomInstance roomInstance; + private ObjectType defaultObjectType; + private final Map objectControls = new HashMap<>(); + protected boolean[][] map; + protected Point start; + protected final IObjectsController objectsController; + protected final Set floorFurniture = new HashSet<>(); + protected final Set wallFurniture = new HashSet<>(); + private final Set pillars; + + public AbstractRoomController(EntityId entityId, EntityData entityData, KwdFile kwdFile, + RoomInstance roomInstance, IObjectsController objectsController) { + this.entityId = entityId; + this.entityData = entityData; + this.kwdFile = kwdFile; + this.roomInstance = roomInstance; + this.objectsController = objectsController; + if (hasPillars()) { + pillars = new HashSet<>(); + } else { + pillars = Collections.emptySet(); + } + } + + protected void setupCoordinates() { + map = roomInstance.getCoordinatesAsMatrix(); + start = roomInstance.getMatrixStartPoint(); + } + + @Override + public void construct() { + setupCoordinates(); + + // Construct the room objects + removeObjects(); + constructObjects(); + if (hasPillars()) { + pillars.addAll(constructPillars()); + } + } + + private boolean hasPillars() { + return getPillarObject(roomInstance.getEntity().getRoomId()) != null; + } + + /** + * Construct room pillars. Info in: + * https://github.com/tonihele/OpenKeeper/issues/116 + * + * @return the list of pillars constructed + */ + protected List constructPillars() { + // TODO: Maybe replace with something similar than the object placement ENUM, there are only few different scenarios of constructing the pillars + return Collections.emptyList(); + } + + /** + * Construct room objects + */ + protected void constructObjects() { + + // Floor objects 0-2 + Room room = roomInstance.getRoom(); + int index = -1; + if (room.getObjects().get(0) > 0 || room.getObjects().get(1) > 0 || room.getObjects().get(2) > 0) { + + // Object map + boolean[][] objectMap = new boolean[map.length][map[0].length]; + + for (int x = 0; x < map.length; x++) { + for (int y = 0; y < map[x].length; y++) { + + // Skip non-room tiles + if (!map[x][y]) { + continue; + } + + // See neighbouring tiles + boolean N = hasSameTile(map, x, y - 1); + boolean NE = hasSameTile(map, x + 1, y - 1); + boolean E = hasSameTile(map, x + 1, y); + boolean SE = hasSameTile(map, x + 1, y + 1); + boolean S = hasSameTile(map, x, y + 1); + boolean SW = hasSameTile(map, x - 1, y + 1); + boolean W = hasSameTile(map, x - 1, y); + boolean NW = hasSameTile(map, x - 1, y - 1); + + if (N && NE && E && SE && S && SW && W && NW) { + + // Building options + N = hasSameTile(objectMap, x, y - 1); + NE = hasSameTile(objectMap, x + 1, y - 1); + E = hasSameTile(objectMap, x + 1, y); + SE = hasSameTile(objectMap, x + 1, y + 1); + S = hasSameTile(objectMap, x, y + 1); + SW = hasSameTile(objectMap, x - 1, y + 1); + W = hasSameTile(objectMap, x - 1, y); + NW = hasSameTile(objectMap, x - 1, y - 1); + if (getRoomObjectLayout() == RoomObjectLayout.ALLOW_DIAGONAL_NEIGHBOUR_ONLY + && (N || E || S || W)) { + continue; + } + if (getRoomObjectLayout() == RoomObjectLayout.ISOLATED + && (N || E || S || W || NE || SE || SW || NW)) { + continue; + } + do { + if (index > 1) { + index = -1; + } + index++; + } while (room.getObjects().get(index) == 0); + + // Add object + objectMap[x][y] = true; + EntityId object = objectsController.loadObject(room.getObjects().get(index), (short) 0, start.x + x, start.y + y); + floorFurniture.add(object); + } + } + } + } + + // Wall objects 3-5 + if (room.getObjects().get(3) > 0 || room.getObjects().get(4) > 0 || room.getObjects().get(5) > 0) { + + } + } + + protected static boolean hasSameTile(boolean[][] map, int x, int y) { + + // Check for out of bounds + if (x < 0 || x >= map.length || y < 0 || y >= map[x].length) { + return false; + } + return map[x][y]; + } + + protected RoomObjectLayout getRoomObjectLayout() { + return RoomObjectLayout.ALLOW_NEIGHBOUR; + } + + public boolean isTileAccessible(Integer fromX, Integer fromY, int toX, int toY) { + return true; + } + + @Override + public final boolean isTileAccessible(Point from, Point to) { + return isTileAccessible(from != null ? from.x : null, (from != null ? from.y : null), to.x, to.y); + } + + protected final void addObjectControl(IRoomObjectControl control) { + objectControls.put(control.getObjectType(), control); + if (defaultObjectType == null) { + defaultObjectType = control.getObjectType(); + } + } + + @Override + public boolean hasObjectControl(ObjectType objectType) { + return objectControls.containsKey(objectType); + } + + @Override + public T getObjectControl(ObjectType objectType) { + return (T) objectControls.get(objectType); + } + + @Override + public void remove() { + roomInstance.setDestroyed(true); + + // Destroy the controls + for (IRoomObjectControl control : objectControls.values()) { + control.destroy(); + } + + // Remove objects + removeObjects(); + + // Remove us + entityData.removeEntity(entityId); + } + + private void removeObjects() { + + // Clear the old ones + // TODO: recycle? + for (EntityId entity : pillars) { + entityData.removeEntity(entity); + } + pillars.clear(); + for (EntityId entity : floorFurniture) { + entityData.removeEntity(entity); + } + floorFurniture.clear(); + for (EntityId entity : wallFurniture) { + entityData.removeEntity(entity); + } + wallFurniture.clear(); + } + + @Override + public void destroy() { + RoomComponent roomComponent = new RoomComponent(getRoomComponent()); + roomComponent.destroyed = true; + entityData.setComponent(entityId, roomComponent); + } + + @Override + public boolean isDestroyed() { + return getRoomComponent().destroyed; + } + + private RoomComponent getRoomComponent() { + return entityData.getComponent(entityId, RoomComponent.class); + } + + /** + * Can store gold to the room? + * + * @return can store gold + */ + @Override + public boolean canStoreGold() { + return hasObjectControl(ObjectType.GOLD); + } + + /** + * Get room max capacity + * + * @return room max capacity + */ + protected int getMaxCapacity() { + IRoomObjectControl control = getDefaultRoomObjectControl(); + if (control != null) { + return control.getMaxCapacity(); + } + return 0; + } + + /** + * Get used capacity + * + * @return the used capacity of the room + */ + protected int getUsedCapacity() { + IRoomObjectControl control = getDefaultRoomObjectControl(); + if (control != null) { + return control.getCurrentCapacity(); + } + return 0; + } + + private IRoomObjectControl getDefaultRoomObjectControl() { + if (defaultObjectType != null) { + return objectControls.get(defaultObjectType); + } + return null; + } + + @Override + public final RoomInstance getRoomInstance() { + return roomInstance; + } + + /** + * Is the room at full capacity + * + * @return max capacity used + */ + @Override + public boolean isFullCapacity() { + return getUsedCapacity() >= getMaxCapacity(); + } + + /** + * Get the room type + * + * @return the room type + */ + @Override + public Room getRoom() { + return roomInstance.getRoom(); + } + + /** + * Are we the dungeon heart? + * + * @return are we? + */ + @Override + public boolean isDungeonHeart() { + return false; + } + + @Override + public void captured(short playerId) { + + // Nothing, hmm, should we move some logic here from the MapController + entityData.setComponent(entityId, new Owner(playerId, playerId)); + + // Notify the controls + for (IRoomObjectControl control : objectControls.values()) { + control.captured(playerId); + } + } + + /** + * Get the total number of furniture in room + * + * @return furniture count + */ + public int getFurnitureCount() { + return wallFurniture.size() + floorFurniture.size(); + } + + /** + * Get the number of floor furniture in a room + * + * @return floor furniture count + */ + @Override + public int getFloorFurnitureCount() { + return floorFurniture.size(); + } + + /** + * Get the number of wall furniture in a room + * + * @return wall furniture count + */ + @Override + public int getWallFurnitureCount() { + return wallFurniture.size(); + } + + @Override + public Set getFloorFurniture() { + return floorFurniture; + } + + @Override + public Set getWallFurniture() { + return wallFurniture; + } + + /** + * Get the object ID for the room pillars + * + * @param roomId the room ID + * @return the object ID for the room pillar or {@code null} if not found + */ + protected static Short getPillarObject(short roomId) { + + // FIXME: Is this data available somewhere?? + switch (roomId) { + case 1: // Treasury + return 76; + case 2: // Lair + return 77; + case 4: // Hatchery + return 78; + case 10: // Workshop + return 80; + case 11: // Prison + //return 81; // Model exists, but not used by the game + return null; + case 12: // Torture + return 82; + case 13: // Temple + //return 83; // This is the actual model, but place candle sticks instead... + return 111; + case 14: // Graveyard + return 84; + case 15: // Casino + return 85; + case 16: // Pit + return 79; + case 26: // Crypt + return 141; + default: + return null; // No pillars + } + } + + @Override + public EntityId getEntityId() { + return entityId; + } + + @Override + public int getHealth() { + return entityData.getComponent(entityId, Health.class).health; + } + + @Override + public int getMaxHealth() { + return entityData.getComponent(entityId, Health.class).maxHealth; + } + + @Override + public Integer getHealthPercent() { + Health health = entityData.getComponent(entityId, Health.class); + return Math.round((float) health.health / health.maxHealth * 100); + } + + @Override + public boolean isAtFullHealth() { + Health health = entityData.getComponent(entityId, Health.class); + + return (health.health == health.maxHealth); + } + + @Override + public short getOwnerId() { + return entityData.getComponent(entityId, Owner.class).ownerId; + } + + @Override + public short getRoomId() { + return roomInstance.getEntity().getId(); + } +} diff --git a/src/toniarts/openkeeper/game/controller/room/FiveByFiveRotatedController.java b/src/toniarts/openkeeper/game/controller/room/FiveByFiveRotatedController.java index 2567b441..79c5fefe 100644 --- a/src/toniarts/openkeeper/game/controller/room/FiveByFiveRotatedController.java +++ b/src/toniarts/openkeeper/game/controller/room/FiveByFiveRotatedController.java @@ -1,138 +1,149 @@ -/* - * Copyright (C) 2014-2015 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.controller.room; - -import com.jme3.math.FastMath; -import com.simsilica.es.EntityData; -import com.simsilica.es.EntityId; -import java.awt.Point; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import toniarts.openkeeper.common.RoomInstance; -import toniarts.openkeeper.game.controller.IGameTimer; -import toniarts.openkeeper.game.controller.IObjectsController; -import toniarts.openkeeper.game.controller.room.storage.RoomGoldControl; -import toniarts.openkeeper.tools.convert.map.KwdFile; -import toniarts.openkeeper.tools.convert.map.Variable; -import toniarts.openkeeper.utils.Utils; - -/** - * Constructs 5 by 5 "rotated" buildings. As far as I know, only Dungeon Heart - * - * @author Toni Helenius - */ -public class FiveByFiveRotatedController extends AbstractRoomController implements ICreatureEntrance { - - public static final short OBJECT_HEART_ID = 13; - public static final short OBJECT_ARCHES_ID = 86; - public static final short OBJECT_BIG_STEPS_ID = 88; - public static final short OBJECT_PLUG_ID = 96; - - private double lastSpawnTime = Double.MIN_VALUE; - private final List spawnPoints = new ArrayList<>(16); - - public FiveByFiveRotatedController(EntityId entityId, EntityData entityData, KwdFile kwdFile, - RoomInstance roomInstance, IObjectsController objectsController, - Map gameSettings, IGameTimer gameTimer) { - super(entityId, entityData, kwdFile, roomInstance, objectsController); - final int maxGold = (int) gameSettings.get(Variable.MiscVariable.MiscType.MAX_GOLD_PER_DUNGEON_HEART_TILE).getValue(); - addObjectControl(new RoomGoldControl(kwdFile, this, objectsController, gameTimer) { - - @Override - protected int getGoldPerObject() { - return maxGold; - } - - @Override - protected int getNumberOfAccessibleTiles() { - return 16; - } - }); - } - - @Override - public void construct() { - super.construct(); - - // Init the spawn points - spawnPoints.clear(); - for (Point p : roomInstance.getCoordinates()) { - if (isTileAccessible(null, null, p.x, p.y)) { - spawnPoints.add(p); - } - } - } - - @Override - protected void constructObjects() { - - // We contruct the Dungeon Heart here - // Because of physics and whatnot, the object are on server, so what about the creation animation? - // The creation animation should be on the client perhaps... We don't care about it... - Point center = roomInstance.getCenter(); - floorFurniture.add(objectsController.loadObject(OBJECT_HEART_ID, roomInstance.getOwnerId(), center.x, center.y)); - - // Construct the plug - floorFurniture.add(objectsController.loadObject(OBJECT_PLUG_ID, roomInstance.getOwnerId(), center.x, center.y)); - - // The arches - floorFurniture.add(objectsController.loadObject(OBJECT_ARCHES_ID, roomInstance.getOwnerId(), center.x, center.y)); - - // The steps between the arches - floorFurniture.add(objectsController.loadObject(OBJECT_BIG_STEPS_ID, roomInstance.getOwnerId(), center.x, center.y)); - floorFurniture.add(objectsController.loadObject(OBJECT_BIG_STEPS_ID, roomInstance.getOwnerId(), center.x, center.y, -FastMath.TWO_PI / 3)); - floorFurniture.add(objectsController.loadObject(OBJECT_BIG_STEPS_ID, roomInstance.getOwnerId(), center.x, center.y, FastMath.TWO_PI / 3)); - } - - @Override - public boolean isTileAccessible(Integer fromX, Integer fromY, int toX, int toY) { - - // The center 3x3 is not accessible - Point roomPoint = roomInstance.worldCoordinateToLocalCoordinate(toX, toY); - return ((roomPoint.x == 0 || roomPoint.x == 4) || (roomPoint.y == 0 || roomPoint.y == 4)); - } - - @Override - public boolean isDungeonHeart() { - return true; - } - - @Override - public Point getEntranceCoordinate() { - - // FIXME: Is it random truly or just one corner?? - return Utils.getRandomItem(spawnPoints); - } - - @Override - public double getLastSpawnTime() { - return lastSpawnTime; - } - - @Override - public void onSpawn(double time, EntityId entityId) { - this.lastSpawnTime = time; - } - - @Override - public void captured(short playerId) { - super.captured(playerId); - lastSpawnTime = Double.MIN_VALUE; - } - -} +/* + * Copyright (C) 2014-2015 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.controller.room; + +import com.jme3.math.FastMath; +import com.simsilica.es.EntityData; +import com.simsilica.es.EntityId; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import toniarts.openkeeper.common.RoomInstance; +import toniarts.openkeeper.game.controller.IGameTimer; +import toniarts.openkeeper.game.controller.IObjectsController; +import toniarts.openkeeper.game.controller.room.storage.RoomGoldControl; +import toniarts.openkeeper.tools.convert.map.KwdFile; +import toniarts.openkeeper.tools.convert.map.Variable; +import toniarts.openkeeper.utils.Utils; + +/** + * Constructs 5 by 5 "rotated" buildings. As far as I know, only Dungeon Heart + * + * @author Toni Helenius + */ +public class FiveByFiveRotatedController extends AbstractRoomController implements ICreatureEntrance { + + public static final short OBJECT_HEART_ID = 13; + public static final short OBJECT_ARCHES_ID = 86; + public static final short OBJECT_BIG_STEPS_ID = 88; + public static final short OBJECT_PLUG_ID = 96; + + private double lastSpawnTime = Double.MIN_VALUE; + private final List spawnPoints = new ArrayList<>(16); + + public FiveByFiveRotatedController(EntityId entityId, EntityData entityData, KwdFile kwdFile, + RoomInstance roomInstance, IObjectsController objectsController, + Map gameSettings, IGameTimer gameTimer) { + super(entityId, entityData, kwdFile, roomInstance, objectsController); + final int maxGold = (int) gameSettings.get(Variable.MiscVariable.MiscType.MAX_GOLD_PER_DUNGEON_HEART_TILE).getValue(); + addObjectControl(new RoomGoldControl(kwdFile, this, objectsController, gameTimer) { + + @Override + protected int getGoldPerObject() { + return maxGold; + } + + @Override + protected int getNumberOfAccessibleTiles() { + return 16; + } + }); + } + + @Override + public void construct() { + super.construct(); + + // Init the spawn points + spawnPoints.clear(); + for (Point p : roomInstance.getCoordinates()) { + if (isTileAccessible(null, null, p.x, p.y)) { + spawnPoints.add(p); + } + } + } + + @Override + protected void constructObjects() { + + // We contruct the Dungeon Heart here + // Because of physics and whatnot, the object are on server, so what about the creation animation? + // The creation animation should be on the client perhaps... We don't care about it... + Point center = roomInstance.getCenter(); + if (isDestroyed()) { + constructDestroyed(center); + } else { + constructNonDestroyed(center); + } + } + + private void constructNonDestroyed(Point center) { + floorFurniture.add(objectsController.loadObject(OBJECT_HEART_ID, roomInstance.getOwnerId(), center.x, center.y)); + + // Construct the plug + floorFurniture.add(objectsController.loadObject(OBJECT_PLUG_ID, roomInstance.getOwnerId(), center.x, center.y)); + + // The arches + floorFurniture.add(objectsController.loadObject(OBJECT_ARCHES_ID, roomInstance.getOwnerId(), center.x, center.y)); + + // The steps between the arches + floorFurniture.add(objectsController.loadObject(OBJECT_BIG_STEPS_ID, roomInstance.getOwnerId(), center.x, center.y)); + floorFurniture.add(objectsController.loadObject(OBJECT_BIG_STEPS_ID, roomInstance.getOwnerId(), center.x, center.y, -FastMath.TWO_PI / 3)); + floorFurniture.add(objectsController.loadObject(OBJECT_BIG_STEPS_ID, roomInstance.getOwnerId(), center.x, center.y, FastMath.TWO_PI / 3)); + } + + private void constructDestroyed(Point center) { + } + + @Override + public boolean isTileAccessible(Integer fromX, Integer fromY, int toX, int toY) { + + // The center 3x3 is not accessible + Point roomPoint = roomInstance.worldCoordinateToLocalCoordinate(toX, toY); + return ((roomPoint.x == 0 || roomPoint.x == 4) || (roomPoint.y == 0 || roomPoint.y == 4)); + } + + @Override + public boolean isDungeonHeart() { + return true; + } + + @Override + public Point getEntranceCoordinate() { + + // FIXME: Is it random truly or just one corner?? + return Utils.getRandomItem(spawnPoints); + } + + @Override + public double getLastSpawnTime() { + return lastSpawnTime; + } + + @Override + public void onSpawn(double time, EntityId entityId) { + this.lastSpawnTime = time; + } + + @Override + public void captured(short playerId) { + super.captured(playerId); + lastSpawnTime = Double.MIN_VALUE; + } + +} diff --git a/src/toniarts/openkeeper/game/controller/room/IRoomController.java b/src/toniarts/openkeeper/game/controller/room/IRoomController.java index 713b2c3c..353d035a 100644 --- a/src/toniarts/openkeeper/game/controller/room/IRoomController.java +++ b/src/toniarts/openkeeper/game/controller/room/IRoomController.java @@ -1,121 +1,122 @@ -/* - * Copyright (C) 2014-2017 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.controller.room; - -import com.simsilica.es.EntityId; -import java.awt.Point; -import java.util.Set; -import toniarts.openkeeper.common.RoomInstance; -import toniarts.openkeeper.game.controller.room.storage.IRoomObjectControl; -import toniarts.openkeeper.tools.convert.map.Room; - -/** - * Controls rooms and provides services related to rooms - * - * @author Toni Helenius - */ -public interface IRoomController { - - /** - * Constructs a room - */ - public void construct(); - - /** - * Checks if the given tile is accessible, from an adjacent tile. If no from - * tile is given, checks general accessibility - * - * @param from from tile, can be {@code null} - * @param to the target tile - * @return true is the tile is accessible - */ - public boolean isTileAccessible(Point from, Point to); - - /** - * Get the actual room instance representation of the room - * - * @return the room instance - */ - public RoomInstance getRoomInstance(); - - /** - * Get the number of floor furniture in a room - * - * @return floor furniture count - */ - public int getFloorFurnitureCount(); - - /** - * Get the number of wall furniture in a room - * - * @return wall furniture count - */ - public int getWallFurnitureCount(); - - /** - * Get the floot furniture IDs - * - * @return floor furniture - */ - public Set getFloorFurniture(); - - /** - * Get the wall furniture IDs - * - * @return wall furniture - */ - public Set getWallFurniture(); - - public boolean canStoreGold(); - - public boolean hasObjectControl(AbstractRoomController.ObjectType objectType); - - public T getObjectControl(AbstractRoomController.ObjectType objectType); - - public Room getRoom(); - - /** - * Are we the dungeon heart? - * - * @return are we? - */ - public boolean isDungeonHeart(); - - /** - * Notify and mark the room as destroyed - */ - public void destroy(); - - /** - * Is this room instance destroyed? Not in the world anymore. - * - * @see #destroy() - * @return is the room destroyed - */ - public boolean isDestroyed(); - - /** - * Signal that the room has been captured - * @param playerId the new owner ID - */ - public void captured(short playerId); - - public boolean isFullCapacity(); - - public EntityId getEntityId(); - -} +/* + * Copyright (C) 2014-2017 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.controller.room; + +import com.simsilica.es.EntityId; +import java.awt.Point; +import java.util.Set; +import toniarts.openkeeper.common.RoomInstance; +import toniarts.openkeeper.game.controller.room.storage.IRoomObjectControl; +import toniarts.openkeeper.game.map.IRoomInformation; +import toniarts.openkeeper.tools.convert.map.Room; + +/** + * Controls rooms and provides services related to rooms + * + * @author Toni Helenius + */ +public interface IRoomController extends IRoomInformation { + + /** + * Constructs a room + */ + public void construct(); + + /** + * Checks if the given tile is accessible, from an adjacent tile. If no from + * tile is given, checks general accessibility + * + * @param from from tile, can be {@code null} + * @param to the target tile + * @return true is the tile is accessible + */ + public boolean isTileAccessible(Point from, Point to); + + /** + * Get the actual room instance representation of the room + * + * @return the room instance + */ + public RoomInstance getRoomInstance(); + + /** + * Get the number of floor furniture in a room + * + * @return floor furniture count + */ + public int getFloorFurnitureCount(); + + /** + * Get the number of wall furniture in a room + * + * @return wall furniture count + */ + public int getWallFurnitureCount(); + + /** + * Get the floot furniture IDs + * + * @return floor furniture + */ + public Set getFloorFurniture(); + + /** + * Get the wall furniture IDs + * + * @return wall furniture + */ + public Set getWallFurniture(); + + public boolean canStoreGold(); + + public boolean hasObjectControl(AbstractRoomController.ObjectType objectType); + + public T getObjectControl(AbstractRoomController.ObjectType objectType); + + public Room getRoom(); + + /** + * Are we the dungeon heart? + * + * @return are we? + */ + public boolean isDungeonHeart(); + + /** + * Remove the room, marks the room as destroyed and releases all the + * controls. The room should not be used after this. + * + * @see #destroy() + */ + public void remove(); + + /** + * Marks the room as destroyed + * + * @see #remove() + */ + public void destroy(); + + /** + * Signal that the room has been captured + * @param playerId the new owner ID + */ + public void captured(short playerId); + + public boolean isFullCapacity(); + +} diff --git a/src/toniarts/openkeeper/game/map/AbstractRoomInformation.java b/src/toniarts/openkeeper/game/map/AbstractRoomInformation.java new file mode 100644 index 00000000..de877ac6 --- /dev/null +++ b/src/toniarts/openkeeper/game/map/AbstractRoomInformation.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014-2020 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.map; + +import com.simsilica.es.EntityComponent; +import com.simsilica.es.EntityId; +import java.util.Objects; +import toniarts.openkeeper.game.component.Health; +import toniarts.openkeeper.game.component.Owner; +import toniarts.openkeeper.game.component.RoomComponent; + +/** + * A presentation of a single map tile. Gets data from specified entity + * + * @author Toni Helenius + */ +public abstract class AbstractRoomInformation implements IRoomInformation { + + protected final EntityId entityId; + + public AbstractRoomInformation(EntityId entityId) { + this.entityId = entityId; + } + + protected abstract T getEntityComponent(Class type); + + @Override + public short getOwnerId() { + return getEntityComponent(Owner.class).ownerId; + } + + @Override + public int getHealth() { + return getEntityComponent(Health.class).health; + } + + @Override + public int getMaxHealth() { + return getEntityComponent(Health.class).maxHealth; + } + + @Override + public Integer getHealthPercent() { + Health health = getEntityComponent(Health.class); + return Math.round((float) health.health / health.maxHealth * 100); + } + + /** + * Is tile at full health + * + * @return true if full health + */ + @Override + public boolean isAtFullHealth() { + Health health = getEntityComponent(Health.class); + return (health.health == health.maxHealth); + } + + @Override + public EntityId getEntityId() { + return entityId; + } + + @Override + public short getRoomId() { + return getEntityComponent(RoomComponent.class).roomId; + } + + @Override + public boolean isDestroyed() { + return getEntityComponent(RoomComponent.class).destroyed; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 41 * hash + Objects.hashCode(this.entityId); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AbstractRoomInformation other = (AbstractRoomInformation) obj; + if (!Objects.equals(this.entityId, other.entityId)) { + return false; + } + return true; + } + +} diff --git a/src/toniarts/openkeeper/game/map/IRoomInformation.java b/src/toniarts/openkeeper/game/map/IRoomInformation.java new file mode 100644 index 00000000..00de6930 --- /dev/null +++ b/src/toniarts/openkeeper/game/map/IRoomInformation.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014-2024 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.map; + +import com.simsilica.es.EntityId; + +/** + * A kind of a room container with no editing functionalities + * + * @author Toni Helenius + */ +public interface IRoomInformation { + + /** + * Get the entity ID of this room + * + * @return + */ + EntityId getEntityId(); + + int getHealth(); + + int getMaxHealth(); + + Integer getHealthPercent(); + + /** + * Is room at full health + * + * @return true if full health + */ + boolean isAtFullHealth(); + + short getOwnerId(); + + short getRoomId(); + + /** + * Is this room instance destroyed? + * + * @see #remove() + * @return is the room destroyed + */ + boolean isDestroyed(); + +} diff --git a/src/toniarts/openkeeper/game/map/IRoomsInformation.java b/src/toniarts/openkeeper/game/map/IRoomsInformation.java new file mode 100644 index 00000000..69d90752 --- /dev/null +++ b/src/toniarts/openkeeper/game/map/IRoomsInformation.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014-2024 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.map; + +import com.simsilica.es.EntityId; + +/** + * Holds the rooms data + * + * @author Toni Helenius + * @param the room data type + */ +public interface IRoomsInformation { + + T getRoomInformation(EntityId entityId); + +} diff --git a/src/toniarts/openkeeper/game/map/MapRoom.java b/src/toniarts/openkeeper/game/map/MapRoom.java deleted file mode 100644 index b42d9cea..00000000 --- a/src/toniarts/openkeeper/game/map/MapRoom.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2014-2017 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.map; - -import com.jme3.export.JmeExporter; -import com.jme3.export.JmeImporter; -import com.jme3.export.Savable; -import java.awt.Point; -import java.io.IOException; -import java.util.List; - -/** - * Represents a room data instance - * - * @author Toni Helenius - */ -public class MapRoom implements Savable { - - private int health; - private int maxHealth; - private final boolean destroyed = false; - private short ownerId; - private short roomId; - private List tiles; - // TODO: Also stuff stored here, enum -> (amount, max amount) - - @Override - public void write(JmeExporter ex) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public void read(JmeImporter im) throws IOException { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - -} diff --git a/src/toniarts/openkeeper/game/state/GameClientState.java b/src/toniarts/openkeeper/game/state/GameClientState.java index 3d0b3f3f..25eefd89 100644 --- a/src/toniarts/openkeeper/game/state/GameClientState.java +++ b/src/toniarts/openkeeper/game/state/GameClientState.java @@ -1,638 +1,638 @@ -/* - * Copyright (C) 2014-2017 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.state; - -import com.jme3.app.Application; -import com.jme3.app.state.AppStateManager; -import com.jme3.cinematic.events.CinematicEvent; -import com.jme3.cinematic.events.CinematicEventListener; -import com.jme3.math.Vector3f; -import com.simsilica.es.EntityId; -import java.awt.Point; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import toniarts.openkeeper.Main; -import toniarts.openkeeper.game.controller.IPlayerController; -import toniarts.openkeeper.game.controller.PlayerController; -import toniarts.openkeeper.game.data.Keeper; -import toniarts.openkeeper.game.data.ResearchableEntity; -import toniarts.openkeeper.game.data.ResearchableType; -import toniarts.openkeeper.game.map.IMapInformation; -import toniarts.openkeeper.game.state.loading.IPlayerLoadingProgress; -import toniarts.openkeeper.game.state.loading.MultiplayerLoadingState; -import toniarts.openkeeper.game.state.loading.SingleBarLoadingState; -import toniarts.openkeeper.game.state.lobby.ClientInfo; -import toniarts.openkeeper.game.state.session.GameSessionClientService; -import toniarts.openkeeper.game.state.session.GameSessionListener; -import toniarts.openkeeper.tools.convert.map.KwdFile; -import toniarts.openkeeper.tools.convert.map.TriggerAction; -import toniarts.openkeeper.tools.convert.map.Variable; -import toniarts.openkeeper.utils.AssetUtils; -import toniarts.openkeeper.view.PlayerCameraState; -import toniarts.openkeeper.view.PlayerEntityViewState; -import toniarts.openkeeper.view.PlayerMapViewState; -import toniarts.openkeeper.view.SystemMessageState; -import toniarts.openkeeper.view.text.TextParser; -import toniarts.openkeeper.view.text.TextParserService; - -/** - * The game client state - * - * @author Toni Helenius - */ -public class GameClientState extends AbstractPauseAwareState { - - private static final Logger logger = System.getLogger(GameClientState.class.getName()); - - private final Main app; - - private AppStateManager stateManager; - - private final KwdFile kwdFile; - - private final Map players = new TreeMap<>(); - private final Map playerControllers = new TreeMap<>(); - private final Object loadingObject = new Object(); - private volatile boolean gameStarted = false; - - private final Short playerId; - private final boolean multiplayer; - private IPlayerLoadingProgress loadingState; - private final GameSessionClientService gameClientService; - private final GameSessionListenerImpl gameSessionListener = new GameSessionListenerImpl(); - private IMapInformation mapInformation; - private PlayerState playerState; - - private PlayerMapViewState playerMapViewState; - private PlayerEntityViewState playerModelViewState; - private TextParser textParser; - - /** - * Single use game states - * - * @param level the level to load - * @param playerId our player ID - * @param players players participating in this game - * @param gameClientService client services - * @param app the main application - */ - public GameClientState(KwdFile level, Short playerId, List players, GameSessionClientService gameClientService, Main app) { - this.kwdFile = level; - this.gameClientService = gameClientService; - this.playerId = playerId; - this.app = (Main) app; - - // Set multiplayer - int humanPlayers = 0; - for (ClientInfo clientInfo : players) { - if (!clientInfo.getKeeper().isAi()) { - humanPlayers++; - if (humanPlayers > 1) { - break; - } - } - } - multiplayer = (humanPlayers > 1); - - // Create the loading state - loadingState = createLoadingState(app); - - // Add the listener - gameClientService.addGameSessionListener(gameSessionListener); - - // Tell that we are ready to start receiving game data - gameClientService.markReady(); - } - - @Override - public void initialize(final AppStateManager stateManager, final Application app) { - this.stateManager = stateManager; - - // Attach the loading state finally - if (!gameStarted) { - synchronized (loadingObject) { - if (!gameStarted) { - stateManager.attach(loadingState); - } - } - } - } - - @Override - public void setEnabled(boolean enabled) { - super.setEnabled(enabled); - } - - private void detachRelatedAppStates() { - stateManager.detach(playerModelViewState); - stateManager.detach(playerMapViewState); - stateManager.detach(playerState); - } - - /** - * If you are getting rid of the game state, use this so that all the - * related states are detached on the same render loop. Otherwise the app - * might crash. - */ - public void detach() { - - // If we are stuck loading - synchronized (loadingObject) { - loadingObject.notifyAll(); - } - - stateManager.detach(this); - detachRelatedAppStates(); - - playerState = null; - playerMapViewState = null; - playerModelViewState = null; - } - - @Override - public void cleanup() { - - // Signal our exit & close all the connections - gameClientService.exitGame(); - ConnectionState cs = stateManager.getState(ConnectionState.class); - if (cs != null) { - cs.disconnect(); - } - - // Detach - detach(); - - super.cleanup(); - } - - @Override - public boolean isPauseable() { - return true; - } - - public boolean isMultiplayer() { - return multiplayer; - } - - private IPlayerLoadingProgress createLoadingState(Main app) { - - // Create the appropriate loaging screen - IPlayerLoadingProgress loader; - if (isMultiplayer()) { - loader = new MultiplayerLoadingState(app, "Multiplayer") { - - @Override - public void onLoad() { - onGameLoad(); - } - - @Override - public void onLoadComplete() { - onGameLoadComplete(); - } - }; - } else { - loader = new SingleBarLoadingState(app, "Singleplayer") { - - @Override - public void onLoad() { - onGameLoad(); - } - - @Override - public void onLoadComplete() { - onGameLoadComplete(); - } - }; - } - return loader; - } - - private void onGameLoadComplete() { - - // Set initialized - GameClientState.this.initialized = true; - - // Set the processors - GameClientState.this.app.setViewProcessors(); - } - - private void onGameLoad() { - try { - - // The game is actually loaded elsewhere but for the visuals we need this - if (!gameStarted) { - synchronized (loadingObject) { - if (!gameStarted) { - loadingObject.wait(); - } - } - } - - } catch (Exception e) { - logger.log(Level.ERROR, "Failed to load the game!", e); - } - } - - /** - * Get the level raw data file - * - * @return the KWD - */ - public KwdFile getLevelData() { - return kwdFile; - } - - public Keeper getPlayer(short playerId) { - return players.get(playerId); - } - - public Keeper getPlayer() { - return players.get(playerId); - } - - public Collection getPlayers() { - return players.values(); - } - - /** - * Get level variable value - * - * @param variable the variable type - * @return variable value - */ - public float getLevelVariable(Variable.MiscVariable.MiscType variable) { - // TODO: player is able to change these, so need a wrapper and store these to GameState - return kwdFile.getVariables().get(variable).getValue(); - } - - /** - * Gets a text parser that can fill up the parameters in translations - * - * @return a text parser - */ - public TextParser getTextParser() { - return textParser; - } - - private class GameSessionListenerImpl implements GameSessionListener { - - private final Object mapDataLoadingObject = new Object(); - private volatile boolean mapDataLoaded = false; - - @Override - public void onGameDataLoaded(Collection players) { - - // This might take awhile, don't block - Thread loadingThread = new Thread(() -> { - - // Now we have the game data, start loading the map - kwdFile.load(); - AssetUtils.prewarmAssets(kwdFile, app.getAssetManager(), app); - for (Keeper keeper : players) { - keeper.setPlayer(kwdFile.getPlayer(keeper.getId())); - GameClientState.this.players.put(keeper.getId(), keeper); - GameClientState.this.playerControllers.put(keeper.getId(), new PlayerController(kwdFile, keeper, kwdFile.getImp(), gameClientService.getEntityData(), kwdFile.getVariables())); - } - - // Create player state - playerState = new PlayerState(playerId, kwdFile, gameClientService.getEntityData(), false, app); - - playerMapViewState = new PlayerMapViewState(app, kwdFile, app.getAssetManager(), players, gameClientService.getEntityData(), playerId, - () -> { - synchronized (mapDataLoadingObject) { - mapDataLoaded = true; - mapDataLoadingObject.notifyAll(); - } - }) { - - private float lastProgress = 0; - - @Override - protected void updateProgress(float progress) { - - // Update ourselves - onLoadStatusUpdate(progress, playerId); - - if (progress - lastProgress >= 0.01f) { - gameClientService.loadStatus(progress); - lastProgress = progress; - } - } - }; - mapInformation = playerMapViewState.getMapInformation(); - textParser = new TextParserService(mapInformation); - playerModelViewState = new PlayerEntityViewState(kwdFile, app.getAssetManager(), gameClientService.getEntityData(), playerId, textParser, app.getRootNode()); - - // Attach the states - stateManager.attach(playerState); - stateManager.attach(playerMapViewState); - stateManager.attach(playerModelViewState); - - // Wait until loaded - if (!mapDataLoaded) { - synchronized (mapDataLoadingObject) { - if (!mapDataLoaded) { - try { - mapDataLoadingObject.wait(); - } catch (InterruptedException ex) { - System.getLogger(GameClientState.class.getName()).log(Level.ERROR, "Map data loading interrupted!", ex); - } - } - } - } - - // Loaded up, send our ready signal on the next frame to ensure all the states are attached - app.enqueue(() -> { - - // Prewarm the whole scene - app.getRenderManager().preloadScene(app.getRootNode()); - - // Signal our readiness - gameClientService.loadComplete(); - }); - - }, "GameDataClientLoader"); - loadingThread.start(); - } - - @Override - public void onLoadComplete(short keeperId) { - loadingState.setProgress(1f, keeperId); - } - - @Override - public void onLoadStatusUpdate(float progress, short keeperId) { - loadingState.setProgress(progress, keeperId); - } - - @Override - public void onGameStarted() { - - // Release loading state from memory - loadingState = null; - - stateManager.getState(SoundState.class).setKwdFile(kwdFile); - stateManager.getState(SoundState.class).setEnabled(true); - - app.enqueue(() -> { - playerState.setEnabled(true); - - // Release the lock and enter to the game phase - synchronized (loadingObject) { - gameStarted = true; - loadingObject.notifyAll(); - } - }); - } - - @Override - public void onTilesChange(List updatedTiles) { - //mapInformation.setTiles(updatedTiles); - //playerMapViewState.onTilesChange(updatedTiles); - } - - @Override - public void onGoldChange(short keeperId, int gold) { - getPlayer(keeperId).setGold(gold); - - // FIXME: See in what thread we are - if (playerState != null && playerState.getPlayerId() == keeperId) { - app.enqueue(() -> { - playerState.onGoldChange(keeperId, gold); - }); - } - } - - @Override - public void onManaChange(short keeperId, int mana, int manaLoose, int manaGain) { - Keeper keeper = getPlayer(keeperId); - keeper.setMana(mana); - keeper.setManaGain(manaGain); - keeper.setManaLoose(manaLoose); - - // FIXME: See in what thread we are - if (playerState != null && playerState.getPlayerId() == keeperId) { - app.enqueue(() -> { - playerState.onManaChange(keeperId, mana, manaLoose, manaGain); - }); - } - } - - @Override - public void onBuild(short keeperId, List tiles) { - playerMapViewState.onBuild(keeperId, tiles); - } - - @Override - public void onSold(short keeperId, List tiles) { - playerMapViewState.onSold(keeperId, tiles); - } - - @Override - public void onGamePaused() { - app.enqueue(() -> { - playerState.onPaused(true); - }); - } - - @Override - public void onGameResumed() { - app.enqueue(() -> { - playerState.onPaused(false); - }); - } - - @Override - public void onSetWidescreen(boolean enable) { - playerState.setWideScreen(enable); - } - - @Override - public void onPlaySpeech(int speechId, boolean showText, boolean introduction, int pathId) { - - // TODO: Refactor these, we don't maybe want this logic here, borderline visuals - stateManager.getState(SoundState.class).attachLevelSpeech(speechId, () -> { - stateManager.getState(SystemMessageState.class).addMessage(SystemMessageState.MessageType.INFO, String.format("${level.%d}", speechId - 1)); - if (showText) { - playerState.setText(speechId, introduction, pathId); - } - }); - } - - @Override - public void onDoTransition(short pathId, Vector3f start) { - - // TODO: Refactor - stateManager.getState(PlayerCameraState.class).doTransition(pathId, start, new CinematicEventListener() { - @Override - public void onPlay(CinematicEvent cinematic) { - - } - - @Override - public void onPause(CinematicEvent cinematic) { - - } - - @Override - public void onStop(CinematicEvent cinematic) { - gameClientService.transitionEnd(); - } - }); - } - - @Override - public void onFlashButton(TriggerAction.MakeType buttonType, short targetId, TriggerAction.ButtonType targetButtonType, boolean enabled, int time) { - playerState.flashButton(buttonType, targetId, targetButtonType, enabled, time); - } - - @Override - public void onRotateViewAroundPoint(Vector3f point, boolean relative, int angle, int time) { - playerState.rotateViewAroundPoint(point, relative, angle, time); - } - - @Override - public void onShowMessage(int textId) { - playerState.showMessage(textId); - } - - @Override - public void onZoomViewToPoint(Vector3f point) { - playerState.zoomToPoint(point); - } - - @Override - public void onTileFlash(List points, boolean enabled, short keeperId) { - playerMapViewState.onTileFlash(points, enabled, keeperId); - } - - @Override - public void onZoomViewToEntity(EntityId entityId) { - playerState.zoomToEntity(entityId, true); - } - - @Override - public void onShowUnitFlower(EntityId entityId, int interval) { - playerModelViewState.showUnitFlower(entityId, interval); - } - - @Override - public void onEntityAdded(short keeperId, ResearchableEntity researchableEntity) { - setResearchableEntity(keeperId, researchableEntity, () -> { - playerState.onEntityAdded(keeperId, researchableEntity); - }); - } - - private void setResearchableEntity(short keeperId, ResearchableEntity researchableEntity, Runnable notifier) { - Keeper keeper = getPlayer(keeperId); - setResearchableEntity(researchableEntity, getResearchableEntitiesList(keeper, researchableEntity)); - - // FIXME: See in what thread we are - if (notifier != null && playerState != null && playerState.getPlayerId() == keeperId) { - app.enqueue(notifier); - } - } - - @Override - public void onEntityRemoved(short keeperId, ResearchableEntity researchableEntity) { - setResearchableEntity(keeperId, researchableEntity, () -> { - playerState.onEntityRemoved(keeperId, researchableEntity); - }); - } - - @Override - public void onResearchStatusChanged(short keeperId, ResearchableEntity researchableEntity) { - setResearchableEntity(keeperId, researchableEntity, () -> { - playerState.onResearchStatusChanged(keeperId, researchableEntity); - }); - } - - private List getResearchableEntitiesList(Keeper keeper, ResearchableEntity researchableEntity) { - List researchableEntities = null; - switch (researchableEntity.getResearchableType()) { - case SPELL: { - researchableEntities = keeper.getAvailableSpells(); - break; - } - case DOOR: { - researchableEntities = keeper.getAvailableDoors(); - break; - } - case ROOM: { - researchableEntities = keeper.getAvailableRooms(); - break; - } - case TRAP: { - researchableEntities = keeper.getAvailableTraps(); - break; - } - } - - return researchableEntities; - } - - private void setResearchableEntity(T researchableEntity, List researchableEntities) { - int index = Collections.binarySearch(researchableEntities, researchableEntity, (ResearchableEntity o1, ResearchableEntity o2) -> { - return getResearchableEntityType(kwdFile, o1.getResearchableType(), o1.getId()).compareTo(getResearchableEntityType(kwdFile, o2.getResearchableType(), o2.getId())); - }); - if (index < 0) { - researchableEntities.add(~index, researchableEntity); - } else if (index >= 0) { - researchableEntities.set(index, researchableEntity); - } - } - - private Comparable getResearchableEntityType(KwdFile kwdFile, ResearchableType researchableType, short typeId) { - switch (researchableType) { - case DOOR: { - return kwdFile.getDoorById(typeId); - } - case ROOM: { - return kwdFile.getRoomById(typeId); - } - case SPELL: { - return kwdFile.getKeeperSpellById(typeId); - } - case TRAP: { - return kwdFile.getTrapById(typeId); - } - } - - return null; - } - - @Override - public void setPossession(EntityId target) { - playerState.setPossession(target); - } - } - - public IMapInformation getMapClientService() { - return mapInformation; - } - - public GameSessionClientService getGameClientService() { - return gameClientService; - } - -} +/* + * Copyright (C) 2014-2017 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.state; + +import com.jme3.app.Application; +import com.jme3.app.state.AppStateManager; +import com.jme3.cinematic.events.CinematicEvent; +import com.jme3.cinematic.events.CinematicEventListener; +import com.jme3.math.Vector3f; +import com.simsilica.es.EntityId; +import java.awt.Point; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import toniarts.openkeeper.Main; +import toniarts.openkeeper.game.controller.IPlayerController; +import toniarts.openkeeper.game.controller.PlayerController; +import toniarts.openkeeper.game.data.Keeper; +import toniarts.openkeeper.game.data.ResearchableEntity; +import toniarts.openkeeper.game.data.ResearchableType; +import toniarts.openkeeper.game.map.IMapInformation; +import toniarts.openkeeper.game.state.loading.IPlayerLoadingProgress; +import toniarts.openkeeper.game.state.loading.MultiplayerLoadingState; +import toniarts.openkeeper.game.state.loading.SingleBarLoadingState; +import toniarts.openkeeper.game.state.lobby.ClientInfo; +import toniarts.openkeeper.game.state.session.GameSessionClientService; +import toniarts.openkeeper.game.state.session.GameSessionListener; +import toniarts.openkeeper.tools.convert.map.KwdFile; +import toniarts.openkeeper.tools.convert.map.TriggerAction; +import toniarts.openkeeper.tools.convert.map.Variable; +import toniarts.openkeeper.utils.AssetUtils; +import toniarts.openkeeper.view.PlayerCameraState; +import toniarts.openkeeper.view.PlayerEntityViewState; +import toniarts.openkeeper.view.PlayerMapViewState; +import toniarts.openkeeper.view.SystemMessageState; +import toniarts.openkeeper.view.text.TextParser; +import toniarts.openkeeper.view.text.TextParserService; + +/** + * The game client state + * + * @author Toni Helenius + */ +public class GameClientState extends AbstractPauseAwareState { + + private static final Logger logger = System.getLogger(GameClientState.class.getName()); + + private final Main app; + + private AppStateManager stateManager; + + private final KwdFile kwdFile; + + private final Map players = new TreeMap<>(); + private final Map playerControllers = new TreeMap<>(); + private final Object loadingObject = new Object(); + private volatile boolean gameStarted = false; + + private final Short playerId; + private final boolean multiplayer; + private IPlayerLoadingProgress loadingState; + private final GameSessionClientService gameClientService; + private final GameSessionListenerImpl gameSessionListener = new GameSessionListenerImpl(); + private IMapInformation mapInformation; + private PlayerState playerState; + + private PlayerMapViewState playerMapViewState; + private PlayerEntityViewState playerModelViewState; + private TextParser textParser; + + /** + * Single use game states + * + * @param level the level to load + * @param playerId our player ID + * @param players players participating in this game + * @param gameClientService client services + * @param app the main application + */ + public GameClientState(KwdFile level, Short playerId, List players, GameSessionClientService gameClientService, Main app) { + this.kwdFile = level; + this.gameClientService = gameClientService; + this.playerId = playerId; + this.app = (Main) app; + + // Set multiplayer + int humanPlayers = 0; + for (ClientInfo clientInfo : players) { + if (!clientInfo.getKeeper().isAi()) { + humanPlayers++; + if (humanPlayers > 1) { + break; + } + } + } + multiplayer = (humanPlayers > 1); + + // Create the loading state + loadingState = createLoadingState(app); + + // Add the listener + gameClientService.addGameSessionListener(gameSessionListener); + + // Tell that we are ready to start receiving game data + gameClientService.markReady(); + } + + @Override + public void initialize(final AppStateManager stateManager, final Application app) { + this.stateManager = stateManager; + + // Attach the loading state finally + if (!gameStarted) { + synchronized (loadingObject) { + if (!gameStarted) { + stateManager.attach(loadingState); + } + } + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + } + + private void detachRelatedAppStates() { + stateManager.detach(playerModelViewState); + stateManager.detach(playerMapViewState); + stateManager.detach(playerState); + } + + /** + * If you are getting rid of the game state, use this so that all the + * related states are detached on the same render loop. Otherwise the app + * might crash. + */ + public void detach() { + + // If we are stuck loading + synchronized (loadingObject) { + loadingObject.notifyAll(); + } + + stateManager.detach(this); + detachRelatedAppStates(); + + playerState = null; + playerMapViewState = null; + playerModelViewState = null; + } + + @Override + public void cleanup() { + + // Signal our exit & close all the connections + gameClientService.exitGame(); + ConnectionState cs = stateManager.getState(ConnectionState.class); + if (cs != null) { + cs.disconnect(); + } + + // Detach + detach(); + + super.cleanup(); + } + + @Override + public boolean isPauseable() { + return true; + } + + public boolean isMultiplayer() { + return multiplayer; + } + + private IPlayerLoadingProgress createLoadingState(Main app) { + + // Create the appropriate loaging screen + IPlayerLoadingProgress loader; + if (isMultiplayer()) { + loader = new MultiplayerLoadingState(app, "Multiplayer") { + + @Override + public void onLoad() { + onGameLoad(); + } + + @Override + public void onLoadComplete() { + onGameLoadComplete(); + } + }; + } else { + loader = new SingleBarLoadingState(app, "Singleplayer") { + + @Override + public void onLoad() { + onGameLoad(); + } + + @Override + public void onLoadComplete() { + onGameLoadComplete(); + } + }; + } + return loader; + } + + private void onGameLoadComplete() { + + // Set initialized + GameClientState.this.initialized = true; + + // Set the processors + GameClientState.this.app.setViewProcessors(); + } + + private void onGameLoad() { + try { + + // The game is actually loaded elsewhere but for the visuals we need this + if (!gameStarted) { + synchronized (loadingObject) { + if (!gameStarted) { + loadingObject.wait(); + } + } + } + + } catch (Exception e) { + logger.log(Level.ERROR, "Failed to load the game!", e); + } + } + + /** + * Get the level raw data file + * + * @return the KWD + */ + public KwdFile getLevelData() { + return kwdFile; + } + + public Keeper getPlayer(short playerId) { + return players.get(playerId); + } + + public Keeper getPlayer() { + return players.get(playerId); + } + + public Collection getPlayers() { + return players.values(); + } + + /** + * Get level variable value + * + * @param variable the variable type + * @return variable value + */ + public float getLevelVariable(Variable.MiscVariable.MiscType variable) { + // TODO: player is able to change these, so need a wrapper and store these to GameState + return kwdFile.getVariables().get(variable).getValue(); + } + + /** + * Gets a text parser that can fill up the parameters in translations + * + * @return a text parser + */ + public TextParser getTextParser() { + return textParser; + } + + private class GameSessionListenerImpl implements GameSessionListener { + + private final Object mapDataLoadingObject = new Object(); + private volatile boolean mapDataLoaded = false; + + @Override + public void onGameDataLoaded(Collection players) { + + // This might take awhile, don't block + Thread loadingThread = new Thread(() -> { + + // Now we have the game data, start loading the map + kwdFile.load(); + AssetUtils.prewarmAssets(kwdFile, app.getAssetManager(), app); + for (Keeper keeper : players) { + keeper.setPlayer(kwdFile.getPlayer(keeper.getId())); + GameClientState.this.players.put(keeper.getId(), keeper); + GameClientState.this.playerControllers.put(keeper.getId(), new PlayerController(kwdFile, keeper, kwdFile.getImp(), gameClientService.getEntityData(), kwdFile.getVariables())); + } + + // Create player state + playerState = new PlayerState(playerId, kwdFile, gameClientService.getEntityData(), false, app); + + playerMapViewState = new PlayerMapViewState(app, kwdFile, app.getAssetManager(), players, gameClientService.getEntityData(), playerId, + () -> { + synchronized (mapDataLoadingObject) { + mapDataLoaded = true; + mapDataLoadingObject.notifyAll(); + } + }) { + + private float lastProgress = 0; + + @Override + protected void updateProgress(float progress) { + + // Update ourselves + onLoadStatusUpdate(progress, playerId); + + if (progress - lastProgress >= 0.01f) { + gameClientService.loadStatus(progress); + lastProgress = progress; + } + } + }; + mapInformation = playerMapViewState.getMapInformation(); + textParser = new TextParserService(mapInformation, playerMapViewState.getRoomsInformation()); + playerModelViewState = new PlayerEntityViewState(kwdFile, app.getAssetManager(), gameClientService.getEntityData(), playerId, textParser, app.getRootNode()); + + // Attach the states + stateManager.attach(playerState); + stateManager.attach(playerMapViewState); + stateManager.attach(playerModelViewState); + + // Wait until loaded + if (!mapDataLoaded) { + synchronized (mapDataLoadingObject) { + if (!mapDataLoaded) { + try { + mapDataLoadingObject.wait(); + } catch (InterruptedException ex) { + System.getLogger(GameClientState.class.getName()).log(Level.ERROR, "Map data loading interrupted!", ex); + } + } + } + } + + // Loaded up, send our ready signal on the next frame to ensure all the states are attached + app.enqueue(() -> { + + // Prewarm the whole scene + app.getRenderManager().preloadScene(app.getRootNode()); + + // Signal our readiness + gameClientService.loadComplete(); + }); + + }, "GameDataClientLoader"); + loadingThread.start(); + } + + @Override + public void onLoadComplete(short keeperId) { + loadingState.setProgress(1f, keeperId); + } + + @Override + public void onLoadStatusUpdate(float progress, short keeperId) { + loadingState.setProgress(progress, keeperId); + } + + @Override + public void onGameStarted() { + + // Release loading state from memory + loadingState = null; + + stateManager.getState(SoundState.class).setKwdFile(kwdFile); + stateManager.getState(SoundState.class).setEnabled(true); + + app.enqueue(() -> { + playerState.setEnabled(true); + + // Release the lock and enter to the game phase + synchronized (loadingObject) { + gameStarted = true; + loadingObject.notifyAll(); + } + }); + } + + @Override + public void onTilesChange(List updatedTiles) { + //mapInformation.setTiles(updatedTiles); + //playerMapViewState.onTilesChange(updatedTiles); + } + + @Override + public void onGoldChange(short keeperId, int gold) { + getPlayer(keeperId).setGold(gold); + + // FIXME: See in what thread we are + if (playerState != null && playerState.getPlayerId() == keeperId) { + app.enqueue(() -> { + playerState.onGoldChange(keeperId, gold); + }); + } + } + + @Override + public void onManaChange(short keeperId, int mana, int manaLoose, int manaGain) { + Keeper keeper = getPlayer(keeperId); + keeper.setMana(mana); + keeper.setManaGain(manaGain); + keeper.setManaLoose(manaLoose); + + // FIXME: See in what thread we are + if (playerState != null && playerState.getPlayerId() == keeperId) { + app.enqueue(() -> { + playerState.onManaChange(keeperId, mana, manaLoose, manaGain); + }); + } + } + + @Override + public void onBuild(short keeperId, List tiles) { + playerMapViewState.onBuild(keeperId, tiles); + } + + @Override + public void onSold(short keeperId, List tiles) { + playerMapViewState.onSold(keeperId, tiles); + } + + @Override + public void onGamePaused() { + app.enqueue(() -> { + playerState.onPaused(true); + }); + } + + @Override + public void onGameResumed() { + app.enqueue(() -> { + playerState.onPaused(false); + }); + } + + @Override + public void onSetWidescreen(boolean enable) { + playerState.setWideScreen(enable); + } + + @Override + public void onPlaySpeech(int speechId, boolean showText, boolean introduction, int pathId) { + + // TODO: Refactor these, we don't maybe want this logic here, borderline visuals + stateManager.getState(SoundState.class).attachLevelSpeech(speechId, () -> { + stateManager.getState(SystemMessageState.class).addMessage(SystemMessageState.MessageType.INFO, String.format("${level.%d}", speechId - 1)); + if (showText) { + playerState.setText(speechId, introduction, pathId); + } + }); + } + + @Override + public void onDoTransition(short pathId, Vector3f start) { + + // TODO: Refactor + stateManager.getState(PlayerCameraState.class).doTransition(pathId, start, new CinematicEventListener() { + @Override + public void onPlay(CinematicEvent cinematic) { + + } + + @Override + public void onPause(CinematicEvent cinematic) { + + } + + @Override + public void onStop(CinematicEvent cinematic) { + gameClientService.transitionEnd(); + } + }); + } + + @Override + public void onFlashButton(TriggerAction.MakeType buttonType, short targetId, TriggerAction.ButtonType targetButtonType, boolean enabled, int time) { + playerState.flashButton(buttonType, targetId, targetButtonType, enabled, time); + } + + @Override + public void onRotateViewAroundPoint(Vector3f point, boolean relative, int angle, int time) { + playerState.rotateViewAroundPoint(point, relative, angle, time); + } + + @Override + public void onShowMessage(int textId) { + playerState.showMessage(textId); + } + + @Override + public void onZoomViewToPoint(Vector3f point) { + playerState.zoomToPoint(point); + } + + @Override + public void onTileFlash(List points, boolean enabled, short keeperId) { + playerMapViewState.onTileFlash(points, enabled, keeperId); + } + + @Override + public void onZoomViewToEntity(EntityId entityId) { + playerState.zoomToEntity(entityId, true); + } + + @Override + public void onShowUnitFlower(EntityId entityId, int interval) { + playerModelViewState.showUnitFlower(entityId, interval); + } + + @Override + public void onEntityAdded(short keeperId, ResearchableEntity researchableEntity) { + setResearchableEntity(keeperId, researchableEntity, () -> { + playerState.onEntityAdded(keeperId, researchableEntity); + }); + } + + private void setResearchableEntity(short keeperId, ResearchableEntity researchableEntity, Runnable notifier) { + Keeper keeper = getPlayer(keeperId); + setResearchableEntity(researchableEntity, getResearchableEntitiesList(keeper, researchableEntity)); + + // FIXME: See in what thread we are + if (notifier != null && playerState != null && playerState.getPlayerId() == keeperId) { + app.enqueue(notifier); + } + } + + @Override + public void onEntityRemoved(short keeperId, ResearchableEntity researchableEntity) { + setResearchableEntity(keeperId, researchableEntity, () -> { + playerState.onEntityRemoved(keeperId, researchableEntity); + }); + } + + @Override + public void onResearchStatusChanged(short keeperId, ResearchableEntity researchableEntity) { + setResearchableEntity(keeperId, researchableEntity, () -> { + playerState.onResearchStatusChanged(keeperId, researchableEntity); + }); + } + + private List getResearchableEntitiesList(Keeper keeper, ResearchableEntity researchableEntity) { + List researchableEntities = null; + switch (researchableEntity.getResearchableType()) { + case SPELL: { + researchableEntities = keeper.getAvailableSpells(); + break; + } + case DOOR: { + researchableEntities = keeper.getAvailableDoors(); + break; + } + case ROOM: { + researchableEntities = keeper.getAvailableRooms(); + break; + } + case TRAP: { + researchableEntities = keeper.getAvailableTraps(); + break; + } + } + + return researchableEntities; + } + + private void setResearchableEntity(T researchableEntity, List researchableEntities) { + int index = Collections.binarySearch(researchableEntities, researchableEntity, (ResearchableEntity o1, ResearchableEntity o2) -> { + return getResearchableEntityType(kwdFile, o1.getResearchableType(), o1.getId()).compareTo(getResearchableEntityType(kwdFile, o2.getResearchableType(), o2.getId())); + }); + if (index < 0) { + researchableEntities.add(~index, researchableEntity); + } else if (index >= 0) { + researchableEntities.set(index, researchableEntity); + } + } + + private Comparable getResearchableEntityType(KwdFile kwdFile, ResearchableType researchableType, short typeId) { + switch (researchableType) { + case DOOR: { + return kwdFile.getDoorById(typeId); + } + case ROOM: { + return kwdFile.getRoomById(typeId); + } + case SPELL: { + return kwdFile.getKeeperSpellById(typeId); + } + case TRAP: { + return kwdFile.getTrapById(typeId); + } + } + + return null; + } + + @Override + public void setPossession(EntityId target) { + playerState.setPossession(target); + } + } + + public IMapInformation getMapClientService() { + return mapInformation; + } + + public GameSessionClientService getGameClientService() { + return gameClientService; + } + +} diff --git a/src/toniarts/openkeeper/game/state/GameServerState.java b/src/toniarts/openkeeper/game/state/GameServerState.java index 3f0273cd..e756f333 100644 --- a/src/toniarts/openkeeper/game/state/GameServerState.java +++ b/src/toniarts/openkeeper/game/state/GameServerState.java @@ -1,404 +1,404 @@ -/* - * Copyright (C) 2014-2017 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.game.state; - -import com.jme3.app.Application; -import com.jme3.app.state.AbstractAppState; -import com.jme3.app.state.AppStateManager; -import com.jme3.math.Vector2f; -import com.simsilica.es.EntityId; -import java.awt.Point; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.List; -import toniarts.openkeeper.Main; -import toniarts.openkeeper.game.controller.GameController; -import toniarts.openkeeper.game.controller.IGameWorldController; -import toniarts.openkeeper.game.controller.IMapController; -import toniarts.openkeeper.game.controller.IPlayerController; -import toniarts.openkeeper.game.controller.player.PlayerDoorControl; -import toniarts.openkeeper.game.controller.player.PlayerRoomControl; -import toniarts.openkeeper.game.controller.player.PlayerSpellControl; -import toniarts.openkeeper.game.controller.player.PlayerTrapControl; -import toniarts.openkeeper.game.data.Keeper; -import toniarts.openkeeper.game.listener.MapListener; -import toniarts.openkeeper.game.listener.PlayerActionListener; -import toniarts.openkeeper.game.state.session.GameSessionServerService; -import toniarts.openkeeper.game.state.session.GameSessionServiceListener; -import toniarts.openkeeper.tools.convert.map.Door; -import toniarts.openkeeper.tools.convert.map.KeeperSpell; -import toniarts.openkeeper.tools.convert.map.KwdFile; -import toniarts.openkeeper.tools.convert.map.Room; -import toniarts.openkeeper.tools.convert.map.Trap; -import toniarts.openkeeper.utils.Utils; - -/** - * The game state that actually runs the game. Has no relation to visuals. - * - * @author Toni Helenius - */ -public class GameServerState extends AbstractAppState { - - private static final Logger logger = System.getLogger(GameServerState.class.getName()); - - private Main app; - - private Thread loader; - private AppStateManager stateManager; - - private final Object loadingObject = new Object(); - private volatile boolean gameLoaded = false; - - private final String level; - private final KwdFile kwdFile; - private final toniarts.openkeeper.game.data.Level levelObject; - - private final boolean campaign; - private final boolean multiplayer; - private final GameSessionServerService gameService; - private IMapController mapController; - private final MapListener mapListener = new MapListenerImpl(); - private final GameSessionServiceListener gameSessionListener = new GameSessionServiceListenerImpl(); - private final PlayerActionListener playerActionListener = new PlayerActionListenerImpl(); - private GameController gameController; - private IGameWorldController gameWorldController; - - /** - * Single use game states - * - * @param level the level to load - * @param players players participating in this game - * @param campaign whether this is a campaign level or not - * @param gameService the game service - */ - public GameServerState(KwdFile level, List players, boolean campaign, GameSessionServerService gameService) { - this.level = null; - this.kwdFile = level; - this.levelObject = null; - this.campaign = campaign; - this.gameService = gameService; - - // Set multiplayer - int humanPlayers = 0; - if (players != null) { - for (Keeper player : players) { - if (!player.isAi()) { - humanPlayers++; - if (humanPlayers > 1) { - break; - } - } - } - } else { - humanPlayers = 1; - } - multiplayer = (humanPlayers > 1); - - // Add the listener - gameService.addGameSessionServiceListener(gameSessionListener); - - // Start loading game - loadGame(players); - } - - public boolean isMultiplayer() { - return multiplayer; - } - - private void loadGame(List players) { - loader = new GameLoader(players); - loader.start(); - } - - @Override - public void initialize(final AppStateManager stateManager, final Application app) { - this.app = (Main) app; - this.stateManager = stateManager; - } - - /** - * If you are getting rid of the game state, use this so that all the - * related states are detached on the same render loop. Otherwise the app - * might crash. - */ - public void detach() { - if (loader != null && loader.isAlive()) { - loader.interrupt(); - } - stateManager.detach(this); - - if (gameController != null) { - try { - gameController.close(); - } catch (Exception ex) { - logger.log(Level.ERROR, "Failed to close the game!", ex); - } - } - } - - @Override - public void cleanup() { - - // Detach - detach(); - - super.cleanup(); - } - - /** - * Load the game - */ - private class GameLoader extends Thread { - - private final List players; - - public GameLoader(List players) { - super("GameDataServerLoader"); - - this.players = players; - } - - @Override - public void run() { - - // Make sure the KWD file is fully loaded - kwdFile.load(); - - // Create the central game controller - gameController = new GameController(kwdFile, players, gameService.getEntityData(), kwdFile.getVariables(), gameService); - gameController.createNewGame(); - - gameWorldController = gameController.getGameWorldController(); - mapController = gameWorldController.getMapController(); - gameWorldController.addListener(playerActionListener); - - // Send the the initial game data - gameService.sendGameData(gameController.getPlayers()); - - // Set up a listener for the map - mapController.addListener(mapListener); - - // Set up a listener for the player changes, they are per player - for (IPlayerController playerController : gameController.getPlayerControllers()) { - playerController.addListener(gameService); - } - - // Nullify the thread object - loader = null; - - // Mark the server end ready - synchronized (loadingObject) { - gameLoaded = true; - loadingObject.notifyAll(); - } - } - } - - /** - * Listen for basically clients' requests - */ - private class GameSessionServiceListenerImpl implements GameSessionServiceListener { - - @Override - public void onStartGame() { - - // Just make sure that the server has fully set up itself before we start the game - if (!gameLoaded) { - synchronized (loadingObject) { - if (!gameLoaded) { - try { - loadingObject.wait(); - } catch (InterruptedException ex) { - logger.log(Level.ERROR, "Failed to load the game.", ex); - } - } - } - } - - // Start the actual game - gameController.startGame(); - } - - @Override - public void onSelectTiles(Vector2f start, Vector2f end, boolean select, short playerId) { - mapController.selectTiles(start, end, select, playerId); - } - - @Override - public void onBuild(Vector2f start, Vector2f end, short roomId, short playerId) { - gameWorldController.build(start, end, playerId, roomId); - } - - @Override - public void onSell(Vector2f start, Vector2f end, short playerId) { - gameWorldController.sell(start, end, playerId); - } - - @Override - public void onInteract(EntityId entity, short playerId) { - gameWorldController.interact(entity, playerId); - } - - @Override - public void onPickUp(EntityId entity, short playerId) { - gameWorldController.pickUp(entity, playerId); - } - - @Override - public void onDrop(EntityId entity, Point tile, Vector2f coordinates, EntityId dropOnEntity, short playerId) { - gameWorldController.drop(entity, tile, coordinates, dropOnEntity, playerId); - } - - @Override - public void onCastKeeperSpell(short keeperSpellId, EntityId target, Point tile, Vector2f position, short playerId) { - gameWorldController.castKeeperSpell(keeperSpellId, target, tile, position, playerId); - } - - @Override - public void onPlaceDoor(short doorId, Point tile, short playerId) { - gameWorldController.placeDoor(doorId, tile, playerId); - } - - @Override - public void onPlaceTrap(short trapId, Point tile, short playerId) { - gameWorldController.placeTrap(trapId, tile, playerId); - } - - @Override - public void onTransitionEnd(short playerId) { - // We are not really interested in this, the status is also tracked in the local clients - } - - @Override - public void onPauseRequest(short playerId) { - // TODO: We should only allow the server owner etc. to pause, otherwise, send a system message that player x wants to pause? - gameController.pauseGame(); - - } - - @Override - public void onResumeRequest(short playerId) { - // TODO: We should only allow the server owner etc. to pause, otherwise, send a system message that player x wants to pause? - gameController.resumeGame(); - } - - @Override - public void onExitGame(short playerId) { - // TODO: Close the server and game only when everybody has left - stateManager.detach(GameServerState.this); - } - - @Override - public void onGetGold(int amount, short playerId) { - gameWorldController.getGold(amount, playerId); - } - - @Override - public void onCheatTriggered(CheatState.CheatType cheat, short playerId) { - if (isMultiplayer()) { - return; // No! Bad! - } - - // See the cheat - switch (cheat) { - case LEVEL_MAX: { - gameWorldController.getCreaturesController().levelUpCreatures(playerId, Utils.MAX_CREATURE_LEVEL); - break; - } - case MANA: { - gameController.getPlayerController(playerId).getManaControl().addMana(100000); - break; - } - case MONEY: { - gameWorldController.addGold(playerId, 100000); - break; - } - case REMOVE_FOW: { - // TODO: - break; - } - case UNLOCK_ROOMS: { - PlayerRoomControl playerRoomControl = gameController.getPlayerController(playerId).getRoomControl(); - for (Room room : kwdFile.getRooms()) { - playerRoomControl.setTypeAvailable(room, true); - } - break; - } - case UNLOCK_DOORS_TRAPS: { - PlayerDoorControl playerDoorControl = gameController.getPlayerController(playerId).getDoorControl(); - for (Door door : kwdFile.getDoors()) { - playerDoorControl.setTypeAvailable(door, true); - } - - PlayerTrapControl playerTrapControl = gameController.getPlayerController(playerId).getTrapControl(); - for (Trap trap : kwdFile.getTraps()) { - playerTrapControl.setTypeAvailable(trap, true); - } - break; - } - case UNLOCK_SPELLS: { - PlayerSpellControl playerSpellControl = gameController.getPlayerController(playerId).getSpellControl(); - for (KeeperSpell keeperSpell : kwdFile.getKeeperSpells()) { - playerSpellControl.setTypeAvailable(keeperSpell, true); - playerSpellControl.setSpellDiscovered(keeperSpell, true); - } - break; - } - case WIN_LEVEL: { - gameController.endGame(playerId, true); - break; - } - default: - logger.log(Level.INFO, "Cheat {0} not implemented!", cheat); - } - } - } - - /** - * Listen for the map changes - */ - private class MapListenerImpl implements MapListener { - - @Override - public void onTilesChange(List updatedTiles) { - gameService.updateTiles(updatedTiles); - } - - @Override - public void onTileFlash(List points, boolean enabled, short keeperId) { - gameService.flashTiles(points, enabled, keeperId); - } - } - - /** - * Listen for the player actions - */ - private class PlayerActionListenerImpl implements PlayerActionListener { - - @Override - public void onBuild(short keeperId, List tiles) { - gameService.onBuild(keeperId, tiles); - } - - @Override - public void onSold(short keeperId, List tiles) { - gameService.onSold(keeperId, tiles); - } - - } - -} +/* + * Copyright (C) 2014-2017 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.game.state; + +import com.jme3.app.Application; +import com.jme3.app.state.AbstractAppState; +import com.jme3.app.state.AppStateManager; +import com.jme3.math.Vector2f; +import com.simsilica.es.EntityId; +import java.awt.Point; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.List; +import toniarts.openkeeper.Main; +import toniarts.openkeeper.game.controller.GameController; +import toniarts.openkeeper.game.controller.IGameWorldController; +import toniarts.openkeeper.game.controller.IMapController; +import toniarts.openkeeper.game.controller.IPlayerController; +import toniarts.openkeeper.game.controller.player.PlayerDoorControl; +import toniarts.openkeeper.game.controller.player.PlayerRoomControl; +import toniarts.openkeeper.game.controller.player.PlayerSpellControl; +import toniarts.openkeeper.game.controller.player.PlayerTrapControl; +import toniarts.openkeeper.game.data.Keeper; +import toniarts.openkeeper.game.listener.MapListener; +import toniarts.openkeeper.game.listener.PlayerActionListener; +import toniarts.openkeeper.game.state.session.GameSessionServerService; +import toniarts.openkeeper.game.state.session.GameSessionServiceListener; +import toniarts.openkeeper.tools.convert.map.Door; +import toniarts.openkeeper.tools.convert.map.KeeperSpell; +import toniarts.openkeeper.tools.convert.map.KwdFile; +import toniarts.openkeeper.tools.convert.map.Room; +import toniarts.openkeeper.tools.convert.map.Trap; +import toniarts.openkeeper.utils.Utils; + +/** + * The game state that actually runs the game. Has no relation to visuals. + * + * @author Toni Helenius + */ +public class GameServerState extends AbstractAppState { + + private static final Logger logger = System.getLogger(GameServerState.class.getName()); + + private Main app; + + private Thread loader; + private AppStateManager stateManager; + + private final Object loadingObject = new Object(); + private volatile boolean gameLoaded = false; + + private final String level; + private final KwdFile kwdFile; + private final toniarts.openkeeper.game.data.Level levelObject; + + private final boolean campaign; + private final boolean multiplayer; + private final GameSessionServerService gameService; + private IMapController mapController; + private final MapListener mapListener = new MapListenerImpl(); + private final GameSessionServiceListener gameSessionListener = new GameSessionServiceListenerImpl(); + private final PlayerActionListener playerActionListener = new PlayerActionListenerImpl(); + private GameController gameController; + private IGameWorldController gameWorldController; + + /** + * Single use game states + * + * @param level the level to load + * @param players players participating in this game + * @param campaign whether this is a campaign level or not + * @param gameService the game service + */ + public GameServerState(KwdFile level, List players, boolean campaign, GameSessionServerService gameService) { + this.level = null; + this.kwdFile = level; + this.levelObject = null; + this.campaign = campaign; + this.gameService = gameService; + + // Set multiplayer + int humanPlayers = 0; + if (players != null) { + for (Keeper player : players) { + if (!player.isAi()) { + humanPlayers++; + if (humanPlayers > 1) { + break; + } + } + } + } else { + humanPlayers = 1; + } + multiplayer = (humanPlayers > 1); + + // Add the listener + gameService.addGameSessionServiceListener(gameSessionListener); + + // Start loading game + loadGame(players); + } + + public boolean isMultiplayer() { + return multiplayer; + } + + private void loadGame(List players) { + loader = new GameLoader(players); + loader.start(); + } + + @Override + public void initialize(final AppStateManager stateManager, final Application app) { + this.app = (Main) app; + this.stateManager = stateManager; + } + + /** + * If you are getting rid of the game state, use this so that all the + * related states are detached on the same render loop. Otherwise the app + * might crash. + */ + public void detach() { + if (loader != null && loader.isAlive()) { + loader.interrupt(); + } + stateManager.detach(this); + + if (gameController != null) { + try { + gameController.close(); + } catch (Exception ex) { + logger.log(Level.ERROR, "Failed to close the game!", ex); + } + } + } + + @Override + public void cleanup() { + + // Detach + detach(); + + super.cleanup(); + } + + /** + * Load the game + */ + private class GameLoader extends Thread { + + private final List players; + + public GameLoader(List players) { + super("GameDataServerLoader"); + + this.players = players; + } + + @Override + public void run() { + + // Make sure the KWD file is fully loaded + kwdFile.load(); + + // Create the central game controller + gameController = new GameController(kwdFile, players, gameService.getEntityData(), kwdFile.getVariables(), gameService); + gameController.createNewGame(); + + gameWorldController = gameController.getGameWorldController(); + mapController = gameWorldController.getMapController(); + gameWorldController.addListener(playerActionListener); + + // Send the the initial game data + gameService.sendGameData(gameController.getPlayers()); + + // Set up a listener for the map + mapController.addListener(mapListener); + + // Set up a listener for the player changes, they are per player + for (IPlayerController playerController : gameController.getPlayerControllers()) { + playerController.addListener(gameService); + } + + // Nullify the thread object + loader = null; + + // Mark the server end ready + synchronized (loadingObject) { + gameLoaded = true; + loadingObject.notifyAll(); + } + } + } + + /** + * Listen for basically clients' requests + */ + private class GameSessionServiceListenerImpl implements GameSessionServiceListener { + + @Override + public void onStartGame() { + + // Just make sure that the server has fully set up itself before we start the game + if (!gameLoaded) { + synchronized (loadingObject) { + if (!gameLoaded) { + try { + loadingObject.wait(); + } catch (InterruptedException ex) { + logger.log(Level.ERROR, "Failed to load the game.", ex); + } + } + } + } + + // Start the actual game + gameController.startGame(); + } + + @Override + public void onSelectTiles(Vector2f start, Vector2f end, boolean select, short playerId) { + mapController.selectTiles(start, end, select, playerId); + } + + @Override + public void onBuild(Vector2f start, Vector2f end, short roomId, short playerId) { + gameWorldController.build(start, end, playerId, roomId); + } + + @Override + public void onSell(Vector2f start, Vector2f end, short playerId) { + gameWorldController.sell(start, end, playerId); + } + + @Override + public void onInteract(EntityId entity, short playerId) { + gameWorldController.interact(entity, playerId); + } + + @Override + public void onPickUp(EntityId entity, short playerId) { + gameWorldController.pickUp(entity, playerId); + } + + @Override + public void onDrop(EntityId entity, Point tile, Vector2f coordinates, EntityId dropOnEntity, short playerId) { + gameWorldController.drop(entity, tile, coordinates, dropOnEntity, playerId); + } + + @Override + public void onCastKeeperSpell(short keeperSpellId, EntityId target, Point tile, Vector2f position, short playerId) { + gameWorldController.castKeeperSpell(keeperSpellId, target, tile, position, playerId); + } + + @Override + public void onPlaceDoor(short doorId, Point tile, short playerId) { + gameWorldController.placeDoor(doorId, tile, playerId); + } + + @Override + public void onPlaceTrap(short trapId, Point tile, short playerId) { + gameWorldController.placeTrap(trapId, tile, playerId); + } + + @Override + public void onTransitionEnd(short playerId) { + // We are not really interested in this, the status is also tracked in the local clients + } + + @Override + public void onPauseRequest(short playerId) { + // TODO: We should only allow the server owner etc. to pause, otherwise, send a system message that player x wants to pause? + gameController.pauseGame(); + + } + + @Override + public void onResumeRequest(short playerId) { + // TODO: We should only allow the server owner etc. to pause, otherwise, send a system message that player x wants to pause? + gameController.resumeGame(); + } + + @Override + public void onExitGame(short playerId) { + // TODO: Close the server and game only when everybody has left + stateManager.detach(GameServerState.this); + } + + @Override + public void onGetGold(int amount, short playerId) { + gameWorldController.getGold(amount, playerId); + } + + @Override + public void onCheatTriggered(CheatState.CheatType cheat, short playerId) { + if (isMultiplayer()) { + return; // No! Bad! + } + + // See the cheat + switch (cheat) { + case LEVEL_MAX: { + gameWorldController.getCreaturesController().levelUpCreatures(playerId, Utils.MAX_CREATURE_LEVEL); + break; + } + case MANA: { + gameController.getPlayerController(playerId).getManaControl().addMana(100000); + break; + } + case MONEY: { + gameWorldController.addGold(playerId, 100000); + break; + } + case REMOVE_FOW: { + // TODO: + break; + } + case UNLOCK_ROOMS: { + PlayerRoomControl playerRoomControl = gameController.getPlayerController(playerId).getRoomControl(); + for (Room room : kwdFile.getRooms()) { + playerRoomControl.setTypeAvailable(room, true); + } + break; + } + case UNLOCK_DOORS_TRAPS: { + PlayerDoorControl playerDoorControl = gameController.getPlayerController(playerId).getDoorControl(); + for (Door door : kwdFile.getDoors()) { + playerDoorControl.setTypeAvailable(door, true); + } + + PlayerTrapControl playerTrapControl = gameController.getPlayerController(playerId).getTrapControl(); + for (Trap trap : kwdFile.getTraps()) { + playerTrapControl.setTypeAvailable(trap, true); + } + break; + } + case UNLOCK_SPELLS: { + PlayerSpellControl playerSpellControl = gameController.getPlayerController(playerId).getSpellControl(); + for (KeeperSpell keeperSpell : kwdFile.getKeeperSpells()) { + playerSpellControl.setTypeAvailable(keeperSpell, true); + playerSpellControl.setSpellDiscovered(keeperSpell, true); + } + break; + } + case WIN_LEVEL: { + gameController.endGame(playerId, true); + break; + } + default: + logger.log(Level.INFO, "Cheat {0} not implemented!", cheat); + } + } + } + + /** + * Listen for the map changes + */ + private class MapListenerImpl implements MapListener { + + @Override + public void onTilesChange(List updatedTiles) { + gameService.updateTiles(updatedTiles); + } + + @Override + public void onTileFlash(List points, boolean enabled, short keeperId) { + gameService.flashTiles(points, enabled, keeperId); + } + } + + /** + * Listen for the player actions + */ + private class PlayerActionListenerImpl implements PlayerActionListener { + + @Override + public void onBuild(short keeperId, List tiles) { + gameService.onBuild(keeperId, tiles); + } + + @Override + public void onSold(short keeperId, List tiles) { + gameService.onSold(keeperId, tiles); + } + + } + +} diff --git a/src/toniarts/openkeeper/game/state/MainMenuState.java b/src/toniarts/openkeeper/game/state/MainMenuState.java index 03306b7f..1e4a9e78 100644 --- a/src/toniarts/openkeeper/game/state/MainMenuState.java +++ b/src/toniarts/openkeeper/game/state/MainMenuState.java @@ -162,7 +162,7 @@ protected void updateProgress(float progress) { if (loadingScreen != null) { loadingScreen.setProgress(1.0f); } - mainMenuEntityViewState = new MainMenuEntityViewState(kwdFile, assetManager, mainMenuEntityData, Player.KEEPER1_ID, new TextParserService(gameController.getGameWorldController().getMapController()), menuNode); + mainMenuEntityViewState = new MainMenuEntityViewState(kwdFile, assetManager, mainMenuEntityData, Player.KEEPER1_ID, new TextParserService(gameController.getGameWorldController().getMapController(), null), menuNode); mainMenuEntityViewState.setEnabled(false); app.getStateManager().attach(mainMenuEntityViewState); diff --git a/src/toniarts/openkeeper/view/PlayerEntityViewState.java b/src/toniarts/openkeeper/view/PlayerEntityViewState.java index f78abea4..8fee6266 100644 --- a/src/toniarts/openkeeper/view/PlayerEntityViewState.java +++ b/src/toniarts/openkeeper/view/PlayerEntityViewState.java @@ -192,7 +192,7 @@ private Spatial createObjectModel(Entity e) { if (objectViewState != null) { result = objectLoader.load(assetManager, objectViewState); if (result != null) { - EntityViewControl control = new ObjectViewControl(e.getId(), entityData, kwdFile.getObject(objectViewState.objectId), objectViewState, assetManager, textParser.getObjectTextParser()); + EntityViewControl control = new ObjectViewControl(e.getId(), entityData, kwdFile.getObject(objectViewState.objectId), objectViewState, assetManager, textParser != null ? textParser.getObjectTextParser() : null); result.addControl(control); result.setCullHint(objectViewState.visible ? Spatial.CullHint.Inherit : Spatial.CullHint.Always); @@ -222,7 +222,7 @@ private Spatial createCreatureModel(Entity e) { Creature creature = kwdFile.getCreature(creatureViewState.creatureId); result = creatureLoader.load(assetManager, creatureViewState); if (result != null) { - EntityViewControl control = new CreatureViewControl(e.getId(), entityData, creature, creatureViewState.state, assetManager, textParser.getCreatureTextParser()); + EntityViewControl control = new CreatureViewControl(e.getId(), entityData, creature, creatureViewState.state, assetManager, textParser != null ? textParser.getCreatureTextParser() : null); result.addControl(control); CreatureFlowerControl flowerControl = new CreatureFlowerControl(e.getId(), entityData, creature, assetManager); @@ -253,7 +253,7 @@ private Spatial createDoorModel(Entity e) { if (doorViewState != null) { Door door = kwdFile.getDoorById(doorViewState.doorId); result = doorLoader.load(assetManager, doorViewState); - EntityViewControl control = new DoorViewControl(e.getId(), entityData, door, doorViewState, assetManager, textParser.getDoorTextParser(), kwdFile.getObject(door.getKeyObjectId())); + EntityViewControl control = new DoorViewControl(e.getId(), entityData, door, doorViewState, assetManager, textParser != null ? textParser.getDoorTextParser() : null, kwdFile.getObject(door.getKeyObjectId())); result.addControl(control); DoorFlowerControl flowerControl = new DoorFlowerControl(e.getId(), entityData, door, assetManager); @@ -283,7 +283,7 @@ private Spatial createTrapModel(Entity e) { if (trapViewState != null) { Trap trap = kwdFile.getTrapById(trapViewState.trapId); result = trapLoader.load(assetManager, trapViewState); - EntityViewControl control = new TrapViewControl(e.getId(), entityData, trap, trapViewState, assetManager, textParser.getTrapTextParser()); + EntityViewControl control = new TrapViewControl(e.getId(), entityData, trap, trapViewState, assetManager, textParser != null ? textParser.getTrapTextParser() : null); result.addControl(control); TrapFlowerControl flowerControl = new TrapFlowerControl(e.getId(), entityData, trap, assetManager); diff --git a/src/toniarts/openkeeper/view/PlayerInteractionState.java b/src/toniarts/openkeeper/view/PlayerInteractionState.java index a398932e..5e70625c 100644 --- a/src/toniarts/openkeeper/view/PlayerInteractionState.java +++ b/src/toniarts/openkeeper/view/PlayerInteractionState.java @@ -398,7 +398,7 @@ private boolean isInteractable() { //RoomInstance roomInstance = getWorldHandler().getMapLoader().getRoomCoordinates().get(new Point((int) p.x, (int) p.y)); //GenericRoom room = getWorldHandler().getMapLoader().getRoomActuals().get(roomInstance); //tooltip.setText(room.getTooltip(player.getPlayerId())); - tooltip.setText(""); + tooltip.setText(textParser.getRoomTextParser().parseText(Utils.getMainTextResourceBundle().getString(Integer.toString(kwdFile.getRoomByTerrain(terrain.getTerrainId()).getTooltipStringId())), tile.getRoomId())); } else { tooltip.setText(textParser.getMapTileTextParser().parseText(Utils.getMainTextResourceBundle().getString(Integer.toString(terrain.getTooltipStringId())), tile)); } diff --git a/src/toniarts/openkeeper/view/PlayerMapViewState.java b/src/toniarts/openkeeper/view/PlayerMapViewState.java index 8db6840d..c454e522 100644 --- a/src/toniarts/openkeeper/view/PlayerMapViewState.java +++ b/src/toniarts/openkeeper/view/PlayerMapViewState.java @@ -22,34 +22,24 @@ import com.jme3.asset.AssetManager; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.simsilica.es.Entity; -import com.simsilica.es.EntityComponent; -import com.simsilica.es.EntityContainer; import com.simsilica.es.EntityData; import java.awt.Point; import java.lang.System.Logger; -import java.lang.System.Logger.Level; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Set; import toniarts.openkeeper.Main; -import toniarts.openkeeper.game.component.Gold; -import toniarts.openkeeper.game.component.Health; -import toniarts.openkeeper.game.component.Mana; -import toniarts.openkeeper.game.component.MapTile; -import toniarts.openkeeper.game.component.Owner; import toniarts.openkeeper.game.data.Keeper; import toniarts.openkeeper.game.listener.MapListener; import toniarts.openkeeper.game.listener.PlayerActionListener; -import toniarts.openkeeper.game.map.AbstractMapTileInformation; -import toniarts.openkeeper.game.map.IMapDataInformation; import toniarts.openkeeper.game.map.IMapInformation; -import toniarts.openkeeper.game.map.IMapTileInformation; +import toniarts.openkeeper.game.map.IRoomsInformation; import toniarts.openkeeper.game.map.MapInformation; import toniarts.openkeeper.tools.convert.map.KwdFile; import toniarts.openkeeper.tools.modelviewer.Debug; import toniarts.openkeeper.view.map.FlashTileViewState; +import toniarts.openkeeper.view.map.MapRoomContainer; +import toniarts.openkeeper.view.map.MapTileContainer; import toniarts.openkeeper.view.map.MapViewController; import toniarts.openkeeper.world.effect.EffectManagerState; import toniarts.openkeeper.world.listener.RoomListener; @@ -76,6 +66,7 @@ public abstract class PlayerMapViewState extends AbstractAppState implements Map private List tileChangeListener; private Map> roomListeners; private final FlashTileViewState flashTileControl; + private final MapRoomContainer mapRoomContainer; public PlayerMapViewState(Main app, final KwdFile kwdFile, final AssetManager assetManager, Collection players, EntityData entityData, short playerId, ILoadCompleteNotifier loadCompleteNotifier) { this.app = app; @@ -88,8 +79,11 @@ public PlayerMapViewState(Main app, final KwdFile kwdFile, final AssetManager as Debug.showNodeAxes(assetManager, worldNode, 10); } + // Load and update rooms + mapRoomContainer = new MapRoomContainer(entityData, kwdFile); + // Make sure we load the whole map before we continue - this.mapTileContainer = new MapTileContainer(entityData, kwdFile) { + mapTileContainer = new MapTileContainer(entityData, kwdFile, this::updateTiles) { @Override protected void onLoadComplete() { @@ -109,13 +103,13 @@ protected void onLoadComplete() { }; - this.mapInformation = new MapInformation(mapTileContainer, kwdFile, players); + mapInformation = new MapInformation(mapTileContainer, kwdFile, players); // Effect manager effectManager = new EffectManagerState(kwdFile, assetManager); // Create the actual map - this.mapLoader = new MapViewController(assetManager, kwdFile, mapInformation, playerId) { + mapLoader = new MapViewController(assetManager, kwdFile, mapInformation, playerId) { @Override protected void updateProgress(float progress) { @@ -124,10 +118,11 @@ protected void updateProgress(float progress) { }; - this.flashTileControl = new FlashTileViewState(mapLoader); + flashTileControl = new FlashTileViewState(mapLoader); // Start collecting the map entities - this.mapTileContainer.start(); + mapRoomContainer.start(); + mapTileContainer.start(); } @Override @@ -156,20 +151,24 @@ public void cleanup() { } // Tile flash state - this.stateManager.detach(flashTileControl); + stateManager.detach(flashTileControl); // Effects - this.stateManager.detach(effectManager); + stateManager.detach(effectManager); // The actual map data - this.mapTileContainer.stop(); + mapRoomContainer.stop(); + mapTileContainer.stop(); super.cleanup(); } @Override public void update(float tpf) { - this.mapTileContainer.update(); + + // Always process rooms before the map tiles + mapRoomContainer.update(); + mapTileContainer.update(); } public AssetManager getAssetManager() { @@ -227,130 +226,17 @@ public IMapInformation getMapInformation() { return mapInformation; } - public interface ILoadCompleteNotifier { - - void onLoadComplete(); - + private void updateTiles(Point[] points) { + mapLoader.updateTiles(points); } - /** - * Contains the map tiles - */ - private abstract class MapTileContainer extends EntityContainer implements IMapDataInformation { - - private final int width; - private final int height; - private final IMapTileInformation[][] tiles; - - private int tilesAdded = 0; - - public MapTileContainer(EntityData entityData, KwdFile kwdFile) { - super(entityData, MapTile.class, Owner.class, Health.class, Gold.class, Mana.class); - - width = kwdFile.getMap().getWidth(); - height = kwdFile.getMap().getHeight(); - - // Duplicate the map - this.tiles = new IMapTileInformation[width][height]; - } - - @Override - protected IMapTileInformation addObject(Entity e) { - logger.log(Level.TRACE, "MapTileContainer.addObject({0})", e); - - IMapTileInformation result = new MapTileInformation(e); - Point p = result.getLocation(); - this.tiles[p.x][p.y] = result; - - // Naive completion checker - tilesAdded++; - if (tilesAdded == getSize()) { - onLoadComplete(); - } - - return result; - } - - @Override - protected void updateObjects(Set set) { - if (set.isEmpty()) { - return; - } - - logger.log(Level.TRACE, "MapTileContainer.updateObjects({0})", set.size()); - - // Collect the tiles - Point[] updatableTiles = new Point[set.size()]; - int i = 0; - for (Entity e : set) { - IMapTileInformation object = getObject(e.getId()); - if (object == null) { - logger.log(Level.WARNING, "Update: No matching object for entity:{0}", e); - continue; - } - updatableTiles[i] = object.getLocation(); - i++; - } - - // Update the batch - mapLoader.updateTiles(updatableTiles); - } - - @Override - protected void updateObject(IMapTileInformation object, Entity e) { - throw new UnsupportedOperationException("Should use the batch method."); - - } - - @Override - protected void removeObject(IMapTileInformation object, Entity e) { - logger.log(Level.TRACE, "MapTileContainer.removeObject({0})", e); - } - - @Override - public int getHeight() { - return height; - } - - @Override - public IMapTileInformation getTile(int x, int y) { - if (x < 0 || y < 0 || x >= width || y >= height) { - return null; - } - return this.tiles[x][y]; - } - - @Override - public int getWidth() { - return width; - } - - @Override - public void setTiles(List mapTiles) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - protected abstract void onLoadComplete(); - + public IRoomsInformation getRoomsInformation() { + return mapRoomContainer; } - /** - * Single map tile that taps into the entity information - */ - private static class MapTileInformation extends AbstractMapTileInformation { - - private final Entity entity; - - public MapTileInformation(Entity entity) { - super(entity.getId()); - - this.entity = entity; - } + public interface ILoadCompleteNotifier { - @Override - protected T getEntityComponent(Class type) { - return entity.get(type); - } + void onLoadComplete(); } diff --git a/src/toniarts/openkeeper/view/map/MapRoomContainer.java b/src/toniarts/openkeeper/view/map/MapRoomContainer.java new file mode 100644 index 00000000..e74466f6 --- /dev/null +++ b/src/toniarts/openkeeper/view/map/MapRoomContainer.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014-2024 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.view.map; + +import com.simsilica.es.Entity; +import com.simsilica.es.EntityComponent; +import com.simsilica.es.EntityContainer; +import com.simsilica.es.EntityData; +import com.simsilica.es.EntityId; +import java.lang.System.Logger.Level; +import java.util.HashMap; +import java.util.Map; +import toniarts.openkeeper.game.component.Health; +import toniarts.openkeeper.game.component.Owner; +import toniarts.openkeeper.game.component.RoomComponent; +import toniarts.openkeeper.game.map.AbstractRoomInformation; +import toniarts.openkeeper.game.map.IRoomInformation; +import toniarts.openkeeper.game.map.IRoomsInformation; +import toniarts.openkeeper.tools.convert.map.KwdFile; + +/** + * Contains the map rooms + */ +public class MapRoomContainer extends EntityContainer implements IRoomsInformation { + + private static final System.Logger logger = System.getLogger(MapRoomContainer.class.getName()); + + private final Map roomMap = new HashMap<>(); + + public MapRoomContainer(EntityData entityData, KwdFile kwdFile) { + super(entityData, RoomComponent.class, Owner.class, Health.class); + } + + @Override + protected IRoomInformation addObject(Entity e) { + logger.log(Level.TRACE, "MapRoomContainer.addObject({0})", e); + IRoomInformation result = new RoomInformation(e); + roomMap.put(e.getId(), result); + + return result; + } + + @Override + protected void updateObject(IRoomInformation object, Entity e) { + logger.log(System.Logger.Level.TRACE, "MapRoomContainer.updateObject({0}, {1})", object, e); + } + + @Override + protected void removeObject(IRoomInformation object, Entity e) { + logger.log(Level.TRACE, "MapTileContainer.removeObject({0})", e); + roomMap.remove(e.getId()); + } + + @Override + public boolean update() { + return super.update(); + + // Also update the room capacity catalogs + } + + @Override + public void stop() { + super.stop(); + + // Also free the room capacity catalog updates + } + + @Override + public IRoomInformation getRoomInformation(EntityId entityId) { + return roomMap.get(entityId); + } + + /** + * Single map tile that taps into the entity information + */ + private static class RoomInformation extends AbstractRoomInformation { + + private final Entity entity; + + public RoomInformation(Entity entity) { + super(entity.getId()); + + this.entity = entity; + } + + @Override + protected T getEntityComponent(Class type) { + return entity.get(type); + } + + } + +} diff --git a/src/toniarts/openkeeper/view/map/MapTileContainer.java b/src/toniarts/openkeeper/view/map/MapTileContainer.java new file mode 100644 index 00000000..be845218 --- /dev/null +++ b/src/toniarts/openkeeper/view/map/MapTileContainer.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2014-2024 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.view.map; + +import com.simsilica.es.Entity; +import com.simsilica.es.EntityComponent; +import com.simsilica.es.EntityContainer; +import com.simsilica.es.EntityData; +import java.awt.Point; +import java.lang.System.Logger.Level; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import toniarts.openkeeper.game.component.Gold; +import toniarts.openkeeper.game.component.Health; +import toniarts.openkeeper.game.component.Mana; +import toniarts.openkeeper.game.component.MapTile; +import toniarts.openkeeper.game.component.Owner; +import toniarts.openkeeper.game.map.AbstractMapTileInformation; +import toniarts.openkeeper.game.map.IMapDataInformation; +import toniarts.openkeeper.game.map.IMapTileInformation; +import toniarts.openkeeper.tools.convert.map.KwdFile; + +/** + * Contains the map tiles + */ +public abstract class MapTileContainer extends EntityContainer implements IMapDataInformation { + + private static final System.Logger logger = System.getLogger(MapTileContainer.class.getName()); + + private final int width; + private final int height; + private final IMapTileInformation[][] tiles; + private final Consumer tileUpdateCallback; + private int tilesAdded = 0; + + public MapTileContainer(EntityData entityData, KwdFile kwdFile, Consumer tileUpdateCallback) { + super(entityData, MapTile.class, Owner.class, Health.class, Gold.class, Mana.class); + + this.tileUpdateCallback = tileUpdateCallback; + width = kwdFile.getMap().getWidth(); + height = kwdFile.getMap().getHeight(); + + // Duplicate the map + this.tiles = new IMapTileInformation[width][height]; + } + + @Override + protected IMapTileInformation addObject(Entity e) { + logger.log(Level.TRACE, "MapTileContainer.addObject({0})", e); + IMapTileInformation result = new MapTileInformation(e); + Point p = result.getLocation(); + this.tiles[p.x][p.y] = result; + + // Naive completion checker + tilesAdded++; + if (tilesAdded == getSize()) { + onLoadComplete(); + } + + return result; + } + + @Override + protected void updateObjects(Set set) { + if (set.isEmpty()) { + return; + } + + logger.log(System.Logger.Level.TRACE, "MapTileContainer.updateObjects({0})", set.size()); + + // Collect the tiles + Point[] updatableTiles = new Point[set.size()]; + int i = 0; + for (Entity e : set) { + IMapTileInformation object = getObject(e.getId()); + if (object == null) { + logger.log(Level.WARNING, "Update: No matching object for entity:{0}", e); + continue; + } + updatableTiles[i] = object.getLocation(); + i++; + } + + // Update the batch + tileUpdateCallback.accept(updatableTiles); + } + + @Override + protected void updateObject(IMapTileInformation object, Entity e) { + throw new UnsupportedOperationException("Should use the batch method."); + } + + @Override + protected void removeObject(IMapTileInformation object, Entity e) { + logger.log(Level.TRACE, "MapTileContainer.removeObject({0})", e); + } + + @Override + public int getHeight() { + return height; + } + + @Override + public IMapTileInformation getTile(int x, int y) { + if (x < 0 || y < 0 || x >= width || y >= height) { + return null; + } + + return this.tiles[x][y]; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public void setTiles(List mapTiles) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + protected abstract void onLoadComplete(); + + /** + * Single map tile that taps into the entity information + */ + private static class MapTileInformation extends AbstractMapTileInformation { + + private final Entity entity; + + public MapTileInformation(Entity entity) { + super(entity.getId()); + + this.entity = entity; + } + + @Override + protected T getEntityComponent(Class type) { + return entity.get(type); + } + + } + +} diff --git a/src/toniarts/openkeeper/view/text/RoomTextParser.java b/src/toniarts/openkeeper/view/text/RoomTextParser.java new file mode 100644 index 00000000..857e8aa8 --- /dev/null +++ b/src/toniarts/openkeeper/view/text/RoomTextParser.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014-2024 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.view.text; + +import com.simsilica.es.EntityId; +import toniarts.openkeeper.game.map.IRoomInformation; +import toniarts.openkeeper.game.map.IRoomsInformation; +import toniarts.openkeeper.utils.TextUtils; + +/** + * Parses text and fills the replacements from room data + * + * @author Toni Helenius + */ +public class RoomTextParser { + + private final IRoomsInformation roomsInformation; + + public RoomTextParser(IRoomsInformation roomsInformation) { + this.roomsInformation = roomsInformation; + } + + public String parseText(String text, EntityId room) { + return TextUtils.parseText(text, (index) -> { + return getReplacement(index, roomsInformation.getRoomInformation(room)); + }); + } + + protected String getReplacement(int index, IRoomInformation room) { + switch (index) { + case 37: + return Integer.toString(room.getHealthPercent()); // Health + case 38: + return ""; // Used capacity + case 39: + return ""; // Max capacity + case 44: // Portal + case 45: // Lairs + case 46: // Hatchery + case 47: // Treasure + case 48: // Library + case 49: // Training Room + case 50: // Workshop + case 51: // Guard Rooms + case 53: // Combat Pit + case 54: // Torture + case 58: // Prison + case 61: // Graveyard + case 62: // Temple + case 63: // Casino + return "0"; // amount + } + + return "Parameter " + index + " not implemented!"; + +// if (roomInstance.getOwnerId() != playerId) { +// return notOwnedTooltip; +// } + } + +} diff --git a/src/toniarts/openkeeper/view/text/TextParser.java b/src/toniarts/openkeeper/view/text/TextParser.java index aca06306..23a283d1 100644 --- a/src/toniarts/openkeeper/view/text/TextParser.java +++ b/src/toniarts/openkeeper/view/text/TextParser.java @@ -1,36 +1,37 @@ -/* - * Copyright (C) 2014-2018 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.view.text; - -/** - * Provides text parsing services for users - * - * @author Toni Helenius - */ -public interface TextParser { - - CreatureTextParser getCreatureTextParser(); - - TrapTextParser getTrapTextParser(); - - DoorTextParser getDoorTextParser(); - - ObjectTextParser getObjectTextParser(); - - MapTileTextParser getMapTileTextParser(); - -} +/* + * Copyright (C) 2014-2018 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.view.text; + +/** + * Provides text parsing services for users + * + * @author Toni Helenius + */ +public interface TextParser { + + CreatureTextParser getCreatureTextParser(); + + TrapTextParser getTrapTextParser(); + + DoorTextParser getDoorTextParser(); + + ObjectTextParser getObjectTextParser(); + + MapTileTextParser getMapTileTextParser(); + + RoomTextParser getRoomTextParser(); +} diff --git a/src/toniarts/openkeeper/view/text/TextParserService.java b/src/toniarts/openkeeper/view/text/TextParserService.java index 6f7d3167..08d94538 100644 --- a/src/toniarts/openkeeper/view/text/TextParserService.java +++ b/src/toniarts/openkeeper/view/text/TextParserService.java @@ -1,67 +1,75 @@ -/* - * Copyright (C) 2014-2018 OpenKeeper - * - * OpenKeeper 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. - * - * OpenKeeper 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 OpenKeeper. If not, see . - */ -package toniarts.openkeeper.view.text; - -import toniarts.openkeeper.game.map.IMapInformation; - -/** - * A kind of facade to the different kind of parsers. Many of them share the - * same parameters - * - * @author Toni Helenius - */ -public class TextParserService implements TextParser { - - private final CreatureTextParser creatureTextParser; - private final TrapTextParser trapTextParser; - private final DoorTextParser doorTextParser; - private final ObjectTextParser objectTextParser; - private final MapTileTextParser mapTileTextParser; - - public TextParserService(IMapInformation mapInformation) { - this.creatureTextParser = new CreatureTextParser(mapInformation); - this.trapTextParser = new TrapTextParser(); - this.doorTextParser = new DoorTextParser(); - this.objectTextParser = new ObjectTextParser(); - this.mapTileTextParser = new MapTileTextParser(); - } - - @Override - public CreatureTextParser getCreatureTextParser() { - return creatureTextParser; - } - - @Override - public TrapTextParser getTrapTextParser() { - return trapTextParser; - } - - @Override - public DoorTextParser getDoorTextParser() { - return doorTextParser; - } - - @Override - public ObjectTextParser getObjectTextParser() { - return objectTextParser; - } - - @Override - public MapTileTextParser getMapTileTextParser() { - return mapTileTextParser; - } -} +/* + * Copyright (C) 2014-2018 OpenKeeper + * + * OpenKeeper 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. + * + * OpenKeeper 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 OpenKeeper. If not, see . + */ +package toniarts.openkeeper.view.text; + +import toniarts.openkeeper.game.map.IMapInformation; +import toniarts.openkeeper.game.map.IRoomsInformation; + +/** + * A kind of facade to the different kind of parsers. Many of them share the + * same parameters + * + * @author Toni Helenius + */ +public class TextParserService implements TextParser { + + private final CreatureTextParser creatureTextParser; + private final TrapTextParser trapTextParser; + private final DoorTextParser doorTextParser; + private final ObjectTextParser objectTextParser; + private final MapTileTextParser mapTileTextParser; + private final RoomTextParser roomTextParser; + + public TextParserService(IMapInformation mapInformation, IRoomsInformation roomsInformation) { + this.creatureTextParser = new CreatureTextParser(mapInformation); + this.trapTextParser = new TrapTextParser(); + this.doorTextParser = new DoorTextParser(); + this.objectTextParser = new ObjectTextParser(); + this.mapTileTextParser = new MapTileTextParser(); + this.roomTextParser = new RoomTextParser(roomsInformation); + } + + @Override + public CreatureTextParser getCreatureTextParser() { + return creatureTextParser; + } + + @Override + public TrapTextParser getTrapTextParser() { + return trapTextParser; + } + + @Override + public DoorTextParser getDoorTextParser() { + return doorTextParser; + } + + @Override + public ObjectTextParser getObjectTextParser() { + return objectTextParser; + } + + @Override + public MapTileTextParser getMapTileTextParser() { + return mapTileTextParser; + } + + @Override + public RoomTextParser getRoomTextParser() { + return roomTextParser; + } +}