diff --git a/animations/78563669-8a6c-4024-82c8-7e4da5b76edd.png b/animations/78563669-8a6c-4024-82c8-7e4da5b76edd.png new file mode 100644 index 0000000..c19f982 Binary files /dev/null and b/animations/78563669-8a6c-4024-82c8-7e4da5b76edd.png differ diff --git a/maps/d1b85d85-c52a-46f5-b81e-444847f8ddae.json b/maps/d1b85d85-c52a-46f5-b81e-444847f8ddae.json index ba81614..12b70bf 100644 --- a/maps/d1b85d85-c52a-46f5-b81e-444847f8ddae.json +++ b/maps/d1b85d85-c52a-46f5-b81e-444847f8ddae.json @@ -84,10 +84,6 @@ "x": 3, "y": 12, "code": "/* \n * Following final parameters are available to use:\n * x: int - the x coordinate of tile the object has been created on\n * y: int - the y coordinate of tile the object has been created on \n * layer: ObjectLayer - current object layer\n * map: GameMap - current map\n */\nwarp(here, A.maps.hero_house.main.home);" - }, { - "x": 11, - "y": 7, - "code": "/* \n * Following final parameters are available to use:\n * here: MapPin - the composite object containing current map UID, \n * layer\u0027s index and x,y coordinates of the current tile \n * x: int - the x coordinate of the current tile\n * y: int - the y coordinate of the current tile \n * layer: ObjectLayer - current object layer\n * map: GameMap - current map\n */\nfriend(here, \"grandma\")\n\t.randomMovementAI(4f, here.toCoordinates(), 5)\n\t.interaction(this::triggerGrandmaDialog);" }, { "x": 5, "y": 4, @@ -100,6 +96,10 @@ "x": 18, "y": 12, "code": "/* \n * Following final parameters are available to use:\n * here: MapPin - the composite object containing current map UID, \n * layer\u0027s index and x,y coordinates of the current tile \n * x: int - the x coordinate of the current tile\n * y: int - the y coordinate of the current tile \n * layer: ObjectLayer - current object layer\n * map: GameMap - current map \n * handler: HeroHomeHandler - current map handler\n * runner: DemoRunner - the game runner of the project\n * context: Context - the game context\n */\nchest(here, \"enforced_chest_left\")\n\t.addItem(new MeleeWeapon(\"wooden_sword\"))\n\t.addItem(new Medicament(\"small_life_potion\", 4))\n\t.shuffle();" + }, { + "x": 13, + "y": 15, + "code": "/* \n * Following final parameters are available to use:\n * here: MapPin - the composite object containing current map UID, \n * layer\u0027s index and x,y coordinates of the current tile \n * x: int - the x coordinate of the current tile\n * y: int - the y coordinate of the current tile \n * layer: ObjectLayer - current object layer\n * map: GameMap - current map\n */\nfriend(here, \"grandma\")\n\t.followPath(this::grandmaPath)\n\t.interaction(this::triggerGrandmaDialog);" }], "labels": [{ "label": "entry", @@ -109,6 +109,14 @@ "label": "Start", "x": 10, "y": 14 + }, { + "label": "Grandma Waking", + "x": 11, + "y": 14 + }, { + "label": "Grandma Origin", + "x": 11, + "y": 7 }] } }, { diff --git a/project.json b/project.json index cec0e41..14ca5fb 100644 --- a/project.json +++ b/project.json @@ -204,6 +204,12 @@ "name": "Heart Emoji", "rows": 6, "columns": 4 + }, { + "uid": "78563669-8a6c-4024-82c8-7e4da5b76edd", + "source": "78563669-8a6c-4024-82c8-7e4da5b76edd.png", + "name": "Zzz", + "rows": 7, + "columns": 4 }], "sounds": [{ "uid": "1311327d-4b74-4252-94da-23ee4129e357", diff --git a/src/main/java/com/bartlomiejpluta/demo/entity/Friend.java b/src/main/java/com/bartlomiejpluta/demo/entity/Friend.java index c1eb989..318fc65 100644 --- a/src/main/java/com/bartlomiejpluta/demo/entity/Friend.java +++ b/src/main/java/com/bartlomiejpluta/demo/entity/Friend.java @@ -5,8 +5,10 @@ import com.bartlomiejpluta.base.api.ai.AI; import com.bartlomiejpluta.base.api.ai.NPC; import com.bartlomiejpluta.base.api.character.Character; import com.bartlomiejpluta.base.api.context.ContextHolder; +import com.bartlomiejpluta.base.lib.ai.FollowPathAI; import com.bartlomiejpluta.base.lib.ai.NoopAI; import com.bartlomiejpluta.base.lib.ai.RandomMovementAI; +import com.bartlomiejpluta.base.util.path.Path; import com.bartlomiejpluta.base.util.random.DiceRoller; import com.bartlomiejpluta.demo.world.item.Item; import lombok.Getter; @@ -74,6 +76,20 @@ public class Friend extends Creature implements NPC { return this; } + public Friend followPath(@NonNull Path path) { + var ai = new FollowPathAI<>(this); + ai.setPath(path); + this.strategy = ai; + return this; + } + + public Friend followPath(@NonNull Function> pathSupplier) { + var ai = new FollowPathAI<>(this); + ai.setPath(pathSupplier.apply(this)); + this.strategy = ai; + return this; + } + public Friend randomMovementAI(float intervalSeconds, Vector2ic origin, int radius) { this.strategy = new RandomMovementAI<>(this, intervalSeconds, origin, radius); return this; diff --git a/src/main/java/com/bartlomiejpluta/demo/entity/NamedCharacter.java b/src/main/java/com/bartlomiejpluta/demo/entity/NamedCharacter.java index 163bbaf..f32ed4f 100644 --- a/src/main/java/com/bartlomiejpluta/demo/entity/NamedCharacter.java +++ b/src/main/java/com/bartlomiejpluta/demo/entity/NamedCharacter.java @@ -3,8 +3,14 @@ package com.bartlomiejpluta.demo.entity; import com.bartlomiejpluta.base.api.character.Character; import com.bartlomiejpluta.base.api.context.Context; import com.bartlomiejpluta.base.api.context.ContextHolder; +import com.bartlomiejpluta.base.lib.animation.AnimationRunner; +import com.bartlomiejpluta.base.lib.animation.SimpleAnimationRunner; import com.bartlomiejpluta.base.lib.character.CharacterDelegate; import com.bartlomiejpluta.demo.runner.DemoRunner; +import lombok.NonNull; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; public abstract class NamedCharacter extends CharacterDelegate { protected final Context context; @@ -21,4 +27,22 @@ public abstract class NamedCharacter extends CharacterDelegate { public int getDialogNameColor() { return 0xFFFFFF; } + + public CompletableFuture runEmoji(@NonNull String animation) { + return runEmoji(animation, null); + } + + public CompletableFuture runEmoji(@NonNull String animation, Consumer customizer) { + var runner = AnimationRunner + .simple(animation) + .scale(0.4f) + .animationSpeed(1.6f) + .offset(0, -30); + + if (customizer != null) { + customizer.accept(runner); + } + + return runner.run(context, this); + } } \ 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 9b145e6..72f2c9f 100644 --- a/src/main/java/com/bartlomiejpluta/demo/map/BaseMapHandler.java +++ b/src/main/java/com/bartlomiejpluta/demo/map/BaseMapHandler.java @@ -41,6 +41,8 @@ public abstract class BaseMapHandler implements MapHandler { protected boolean dayNightCycle = false; + protected boolean controls = true; + @Override public void onCreate(Context context, GameMap map) { this.context = context; @@ -73,6 +75,10 @@ public abstract class BaseMapHandler implements MapHandler { player.interact(); } + if (!controls) { + return; + } + InputUtil.handleBasicControl(player, input); } diff --git a/src/main/java/com/bartlomiejpluta/demo/map/HeroHomeHandler.java b/src/main/java/com/bartlomiejpluta/demo/map/HeroHomeHandler.java index 1e116c6..ca47480 100644 --- a/src/main/java/com/bartlomiejpluta/demo/map/HeroHomeHandler.java +++ b/src/main/java/com/bartlomiejpluta/demo/map/HeroHomeHandler.java @@ -1,25 +1,51 @@ package com.bartlomiejpluta.demo.map; +import A.animations; +import A.maps; import com.bartlomiejpluta.base.api.context.Context; import com.bartlomiejpluta.base.api.map.model.GameMap; +import com.bartlomiejpluta.base.util.path.CharacterPath; +import com.bartlomiejpluta.base.util.pathfinder.AstarPathFinder; +import com.bartlomiejpluta.base.util.pathfinder.PathFinder; import com.bartlomiejpluta.demo.entity.Friend; import java.util.concurrent.CompletableFuture; -import static com.bartlomiejpluta.base.lib.animation.AnimationRunner.simple; +import static com.bartlomiejpluta.base.api.move.Direction.LEFT; public class HeroHomeHandler extends BaseMapHandler { + private final PathFinder finder = new AstarPathFinder(100); + @Override - public void onOpen(Context context, GameMap map) { + public void onCreate(Context context, GameMap map) { + super.onCreate(context, map); map.setAmbientColor(0.05f, 0.01f, 0.01f); - dialog(player, "Ahhh, another beautiful day for an adventure... Let's go!"); + } + + protected CharacterPath grandmaPath(Friend grandma) { + return new CharacterPath() + .run(() -> controls = false) + .run(() -> player.runEmoji(A.animations.zzz.$, r -> r.repeat(5))) + .insertPath(finder.findPath(grandma.getLayer(), grandma, maps.hero_home.main.grandma_waking.toCoordinates())) + .wait(2f) + .turn(LEFT) + .wait(1f) + .suspend(() -> morningDialogWithGrandma(grandma)) + .wait(1f) + .run(() -> controls = true) + .insertPath(finder.findPath(grandma.getLayer(), maps.hero_home.main.grandma_waking.toCoordinates(), maps.hero_home.main.grandma_origin.toCoordinates())) + .run(() -> grandma.randomMovementAI(4f, grandma.getCoordinates(), 4)); + } + + private CompletableFuture morningDialogWithGrandma(Friend grandma) { + return dialog(grandma, "Hello Honey, wake up, it's another beautiful day!") + .thenCompose(n -> dialog(player, "Ahhh, good morning Grandma!")) + .thenCompose(n -> dialog(grandma, "Have a wonderful day, Luna!")) + .thenCompose(n -> dialog(player, "Thank you Grandma, have a nice day too!")); } protected CompletableFuture triggerGrandmaDialog(Friend grandma) { - return dialog(player, "Good morning Grandma, how are you doing?") - .thenCompose(n -> dialog(grandma, "Hello Honey... I'm fine thank you. Have a sit, I will bring you a breakfast in a moment.")) - .thenCompose(n -> dialog(player, "Thank you Grandma.")) - .thenCompose(n -> dialog(grandma, "What are you going to do today, Luna?")) + return dialog(grandma, "What are you going to do today, Luna?") .thenCompose(n -> dialogChoice(player, "I'm going to fix your roof, Grandma", "I'd like to look for some hidden treasure around your house", @@ -46,8 +72,8 @@ public class HeroHomeHandler extends BaseMapHandler { protected CompletableFuture triggerNekoDialog(Friend neko) { return dialog(player, "Ohhh, here you are Kitty...") .thenCompose(n -> CompletableFuture.allOf( - simple(A.animations.heart_emoji.$).scale(0.4f).animationSpeed(1.7f).offset(0, -30).run(context, player), - simple(A.animations.heart_emoji.$).scale(0.4f).animationSpeed(1.7f).offset(0, -15).delay(100).run(context, neko) + player.runEmoji(animations.heart_emoji.$), + neko.runEmoji(animations.heart_emoji.$, r -> r.offset(0, -15).delay(100)) )) .thenCompose(n -> dialog(neko, "Meow, meow...")) .thenCompose(n -> dialog(neko, "Purr, purr..."));