diff --git a/jsettlers.buildingcreator/src/main/java/jsettlers/buildingcreator/job/EBuildingJobType.java b/jsettlers.buildingcreator/src/main/java/jsettlers/buildingcreator/job/EBuildingJobType.java new file mode 100644 index 0000000000..b3bb6e948a --- /dev/null +++ b/jsettlers.buildingcreator/src/main/java/jsettlers/buildingcreator/job/EBuildingJobType.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2015 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *******************************************************************************/ +package jsettlers.buildingcreator.job; + +public enum EBuildingJobType { + /** + * Waits a given time. + *

+ * SUCCESS: The time elapsed. + *

+ * Fail: impossible. + * + * @see BuildingJob#getTime(); + */ + WAIT, + /** + * Lets the settler walk in a given direction. The settler may wait. + *

+ * Parameter: direction + *

+ * SUCCESS: The settler is at the position + *

+ * Fail: Should not happen normally. + */ + WALK, + /** + * Shows the settler at a given position. + *

+ * Parameter: dx, dy + *

+ * SUCCESS: The settler appeared. + *

+ * Fail: The settler could not appear at the given position. + */ + SHOW, + /** + * Lets the settler disappear. + *

+ * Parameter: none + *

+ * SUCCESS: The settler disappeared instantly. + *

+ * Fail: impossible + */ + HIDE, + + /** + * Sets the material property of the settler. + *

+ * Parameter: material + *

+ * SUCCESS: good + *

+ * Fail: There was no given material at that position. + */ + SET_MATERIAL, + + /** + * Picks up the specified material. Does not change the material type assigned to the settler + *

+ * Parameter: material + *

+ * SUCCESS: There was a material at that position, one item was removed. + *

+ * Fail: There was no given material at that position. + */ + TAKE, + /** + * Lets the settler drop the given material to the stack at the positon. + *

+ * Parameter: material + *

+ * SUCCESS: When the settler dropped the material. + *

+ * Fail: If the drop is impossible. + */ + DROP, + /** + * Searches a given search type. + *

+ * Uses the special {@link BuildingSearchJob} class. + *

+ * SUCCESS: The settler found the thing he should search and went to it. + *

+ * Fail: If the searched thing was not found. The settler does not need to go back. + * + * @see BuildingSearchType + */ + SEARCH, + /** + * Goes to the position relative to the building. + *

+ * SUCCESS: The settler is at the position + *

+ * Fail: The position is unreachable. + */ + GO_TO, + + /** + * Look at + *

+ * SUCCESS: The settler looks at the given new direction. + *

+ * Fail: impossible + */ + LOOK_AT, + /** + * Plays an action animation. + *

+ * Parameter: time - the time the action should take. + *

+ * SUCCESS: The animation was played. + *

+ * Fail: something was wrong... + */ + PLAY_ACTION1, + /** + * @see EBuildingJobType#PLAY_ACTION1 + */ + PLAY_ACTION2, + PLAY_ACTION3 +} diff --git a/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java b/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java index 5e375732e5..5b99551b7e 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java +++ b/jsettlers.common/src/main/java/jsettlers/common/CommonConstants.java @@ -14,8 +14,6 @@ *******************************************************************************/ package jsettlers.common; -import jsettlers.common.ai.EPlayerType; - public abstract class CommonConstants { /** * A byte value indicating that the given position is visible. @@ -69,8 +67,15 @@ public abstract class CommonConstants { * Option to disable the loading of original maps. */ public static boolean DISABLE_ORIGINAL_MAPS = false; + /** * Disables the checksum test for original maps. */ public static boolean DISABLE_ORIGINAL_MAPS_CHECKSUM = false; + + /** + * Enables debugging of behavior trees + */ + public static boolean DEBUG_BEHAVIOR_TREES = false; + } diff --git a/jsettlers.common/src/main/java/jsettlers/common/action/Action.java b/jsettlers.common/src/main/java/jsettlers/common/action/Action.java index b17df2b211..16359e2a4a 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/action/Action.java +++ b/jsettlers.common/src/main/java/jsettlers/common/action/Action.java @@ -17,7 +17,7 @@ /** * This is a action the user has requested. *

- * Each Action has an active status, that indicates that it is currently executed. When the execution of the action is begun, the flag should be set + * Each action has an active status, that indicates that it is currently executed. When the execution of the action is begun, the flag should be set * so that the user interface enters a blocking mode, and goes back to normal mode when the action is finished. It is not guaranteed that there is no * other action being sent during that time, e.g. an cancel-action. *

diff --git a/jsettlers.common/src/main/java/jsettlers/common/buildings/jobs/EBuildingJobType.java b/jsettlers.common/src/main/java/jsettlers/common/buildings/jobs/EBuildingJobType.java index e429ebd874..33c4fbe4bf 100644 --- a/jsettlers.common/src/main/java/jsettlers/common/buildings/jobs/EBuildingJobType.java +++ b/jsettlers.common/src/main/java/jsettlers/common/buildings/jobs/EBuildingJobType.java @@ -27,7 +27,7 @@ public enum EBuildingJobType { *

* Parameter: time (in seconds) *

- * Success: The time elapsed. + * SUCCESS: The time elapsed. *

* Fail: impossible. * @@ -40,7 +40,7 @@ public enum EBuildingJobType { *

* Parameter: direction *

- * Success: The settler is at the position + * SUCCESS: The settler is at the position *

* Fail: Should not happen normally. */ @@ -51,7 +51,7 @@ public enum EBuildingJobType { *

* Parameter: dx, dy *

- * Success: The settler appeared. + * SUCCESS: The settler appeared. *

* Fail: The settler could not appear at the given position. */ @@ -62,7 +62,7 @@ public enum EBuildingJobType { *

* Parameter: none *

- * Success: The settler disappeared instantly. + * SUCCESS: The settler disappeared instantly. *

* Fail: impossible */ @@ -73,7 +73,7 @@ public enum EBuildingJobType { *

* Parameter: material *

- * Success: always + * SUCCESS: always *

* Fail: never */ @@ -84,7 +84,7 @@ public enum EBuildingJobType { *

* Parameter: material *

- * Success: There was a material at that position, one item was removed. + * SUCCESS: There was a material at that position, one item was removed. *

* Fail: There was no given material at that position. */ @@ -97,7 +97,7 @@ public enum EBuildingJobType { *

* Parameter: material *

- * Success: When the settler dropped the material. + * SUCCESS: When the settler dropped the material. *

* Fail: If the drop is impossible, e.g. because there is already material at that position. */ @@ -112,7 +112,7 @@ public enum EBuildingJobType { *

* This job always fails if the working radius is 0. *

- * Success: A path to the searched thing has been found. + * SUCCESS: A path to the searched thing has been found. *

* Fail: If the searched thing was not found. *

@@ -132,7 +132,7 @@ public enum EBuildingJobType { *

* This job always fails if the working radius is 0. *

- * Success: A path to the searched thing has been found. + * SUCCESS: A path to the searched thing has been found. *

* Fail: If the searched thing was not found. * @@ -149,7 +149,7 @@ public enum EBuildingJobType { /** * Goes to the position relative to the building. *

- * Success: The settler is at the position + * SUCCESS: The settler is at the position *

* Fail: The position is unreachable. */ @@ -178,7 +178,7 @@ public enum EBuildingJobType { *

* Parameter: direction *

- * Success: The settler looks at the given new direction. + * SUCCESS: The settler looks at the given new direction. *

* Fail: impossible */ @@ -189,7 +189,7 @@ public enum EBuildingJobType { *

* Parameter: time (the time the action should take) *

- * Success: The animation was played. + * SUCCESS: The animation was played. *

* Fail: should not happen. */ @@ -210,7 +210,7 @@ public enum EBuildingJobType { *

* Parameters: type ({@link jsettlers.common.material.ESearchType}) *

- * Success: the given search type has been executed + * SUCCESS: the given search type has been executed *

* Fail: the given search type couldn't be executed */ @@ -221,7 +221,7 @@ public enum EBuildingJobType { *

* Parameters: dx, dy, material *

- * Success: There is material at that position. + * SUCCESS: There is material at that position. *

* Fail: There is no matching material at that position */ @@ -232,7 +232,7 @@ public enum EBuildingJobType { *

* Parameters: dx, dy, material *

- * Success: The material may be placed at the given position + * SUCCESS: The material may be placed at the given position *

* Fail: There is a full stack at that position, a wrong stack or it is blocked otherwise. */ diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java index 64207f43ce..f716a614e6 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/reader/AdvancedDatFileReader.java @@ -438,7 +438,7 @@ private void readSequencesAt(ByteReader reader, int sequenceIndexStart) throws I int pointerCount = reader.read16(); if (byteCount != pointerCount * 4 + 8) { - throw new IOException("Sequence index block length (" + pointerCount + ") and " + "bytecount (" + byteCount + ") are not consistent."); + throw new IOException("sequence index block length (" + pointerCount + ") and " + "bytecount (" + byteCount + ") are not consistent."); } int[] sequenceIndexPointers = new int[pointerCount]; diff --git a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/sequence/ArraySequence.java b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/sequence/ArraySequence.java index 42dd363a43..087a0d7ec5 100644 --- a/jsettlers.graphics/src/main/java/jsettlers/graphics/image/sequence/ArraySequence.java +++ b/jsettlers.graphics/src/main/java/jsettlers/graphics/image/sequence/ArraySequence.java @@ -45,7 +45,7 @@ public ArraySequence(T[] images) { /* * (non-Javadoc) * - * @see Sequence#length() + * @see sequence#length() */ @Override public int length() { @@ -55,7 +55,7 @@ public int length() { /* * (non-Javadoc) * - * @see Sequence#getImageLink(int) + * @see sequence#getImageLink(int) */ @Override public T getImage(int index) { @@ -66,7 +66,7 @@ public T getImage(int index) { /* * (non-Javadoc) * - * @see Sequence#getImageSafe(int) + * @see sequence#getImageSafe(int) */ @Override public Image getImageSafe(int index) { diff --git a/jsettlers.logic/build.gradle b/jsettlers.logic/build.gradle index cda994ba05..ee1cfa95ab 100644 --- a/jsettlers.logic/build.gradle +++ b/jsettlers.logic/build.gradle @@ -21,6 +21,7 @@ task unitTest(type: Test) { dependencies { implementation project(':jsettlers.common') implementation project(':jsettlers.network') + implementation 'org.apache.commons:commons-text:1.3' testImplementation project(':jsettlers.testutils') testImplementation project(':jsettlers.main.swing') diff --git a/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiPositions.java b/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiPositions.java index 3a76eb412a..7ec1286dec 100644 --- a/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiPositions.java +++ b/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/AiPositions.java @@ -280,7 +280,7 @@ public String toString() { } public ShortPoint2D getBestRatedPoint(PositionRater rater) { - // TODO: Parallel ? + // TODO: parallel ? int currentBestRating = PositionRater.RATE_INVALID; ShortPoint2D currentBest = null; for (int i = 0; i < size; i++) { diff --git a/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/pioneers/PioneerGroup.java b/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/pioneers/PioneerGroup.java index 703be8ff9b..609619b78b 100644 --- a/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/pioneers/PioneerGroup.java +++ b/jsettlers.logic/src/main/java/jsettlers/ai/highlevel/pioneers/PioneerGroup.java @@ -27,6 +27,7 @@ import jsettlers.input.tasks.ConvertGuiTask; import jsettlers.logic.map.grid.movable.MovableGrid; import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.MovableDataManager; import jsettlers.logic.movable.interfaces.ILogicMovable; import jsettlers.network.client.interfaces.ITaskScheduler; @@ -60,7 +61,7 @@ public void clear() { public void removeDeadPioneers() { Collection idsToRemove = new ArrayList<>(pioneerIds.size()); for (Integer pioneerId : pioneerIds) { - if (Movable.getMovableByID(pioneerId) == null) { + if (MovableDataManager.getMovableByID(pioneerId) == null) { idsToRemove.add(pioneerId); } } @@ -90,10 +91,11 @@ public void fill(ITaskScheduler taskScheduler, AiStatistics aiStatistics, byte p } public PioneerGroup getPioneersWithNoAction() { - List pioneersWithNoAction = stream(pioneerIds).filter(pioneerId -> Movable.getMovableByID(pioneerId).getAction() == EMovableAction.NO_ACTION).collect(Collectors.toList()); + List pioneersWithNoAction = stream(pioneerIds).filter(pioneerId -> MovableDataManager.getMovableByID(pioneerId).getAction() == EMovableAction.NO_ACTION).collect(Collectors.toList()); return new PioneerGroup(pioneersWithNoAction); } + public void addAll(List pioneerIds) { this.pioneerIds.addAll(pioneerIds); } diff --git a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java index 59dcfaee2e..7b8f51d6d6 100644 --- a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java +++ b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/FogOfWar.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; +import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; import jsettlers.algorithms.fogofwar.CachedViewCircle.CachedViewCircleIterator; @@ -29,26 +30,26 @@ /** * This class holds the fog of war for a given map and team. - * + * * @author Andreas Eberle */ public final class FogOfWar implements Serializable { - private static final long serialVersionUID = 1877994785778678510L; + private static final long serialVersionUID = 1877994785778678510L; /** * Longest distance any unit may look */ private static final byte MAX_VIEW_DISTANCE = 65; - static final int PADDING = 10; + static final int PADDING = 10; private final byte team; - private final short width; - private final short height; - private byte[][] sight; + private final short width; + private final short height; + private byte[][] sight; - private transient boolean enabled = Constants.FOG_OF_WAR_DEFAULT_ENABLED; + private transient boolean enabled = Constants.FOG_OF_WAR_DEFAULT_ENABLED; private transient IFogOfWarGrid grid; - private transient boolean canceled; + private transient boolean canceled; public FogOfWar(short width, short height, IPlayer player) { this.width = width; @@ -70,7 +71,7 @@ public void start(IFogOfWarGrid grid) { /** * Gets the visible status of a map pint - * + * * @param x * The x coordinate of the point in 0..(mapWidth - 1) * @param y @@ -98,9 +99,9 @@ public void setEnabled(boolean enabled) { } final class NewFoWThread extends Thread { - private static final byte DIM_DOWN_SPEED = 10; - private final CircleDrawer drawer = new CircleDrawer(); - private byte[][] buffer = new byte[width][height]; + private static final byte DIM_DOWN_SPEED = 10; + private final CircleDrawer drawer = new CircleDrawer(); + private byte[][] buffer = new byte[width][height]; NewFoWThread() { super("FoWThread"); @@ -143,10 +144,10 @@ private void rebuildSight() { } } - ConcurrentLinkedQueue buildings = grid.getBuildingViewDistancables(); + Collection buildings = grid.getBuildingViewDistancables(); applyViewDistances(buildings); - ConcurrentLinkedQueue movables = grid.getMovableViewDistancables(); + Collection movables = grid.getMovableViewDistancables(); applyViewDistances(movables); byte[][] temp = sight; @@ -154,14 +155,15 @@ private void rebuildSight() { buffer = temp; } - private void applyViewDistances(ConcurrentLinkedQueue objects) { + private void applyViewDistances(Collection objects) { for (IViewDistancable curr : objects) { if (isPlayerOK(curr)) { short distance = curr.getViewDistance(); if (distance > 0) { ShortPoint2D pos = curr.getPosition(); - if (pos != null) + if (pos != null) { drawer.drawCircleToBuffer(pos.x, pos.y, distance); + } } } } @@ -178,7 +180,7 @@ private void mySleep(long ms) { } final class CircleDrawer { - private byte[][] buffer; + private byte[][] buffer; private final CachedViewCircle[] cachedCircles = new CachedViewCircle[MAX_VIEW_DISTANCE]; public final void setBuffer(byte[][] buffer) { diff --git a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/IFogOfWarGrid.java b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/IFogOfWarGrid.java index e277a8af10..c6bb10ec8b 100644 --- a/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/IFogOfWarGrid.java +++ b/jsettlers.logic/src/main/java/jsettlers/algorithms/fogofwar/IFogOfWarGrid.java @@ -14,6 +14,7 @@ *******************************************************************************/ package jsettlers.algorithms.fogofwar; +import java.util.Collection; import java.util.concurrent.ConcurrentLinkedQueue; import jsettlers.common.mapobject.IMapObject; @@ -31,8 +32,8 @@ public interface IFogOfWarGrid { IMapObject getMapObjectsAt(short x, short y); - ConcurrentLinkedQueue getMovableViewDistancables(); + Collection getMovableViewDistancables(); - ConcurrentLinkedQueue getBuildingViewDistancables(); + Collection getBuildingViewDistancables(); } diff --git a/jsettlers.logic/src/main/java/jsettlers/input/GuiInterface.java b/jsettlers.logic/src/main/java/jsettlers/input/GuiInterface.java index 3f26b95906..915638c84f 100644 --- a/jsettlers.logic/src/main/java/jsettlers/input/GuiInterface.java +++ b/jsettlers.logic/src/main/java/jsettlers/input/GuiInterface.java @@ -144,7 +144,7 @@ public void run() { @Override public void action(IAction action) { if (action.getActionType() != EActionType.SCREEN_CHANGE) { - System.out.println("action(Action): " + action.getActionType() + " at game time: " + MatchConstants.clock().getTime()); + System.out.println("action(action): " + action.getActionType() + " at game time: " + MatchConstants.clock().getTime()); } switch (action.getActionType()) { diff --git a/jsettlers.logic/src/main/java/jsettlers/input/GuiTaskExecutor.java b/jsettlers.logic/src/main/java/jsettlers/input/GuiTaskExecutor.java index 116e8294eb..3723b8cf2a 100644 --- a/jsettlers.logic/src/main/java/jsettlers/input/GuiTaskExecutor.java +++ b/jsettlers.logic/src/main/java/jsettlers/input/GuiTaskExecutor.java @@ -25,6 +25,7 @@ import jsettlers.common.buildings.IBuilding; import jsettlers.common.map.shapes.HexGridArea; import jsettlers.common.movable.EMovableType; +import jsettlers.common.movable.IMovable; import jsettlers.common.position.ShortPoint2D; import jsettlers.common.utils.mutables.MutableInt; import jsettlers.input.tasks.ChangeTowerSoldiersGuiTask; @@ -55,6 +56,7 @@ import jsettlers.logic.buildings.workers.DockyardBuilding; import jsettlers.logic.map.grid.partition.manager.settings.MaterialProductionSettings; import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.MovableDataManager; import jsettlers.logic.movable.interfaces.ILogicMovable; import jsettlers.network.client.task.packets.TaskPacket; import jsettlers.network.synchronic.timer.ITaskExecutor; @@ -275,7 +277,7 @@ private void setBuildingPriority(SetBuildingPriorityGuiTask task) { private void convertMovables(ConvertGuiTask guiTask) { for (Integer currID : guiTask.getSelection()) { - ILogicMovable movable = Movable.getMovableByID(currID); + ILogicMovable movable = MovableDataManager.getMovableByID(currID); if (movable != null) { movable.convertTo(guiTask.getTargetType()); } @@ -285,7 +287,7 @@ private void convertMovables(ConvertGuiTask guiTask) { private void stopOrStartWorking(List selectedMovables, boolean stop) { for (Integer currID : selectedMovables) { - ILogicMovable movable = Movable.getMovableByID(currID); + ILogicMovable movable = MovableDataManager.getMovableByID(currID); if (movable != null) { movable.stopOrStartWorking(stop); } @@ -294,7 +296,7 @@ private void stopOrStartWorking(List selectedMovables, boolean stop) { private void killSelectedMovables(List selectedMovables) { for (Integer currID : selectedMovables) { - ILogicMovable curr = Movable.getMovableByID(currID); + ILogicMovable curr = MovableDataManager.getMovableByID(currID); if (curr != null) { curr.kill(); } @@ -310,7 +312,18 @@ private void killSelectedMovables(List selectedMovables) { * A list of the id's of the movables. */ private void moveSelectedTo(ShortPoint2D targetPosition, List movableIds) { - List movables = stream(movableIds).map(Movable::getMovableByID).filter(Objects::nonNull).collect(Collectors.toList()); + if (movableIds.size() == 1) { + ILogicMovable currMovable = MovableDataManager.getMovableByID(movableIds.get(0)); + if (currMovable != null) { + currMovable.moveTo(targetPosition); + } + } else if (!movableIds.isEmpty()) { + sendMovablesNew(targetPosition, movableIds); + } + } + + private void sendMovablesNew(ShortPoint2D targetPosition, List movableIds) { + List movables = stream(movableIds).map(MovableDataManager::getMovableByID).filter(Objects::nonNull).collect(Collectors.toList()); if (movables.isEmpty()) { return; @@ -389,8 +402,8 @@ private void unloadFerry(MovableGuiTask task) { private void forMovables(MovableGuiTask task, Consumer movableConsumer) { stream(task.getSelection()) - .map(Movable::getMovableByID) - .filter(ILogicMovable::isAlive) + .map(MovableDataManager::getMovableByID) + .filter(IMovable::isAlive) .filter(movable -> movable.getMovableType() == EMovableType.FERRY) .forEach(movableConsumer); } diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/buildings/spawn/SpawnBuilding.java b/jsettlers.logic/src/main/java/jsettlers/logic/buildings/spawn/SpawnBuilding.java index ba8a5e7a2d..b02c3d96c6 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/buildings/spawn/SpawnBuilding.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/buildings/spawn/SpawnBuilding.java @@ -20,7 +20,7 @@ import jsettlers.common.position.ShortPoint2D; import jsettlers.logic.buildings.Building; import jsettlers.logic.buildings.IBuildingsGrid; -import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.EntityFactory; import jsettlers.logic.movable.interfaces.ILogicMovable; import jsettlers.logic.player.Player; @@ -56,7 +56,7 @@ protected int subTimerEvent() { ILogicMovable movableAtDoor = super.grid.getMovable(super.getDoor()); if (movableAtDoor == null) { - movableAtDoor = new Movable(super.grid.getMovableGrid(), getMovableType(), getDoor(), super.getPlayer()); + movableAtDoor = EntityFactory.createMovable(super.grid.getMovableGrid(), getMovableType(), getDoor(), super.getPlayer()); produced++; if (produced < getProduceLimit()) { diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/constants/Constants.java b/jsettlers.logic/src/main/java/jsettlers/logic/constants/Constants.java index ef74fbb19f..050ebf7ec6 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/constants/Constants.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/constants/Constants.java @@ -44,6 +44,8 @@ private Constants() { public static final short MOVABLE_BEND_DURATION = 500; + public static final short BRICKLAYER_ACTION_DURATION = 1000; + public static final short MOVABLE_VIEW_DISTANCE = 8; public static final short MOVABLE_FLOCK_TO_DECENTRALIZE_MAX_RADIUS = 2; diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/GameSerializer.java b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/GameSerializer.java index f3c62f33e9..f50d900c83 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/GameSerializer.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/GameSerializer.java @@ -23,6 +23,7 @@ import jsettlers.logic.buildings.trading.MarketBuilding; import jsettlers.logic.map.loading.MapLoadException; import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.MovableDataManager; /** * This class serializes and deserializes the {@link MainGrid} and therefore the complete game state. @@ -94,7 +95,7 @@ public void run() { Building.writeStaticState(oos); MarketBuilding.writeStaticState(oos); HarborBuilding.writeStaticState(oos); - Movable.writeStaticState(oos); + MovableDataManager.writeStaticState(oos); oos.writeObject(grid); } catch (Throwable t) { t.printStackTrace(); @@ -118,7 +119,7 @@ public void run() { Building.readStaticState(ois); MarketBuilding.readStaticState(ois); HarborBuilding.readStaticState(ois); - Movable.readStaticState(ois); + MovableDataManager.readStaticState(ois); grid = (MainGrid) ois.readObject(); } catch (Throwable t) { t.printStackTrace(); diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java index 4c8ec5e621..138fed1494 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/map/grid/MainGrid.java @@ -18,6 +18,7 @@ import java.io.ObjectInputStream; import java.io.Serializable; import java.util.BitSet; +import java.util.Collection; import java.util.Date; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; @@ -115,7 +116,8 @@ import jsettlers.logic.map.loading.list.MapList; import jsettlers.logic.map.loading.newmap.MapFileHeader; import jsettlers.logic.map.loading.newmap.MapFileHeader.MapType; -import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.EntityFactory; +import jsettlers.logic.movable.MovableDataManager; import jsettlers.logic.movable.interfaces.AbstractMovableGrid; import jsettlers.logic.movable.interfaces.IAttackable; import jsettlers.logic.movable.interfaces.ILogicMovable; @@ -437,7 +439,7 @@ public final boolean isInBounds(int x, int y) { } final ILogicMovable createNewMovableAt(ShortPoint2D pos, EMovableType type, Player player) { - return new Movable(movablePathfinderGrid, type, pos, player); + return EntityFactory.createMovable(movablePathfinderGrid, type, pos, player); } /** @@ -942,7 +944,7 @@ public void hitWithArrowAt(ArrowObject arrow) { @Override public void spawnDonkey(ShortPoint2D position, Player player) { Player realPlayer = partitionsGrid.getPlayer(player.getPlayerId()); - ILogicMovable donkey = new Movable(movablePathfinderGrid, EMovableType.DONKEY, position, realPlayer); + ILogicMovable donkey = EntityFactory.createMovable(movablePathfinderGrid, EMovableType.DONKEY, position, realPlayer); donkey.leavePosition(); } @@ -2050,8 +2052,8 @@ public final IMapObject getMapObjectsAt(short x, short y) { } @Override - public final ConcurrentLinkedQueue getMovableViewDistancables() { - return Movable.getAllMovables(); + public final Collection getMovableViewDistancables() { + return MovableDataManager.getAllMovables(); } @Override diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/BehaviorTreeHelper.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/BehaviorTreeHelper.java new file mode 100644 index 0000000000..6d8daa84cd --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/BehaviorTreeHelper.java @@ -0,0 +1,303 @@ +package jsettlers.logic.movable; + +import jsettlers.common.material.EMaterialType; +import jsettlers.common.movable.EMovableAction; +import jsettlers.common.movable.EMovableType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.constants.MatchConstants; +import jsettlers.logic.movable.components.AnimationComponent; +import jsettlers.logic.movable.components.GameFieldComponent; +import jsettlers.logic.movable.components.MaterialComponent; +import jsettlers.logic.movable.components.SteeringComponent; +import jsettlers.logic.movable.simplebehaviortree.IBooleanConditionFunction; +import jsettlers.logic.movable.simplebehaviortree.IEMaterialTypeSupplier; +import jsettlers.logic.movable.simplebehaviortree.IIntegerSupplier; +import jsettlers.logic.movable.simplebehaviortree.INodeStatusActionConsumer; +import jsettlers.logic.movable.simplebehaviortree.INodeStatusActionFunction; +import jsettlers.logic.movable.simplebehaviortree.IShortSupplier; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; +import jsettlers.logic.movable.simplebehaviortree.nodes.Action; +import jsettlers.logic.movable.simplebehaviortree.nodes.AlwaysFail; +import jsettlers.logic.movable.simplebehaviortree.nodes.AlwaysRunning; +import jsettlers.logic.movable.simplebehaviortree.nodes.AlwaysSucceed; +import jsettlers.logic.movable.simplebehaviortree.nodes.Condition; +import jsettlers.logic.movable.simplebehaviortree.nodes.Debug; +import jsettlers.logic.movable.simplebehaviortree.nodes.Guard; +import jsettlers.logic.movable.simplebehaviortree.nodes.Inverter; +import jsettlers.logic.movable.simplebehaviortree.nodes.MemSelector; +import jsettlers.logic.movable.simplebehaviortree.nodes.MemSequence; +import jsettlers.logic.movable.simplebehaviortree.nodes.NotificationCondition; +import jsettlers.logic.movable.simplebehaviortree.nodes.Parallel; +import jsettlers.logic.movable.simplebehaviortree.nodes.Property; +import jsettlers.logic.movable.simplebehaviortree.nodes.Repeat; +import jsettlers.logic.movable.simplebehaviortree.nodes.Selector; +import jsettlers.logic.movable.simplebehaviortree.nodes.Sequence; +import jsettlers.logic.movable.simplebehaviortree.nodes.Wait; + +public final class BehaviorTreeHelper { + + /* --- Node Factory --- */ + + public static Action action(INodeStatusActionConsumer action) { + return new Action<>(action); + } + + public static Node action(String debugMessage, INodeStatusActionConsumer action) { + return debug(debugMessage, action(action)); + } + + public static Action action(INodeStatusActionFunction action) { + return new Action<>(action); + } + + public static Node action(String debugMessage, INodeStatusActionFunction action) { + return debug(debugMessage, action(action)); + } + + public static Condition condition(IBooleanConditionFunction condition) { + return new Condition<>(condition); + } + + public static Node condition(String debugMessage, IBooleanConditionFunction condition) { + return debug(debugMessage, condition(condition)); + } + + public static AlwaysFail alwaysFail() { + return new AlwaysFail<>(); + } + + public static AlwaysSucceed alwaysSucceed() { + return new AlwaysSucceed<>(); + } + + public static Guard guard(IBooleanConditionFunction condition, Node child) { + return guard(condition, true, child); + } + + public static Node guard(String debugMessage, IBooleanConditionFunction condition, Node child) { + return guard(debugMessage, condition, true, child); + } + + public static Guard guard(IBooleanConditionFunction condition, boolean shouldBe, Node child) { + return new Guard<>(condition, shouldBe, child); + } + + public static Node guard(String debugMessage, IBooleanConditionFunction condition, boolean shouldBe, Node child) { + return debug(debugMessage, guard(condition, shouldBe, child)); + } + + public static Inverter inverter(Node child) { + return new Inverter<>(child); + } + + @SafeVarargs + public static MemSelector memSelector(Node... children) { + return new MemSelector<>(children); + } + + @SafeVarargs + public static Node memSelector(String debugMessage, Node... children) { + return debug(debugMessage, memSelector(children)); + } + + @SafeVarargs + public static MemSequence memSequence(Node... children) { + return new MemSequence<>(children); + } + + @SafeVarargs + public static Node memSequence(String debugMessage, Node... children) { + return debug(debugMessage, memSequence(children)); + } + + @SafeVarargs + public static Parallel parallel(Parallel.Policy successPolicy, boolean preemptive, Node... children) { + return new Parallel<>(successPolicy, preemptive, children); + } + + public static Node repeat(String debugMessage, Repeat.Policy policy, Node condition, Node child) { + return debug(debugMessage, new Repeat<>(policy, condition, child)); + } + + public static Repeat repeat(Repeat.Policy policy, Node condition, Node child) { + return new Repeat<>(policy, condition, child); + } + + public static Repeat repeat(Node condition, Node child) { + return new Repeat<>(condition, child); + } + + @SafeVarargs + public static Selector selector(Node... children) { + return new Selector<>(children); + } + + @SafeVarargs + public static Node selector(String debugMessage, Node... children) { + return debug(debugMessage, new Selector<>(children)); + } + + @SafeVarargs + public static Sequence sequence(Node... children) { + return new Sequence<>(children); + } + + @SafeVarargs + public static Node sequence(String debugMessage, Node... children) { + return debug(debugMessage, sequence(children)); + } + + public static Wait wait(Node condition) { + return new Wait<>(condition); + } + + public static NotificationCondition notificationCondition(Class type) { + return new NotificationCondition(type); + } + + public static NotificationCondition notificationCondition(Class type, boolean consume) { + return new NotificationCondition(type, consume); + } + + public static NotificationCondition notificationCondition(Class type, IBooleanConditionFunction predicate, boolean consume) { + return new NotificationCondition(type, predicate, consume); + } + + public static Node waitForTargetReachedAndFailIfNotReachable() { + return sequence("waitForTargetReachedAndFailIfNotReachable", + inverter( + notificationCondition(SteeringComponent.TargetNotReachedNotification.class, true) + ), + wait( + notificationCondition(SteeringComponent.TargetReachedNotification.class, true) + ) + ); + } + + public static Node waitForPathFinished(Node targetReachedChild, Node targetNotReachedChild) { + return debug("waitForPathFinished", selector( + triggerGuard(SteeringComponent.TargetReachedNotification.class, debug("TargetReachedNotification", targetReachedChild)), + triggerGuard(SteeringComponent.TargetNotReachedNotification.class, debug("TargetNotReachedNotification", targetNotReachedChild)), + debug("path not finished yet", new AlwaysRunning<>()) + )); + } + + public static Node waitForPathFinished(Node targetReachedChild, Node targetNotReachedChild, Node whenPathFinished) { + return sequence( + waitForPathFinished(targetReachedChild, targetNotReachedChild), + debug("path finished", whenPathFinished) + ); + } + + public static Property setAttackableWhile(boolean value, Node child) { + return new Property<>( + (context, v) -> context.entity.attackableComponent().isAttackable(v), + (context) -> context.entity.attackableComponent().isAttackable(), value, child + ); + } + + public static Node defaultIdleBehavior() { + return debug("idle behavior", + setIdleBehaviorActiveWhile(true, + alwaysSucceed() + ) + ); + } + + public static Property setIdleBehaviorActiveWhile(boolean value, Node child) { + return new Property<>( + (context, v) -> context.entity.steeringComponent().IsIdleBehaviorActive(v), + (context) -> context.entity.steeringComponent().IsIdleBehaviorActive(), value, child + ); + } + + public static Guard triggerGuard(Class type, Node child) { + return new Guard<>(entity -> entity.component.hasNotificationOfType(type), true, child); + } + + public static Action startAnimation(EMovableAction animation, IShortSupplier durationSupplier, boolean isChained) { + return new Action<>(context -> { + context.entity.getAnimationComponent().startAnimation(animation, durationSupplier.apply(context), isChained); + }); + } + + public static Node startAndWaitForAnimation(EMovableAction animation, short duration) { + return startAndWaitForAnimation(animation, c->duration, false); + } + + public static Node startAndWaitForAnimation(EMovableAction animation, IShortSupplier durationSupplier, boolean isChained) { + return memSequence("startAndWaitForAnimation with " + animation, + debug("start animation", startAnimation(animation, durationSupplier, isChained)), + debug("wait for animation to finish", waitForNotification(AnimationComponent.AnimationFinishedNotification.class, n -> n.type == animation, true)) + ); + } + + public static void convertTo(Entity entity, EMovableType type) { + Entity blueprint = EntityFactory.createEntity(entity.gameFieldComponent().movableGrid, type, entity.movableComponent().getPosition(), entity.movableComponent().getPlayer()); + entity.convertTo(blueprint); + } + + public static Node alwaysSucceed(Node child) { + return new Selector(child, new AlwaysSucceed()); + } + + public static Sleep sleep(IIntegerSupplier delaySupplier) { + return new Sleep(delaySupplier); + } + + public static Sleep sleep(int delay) { + return new Sleep(c->delay); + } + + public static class Sleep extends Node { + private static final long serialVersionUID = 8774557186392581042L; + int endTime; + final IIntegerSupplier delaySupplier; + + public Sleep(IIntegerSupplier delaySupplier) { + super(); + this.delaySupplier = delaySupplier; + } + + @Override + public NodeStatus onTick(Tick tick) { + int remaining = endTime - MatchConstants.clock().getTime(); + if (remaining <= 0) { return NodeStatus.SUCCESS; } + tick.target.entity.setInvocationDelay(remaining); + return NodeStatus.RUNNING; + } + + @Override + public void onOpen(Tick tick) { + endTime = MatchConstants.clock().getTime() + delaySupplier.apply(tick.target); + } + } + + public static Node waitForNotification(Class type, boolean consume) { + return wait(notificationCondition(type, consume)); + } + + public static Node waitForNotification(Class type, IBooleanConditionFunction predicate, boolean consume) { + return wait(notificationCondition(type, predicate, consume)); + } + + public static Debug debug(String msg, Node child) { + return new Debug(msg, child); + } + + public static Debug debug(String msg) { + return new Debug(msg); + } + + public static Action dropMaterial(IEMaterialTypeSupplier materialTypeSupplier) { + return new Action<>(c -> { + EMaterialType material = materialTypeSupplier.apply(c); + if (material.isDroppable()) { + c.entity.gameFieldComponent().movableGrid.dropMaterial(c.entity.movableComponent().getPosition(), material, true, false); + } + c.entity.materialComponent().setMaterial(EMaterialType.NO_MATERIAL); + }); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/Context.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Context.java new file mode 100644 index 0000000000..92b2a2b1ce --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Context.java @@ -0,0 +1,24 @@ +package jsettlers.logic.movable; + +import java.io.Serializable; + +import jsettlers.logic.movable.components.Component; + +/** + * Created by homoroselaps + */ +public final class Context implements Serializable { + public final Entity entity; + public final Component component; + + public int debugLevel = 0; + + public Context(Entity entity, Component component) { + this.entity = entity; + this.component = component; + } + + public Entity getEntity() { return entity; } + + public Component getComponent() { return component; } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/Entity.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Entity.java new file mode 100644 index 0000000000..64b42211ac --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Entity.java @@ -0,0 +1,322 @@ +package jsettlers.logic.movable; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import java8.util.Optional; +import jsettlers.logic.constants.Constants; +import jsettlers.logic.movable.components.AnimationComponent; +import jsettlers.logic.movable.components.AttackableComponent; +import jsettlers.logic.movable.components.BearerComponent; +import jsettlers.logic.movable.components.BricklayerComponent; +import jsettlers.logic.movable.components.BuildingWorkerComponent; +import jsettlers.logic.movable.components.Component; +import jsettlers.logic.movable.components.DonkeyComponent; +import jsettlers.logic.movable.components.GameFieldComponent; +import jsettlers.logic.movable.components.MarkedPositonComponent; +import jsettlers.logic.movable.components.MaterialComponent; +import jsettlers.logic.movable.components.MovableComponent; +import jsettlers.logic.movable.components.MultiMaterialComponent; +import jsettlers.logic.movable.components.SpecialistComponent; +import jsettlers.logic.movable.components.SteeringComponent; +import jsettlers.logic.timer.IScheduledTimerable; +import jsettlers.logic.timer.RescheduleTimer; + +/** + * @author homoroselaps + */ + +public class Entity implements Serializable, IScheduledTimerable { + private static final long serialVersionUID = -5615478576016074072L; + + private final int id; + + private boolean debug; + + private final Map, Component> components = new IdentityHashMap<>(); + private final Map, Component> componentLookup = new IdentityHashMap<>(); + + private Set notificationsNext; + private Set notificationsCurrent; + + public Set getAllNotifications() { + return notificationsCurrent; + } + + + public enum State { + ACTIVE, + INACTIVE, + UNINITALIZED + } + + private State state; + private int invocationDelay; + + public Entity() { + id = MovableDataManager.getNextID(); + state = State.UNINITALIZED; + notificationsNext = new HashSet<>(); + notificationsCurrent = new HashSet<>(); + resetInvokationDelay(); + } + + public Entity(Component... cs) { + this(); + for (Component c : cs) { + add(c); + } + } + + /** + * Checks whether or not all Component dependencies are satisfied. + * @return {@code true} if all Component dependencies are satisfied, {@code false} otherwise. + */ + boolean checkComponentDependencies() { + for (Class cmp : this.componentLookup.keySet()) { + Requires ann = cmp.getAnnotation(Requires.class); + if (ann == null) { continue; } + for (Class dependency : ann.value()) { + assert componentLookup.containsKey(dependency) : componentLookup.get(cmp).getClass().getName() + "[" + cmp.getName() + "]: " + dependency.getName() + " missing"; + } + } + return true; + } + + void toggleDebug() { + debug = !debug; + } + + public boolean isInDebugMode() { + return debug; + } + + private int resetInvokationDelay() { + int lastValue = invocationDelay; + invocationDelay = -1; + return lastValue; + } + + public void setInvocationDelay(int delay) { + invocationDelay = invocationDelay > 0 ? Math.min(invocationDelay, delay) : delay; + } + + public boolean isActive() { + return state == State.ACTIVE; + } + + public void setActive(boolean active) { + if (active == isActive()) { + return; + } + + if (state == State.UNINITALIZED) { + initialize(); + } + + if (active) { + state = State.ACTIVE; + for (Component c : components.values()) { + c.enable(); + } + } else { + state = State.INACTIVE; + for (Component c : components.values()) { + c.disable(); + } + } + } + + private void initialize() { + for (Component component : components.values()) { + component.wakeUp(); + } + RescheduleTimer.add(this, Constants.MOVABLE_INTERRUPT_PERIOD); + } + + private void invokeUpdate() { + for (Component component : components.values()) { + component.update(); + } + for (Component component : components.values()) { + component.lateUpdate(); + } + } + + private void invokeDestroy() { + for (Component component : components.values()) { + component.destroy(); + } + } + + public int getID() { + return id; + } + + public void add(Component c) { + Class cls = c.getClass(); + assert !componentLookup.containsKey(cls) : "Component already registered" + cls.getName(); + componentLookup.put(cls, c); + components.put(cls, c); + c.entity = this; + // Iterate over all super classes + cls = cls.getSuperclass(); + while (cls != null && cls != Component.class) { + assert !componentLookup.containsKey(cls) : "Component already registered" + cls.getName(); + componentLookup.put(cls, c); + cls = cls.getSuperclass(); + } + } + + public Component remove(Class c) { + Component result = componentLookup.remove(c); + components.remove(c); + + Class cls = c.getSuperclass(); + while (cls != null && cls != Component.class) { + componentLookup.remove(cls); + cls = cls.getSuperclass(); + } + + return result; + } + + @SuppressWarnings("unchecked") + public C getComponent(Class c) { + return (C) componentLookup.get(c); + } + + @SuppressWarnings("unchecked") + public Optional getComponentOptional(Class c) { + return Optional.ofNullable((C) componentLookup.get(c)); + } + + public boolean containsComponent(Class c) { + return componentLookup.containsKey(c); + } + + public void raiseNotification(Notification note) { + notificationsNext.add(note); + } + + public void convertTo(Entity blueprint) { + blueprint.setActive(false); + + // remove all unused components + Iterator> local_component_it = components.keySet().iterator(); + while (local_component_it.hasNext()) { + Class cls = local_component_it.next(); + if (!blueprint.components.containsKey(cls)) { + local_component_it.remove(); + remove(cls); + } + } + + // add all new components + List newComponents = new ArrayList<>(); + Iterator> blueprint_component_it = blueprint.components.keySet().iterator(); + while (blueprint_component_it.hasNext()) { + Class cls = blueprint_component_it.next(); + // ignore components we already have + if (components.containsKey(cls)) { + continue; + } + Component comp = blueprint.getComponent(cls); + newComponents.add(comp); + add(comp); + blueprint_component_it.remove(); + blueprint.remove(cls); + } + + if (state != State.UNINITALIZED) { + // initialize all new components + for (Component c : newComponents) { + c.wakeUp(); + } + if (state == State.ACTIVE) { + for (Component c : newComponents) { + c.enable(); + } + } + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(30); + sb.append("Entity"); + sb.append("(").append(id).append(")"); + sb.append("["); + for (Component c : components.values()) { + sb.append(c).append(", "); + } + sb.append("]"); + return sb.toString(); + } + + @Override + public int hashCode() { + return id; + } + + @Override + public int timerEvent() { + if (!isActive()) { return -1; } + + invokeUpdate(); + + notificationsCurrent = notificationsNext; + notificationsNext = new HashSet<>(); + + return Math.max(resetInvokationDelay(), Constants.MOVABLE_INTERRUPT_PERIOD); + } + + @Override + public void kill() { + setActive(false); + invokeDestroy(); + } + + public final AnimationComponent getAnimationComponent() { + return getComponent(AnimationComponent.class); + } + + public final SteeringComponent steeringComponent() { + return getComponent(SteeringComponent.class); + } + + public final MovableComponent movableComponent() { + return getComponent(MovableComponent.class); + } + + public final BearerComponent bearerComponent() { + return getComponent(BearerComponent.class); + } + + public final SpecialistComponent specialistComponent() { + return getComponent(SpecialistComponent.class); + } + + public final GameFieldComponent gameFieldComponent() { + return getComponent(GameFieldComponent.class); + } + + public final MaterialComponent materialComponent() { return getComponent(MaterialComponent.class); } + + public final MultiMaterialComponent multiMaterialComponent() { return getComponent(MultiMaterialComponent.class); } + + public final DonkeyComponent donkeyComponent() { return getComponent(DonkeyComponent.class); } + + public final AttackableComponent attackableComponent() { return getComponent(AttackableComponent.class); } + + public final MarkedPositonComponent markedPositonComponent() { return getComponent(MarkedPositonComponent.class); } + + public final BuildingWorkerComponent buildingWorkerComponent() { return getComponent(BuildingWorkerComponent.class); } + + public final BricklayerComponent bricklayerComponent() { return getComponent(BricklayerComponent.class); } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/EntityFactory.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/EntityFactory.java new file mode 100644 index 0000000000..31e1b550ec --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/EntityFactory.java @@ -0,0 +1,191 @@ +package jsettlers.logic.movable; + +import jsettlers.common.material.EMaterialType; +import jsettlers.common.movable.EDirection; +import jsettlers.common.movable.EMovableType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.common.selectable.ESelectionType; +import jsettlers.logic.constants.MatchConstants; +import jsettlers.logic.movable.components.AnimationComponent; +import jsettlers.logic.movable.components.AttackableComponent; +import jsettlers.logic.movable.components.BearerBehaviorComponent; +import jsettlers.logic.movable.components.BearerComponent; +import jsettlers.logic.movable.components.BricklayerBehaviorComponent; +import jsettlers.logic.movable.components.BricklayerComponent; +import jsettlers.logic.movable.components.BuildingWorkerBehaviorComponent; +import jsettlers.logic.movable.components.BuildingWorkerComponent; +import jsettlers.logic.movable.components.DonkeyBehaviorComponent; +import jsettlers.logic.movable.components.DonkeyComponent; +import jsettlers.logic.movable.components.GameFieldComponent; +import jsettlers.logic.movable.components.GeologistBehaviorComponent; +import jsettlers.logic.movable.components.MarkedPositonComponent; +import jsettlers.logic.movable.components.MaterialComponent; +import jsettlers.logic.movable.components.MovableComponent; +import jsettlers.logic.movable.components.MultiMaterialComponent; +import jsettlers.logic.movable.components.PlayerComandComponent; +import jsettlers.logic.movable.components.SelectableComponent; +import jsettlers.logic.movable.components.SpecialistComponent; +import jsettlers.logic.movable.components.SteeringComponent; +import jsettlers.logic.movable.interfaces.AbstractMovableGrid; +import jsettlers.logic.movable.interfaces.ILogicMovable; +import jsettlers.logic.player.Player; + +/** + * @author homoroselaps + */ +public final class EntityFactory { + private EntityFactory() {} + + public static ILogicMovable createMovable(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + switch (movableType) { + //case BEARER: + case BRICKLAYER: + case GEOLOGIST: + case SMITH: + case LUMBERJACK: + case STONECUTTER: + case SAWMILLER: + case FORESTER: + case MELTER: + case MINER: + case FISHERMAN: + case FARMER: + case MILLER: + case BAKER: + case PIG_FARMER: + case DONKEY_FARMER: + case SLAUGHTERER: + case CHARCOAL_BURNER: + case WATERWORKER: + case WINEGROWER: + case HEALER: + case DOCKWORKER: + case DONKEY: + return new MovableWrapper(createActiveEntity(grid, movableType, position, player)); + default: + return new Movable(grid, movableType, position, player); + } + } + + private static Entity createActiveEntity(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = createEntity(grid, movableType, position, player); + entity.setActive(true); + return entity; + } + + public static Entity createEntity(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = null; + switch (movableType) { + case BEARER: + entity = createBearer(grid, movableType, position, player); + break; + case SMITH: + case LUMBERJACK: + case STONECUTTER: + case SAWMILLER: + case FORESTER: + case MELTER: + case MINER: + case FISHERMAN: + case FARMER: + case MILLER: + case BAKER: + case PIG_FARMER: + case DONKEY_FARMER: + case SLAUGHTERER: + case CHARCOAL_BURNER: + case WATERWORKER: + case WINEGROWER: + case HEALER: + case DOCKWORKER: + entity = createBuildingWorker(grid, movableType, position, player); + break; + case GEOLOGIST: + entity = createGeologist(grid, movableType, position, player); + break; + case DONKEY: + entity = createDonkey(grid, movableType, position, player); + break; + case BRICKLAYER: + entity = createBricklayer(grid, movableType, position, player); + break; + } + assert entity != null : "Type not found by EntityFactory"; + assert entity.checkComponentDependencies() : "Not all Component dependencies are resolved."; + return entity; + } + + private static Entity createBricklayer(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = new Entity(); + entity.add(new BricklayerComponent()); + entity.add(new BricklayerBehaviorComponent()); + entity.add(new AnimationComponent()); + EDirection dir = EDirection.VALUES[MatchConstants.random().nextInt(EDirection.NUMBER_OF_DIRECTIONS)]; + entity.add(new MovableComponent(movableType, player, position, dir)); + entity.add(new SteeringComponent()); + entity.add(new GameFieldComponent(grid)); + entity.add(new SelectableComponent(ESelectionType.PEOPLE)); + return entity; + } + + private static Entity createBuildingWorker(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = new Entity(); + entity.add(new MaterialComponent()); + entity.add(new BuildingWorkerComponent()); + entity.add(new BuildingWorkerBehaviorComponent()); + entity.add(new AnimationComponent()); + EDirection dir = EDirection.VALUES[MatchConstants.random().nextInt(EDirection.NUMBER_OF_DIRECTIONS)]; + entity.add(new MovableComponent(movableType, player, position, dir)); + entity.add(new SteeringComponent()); + entity.add(new GameFieldComponent(grid)); + entity.add(new SelectableComponent(ESelectionType.PEOPLE)); + entity.add(new MarkedPositonComponent()); + return entity; + } + + private static Entity createDonkey(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = new Entity(); + entity.add(new MultiMaterialComponent()); + entity.add(new DonkeyBehaviorComponent()); + entity.add(new AnimationComponent()); + entity.add(new AttackableComponent(movableType)); + EDirection dir = EDirection.VALUES[MatchConstants.random().nextInt(EDirection.NUMBER_OF_DIRECTIONS)]; + entity.add(new MovableComponent(movableType, player, position, dir)); + entity.add(new SteeringComponent()); + entity.add(new GameFieldComponent(grid)); + entity.add(new DonkeyComponent()); + entity.add(new SelectableComponent(ESelectionType.PEOPLE)); + return entity; + } + + public static Entity createGeologist(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = new Entity(); + entity.add(new GeologistBehaviorComponent()); + entity.add(new SpecialistComponent()); + entity.add(new AnimationComponent()); + entity.add(new AttackableComponent(movableType)); + entity.add(new MaterialComponent()); + entity.add(new MarkedPositonComponent()); + EDirection dir = EDirection.VALUES[MatchConstants.random().nextInt(EDirection.NUMBER_OF_DIRECTIONS)]; + entity.add(new MovableComponent(movableType, player, position, dir)); + entity.add(new SteeringComponent()); + entity.add(new GameFieldComponent(grid)); + entity.add(new SelectableComponent(movableType.selectionType)); + entity.add(new PlayerComandComponent()); + return entity; + } + + public static Entity createBearer(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D position, Player player) { + Entity entity = new Entity(); + entity.add(new BearerBehaviorComponent()); + entity.add(new BearerComponent()); + entity.add(new AnimationComponent()); + entity.add(new MaterialComponent()); + EDirection dir = EDirection.VALUES[MatchConstants.random().nextInt(EDirection.NUMBER_OF_DIRECTIONS)]; + entity.add(new MovableComponent(movableType, player, position, dir)); + entity.add(new SteeringComponent()); + entity.add(new GameFieldComponent(grid)); + entity.add(new SelectableComponent(movableType.selectionType)); + return entity; + } +} \ No newline at end of file diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableBearerWrapper.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableBearerWrapper.java new file mode 100644 index 0000000000..179b14e8c3 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableBearerWrapper.java @@ -0,0 +1,49 @@ +package jsettlers.logic.movable; + +import jsettlers.common.material.EMaterialType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.map.grid.partition.manager.manageables.IManageableBearer; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IBarrack; +import jsettlers.logic.map.grid.partition.manager.materials.interfaces.IMaterialOffer; +import jsettlers.logic.map.grid.partition.manager.materials.interfaces.IMaterialRequest; +import jsettlers.logic.map.grid.partition.manager.objects.WorkerCreationRequest; +import jsettlers.logic.movable.components.BearerComponent; +import jsettlers.logic.movable.components.MovableComponent; + +/** + * @author homoroselaps + */ +public final class ManageableBearerWrapper implements IManageableBearer { + private static final long serialVersionUID = 2252932151684965586L; + + private final Entity entity; + + public ManageableBearerWrapper(Entity entity) { + this.entity = entity; + } + + @Override + public ShortPoint2D getPosition() { + return entity.getComponent(MovableComponent.class).getPosition(); + } + + @Override + public boolean becomeWorker(IWorkerRequester requester, WorkerCreationRequest request) { + return entity.getComponent(BearerComponent.class).becomeWorker(requester, request); + } + + @Override + public boolean becomeWorker(IWorkerRequester requester, WorkerCreationRequest request, IMaterialOffer offer) { + return entity.getComponent(BearerComponent.class).becomeWorker(requester, request, offer); + } + + @Override + public boolean becomeSoldier(IBarrack barrack) { + return entity.getComponent(BearerComponent.class).becomeSoldier(barrack); + } + + @Override + public void deliver(EMaterialType materialType, IMaterialOffer offer, IMaterialRequest request) { + entity.getComponent(BearerComponent.class).deliver(materialType, offer, request); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableBricklayerWrapper.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableBricklayerWrapper.java new file mode 100644 index 0000000000..d4257a7678 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableBricklayerWrapper.java @@ -0,0 +1,25 @@ +package jsettlers.logic.movable; + +import jsettlers.common.movable.EDirection; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.map.grid.partition.manager.manageables.IManageableBricklayer; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IConstructableBuilding; +import jsettlers.logic.movable.components.BricklayerComponent; + +public final class ManageableBricklayerWrapper implements IManageableBricklayer { + private static final long serialVersionUID = 2252932351648921543L; + + private final Entity entity; + + public ManageableBricklayerWrapper(Entity entity) { this.entity = entity; } + + @Override + public ShortPoint2D getPosition() { + return entity.movableComponent().getPosition(); + } + + @Override + public boolean setBricklayerJob(IConstructableBuilding constructionSite, ShortPoint2D bricklayerTargetPos, EDirection direction) { + return entity.getComponent(BricklayerComponent.class).assignBricklayerJob(constructionSite, bricklayerTargetPos, direction); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableWorkerWrapper.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableWorkerWrapper.java new file mode 100644 index 0000000000..e5d22bdf6d --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/ManageableWorkerWrapper.java @@ -0,0 +1,41 @@ +package jsettlers.logic.movable; + +import jsettlers.common.movable.EMovableType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.map.grid.partition.manager.manageables.IManageableWorker; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IWorkerRequestBuilding; +import jsettlers.logic.movable.components.BuildingWorkerComponent; +import jsettlers.logic.movable.components.MovableComponent; + +public final class ManageableWorkerWrapper implements IManageableWorker { + private static final long serialVersionUID = 2252932351688961586L; + + private final Entity entity; + + public ManageableWorkerWrapper(Entity entity) { this.entity = entity; } + + @Override + public EMovableType getMovableType() { + return entity.movableComponent().getMovableType(); + } + + @Override + public void setWorkerJob(IWorkerRequestBuilding building) { + entity.getComponent(BuildingWorkerComponent.class).setWorkerJob(building); + } + + @Override + public void buildingDestroyed() { + entity.getComponent(BuildingWorkerComponent.class).buildingDestroyed(); + } + + @Override + public boolean isAlive() { + return entity.isActive(); + } + + @Override + public ShortPoint2D getPosition() { + return entity.movableComponent().getPosition(); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/Movable.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Movable.java index 484e242fb7..100e368805 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/movable/Movable.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Movable.java @@ -53,12 +53,9 @@ * @author Andreas Eberle */ public final class Movable implements ILogicMovable { + private static final long serialVersionUID = 2472076796407425256L; private static final int SHIP_PUSH_DISTANCE = 10; - private static final HashMap movablesByID = new HashMap<>(); - private static final ConcurrentLinkedQueue allMovables = new ConcurrentLinkedQueue<>(); - private static int nextID = Integer.MIN_VALUE; - protected final AbstractMovableGrid grid; private final int id; private final Player player; @@ -108,29 +105,14 @@ public Movable(AbstractMovableGrid grid, EMovableType movableType, ShortPoint2D RescheduleTimer.add(this, Constants.MOVABLE_INTERRUPT_PERIOD); - this.id = nextID++; - movablesByID.put(this.id, this); - allMovables.offer(this); + this.id = MovableDataManager.getNextID(); + MovableDataManager.add( this); grid.enterPosition(position, this, true); } - @SuppressWarnings("unchecked") - public static void readStaticState(ObjectInputStream ois) throws IOException, ClassNotFoundException { - nextID = ois.readInt(); - allMovables.clear(); - allMovables.addAll((Collection) ois.readObject()); - movablesByID.putAll((Map) ois.readObject()); - } - - public static void writeStaticState(ObjectOutputStream oos) throws IOException { - oos.writeInt(nextID); - oos.writeObject(allMovables); - oos.writeObject(movablesByID); - } - /** - * Tests if this movable can receive moveTo requests and if so, directs it to go to the given position. + * Tests if this movable can receive sendMoveToCommand requests and if so, directs it to go to the given position. * * @param targetPosition * Desired position the movable should move to @@ -785,28 +767,6 @@ private void setState(EMovableState newState) { this.state = newState; } - /** - * Used for networking to identify movables over the network. - * - * @param id - * id to be looked for - * @return returns the movable with the given ID
- * or null if the id can not be found - */ - public static ILogicMovable getMovableByID(int id) { - return movablesByID.get(id); - } - - public static ConcurrentLinkedQueue getAllMovables() { - return allMovables; - } - - public static void resetState() { - allMovables.clear(); - movablesByID.clear(); - nextID = Integer.MIN_VALUE; - } - /** * kills this movable. */ @@ -827,8 +787,7 @@ public final void kill() { this.state = EMovableState.DEAD; this.selected = false; - movablesByID.remove(this.getID()); - allMovables.remove(this); + MovableDataManager.remove(this); } /** diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableDataManager.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableDataManager.java new file mode 100644 index 0000000000..71fb7f4427 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableDataManager.java @@ -0,0 +1,73 @@ +package jsettlers.logic.movable; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentLinkedQueue; + +import jsettlers.logic.movable.interfaces.ILogicMovable; + +/** + * @author homoroselaps + */ + +public final class MovableDataManager { + private static final HashMap movablesByID = new HashMap<>(); + private static final ConcurrentLinkedQueue allMovables = new ConcurrentLinkedQueue<>(); + + private static int nextID = Integer.MIN_VALUE; + + /** + * Used for networking to identify movables over the network. + * + * @param id + * id to be looked for + * @return returns the movable with the given ID
+ * or null if the id can not be found + */ + public static ILogicMovable getMovableByID(int id) { + return movablesByID.get(id); + } + + public static Collection getAllMovables() { + return allMovables; + } + + public static void add(ILogicMovable movable) { + movablesByID.put(movable.getID(), movable); + allMovables.offer(movable); + } + + public static void remove(ILogicMovable movable) { + movablesByID.remove(movable.getID()); + allMovables.remove(movable); + } + + public static void resetState() { + allMovables.clear(); + movablesByID.clear(); + nextID = Integer.MIN_VALUE; + } + + static int getNextID() { + return nextID++; + } + + public static void writeStaticState(ObjectOutputStream oos) throws IOException { + oos.writeObject(movablesByID); + oos.writeObject(allMovables); + oos.writeInt(nextID); + } + + @SuppressWarnings("unchecked") + public static void readStaticState(ObjectInputStream ois) throws IOException, ClassNotFoundException { + movablesByID.clear(); + movablesByID.putAll((Map) ois.readObject()); + allMovables.clear(); + allMovables.addAll((Collection) ois.readObject()); + nextID = ois.readInt(); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableStrategy.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableStrategy.java index b0afae1443..f344f3211a 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableStrategy.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableStrategy.java @@ -219,7 +219,7 @@ protected final void abortPath() { * Checks preconditions before the next path step can be gone. * * @param pathTarget - * Target of the current path. + * target of the current path. * @param step * The number of the current step where 1 means the first step. * @return true if the path should be continued
diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableWrapper.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableWrapper.java new file mode 100644 index 0000000000..6ecd65f276 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/MovableWrapper.java @@ -0,0 +1,297 @@ +package jsettlers.logic.movable; + +import java.io.Serializable; +import java.util.List; + +import jsettlers.algorithms.path.Path; +import jsettlers.common.buildings.EBuildingType; +import jsettlers.common.material.EMaterialType; +import jsettlers.common.movable.EDirection; +import jsettlers.common.movable.EMovableAction; +import jsettlers.common.movable.EMovableType; +import jsettlers.common.movable.IMovable; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.common.selectable.ESelectionType; +import jsettlers.logic.buildings.military.IBuildingOccupyableMovable; +import jsettlers.logic.buildings.military.occupying.IOccupyableBuilding; +import jsettlers.logic.movable.components.AnimationComponent; +import jsettlers.logic.movable.components.AttackableComponent; +import jsettlers.logic.movable.components.BuildingWorkerComponent; +import jsettlers.logic.movable.components.MaterialComponent; +import jsettlers.logic.movable.components.MovableComponent; +import jsettlers.logic.movable.components.PlayerComandComponent; +import jsettlers.logic.movable.components.SelectableComponent; +import jsettlers.logic.movable.components.SteeringComponent; +import jsettlers.logic.movable.interfaces.IAttackable; +import jsettlers.logic.movable.interfaces.ILogicMovable; +import jsettlers.logic.player.Player; + +/** + * @author homoroselaps + */ +public final class MovableWrapper implements ILogicMovable, Serializable { + private static final long serialVersionUID = -2853861825853788354L; + + private final Entity entity; + + public MovableWrapper(Entity entity) { + this.entity = entity; + } + + //region Interface Implementations + @Override + public short getViewDistance() { + return entity.getComponentOptional(MovableComponent.class).map(MovableComponent::getViewDistance).orElse((short) 0); + } + + @Override + public boolean needsPlayersGround() { + return entity.getComponentOptional(MovableComponent.class).map(MovableComponent::needsPlayersGround).orElse(false); + } + + @Override + public boolean isShip() { + return false; + } + + @Override + public EDirection getDirection() { + return entity.getComponentOptional(MovableComponent.class).map(MovableComponent::getViewDirection).orElse(EDirection.NORTH_EAST); + } + + @Override + public EMovableAction getAction() { + return entity.getComponentOptional(AnimationComponent.class).map(AnimationComponent::getAnimation).orElse(EMovableAction.NO_ACTION); + } + + @Override + public float getMoveProgress() { + return entity.getComponentOptional(AnimationComponent.class).map(AnimationComponent::getAnimationProgress).orElse(0f); + } + + @Override + public EMaterialType getMaterial() { + return entity.getComponentOptional(MaterialComponent.class).map(MaterialComponent::getMaterial).orElse(EMaterialType.NO_MATERIAL); + } + + @Override + public void receiveHit(float strength, ShortPoint2D attackerPos, byte attackingPlayer) { + entity.getComponentOptional(AttackableComponent.class).ifPresent(component -> component.receiveHit(strength, attackerPos, attackingPlayer)); + } + + @Override + public float getHealth() { + return entity.getComponentOptional(AttackableComponent.class).map(AttackableComponent::getHealth).orElse(entity.movableComponent().getMovableType().getHealth()); + } + + @Override + public boolean isAlive() { + return entity.getComponentOptional(AttackableComponent.class).map(c -> c.getHealth() > 0).orElse(true); + } + + @Override + public boolean isRightstep() { + return entity.getComponentOptional(AnimationComponent.class).map(AnimationComponent::isRightStep).orElse(false); + } + + @Override + public void stopOrStartWorking(boolean stop) { + entity.getComponentOptional(PlayerComandComponent.class).ifPresent(component -> { + if (stop) { + component.sendStopWorkCommand(); + } else { + component.sendStartWorkCommand(); + } + }); + } + + @Override + public List getPassengers() { + return null; + } + + @Override + public int getNumberOfCargoStacks() { + return 0; + } + + @Override + public EMaterialType getCargoType(int stack) { + return null; + } + + @Override + public int getCargoCount(int stack) { + return 0; + } + + @Override + public EBuildingType getGarrisonedBuildingType() { + return entity.getComponentOptional(BuildingWorkerComponent.class).map(BuildingWorkerComponent::getBuildingType).orElse(null); + } + + @Override + public boolean isSelected() { + return entity.getComponentOptional(SelectableComponent.class).map(SelectableComponent::isSelected).orElse(false); + } + + @Override + public void setSelected(boolean selected) { + entity.getComponentOptional(SelectableComponent.class).ifPresent(component -> component.setSelected(selected)); + } + + @Override + public ESelectionType getSelectionType() { + return entity.getComponentOptional(SelectableComponent.class).map(SelectableComponent::getSelectionType).orElse(ESelectionType.PEOPLE); + } + + @Override + public boolean isAttackable() { + return entity.getComponentOptional(AttackableComponent.class).map(AttackableComponent::isAttackable).orElse(false); + } + + @Override + public void setSoundPlayed() { + entity.getComponent(AnimationComponent.class).setSoundPlayed(); + } + + @Override + public boolean isSoundPlayed() { + return entity.getComponentOptional(AnimationComponent.class).map(AnimationComponent::isSoundPlayed).orElse(false); + } + + @Override + public EMovableType getMovableType() { + return entity.getComponent(MovableComponent.class).getMovableType(); + } + + @Override + public boolean isTower() { + return false; + } + + @Override + public void debug() { + System.out.println("debug: " + entity); + entity.toggleDebug(); + } + + @Override + public void informAboutAttackable(IAttackable attackable) { + entity.getComponent(AttackableComponent.class).informAboutAttackable((ILogicMovable) attackable); + } + + @Override + public boolean push(ILogicMovable pushingMovable) { + entity.raiseNotification(new SteeringComponent.LeavePositionRequest(pushingMovable)); + return false; + } + + @Override + public Path getPath() { + return null; + } + + @Override + public void goSinglePathStep() { + assert false : "not implemented"; + } + + @Override + public ShortPoint2D getPosition() { + return entity.movableComponent().getPosition(); + } + + @Override + public ILogicMovable getPushedFrom() { + assert false : "not implemented"; + return null; + } + + @Override + public boolean isProbablyPushable(ILogicMovable pushingMovable) { + //assert false: "not implemented"; + return false; + } + + @Override + public void leavePosition() { + // The same as push - request to leave the place + //TODO: call to new implementation of push + } + + @Override + public boolean canOccupyBuilding() { + //TODO: this method has no right to exist, refactor together with @setOccupyableBuilding + return false; + //return entity.getComponent(SelectableComponent.class).getSelectionType() == ESelectionType.SOLDIERS; + } + + @Override + public IBuildingOccupyableMovable setOccupyableBuilding(IOccupyableBuilding building) { + //TODO: rename to occupyBuilding + return null; + } + + @Override + public void checkPlayerOfPosition(Player playerOfPosition) { + //TODO: rename to: player of current position changed + //TODO: implement event + } + + @Override + public void convertTo(EMovableType newMovableType) { + entity.movableComponent().convertTo(newMovableType); + } + + @Override + public Player getPlayer() { + //TODO: switch to playerID or player everywhere + return entity.getComponent(MovableComponent.class).getPlayer(); + } + + @Override + public void moveTo(ShortPoint2D targetPosition) { + entity.getComponentOptional(PlayerComandComponent.class).ifPresent(component -> component.sendMoveToCommand(targetPosition)); + } + + @Override + public void unloadFerry() { + if (this.getMovableType() != EMovableType.FERRY) { + return; + } + //TODO call method of ferry Component + } + + @Override + public boolean addPassenger(ILogicMovable movable) { + return false; + } + + @Override + public void moveToFerry(ILogicMovable ferry, ShortPoint2D entrancePosition) { + assert false: "not implemented"; + } + + @Override + public void leaveFerryAt(ShortPoint2D position) { + assert false: "not implemented"; + } + + @Override + public int getID() { + return entity.getID(); + } + + @Override + public int timerEvent() { + return entity.timerEvent(); + } + + @Override + public void kill() { + entity.kill(); + } + + //endregion +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/Notification.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Notification.java new file mode 100644 index 0000000000..ce028e5333 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Notification.java @@ -0,0 +1,5 @@ +package jsettlers.logic.movable; + +import java.io.Serializable; + +public abstract class Notification implements Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/Requires.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Requires.java new file mode 100644 index 0000000000..b038de688b --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/Requires.java @@ -0,0 +1,17 @@ +package jsettlers.logic.movable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jsettlers.logic.movable.components.Component; + +/** + * @author homoroselaps + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Requires { + Class[] value(); +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/AnimationComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/AnimationComponent.java new file mode 100644 index 0000000000..04ea850723 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/AnimationComponent.java @@ -0,0 +1,86 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.movable.EMovableAction; +import jsettlers.logic.constants.MatchConstants; +import jsettlers.logic.movable.Notification; + +/** + * @author homoroselaps + */ +public class AnimationComponent extends Component { + private static final long serialVersionUID = 8064683552580286008L; + + private EMovableAction animation = EMovableAction.NO_ACTION; + private int animationStartTime; + private short animationDuration; + private boolean isSoundPlayed = false; + private boolean isRightStep = false; + private boolean isChained = false; + + public static class AnimationFinishedNotification extends Notification { + public final EMovableAction type; + + public AnimationFinishedNotification(EMovableAction animationType) { + this.type = animationType; + } + } + + public AnimationComponent() { } + + @Override + protected void onUpdate() { + if (animation != EMovableAction.NO_ACTION && isAnimationFinished()) { + stopAnimation(); + } + } + + @Override + protected void onLateUpdate() { + if (!isAnimationFinished()) { entity.setInvocationDelay(getRemainingTime()); } + } + + public EMovableAction getAnimation() { + return animation; + } + + public float getAnimationProgress() { + return ((float) (MatchConstants.clock().getTime() - animationStartTime)) / animationDuration; + } + + public boolean isAnimationFinished() { + return animationStartTime + animationDuration <= MatchConstants.clock().getTime(); + } + + public void startAnimation(EMovableAction animation, short duration, boolean isChained) { + this.animationStartTime = MatchConstants.clock().getTime(); + this.animationDuration = duration; + this.animation = animation; + this.isSoundPlayed = false; + this.isChained = isChained; + } + + private void stopAnimation() { + this.entity.raiseNotification(new AnimationFinishedNotification(this.animation)); + if (!isChained) this.animation = EMovableAction.NO_ACTION; + } + + public boolean isRightStep() { + return isRightStep; + } + + public void switchStep() { + isRightStep = !isRightStep; + } + + public void setSoundPlayed() { + isSoundPlayed = true; + } + + public boolean isSoundPlayed() { + return isSoundPlayed; + } + + public int getRemainingTime() { + return animationStartTime + animationDuration - MatchConstants.clock().getTime(); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/AttackableComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/AttackableComponent.java new file mode 100644 index 0000000000..9b7aa23f0b --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/AttackableComponent.java @@ -0,0 +1,47 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.movable.EMovableType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.movable.Notification; +import jsettlers.logic.movable.interfaces.ILogicMovable; + +/** + * @author homoroselaps + */ + +public class AttackableComponent extends Component { + public static class ReceivedHit extends Notification {} + + private static final long serialVersionUID = -5453513130369184993L; + + private float health; + private boolean isAttackable = false; + + public AttackableComponent(EMovableType movableType) { + this.health = movableType.getHealth(); + } + + public boolean isAttackable() { return isAttackable; } + + public void isAttackable(boolean isAttackable) { this.isAttackable = isAttackable; } + + public void receiveHit(float strength, ShortPoint2D attackerPos, byte attackingPlayer) { + health -= strength; + entity.raiseNotification(new ReceivedHit()); + } + + public float getHealth() { + return health; + } + + public void setHealth(float health) { this.health = health; } + + public void informAboutAttackable(ILogicMovable other) { + assert false : "Not implemented"; + } + + @Override + protected void onDestroy() { + health = -200; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BearerBehaviorComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BearerBehaviorComponent.java new file mode 100644 index 0000000000..08b75b986a --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BearerBehaviorComponent.java @@ -0,0 +1,249 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.material.EMaterialType; +import jsettlers.common.movable.EMovableAction; +import jsettlers.common.movable.EMovableType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.constants.Constants; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.ManageableBearerWrapper; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.nodes.Action; + +import static jsettlers.logic.movable.BehaviorTreeHelper.action; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysFail; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysSucceed; +import static jsettlers.logic.movable.BehaviorTreeHelper.condition; +import static jsettlers.logic.movable.BehaviorTreeHelper.convertTo; +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; +import static jsettlers.logic.movable.BehaviorTreeHelper.dropMaterial; +import static jsettlers.logic.movable.BehaviorTreeHelper.guard; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.selector; +import static jsettlers.logic.movable.BehaviorTreeHelper.sequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.setIdleBehaviorActiveWhile; +import static jsettlers.logic.movable.BehaviorTreeHelper.sleep; +import static jsettlers.logic.movable.BehaviorTreeHelper.startAndWaitForAnimation; +import static jsettlers.logic.movable.BehaviorTreeHelper.triggerGuard; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForTargetReachedAndFailIfNotReachable; + +/** + * @author homoroselaps + */ + +@Requires({ + MaterialComponent.class, + BearerComponent.class, + SteeringComponent.class, + GameFieldComponent.class, + AnimationComponent.class, + MovableComponent.class +}) +public final class BearerBehaviorComponent extends BehaviorComponent { + private static final long serialVersionUID = -4581600901753172458L; + + @Override + protected Node createBehaviorTree() { + return setIdleBehaviorActiveWhile(false, + selector( + triggerGuard(BearerComponent.DeliveryJob.class, + guard("DeliveryJob", c -> c.entity.bearerComponent().hasJob(), false, + debug("accepting delivery job", acceptDeliveryJob()) + ) + ), + triggerGuard(BearerComponent.BecomeSoldierJob.class, + guard("BecomeSoldierJob", c -> c.entity.bearerComponent().hasJob(), false, + debug("accepting become soldier job", acceptBecomeSoldierJob()) + ) + ), + triggerGuard(BearerComponent.BecomeWorkerJob.class, + guard("BecomeWorkerJob", c -> c.entity.bearerComponent().hasJob(), false, + debug("accepting become worker job", acceptBecomeWorkerJob()) + ) + ), + guard(c -> c.entity.bearerComponent().hasBecomeWorkerJob(), true, + selector("hasBecomeWorkerJob", + memSequence("try to fulfil the job", + alwaysSucceed(guard("grab a tool if needed", c -> c.entity.bearerComponent().materialOffer != null, + selector( + memSequence( + action("go to the tool", c -> { + c.entity.steeringComponent().setTarget(c.entity.bearerComponent().materialOffer.getPosition()); + }), + waitForTargetReachedAndFailIfNotReachable(), + condition("can we pick it up?", BearerBehaviorComponent::canTakeMaterial), + startAndWaitForAnimation(EMovableAction.BEND_DOWN, Constants.MOVABLE_BEND_DURATION), + tryTakeMaterialFromMap(), + startAndWaitForAnimation(EMovableAction.RAISE_UP, Constants.MOVABLE_BEND_DURATION) + ), + sequence("handle failure", + action(BearerBehaviorComponent::distributionAborted), + alwaysFail() + ) + ) + )), + action("convert Entity to a worker", c -> { + convertTo(c.entity, c.entity.bearerComponent().workerCreationRequest.requestedMovableType()); + }) + ), + sequence("handle failure", + action(BearerBehaviorComponent::workerCreationRequestFailed), + action(BearerBehaviorComponent::resetJob), + alwaysFail() + ) + ) + ), + guard(c -> c.entity.bearerComponent().hasDeliveryJob(), true, + memSequence( + selector( + memSequence("go to materialOffer and take material", + action(c->{c.entity.steeringComponent().setTarget(c.entity.bearerComponent().materialOffer.getPosition());}), + waitForTargetReachedAndFailIfNotReachable(), + debug("can take material", condition(BearerBehaviorComponent::canTakeMaterial)), + startAndWaitForAnimation(EMovableAction.BEND_DOWN, c->Constants.MOVABLE_BEND_DURATION, true), + tryTakeMaterialFromMap(), + startAndWaitForAnimation(EMovableAction.RAISE_UP, Constants.MOVABLE_BEND_DURATION) + ), + sequence("handle failure", + action(BearerBehaviorComponent::distributionAborted), + action(BearerBehaviorComponent::deliveryAborted), + action(BearerBehaviorComponent::resetJob), + alwaysFail() + ) + ), + selector( + memSequence("go to request & drop material", + action(c -> { + c.entity.steeringComponent().setTarget(c.entity.bearerComponent().deliveryRequest.getPosition()); + }), + waitForTargetReachedAndFailIfNotReachable(), + condition(c -> c.entity.bearerComponent().materialType.isDroppable()), + startAndWaitForAnimation(EMovableAction.BEND_DOWN, c->Constants.MOVABLE_BEND_DURATION, true), + guard(BearerBehaviorComponent::canFulfillRequest, sequence( + dropMaterial(c->c.entity.materialComponent().getMaterial()), + action(BearerBehaviorComponent::deliveryFulfilled) + )), + startAndWaitForAnimation(EMovableAction.RAISE_UP, Constants.MOVABLE_BEND_DURATION), + action(BearerBehaviorComponent::resetJob) + ), + sequence("handle failure", + debug("reoffer the material", dropMaterial(c->c.entity.materialComponent().getMaterial())), + action(BearerBehaviorComponent::deliveryAborted), + action(BearerBehaviorComponent::resetJob), + alwaysFail() + ) + ) + ) + ), + guard(c -> c.entity.bearerComponent().hasBecomeSoldierJob(), true, + selector( + memSequence("become a soldier", + action(c -> { + c.entity.steeringComponent().setTarget(c.entity.bearerComponent().barrack.getDoor()); + }), + waitForTargetReachedAndFailIfNotReachable(), + tryTakeWeapon_ConvertToSoldier() + ), + sequence("handle failure", + action(BearerBehaviorComponent::bearerRequestFailed), + action(BearerBehaviorComponent::resetJob), + alwaysFail() + ) + ) + ), + debug("idle behavior", + setIdleBehaviorActiveWhile(true, + sleep(1000) + ) + ) + ) + ); + } + + private static Action acceptDeliveryJob() { + return new Action<>(context -> { + context.component.forFirstNotificationOfTypeC(BearerComponent.DeliveryJob.class, job -> { + job.offer.distributionAccepted(); + job.request.deliveryAccepted(); + context.entity.bearerComponent().setDeliveryJob(job); + }, true); + }); + } + + private static Action acceptBecomeSoldierJob() { + return new Action<>(context -> { + context.component.forFirstNotificationOfTypeC(BearerComponent.BecomeSoldierJob.class, context.entity.bearerComponent()::setBecomeSoldierJob, true); + }); + } + + private static Action acceptBecomeWorkerJob() { + return new Action<>(context -> { + context.component.forFirstNotificationOfTypeC(BearerComponent.BecomeWorkerJob.class, job -> { + if (job.offer != null) job.offer.distributionAccepted(); + context.entity.bearerComponent().setBecomeWorkerJob(job); + }, true); + }); + } + + private static Node tryTakeMaterialFromMap() { + return debug("try take material", new Action<>(c -> { + EMaterialType materialToTake = c.entity.bearerComponent().materialType; + if (c.entity.gameFieldComponent().movableGrid.takeMaterial(c.entity.movableComponent().getPosition(), materialToTake)) { + c.entity.materialComponent().setMaterial(materialToTake); + c.entity.bearerComponent().materialOffer.offerTaken(); + return NodeStatus.SUCCESS; + } + return NodeStatus.FAILURE; + })); + } + + private static Action tryTakeWeapon_ConvertToSoldier() { + return new Action<>(c -> { + ShortPoint2D targetPosition = c.entity.bearerComponent().barrack.getSoldierTargetPosition(); + EMovableType type = c.entity.bearerComponent().barrack.popWeaponForBearer(); + if (type != null) { + convertTo(c.entity, type); + c.entity.steeringComponent().setTarget(targetPosition); + c.entity.movableComponent().getPlayer().getEndgameStatistic().incrementAmountOfProducedSoldiers(); + return NodeStatus.SUCCESS; + } + return NodeStatus.FAILURE; + }); + } + + private static boolean canFulfillRequest(Context c) { + return c.entity.bearerComponent().deliveryRequest.isActive() && c.entity.bearerComponent().deliveryRequest.getPosition().equals(c.entity.movableComponent().getPosition()); + } + + private static boolean canTakeMaterial(Context c) { + EMaterialType materialToTake = c.entity.bearerComponent().materialType; + return c.entity.gameFieldComponent().movableGrid.canTakeMaterial(c.entity.movableComponent().getPosition(), materialToTake); + } + + private static void bearerRequestFailed(Context c) { + c.entity.bearerComponent().barrack.bearerRequestFailed(); + } + + private static void resetJob(Context c) { + c.entity.bearerComponent().resetJob(); + c.entity.gameFieldComponent().movableGrid.addJobless(new ManageableBearerWrapper(c.entity)); + } + + private static void distributionAborted(Context c) { + c.entity.bearerComponent().materialOffer.distributionAborted(); + } + + private static void deliveryFulfilled(Context c) { + c.entity.bearerComponent().deliveryRequest.deliveryFulfilled(); + } + + private static void deliveryAborted(Context c) { + c.entity.bearerComponent().deliveryRequest.deliveryAborted(); + } + + private static void workerCreationRequestFailed(Context c) { + c.entity.bearerComponent().workerRequester.workerCreationRequestFailed(c.entity.bearerComponent().workerCreationRequest); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BearerComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BearerComponent.java new file mode 100644 index 0000000000..c7b6f0f491 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BearerComponent.java @@ -0,0 +1,150 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.material.EMaterialType; +import jsettlers.logic.map.grid.partition.manager.manageables.IManageableBearer; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IBarrack; +import jsettlers.logic.map.grid.partition.manager.materials.interfaces.IMaterialOffer; +import jsettlers.logic.map.grid.partition.manager.materials.interfaces.IMaterialRequest; +import jsettlers.logic.map.grid.partition.manager.objects.WorkerCreationRequest; +import jsettlers.logic.movable.ManageableBearerWrapper; +import jsettlers.logic.movable.Notification; + +/** + * @author homoroselaps + */ + +public class BearerComponent extends Component { + private static final long serialVersionUID = -3315837668805312398L; + + public static class DeliveryJob extends Notification { + public final EMaterialType materialType; + public final IMaterialOffer offer; + public final IMaterialRequest request; + + public DeliveryJob(EMaterialType materialType, IMaterialOffer offer, IMaterialRequest request) { + this.offer = offer; + this.request = request; + this.materialType = materialType; + } + } + + public static class BecomeWorkerJob extends Notification { + public final IManageableBearer.IWorkerRequester requester; + public final WorkerCreationRequest workerCreationRequest; + public final IMaterialOffer offer; + + public BecomeWorkerJob(IManageableBearer.IWorkerRequester requester, WorkerCreationRequest workerCreationRequest, IMaterialOffer offer) { + this.requester = requester; + this.workerCreationRequest = workerCreationRequest; + this.offer = offer; + } + } + + public static class BecomeSoldierJob extends Notification { + public final IBarrack barrack; + + public BecomeSoldierJob(IBarrack barrack) { + this.barrack = barrack; + } + } + + public EMaterialType materialType; + IMaterialOffer materialOffer; + IMaterialRequest deliveryRequest; + + IManageableBearer.IWorkerRequester workerRequester; + WorkerCreationRequest workerCreationRequest; + + public IBarrack barrack; + + private boolean hasDeliveryJob = false; + private boolean hasBecomeWorkerJob = false; + private boolean hasBecomeSoldierJob = false; + + public boolean hasJob() { + return hasBecomeSoldierJob || hasDeliveryJob || hasBecomeWorkerJob; + } + + public void setBecomeSoldierJob(BecomeSoldierJob job) { + assert job != null : "No Null"; + resetJob(); + barrack = job.barrack; + hasBecomeSoldierJob = true; + } + + public boolean hasBecomeSoldierJob() { + return hasBecomeSoldierJob; + } + + public void setDeliveryJob(DeliveryJob job) { + assert job != null : "No Null"; + resetJob(); + materialType = job.materialType; + materialOffer = job.offer; + deliveryRequest = job.request; + hasDeliveryJob = true; + } + + public boolean hasDeliveryJob() { + return hasDeliveryJob; + } + + public void setBecomeWorkerJob(BecomeWorkerJob job) { + assert job != null : "No Null"; + resetJob(); + materialOffer = job.offer; + workerRequester = job.requester; + workerCreationRequest = job.workerCreationRequest; + hasBecomeWorkerJob = true; + } + + @Override + protected void onWakeUp() { + this.entity.gameFieldComponent().movableGrid.addJobless(new ManageableBearerWrapper(this.entity)); + } + + public boolean hasBecomeWorkerJob() { + return hasBecomeWorkerJob; + } + + public void resetJob() { + hasDeliveryJob = false; + hasBecomeSoldierJob = false; + hasBecomeWorkerJob = false; + + materialOffer = null; + workerRequester = null; + workerCreationRequest = null; + barrack = null; + deliveryRequest = null; + materialType = EMaterialType.NO_MATERIAL; + } + + public void deliver(EMaterialType materialType, IMaterialOffer offer, IMaterialRequest request) { + entity.raiseNotification(new DeliveryJob(materialType, offer, request)); + } + + public boolean becomeWorker(IManageableBearer.IWorkerRequester requester, WorkerCreationRequest workerCreationRequest) { + if (!hasJob()) { + entity.raiseNotification(new BecomeWorkerJob(requester, workerCreationRequest, null)); + return true; + } + return false; + } + + public boolean becomeWorker(IManageableBearer.IWorkerRequester requester, WorkerCreationRequest workerCreationRequest, IMaterialOffer offer) { + if (!hasJob()) { + entity.raiseNotification(new BecomeWorkerJob(requester, workerCreationRequest, offer)); + return true; + } + return false; + } + + public boolean becomeSoldier(IBarrack barrack) { + if (!hasJob()) { + entity.raiseNotification(new BecomeSoldierJob(barrack)); + return true; + } + return false; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BehaviorComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BehaviorComponent.java new file mode 100644 index 0000000000..6a6f6d7a06 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BehaviorComponent.java @@ -0,0 +1,30 @@ +package jsettlers.logic.movable.components; + +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.Root; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; + +/** + * @author homoroselaps + */ +public abstract class BehaviorComponent extends Component { + private static final long serialVersionUID = -7388888039559869043L; + + private Tick tick; + + @Override + protected void onWakeUp() { + tick = new Tick<>(new Context(entity, this), new Root<>(debug("==== of " + entity.getID(), createBehaviorTree()))); + } + + @Override + protected void onUpdate() { + tick.target.debugLevel = 0; + tick.tick(); + } + + protected abstract Node createBehaviorTree(); +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BricklayerBehaviorComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BricklayerBehaviorComponent.java new file mode 100644 index 0000000000..112c71fa1e --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BricklayerBehaviorComponent.java @@ -0,0 +1,90 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.movable.EMovableAction; +import jsettlers.logic.constants.Constants; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.nodes.Repeat; + +import static jsettlers.logic.movable.BehaviorTreeHelper.action; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysFail; +import static jsettlers.logic.movable.BehaviorTreeHelper.condition; +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; +import static jsettlers.logic.movable.BehaviorTreeHelper.guard; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.repeat; +import static jsettlers.logic.movable.BehaviorTreeHelper.selector; +import static jsettlers.logic.movable.BehaviorTreeHelper.sequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.setIdleBehaviorActiveWhile; +import static jsettlers.logic.movable.BehaviorTreeHelper.sleep; +import static jsettlers.logic.movable.BehaviorTreeHelper.startAndWaitForAnimation; +import static jsettlers.logic.movable.BehaviorTreeHelper.triggerGuard; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForTargetReachedAndFailIfNotReachable; + +/** + * @author homoroselaps + */ + +@Requires({ + MaterialComponent.class, + SteeringComponent.class, + GameFieldComponent.class, + AnimationComponent.class, + MovableComponent.class +}) +public final class BricklayerBehaviorComponent extends BehaviorComponent { + private static final long serialVersionUID = -4581601951753172458L; + + @Override + protected Node createBehaviorTree() { + return setIdleBehaviorActiveWhile(false, + selector( + triggerGuard(BricklayerComponent.BricklayerJob.class, + action("accepting Bricklayer job", context -> { + context.component.forFirstNotificationOfTypeC(BricklayerComponent.BricklayerJob.class, job -> { + context.entity.bricklayerComponent().setBricklayerJob(job); + }, true); + }) + ), + guard("has active bricklayer job",c -> c.entity.bricklayerComponent().hasJob() && c.entity.bricklayerComponent().isBricklayerRequestActive(), + selector( + memSequence( + action(c->{c.entity.steeringComponent().setTarget(c.entity.bricklayerComponent().getBricklayerTargetPos());}), + selector( + waitForTargetReachedAndFailIfNotReachable(), + sequence( + abortJob(), + alwaysFail() + ) + ), + action("look in direction", c -> { c.entity.movableComponent().setViewDirection(c.entity.bricklayerComponent().getLookDirection());}), + repeat("try to build", Repeat.Policy.NONPREEMPTIVE, + condition(c -> c.entity.bricklayerComponent().isBricklayerRequestActive()), + memSequence( + action("try take material", c -> { return NodeStatus.of(c.entity.bricklayerComponent().tryTakeMaterialFromConstructionSite()); } ), + startAndWaitForAnimation(EMovableAction.ACTION1, Constants.BRICKLAYER_ACTION_DURATION) + ) + ) + ), + jobFinished() + ) + ), + debug("idle behavior", + setIdleBehaviorActiveWhile(true, + sleep(1000) + ) + ) + ) + ); + } + + private static Node jobFinished() { + return debug("job finished",action(c->{c.entity.bricklayerComponent().jobFinished();})); + } + + private static Node abortJob() { + return debug("abort job",action(c->{c.entity.bricklayerComponent().abortJob();})); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BricklayerComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BricklayerComponent.java new file mode 100644 index 0000000000..cd1bc04743 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BricklayerComponent.java @@ -0,0 +1,79 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.movable.EDirection; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IConstructableBuilding; +import jsettlers.logic.movable.ManageableWorkerWrapper; +import jsettlers.logic.movable.Notification; + +/** + * @author homoroselaps + */ + +public class BricklayerComponent extends Component { + private static final long serialVersionUID = -3315837368825352398L; + + private IConstructableBuilding constructionSite = null; + private ShortPoint2D bricklayerTargetPos = null; + private EDirection lookDirection = null; + + public static class BricklayerJob extends Notification { + public final IConstructableBuilding constructionSite; + public final ShortPoint2D bricklayerTargetPos; + public final EDirection lookDirection; + + public BricklayerJob(IConstructableBuilding constructionSite, ShortPoint2D bricklayerTargetPos, EDirection lookDirection) { + this.constructionSite = constructionSite; + this.bricklayerTargetPos = bricklayerTargetPos; + this.lookDirection = lookDirection; + } + } + + public ShortPoint2D getBricklayerTargetPos() { return bricklayerTargetPos; } + + public EDirection getLookDirection() { return lookDirection; } + + public boolean hasJob() { + return constructionSite != null; + } + + public void resetJob() { + constructionSite = null; + bricklayerTargetPos = null; + lookDirection = null; + } + + public void jobFinished() { + resetJob(); + entity.gameFieldComponent().movableGrid.addJobless(new ManageableWorkerWrapper(entity)); + } + + public void abortJob() { + if (constructionSite != null) { + constructionSite.bricklayerRequestFailed(bricklayerTargetPos, lookDirection); + } + jobFinished(); + } + + public void setBricklayerJob(BricklayerJob job) { + constructionSite = job.constructionSite; + bricklayerTargetPos = job.bricklayerTargetPos; + lookDirection = job.lookDirection; + } + + public boolean assignBricklayerJob(IConstructableBuilding constructionSite, ShortPoint2D bricklayerTargetPos, EDirection direction) { + if (!hasJob()) { + entity.raiseNotification(new BricklayerComponent.BricklayerJob(constructionSite, bricklayerTargetPos, direction)); + return true; + } + return false; + } + + public boolean isBricklayerRequestActive() { + return constructionSite != null && constructionSite.isBricklayerRequestActive(); + } + + public boolean tryTakeMaterialFromConstructionSite() { + return constructionSite != null && constructionSite.tryToTakeMaterial(); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BuildingWorkerBehaviorComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BuildingWorkerBehaviorComponent.java new file mode 100644 index 0000000000..3c67e0642c --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BuildingWorkerBehaviorComponent.java @@ -0,0 +1,527 @@ +package jsettlers.logic.movable.components; + +import com.sun.net.httpserver.Authenticator; + +import jsettlers.algorithms.path.Path; +import jsettlers.common.buildings.EBuildingType; +import jsettlers.common.buildings.jobs.EBuildingJobType; +import jsettlers.common.buildings.jobs.IBuildingJob; +import jsettlers.common.mapobject.EMapObjectType; +import jsettlers.common.material.EMaterialType; +import jsettlers.common.material.EPriority; +import jsettlers.common.movable.EDirection; +import jsettlers.common.movable.EMovableAction; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.buildings.workers.DockyardBuilding; +import jsettlers.logic.buildings.workers.MillBuilding; +import jsettlers.logic.buildings.workers.SlaughterhouseBuilding; +import jsettlers.logic.constants.Constants; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IWorkerRequestBuilding; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.EGoInDirectionMode; +import jsettlers.logic.movable.MovableWrapper; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.simplebehaviortree.IBooleanConditionFunction; +import jsettlers.logic.movable.simplebehaviortree.INodeStatusActionConsumer; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.nodes.Action; +import jsettlers.logic.movable.simplebehaviortree.nodes.AlwaysSucceed; + +import static jsettlers.logic.movable.BehaviorTreeHelper.action; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysFail; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysSucceed; +import static jsettlers.logic.movable.BehaviorTreeHelper.condition; +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; +import static jsettlers.logic.movable.BehaviorTreeHelper.defaultIdleBehavior; +import static jsettlers.logic.movable.BehaviorTreeHelper.dropMaterial; +import static jsettlers.logic.movable.BehaviorTreeHelper.guard; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSelector; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.selector; +import static jsettlers.logic.movable.BehaviorTreeHelper.sequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.setIdleBehaviorActiveWhile; +import static jsettlers.logic.movable.BehaviorTreeHelper.sleep; +import static jsettlers.logic.movable.BehaviorTreeHelper.startAndWaitForAnimation; +import static jsettlers.logic.movable.BehaviorTreeHelper.triggerGuard; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForPathFinished; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForTargetReachedAndFailIfNotReachable; + +@Requires({ + GameFieldComponent.class, + MovableComponent.class, + SteeringComponent.class, + BuildingWorkerComponent.class, + MaterialComponent.class, + MarkedPositonComponent.class +}) +public class BuildingWorkerBehaviorComponent extends BehaviorComponent { + private static final long serialVersionUID = -5394764830129769392L; + + @Override + protected Node createBehaviorTree() { + return setIdleBehaviorActiveWhile(false, + selector( + triggerGuard(BuildingWorkerComponent.BuildingDestroyed.class, sequence( + action("handle building destroyed", c-> { + c.entity.movableComponent().setVisible(true); + c.entity.steeringComponent().resetTarget(); + }), + action(c->{ c.entity.buildingWorkerComponent().reportAsJobless(); }), + dropMaterial(c->c.entity.materialComponent().getMaterial()), + action(c->{ c.entity.markedPositonComponent().clearMark();}) + )), + guard("has a job", c->c.entity.buildingWorkerComponent().hasJob(), + memSelector("try execute job", + guard(isCurrentJobType(EBuildingJobType.GO_TO), + selector( + memSequence("go to job pos", + action(c->{c.entity.steeringComponent().setTarget(c.entity.buildingWorkerComponent().getCurrentJobPos());}), + waitForTargetReachedAndFailIfNotReachable(), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.TRY_TAKING_RESOURCE), + sequence("try taking resource", + action(c->{ c.entity.markedPositonComponent().clearMark();}), + selector( + sequence( + tryTakingResource(), + jobFinished()), + jobFailed() + ) + ) + ), + guard(isCurrentJobType(EBuildingJobType.TRY_TAKING_FOOD), + selector( + sequence( + tryTakingFood(), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.WAIT), + memSequence("wait", + sleep(c -> (short)c.entity.buildingWorkerComponent().getCurrentJob().getTime() * 1000), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.WALK), + memSequence("walk in direction of job", + action(c->{c.entity.steeringComponent().goInDirection(c.entity.buildingWorkerComponent().getCurrentJob().getDirection(), EGoInDirectionMode.GO_IF_ALLOWED_WAIT_TILL_FREE);}), + waitForTargetReachedAndFailIfNotReachable(), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.SHOW), + sequence( + condition("Is building not stopped",c->c.entity.buildingWorkerComponent().getBuildingPriority() != EPriority.STOPPED), + debug("show", show()), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.HIDE), + sequence( + action("hide", c->{c.entity.movableComponent().setVisible(false);}), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.SET_MATERIAL), + sequence( + action("set material", c->{ + entity.materialComponent().setMaterial(entity.buildingWorkerComponent().getCurrentJob().getMaterial());} + ), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.TAKE), + selector( + memSequence("try take material", + debug("can take material", condition(BuildingWorkerBehaviorComponent::canTakeMaterial)), + startAndWaitForAnimation(EMovableAction.BEND_DOWN, c->Constants.MOVABLE_BEND_DURATION, true), + tryTakeMaterial(), + startAndWaitForAnimation(EMovableAction.RAISE_UP, Constants.MOVABLE_BEND_DURATION), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.DROP), + memSequence("drop Material", + startAndWaitForAnimation(EMovableAction.BEND_DOWN, c->Constants.MOVABLE_BEND_DURATION, true), + dropMaterial(c->c.entity.buildingWorkerComponent().getCurrentJob().getMaterial()), + alwaysSucceed(guard("increment gold count if goldmelt",c->c.entity.buildingWorkerComponent().getBuildingType() == EBuildingType.GOLDMELT, + action(c->{c.entity.movableComponent().getPlayer().getEndgameStatistic().incrementAmountOfProducedGold();}) + )), + startAndWaitForAnimation(EMovableAction.RAISE_UP, Constants.MOVABLE_BEND_DURATION), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.DROP_POPPED), + memSequence("drop Material", + startAndWaitForAnimation(EMovableAction.BEND_DOWN, c->Constants.MOVABLE_BEND_DURATION, true), + dropMaterial(c->c.entity.buildingWorkerComponent().getPoppedMaterial()), + startAndWaitForAnimation(EMovableAction.RAISE_UP, Constants.MOVABLE_BEND_DURATION), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PRE_SEARCH), + selector( + sequence("search for work", + condition("find path to work",c->c.entity.buildingWorkerComponent().preSearchPath(false)), + action("building can work", c->{c.entity.buildingWorkerComponent().workSearchSucceeded();}), + jobFinished() + ), + sequence("no work found", + action("building cannot work", c->{c.entity.buildingWorkerComponent().workSearchFailed();}), + jobFailed() + ) + ) + + ), + guard(isCurrentJobType(EBuildingJobType.PRE_SEARCH_IN_AREA), + selector( + sequence("search for work", + condition("found path to work",c->c.entity.buildingWorkerComponent().preSearchPath(true)), + action("building can work", c->{c.entity.buildingWorkerComponent().workSearchSucceeded();}), + jobFinished() + ), + sequence("no work found", + action("building cannot work", c->{c.entity.buildingWorkerComponent().workSearchFailed();}), + jobFailed() + ) + ) + ), + guard(isCurrentJobType(EBuildingJobType.FOLLOW_SEARCHED), + selector( + memSequence("follow presearched path", + action(c->{ + Path path = c.entity.buildingWorkerComponent().getPreSearchedPath(); + c.entity.buildingWorkerComponent().mark(path.getTargetPosition()); + c.entity.steeringComponent().setPath(path); + }), + waitForTargetReachedAndFailIfNotReachable(), + jobFinished() + ), + sequence("path aborted", + guard("has current Job", c->c.entity.buildingWorkerComponent().getCurrentJob() != null, + jobFailed() + ), + action(c->{c.entity.buildingWorkerComponent().clearMark();}) + ) + ) + ), + guard(isCurrentJobType(EBuildingJobType.LOOK_AT_SEARCHED), + selector( + sequence( + action("Look in direction",c->{ + EDirection direction = c.entity.gameFieldComponent().movableGrid.getDirectionOfSearched(c.entity.movableComponent().getPosition(), c.entity.buildingWorkerComponent().getCurrentJob().getSearchType()); + if (direction != null) { + c.entity.movableComponent().setViewDirection(direction); + return NodeStatus.SUCCESS; + } + return NodeStatus.FAILURE; + }), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.GO_TO_DOCK), + selector( + memSequence("go to docks", + condition("set target position", c-> { + DockyardBuilding dockyard = (DockyardBuilding)c.entity.buildingWorkerComponent().getBuilding(); + ShortPoint2D dockEndPosition = dockyard.getDock().getEndPosition(); + return c.entity.steeringComponent().setTarget(dockEndPosition); + }), + waitForTargetReachedAndFailIfNotReachable(), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.BUILD_SHIP), + sequence( + action("build ship action", c->{ + DockyardBuilding dockyard = (DockyardBuilding)c.entity.buildingWorkerComponent().getBuilding(); + dockyard.buildShipAction(); + }), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.LOOK_AT), + sequence( + action("look in job direction", c->{c.entity.movableComponent().setViewDirection(c.entity.buildingWorkerComponent().getCurrentJob().getDirection());}), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.EXECUTE), + sequence( + action("clear Mark", c->{c.entity.buildingWorkerComponent().clearMark();}), + selector( + sequence( + action("execute Search", c-> { + ShortPoint2D pos = c.entity.movableComponent().getPosition(); + MovableWrapper movable = c.entity.movableComponent().getMovableWrapper(); + c.entity.gameFieldComponent().movableGrid.executeSearchType(movable, pos, c.entity.buildingWorkerComponent().getCurrentJob().getSearchType()); + }), + jobFinished() + ), + jobFailed() + ) + ) + ), + guard(isCurrentJobType(EBuildingJobType.PLAY_ACTION1), + sequence("play action 1", + startAndWaitForAnimation(EMovableAction.ACTION1, c->(short) (1000 * c.entity.buildingWorkerComponent().getCurrentJob().getTime()), false), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PLAY_ACTION2), + sequence("play action 2", + startAndWaitForAnimation(EMovableAction.ACTION2, c->(short) (1000 * c.entity.buildingWorkerComponent().getCurrentJob().getTime()), false), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PLAY_ACTION3), + sequence("play action 3", + startAndWaitForAnimation(EMovableAction.ACTION3, c->(short) (1000 * c.entity.buildingWorkerComponent().getCurrentJob().getTime()), false), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.AVAILABLE), + selector( + sequence( + condition("is material available", c-> + c.entity.gameFieldComponent().movableGrid.canTakeMaterial(c.entity.buildingWorkerComponent().getCurrentJobPos(), c.entity.buildingWorkerComponent().getCurrentJob().getMaterial()) + ), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.NOT_FULL), + selector( + sequence( + condition("is material available", c-> + c.entity.gameFieldComponent().movableGrid.canPushMaterial(c.entity.buildingWorkerComponent().getCurrentJobPos()) + ), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.SMOKE_ON), + sequence( + placeSmoke(true), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.SMOKE_OFF), + sequence( + placeSmoke(false), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.START_WORKING), + sequence( + selector( + guard("is slaughterhouse", c->c.entity.buildingWorkerComponent().getBuilding() instanceof SlaughterhouseBuilding, + action("play slaughter sound", c->{((SlaughterhouseBuilding) c.entity.buildingWorkerComponent().getBuilding()).requestSound();}) + ), + guard("is MillBuilding", c->c.entity.buildingWorkerComponent().getBuilding() instanceof MillBuilding, + action("start rotate the mill", c->{((MillBuilding) c.entity.buildingWorkerComponent().getBuilding()).setRotating(true);}) + ) + ), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.STOP_WORKING), + sequence( + selector( + guard("is slaughterhouse", c->c.entity.buildingWorkerComponent().getBuilding() instanceof SlaughterhouseBuilding, + action("play slaughter sound", c->{((SlaughterhouseBuilding) c.entity.buildingWorkerComponent().getBuilding()).requestSound();}) + ), + guard("is MillBuilding", c->c.entity.buildingWorkerComponent().getBuilding() instanceof MillBuilding, + action("stop rotate the mill", c->{((MillBuilding) c.entity.buildingWorkerComponent().getBuilding()).setRotating(false);}) + ) + ), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PIG_IS_ADULT), + selector( + sequence( + condition("is pig adult", c->c.entity.gameFieldComponent().movableGrid.isPigAdult(c.entity.buildingWorkerComponent().getCurrentJobPos())), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PIG_IS_THERE), + selector( + sequence( + condition("is pig there", c->c.entity.gameFieldComponent().movableGrid.hasPigAt(c.entity.buildingWorkerComponent().getCurrentJobPos())), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PIG_PLACE), + sequence( + placeOrRemovePigAction(true), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.PIG_REMOVE), + sequence( + placeOrRemovePigAction(false), + jobFinished() + ) + ), + guard(isCurrentJobType(EBuildingJobType.POP_TOOL), + selector( + sequence( + popToolRequestAction(), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.POP_WEAPON), + selector( + sequence( + popWeaponRequestAction(), + jobFinished() + ), + jobFailed() + ) + ), + guard(isCurrentJobType(EBuildingJobType.GROW_DONKEY), + selector( + sequence( + growDonkeyAction(), + jobFinished() + ), + jobFailed() + ) + ) + ) + ), + defaultIdleBehavior() + ) + ); + } + + private static Node placeSmoke(boolean onOrOff) { + return action("place smoke",c->{ + c.entity.gameFieldComponent().movableGrid.placeSmoke(c.entity.buildingWorkerComponent().getCurrentJobPos(), onOrOff); + c.entity.buildingWorkerComponent().addMapObjectCleanupPosition(c.entity.buildingWorkerComponent().getCurrentJobPos(), EMapObjectType.SMOKE); + }); + } + + private static IBooleanConditionFunction isCurrentJobType(EBuildingJobType type) { + return c->c.entity.buildingWorkerComponent().getCurrentJob().getType() == type; + } + + private static Node jobFinished() { + return debug("job finished",action(c->{c.entity.buildingWorkerComponent().jobFinished();})); + } + + private static Node jobFailed() { + return debug("job failed",action(c->{c.entity.buildingWorkerComponent().jobFailed();})); + } + + private static Node tryTakingResource() { + return new Action<>(c->{return NodeStatus.of(c.entity.buildingWorkerComponent().tryTakingResource());}); + } + + private static Node tryTakingFood() { + return new Action<>(c->{return NodeStatus.of(c.entity.buildingWorkerComponent().tryTakingFood());}); + } + + private static Node show() { + return new Action<>(c->{ + BuildingWorkerComponent bwc = c.entity.buildingWorkerComponent(); + MovableComponent mc = c.entity.movableComponent(); + + ShortPoint2D pos = bwc.getCurrentJobPos(); + if (bwc.getCurrentJob().getDirection() != null) { + mc.setViewDirection(bwc.getCurrentJob().getDirection()); + } + mc.setPos(pos); + mc.setVisible(true); + }); + } + + private static boolean canTakeMaterial(Context c) { + EMaterialType materialToTake = c.entity.buildingWorkerComponent().getCurrentJob().getMaterial(); + boolean takeFromMap = c.entity.buildingWorkerComponent().getCurrentJob().isTakeMaterialFromMap(); + return !takeFromMap || c.entity.gameFieldComponent().movableGrid.canTakeMaterial(c.entity.movableComponent().getPosition(), materialToTake); + } + + private static Node tryTakeMaterial() { + return debug("try take material", new Action<>(c -> { + final BuildingWorkerComponent bwc = c.entity.buildingWorkerComponent(); + final GameFieldComponent gfc = c.entity.gameFieldComponent(); + + final EMaterialType materialToTake = bwc.getCurrentJob().getMaterial(); + final boolean takeFromMap = bwc.getCurrentJob().isTakeMaterialFromMap(); + if (gfc.movableGrid.takeMaterial(c.entity.movableComponent().getPosition(), materialToTake) || !takeFromMap) { + c.entity.materialComponent().setMaterial(materialToTake); + return NodeStatus.SUCCESS; + } + return NodeStatus.FAILURE; + })); + } + + private static Node placeOrRemovePigAction(boolean placePig) { + return action(placePig ? "place " : "remove " + "pig",c-> { + ShortPoint2D pos = c.entity.buildingWorkerComponent().getCurrentJobPos(); + c.entity.gameFieldComponent().movableGrid.placePigAt(pos, placePig); + c.entity.buildingWorkerComponent().getBuilding().addMapObjectCleanupPosition(pos, EMapObjectType.PIG); + }); + } + + private static Node growDonkeyAction() { + return action("grow Donkey if at position", c->{ + ShortPoint2D pos = c.entity.buildingWorkerComponent().getCurrentJobPos(); + if (c.entity.gameFieldComponent().movableGrid.feedDonkeyAt(pos)) { + c.entity.buildingWorkerComponent().getBuilding().addMapObjectCleanupPosition(pos, EMapObjectType.DONKEY); + return NodeStatus.SUCCESS; + } else { + return NodeStatus.FAILURE; + } + }); + } + + private static Node popWeaponRequestAction() { + return action("pop requested action if available",c->{ + EMaterialType poppedMaterial = c.entity.buildingWorkerComponent().getBuilding().getMaterialProduction().getWeaponToProduce(); + c.entity.buildingWorkerComponent().setPoppedMaterial(poppedMaterial); + return NodeStatus.of(poppedMaterial != null); + }); + } + + private static Node popToolRequestAction() { + return action("pop requested tool if available", c->{ + IWorkerRequestBuilding building = c.entity.buildingWorkerComponent().getBuilding(); + ShortPoint2D pos = building.getDoor(); + + EMaterialType poppedMaterial = building.getMaterialProduction().drawRandomAbsolutelyRequestedTool(); // first priority: Absolutely set tool production requests of user + if (poppedMaterial == null) { + poppedMaterial = c.entity.gameFieldComponent().movableGrid.popToolProductionRequest(pos); // second priority: Tools needed by settlers (automated production) + } + if (poppedMaterial == null) { + poppedMaterial = building.getMaterialProduction().drawRandomRelativelyRequestedTool(); // third priority: Relatively set tool production requests of user + } + c.entity.buildingWorkerComponent().setPoppedMaterial(poppedMaterial); + + return NodeStatus.of(poppedMaterial != null); + }); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BuildingWorkerComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BuildingWorkerComponent.java new file mode 100644 index 0000000000..7d15715ecf --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/BuildingWorkerComponent.java @@ -0,0 +1,190 @@ +package jsettlers.logic.movable.components; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import jsettlers.algorithms.path.Path; +import jsettlers.common.buildings.EBuildingType; +import jsettlers.common.buildings.jobs.IBuildingJob; +import jsettlers.common.landscape.EResourceType; +import jsettlers.common.mapobject.EMapObjectType; +import jsettlers.common.material.EMaterialType; +import jsettlers.common.material.EPriority; +import jsettlers.common.menu.messages.SimpleMessage; +import jsettlers.common.movable.EDirection; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.DockPosition; +import jsettlers.logic.buildings.workers.DockyardBuilding; +import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IWorkerRequestBuilding; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.ManageableWorkerWrapper; +import jsettlers.logic.movable.Notification; +import jsettlers.logic.movable.Requires; + +/** + * @author homoroselaps + */ +@Requires({ + GameFieldComponent.class, + MovableComponent.class, + SteeringComponent.class, +}) +public class BuildingWorkerComponent extends Component { + private static final long serialVersionUID = -5007619305126786807L; + private ShortPoint2D markedPosition; + + public static class BuildingDestroyed extends Notification { } + + private transient IBuildingJob currentJob = null; + protected IWorkerRequestBuilding building; + private EMaterialType poppedMaterial = EMaterialType.NO_MATERIAL; + private Path preSearchedPath = null; + private int searchFailedCount = 0; + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + String currentJobName = ois.readUTF(); + if (currentJobName.equals("null")) { + currentJob = null; + } else { + currentJob = building.getBuildingType().getJobByName(currentJobName); + } + } + + private void writeObject(ObjectOutputStream oos) throws IOException { + oos.defaultWriteObject(); + if (currentJob != null) { + oos.writeUTF(currentJob.getName()); + } else { + oos.writeUTF("null"); + } + } + + @Override + protected void onWakeUp() { + reportAsJobless(); + } + + public boolean hasJob() { return currentJob != null; } + + public IBuildingJob getCurrentJob() { return currentJob; } + + public ShortPoint2D getCurrentJobPos() { + return currentJob.calculatePoint(building); + } + + public void jobFinished() { + this.currentJob = this.currentJob.getNextSucessJob(); + } + + public void jobFailed() { + this.currentJob = this.currentJob.getNextFailJob(); + } + + public EMaterialType getPoppedMaterial() { + return poppedMaterial; + } + + public void setPoppedMaterial(EMaterialType material) { + poppedMaterial = material; + } + + public Path getPreSearchedPath() { + return preSearchedPath; + } + + public boolean preSearchPath(boolean searchInArea) { + assert entity.movableComponent().getPosition() == getCurrentJob(); + + entity.movableComponent().setPos(getCurrentJobPos()); + ShortPoint2D workAreaCenter = building.getWorkAreaCenter(); + preSearchedPath = entity.steeringComponent().preSearchPath(!searchInArea, workAreaCenter.x, workAreaCenter.y, building.getBuildingType().getWorkRadius(), currentJob.getSearchType()); + return preSearchedPath != null; + } + + public void workSearchSucceeded() { + searchFailedCount = 0; + this.building.setCannotWork(false); + } + + public void workSearchFailed() { + searchFailedCount++; + if (searchFailedCount > 10) { + this.building.setCannotWork(true); + entity.movableComponent().getPlayer().showMessage(SimpleMessage.cannotFindWork(building)); + } + } + + public void mark(ShortPoint2D position) { + clearMark(); + markedPosition = position; + entity.gameFieldComponent().movableGrid.setMarked(position, true); + } + + public void clearMark() { + if (markedPosition != null) { + entity.gameFieldComponent().movableGrid.setMarked(markedPosition, false); + markedPosition = null; + } + } + + public EPriority getBuildingPriority() { + if (building != null) { + return building.getPriority(); + } else { + return null; + } + } + + public void addMapObjectCleanupPosition(ShortPoint2D pos, EMapObjectType objectType){ + building.addMapObjectCleanupPosition(pos, objectType); + } + + public EBuildingType getBuildingType() { + if (building != null) { + return building.getBuildingType(); + } else { + return null; + } + } + + public IWorkerRequestBuilding getBuilding() { + return building; + } + + public void setWorkerJob(IWorkerRequestBuilding building) { + this.building = building; + this.currentJob = building.getBuildingType().getStartJob(); + this.building.occupyBuilding(new ManageableWorkerWrapper(entity)); + } + + public void buildingDestroyed() { + entity.raiseNotification(new BuildingDestroyed()); + } + + public void reportAsJobless() { + entity.gameFieldComponent().movableGrid.addJobless(new ManageableWorkerWrapper(entity)); + currentJob = null; + building = null; + } + + public boolean tryTakingResource() { + switch (building.getBuildingType()) { + case FISHER: + MovableComponent movableComponent = this.entity.movableComponent(); + EDirection fishDirection = movableComponent.getViewDirection(); + return this.entity.gameFieldComponent().movableGrid.tryTakingResource(fishDirection.getNextHexPoint(movableComponent.getPosition()), EResourceType.FISH); + case COALMINE: + case IRONMINE: + case GOLDMINE: + return building.tryTakingResource(); + default: + return false; + } + } + + public boolean tryTakingFood() { + return building.tryTakingFood(currentJob.getFoodOrder()); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/Component.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/Component.java new file mode 100644 index 0000000000..69767fd2f6 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/Component.java @@ -0,0 +1,112 @@ +package jsettlers.logic.movable.components; + +import java.io.Serializable; +import java.util.HashSet; + +import java8.util.Optional; +import java8.util.function.Consumer; +import java8.util.function.Predicate; +import java8.util.stream.Stream; +import jsettlers.common.movable.EMovableType; +import jsettlers.logic.movable.Entity; +import jsettlers.logic.movable.Notification; + +import static java8.util.stream.StreamSupport.stream; + +public abstract class Component implements Serializable { + private static final long serialVersionUID = -3071296154652495126L; + + public Entity entity; + private HashSet consumedNotifications = new HashSet<>(); + + /** + * Called once when the entity gets enabled for the first time + * If you want to save references as shorthand to other components do so in onEnable + */ + public final void wakeUp() { + onWakeUp(); + } + + protected void onWakeUp() {} + + public final void update() { + consumedNotifications.clear(); + onUpdate(); + } + + protected void onUpdate() {} + + public final void lateUpdate() { onLateUpdate(); } + + protected void onLateUpdate() {} + + /** + * Called when the entity is set to active + * If you want to save references as shorthand to other components do so in onEnable + */ + public final void enable() { onEnable(); } + + protected void onEnable() {} + + /** + * Called when the entity is set to inactive + */ + public final void disable() { onDisable(); } + + protected void onDisable() {} + + /** + * Called before the entity gets destroyed/killed + */ + public final void destroy() { onDestroy(); } + + protected void onDestroy() {} + + Optional getNextNotification(Class type, boolean consume) { + return getNextNotification(type, n -> true, consume); + } + + Optional getNextNotification(Class type, Predicate predicate, boolean consume) { + Optional result = getNotificationsOfType(type).filter(predicate).findFirst(); + if (consume) { + result.ifPresent(this::consumeNotification); + } + return result; + } + + public boolean hasNotificationOfType(Class type) { + return getNotificationsOfType(type).findAny().isPresent(); + } + + public boolean hasNotificationOfType(Class type, boolean consume) { // this implementation uses findFirst to guarantee determinism + return getNextNotification(type, consume).isPresent(); + } + + public boolean hasNotificationOfType(Class type, Predicate predicate, boolean consume) { // this implementation uses findFirst to guarantee determinism + return getNextNotification(type, predicate, consume).isPresent(); + } + + private Stream getNotificationsOfType(Class type) { + //noinspection unchecked + return stream(entity.getAllNotifications()) + .parallel() + .filter(notification -> !consumedNotifications.contains(notification)) + .filter(type::isInstance).map(notification -> (T) notification); + } + + public void forFirstNotificationOfTypeC(Class type, Consumer consumer, boolean consume) { + getNextNotification(type, consume).ifPresent(consumer); + } + + public boolean forFirstNotificationOfTypeP(Class type, Predicate predicate, boolean consume) { + return getNextNotification(type, predicate, consume).isPresent(); + } + + boolean consumeNotification(Notification notification) { + if (entity.getAllNotifications().contains(notification)) { + consumedNotifications.add(notification); + return true; + } + return false; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/DonkeyBehaviorComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/DonkeyBehaviorComponent.java new file mode 100644 index 0000000000..5d5d386c35 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/DonkeyBehaviorComponent.java @@ -0,0 +1,151 @@ +package jsettlers.logic.movable.components; + +import java8.util.Optional; +import jsettlers.common.material.EMaterialType; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.nodes.Action; +import jsettlers.logic.movable.simplebehaviortree.nodes.MemSelector; +import jsettlers.logic.movable.simplebehaviortree.nodes.Repeat; +import jsettlers.logic.movable.strategies.trading.ITradeBuilding; + +import static jsettlers.logic.movable.BehaviorTreeHelper.action; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysSucceed; +import static jsettlers.logic.movable.BehaviorTreeHelper.condition; +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; +import static jsettlers.logic.movable.BehaviorTreeHelper.guard; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.repeat; +import static jsettlers.logic.movable.BehaviorTreeHelper.selector; +import static jsettlers.logic.movable.BehaviorTreeHelper.sequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.setAttackableWhile; +import static jsettlers.logic.movable.BehaviorTreeHelper.setIdleBehaviorActiveWhile; +import static jsettlers.logic.movable.BehaviorTreeHelper.sleep; +import static jsettlers.logic.movable.BehaviorTreeHelper.triggerGuard; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForTargetReachedAndFailIfNotReachable; + +/** + * @author homoroselaps + */ + +@Requires({ + MultiMaterialComponent.class, + DonkeyComponent.class, + SteeringComponent.class, + AttackableComponent.class, + GameFieldComponent.class, + AnimationComponent.class, + MovableComponent.class +}) +public final class DonkeyBehaviorComponent extends BehaviorComponent { + private static final long serialVersionUID = -9105595769767841134L; + + @Override + protected Node createBehaviorTree() { + return setAttackableWhile(false, + setIdleBehaviorActiveWhile(false, + selector( + triggerGuard(AttackableComponent.ReceivedHit.class, + debug("received hit", sequence( + debug("unassign market", action(c -> { + c.entity.donkeyComponent().resetMarket(); + })), + debug("stop going to market", action(c -> { + c.entity.steeringComponent().resetTarget(); + })), + debug("drop all materials", repeat(condition(c -> !c.entity.multiMaterialComponent().isEmpty()), alwaysSucceed(tryDropMaterial()))) + )) + ), + guard(DonkeyBehaviorComponent::hasValidMarket, true, + selector( + debug("fulfill request", memSequence( + debug("go to market", action(c -> { + c.entity.steeringComponent().setTarget(c.entity.donkeyComponent().getMarket().getPickUpPosition()); + })), + debug("wait for target reached", waitForTargetReachedAndFailIfNotReachable()), + debug("check for pending transport jobs", condition(c -> c.entity.donkeyComponent().getMarket().needsTrader())), + debug("take material", tryTakeMaterialFromMarket()), + debug("optionally take a second material", alwaysSucceed(tryTakeMaterialFromMarket())), + setAttackableWhile(true, + debug("follow waypoints", repeat(Repeat.Policy.NONPREEMPTIVE, + condition(c -> c.entity.donkeyComponent().hasNextWaypoint()), + memSequence( + debug("go to next waypoint", action(c -> { + c.entity.steeringComponent().setTarget(c.entity.donkeyComponent().peekNextWaypoint()); + })), + debug("wait", waitForTargetReachedAndFailIfNotReachable()), + action(c -> { + c.entity.donkeyComponent().getNextWaypoint(); + }) + ) + )) + ), + debug("drop all materials", repeat(condition(c -> !c.entity.multiMaterialComponent().isEmpty()), alwaysSucceed(tryDropMaterial()))), + selector( + debug("try find new market", tryFindNewMarket()), + debug("go back to market", memSequence( + action(c -> { + c.entity.steeringComponent().setTarget(c.entity.donkeyComponent().getMarket().getPickUpPosition()); + }), + alwaysSucceed(debug("wait", waitForTargetReachedAndFailIfNotReachable())), + action(c -> { + c.entity.donkeyComponent().resetMarket(); + }) + )) + ) + )), + debug("resolve failures", sequence( + debug("invalidate market", action(c -> { + c.entity.donkeyComponent().resetMarket(); + })), + debug("drop materials", repeat(condition(c -> !c.entity.multiMaterialComponent().isEmpty()), alwaysSucceed(tryDropMaterial()))) + )) + ) + ), + // if no market in need then wait for second + guard(DonkeyBehaviorComponent::hasValidMarket, false, + setIdleBehaviorActiveWhile(true, + debug("search for valid market", new MemSelector<>( + tryFindNewMarket(), + sleep(1000) + )) + ) + ) + ) + ) + ); + } + + private static Action tryDropMaterial() { + return new Action<>(c -> { + EMaterialType material = c.entity.multiMaterialComponent().removeMaterial(); + if (material == EMaterialType.NO_MATERIAL) { return NodeStatus.FAILURE; } + c.entity.gameFieldComponent().movableGrid.dropMaterial(c.entity.movableComponent().getPosition(), material, true, true); + return NodeStatus.SUCCESS; + }); + } + + private static Action tryTakeMaterialFromMarket() { + return new Action<>(c -> { + Optional material = c.entity.donkeyComponent().getMarket().tryToTakeMaterial(1); + if (!material.isPresent()) { return NodeStatus.FAILURE; } + c.entity.multiMaterialComponent().addMaterial(material.get().materialType); + return NodeStatus.SUCCESS; + }); + } + + private static Action tryFindNewMarket() { + return new Action<>(c -> { + ITradeBuilding market = c.entity.donkeyComponent().findTradeBuildingWithWork(); + if (market == null) { return NodeStatus.FAILURE; } + c.entity.donkeyComponent().setMarket(market); + return NodeStatus.SUCCESS; + }); + } + + private static boolean hasValidMarket(Context c) { + return c.entity.donkeyComponent().getMarket() != null; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/DonkeyComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/DonkeyComponent.java new file mode 100644 index 0000000000..ddf585beb1 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/DonkeyComponent.java @@ -0,0 +1,70 @@ +package jsettlers.logic.movable.components; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import java8.util.stream.Collectors; +import java8.util.stream.Stream; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.buildings.trading.MarketBuilding; +import jsettlers.logic.constants.MatchConstants; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.strategies.trading.ITradeBuilding; + +/** + * @author homoroselaps + */ + +@Requires({MovableComponent.class}) +public class DonkeyComponent extends Component { + private static final long serialVersionUID = -4747039405397703303L; + + private ITradeBuilding market; + private Iterator waypoints; + private ShortPoint2D nextWaypoint; + + public ITradeBuilding getMarket() { + return market; + } + + public void setMarket(ITradeBuilding market) { + assert market != null : "market should not be null, use reset() instead"; + this.market = market; + this.waypoints = market.getWaypointsIterator(); + this.nextWaypoint = waypoints != null ? waypoints.next() : null; + } + + public void resetMarket() { + this.market = null; + this.waypoints = null; + } + + public ShortPoint2D getNextWaypoint() { + ShortPoint2D last = nextWaypoint; + this.nextWaypoint = waypoints != null ? waypoints.next() : null; + return last; + } + + public boolean hasNextWaypoint() { + return nextWaypoint != null; + } + + public ShortPoint2D peekNextWaypoint() { + return nextWaypoint; + } + + public ITradeBuilding findTradeBuildingWithWork() { + List tradeBuilding = getTradersWithWork().filter(ITradeBuilding::needsTrader).collect(Collectors.toList()); + + if (!tradeBuilding.isEmpty()) { // randomly distribute the donkeys onto the markets needing them + return tradeBuilding.get(MatchConstants.random().nextInt(tradeBuilding.size())); + } else { + return null; + } + } + + protected Stream getTradersWithWork() { + return MarketBuilding.getAllMarkets(entity.movableComponent().getPlayer()); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/GameFieldComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/GameFieldComponent.java new file mode 100644 index 0000000000..1828c53b8b --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/GameFieldComponent.java @@ -0,0 +1,26 @@ +package jsettlers.logic.movable.components; + +import jsettlers.logic.movable.MovableDataManager; +import jsettlers.logic.movable.interfaces.AbstractMovableGrid; +import jsettlers.logic.movable.interfaces.ILogicMovable; + +/** + * @author homoroselaps + */ +public class GameFieldComponent extends Component { + private static final long serialVersionUID = 476680901281177567L; + + public final AbstractMovableGrid movableGrid; + + public GameFieldComponent(AbstractMovableGrid grid) { + this.movableGrid = grid; + } + + void addNewMovable(ILogicMovable movable) { + MovableDataManager.add(movable); + } + + void removeMovable(ILogicMovable movable) { + MovableDataManager.remove(movable); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/GeologistBehaviorComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/GeologistBehaviorComponent.java new file mode 100644 index 0000000000..ed63309bda --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/GeologistBehaviorComponent.java @@ -0,0 +1,198 @@ +package jsettlers.logic.movable.components; + +import jsettlers.algorithms.path.Path; +import jsettlers.common.map.shapes.HexGridArea; +import jsettlers.common.material.ESearchType; +import jsettlers.common.movable.EMovableAction; +import jsettlers.common.position.MutablePoint2D; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.common.utils.mutables.MutableDouble; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.nodes.Action; + +import static jsettlers.logic.movable.BehaviorTreeHelper.action; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysSucceed; +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; +import static jsettlers.logic.movable.BehaviorTreeHelper.defaultIdleBehavior; +import static jsettlers.logic.movable.BehaviorTreeHelper.guard; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.selector; +import static jsettlers.logic.movable.BehaviorTreeHelper.setIdleBehaviorActiveWhile; +import static jsettlers.logic.movable.BehaviorTreeHelper.startAndWaitForAnimation; +import static jsettlers.logic.movable.BehaviorTreeHelper.triggerGuard; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForPathFinished; +import static jsettlers.logic.movable.BehaviorTreeHelper.waitForTargetReachedAndFailIfNotReachable; + +/** + * @author homoroselaps + */ + +@Requires({ + SpecialistComponent.class, + SteeringComponent.class, + AttackableComponent.class, + GameFieldComponent.class, + AnimationComponent.class, + MovableComponent.class, + PlayerComandComponent.class, + MarkedPositonComponent.class, +}) +public final class GeologistBehaviorComponent extends BehaviorComponent { + private static final long serialVersionUID = -4157235942699928852L; + + private static final short ACTION1_DURATION = 1400; + private static final short ACTION2_DURATION = 1500; + + private boolean goingToPlayerCommandLocation = false; + + @Override + protected Node createBehaviorTree() { + return setIdleBehaviorActiveWhile(false, + selector( + triggerGuard(PlayerComandComponent.MoveToCommand.class, + action("MoveToCommand", c -> { + c.component.forFirstNotificationOfTypeC(PlayerComandComponent.MoveToCommand.class, command -> c.entity.steeringComponent().setTarget(command.pos), true); + goingToPlayerCommandLocation = true; + c.entity.specialistComponent().setIsWorking(true); + }) + ), + triggerGuard(PlayerComandComponent.StartWorkCommand.class, + debug("StartWorkCommand", + setIsWorkingAction(true) + ) + ), + triggerGuard(PlayerComandComponent.StopWorkCommand.class, + debug("StopWorkCommand", + setIsWorkingAction(false) + ) + ), + guard(c -> goingToPlayerCommandLocation, true, + waitForPathFinished(null, setIsWorkingAction(false), + action(c -> { goingToPlayerCommandLocation = false; })) + ), + guard(c -> c.entity.specialistComponent().isWorking(), true, + selector("isWorking", + memSequence("find a place and work there", + debug("FindGoToWorkablePosition", new FindGoToWorkablePosition()), + waitForTargetReachedAndFailIfNotReachable(), + debug("markOnCurrentPositionIfWorkingIsPossible", markOnCurrentPositionIfWorkingIsPossible()), + startAndWaitForAnimation(EMovableAction.ACTION1, ACTION1_DURATION), + startAndWaitForAnimation(EMovableAction.ACTION2, ACTION2_DURATION), + debug("placeSign", placeSign()) + ), + debug("on failure: stop working", setIsWorkingAction(false)) + ) + ), + defaultIdleBehavior() + ) + ); + } + + @Override + protected void onLateUpdate() { + if (!entity.specialistComponent().isWorking()) { + entity.setInvocationDelay(500); + } + } + + private Action setIsWorkingAction(boolean isWorking) { + return action(c -> { + c.entity.specialistComponent().setIsWorking(isWorking); + }); + } + + private Node placeSign() { + return new Action<>(c -> { + ShortPoint2D position = c.entity.movableComponent().getPosition(); + + c.entity.markedPositonComponent().clearMark(); + c.entity.gameFieldComponent().movableGrid.executeSearchType(c.entity.movableComponent(), position, ESearchType.RESOURCE_SIGNABLE); + }); + } + + private static Action markOnCurrentPositionIfWorkingIsPossible() { + return new Action<>(c -> { + ShortPoint2D position = c.entity.movableComponent().getPosition(); + + if (c.entity.specialistComponent().getCenterOfWork() == null) { + c.entity.specialistComponent().setCenterOfWork(position); + } + + c.entity.markedPositonComponent().clearMark(); + boolean canWorkOnPos = c.entity.gameFieldComponent().movableGrid.fitsSearchType(c.entity.movableComponent(), position.x, position.y, ESearchType.RESOURCE_SIGNABLE); + + if (canWorkOnPos) { + c.entity.markedPositonComponent().setMark(position); + return NodeStatus.SUCCESS; + } + return NodeStatus.FAILURE; + } + ); + } + + private static class FindGoToWorkablePosition extends Action { + private static final long serialVersionUID = -5393050237159114345L; + + FindGoToWorkablePosition() { + super(FindGoToWorkablePosition::run); + } + + public static NodeStatus run(Context c) { + MovableComponent movableComponent = c.entity.movableComponent(); + GameFieldComponent gameFieldComponent = c.entity.gameFieldComponent(); + SpecialistComponent specialistComponent = c.entity.specialistComponent(); + SteeringComponent steeringComponent = c.entity.steeringComponent(); + + if (specialistComponent.getCenterOfWork() == null) { + specialistComponent.setCenterOfWork(movableComponent.getPosition()); + } + + ShortPoint2D closeWorkablePos = getCloseWorkablePos(c); + + if (closeWorkablePos != null && steeringComponent.setTarget(closeWorkablePos)) { + c.entity.markedPositonComponent().setMark(closeWorkablePos); + return NodeStatus.SUCCESS; + } + specialistComponent.setCenterOfWork(null); + + ShortPoint2D pos = movableComponent.getPosition(); + Path path = steeringComponent.preSearchPath(true, pos.x, pos.y, (short) 30, ESearchType.RESOURCE_SIGNABLE); + if (path != null) { + steeringComponent.setPath(path); + return NodeStatus.SUCCESS; + } + + return NodeStatus.FAILURE; + } + + private static ShortPoint2D getCloseWorkablePos(Context c) { + MovableComponent movableComponent = c.entity.movableComponent(); + GameFieldComponent gameFieldComponent = c.entity.gameFieldComponent(); + SpecialistComponent specialistComponent = c.entity.specialistComponent(); + + MutablePoint2D bestNeighbourPos = new MutablePoint2D(-1, -1); + MutableDouble bestNeighbourDistance = new MutableDouble(Double.MAX_VALUE); // distance from start point + + HexGridArea.streamBorder(movableComponent.getPosition(), 2).filter((x, y) -> + gameFieldComponent.movableGrid.isValidPosition(movableComponent, x, y) + && gameFieldComponent.movableGrid.fitsSearchType(movableComponent, x, y, ESearchType.RESOURCE_SIGNABLE) + ).forEach((x, y) -> { + double distance = ShortPoint2D.getOnGridDist(x - specialistComponent.getCenterOfWork().x, y - specialistComponent.getCenterOfWork().y); + if (distance < bestNeighbourDistance.value) { + bestNeighbourDistance.value = distance; + bestNeighbourPos.x = x; + bestNeighbourPos.y = y; + } + }); + + if (bestNeighbourDistance.value != Double.MAX_VALUE) { + return bestNeighbourPos.createShortPoint2D(); + } else { + return null; + } + } + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MarkedPositonComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MarkedPositonComponent.java new file mode 100644 index 0000000000..9f8958a7b5 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MarkedPositonComponent.java @@ -0,0 +1,30 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.position.ShortPoint2D; + +/** + * @author homoroselaps + */ +public class MarkedPositonComponent extends Component { + private static final long serialVersionUID = -3582535279041109009L; + + private ShortPoint2D markedPosition; + + @Override + protected void onDestroy() { + clearMark(); + } + + public void setMark(ShortPoint2D position) { + clearMark(); + markedPosition = position; + entity.gameFieldComponent().movableGrid.setMarked(position, true); + } + + public void clearMark() { + if (markedPosition != null) { + entity.gameFieldComponent().movableGrid.setMarked(markedPosition, false); + markedPosition = null; + } + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MaterialComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MaterialComponent.java new file mode 100644 index 0000000000..0f423d7e68 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MaterialComponent.java @@ -0,0 +1,21 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.material.EMaterialType; + +/** + * @author homoroselaps + */ + +public class MaterialComponent extends Component { + private static final long serialVersionUID = -3337241844215162194L; + + private EMaterialType material = EMaterialType.NO_MATERIAL; + + public EMaterialType getMaterial() { + return material; + } + + public void setMaterial(EMaterialType material) { + this.material = material; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MovableComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MovableComponent.java new file mode 100644 index 0000000000..90ffbed140 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MovableComponent.java @@ -0,0 +1,145 @@ +package jsettlers.logic.movable.components; + +import java.io.IOException; +import java.io.ObjectInputStream; + +import jsettlers.algorithms.path.IPathCalculatable; +import jsettlers.common.mapobject.EMapObjectType; +import jsettlers.common.movable.EDirection; +import jsettlers.common.movable.EMovableType; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.constants.Constants; +import jsettlers.logic.movable.EntityFactory; +import jsettlers.logic.movable.MovableWrapper; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.player.Player; + +/** + * @author homoroselaps + */ +@Requires({GameFieldComponent.class}) +public class MovableComponent extends Component implements IPathCalculatable { + private static final long serialVersionUID = -7615132582559956988L; + + private EMovableType movableType; + private Player player; + private ShortPoint2D position; + private EDirection viewDirection; + private boolean visible = true; + //TODO: make @movableWrapper not necessary + private MovableWrapper movableWrapper; + + private GameFieldComponent gameComponent; + + public MovableComponent(EMovableType movableType, Player player, ShortPoint2D position, EDirection viewDirection) { + this.movableType = movableType; + this.player = player; + this.position = position; + this.viewDirection = viewDirection; + } + + @Override + protected void onWakeUp() { + movableWrapper = new MovableWrapper(entity); + } + + @Override + protected void onEnable() { + gameComponent = entity.getComponent(GameFieldComponent.class); + gameComponent.addNewMovable(movableWrapper); + gameComponent.movableGrid.enterPosition(position, movableWrapper, true); + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + } + + @Override + protected void onDisable() { + // TODO: refactor leavePosition to not use the instance + gameComponent.movableGrid.leavePosition(position, gameComponent.movableGrid.getMovableAt(position.x, position.y)); + gameComponent.removeMovable(getMovableWrapper()); + } + + @Override + protected void onDestroy() { + gameComponent.movableGrid.addSelfDeletingMapObject(position, EMapObjectType.GHOST, Constants.GHOST_PLAY_DURATION, player); + } + + public MovableWrapper getMovableWrapper() { + return movableWrapper; + } + + public void setViewDirection(EDirection viewDirection) { + this.viewDirection = viewDirection; + } + + public EDirection getViewDirection() { + return viewDirection; + } + + public short getViewDistance() { + return Constants.MOVABLE_VIEW_DISTANCE; + } + + @Override + public boolean needsPlayersGround() { + return movableType.needsPlayersGround(); + } + + @Override + public boolean isShip() { + return this.getMovableType() == EMovableType.FERRY; + } + + @Override + public ShortPoint2D getPosition() { + return position; + } + + public void setPos(ShortPoint2D position) { + if (visible) { + gameComponent.movableGrid.leavePosition(this.position, movableWrapper); + gameComponent.movableGrid.enterPosition(position, movableWrapper, false); + } + this.position = position; + } + + public void setPlayer(Player player) { + this.player = player; + } + + public Player getPlayer() {return this.player;} + + public EMovableType getMovableType() { + return movableType; + } + + final void setVisible(boolean visible) { + if (this.visible == visible) return; // nothing to change + + if (this.visible) { // is visible and gets invisible + gameComponent.movableGrid.leavePosition(position, movableWrapper); + } else { + gameComponent.movableGrid.enterPosition(position, movableWrapper, true); + } + + this.visible = visible; + } + + public void convertTo(EMovableType newMovableType) { + if (newMovableType == EMovableType.BEARER && !player.equals(gameComponent.movableGrid.getPlayerAt(position))) { + return; // can't convert to bearer if the ground does not belong to the player + } + if (!(movableType == EMovableType.BEARER || (movableType == EMovableType.PIONEER && newMovableType == EMovableType.BEARER) || movableType == newMovableType)) { + System.err.println("Tried invalid conversion from " + movableType + " to " + newMovableType); + return; // can't convert between this types + } + entity.getComponentOptional(AttackableComponent.class).ifPresent(c -> + c.setHealth(c.getHealth() / movableType.getHealth() * newMovableType.getHealth()) + ); + movableType = newMovableType; + setVisible(true); // ensure the movable is visible + entity.convertTo(EntityFactory.createEntity(gameComponent.movableGrid, newMovableType, position, player)); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MultiMaterialComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MultiMaterialComponent.java new file mode 100644 index 0000000000..2a40153b10 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/MultiMaterialComponent.java @@ -0,0 +1,62 @@ +package jsettlers.logic.movable.components; + +import java.util.HashMap; +import java.util.Map; + +import java8.util.Maps; +import jsettlers.common.material.EMaterialType; + +/** + * @author homoroselaps + */ + +public class MultiMaterialComponent extends MaterialComponent { + private static final long serialVersionUID = -2141241181575955088L; + + private final Map materials = new HashMap<>(); + + private int sum = 0; + + public void addMaterial(EMaterialType material) { + if (material == null || material == EMaterialType.NO_MATERIAL) { return; } + materials.put(material, Maps.getOrDefault(materials, material, 0) + 1); + sum++; + super.setMaterial(EMaterialType.BASKET); + } + + @Override + public void setMaterial(EMaterialType material) { + addMaterial(material); + } + + public EMaterialType removeMaterial(EMaterialType material) { + int amount = Maps.getOrDefault(materials, material, 0); + EMaterialType result = EMaterialType.NO_MATERIAL; + if (amount > 0) { + materials.put(material, amount - 1); + sum--; + result = material; + } + if (isEmpty()) { super.setMaterial(EMaterialType.NO_MATERIAL); } + return result; + } + + public EMaterialType removeMaterial() { + EMaterialType result = EMaterialType.NO_MATERIAL; + for (EMaterialType material : materials.keySet()) { + int amount = Maps.getOrDefault(materials, material, 0); + if (amount > 0) { + materials.put(material, amount - 1); + sum--; + result = material; + break; + } + } + if (isEmpty()) { super.setMaterial(EMaterialType.NO_MATERIAL); } + return result; + } + + public boolean isEmpty() { + return sum <= 0; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/PlayerComandComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/PlayerComandComponent.java new file mode 100644 index 0000000000..c4bda47475 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/PlayerComandComponent.java @@ -0,0 +1,48 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.movable.Notification; + +/** + * @author homoroselaps + */ + +public class PlayerComandComponent extends Component { + private static final long serialVersionUID = -3188445864619388414L; + + public static class LeftClickCommand extends Notification { + public final ShortPoint2D pos; + + LeftClickCommand(ShortPoint2D pos) { + this.pos = pos; + } + } + + public static class MoveToCommand extends Notification { + public final ShortPoint2D pos; + + MoveToCommand(ShortPoint2D pos) { + this.pos = pos; + } + } + + public static class StartWorkCommand extends Notification {} + + public static class StopWorkCommand extends Notification {} + + public void sendLeftClick(ShortPoint2D pos) { + entity.raiseNotification(new LeftClickCommand(pos)); + } + + public void sendMoveToCommand(ShortPoint2D pos) { + entity.raiseNotification(new MoveToCommand(pos)); + } + + public void sendStartWorkCommand() { + entity.raiseNotification(new StartWorkCommand()); + } + + public void sendStopWorkCommand() { + entity.raiseNotification(new StopWorkCommand()); + } +} \ No newline at end of file diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SelectableComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SelectableComponent.java new file mode 100644 index 0000000000..e55c3e8385 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SelectableComponent.java @@ -0,0 +1,34 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.selectable.ESelectionType; + +/** + * @author homoroselaps + */ +public class SelectableComponent extends Component { + private static final long serialVersionUID = 665477836143096339L; + + private final ESelectionType selectionType; + private boolean selected; + + public SelectableComponent(ESelectionType selectionType) { + this.selectionType = selectionType; + } + + public ESelectionType getSelectionType() { + return selectionType; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + @Override + protected void onDisable() { + setSelected(false); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SpecialistComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SpecialistComponent.java new file mode 100644 index 0000000000..94754467b8 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SpecialistComponent.java @@ -0,0 +1,26 @@ +package jsettlers.logic.movable.components; + +import jsettlers.common.position.ShortPoint2D; + +/** + * @author homoroselaps + */ +public class SpecialistComponent extends Component { + private static final long serialVersionUID = -5944104465410121876L; + + private boolean isWorking = false; + private ShortPoint2D centerOfWork; + + + public boolean isWorking() { + return isWorking; + } + + public void setIsWorking(boolean isWorking) { + this.isWorking = isWorking; + } + + public ShortPoint2D getCenterOfWork() { return centerOfWork; } + + public void setCenterOfWork(ShortPoint2D centerOfWork) { this.centerOfWork = centerOfWork; } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SteeringComponent.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SteeringComponent.java new file mode 100644 index 0000000000..d71cc1897d --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/components/SteeringComponent.java @@ -0,0 +1,274 @@ +package jsettlers.logic.movable.components; + +import java8.util.Optional; +import jsettlers.algorithms.path.Path; +import jsettlers.common.material.ESearchType; +import jsettlers.common.movable.EDirection; +import jsettlers.common.movable.EMovableAction; +import jsettlers.common.position.ILocatable; +import jsettlers.common.position.ShortPoint2D; +import jsettlers.logic.constants.MatchConstants; +import jsettlers.logic.movable.BehaviorTreeHelper; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.EGoInDirectionMode; +import jsettlers.logic.movable.Notification; +import jsettlers.logic.movable.Requires; +import jsettlers.logic.movable.interfaces.ILogicMovable; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Root; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +import static jsettlers.logic.movable.BehaviorTreeHelper.action; +import static jsettlers.logic.movable.BehaviorTreeHelper.condition; +import static jsettlers.logic.movable.BehaviorTreeHelper.debug; +import static jsettlers.logic.movable.BehaviorTreeHelper.guard; +import static jsettlers.logic.movable.BehaviorTreeHelper.memSequence; +import static jsettlers.logic.movable.BehaviorTreeHelper.selector; +import static jsettlers.logic.movable.BehaviorTreeHelper.sleep; +import static jsettlers.logic.movable.BehaviorTreeHelper.alwaysSucceed; +import static jsettlers.logic.movable.BehaviorTreeHelper.triggerGuard; + +/** + * @author homoroselaps + */ +@Requires({ + GameFieldComponent.class, + MovableComponent.class, + AnimationComponent.class +}) +public class SteeringComponent extends Component { + private static final long serialVersionUID = 8281773945922792414L; + + private Path path; + private GameFieldComponent gameFieldComponent; + private MovableComponent movableComponent; + private AnimationComponent animationComponent; + private Tick tick; + private boolean isIdleBehaviorActive = false; + + public boolean IsIdleBehaviorActive() { return isIdleBehaviorActive; } + + public void IsIdleBehaviorActive(boolean value) { isIdleBehaviorActive = value; } + + public static class TargetReachedNotification extends Notification {} + + public static class TargetNotReachedNotification extends Notification {} + + public static class LeavePositionRequest extends Notification { + public final ILocatable sender; + + public LeavePositionRequest(ILocatable sender) { + this.sender = sender; + } + } + + @Override + protected void onWakeUp() { + tick = new Tick<>(new Context(entity, this), new Root<>(debug("==== of " + entity.getID(), createBehaviorTree()))); + } + + @Override + protected void onEnable() { + gameFieldComponent = entity.getComponent(GameFieldComponent.class); + movableComponent = entity.getComponent(MovableComponent.class); + animationComponent = entity.getComponent(AnimationComponent.class); + } + + public boolean setTarget(ShortPoint2D targetPos) { + if (movableComponent.getPosition().equals(targetPos)) { + entity.raiseNotification(new TargetReachedNotification()); + return true; + } + path = gameFieldComponent.movableGrid.calculatePathTo(movableComponent, targetPos); + return path != null; + } + + public void resetTarget() { + path = null; + } + + public void setPath(Path path) { + assert path != null : "path must not be null"; + this.path = path; + } + + public Path preSearchPath(boolean dijkstra, short centerX, short centerY, short radius, ESearchType searchType) { + if (dijkstra) { + return gameFieldComponent.movableGrid.searchDijkstra(movableComponent, centerX, centerY, radius, searchType); + } else { + return gameFieldComponent.movableGrid.searchInArea(movableComponent, centerX, centerY, radius, searchType); + } + } + + protected Node createBehaviorTree() { + return selector( + guard(c -> path != null, true, + debug("follow path", action(c -> { followPath(); })) + ), + guard(c -> ((SteeringComponent)c.component).isIdleBehaviorActive, + selector( + guard(c -> gameFieldComponent.movableGrid.isBlockedOrProtected(movableComponent.getPosition().x, movableComponent.getPosition().y), true, + action(c -> { + goToNonBlockedOrProtectedPosition(); + }) + ), + debug("if LeavePositionRequest", triggerGuard(LeavePositionRequest.class, + debug("try go in random direction", action(context -> { + Optional note = context.component.getNextNotification(LeavePositionRequest.class, false); + if (note.isPresent() && goToRandomDirection(note.get().sender)) { + context.component.consumeNotification(note.get()); + return NodeStatus.SUCCESS; + } + return NodeStatus.FAILURE; + })) + )), + debug("move away from other movables", memSequence( + condition(c -> this.flockToDecentralize()), + sleep(500) + )), + debug("turn in a random direction", memSequence( + action(c -> { + turnInRandomDirection(); + }), + sleep(1000) + )) + ) + ), + debug("nothing to do", alwaysSucceed()) + ); + } + + @Override + protected void onUpdate() { + tick.tick(); + } + + private boolean goToRandomDirection(ILocatable pushingMovable) { + int offset = MatchConstants.random().nextInt(EDirection.NUMBER_OF_DIRECTIONS); + EDirection pushedFromDir = EDirection.getDirection(movableComponent.getPosition(), pushingMovable.getPosition()); + + for (int i = 0; i < EDirection.NUMBER_OF_DIRECTIONS; i++) { + EDirection currDir = EDirection.VALUES[(i + offset) % EDirection.NUMBER_OF_DIRECTIONS]; + if (currDir != pushedFromDir && goInDirection(currDir, EGoInDirectionMode.GO_IF_ALLOWED_AND_FREE)) { + return true; + } + } + + return false; + } + + private void followPath() { + // if path is finished + if (!path.hasNextStep()) { + path = null; + entity.raiseNotification(new TargetReachedNotification()); + return; + } + + ILogicMovable blockingMovable = gameFieldComponent.movableGrid.getMovableAt(path.nextX(), path.nextY()); + if (blockingMovable == null) { // if we can go on to the next step + if (gameFieldComponent.movableGrid.isValidNextPathPosition(movableComponent, path.getNextPos(), path.getTargetPosition())) { // next position is valid + goSingleStep(path.getNextPos()); + path.goToNextStep(); + } else { // next position is invalid + + Path newPath = gameFieldComponent.movableGrid.calculatePathTo(movableComponent, path.getTargetPosition()); // try to find a new path + + if (newPath == null) { // no path found + path = null; + entity.raiseNotification(new TargetNotReachedNotification()); + } else { + this.path = newPath; // continue with new path + if (gameFieldComponent.movableGrid.hasNoMovableAt(path.nextX(), path.nextY())) { // path is valid, but maybe blocked (leaving blocked area) + goSingleStep(path.getNextPos()); + path.goToNextStep(); + } + } + } + } else { // step not possible, so try it next time (push not supported) + blockingMovable.push(movableComponent.getMovableWrapper()); + } + } + + private void goToNonBlockedOrProtectedPosition() { + Path newPath = gameFieldComponent.movableGrid.searchDijkstra(movableComponent, movableComponent.getPosition().x, movableComponent.getPosition().y, (short) 50, ESearchType + .NON_BLOCKED_OR_PROTECTED); + if (newPath == null) { + entity.kill(); + } else { + setPath(newPath); + } + } + + private void turnInRandomDirection() { + int turnDirection = MatchConstants.random().nextInt(-8, 8); + if (Math.abs(turnDirection) <= 1) { + movableComponent.setViewDirection(movableComponent.getViewDirection().getNeighbor(turnDirection)); + } + } + + private void goSingleStep(ShortPoint2D targetPosition) { + movableComponent.setViewDirection(EDirection.getDirection(movableComponent.getPosition(), targetPosition)); + movableComponent.setPos(targetPosition); + animationComponent.startAnimation(EMovableAction.WALKING, movableComponent.getMovableType().getStepDurationMs(), false); + animationComponent.switchStep(); + } + + /** + * Tries to walk the movable into a position where it has a minimum distance to others. + * + * @return true if the movable moves to flock, false if no flocking is required. + */ + private boolean flockToDecentralize() { + ShortPoint2D decentVector = gameFieldComponent.movableGrid.calcDecentralizeVector(movableComponent.getPosition().x, movableComponent.getPosition().y); + + EDirection randomDirection = movableComponent.getViewDirection().getNeighbor(MatchConstants.random().nextInt(-1, 1)); + int dx = randomDirection.gridDeltaX + decentVector.x; + int dy = randomDirection.gridDeltaY + decentVector.y; + + if (ShortPoint2D.getOnGridDist(dx, dy) >= 2) { + return goInDirection(EDirection.getApproxDirection(0, 0, dx, dy), EGoInDirectionMode.GO_IF_ALLOWED_AND_FREE); + } else { + return false; + } + } + + /** + * Tries to go a step in the given direction. + * + * @param direction + * direction to go + * @param mode + * Use the given mode to go.
+ * @return true if the step can and will immediately be executed.
+ * false if the target position is generally blocked or a movable occupies that position. + */ + final boolean goInDirection(EDirection direction, EGoInDirectionMode mode) { + ShortPoint2D targetPosition = direction.getNextHexPoint(movableComponent.getPosition()); + + switch (mode) { + case GO_IF_ALLOWED_WAIT_TILL_FREE: { + movableComponent.setViewDirection(direction); + this.setPath(new Path(targetPosition)); + return true; + } + case GO_IF_ALLOWED_AND_FREE: + if ((gameFieldComponent.movableGrid.isValidPosition(movableComponent, targetPosition.x, targetPosition.y) + && gameFieldComponent.movableGrid.hasNoMovableAt(targetPosition.x, targetPosition.y))) { + goSingleStep(targetPosition); + return true; + } else { + break; + } + case GO_IF_FREE: + if (gameFieldComponent.movableGrid.isFreePosition(targetPosition.x, targetPosition.y)) { + goSingleStep(targetPosition); + return true; + } else { + break; + } + } + return false; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Composite.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Composite.java new file mode 100644 index 0000000000..9d6143fd46 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Composite.java @@ -0,0 +1,10 @@ +package jsettlers.logic.movable.simplebehaviortree; + +public class Composite extends Node { + private static final long serialVersionUID = 8795400757387672902L; + + @SafeVarargs + protected Composite(Node... children) { + super(children); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Decorator.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Decorator.java new file mode 100644 index 0000000000..7f864cc010 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Decorator.java @@ -0,0 +1,12 @@ +package jsettlers.logic.movable.simplebehaviortree; + +public class Decorator extends Node { + private static final long serialVersionUID = 2453864576230160564L; + + public final Node child; + + public Decorator(Node child) { + super(child); + this.child = child; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IBooleanConditionFunction.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IBooleanConditionFunction.java new file mode 100644 index 0000000000..5b13f27fa8 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IBooleanConditionFunction.java @@ -0,0 +1,9 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; + +import java8.util.function.Function; +import java8.util.function.Predicate; + +@FunctionalInterface +public interface IBooleanConditionFunction extends Predicate, Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IEMaterialTypeSupplier.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IEMaterialTypeSupplier.java new file mode 100644 index 0000000000..33e917ac50 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IEMaterialTypeSupplier.java @@ -0,0 +1,9 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; + +import java8.util.function.Function; +import jsettlers.common.material.EMaterialType; + +@FunctionalInterface +public interface IEMaterialTypeSupplier extends Function, Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IIntegerSupplier.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IIntegerSupplier.java new file mode 100644 index 0000000000..5e7a72452b --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IIntegerSupplier.java @@ -0,0 +1,8 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; + +import java8.util.function.Function; + +@FunctionalInterface +public interface IIntegerSupplier extends Function, Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/INodeStatusActionConsumer.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/INodeStatusActionConsumer.java new file mode 100644 index 0000000000..2b3647bf7d --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/INodeStatusActionConsumer.java @@ -0,0 +1,8 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; + +import java8.util.function.Consumer; + +@FunctionalInterface +public interface INodeStatusActionConsumer extends Consumer, Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/INodeStatusActionFunction.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/INodeStatusActionFunction.java new file mode 100644 index 0000000000..e5a134fea9 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/INodeStatusActionFunction.java @@ -0,0 +1,8 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; + +import java8.util.function.Function; + +@FunctionalInterface +public interface INodeStatusActionFunction extends Function, Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IShortSupplier.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IShortSupplier.java new file mode 100644 index 0000000000..5ef687ab98 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/IShortSupplier.java @@ -0,0 +1,8 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; + +import java8.util.function.Function; + +@FunctionalInterface +public interface IShortSupplier extends Function, Serializable {} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Node.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Node.java new file mode 100644 index 0000000000..8b11733e55 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Node.java @@ -0,0 +1,83 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; + +public class Node implements Serializable { + private static final long serialVersionUID = -4544227752720944971L; + + private int id; + private boolean isOpen = false; + + public int getId() { return id; } + + protected final ArrayList> children; + + @SafeVarargs + public Node(Node... children) { + this.children = new ArrayList<>(children.length); + this.children.addAll(Arrays.asList(children)); + } + + public NodeStatus execute(Tick tick) { + if (!isOpen) { + open(tick); + } + enter(tick); + NodeStatus status = this.tick(tick); + exit(tick); + if (!status.equals(NodeStatus.RUNNING)) { + close(tick); + } + return status; + } + + private void enter(Tick tick) { + tick.visitNode(this); + onEnter(tick); + } + + private void open(Tick tick) { + isOpen = true; + onOpen(tick); + } + + private NodeStatus tick(Tick tick) { + tick.tickNode(this); + return onTick(tick); + } + + public void close(Tick tick) { + if (isOpen) { + tick.leaveNode(this); + isOpen = false; + onClose(tick); + } + } + + private void exit(Tick tick) { + onExit(tick); + } + + protected void onEnter(Tick tick) { } + + protected void onOpen(Tick tick) { } + + protected NodeStatus onTick(Tick tick) { + return NodeStatus.SUCCESS; + } + + protected void onClose(Tick tick) { } + + protected void onExit(Tick tick) { } + + protected int initiate(int maxId) { + maxId++; + this.id = maxId; + for (Node child : children) { + maxId = child.initiate(maxId); + } + return maxId; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/NodeStatus.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/NodeStatus.java new file mode 100644 index 0000000000..b738ebe3bf --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/NodeStatus.java @@ -0,0 +1,11 @@ +package jsettlers.logic.movable.simplebehaviortree; + +public enum NodeStatus { + SUCCESS, + FAILURE, + RUNNING; + + public static NodeStatus of(boolean value) { + return value ? SUCCESS : FAILURE; + } +} \ No newline at end of file diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Root.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Root.java new file mode 100644 index 0000000000..9e7a398206 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Root.java @@ -0,0 +1,27 @@ +package jsettlers.logic.movable.simplebehaviortree; + +public class Root extends Node { + private static final long serialVersionUID = 4857616270171506110L; + + protected final Node child; + private int maxID = -1; + + public int getChildrenCount() { + return maxID + 1; + } + + public Root(Node child) { + super(child); + this.child = child; + } + + @Override + protected NodeStatus onTick(Tick tick) { + return child.execute(tick); + } + + public Root init() { + maxID = initiate(-1); + return this; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Tick.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Tick.java new file mode 100644 index 0000000000..4d628a7ea3 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/Tick.java @@ -0,0 +1,53 @@ +package jsettlers.logic.movable.simplebehaviortree; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.Stack; + +public class Tick implements Serializable { + private static final long serialVersionUID = 3673558738736795584L; + + public final Root root; + public final T target; + + private final Stack> openNodes = new Stack<>(); + private boolean blockOpenNodes = false; + + public Tick(T target, Root root) { + this.root = root; + this.target = target; + } + + public NodeStatus tick() { + LinkedList> lastOpenNodes = new LinkedList<>(openNodes); + openNodes.clear(); + NodeStatus state = root.execute(this); + for (Node node : openNodes) { + if (lastOpenNodes.size() <= 0) { + break; + } + if (node == lastOpenNodes.peek()) { + lastOpenNodes.removeFirst(); + } else { + break; + } + } + blockOpenNodes = true; + for (Node node : lastOpenNodes) { + node.close(this); + } + blockOpenNodes = false; + return state; + } + + public void visitNode(Node node) { + if (!blockOpenNodes) { openNodes.push(node); } + } + + public void tickNode(Node node) { + } + + public void leaveNode(Node node) { + if (!blockOpenNodes) { openNodes.pop(); } + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Action.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Action.java new file mode 100644 index 0000000000..d50b316e37 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Action.java @@ -0,0 +1,30 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.INodeStatusActionConsumer; +import jsettlers.logic.movable.simplebehaviortree.INodeStatusActionFunction; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Action extends Node { + private static final long serialVersionUID = -4535362950446826714L; + + private final INodeStatusActionFunction action; + + public Action(INodeStatusActionFunction action) { + super(); + this.action = action; + } + + public Action(INodeStatusActionConsumer action) { + this(t -> { + action.accept(t); + return NodeStatus.SUCCESS; + }); + } + + @Override + protected NodeStatus onTick(Tick tick) { + return action.apply(tick.target); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysFail.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysFail.java new file mode 100644 index 0000000000..252c7a8666 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysFail.java @@ -0,0 +1,14 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public final class AlwaysFail extends Node { + private static final long serialVersionUID = 5577842967150867903L; + + @Override + protected NodeStatus onTick(Tick tick) { + return NodeStatus.FAILURE; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysRunning.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysRunning.java new file mode 100644 index 0000000000..e46791c571 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysRunning.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package jsettlers.logic.movable.simplebehaviortree.nodes; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public final class AlwaysRunning extends Node { + private static final long serialVersionUID = -216524395248690797L; + + @Override + protected NodeStatus onTick(Tick tick) { + return NodeStatus.RUNNING; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysSucceed.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysSucceed.java new file mode 100644 index 0000000000..c49fc3024e --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/AlwaysSucceed.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package jsettlers.logic.movable.simplebehaviortree.nodes; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public final class AlwaysSucceed extends Node { + private static final long serialVersionUID = -216524395248690797L; + + @Override + protected NodeStatus onTick(Tick tick) { + return NodeStatus.SUCCESS; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Condition.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Condition.java new file mode 100644 index 0000000000..d6f1ca673a --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Condition.java @@ -0,0 +1,21 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.IBooleanConditionFunction; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Condition extends Node { + private static final long serialVersionUID = -5811980322685099119L; + + private final IBooleanConditionFunction condition; + + public Condition(IBooleanConditionFunction condition) { + this.condition = condition; + } + + @Override + protected NodeStatus onTick(Tick tick) { + return condition.test(tick.target) ? NodeStatus.SUCCESS : NodeStatus.FAILURE; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Debug.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Debug.java new file mode 100644 index 0000000000..82c54c9328 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Debug.java @@ -0,0 +1,51 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import org.apache.commons.lang3.StringUtils; + +import jsettlers.common.CommonConstants; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.simplebehaviortree.Decorator; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Debug extends Decorator { + private static final long serialVersionUID = 9019598003328102086L; + + private final String message; + + public Debug(String message) { + super(null); + this.message = message; + } + + public Debug(String message, Node child) { + super(child); + this.message = message; + } + + @Override + public NodeStatus onTick(Tick tick) { + if (CommonConstants.DEBUG_BEHAVIOR_TREES && tick.target.entity.isInDebugMode()) { + System.out.println(indent(tick, message)); + } + + if (child != null) { + tick.target.debugLevel++; + NodeStatus result = child.execute(tick); + tick.target.debugLevel--; + + if (CommonConstants.DEBUG_BEHAVIOR_TREES && tick.target.entity.isInDebugMode()) { + System.out.println(indent(tick, message + ": " + result)); + } + + return result; + } + + return NodeStatus.SUCCESS; + } + + private String indent(Tick tick, String message) { + return StringUtils.repeat('\t', tick.target.debugLevel) + message; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/DynamicGuardSelector.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/DynamicGuardSelector.java new file mode 100644 index 0000000000..eecfd0763e --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/DynamicGuardSelector.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import java8.util.Optional; +import jsettlers.logic.movable.simplebehaviortree.Composite; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +import static java8.util.stream.StreamSupport.stream; + +public class DynamicGuardSelector extends Composite { + + private Guard runningChild = null; + + @SafeVarargs + public DynamicGuardSelector(Guard... childrenGuards) { + super(childrenGuards); + } + + @Override + protected NodeStatus onTick(Tick tick) { + Optional> childToRunOptional = stream(children).map(node -> (Guard) node).filter(guard -> guard.checkGuardCondition(tick)).findFirst(); + + if (!childToRunOptional.isPresent()) { + return NodeStatus.FAILURE; + } + + Guard childToRun = childToRunOptional.get(); + + runningChild = childToRun; + return childToRun.execute(tick); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Guard.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Guard.java new file mode 100644 index 0000000000..41968c8d77 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Guard.java @@ -0,0 +1,38 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.IBooleanConditionFunction; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Guard extends Node { + private static final long serialVersionUID = -4675927057210755053L; + + protected final IBooleanConditionFunction condition; + protected final Node child; + protected final boolean value; + + public Guard(IBooleanConditionFunction condition, Node child) { + super(child); + this.condition = condition; + this.child = child; + value = true; + } + + public Guard(IBooleanConditionFunction condition, boolean shouldBe, Node child) { + super(child); + this.condition = condition; + this.child = child; + value = shouldBe; + } + + @Override + protected NodeStatus onTick(Tick tick) { + boolean result = condition.test(tick.target); + if (result == value) { return child.execute(tick); } else { return NodeStatus.FAILURE; } + } + + public boolean checkGuardCondition(Tick tick) { + return condition.test(tick.target); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/IGetPropertyProducer.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/IGetPropertyProducer.java new file mode 100644 index 0000000000..cdada176b6 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/IGetPropertyProducer.java @@ -0,0 +1,11 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import java.io.Serializable; + +/** + * Created by homoroselaps + */ +@FunctionalInterface +public interface IGetPropertyProducer extends Serializable { + PropertyType apply(T entity); +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/ISetPropertyConsumer.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/ISetPropertyConsumer.java new file mode 100644 index 0000000000..d27f82dd0f --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/ISetPropertyConsumer.java @@ -0,0 +1,13 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import java.io.Serializable; + +/** + * Created by homoroselaps + */ + +@FunctionalInterface +public interface ISetPropertyConsumer extends Serializable { + void accept(T entity, PropertyType value); +} + diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Inverter.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Inverter.java new file mode 100644 index 0000000000..fbba5dee9a --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Inverter.java @@ -0,0 +1,29 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Decorator; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Inverter extends Decorator { + private static final long serialVersionUID = -3568446114722874065L; + + public Inverter(Node child) { + super(child); + } + + @Override + protected NodeStatus onTick(Tick tick) { + NodeStatus result = child.execute(tick); + switch (result) { + case SUCCESS: + return NodeStatus.FAILURE; + case FAILURE: + return NodeStatus.SUCCESS; + case RUNNING: + return result; + default: + throw new IllegalStateException("Unknown NodeStatus: " + result); + } + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/MemSelector.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/MemSelector.java new file mode 100644 index 0000000000..491fbdd5ce --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/MemSelector.java @@ -0,0 +1,33 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Composite; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class MemSelector extends Composite { + private static final long serialVersionUID = -4098732000225742833L; + + private int index = 0; + + @SafeVarargs + public MemSelector(Node... children) { + super(children); + } + + @Override + protected NodeStatus onTick(Tick tick) { + for (; index < children.size(); index++) { + NodeStatus status = children.get(index).execute(tick); + if (status != NodeStatus.FAILURE) { + return status; + } + } + return NodeStatus.FAILURE; + } + + @Override + protected void onOpen(Tick tick) { + index = 0; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/MemSequence.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/MemSequence.java new file mode 100644 index 0000000000..567dfe9c9d --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/MemSequence.java @@ -0,0 +1,33 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Composite; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class MemSequence extends Composite { + private static final long serialVersionUID = -6313424360855786743L; + + private int index = 0; + + @SafeVarargs + public MemSequence(Node... children) { + super(children); + } + + @Override + protected NodeStatus onTick(Tick tick) { + for (; index < children.size(); index++) { + NodeStatus status = children.get(index).execute(tick); + if (status != NodeStatus.SUCCESS) { + return status; + } + } + return NodeStatus.SUCCESS; + } + + @Override + protected void onOpen(Tick tick) { + index = 0; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/NotificationCondition.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/NotificationCondition.java new file mode 100644 index 0000000000..90dfb55c9b --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/NotificationCondition.java @@ -0,0 +1,22 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.common.movable.EMovableAction; +import jsettlers.logic.movable.Context; +import jsettlers.logic.movable.Notification; +import jsettlers.logic.movable.simplebehaviortree.IBooleanConditionFunction; + +public class NotificationCondition extends Condition { + private static final long serialVersionUID = 1780756145252644771L; + + public NotificationCondition(Class type) { + this(type, false); + } + + public NotificationCondition(Class type, boolean consume) { + super((context) -> context.component.hasNotificationOfType(type, consume)); + } + + public NotificationCondition(Class type, IBooleanConditionFunction predicate, boolean consume) { + super((context) -> context.component.forFirstNotificationOfTypeP(type, predicate, consume)); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Parallel.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Parallel.java new file mode 100644 index 0000000000..cb3328f472 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Parallel.java @@ -0,0 +1,80 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Composite; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Parallel extends Composite { + private static final long serialVersionUID = 3614671053589100247L; + + /* + Run each child until it's completed + Return SUCCESS when + | ONE -> one child was successful + | ALL -> all children were successful + + Return RUNNING when + | preemptive = true -> successPolicy not fullfilled && any child of it's children still running + | preemptive = false -> if any of it's children is still running + + Return FAILURE in all other cases + */ + + public enum Policy { + ONE, + ALL + } + + private final Policy successPolicy; + private final NodeStatus[] childStatus; + private final boolean preemptive; + private int successCount; + + @SafeVarargs + public Parallel(Policy successPolicy, boolean preemptive, Node... children) { + super(children); + childStatus = new NodeStatus[this.children.size()]; + this.successPolicy = successPolicy; + this.preemptive = preemptive; + } + + @Override + protected NodeStatus onTick(Tick tick) { + boolean anyRunning = false; + for (int index = 0; index < children.size(); index++) { + if (!childStatus[index].equals(NodeStatus.RUNNING)) { + continue; + } + NodeStatus status = children.get(index).execute(tick); + childStatus[index] = status; + if (status.equals(NodeStatus.SUCCESS)) { + successCount++; + } else if (status.equals(NodeStatus.RUNNING)) { + anyRunning = true; + } + } + + boolean successCondition = successPolicy == Policy.ONE && successCount >= 1 || successPolicy == Policy.ALL && successCount == children.size(); + + if (anyRunning && preemptive && !successCondition) { + return NodeStatus.RUNNING; + } else if (anyRunning && !preemptive) { + return NodeStatus.RUNNING; + } + + if (successCondition) { + return NodeStatus.SUCCESS; + } else { + return NodeStatus.FAILURE; + } + } + + @Override + protected void onOpen(Tick tick) { + for (int i = 0; i < childStatus.length; i++) { + childStatus[i] = NodeStatus.RUNNING; + } + successCount = 0; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Property.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Property.java new file mode 100644 index 0000000000..1d62903c13 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Property.java @@ -0,0 +1,38 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Decorator; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Property extends Decorator { + private static final long serialVersionUID = 6714370606784586530L; + + private PropertyType oldValue; + private final PropertyType newValue; + private final ISetPropertyConsumer setter; + private final IGetPropertyProducer getter; + + public Property(ISetPropertyConsumer setter, IGetPropertyProducer getter, PropertyType value, Node child) { + super(child); + this.newValue = value; + this.setter = setter; + this.getter = getter; + } + + @Override + protected void onEnter(Tick tick) { + oldValue = getter.apply(tick.target); + setter.accept(tick.target, newValue); + } + + @Override + protected void onClose(Tick tick) { + setter.accept(tick.target, oldValue); + } + + @Override + protected NodeStatus onTick(Tick tick) { + return child.execute(tick); + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Repeat.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Repeat.java new file mode 100644 index 0000000000..332ce900e0 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Repeat.java @@ -0,0 +1,74 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Decorator; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Repeat extends Decorator { + /* + Run the child if condition=SUCCESS + Preemptive = check condition every tick before executing the child + NonPreemptive = as long as the child is running the condition is not checked + + Return RUNNING if child=RUNNING || condition=RUNNING + Return SUCCESS if condition=FAILURE + Return FAILURE if child=FAILURE + */ + + public enum Policy { + PREEMPTIVE, + NONPREEMPTIVE + } + + private static final long serialVersionUID = -661870259301299858L; + + private final Node condition; + private final Policy policy; + private boolean childRunning; + + public Repeat(Node condition, Node child) { + this(Policy.NONPREEMPTIVE, condition, child); + } + + public Repeat(Policy policy, Node condition, Node child) { + super(child); + this.condition = condition; + this.policy = policy; + } + + @Override + protected NodeStatus onTick(Tick tick) { + while (true) { + if (policy.equals(Policy.PREEMPTIVE) || !childRunning) { + NodeStatus cond = condition.execute(tick); + switch (cond) { + case RUNNING: + return NodeStatus.RUNNING; + case FAILURE: + return NodeStatus.SUCCESS; + case SUCCESS: + break; + } + } + + NodeStatus status = child.execute(tick); + switch (status) { + case SUCCESS: + childRunning = false; + break; + case RUNNING: + childRunning = true; + return NodeStatus.RUNNING; + case FAILURE: + childRunning = false; + return NodeStatus.FAILURE; + } + } + } + + @Override + protected void onOpen(Tick tick) { + childRunning = false; + } +} \ No newline at end of file diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Selector.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Selector.java new file mode 100644 index 0000000000..1507ad4e14 --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Selector.java @@ -0,0 +1,26 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Composite; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Selector extends Composite { + private static final long serialVersionUID = 6187523767823138311L; + + @SafeVarargs + public Selector(Node... children) { + super(children); + } + + @Override + protected NodeStatus onTick(Tick tick) { + for (Node node : children) { + NodeStatus status = node.execute(tick); + if (!status.equals(NodeStatus.FAILURE)) { + return status; + } + } + return NodeStatus.FAILURE; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Sequence.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Sequence.java new file mode 100644 index 0000000000..7cacc85b8a --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Sequence.java @@ -0,0 +1,26 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Composite; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +public class Sequence extends Composite { + private static final long serialVersionUID = -1764866628237365366L; + + @SafeVarargs + public Sequence(Node... children) { + super(children); + } + + @Override + protected NodeStatus onTick(Tick tick) { + for (Node node : children) { + NodeStatus status = node.execute(tick); + if (status != NodeStatus.SUCCESS) { + return status; + } + } + return NodeStatus.SUCCESS; + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Wait.java b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Wait.java new file mode 100644 index 0000000000..87e20ce1aa --- /dev/null +++ b/jsettlers.logic/src/main/java/jsettlers/logic/movable/simplebehaviortree/nodes/Wait.java @@ -0,0 +1,31 @@ +package jsettlers.logic.movable.simplebehaviortree.nodes; + +import jsettlers.logic.movable.simplebehaviortree.Decorator; +import jsettlers.logic.movable.simplebehaviortree.Node; +import jsettlers.logic.movable.simplebehaviortree.NodeStatus; +import jsettlers.logic.movable.simplebehaviortree.Tick; + +/** + * @author homoroselaps + */ + +public class Wait extends Decorator { + private static final long serialVersionUID = -6025244799010530015L; + + public Wait(Node condition) { + super(condition); + } + + @Override + protected NodeStatus onTick(Tick tick) { + NodeStatus status = child.execute(tick); + switch (status) { + case SUCCESS: + return NodeStatus.SUCCESS; + default: + case FAILURE: + case RUNNING: + return NodeStatus.RUNNING; + } + } +} diff --git a/jsettlers.logic/src/main/java/jsettlers/logic/player/SettlerInformation.java b/jsettlers.logic/src/main/java/jsettlers/logic/player/SettlerInformation.java index 3caf1e1ba8..fa999805df 100644 --- a/jsettlers.logic/src/main/java/jsettlers/logic/player/SettlerInformation.java +++ b/jsettlers.logic/src/main/java/jsettlers/logic/player/SettlerInformation.java @@ -19,14 +19,14 @@ import jsettlers.common.movable.EMovableType; import jsettlers.common.player.ISettlerInformation; -import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.MovableDataManager; class SettlerInformation implements ISettlerInformation { private final int[] movables = new int[EMovableType.NUMBER_OF_MOVABLETYPES]; SettlerInformation(byte playerId) { - stream(Movable.getAllMovables()) + stream(MovableDataManager.getAllMovables()) .filter(movable -> movable.getPlayer().getPlayerId() == playerId) .forEach(movable -> { int movableTypeIndex = movable.getMovableType().ordinal(); diff --git a/jsettlers.logic/src/main/java/jsettlers/main/JSettlersGame.java b/jsettlers.logic/src/main/java/jsettlers/main/JSettlersGame.java index fc39e6160f..24ed12e328 100644 --- a/jsettlers.logic/src/main/java/jsettlers/main/JSettlersGame.java +++ b/jsettlers.logic/src/main/java/jsettlers/main/JSettlersGame.java @@ -53,6 +53,7 @@ import jsettlers.logic.map.loading.MapLoadException; import jsettlers.logic.map.loading.MapLoader; import jsettlers.logic.movable.Movable; +import jsettlers.logic.movable.MovableDataManager; import jsettlers.logic.player.Player; import jsettlers.logic.player.PlayerSetting; import jsettlers.logic.timer.RescheduleTimer; @@ -418,7 +419,7 @@ private static DateFormat getLogDateFormatter() { public static void clearState() { RescheduleTimer.stopAndClear(); - Movable.resetState(); + MovableDataManager.resetState(); Building.clearState(); MarketBuilding.clearState(); HarborBuilding.clearState(); diff --git a/jsettlers.main.android/src/main/java/jsettlers/main/android/mainmenu/gamesetup/MapSetupFragment.java b/jsettlers.main.android/src/main/java/jsettlers/main/android/mainmenu/gamesetup/MapSetupFragment.java index 5b9c2b22a6..bb2d4b7743 100644 --- a/jsettlers.main.android/src/main/java/jsettlers/main/android/mainmenu/gamesetup/MapSetupFragment.java +++ b/jsettlers.main.android/src/main/java/jsettlers/main/android/mainmenu/gamesetup/MapSetupFragment.java @@ -95,7 +95,7 @@ void setupView() { recyclerView.setHasFixedSize(true); FragmentUtil.setActionBar(this, toolbar); - // Disable these for now, as these features are not implemented yet. + // disable these for now, as these features are not implemented yet. startResourcesSpinner.setEnabled(false); peacetimeSpinner.setEnabled(false); } diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/SwingManagedJSettlers.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/SwingManagedJSettlers.java index b15a0fb33b..8b7f7dfb06 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/SwingManagedJSettlers.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/SwingManagedJSettlers.java @@ -76,6 +76,7 @@ private static void loadCommandLineSettings() { CommonConstants.ACTIVATE_ALL_PLAYERS = SettingsManager.getInstance().isActivateAllPlayers(); CommonConstants.ENABLE_CONSOLE_LOGGING = SettingsManager.getInstance().useConsoleOutput(); CommonConstants.DISABLE_ORIGINAL_MAPS = SettingsManager.getInstance().areOriginalMapsDisabled(); + CommonConstants.DEBUG_BEHAVIOR_TREES = SettingsManager.getInstance().debugBehaviorTrees(); AbstractLabels.setPreferredLocale(SettingsManager.getInstance().getLocale()); } diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/FilesystemTreeNode.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/FilesystemTreeNode.java index ae28e1c18a..84d072f10f 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/FilesystemTreeNode.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/FilesystemTreeNode.java @@ -82,7 +82,7 @@ public File getFile() { } /** - * @return Root node + * @return root node */ private RootTreeNode findRoot() { TreeNode node = getParent(); diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/RootTreeNode.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/RootTreeNode.java index f891415c8d..5826e7b025 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/RootTreeNode.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/foldertree/RootTreeNode.java @@ -19,7 +19,7 @@ import javax.swing.tree.DefaultTreeModel; /** - * Root node of the tree, with the functionality to load from filesystem and display blocking panel + * root node of the tree, with the functionality to load from filesystem and display blocking panel * * Usually control operation and model should not be mixed, but in this case it's the easyest solution, and its only data loading, not really * controlling diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java index d6fcf11370..3210282cc2 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/menu/openpanel/MapListCellRenderer.java @@ -168,7 +168,7 @@ public MapListCellRenderer() { iconLabel.setOpaque(false); iconLabel.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0)); - // Update UI + // update UI SwingUtilities.updateComponentTreeUI(contentsPanel); } diff --git a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java index d7851380ac..3ef9ea23cd 100644 --- a/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java +++ b/jsettlers.main.swing/src/main/java/jsettlers/main/swing/settings/SettingsManager.java @@ -38,7 +38,7 @@ public class SettingsManager implements ISoundSettingsProvider { private static final String CONFIGURATION_FILE = ".jsettlers"; - private static final String ENV_PREFIX = "SETTLERS_"; + private static final String ENV_PREFIX = "SETTLERS_"; private static final String PROPERTIES_PREFIX = "settlers."; public static final String SETTING_UUID = "gid"; @@ -49,25 +49,26 @@ public class SettingsManager implements ISoundSettingsProvider { private static final String SETTING_SETTLERS_FOLDER = "settlers-folder"; private static final String SETTING_SETTLERS_VERSION_ID = "settlers-folder-version-id"; - private static final String SETTING_USERNAME = "name"; - private static final String SETTING_LOCALE = "locale"; - private static final String SETTING_SERVER = "server"; - private static final String SETTING_VOLUME = "volume"; + private static final String SETTING_USERNAME = "name"; + private static final String SETTING_LOCALE = "locale"; + private static final String SETTING_SERVER = "server"; + private static final String SETTING_VOLUME = "volume"; private static final String SETTING_FULL_SCREEN_MODE = "fullScreenMode"; - private static final String SETTING_CONTROL_ALL = "control-all"; - private static final String SETTING_ACTIVATE_ALL_PLAYERS = "activate-all-players"; - private static final String SETTING_ENABLE_CONSOLE_LOGGING = "console-output"; - private static final String SETTING_DISABLE_ORIGINAL_MAPS = "disable-original-maps"; - private static final String SETTING_MAPFILE = "map-file"; - private static final String SETTING_RANDOM = "random"; - private static final String SETTING_REPLAY_FILE = "replay-file"; - private static final String SETTING_TARGET_TIME = "target-time"; - private static final String SETTING_MAPS = "maps"; + private static final String SETTING_CONTROL_ALL = "control-all"; + private static final String SETTING_ACTIVATE_ALL_PLAYERS = "activate-all-players"; + private static final String SETTING_ENABLE_CONSOLE_LOGGING = "console-output"; + private static final String SETTING_DISABLE_ORIGINAL_MAPS = "disable-original-maps"; + private static final String SETTING_MAPFILE = "map-file"; + private static final String SETTING_RANDOM = "random"; + private static final String SETTING_REPLAY_FILE = "replay-file"; + private static final String SETTING_TARGET_TIME = "target-time"; + private static final String SETTING_MAPS = "maps"; + private static final String SETTING_ENABLE_BEHAVIOR_TREE_DEBUGGING = "debug-behavior"; private static SettingsManager manager; - private final Properties storedSettings = new Properties(); + private final Properties storedSettings = new Properties(); private final Map runtimeProperties = new HashMap<>(); public static void setup(String... args) throws IOException { @@ -105,7 +106,7 @@ private void loadEntry(Map.Entry e, String envPrefix) { System.out.println("Argument: " + key + " -> " + e.getValue().toString()); } } - + private void loadArguments(String[] args) { Pattern parameterPattern = Pattern.compile("--(.*?)=(.*?)"); Pattern optionPattern = Pattern.compile("--(.*?)"); @@ -241,6 +242,10 @@ public boolean useConsoleOutput() { return getOptional(SETTING_ENABLE_CONSOLE_LOGGING); } + public boolean debugBehaviorTrees() { + return getOptional(SETTING_ENABLE_BEHAVIOR_TREE_DEBUGGING); + } + public boolean areOriginalMapsDisabled() { return getOptional(SETTING_DISABLE_ORIGINAL_MAPS); } diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java index de675a6eda..7e37c4bdae 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/control/EditorControl.java @@ -163,7 +163,7 @@ protected void changeTool(Tool lastPathComponent) { private final Timer redrawTimer = new Timer(true); /** - * Action to fix all errors automatically, if clear what to do + * action to fix all errors automatically, if clear what to do */ private AutoFixErrorAction autoFixErrorAction; @@ -206,7 +206,7 @@ public Component getListCellRendererComponent(JList list, Object value, int i } /** - * Update the player selection combobox + * update the player selection combobox */ private void updatePlayerCombobox() { // create a new model, because a swing bug there are sometimes problems updating an existing model diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/action/CombiningActionFirerer.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/action/CombiningActionFirerer.java index afbdc6ed26..f22bd9cb52 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/action/CombiningActionFirerer.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/action/CombiningActionFirerer.java @@ -39,7 +39,7 @@ public class CombiningActionFirerer implements ActionFireable { private final BlockingQueue toFire = new LinkedBlockingQueue<>(); /** - * Target + * target */ private final ActionFireable fireTo; @@ -47,7 +47,7 @@ public class CombiningActionFirerer implements ActionFireable { * Constructor * * @param fireTo - * Target + * target */ public CombiningActionFirerer(ActionFireable fireTo) { this.fireTo = fireTo; diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/tools/ToolTreeModel.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/tools/ToolTreeModel.java index 7683148171..2f9a4e6884 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/tools/ToolTreeModel.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/tools/ToolTreeModel.java @@ -31,7 +31,7 @@ public class ToolTreeModel implements TreeModel { /** - * Root node + * root node */ private final ToolNode root; @@ -39,7 +39,7 @@ public class ToolTreeModel implements TreeModel { * Constructor * * @param root - * Root node + * root node */ public ToolTreeModel(ToolNode root) { this.root = root; diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/AbstractOkCancelDialog.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/AbstractOkCancelDialog.java index 8693a4950b..e0ef21ff7b 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/AbstractOkCancelDialog.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/AbstractOkCancelDialog.java @@ -93,7 +93,7 @@ private void initButtons() { } /** - * Action performed before cancel + * action performed before cancel * * @return true to close the dialog */ @@ -102,7 +102,7 @@ protected boolean beforeCancelAction() { } /** - * Action performed before OK + * action performed before OK * * @return true to close the dialog */ diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/EditorFrame.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/EditorFrame.java index 4fe9dd73e2..9abab584f9 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/EditorFrame.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/EditorFrame.java @@ -94,7 +94,7 @@ public abstract class EditorFrame extends JFrame { * Constructor * * @param root - * Root panel + * root panel * @param sidebar * Sidebar panel */ @@ -172,7 +172,7 @@ public void actionPerformed(ActionEvent e) { * @param actionName * Name of the action * @param action - * Action to execute + * action to execute */ public void registerAction(String actionName, Action action) { ActionMap actionMap = ((JPanel) this.getContentPane()).getActionMap(); @@ -189,10 +189,10 @@ public void registerAction(String actionName, Action action) { } /** - * Enable / disable an action + * enable / disable an action * * @param actionName - * Action name + * action name * @param enable * enabled / disabled */ @@ -263,7 +263,7 @@ private JMenu createMenu(String menuName) { } else { final Action action = actionMap.get(menuActionName); if (action == null) { - System.err.println("Action \"" + menuActionName + "\" not found!"); + System.err.println("action \"" + menuActionName + "\" not found!"); continue; } @@ -362,7 +362,7 @@ private void createToolbar() { } else { final Action action = actionMap.get(toolName); if (action == null) { - System.err.println("Action \"" + toolName + "\" not found!"); + System.err.println("action \"" + toolName + "\" not found!"); continue; } final JButton bt = tb.add(action); @@ -389,7 +389,7 @@ private void createToolbar() { * @param bt * Button * @param action - * Action + * action */ private void setButtonText(JButton bt, Action action) { diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ShapeSelectionPanel.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ShapeSelectionPanel.java index 000c29129b..43f0fe4c5a 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ShapeSelectionPanel.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ShapeSelectionPanel.java @@ -117,7 +117,7 @@ public ShapeType getActiveShape() { } /** - * Update the property slider + * update the property slider */ private void updateStrokeProperties() { for (StrokenSlider s : properties.values()) { @@ -137,7 +137,7 @@ private void updateStrokeProperties() { } /** - * Enable / Disable tool buttons + * enable / disable tool buttons * * @param tool * Tool diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/StrokenSlider.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/StrokenSlider.java index 3f3a02b0c3..a06a6763fa 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/StrokenSlider.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/StrokenSlider.java @@ -109,7 +109,7 @@ public void setValue(int n) { } /** - * Update the displayed string + * update the displayed string */ private void updateString() { setString(displayName + " " + getValue()); diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ToolSidebar.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ToolSidebar.java index d09ca8e539..77515b8ea3 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ToolSidebar.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/main/window/sidebar/ToolSidebar.java @@ -331,7 +331,7 @@ public int getActivePlayer() { } /** - * Update the shape buttons + * update the shape buttons * * @param tool * Selected tool diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/AutoFixErrorAction.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/AutoFixErrorAction.java index b62f26f08a..a1e4e1b3ee 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/AutoFixErrorAction.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/AutoFixErrorAction.java @@ -25,7 +25,7 @@ import jsettlers.mapcreator.mapvalidator.result.fix.FixData; /** - * Action to fix all errors automatically, if clear what to do + * action to fix all errors automatically, if clear what to do * * @author Andreas Butti */ @@ -77,7 +77,7 @@ public void actionPerformed(ActionEvent e) { } /** - * Update the error text and icon, if an error or not + * update the error text and icon, if an error or not */ @Override public void validationFinished(ValidationListModel list) { diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/GotoNextErrorAction.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/GotoNextErrorAction.java index 7ce6de7bf9..3f6db0ea0e 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/GotoNextErrorAction.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/GotoNextErrorAction.java @@ -27,7 +27,7 @@ import jsettlers.mapcreator.mapvalidator.result.ValidationListModel; /** - * Action to display next error, and jumpt to it, disabled if there is no error + * action to display next error, and jumpt to it, disabled if there is no error * * @author Andreas Butti */ @@ -65,7 +65,7 @@ public void actionPerformed(ActionEvent e) { } /** - * Update the error text and icon, if an error or not + * update the error text and icon, if an error or not */ @Override public void validationFinished(ValidationListModel list) { diff --git a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/ShowErrorsAction.java b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/ShowErrorsAction.java index 813ee3589d..5d35b89677 100644 --- a/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/ShowErrorsAction.java +++ b/jsettlers.mapcreator/src/main/java/jsettlers/mapcreator/mapvalidator/ShowErrorsAction.java @@ -26,7 +26,7 @@ import jsettlers.mapcreator.mapvalidator.result.ValidationListModel; /** - * Action to display errors, display error count as text (always enabled, always clickable) + * action to display errors, display error count as text (always enabled, always clickable) * * @author Andreas Butti */ @@ -63,7 +63,7 @@ public void actionPerformed(ActionEvent e) { } /** - * Update the error text and icon, if an error or not + * update the error text and icon, if an error or not */ @Override public void validationFinished(ValidationListModel list) { diff --git a/jsettlers.tools/src/main/java/jsettlers/TestToolUtils.java b/jsettlers.tools/src/main/java/jsettlers/TestToolUtils.java index ceb2c149f6..c26b56113f 100644 --- a/jsettlers.tools/src/main/java/jsettlers/TestToolUtils.java +++ b/jsettlers.tools/src/main/java/jsettlers/TestToolUtils.java @@ -47,9 +47,9 @@ public static IMapInterfaceConnector openTestWindow(IStartedGame startedGame) th mapInterfaceConnector.addListener(action -> { if (action.getActionType() == EActionType.SELECT_POINT) { PointAction selectAction = (PointAction) action; - System.out.println("Action preformed: " + action.getActionType() + " at: " + selectAction.getPosition()); + System.out.println("action preformed: " + action.getActionType() + " at: " + selectAction.getPosition()); } else { - System.out.println("Action preformed: " + action.getActionType()); + System.out.println("action preformed: " + action.getActionType()); } }); diff --git a/jsettlers.tools/src/main/java/jsettlers/dev/helper/translation/ConvertPropertiesToStandardEncoding.java b/jsettlers.tools/src/main/java/jsettlers/dev/helper/translation/ConvertPropertiesToStandardEncoding.java index c8db827b9d..8bfed888b2 100644 --- a/jsettlers.tools/src/main/java/jsettlers/dev/helper/translation/ConvertPropertiesToStandardEncoding.java +++ b/jsettlers.tools/src/main/java/jsettlers/dev/helper/translation/ConvertPropertiesToStandardEncoding.java @@ -49,7 +49,7 @@ private static String readProperty(File file) throws IOException { * @param contents * Contents to write out * @param f - * Target file + * target file * @throws IOException * @throws FileNotFoundException */ diff --git a/maps/test/500-6-6_players_fun_specialists-2018-05-12_12-03-11.rmap b/maps/test/500-6-6_players_fun_specialists-2018-05-12_12-03-11.rmap new file mode 100644 index 0000000000..d4d4862cdb Binary files /dev/null and b/maps/test/500-6-6_players_fun_specialists-2018-05-12_12-03-11.rmap differ