From 785e1ac52420fc1fb85af5849f30e1dff607a502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Wed, 17 Aug 2022 19:12:00 +0200 Subject: [PATCH] Implement simple follow & attack AI for enemies --- data.mv.db | Bin 28672 -> 28672 bytes maps/f845355e-b9ad-4884-a217-dd3a4c18a3fa.dat | Bin 13480 -> 13520 bytes .../demo/ai/SimpleEnemyAI.java | 43 ++++++++++++++++++ .../bartlomiejpluta/demo/entity/Enemy.java | 19 +++++++- .../demo/event/EnemyDiedEvent.java | 20 ++++++++ .../demo/map/BaseMapHandler.java | 3 +- 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/bartlomiejpluta/demo/ai/SimpleEnemyAI.java create mode 100644 src/main/java/com/bartlomiejpluta/demo/event/EnemyDiedEvent.java diff --git a/data.mv.db b/data.mv.db index 94b5f364468b90454faf570747b4c9923679ed5e..ca2bddcb174b6d0f47361f1deb7eac8f2f6f2587 100644 GIT binary patch literal 28672 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 CZS6|{ diff --git a/maps/f845355e-b9ad-4884-a217-dd3a4c18a3fa.dat b/maps/f845355e-b9ad-4884-a217-dd3a4c18a3fa.dat index 0688ac6a9975c2922b659c2f9d73550bd7aea8c0..647f9854216b8ede8db526c596b11c5bd215f251 100644 GIT binary patch delta 98 zcmZ3Hc_DMdR2lKvf?Qm_iJ5r{K8cm7MN&P291I{hF+q9rd6^70j-SjNq5^zUpElbo ypJG(kOUuv6$uD=zOK~hINlebxC@Rg%OD)n%Pb~?^0U4m7siR delta 58 zcmcbRxgvAJR2lKl0$g0aiJ5r{K8cm7MN&@$I2b^1VuJGK^D-H192c26SOs{bPHnbV MKE=4%P<1{(06`-XLI3~& diff --git a/src/main/java/com/bartlomiejpluta/demo/ai/SimpleEnemyAI.java b/src/main/java/com/bartlomiejpluta/demo/ai/SimpleEnemyAI.java new file mode 100644 index 0000000..7bc2a1a --- /dev/null +++ b/src/main/java/com/bartlomiejpluta/demo/ai/SimpleEnemyAI.java @@ -0,0 +1,43 @@ +package com.bartlomiejpluta.demo.ai; + +import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer; + +import com.bartlomiejpluta.base.util.pathfinder.*; +import com.bartlomiejpluta.base.api.ai.*; +import com.bartlomiejpluta.base.lib.ai.*; + +import com.bartlomiejpluta.demo.entity.Enemy; +import com.bartlomiejpluta.demo.entity.Character; + +public class SimpleEnemyAI extends FollowEntityAI { + private static final int ASTAR_MAX_NODES = 100; + private static final int IDLE_MOVEMENT_INTERVAL = 4; + private final AI idle; + private final int range; + + public SimpleEnemyAI(Enemy enemy, Character target, int range) { + super(new AstarPathFinder(ASTAR_MAX_NODES), enemy, target); + this.range = range; + this.idle = new RandomMovementAI(enemy, IDLE_MOVEMENT_INTERVAL); + } + + @Override + protected boolean sees(Enemy enemy, Character target, ObjectLayer layer, int distance) { + return distance < range; + } + + @Override + protected void interact(Enemy enemy, Character target, ObjectLayer layer, float dt) { + enemy.attack(); + } + + @Override + protected void follow(Enemy enemy, Character target, ObjectLayer layer, float dt) { + // noop + } + + @Override + protected void idle(Enemy enemy, Character target, ObjectLayer layer, float dt) { + idle.nextActivity(layer, dt); + } +} \ No newline at end of file diff --git a/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java b/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java index 6dd0cf3..b54d25f 100644 --- a/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java +++ b/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java @@ -1,17 +1,21 @@ package com.bartlomiejpluta.demo.entity; import lombok.*; + import com.bartlomiejpluta.base.api.context.Context; import com.bartlomiejpluta.base.api.entity.Entity; import com.bartlomiejpluta.base.api.ai.AI; import com.bartlomiejpluta.base.api.ai.NPC; +import com.bartlomiejpluta.base.api.move.MoveEvent; import com.bartlomiejpluta.base.lib.ai.NoopAI; import com.bartlomiejpluta.demo.runner.DemoRunner; import com.bartlomiejpluta.demo.database.model.EnemyModel; - import com.bartlomiejpluta.demo.world.weapon.MeleeWeapon; +import com.bartlomiejpluta.demo.event.EnemyDiedEvent; +import com.bartlomiejpluta.demo.ai.*; + public class Enemy extends Character implements NPC { private final EnemyModel template; @@ -41,10 +45,23 @@ public class Enemy extends Character implements NPC { setZIndex(-1); ai = NoopAI.INSTANCE; + + getLayer().handleEvent(new EnemyDiedEvent(this)); } @Override public String toString() { return template.getName() + "@" + hashCode(); } + + public Enemy followAndAttack(Character target, int range) { + var ai = new SimpleEnemyAI(this, target, range); + + addEventListener(MoveEvent.TYPE, e -> ai.recomputePath()); + addEventListener(EnemyDiedEvent.TYPE, e -> ai.recomputePath()); + + this.ai = ai; + + return this; + } } \ No newline at end of file diff --git a/src/main/java/com/bartlomiejpluta/demo/event/EnemyDiedEvent.java b/src/main/java/com/bartlomiejpluta/demo/event/EnemyDiedEvent.java new file mode 100644 index 0000000..16412b8 --- /dev/null +++ b/src/main/java/com/bartlomiejpluta/demo/event/EnemyDiedEvent.java @@ -0,0 +1,20 @@ +package com.bartlomiejpluta.demo.event; + +import lombok.*; + +import com.bartlomiejpluta.base.api.event.*; +import com.bartlomiejpluta.base.lib.event.*; +import com.bartlomiejpluta.demo.entity.Enemy; + +@Getter +@RequiredArgsConstructor +public class EnemyDiedEvent extends BaseEvent { + public static final EventType TYPE = new EventType<>("ENEMY_DIED_EVENT"); + + private final Enemy enemy; + + @Override + public EventType getType() { + return TYPE; + } +} \ No newline at end of file diff --git a/src/main/java/com/bartlomiejpluta/demo/map/BaseMapHandler.java b/src/main/java/com/bartlomiejpluta/demo/map/BaseMapHandler.java index 667d001..283f41d 100644 --- a/src/main/java/com/bartlomiejpluta/demo/map/BaseMapHandler.java +++ b/src/main/java/com/bartlomiejpluta/demo/map/BaseMapHandler.java @@ -75,9 +75,10 @@ public abstract class BaseMapHandler implements MapHandler { cameraController.update(); } - public void enemy(int x, int y, @NonNull String id) { + public Enemy enemy(int x, int y, @NonNull String id) { var enemy = new Enemy(context, runner.getEnemyDAO().get(id)); enemy.setCoordinates(x, y); mainLayer.addEntity(enemy); + return enemy; } } \ No newline at end of file