Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
ccd3e44130
|
|||
|
9124f5c58a
|
|||
|
7b778845ba
|
|||
|
f98806f2f9
|
|||
|
0f365410b0
|
|||
|
1e3e2e6abb
|
|||
|
1e4a9a02c9
|
|||
|
0bfe53cd8e
|
|||
|
bf04597af6
|
|||
|
ffdc9e930a
|
|||
|
b4f385db5c
|
|||
|
dd7a4bf304
|
|||
|
e28667c14e
|
|||
|
2bd9d077cc
|
|||
|
a2d95855c5
|
|||
|
ad30a8dcf5
|
|||
|
1eda877a09
|
|||
|
8dcbf5a972
|
|||
|
f28f647368
|
|||
|
da77d25f6d
|
BIN
animations/312cc4e6-8c44-43e7-828a-e7e2a77836f3.png
Normal file
|
After Width: | Height: | Size: 951 B |
BIN
animations/54f657bd-8108-464c-9bbe-63944fc14f6b.png
Normal file
|
After Width: | Height: | Size: 276 KiB |
BIN
animations/61e67e44-a0cd-4210-8d1e-ccddcd62c78d.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
animations/e6f067f1-eba0-4e62-99c3-2fd867e6f142.png
Normal file
|
After Width: | Height: | Size: 324 KiB |
BIN
audio/1311327d-4b74-4252-94da-23ee4129e357.ogg
Normal file
BIN
audio/7c33cfee-e6a8-42b8-8b1d-c801b242dcf0.ogg
Normal file
BIN
audio/cd8a40f2-1e2e-4e1d-a13f-b4fe210a04df.ogg
Normal file
BIN
audio/e452e215-f581-40fe-a5cf-f555d3db83b8.ogg
Normal file
BIN
data.mv.db
BIN
entsets/15415d5a-2f53-4ee0-8f8f-8e81d702ccdb.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
entsets/4fff029b-6c24-4c83-9dfb-51f5512a687e.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
entsets/55a8c74b-e636-4c0a-9787-1878e672e368.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
entsets/71414ffb-0e1c-4778-9a1a-0f9f53388fd0.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
entsets/a1d5baf4-d5e8-4be9-ba7b-b37ca81b59fb.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
entsets/a5c6c6eb-e6cf-4b9e-8c35-11e4c8587ce7.png
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
entsets/a973f4ec-a7ad-4fd7-8002-b50a854fb733.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
22
project.bep
@@ -4,8 +4,26 @@ $f845355e-b9ad-4884-a217-dd3a4c18a3fa(f845355e-b9ad-4884-a217-dd3a4c18a3fa.dat
|
||||
$d314b030-f865-432e-a356-3845f8aac7bc(d314b030-f865-432e-a356-3845f8aac7bc.pngForrest Temple ](2Z
|
||||
$815a5c5c-4979-42f5-a42a-ccbbff9a97e5(815a5c5c-4979-42f5-a42a-ccbbff9a97e5.pngLuna (2Z
|
||||
$cb4c89a7-a421-49ea-8c58-571e9b215d37(cb4c89a7-a421-49ea-8c58-571e9b215d37.pngDeku (2\
|
||||
$9da2c95b-45b7-49e5-957b-c1c8803cdf28(9da2c95b-45b7-49e5-957b-c1c8803cdf28.pngCorpse (:`
|
||||
$9da2c95b-45b7-49e5-957b-c1c8803cdf28(9da2c95b-45b7-49e5-957b-c1c8803cdf28.pngCorpse (2Z
|
||||
$4fff029b-6c24-4c83-9dfb-51f5512a687e(4fff029b-6c24-4c83-9dfb-51f5512a687e.pngGaro (2\
|
||||
$71414ffb-0e1c-4778-9a1a-0f9f53388fd0(71414ffb-0e1c-4778-9a1a-0f9f53388fd0.pngBlanca (2\
|
||||
$a5c6c6eb-e6cf-4b9e-8c35-11e4c8587ce7(a5c6c6eb-e6cf-4b9e-8c35-11e4c8587ce7.pngTurtle (2\
|
||||
$15415d5a-2f53-4ee0-8f8f-8e81d702ccdb(15415d5a-2f53-4ee0-8f8f-8e81d702ccdb.pngChests (2[
|
||||
$a1d5baf4-d5e8-4be9-ba7b-b37ca81b59fb(a1d5baf4-d5e8-4be9-ba7b-b37ca81b59fb.pngEagle (2`
|
||||
$a973f4ec-a7ad-4fd7-8002-b50a854fb733(a973f4ec-a7ad-4fd7-8002-b50a854fb733.png
|
||||
Silver Bat (2^
|
||||
$55a8c74b-e636-4c0a-9787-1878e672e368(55a8c74b-e636-4c0a-9787-1878e672e368.pngSkeleton (:`
|
||||
$2261c04f-b02e-4486-b388-8a0fa41622e9(2261c04f-b02e-4486-b388-8a0fa41622e9.ttfRoboto RegularB\
|
||||
$ab9d40b4-eb28-45d7-bff2-9432a05eb41a(ab9d40b4-eb28-45d7-bff2-9432a05eb41a.xml
|
||||
Start MenuB[
|
||||
$56ca6b39-f949-4212-9c23-312db25887e0(56ca6b39-f949-4212-9c23-312db25887e0.xml Game Menu
|
||||
$56ca6b39-f949-4212-9c23-312db25887e0(56ca6b39-f949-4212-9c23-312db25887e0.xml Game MenuBU
|
||||
$00bd0625-b3b8-4abf-97b7-91f42bce28ec(00bd0625-b3b8-4abf-97b7-91f42bce28ec.xmlHUDJ[
|
||||
$61e67e44-a0cd-4210-8d1e-ccddcd62c78d(61e67e44-a0cd-4210-8d1e-ccddcd62c78d.pngSlash (JZ
|
||||
$e6f067f1-eba0-4e62-99c3-2fd867e6f142(e6f067f1-eba0-4e62-99c3-2fd867e6f142.pngPoof (J[
|
||||
$312cc4e6-8c44-43e7-828a-e7e2a77836f3(312cc4e6-8c44-43e7-828a-e7e2a77836f3.pngArrow (J[
|
||||
$54f657bd-8108-464c-9bbe-63944fc14f6b(54f657bd-8108-464c-9bbe-63944fc14f6b.pngPunch (R]
|
||||
$1311327d-4b74-4252-94da-23ee4129e357(1311327d-4b74-4252-94da-23ee4129e357.oggSword slashR\
|
||||
$e452e215-f581-40fe-a5cf-f555d3db83b8(e452e215-f581-40fe-a5cf-f555d3db83b8.ogg
|
||||
Deku deathRW
|
||||
$cd8a40f2-1e2e-4e1d-a13f-b4fe210a04df(cd8a40f2-1e2e-4e1d-a13f-b4fe210a04df.oggArrowR]
|
||||
$7c33cfee-e6a8-42b8-8b1d-c801b242dcf0(7c33cfee-e6a8-42b8-8b1d-c801b242dcf0.oggArrow punch
|
||||
42
src/main/java/com/bartlomiejpluta/demo/ai/AnimalAI.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.bartlomiejpluta.demo.ai;
|
||||
|
||||
import lombok.*;
|
||||
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
|
||||
import com.bartlomiejpluta.base.api.ai.*;
|
||||
import com.bartlomiejpluta.base.api.move.Direction;
|
||||
import com.bartlomiejpluta.base.lib.ai.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.entity.Enemy;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
public class AnimalAI implements AI {
|
||||
private final Enemy animal;
|
||||
private final Character character;
|
||||
private final int range;
|
||||
private final AI idleAI;
|
||||
private final AI runawayAI;
|
||||
|
||||
public AnimalAI(Enemy animal, Character character, int range) {
|
||||
this.animal = animal;
|
||||
this.character = character;
|
||||
this.range = range;
|
||||
this.idleAI = new RandomMovementAI<>(animal, 4);
|
||||
this.runawayAI = new RunawayAI<>(animal, character);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextActivity(ObjectLayer layer, float dt) {
|
||||
if(animal.isMoving()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var distance = animal.manhattanDistance(character);
|
||||
|
||||
if(animal.getHp() < animal.getMaxHp() && distance < range) {
|
||||
runawayAI.nextActivity(layer, dt);
|
||||
return;
|
||||
}
|
||||
|
||||
idleAI.nextActivity(layer, dt);
|
||||
}
|
||||
}
|
||||
53
src/main/java/com/bartlomiejpluta/demo/ai/ArcherAI.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.bartlomiejpluta.demo.ai;
|
||||
|
||||
import com.bartlomiejpluta.base.api.ai.AI;
|
||||
import com.bartlomiejpluta.base.api.ai.NPC;
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
|
||||
import com.bartlomiejpluta.base.api.move.MoveEvent;
|
||||
import com.bartlomiejpluta.base.lib.ai.KeepStraightDistanceAI;
|
||||
import com.bartlomiejpluta.base.lib.ai.RandomMovementAI;
|
||||
import com.bartlomiejpluta.base.util.path.MovementPath;
|
||||
import com.bartlomiejpluta.base.util.path.PathExecutor;
|
||||
import com.bartlomiejpluta.base.util.pathfinder.AstarPathFinder;
|
||||
import com.bartlomiejpluta.base.util.pathfinder.PathFinder;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector2ic;
|
||||
|
||||
import com.bartlomiejpluta.demo.entity.Enemy;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
public class ArcherAI extends KeepStraightDistanceAI<Enemy, Character> {
|
||||
private static final int ASTAR_MAX_NODES = 100;
|
||||
private static final int IDLE_MOVEMENT_INTERVAL = 4;
|
||||
private final int range;
|
||||
private final AI idle;
|
||||
|
||||
public ArcherAI(Enemy enemy, Character target, int minRange, int maxRange, int range) {
|
||||
super(new AstarPathFinder(ASTAR_MAX_NODES), enemy, target, minRange, maxRange);
|
||||
this.range = range;
|
||||
this.idle = new RandomMovementAI<>(enemy, IDLE_MOVEMENT_INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean sees(Enemy enemy, Character target, ObjectLayer layer) {
|
||||
return enemy.manhattanDistance(target) < 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);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ public class SimpleEnemyAI extends FollowEntityAI<Enemy, Character> {
|
||||
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);
|
||||
this.idle = new RandomMovementAI<>(enemy, IDLE_MOVEMENT_INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.bartlomiejpluta.demo.ai;
|
||||
|
||||
import lombok.*;
|
||||
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
|
||||
import com.bartlomiejpluta.base.api.ai.*;
|
||||
import com.bartlomiejpluta.base.lib.ai.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.entity.Enemy;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class SimpleSniperAI implements AI {
|
||||
private final Enemy enemy;
|
||||
private Character target;
|
||||
private int range;
|
||||
|
||||
@Override
|
||||
public void nextActivity(ObjectLayer layer, float dt) {
|
||||
var enemyCoords = enemy.getCoordinates();
|
||||
var targetCoords = target.getCoordinates();
|
||||
if(enemy.manhattanDistance(target) <= range && (enemyCoords.x() == targetCoords.x() || enemyCoords.y() == targetCoords.y())) {
|
||||
var direction = enemy.getDirectionTowards(target);
|
||||
enemy.setFaceDirection(direction);
|
||||
enemy.attack();
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/main/java/com/bartlomiejpluta/demo/ai/WeaponBasedAI.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package com.bartlomiejpluta.demo.ai;
|
||||
|
||||
import lombok.*;
|
||||
import com.bartlomiejpluta.base.api.ai.AI;
|
||||
import com.bartlomiejpluta.base.api.move.MoveEvent;
|
||||
import com.bartlomiejpluta.base.api.map.layer.object.ObjectLayer;
|
||||
import com.bartlomiejpluta.base.lib.ai.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
import com.bartlomiejpluta.demo.entity.Enemy;
|
||||
import com.bartlomiejpluta.demo.world.weapon.*;
|
||||
|
||||
public class WeaponBasedAI implements AI {
|
||||
private static final int RANGE = 10;
|
||||
private static final int MIN_RANGE = 3;
|
||||
private static final int MAX_RANGE = 12;
|
||||
private final Enemy enemy;
|
||||
private final Character target;
|
||||
private final RunawayAI runawayAI;
|
||||
private final SimpleEnemyAI meleeAI;
|
||||
private final ArcherAI archerAI;
|
||||
|
||||
public WeaponBasedAI(@NonNull Enemy enemy, @NonNull Character target) {
|
||||
this.enemy = enemy;
|
||||
this.target = target;
|
||||
this.runawayAI = new RunawayAI<>(enemy, target);
|
||||
this.meleeAI = new SimpleEnemyAI(enemy, target, RANGE);
|
||||
this.archerAI = new ArcherAI(enemy, target, MIN_RANGE, MAX_RANGE, RANGE);
|
||||
}
|
||||
|
||||
public void recomputePath() {
|
||||
meleeAI.recomputePath();
|
||||
archerAI.recomputePath();
|
||||
}
|
||||
|
||||
public void recomputePath(@NonNull MoveEvent event) {
|
||||
meleeAI.recomputePath(event);
|
||||
archerAI.recomputePath(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextActivity(ObjectLayer layer, float dt) {
|
||||
var lastAttacker = enemy.getLastAttacker();
|
||||
if(lastAttacker != null && lastAttacker instanceof Character) {
|
||||
var attacker = (Character) lastAttacker;
|
||||
if(attacker.isAlive()) {
|
||||
runawayAI.setDanger(attacker);
|
||||
meleeAI.setTarget(attacker);
|
||||
archerAI.setTarget(attacker);
|
||||
} else {
|
||||
runawayAI.setDanger(target);
|
||||
meleeAI.setTarget(target);
|
||||
archerAI.setTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
var meleeWeapon = enemy.getMeleeWeapon();
|
||||
var rangedWeapon = enemy.getRangedWeapon();
|
||||
|
||||
if(meleeWeapon == null && rangedWeapon == null) {
|
||||
runawayAI.nextActivity(layer, dt);
|
||||
return;
|
||||
}
|
||||
|
||||
if(rangedWeapon == null || enemy.manhattanDistance(target) == 1) {
|
||||
enemy.setWeapon(meleeWeapon);
|
||||
meleeAI.nextActivity(layer, dt);
|
||||
return;
|
||||
}
|
||||
|
||||
if(enemy.getWeapon() instanceof MeleeWeapon) {
|
||||
meleeAI.nextActivity(layer, dt);
|
||||
}
|
||||
|
||||
enemy.setWeapon(rangedWeapon);
|
||||
archerAI.nextActivity(layer, dt);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.bartlomiejpluta.demo.database.dao;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
|
||||
import com.bartlomiejpluta.demo.database.model.EnemyModel;
|
||||
|
||||
public class EnemyDAO {
|
||||
private final Map<String, EnemyModel> enemies = new HashMap<>();
|
||||
|
||||
public void init(Context context) {
|
||||
context.withDatabase(db -> {
|
||||
var result = db.prepareStatement("SELECT * FROM `enemy`").executeQuery();
|
||||
|
||||
while(result.next()) {
|
||||
var enemy = EnemyModel.builder()
|
||||
.id(result.getString("id"))
|
||||
.name(result.getString("name"))
|
||||
.entitySet(result.getString("entset"))
|
||||
.deadEntitySet(result.getString("dead_entset"))
|
||||
.hp(result.getInt("hp"))
|
||||
.speed(result.getFloat("speed"))
|
||||
.animationSpeed(result.getFloat("animation_speed"))
|
||||
.blocking(result.getBoolean("blocking"))
|
||||
.meleeWeapon(result.getString("melee_weapon"))
|
||||
.build();
|
||||
enemies.put(result.getString("id"), enemy);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public EnemyModel get(String id) {
|
||||
return enemies.get(id);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.bartlomiejpluta.demo.database.dao;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
|
||||
import com.bartlomiejpluta.demo.database.model.MeleeWeaponModel;
|
||||
|
||||
public class MeleeWeaponDAO {
|
||||
private final Map<String, MeleeWeaponModel> items = new HashMap<>();
|
||||
|
||||
public void init(Context context) {
|
||||
context.withDatabase(db -> {
|
||||
var result = db.prepareStatement("SELECT * FROM `melee_weapon`").executeQuery();
|
||||
|
||||
while(result.next()) {
|
||||
var weapon = MeleeWeaponModel.builder()
|
||||
.id(result.getString("id"))
|
||||
.name(result.getString("name"))
|
||||
.damage(result.getString("damage"))
|
||||
.cooldown(result.getInt("cooldown"))
|
||||
.build();
|
||||
items.put(result.getString("id"), weapon);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MeleeWeaponModel get(String id) {
|
||||
return items.get(id);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.bartlomiejpluta.demo.database.model;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class EnemyModel {
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final String entitySet;
|
||||
private final String deadEntitySet;
|
||||
private final int hp;
|
||||
private final float speed;
|
||||
private final float animationSpeed;
|
||||
private final boolean blocking;
|
||||
private final String meleeWeapon;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.bartlomiejpluta.demo.database.model;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class MeleeWeaponModel {
|
||||
private final String id;
|
||||
private final String name;
|
||||
private final String damage;
|
||||
private final int cooldown;
|
||||
}
|
||||
@@ -5,12 +5,12 @@ import org.slf4j.*;
|
||||
import org.joml.Vector2i;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.base.lib.entity.EntityDelegate;
|
||||
import com.bartlomiejpluta.base.lib.animation.AnimationRunner;
|
||||
|
||||
import com.bartlomiejpluta.demo.runner.DemoRunner;
|
||||
import com.bartlomiejpluta.demo.world.weapon.MeleeWeapon;
|
||||
import com.bartlomiejpluta.demo.world.weapon.Weapon;
|
||||
|
||||
public class Character extends EntityDelegate {
|
||||
public abstract class Character extends NamedEntity {
|
||||
private static final Logger log = LoggerFactory.getLogger(Character.class);
|
||||
protected final Context context;
|
||||
protected final DemoRunner runner;
|
||||
@@ -23,11 +23,18 @@ public class Character extends EntityDelegate {
|
||||
@Getter
|
||||
protected boolean immortal = false;
|
||||
|
||||
@Getter
|
||||
protected int maxHp;
|
||||
|
||||
@Getter
|
||||
protected int hp;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private MeleeWeapon weapon;
|
||||
private Weapon weapon;
|
||||
|
||||
@Getter
|
||||
private NamedEntity lastAttacker;
|
||||
|
||||
public Character(@NonNull Context context, @NonNull Entity entity) {
|
||||
super(entity);
|
||||
@@ -41,22 +48,19 @@ public class Character extends EntityDelegate {
|
||||
}
|
||||
|
||||
if(attackCooldown >= weapon.getCooldown()) {
|
||||
var facingNeighbour = getCoordinates().add(getFaceDirection().vector, new Vector2i());
|
||||
for(var entity : getLayer().getEntities()) {
|
||||
if(entity.getCoordinates().equals(facingNeighbour) && entity.isBlocking() && entity instanceof Character) {
|
||||
weapon.attack((Character) entity);
|
||||
attackCooldown = 0;
|
||||
}
|
||||
if(weapon.attack(this)) {
|
||||
attackCooldown = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hit(int dmg) {
|
||||
public void hit(NamedEntity source, int dmg) {
|
||||
this.lastAttacker = source;
|
||||
|
||||
if(immortal) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(toString() + " received " + dmg + " damage");
|
||||
hp -= dmg;
|
||||
}
|
||||
|
||||
@@ -75,6 +79,8 @@ public class Character extends EntityDelegate {
|
||||
}
|
||||
|
||||
protected void die() {
|
||||
log.info(toString() + " died with HP = " + hp);
|
||||
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
}
|
||||
@@ -8,27 +8,54 @@ 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.base.lib.ai.*;
|
||||
import com.bartlomiejpluta.base.lib.animation.*;
|
||||
import com.bartlomiejpluta.base.util.random.DiceRoller;
|
||||
|
||||
import com.bartlomiejpluta.demo.runner.DemoRunner;
|
||||
import com.bartlomiejpluta.demo.database.model.EnemyModel;
|
||||
import com.bartlomiejpluta.demo.world.weapon.MeleeWeapon;
|
||||
import com.bartlomiejpluta.base.generated.db.model.EnemyModel;
|
||||
import com.bartlomiejpluta.demo.world.weapon.*;
|
||||
import com.bartlomiejpluta.demo.event.EnemyDiedEvent;
|
||||
import com.bartlomiejpluta.demo.ai.*;
|
||||
import com.bartlomiejpluta.demo.ai.ArcherAI;
|
||||
|
||||
|
||||
public class Enemy extends Character implements NPC {
|
||||
private final EnemyModel template;
|
||||
private AI ai = NoopAI.INSTANCE;
|
||||
private final AnimationRunner dieAnimation;
|
||||
|
||||
@Getter
|
||||
private MeleeWeapon meleeWeapon;
|
||||
|
||||
@Getter
|
||||
private RangedWeapon rangedWeapon;
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
|
||||
public Enemy(@NonNull Context context, @NonNull EnemyModel template) {
|
||||
super(context, context.createEntity(template.getEntitySet()));
|
||||
super(context, context.createEntity(template.getEntset()));
|
||||
this.template = template;
|
||||
hp = template.getHp();
|
||||
name = template.getName();
|
||||
maxHp = DiceRoller.of(template.getHp()).roll();
|
||||
hp = maxHp;
|
||||
setSpeed(template.getSpeed());
|
||||
setAnimationSpeed(template.getAnimationSpeed());
|
||||
setBlocking(template.isBlocking());
|
||||
setWeapon(new MeleeWeapon(((DemoRunner) context.getGameRunner()).getMeleeWeaponDAO().get(template.getMeleeWeapon())));
|
||||
var runner = (DemoRunner) context.getGameRunner();
|
||||
var meleeWeaponTemplate = template.getMeleeWeapon();
|
||||
var rangedWeaponTemplate = template.getRangedWeapon();
|
||||
|
||||
if(meleeWeaponTemplate != null) {
|
||||
this.meleeWeapon = new MeleeWeapon(context, runner.getMeleeWeaponDAO().find(meleeWeaponTemplate));
|
||||
}
|
||||
|
||||
if(rangedWeaponTemplate != null) {
|
||||
this.rangedWeapon = new RangedWeapon(context, runner.getRangedWeaponDAO().find(rangedWeaponTemplate));
|
||||
}
|
||||
|
||||
this.dieAnimation = new SimpleAnimationRunner(template.getDieAnimation());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,25 +66,56 @@ public class Enemy extends Character implements NPC {
|
||||
@Override
|
||||
public void die() {
|
||||
super.die();
|
||||
changeEntitySet(template.getDeadEntitySet());
|
||||
changeEntitySet(template.getDeadEntset());
|
||||
setScale(0.5f);
|
||||
setBlocking(false);
|
||||
setZIndex(-1);
|
||||
|
||||
ai = NoopAI.INSTANCE;
|
||||
|
||||
getLayer().handleEvent(new EnemyDiedEvent(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return template.getName() + "@" + hashCode();
|
||||
dieAnimation.run(context, getLayer(), this);
|
||||
context.playSound(template.getDieSound());
|
||||
context.fireEvent(new EnemyDiedEvent(this));
|
||||
}
|
||||
|
||||
public Enemy followAndAttack(Character target, int range) {
|
||||
var ai = new SimpleEnemyAI(this, target, range);
|
||||
|
||||
addEventListener(MoveEvent.TYPE, e -> ai.recomputePath());
|
||||
addEventListener(MoveEvent.TYPE, ai::recomputePath);
|
||||
addEventListener(EnemyDiedEvent.TYPE, e -> ai.recomputePath());
|
||||
|
||||
this.ai = ai;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Enemy campAndHunt(Character target, int range) {
|
||||
this.ai = new SimpleSniperAI(this, target, range);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Enemy asAnimal(Character source, int range) {
|
||||
this.ai = new AnimalAI(this, source, range);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Enemy archer(Character target, int minRange, int maxRange, int range) {
|
||||
var ai = new ArcherAI(this, target, minRange, maxRange, range);
|
||||
|
||||
addEventListener(MoveEvent.TYPE, ai::recomputePath);
|
||||
addEventListener(EnemyDiedEvent.TYPE, e -> ai.recomputePath());
|
||||
|
||||
this.ai = ai;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Enemy defaultAI() {
|
||||
var ai = new WeaponBasedAI(this, runner.getPlayer());
|
||||
|
||||
addEventListener(MoveEvent.TYPE, ai::recomputePath);
|
||||
addEventListener(EnemyDiedEvent.TYPE, e -> ai.recomputePath());
|
||||
|
||||
this.ai = ai;
|
||||
|
||||
77
src/main/java/com/bartlomiejpluta/demo/entity/MapObject.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package com.bartlomiejpluta.demo.entity;
|
||||
|
||||
import lombok.*;
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.move.*;
|
||||
import com.bartlomiejpluta.base.lib.entity.EntityDelegate;
|
||||
import com.bartlomiejpluta.base.util.path.*;
|
||||
|
||||
import com.bartlomiejpluta.base.generated.db.model.MapObjectModel;
|
||||
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
public class MapObject extends EntityDelegate {
|
||||
private final PathExecutor<MapObject> pathExecutor = new PathExecutor<>(this);
|
||||
private final Context context;
|
||||
private final MapObjectModel template;
|
||||
private final Short frame;
|
||||
private final String interactSound;
|
||||
private boolean interacting = false;
|
||||
|
||||
public MapObject(@NonNull Context context, @NonNull MapObjectModel template) {
|
||||
super(context.createEntity(template.getEntset()));
|
||||
this.context = context;
|
||||
this.template = template;
|
||||
this.frame = template.getFrame();
|
||||
this.interactSound = template.getInteractSound();
|
||||
|
||||
setBlocking(true);
|
||||
disableAnimation();
|
||||
|
||||
if(frame != null) {
|
||||
setAnimationFrame(frame);
|
||||
}
|
||||
|
||||
pathExecutor.setPath(
|
||||
frame != null
|
||||
? new EntityPath<MapObject>()
|
||||
.run(this::startInteraction)
|
||||
.turn(Direction.LEFT, frame)
|
||||
.wait(0.05f)
|
||||
.turn(Direction.RIGHT, frame)
|
||||
.wait(0.05f)
|
||||
.turn(Direction.UP, frame)
|
||||
.wait(0.5f)
|
||||
.turn(Direction.RIGHT, frame)
|
||||
.wait(0.05f)
|
||||
.turn(Direction.LEFT, frame)
|
||||
.wait(0.05f)
|
||||
.turn(Direction.DOWN, frame)
|
||||
.wait(0.5f)
|
||||
.run(this::finishInteraction)
|
||||
: new EntityPath<MapObject>()
|
||||
);
|
||||
}
|
||||
|
||||
public void interact(Character character) {
|
||||
interacting = true;
|
||||
}
|
||||
|
||||
private void startInteraction() {
|
||||
if(interactSound != null) {
|
||||
context.playSound(interactSound);
|
||||
}
|
||||
}
|
||||
|
||||
private void finishInteraction() {
|
||||
interacting = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
if(interacting) {
|
||||
pathExecutor.execute(getLayer(), dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.bartlomiejpluta.demo.entity;
|
||||
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.base.lib.entity.EntityDelegate;
|
||||
|
||||
public abstract class NamedEntity extends EntityDelegate {
|
||||
|
||||
public NamedEntity(Entity entity) {
|
||||
super(entity);
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
}
|
||||
@@ -1,15 +1,26 @@
|
||||
package com.bartlomiejpluta.demo.entity;
|
||||
|
||||
import lombok.*;
|
||||
import org.slf4j.*;
|
||||
import org.joml.Vector2i;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.demo.entity.MapObject;
|
||||
|
||||
public class Player extends Character {
|
||||
private static final Logger log = LoggerFactory.getLogger(Player.class);
|
||||
|
||||
public Player(@NonNull Context context, @NonNull Entity entity) {
|
||||
super(context, entity);
|
||||
this.hp = 500;
|
||||
this.maxHp = 500;
|
||||
}
|
||||
|
||||
public void interact() {
|
||||
var coords = getCoordinates().add(getFaceDirection().vector, new Vector2i());
|
||||
for(var entity : getLayer().getEntities()) {
|
||||
if(entity.getCoordinates().equals(coords) && entity instanceof MapObject) {
|
||||
((MapObject) entity).interact(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -17,4 +28,9 @@ public class Player extends Character {
|
||||
super.die();
|
||||
runner.returnToStartMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Player";
|
||||
}
|
||||
}
|
||||
22
src/main/java/com/bartlomiejpluta/demo/event/HitEvent.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.bartlomiejpluta.demo.event;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import com.bartlomiejpluta.base.api.event.*;
|
||||
import com.bartlomiejpluta.base.lib.event.*;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class HitEvent extends BaseEvent {
|
||||
public static final EventType<HitEvent> TYPE = new EventType<>("HIT_EVENT");
|
||||
|
||||
private final Character attacker;
|
||||
private final Character target;
|
||||
private final int damage;
|
||||
|
||||
@Override
|
||||
public EventType<HitEvent> getType() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
65
src/main/java/com/bartlomiejpluta/demo/gui/Bar.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.bartlomiejpluta.demo.gui;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
import com.bartlomiejpluta.base.api.gui.*;
|
||||
import com.bartlomiejpluta.base.lib.gui.*;
|
||||
|
||||
import com.bartlomiejpluta.base.api.screen.*;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.input.*;
|
||||
|
||||
public class Bar extends BaseComponent {
|
||||
|
||||
@Setter
|
||||
private float value = 1.0f;
|
||||
private float actualValue = 1.0f;
|
||||
private float speed = 0.05f;
|
||||
private final Color stroke;
|
||||
private final Color fill;
|
||||
|
||||
public Bar(Context context, GUI gui) {
|
||||
super(context, gui);
|
||||
|
||||
this.stroke = gui.createColor();
|
||||
this.fill = gui.createColor();
|
||||
|
||||
stroke.setAlpha(1f);
|
||||
fill.setAlpha(1f);
|
||||
}
|
||||
|
||||
public void setStrokeColor(Integer hex) {
|
||||
stroke.setRGB(hex);
|
||||
}
|
||||
|
||||
public void setFillColor(Integer hex) {
|
||||
fill.setRGB(hex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getContentWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getContentHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Screen screen, GUI gui) {
|
||||
var remainingDistance = value - actualValue;
|
||||
actualValue += remainingDistance * speed;
|
||||
|
||||
gui.beginPath();
|
||||
gui.drawRectangle(x, y, Math.max(width * actualValue, 0), height);
|
||||
gui.setFillColor(fill);
|
||||
gui.fill();
|
||||
gui.closePath();
|
||||
gui.beginPath();
|
||||
gui.drawRectangle(x, y, width, height);
|
||||
gui.setStrokeColor(stroke);
|
||||
gui.stroke();
|
||||
gui.closePath();
|
||||
}
|
||||
}
|
||||
100
src/main/java/com/bartlomiejpluta/demo/gui/HUD.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package com.bartlomiejpluta.demo.gui;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.bartlomiejpluta.base.api.gui.*;
|
||||
import com.bartlomiejpluta.base.lib.gui.*;
|
||||
|
||||
import com.bartlomiejpluta.base.api.screen.*;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.input.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.runner.DemoRunner;
|
||||
import com.bartlomiejpluta.demo.entity.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.event.*;
|
||||
import com.bartlomiejpluta.demo.util.LimitedQueue;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class HUD extends BorderLayout {
|
||||
private static final int MAX_LOG_SIZE = 10;
|
||||
private static final float LOG_VISIBILITY_DURATION = 8000f;
|
||||
private static final float LOG_VISIBILITY_FADING_OUT = 1000f;
|
||||
private final DemoRunner runner;
|
||||
private final Player player;
|
||||
private final Runtime runtime;
|
||||
private LimitedQueue<String> logger = new LimitedQueue<>(MAX_LOG_SIZE);
|
||||
|
||||
private float logVisibilityDuration = 0f;
|
||||
|
||||
@Ref("hp")
|
||||
private Bar hp;
|
||||
|
||||
@Ref("debug")
|
||||
private Label debugLbl;
|
||||
|
||||
@Ref("log")
|
||||
private Label logLbl;
|
||||
|
||||
public HUD(Context context, GUI gui) {
|
||||
super(context, gui);
|
||||
this.runner = (DemoRunner) context.getGameRunner();
|
||||
this.player = runner.getPlayer();
|
||||
this.runtime = Runtime.getRuntime();
|
||||
context.addEventListener(HitEvent.TYPE, this::logHitEvent);
|
||||
context.addEventListener(EnemyDiedEvent.TYPE, this::logEnemyDiedEvent);
|
||||
}
|
||||
|
||||
private void logHitEvent(HitEvent event) {
|
||||
log(String.format("%s hits %s with damage = %d", event.getAttacker().getName(), event.getTarget().getName(), event.getDamage()));
|
||||
}
|
||||
|
||||
private void log(String message) {
|
||||
logger.add(message);
|
||||
log.info(message);
|
||||
logLbl.setText(logger.stream().collect(Collectors.joining("\n")));
|
||||
logVisibilityDuration = LOG_VISIBILITY_DURATION;
|
||||
}
|
||||
|
||||
private void logEnemyDiedEvent(EnemyDiedEvent event) {
|
||||
log(String.format("%s has died with HP = %d", event.getEnemy().getName(), event.getEnemy().getHp()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
super.update(dt);
|
||||
|
||||
hp.setValue((float) player.getHp() / (float) player.getMaxHp());
|
||||
|
||||
if(logVisibilityDuration > 0) {
|
||||
logVisibilityDuration -= dt * 1000;
|
||||
} else {
|
||||
logVisibilityDuration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Screen screen, GUI gui) {
|
||||
var coords = player.getCoordinates();
|
||||
var pos = player.getPosition();
|
||||
debugLbl.setText(String.format(
|
||||
"FPS: %.2f\n" +
|
||||
"Mem: %.2f / %.2f [MB]\n" +
|
||||
"Coords: %d : %d\n" +
|
||||
"Pos: %.2f : %.2f\n" +
|
||||
"Entities: %d",
|
||||
runner.instantFPS(),
|
||||
runtime.totalMemory() / 1024f / 1024f,
|
||||
runtime.maxMemory() / 1024f / 1024f,
|
||||
coords.x(), coords.y(),
|
||||
pos.x(), pos.y(),
|
||||
player.getLayer().getEntities().size() - 1
|
||||
));
|
||||
|
||||
logLbl.setAlpha(Math.min(1f, logVisibilityDuration / LOG_VISIBILITY_FADING_OUT));
|
||||
|
||||
super.draw(screen, gui);
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,27 @@ import com.bartlomiejpluta.base.api.input.*;
|
||||
|
||||
import com.bartlomiejpluta.base.lib.camera.*;
|
||||
|
||||
import com.bartlomiejpluta.base.util.world.EntitySpawner;
|
||||
|
||||
import com.bartlomiejpluta.demo.runner.DemoRunner;
|
||||
import com.bartlomiejpluta.demo.entity.*;
|
||||
import com.bartlomiejpluta.demo.event.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.util.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
|
||||
public abstract class BaseMapHandler implements MapHandler {
|
||||
protected Screen screen;
|
||||
protected Context context;
|
||||
protected DemoRunner runner;
|
||||
protected Camera camera;
|
||||
protected GameMap map;
|
||||
protected Player player;
|
||||
protected ObjectLayer mainLayer;
|
||||
protected CameraController cameraController;
|
||||
protected final List<EntitySpawner> spawners = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void onCreate(Context context, GameMap map) {
|
||||
@@ -31,6 +41,7 @@ public abstract class BaseMapHandler implements MapHandler {
|
||||
this.screen = context.getScreen();
|
||||
this.runner = (DemoRunner) context.getGameRunner();
|
||||
this.camera = context.getCamera();
|
||||
this.map = map;
|
||||
this.player = runner.getPlayer();
|
||||
this.cameraController = FollowingCameraController
|
||||
.on(screen, camera, map)
|
||||
@@ -47,6 +58,10 @@ public abstract class BaseMapHandler implements MapHandler {
|
||||
player.attack();
|
||||
}
|
||||
|
||||
if(input.isKeyPressed(Key.KEY_ENTER)) {
|
||||
player.interact();
|
||||
}
|
||||
|
||||
if(input.isKeyPressed(Key.KEY_LEFT_CONTROL)) {
|
||||
if(input.isKeyPressed(Key.KEY_DOWN)) {
|
||||
player.setFaceDirection(Direction.DOWN);
|
||||
@@ -73,12 +88,32 @@ public abstract class BaseMapHandler implements MapHandler {
|
||||
@Override
|
||||
public void update(Context context, GameMap map, float dt) {
|
||||
cameraController.update();
|
||||
for(var spawner : spawners) {
|
||||
spawner.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
public Enemy enemy(@NonNull String id) {
|
||||
return new Enemy(context, runner.getEnemyDAO().find(id));
|
||||
}
|
||||
|
||||
public Enemy enemy(int x, int y, @NonNull String id) {
|
||||
var enemy = new Enemy(context, runner.getEnemyDAO().get(id));
|
||||
var enemy = new Enemy(context, runner.getEnemyDAO().find(id));
|
||||
enemy.setCoordinates(x, y);
|
||||
mainLayer.addEntity(enemy);
|
||||
return enemy;
|
||||
}
|
||||
|
||||
public MapObject object(int x, int y, @NonNull String id) {
|
||||
var object = new MapObject(context, runner.getMapObjectDAO().find(id));
|
||||
object.setCoordinates(x, y);
|
||||
mainLayer.addEntity(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
public EntitySpawner spawner(int x, int y, ObjectLayer layer) {
|
||||
var spawner = new EntitySpawner(x, y, context, map, layer).trackEntities(EnemyDiedEvent.TYPE);
|
||||
this.spawners.add(spawner);
|
||||
return spawner;
|
||||
}
|
||||
}
|
||||
@@ -9,39 +9,53 @@ import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.input.Input;
|
||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||
import com.bartlomiejpluta.base.api.runner.GameRunner;
|
||||
import com.bartlomiejpluta.base.api.gui.GUI;
|
||||
|
||||
import com.bartlomiejpluta.base.util.profiler.FPSProfiler;
|
||||
|
||||
import com.bartlomiejpluta.base.generated.db.dao.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.map.ForrestTempleHandler;
|
||||
import com.bartlomiejpluta.demo.entity.Player;
|
||||
import com.bartlomiejpluta.demo.menu.MenuManager;
|
||||
import com.bartlomiejpluta.demo.database.dao.*;
|
||||
|
||||
import com.bartlomiejpluta.demo.world.weapon.MeleeWeapon;
|
||||
import com.bartlomiejpluta.demo.world.weapon.*;
|
||||
|
||||
public class DemoRunner implements GameRunner {
|
||||
private static final Logger log = LoggerFactory.getLogger(DemoRunner.class);
|
||||
private Screen screen;
|
||||
private Context context;
|
||||
private MenuManager menu;
|
||||
private GUI hud;
|
||||
|
||||
@Getter
|
||||
private MeleeWeaponDAO meleeWeaponDAO = new MeleeWeaponDAO();
|
||||
@Getter
|
||||
private MeleeWeaponDAO meleeWeaponDAO;
|
||||
|
||||
@Getter
|
||||
private EnemyDAO enemyDAO = new EnemyDAO();
|
||||
@Getter
|
||||
private RangedWeaponDAO rangedWeaponDAO;
|
||||
|
||||
@Getter
|
||||
private Player player;
|
||||
@Getter
|
||||
private EnemyDAO enemyDAO;
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
@Getter
|
||||
private MapObjectDAO mapObjectDAO;
|
||||
|
||||
@Getter
|
||||
private Player player;
|
||||
|
||||
private final FPSProfiler fpsProfiler = FPSProfiler.create(20);
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
this.context = context;
|
||||
this.screen = context.getScreen();
|
||||
|
||||
configureScreen();
|
||||
configureCamera();
|
||||
initDAOs();
|
||||
initMenu();
|
||||
initPlayer();
|
||||
initDAOs();
|
||||
initHUD();
|
||||
initMenu();
|
||||
|
||||
menu.showStartMenu();
|
||||
|
||||
@@ -59,14 +73,23 @@ public class DemoRunner implements GameRunner {
|
||||
}
|
||||
|
||||
private void initDAOs() {
|
||||
meleeWeaponDAO.init(context);
|
||||
enemyDAO.init(context);
|
||||
meleeWeaponDAO = new MeleeWeaponDAO(context);
|
||||
enemyDAO = new EnemyDAO(context);
|
||||
rangedWeaponDAO = new RangedWeaponDAO(context);
|
||||
mapObjectDAO = new MapObjectDAO(context);
|
||||
}
|
||||
|
||||
private void initMenu() {
|
||||
this.menu = new MenuManager(this, context);
|
||||
}
|
||||
|
||||
private void initHUD() {
|
||||
hud = context.newGUI();
|
||||
hud.hide();
|
||||
var hudComponent = hud.inflateComponent("00bd0625-b3b8-4abf-97b7-91f42bce28ec");
|
||||
hud.setRoot(hudComponent);
|
||||
}
|
||||
|
||||
private void initPlayer() {
|
||||
this.player = new Player(context, context.createEntity("815a5c5c-4979-42f5-a42a-ccbbff9a97e5"));
|
||||
}
|
||||
@@ -78,7 +101,7 @@ public class DemoRunner implements GameRunner {
|
||||
this.player.setAnimationSpeed(0.005f);
|
||||
this.player.setBlocking(true);
|
||||
this.player.setCoordinates(0, 11);
|
||||
this.player.setWeapon(new MeleeWeapon(meleeWeaponDAO.get("wooden_sword")));
|
||||
this.player.setWeapon(new RangedWeapon(context, rangedWeaponDAO.find("wooden_bow")));
|
||||
}
|
||||
|
||||
public void newGame() {
|
||||
@@ -88,10 +111,12 @@ public class DemoRunner implements GameRunner {
|
||||
context.openMap(ForrestTempleHandler.UID);
|
||||
context.getMap().getObjectLayer(ForrestTempleHandler.MAIN_LAYER).addEntity(this.player);
|
||||
context.resume();
|
||||
hud.show();
|
||||
}
|
||||
|
||||
public void returnToStartMenu() {
|
||||
menu.closeAll();
|
||||
hud.hide();
|
||||
context.pause();
|
||||
context.closeMap();
|
||||
menu.disableGameMenu();
|
||||
@@ -102,9 +127,13 @@ public class DemoRunner implements GameRunner {
|
||||
context.close();
|
||||
}
|
||||
|
||||
public double instantFPS() {
|
||||
return fpsProfiler.getInstantFPS();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
|
||||
fpsProfiler.update(dt);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package com.bartlomiejpluta.demo.util;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class DiceRoller {
|
||||
private static final Pattern CODE_PATTERN = Pattern.compile("(\\d+)d(\\d+)([+-]\\d+)?");
|
||||
private final Random random = new Random();
|
||||
private int rolls;
|
||||
private int dice;
|
||||
private int modifier;
|
||||
|
||||
public int roll() {
|
||||
var sum = modifier;
|
||||
|
||||
for(int i=0; i<rolls; ++i) {
|
||||
sum += random.nextInt(dice) + 1;
|
||||
}
|
||||
|
||||
return Math.max(0, sum);
|
||||
}
|
||||
|
||||
public static DiceRoller of(String code) {
|
||||
var matcher = CODE_PATTERN.matcher(code);
|
||||
matcher.matches();
|
||||
return new DiceRoller(Integer.valueOf(matcher.group(1)), Integer.valueOf(matcher.group(2)), Integer.valueOf(matcher.group(3)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.bartlomiejpluta.demo.util;
|
||||
|
||||
import lombok.*;
|
||||
import java.util.LinkedList;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class LimitedQueue<E> extends LinkedList<E> {
|
||||
private int limit;
|
||||
|
||||
@Override
|
||||
public boolean add(E o) {
|
||||
super.add(o);
|
||||
|
||||
while (size() > limit) {
|
||||
super.remove();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,22 @@ package com.bartlomiejpluta.demo.world.weapon;
|
||||
import java.util.Random;
|
||||
|
||||
import lombok.*;
|
||||
import com.bartlomiejpluta.demo.database.model.MeleeWeaponModel;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
import com.bartlomiejpluta.demo.util.DiceRoller;
|
||||
import org.joml.Vector2i;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.lib.animation.*;
|
||||
import com.bartlomiejpluta.base.util.random.DiceRoller;
|
||||
|
||||
public class MeleeWeapon {
|
||||
import com.bartlomiejpluta.base.generated.db.model.MeleeWeaponModel;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
import com.bartlomiejpluta.demo.event.HitEvent;
|
||||
|
||||
public class MeleeWeapon implements Weapon {
|
||||
private final Random random = new Random();
|
||||
private final Context context;
|
||||
private final DiceRoller roller;
|
||||
private final AnimationRunner animation;
|
||||
private final String sound;
|
||||
|
||||
@Getter
|
||||
private String name;
|
||||
@@ -17,13 +26,36 @@ public class MeleeWeapon {
|
||||
@Getter
|
||||
private int cooldown;
|
||||
|
||||
public MeleeWeapon(MeleeWeaponModel template) {
|
||||
public MeleeWeapon(@NonNull Context context, @NonNull MeleeWeaponModel template) {
|
||||
this.context = context;
|
||||
this.name = template.getName();
|
||||
this.roller = DiceRoller.of(template.getDamage());
|
||||
this.cooldown = template.getCooldown();
|
||||
this.animation = new RandomAnimationsRunner(5)
|
||||
.nRange(0, 2f)
|
||||
.nScale(0.2f, 0.15f)
|
||||
.uAnimationSpeed(0.01f, 0.05f)
|
||||
.offset(0, -10)
|
||||
.uDelay(0, 500)
|
||||
.with(template.getAnimation());
|
||||
this.sound = template.getSound();
|
||||
}
|
||||
|
||||
public void attack(Character character) {
|
||||
character.hit(roller.roll());
|
||||
@Override
|
||||
public boolean attack(Character attacker) {
|
||||
var facingNeighbour = attacker.getCoordinates().add(attacker.getFaceDirection().vector, new Vector2i());
|
||||
for(var entity : attacker.getLayer().getEntities()) {
|
||||
if(entity.getCoordinates().equals(facingNeighbour) && entity.isBlocking() && entity instanceof Character) {
|
||||
var character = (Character) entity;
|
||||
var damage = roller.roll();
|
||||
character.hit(attacker, damage);
|
||||
animation.run(context, character.getLayer(), character);
|
||||
context.playSound(sound);
|
||||
context.fireEvent(new HitEvent(attacker, character, damage));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.bartlomiejpluta.demo.world.weapon;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import lombok.*;
|
||||
import com.bartlomiejpluta.base.api.context.Context;
|
||||
import com.bartlomiejpluta.base.api.entity.Entity;
|
||||
import com.bartlomiejpluta.base.api.animation.Animation;
|
||||
import com.bartlomiejpluta.base.api.move.*;
|
||||
import com.bartlomiejpluta.base.lib.animation.*;
|
||||
import com.bartlomiejpluta.base.util.random.DiceRoller;
|
||||
|
||||
import com.bartlomiejpluta.base.generated.db.model.RangedWeaponModel;
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
import com.bartlomiejpluta.demo.entity.NamedEntity;
|
||||
|
||||
import com.bartlomiejpluta.demo.event.HitEvent;
|
||||
|
||||
public class RangedWeapon implements Weapon {
|
||||
private final Random random = new Random();
|
||||
private final Context context;
|
||||
private final DiceRoller dmgRoller;
|
||||
private final DiceRoller rangeRoller;
|
||||
private final BulletAnimationRunner animation;
|
||||
private final String sound;
|
||||
private final AnimationRunner punchAnimation;
|
||||
private final String punchSound;
|
||||
private final AnimationRunner missAnimation;
|
||||
private final String missSound;
|
||||
|
||||
@Getter
|
||||
private String name;
|
||||
|
||||
@Getter
|
||||
private int cooldown;
|
||||
|
||||
public RangedWeapon(@NonNull Context context, @NonNull RangedWeaponModel template) {
|
||||
this.context = context;
|
||||
this.name = template.getName();
|
||||
this.dmgRoller = DiceRoller.of(template.getDamage());
|
||||
this.rangeRoller = DiceRoller.of(template.getRange());
|
||||
this.cooldown = template.getCooldown();
|
||||
this.animation = new BulletAnimationRunner(template.getAnimation())
|
||||
.infinite()
|
||||
.offset(0, -15)
|
||||
.onHit(this::onHit)
|
||||
.onMiss(this::onMiss)
|
||||
.speed(0.25f)
|
||||
.animationSpeed(0.07f)
|
||||
.scale(0.6f);
|
||||
this.sound = template.getSound();
|
||||
this.punchAnimation = new SimpleAnimationRunner(template.getPunchAnimation());
|
||||
this.punchSound = template.getPunchSound();
|
||||
this.missAnimation = new SimpleAnimationRunner(template.getMissAnimation())
|
||||
.scale(0.4f);
|
||||
this.missSound = template.getMissSound();
|
||||
}
|
||||
|
||||
private void onHit(Movable attacker, Entity target) {
|
||||
if(target.isBlocking() && target instanceof Character) {
|
||||
var namedAttacker = (Character) attacker;
|
||||
var character = (Character) target;
|
||||
var damage = dmgRoller.roll();
|
||||
character.hit(namedAttacker, damage);
|
||||
punchAnimation.run(context, character.getLayer(), character);
|
||||
context.playSound(punchSound);
|
||||
context.fireEvent(new HitEvent(namedAttacker, character, damage));
|
||||
}
|
||||
}
|
||||
|
||||
private void onMiss(Movable attacker, Animation animation) {
|
||||
missAnimation.run(context, ((Character) attacker).getLayer(), animation.getPosition());
|
||||
context.playSound(missSound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean attack(Character attacker) {
|
||||
var direction = attacker.getFaceDirection();
|
||||
context.playSound(sound);
|
||||
animation
|
||||
.range(rangeRoller.roll())
|
||||
.direction(direction)
|
||||
.rotation(direction.xAngle - 180)
|
||||
.run(context, attacker.getLayer(), attacker);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.bartlomiejpluta.demo.world.weapon;
|
||||
|
||||
import com.bartlomiejpluta.demo.entity.Character;
|
||||
|
||||
public interface Weapon {
|
||||
String getName();
|
||||
int getCooldown();
|
||||
boolean attack(Character attacker);
|
||||
}
|
||||
55
widgets/00bd0625-b3b8-4abf-97b7-91f42bce28ec.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<demo:HUD
|
||||
xmlns:base="com.bartlomiejpluta.base.lib.gui"
|
||||
xmlns:demo="com.bartlomiejpluta.demo.gui"
|
||||
widthMode="SizeMode.RELATIVE"
|
||||
heightMode="SizeMode.RELATIVE"
|
||||
width="1f"
|
||||
height="1f">
|
||||
|
||||
<base:BorderLayout-TopLeft>
|
||||
|
||||
<demo:Bar
|
||||
ref="hp"
|
||||
strokeColor="0x111111"
|
||||
fillColor="0xFF0000"
|
||||
widthMode="SizeMode.ABSOLUTE"
|
||||
heightMode="SizeMode.ABSOLUTE"
|
||||
width="250f"
|
||||
height="20f"
|
||||
margin="5f" />
|
||||
|
||||
</base:BorderLayout-TopLeft>
|
||||
|
||||
<base:BorderLayout-BottomLeft>
|
||||
|
||||
<base:Label
|
||||
ref="log"
|
||||
font=""2261c04f-b02e-4486-b388-8a0fa41622e9""
|
||||
widthMode="SizeMode.ABSOLUTE"
|
||||
width="400f"
|
||||
height="25f"
|
||||
margin="10f"
|
||||
alignment="GUI.ALIGN_BOTTOM | GUI.ALIGN_LEFT"
|
||||
color="0xFFFFFF"
|
||||
fontSize="15f"/>
|
||||
|
||||
</base:BorderLayout-BottomLeft>
|
||||
|
||||
<base:BorderLayout-BottomRight>
|
||||
|
||||
<base:Label
|
||||
ref="debug"
|
||||
font=""2261c04f-b02e-4486-b388-8a0fa41622e9""
|
||||
widthMode="SizeMode.ABSOLUTE"
|
||||
width="200f"
|
||||
height="25f"
|
||||
margin="10f"
|
||||
alignment="GUI.ALIGN_BOTTOM | GUI.ALIGN_RIGHT"
|
||||
color="0xFFFFFF"
|
||||
fontSize="15f"/>
|
||||
|
||||
</base:BorderLayout-BottomRight>
|
||||
|
||||
</demo:HUD>
|
||||