/******************************************************************************* * Copyright (c) 2015 - 2017 * * 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.strategies; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import jsettlers.common.buildings.jobs.EBuildingJobType; 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.movable.EDirection; import jsettlers.common.movable.EMovableAction; import jsettlers.common.movable.EMovableType; import jsettlers.common.position.ShortPoint2D; import jsettlers.graphics.messages.SimpleMessage; import jsettlers.logic.buildings.workers.MillBuilding; import jsettlers.logic.map.grid.partition.manager.manageables.IManageableWorker; import jsettlers.logic.map.grid.partition.manager.manageables.interfaces.IWorkerRequestBuilding; import jsettlers.logic.movable.EGoInDirectionMode; import jsettlers.logic.movable.Movable; import jsettlers.logic.movable.MovableStrategy; /** * @author Andreas Eberle */ public final class BuildingWorkerStrategy extends MovableStrategy implements IManageableWorker { private static final long serialVersionUID = 5949318243804026519L; private transient IBuildingJob currentJob = null; protected IWorkerRequestBuilding building; private boolean done; private boolean killed; private EMaterialType poppedMaterial; private int searchFailedCtr = 0; private ShortPoint2D markedPosition; public BuildingWorkerStrategy(Movable movable) { super(movable); reportAsJobless(); } 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 action() { if (isJobless()) return; if (!building.isNotDestroyed()) { // check if building is still ok buildingDestroyed(); return; } switch (currentJob.getType()) { case GO_TO: gotoAction(); break; case TRY_TAKING_RESOURCE: clearMark(); if (tryTakingResource()) { jobFinished(); } else { jobFailed(); } break; case TRY_TAKING_FOOD: if (building.tryTakingFoood(currentJob.getFoodOrder())) { jobFinished(); } else { jobFailed(); } break; case WAIT: { short waitTime = (short) (currentJob.getTime() * 1000); super.sleep(waitTime); jobFinished(); break; } case WALK: IBuildingJob job = currentJob; super.goInDirection(currentJob.getDirection(), EGoInDirectionMode.GO_IF_ALLOWED_WAIT_TILL_FREE); if (currentJob == job) { // the path could fail and call abortPath(). jobFinished(); } break; case SHOW: { if (building.getPriority() == EPriority.STOPPED) { break; } ShortPoint2D pos = getCurrentJobPos(); if (currentJob.getDirection() != null) { super.lookInDirection(currentJob.getDirection()); } super.setPosition(pos); super.setVisible(true); jobFinished(); break; } case HIDE: super.setVisible(false); jobFinished(); break; case SET_MATERIAL: super.setMaterial(currentJob.getMaterial()); jobFinished(); break; case TAKE: takeAction(); break; case DROP: dropAction(currentJob.getMaterial()); break; case DROP_POPPED: dropAction(poppedMaterial); break; case PRE_SEARCH: preSearchPathAction(true); break; case PRE_SEARCH_IN_AREA: preSearchPathAction(false); break; case FOLLOW_SEARCHED: followPreSearchedAction(); break; case LOOK_AT_SEARCHED: lookAtSearched(); break; case LOOK_AT: super.lookInDirection(currentJob.getDirection()); jobFinished(); break; case EXECUTE: executeAction(); break; case PLAY_ACTION1: super.playAction(EMovableAction.ACTION1, currentJob.getTime()); jobFinished(); break; case PLAY_ACTION2: super.playAction(EMovableAction.ACTION2, currentJob.getTime()); jobFinished(); break; case AVAILABLE: if (super.getGrid().canTakeMaterial(getCurrentJobPos(), currentJob.getMaterial())) { jobFinished(); } else { jobFailed(); } break; case NOT_FULL: if (super.getGrid().canPushMaterial(getCurrentJobPos())) { jobFinished(); } else { jobFailed(); } break; case SMOKE_ON: case SMOKE_OFF: { super.getGrid().placeSmoke(getCurrentJobPos(), currentJob.getType() == EBuildingJobType.SMOKE_ON); building.addMapObjectCleanupPosition(getCurrentJobPos(), EMapObjectType.SMOKE); jobFinished(); break; } case START_WORKING: case STOP_WORKING: if (building instanceof MillBuilding) { ((MillBuilding) building).setRotating(currentJob.getType() == EBuildingJobType.START_WORKING); } jobFinished(); break; case PIG_IS_ADULT: if (super.getGrid().isPigAdult(getCurrentJobPos())) { jobFinished(); } else { jobFailed(); } break; case PIG_IS_THERE: if (super.getGrid().hasPigAt(getCurrentJobPos())) { jobFinished(); } else { jobFailed(); } break; case PIG_PLACE: case PIG_REMOVE: placeOrRemovePigAction(); break; case POP_TOOL: popToolRequestAction(); break; case POP_WEAPON: popWeaponRequestAction(); break; case GROW_DONKEY: growDonkeyAction(); break; } } private boolean isJobless() { return currentJob == null; } private void followPreSearchedAction() { ShortPoint2D pathTargetPos = super.followPresearchedPath(); mark(pathTargetPos); jobFinished(); } private void placeOrRemovePigAction() { ShortPoint2D pos = getCurrentJobPos(); super.getGrid().placePigAt(pos, currentJob.getType() == EBuildingJobType.PIG_PLACE); building.addMapObjectCleanupPosition(pos, EMapObjectType.PIG); jobFinished(); } private void growDonkeyAction() { ShortPoint2D pos = getCurrentJobPos(); if (super.getGrid().feedDonkeyAt(pos)) { building.addMapObjectCleanupPosition(pos, EMapObjectType.DONKEY); jobFinished(); } else { jobFailed(); } } private void popWeaponRequestAction() { poppedMaterial = building.getMaterialProduction().getWeaponToProduce(); if (poppedMaterial != null) { jobFinished(); } else { jobFailed(); } } private void popToolRequestAction() { ShortPoint2D pos = building.getDoor(); poppedMaterial = building.getMaterialProduction().getAbsolutelyRequestedMaterial(EMaterialType.TOOLS); // first priority: Absolutely set tool production requests of user if (poppedMaterial == null) { poppedMaterial = super.getGrid().popToolProductionRequest(pos); // second priority: Tools needed by settlers (automated production) } if (poppedMaterial == null) { poppedMaterial = building.getMaterialProduction().getRelativelyRequestedMaterial(EMaterialType.TOOLS); // third priority: Relatively set tool production requests of user } if (poppedMaterial != null) { jobFinished(); } else { jobFailed(); } } private void executeAction() { clearMark(); if (super.getGrid().executeSearchType(movable, movable.getPos(), currentJob.getSearchType())) { jobFinished(); } else { jobFailed(); } } private void takeAction() { if (super.take(currentJob.getMaterial(), currentJob.isTakeMaterialFromMap())) { jobFinished(); } else { jobFailed(); } } private void dropAction(EMaterialType materialType) { super.drop(materialType); if (materialType == EMaterialType.GOLD) { movable.getPlayer().getEndgameStatistic().incrementAmountOfProducedGold(); } jobFinished(); } /** * @param dijkstra * if true, dijkstra algorithm is used<br> * if false, in area finder is used. */ private void preSearchPathAction(boolean dijkstra) { super.setPosition(getCurrentJobPos()); ShortPoint2D workAreaCenter = building.getWorkAreaCenter(); boolean pathFound = super.preSearchPath(dijkstra, workAreaCenter.x, workAreaCenter.y, building.getBuildingType().getWorkRadius(), currentJob.getSearchType()); if (pathFound) { jobFinished(); searchFailedCtr = 0; this.building.setCannotWork(false); } else { jobFailed(); searchFailedCtr++; if (searchFailedCtr > 10) { this.building.setCannotWork(true); movable.getPlayer().showMessage(SimpleMessage.cannotFindWork(building)); } } } private boolean tryTakingResource() { switch (building.getBuildingType()) { case FISHER: EDirection fishDirection = movable.getDirection(); return super.getGrid().tryTakingRecource(fishDirection.getNextHexPoint(movable.getPos()), EResourceType.FISH); case COALMINE: case IRONMINE: case GOLDMINE: return building.tryTakingResource(); default: return false; } } private void gotoAction() { if (!done) { this.done = true; if (!super.goToPos(getCurrentJobPos())) { jobFailed(); } } else { jobFinished(); // start next action } } private void jobFinished() { this.currentJob = this.currentJob.getNextSucessJob(); done = false; } private void jobFailed() { this.currentJob = this.currentJob.getNextFailJob(); done = false; } private ShortPoint2D getCurrentJobPos() { return currentJob.calculatePoint(building); } private void lookAtSearched() { EDirection direction = super.getGrid().getDirectionOfSearched(movable.getPos(), currentJob.getSearchType()); if (direction != null) { super.lookInDirection(direction); jobFinished(); } else { jobFailed(); } } @Override public EMovableType getMovableType() { return movable.getMovableType(); } @Override public void setWorkerJob(IWorkerRequestBuilding building) { this.building = building; this.currentJob = building.getBuildingType().getStartJob(); super.enableNothingToDoAction(false); this.done = false; building.occupyBuilding(this); } @Override public void buildingDestroyed() { super.setVisible(true); super.abortPath(); reportAsJobless(); dropCurrentMaterial(); clearMark(); } private void dropCurrentMaterial() { EMaterialType material = movable.getMaterial(); if (material.isDroppable()) { super.getGrid().dropMaterial(movable.getPos(), material, true, false); } super.setMaterial(EMaterialType.NO_MATERIAL); } private void reportAsJobless() { super.getGrid().addJobless(this); super.enableNothingToDoAction(true); this.currentJob = null; this.building = null; } private void mark(ShortPoint2D position) { clearMark(); markedPosition = position; super.getGrid().setMarked(position, true); } private void clearMark() { if (markedPosition != null) { super.getGrid().setMarked(markedPosition, false); markedPosition = null; } } @Override protected void strategyKilledEvent(ShortPoint2D pathTarget) { // used in overriding methods killed = true; dropCurrentMaterial(); if (isJobless()) { super.getGrid().removeJobless(this); } else { super.enableNothingToDoAction(true); currentJob = null; } if (building != null) { building.leaveBuilding(this); } clearMark(); } @Override protected void pathAborted(ShortPoint2D pathTarget) { if (currentJob != null) { jobFailed(); } clearMark(); } @Override protected boolean checkPathStepPreconditions(ShortPoint2D pathTarget, int step) { return isJobless() || building != null; } @Override public boolean isAlive() { return !killed; } }