/* * The MIT License (MIT) * * FXGL - JavaFX Game Library * * Copyright (c) 2015-2017 AlmasB (almaslvl@gmail.com) * * 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 sandbox; import com.almasb.fxgl.ai.goap.GoapAction; import com.almasb.fxgl.ai.goap.GoapAgent; import com.almasb.fxgl.ai.goap.GoapControl; import com.almasb.fxgl.ai.goap.State; import com.almasb.fxgl.app.FXGL; import com.almasb.fxgl.app.GameApplication; import com.almasb.fxgl.ecs.Entity; import com.almasb.fxgl.entity.Entities; import com.almasb.fxgl.entity.GameEntity; import com.almasb.fxgl.settings.GameSettings; import javafx.scene.text.Text; import java.util.HashSet; import java.util.Map; import java.util.Queue; /** * * * @author Almas Baimagambetov (AlmasB) (almaslvl@gmail.com) */ public class GoapSample extends GameApplication { @Override protected void initSettings(GameSettings settings) { settings.setWidth(800); settings.setHeight(600); settings.setTitle("GoapSample"); settings.setVersion("0.1"); settings.setIntroEnabled(false); settings.setMenuEnabled(false); settings.setProfilingEnabled(false); settings.setCloseConfirmation(false); } private static GameEntity player, coin, weapon, agent, guard; @Override protected void initGameVars(Map<String, Object> vars) { vars.put("playerInvincible", true); vars.put("playerAlive", true); } @Override protected void initGame() { initObjects(); HashSet<GoapAction> actions = initActions(); GoapControl goapControl = new GoapControl(new KillerAgent(), 100, actions); agent.addControl(goapControl); guard.addControl(new GoapControl(new GuardAgent(), 125, actions)); } private void initObjects() { player = Entities.builder() .at(300, 300) .viewFromNode(new Text("PLAYER")) .buildAndAttach(getGameWorld()); coin = Entities.builder() .at(500, 100) .viewFromNode(new Text("COIN")) .buildAndAttach(getGameWorld()); weapon = Entities.builder() .at(30, 500) .viewFromNode(new Text("WEAPON")) .buildAndAttach(getGameWorld()); agent = Entities.builder() .at(400, 400) .viewFromNode(new Text("AGENT")) .buildAndAttach(getGameWorld()); guard = Entities.builder() .at(600, 50) .viewFromNode(new Text("GUARD")) .buildAndAttach(getGameWorld()); agent.setProperty("hasWeapon", false); agent.setProperty("hasCoin", false); } private HashSet<GoapAction> initActions() { HashSet<GoapAction> actions = new HashSet<>(); actions.add(new PickupWeapon()); actions.add(new KillPlayer()); actions.add(new PickupCoin()); actions.add(new ReviveAction()); actions.add(new BlowUpAction()); actions.add(new WaitAction()); return actions; } private class KillerAgent implements GoapAgent { @Override public State obtainWorldState(Entity entity) { State worldState = new State(); worldState.add("playerInvincible", getGameState().getBoolean("playerInvincible")); worldState.add("playerAlive", getGameState().getBoolean("playerAlive")); worldState.add("hasWeapon", entity.getProperty("hasWeapon")); worldState.add("hasCoin", entity.getProperty("hasCoin")); return worldState; } @Override public State createGoalState(Entity entity) { State goal = new State(); goal.add("playerAlive", false); return goal; } @Override public void planFailed(Entity entity, State failedGoal) {} @Override public void planFound(Entity entity, State goal, Queue<GoapAction> actions) { System.out.println("Plan found!"); for (GoapAction action : actions) { System.out.println(action); } } @Override public void actionsFinished(Entity entity) { entity.removeControl(GoapControl.class); } @Override public void planAborted(Entity entity, GoapAction aborter) {} } private class GuardAgent implements GoapAgent { @Override public State obtainWorldState(Entity entity) { State worldState = new State(); worldState.add("playerInvincible", getGameState().getBoolean("playerInvincible")); worldState.add("playerAlive", getGameState().getBoolean("playerAlive")); return worldState; } @Override public State createGoalState(Entity entity) { State goal = new State(); if (!getGameState().getBoolean("playerAlive")) { goal.add("playerAlive", true); } else { goal.add("wait", true); } return goal; } @Override public void planFailed(Entity entity, State failedGoal) {} @Override public void planFound(Entity entity, State goal, Queue<GoapAction> actions) { System.out.println("Plan found! Guard"); for (GoapAction action : actions) { System.out.println(action); } } @Override public void actionsFinished(Entity entity) { entity.removeControl(GoapControl.class); } @Override public void planAborted(Entity entity, GoapAction aborter) {} } private static class BaseGoapAction extends GoapAction { private boolean done = false; @Override public void reset() { } @Override public boolean isDone() { return done; } @Override public boolean checkProceduralPrecondition(Entity agent) { return true; } @Override public boolean perform(Entity agent) { done = true; // pickup / kill instantly return true; } @Override public boolean requiresInRange() { return true; } } private static class KillPlayer extends BaseGoapAction { public KillPlayer() { addPrecondition("playerInvincible", false); addPrecondition("playerAlive", true); addPrecondition("hasWeapon", true); addEffect("playerAlive", false); } @Override public boolean checkProceduralPrecondition(Entity agent) { setTarget(player); return true; } @Override public boolean perform(Entity agent) { FXGL.getApp().getGameState().setValue("playerAlive", false); return super.perform(agent); } } private static class PickupWeapon extends BaseGoapAction { public PickupWeapon() { addPrecondition("hasWeapon", false); addEffect("hasWeapon", true); } @Override public boolean checkProceduralPrecondition(Entity agent) { setTarget(weapon); return true; } @Override public boolean perform(Entity agent) { agent.setProperty("hasWeapon", true); return super.perform(agent); } } private static class PickupCoin extends BaseGoapAction { public PickupCoin() { addPrecondition("hasCoin", false); addEffect("hasCoin", true); addEffect("playerInvincible", false); } @Override public boolean checkProceduralPrecondition(Entity agent) { setTarget(coin); return true; } @Override public boolean perform(Entity agent) { agent.setProperty("hasCoin", true); FXGL.getApp().getGameState().setValue("playerInvincible", false); return super.perform(agent); } } private static class ReviveAction extends BaseGoapAction { public ReviveAction() { addPrecondition("playerAlive", false); addEffect("playerAlive", true); } @Override public boolean checkProceduralPrecondition(Entity agent) { setTarget(player); return true; } } private static class BlowUpAction extends BaseGoapAction { public BlowUpAction() { setCost(10.0f); } } private static class WaitAction extends BaseGoapAction { public WaitAction() { addEffect("wait", true); } } public static void main(String[] args) { launch(args); } }