7 Commits

15 changed files with 314 additions and 17 deletions

1
.envrc Normal file
View File

@@ -0,0 +1 @@
use flake

3
.gitignore vendored
View File

@@ -1,2 +1,5 @@
.direnv
build/
data.trace.db
*.jar
result

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@@ -107,12 +107,13 @@ CREATE MEMORY TABLE "PUBLIC"."CONFIG"(
"VALUE" VARCHAR
);
ALTER TABLE "PUBLIC"."CONFIG" ADD CONSTRAINT "PUBLIC"."CONSTRAINT_7" PRIMARY KEY("KEY");
-- 4 +/- SELECT COUNT(*) FROM PUBLIC.CONFIG;
-- 5 +/- SELECT COUNT(*) FROM PUBLIC.CONFIG;
INSERT INTO "PUBLIC"."CONFIG" VALUES
('start_game', 'Hero Home,Main,Start'),
('screen', '1000x800'),
('camera_scale', '2'),
('full_day_duration', '600');
('full_day_duration', '600'),
('initial_time', '08:30');
CREATE MEMORY TABLE "PUBLIC"."LEVELS"(
"LEVEL" INT DEFAULT NEXT VALUE FOR "PUBLIC"."SYSTEM_SEQUENCE_704587BB_DC0E_44AB_A7F0_3DE0CA44FE3F" NOT NULL NULL_TO_DEFAULT SEQUENCE "PUBLIC"."SYSTEM_SEQUENCE_704587BB_DC0E_44AB_A7F0_3DE0CA44FE3F",
"MAX_HP" VARCHAR NOT NULL

111
flake.lock generated Normal file
View File

@@ -0,0 +1,111 @@
{
"nodes": {
"base": {
"inputs": {
"flake-utils": [
"flake-utils"
],
"gradle2nix": "gradle2nix",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1752221128,
"narHash": "sha256-dFWsRVenkLOtbBx67JfAjVD5PyiKJY4TMCbXbf7bGT4=",
"ref": "refs/heads/master",
"rev": "41cc804cc3855553587eafd13597054d93097972",
"revCount": 600,
"type": "git",
"url": "https://git.orleander.pl/bartek/base.git"
},
"original": {
"type": "git",
"url": "https://git.orleander.pl/bartek/base.git"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gradle2nix": {
"inputs": {
"flake-utils": [
"base",
"flake-utils"
],
"nixpkgs": [
"base",
"nixpkgs"
]
},
"locked": {
"lastModified": 1743629487,
"narHash": "sha256-MjnEgT9MhO2HknLhrx7GvBRVxdOzSKydIJMyzawe2Fk=",
"owner": "tadfisher",
"repo": "gradle2nix",
"rev": "293ecbdc10d32d9d4bdc2d23213b9be09ce247ee",
"type": "github"
},
"original": {
"owner": "tadfisher",
"ref": "v2",
"repo": "gradle2nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1751943650,
"narHash": "sha256-7orTnNqkGGru8Je6Un6mq1T8YVVU/O5kyW4+f9C1mZQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88983d4b665fb491861005137ce2b11a9f89f203",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-25.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"base": "base",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

28
flake.nix Normal file
View File

@@ -0,0 +1,28 @@
{
description = "The BASE engine demo game";
inputs = {
nixpkgs.url = "nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils";
base.url = "git+https://git.orleander.pl/bartek/base.git";
base.inputs.nixpkgs.follows = "nixpkgs";
base.inputs.flake-utils.follows = "flake-utils";
};
outputs = inputs @ {
self,
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {inherit system;};
in {
packages = rec {
game = pkgs.callPackage ./game.nix (inputs // {inherit system;});
default = game;
};
});
}

44
game.nix Normal file
View File

@@ -0,0 +1,44 @@
{
pkgs,
base,
lib,
stdenv,
system,
makeWrapper,
jdk17,
xorg,
openjfx17,
glib,
alsa-lib,
libGL,
gtk3,
...
}:
stdenv.mkDerivation rec {
pname = "base-demo";
version = "0.0.1";
src = ./.;
nativeBuildInputs = [base.packages.${system}.default makeWrapper];
buildPhase = ''
base-editor -bHp $src/project.json -o build
'';
installPhase = ''
mkdir -p $out/bin
mkdir -p $out/share/java
cp build/out/game.jar $out/share/java/base-demo-game.jar
makeWrapper "${jdk17}/bin/java" $out/bin/base-demo \
--add-flags "-jar $out/share/java/base-demo-game.jar" \
--prefix LD_LIBRARY_PATH : "${xorg.libXtst}/lib" \
--prefix LD_LIBRARY_PATH : "${openjfx17}/lib" \
--prefix LD_LIBRARY_PATH : "${glib.out}/lib" \
--prefix LD_LIBRARY_PATH : "${alsa-lib}/lib" \
--prefix LD_LIBRARY_PATH : "${libGL}/lib" \
--prefix LD_LIBRARY_PATH : "${gtk3}/lib"
'';
}

View File

@@ -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
}]
}
}, {

View File

@@ -44,6 +44,12 @@
"name": "FSM 5",
"rows": 16,
"columns": 16
}, {
"uid": "413e09cf-ba41-4fe8-ac47-9697b5ad0245",
"source": "413e09cf-ba41-4fe8-ac47-9697b5ad0245.png",
"name": "Forrest",
"rows": 35,
"columns": 16
}],
"characterSets": [{
"uid": "0dcbaf26-d634-4ca8-9691-7a8ff966f702",
@@ -204,6 +210,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",

View File

@@ -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<Friend> path) {
var ai = new FollowPathAI<>(this);
ai.setPath(path);
this.strategy = ai;
return this;
}
public Friend followPath(@NonNull Function<Friend, Path<Friend>> 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;

View File

@@ -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<Void> runEmoji(@NonNull String animation) {
return runEmoji(animation, null);
}
public CompletableFuture<Void> runEmoji(@NonNull String animation, Consumer<SimpleAnimationRunner> 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);
}
}

View File

@@ -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);
}

View File

@@ -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<Friend> grandmaPath(Friend grandma) {
return new CharacterPath<Friend>()
.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<Object> 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<Object> 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<Object> 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..."));

View File

@@ -74,13 +74,13 @@ public class DemoRunner implements GameRunner {
var layer = map.layer(start[1]);
var label = layer.label(start[2]);
time.setTime(dao.config.find("initial_time").getValue());
context.openMap(map.$);
context.getMap().getObjectLayer(layer.$).addEntity(this.player);
player.setCoordinates(label.getX(), label.getY());
context.resume();
hud.show();
var x = A.maps.hero_home.main.entry;
}
public void returnToStartMenu() {

View File

@@ -6,7 +6,11 @@ import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import static java.lang.Math.max;
import static java.lang.Math.min;
public class WorldTime {
private static final float MINUTES_PER_DAY = 1440f; // 60min * 24
@Getter
private float progress = 0;
@@ -18,6 +22,19 @@ public class WorldTime {
this.period = Float.parseFloat(dao.config.find("full_day_duration").getValue());
}
public void setProgress(float progress) {
this.progress = max(0, min(1, progress));
}
public void setTime(@NonNull String time) {
var splitted = time.split(":");
this.setTime(Integer.valueOf(splitted[0], 10), Integer.valueOf(splitted[1], 10));
}
public void setTime(int h, int m) {
this.progress = (h * 60 + m) / MINUTES_PER_DAY;
}
public int getHour() {
return (int) (progress * 24);
}