diff --git a/data.mv.db b/data.mv.db index 22f5f10..8d20fc0 100644 Binary files a/data.mv.db and b/data.mv.db differ diff --git a/maps/551e1afc-9cda-4d9f-8817-bfd831fc0a75.dat b/maps/551e1afc-9cda-4d9f-8817-bfd831fc0a75.dat index 1aea8a0..5fc7043 100644 Binary files a/maps/551e1afc-9cda-4d9f-8817-bfd831fc0a75.dat and b/maps/551e1afc-9cda-4d9f-8817-bfd831fc0a75.dat differ diff --git a/project.bep b/project.bep index 99e07bc..ef4d5bc 100644 --- a/project.bep +++ b/project.bep @@ -20,7 +20,8 @@ Start MenuB[ $56ca6b39-f949-4212-9c23-312db25887e0(56ca6b39-f949-4212-9c23-312db25887e0.xml Game MenuBU $00bd0625-b3b8-4abf-97b7-91f42bce28ec(00bd0625-b3b8-4abf-97b7-91f42bce28ec.xmlHUDB[ $c473a91a-ff25-4e71-9bec-b35e48102aeb(c473a91a-ff25-4e71-9bec-b35e48102aeb.xml EquipmentB^ -$53ca3e54-0f8d-44fa-8281-acd9c5bba743(53ca3e54-0f8d-44fa-8281-acd9c5bba743.xml Eq Item MenuJZ +$53ca3e54-0f8d-44fa-8281-acd9c5bba743(53ca3e54-0f8d-44fa-8281-acd9c5bba743.xml Eq Item MenuB[ +$d78413cd-0dad-4b51-8dd1-54e33535fe53(d78413cd-0dad-4b51-8dd1-54e33535fe53.xml Loot MenuJZ $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 (J[ diff --git a/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java b/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java index 94361b4..8bb43a4 100644 --- a/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java +++ b/src/main/java/com/bartlomiejpluta/demo/entity/Enemy.java @@ -1,5 +1,7 @@ package com.bartlomiejpluta.demo.entity; +import DB.EnemyDropDAO; +import DB.dao; import com.bartlomiejpluta.base.api.ai.AI; import com.bartlomiejpluta.base.api.ai.NPC; import com.bartlomiejpluta.base.api.context.ContextHolder; @@ -7,20 +9,31 @@ import com.bartlomiejpluta.base.api.move.MoveEvent; import com.bartlomiejpluta.base.lib.ai.NoopAI; import com.bartlomiejpluta.base.lib.animation.AnimationRunner; import com.bartlomiejpluta.base.lib.animation.SimpleAnimationRunner; +import com.bartlomiejpluta.base.lib.db.Relop; import com.bartlomiejpluta.base.util.random.DiceRoller; import com.bartlomiejpluta.demo.ai.*; import com.bartlomiejpluta.demo.event.EnemyDiedEvent; -import com.bartlomiejpluta.demo.runner.DemoRunner; +import com.bartlomiejpluta.demo.world.item.Item; import com.bartlomiejpluta.demo.world.weapon.Ammunition; import com.bartlomiejpluta.demo.world.weapon.MeleeWeapon; import com.bartlomiejpluta.demo.world.weapon.RangedWeapon; import lombok.Getter; import lombok.NonNull; +import java.util.Random; + +import static com.bartlomiejpluta.demo.util.ListUtil.randomIntSequence; + public class Enemy extends Creature implements NPC { + public static final int MAX_LOOT = 4 * 4; + + private final Random random = new Random(); private final DB.model.EnemyModel template; private final AnimationRunner dieAnimation; + + @Getter + private final Item[] loot = new Item[MAX_LOOT]; @Getter private final String name; private AI ai = NoopAI.INSTANCE; @@ -78,8 +91,35 @@ public class Enemy extends Creature implements NPC { dieAnimation.run(context, getLayer(), this); context.playSound(A.sounds.get(template.getDieSound()).uid); context.fireEvent(new EnemyDiedEvent(this)); + + drawLoot(); } + private void drawLoot() { + var def = dao.enemy_drop.query() + .where(EnemyDropDAO.Column.ENEMY, Relop.EQ, template.getId()) + .orderBy("rand()") + .find(); + + var indexesIterator = randomIntSequence(0, loot.length).iterator(); + for (var d : def) { + if (!indexesIterator.hasNext()) { + break; + } + + var index = indexesIterator.next(); + + var split = d.getItem().split(":"); + loot[index] = switch (split[0]) { + case "melee_weapon" -> new MeleeWeapon(split[1]); + case "ranged_weapon" -> new RangedWeapon(split[1]); + case "ammo" -> new Ammunition(split[1], DiceRoller.of(d.getAmount()).roll()); + default -> throw new IllegalArgumentException("Unsupported item type"); + }; + } + } + + public Enemy followAndAttack(Creature target, int range) { var ai = new SimpleEnemyAI(this, target, range); diff --git a/src/main/java/com/bartlomiejpluta/demo/entity/Player.java b/src/main/java/com/bartlomiejpluta/demo/entity/Player.java index 677ae29..b4b1076 100644 --- a/src/main/java/com/bartlomiejpluta/demo/entity/Player.java +++ b/src/main/java/com/bartlomiejpluta/demo/entity/Player.java @@ -7,9 +7,11 @@ import com.bartlomiejpluta.demo.world.weapon.Weapon; import lombok.NonNull; import org.joml.Vector2i; +import java.util.List; + public class Player extends Creature { private final Item[] equipment = new Item[4 * 4]; - private int pickingItemToEquipmentCooldown = 0; + private int interactionCooldown = 0; public Player(@NonNull Character entity) { super(entity); @@ -18,32 +20,50 @@ public class Player extends Creature { } public void interact() { + if (interactionCooldown > 0) { + return; + } + var coords = getCoordinates().add(getFaceDirection().vector, new Vector2i()); var entities = getLayer().getEntities(); for (var i = 0; i < entities.size(); ++i) { var entity = entities.get(i); + // Use some map object which player is looking at if (entity.getCoordinates().equals(coords) && entity instanceof MapObject object) { object.interact(this); return; } - if (pickingItemToEquipmentCooldown == 0 && entity.getCoordinates().equals(getCoordinates()) && entity instanceof Item item) { - pushItemToEquipment(item); - getLayer().removeEntity(item); - pickingItemToEquipmentCooldown = PICKING_ITEM_TO_EQUIPMENT_COOLDOWN; - return; + if (entity.getCoordinates().equals(getCoordinates())) { + + // Pick some item from the ground + if (entity instanceof Item item) { + pushItemToEquipment(item); + getLayer().removeEntity(item); + interactionCooldown = INTERACTION_COOLDOWN; + return; + } + + // Search the enemy corpse + if (entity instanceof Enemy enemy && !enemy.isAlive()) { + runner.openLootWindow(enemy); + interactionCooldown = INTERACTION_COOLDOWN; + return; + } } } } - private void pushItemToEquipment(@NonNull Item item) { + public boolean pushItemToEquipment(@NonNull Item item) { for (int i = 0; i < equipment.length; ++i) { if (equipment[i] == null) { equipment[i] = item; - return; + return true; } } + + return false; } public Item getEquipmentItem(int index) { @@ -124,10 +144,10 @@ public class Player extends Creature { public void update(float dt) { super.update(dt); - if (pickingItemToEquipmentCooldown > 0) { - pickingItemToEquipmentCooldown = (int) Math.max(0, pickingItemToEquipmentCooldown - dt * 1000); + if (interactionCooldown > 0) { + interactionCooldown = (int) Math.max(0, interactionCooldown - dt * 1000); } } - private static final int PICKING_ITEM_TO_EQUIPMENT_COOLDOWN = 300; + private static final int INTERACTION_COOLDOWN = 300; } \ No newline at end of file diff --git a/src/main/java/com/bartlomiejpluta/demo/gui/EquipmentWindow.java b/src/main/java/com/bartlomiejpluta/demo/gui/EquipmentWindow.java index 4cf49f9..b3d84f0 100644 --- a/src/main/java/com/bartlomiejpluta/demo/gui/EquipmentWindow.java +++ b/src/main/java/com/bartlomiejpluta/demo/gui/EquipmentWindow.java @@ -55,8 +55,8 @@ public class EquipmentWindow extends DecoratedWindow { } @Override - public void onOpen(WindowManager manager) { - super.onOpen(manager); + public void onOpen(WindowManager manager, Object... args) { + super.onOpen(manager, args); cancelBtn.setAction(manager::close); eqGrid.setOnSelect(this::updateItemDetails); @@ -72,14 +72,14 @@ public class EquipmentWindow extends DecoratedWindow { for (var child : eqGrid.getChildren()) { var slot = (ItemIconView) child; slot.setItem(player.getEquipmentItem(i++)); - slot.setAction(handleItem(slot)); + slot.setAction(handleClick(slot)); } weapon.setItem(player.getWeapon()); ammo.setItem(player.getAmmunition()); } - private Consumer handleItem(ItemIconView slot) { + private Consumer handleClick(ItemIconView slot) { return item -> { useBtn.setText(getButtonTitle(item)); eqItemMenu.select(0); diff --git a/src/main/java/com/bartlomiejpluta/demo/gui/LootWindow.java b/src/main/java/com/bartlomiejpluta/demo/gui/LootWindow.java new file mode 100644 index 0000000..c3a5d23 --- /dev/null +++ b/src/main/java/com/bartlomiejpluta/demo/gui/LootWindow.java @@ -0,0 +1,83 @@ +package com.bartlomiejpluta.demo.gui; + +import com.bartlomiejpluta.base.api.context.Context; +import com.bartlomiejpluta.base.api.gui.*; +import com.bartlomiejpluta.base.lib.gui.VGridOptionChoice; +import com.bartlomiejpluta.demo.entity.Enemy; +import com.bartlomiejpluta.demo.entity.Player; +import com.bartlomiejpluta.demo.runner.DemoRunner; +import com.bartlomiejpluta.demo.world.item.Item; + +import java.util.Map; + +public class LootWindow extends DecoratedWindow implements Inflatable { + private final Player player; + + @Ref("loot") + private VGridOptionChoice lootMenu; + + private Item[] loot; + + private ItemIconView[] slots = new ItemIconView[Enemy.MAX_LOOT]; + + public LootWindow(Context context, GUI gui, Map refs) { + super(context, gui, refs); + this.player = ((DemoRunner) context.getGameRunner()).getPlayer(); + } + + @Override + public void onInflate() { + for (int i = 0; i < Enemy.MAX_LOOT; ++i) { + var itemView = new ItemIconView(context, gui, Map.of()); + itemView.setMargin(5f); + lootMenu.add(itemView); + slots[i] = itemView; + itemView.setAction(this::handleClick); + } + } + + @Override + public void onOpen(WindowManager manager, Object[] args) { + super.onOpen(manager, args); + + var creature = (Enemy) args[0]; + this.loot = creature.getLoot(); + updateSlots(); + } + + @Override + public void onClose(WindowManager manager) { + super.onClose(manager); + + clearSlots(); + this.loot = null; + } + + private void handleClick(Item item) { + if (item == null) { + return; + } + + if (player.pushItemToEquipment(item)) { + for (int i = 0; i < Enemy.MAX_LOOT; i++) { + if (loot[i] == item) { + loot[i] = null; + slots[i].setItem(null); + break; + } + } + } + } + + private void updateSlots() { + for (int i = 0; i < Enemy.MAX_LOOT; ++i) { + slots[i].setItem(loot[i]); + } + } + + private void clearSlots() { + for (var slot : slots) { + slot.setItem(null); + } + } +} diff --git a/src/main/java/com/bartlomiejpluta/demo/menu/MenuManager.java b/src/main/java/com/bartlomiejpluta/demo/menu/MenuManager.java index 8698e9e..6382758 100644 --- a/src/main/java/com/bartlomiejpluta/demo/menu/MenuManager.java +++ b/src/main/java/com/bartlomiejpluta/demo/menu/MenuManager.java @@ -1,5 +1,6 @@ package com.bartlomiejpluta.demo.menu; +import A.widgets; import com.bartlomiejpluta.base.api.context.Context; import com.bartlomiejpluta.base.api.gui.DisplayMode; import com.bartlomiejpluta.base.api.gui.GUI; @@ -8,8 +9,10 @@ import com.bartlomiejpluta.base.api.gui.WindowManager; import com.bartlomiejpluta.base.api.input.Key; import com.bartlomiejpluta.base.api.input.KeyAction; import com.bartlomiejpluta.base.api.input.KeyEvent; +import com.bartlomiejpluta.demo.entity.Enemy; import com.bartlomiejpluta.demo.gui.EquipmentWindow; import com.bartlomiejpluta.demo.gui.GameMenuWindow; +import com.bartlomiejpluta.demo.gui.LootWindow; import com.bartlomiejpluta.demo.gui.StartMenuWindow; import com.bartlomiejpluta.demo.runner.DemoRunner; import lombok.NonNull; @@ -25,7 +28,7 @@ public class MenuManager { private final StartMenuWindow startMenu; private final GameMenuWindow gameMenu; private final EquipmentWindow equipment; - + private final LootWindow loot; private final Consumer gameMenuHandler = this::handleGameMenuKeyEvent; public MenuManager(@NonNull DemoRunner runner, @NonNull Context context) { @@ -36,16 +39,17 @@ public class MenuManager { this.gui.setRoot(this.manager); - this.startMenu = (StartMenuWindow) gui.inflateWindow(A.widgets.start_menu.uid); + this.startMenu = gui.inflateWindow(A.widgets.start_menu.uid, StartMenuWindow.class); this.startMenu.getNewGameBtn().setAction(runner::newGame); this.startMenu.getExitBtn().setAction(runner::exit); - this.gameMenu = (GameMenuWindow) gui.inflateWindow(A.widgets.game_menu.uid); + this.gameMenu = gui.inflateWindow(A.widgets.game_menu.uid, GameMenuWindow.class); this.gameMenu.getResumeGameBtn().setAction(this::resumeGame); this.gameMenu.getStartMenuBtn().setAction(runner::returnToStartMenu); this.gameMenu.getExitBtn().setAction(runner::exit); - this.equipment = (EquipmentWindow) gui.inflateWindow(A.widgets.equipment.uid); + this.equipment = gui.inflateWindow(A.widgets.equipment.uid, EquipmentWindow.class); + this.loot = gui.inflateWindow(widgets.loot_menu.uid, LootWindow.class); } private void handleGameMenuKeyEvent(KeyEvent event) { @@ -97,6 +101,12 @@ public class MenuManager { manager.setDisplayMode(DisplayMode.DISPLAY_TOP); } + public void openLootWindow(@NonNull Enemy enemy) { + manager.closeAll(); + + manager.open(loot, enemy); + } + public void closeAll() { manager.closeAll(); } diff --git a/src/main/java/com/bartlomiejpluta/demo/runner/DemoRunner.java b/src/main/java/com/bartlomiejpluta/demo/runner/DemoRunner.java index 862a8ec..acf15d5 100644 --- a/src/main/java/com/bartlomiejpluta/demo/runner/DemoRunner.java +++ b/src/main/java/com/bartlomiejpluta/demo/runner/DemoRunner.java @@ -5,6 +5,7 @@ import com.bartlomiejpluta.base.api.gui.GUI; import com.bartlomiejpluta.base.api.runner.GameRunner; import com.bartlomiejpluta.base.api.screen.Screen; import com.bartlomiejpluta.base.util.profiler.FPSProfiler; +import com.bartlomiejpluta.demo.entity.Enemy; import com.bartlomiejpluta.demo.entity.Player; import com.bartlomiejpluta.demo.menu.MenuManager; import com.bartlomiejpluta.demo.world.weapon.RangedWeapon; @@ -96,6 +97,10 @@ public class DemoRunner implements GameRunner { menu.showStartMenu(); } + public void openLootWindow(Enemy enemy) { + menu.openLootWindow(enemy); + } + public void exit() { context.close(); } diff --git a/src/main/java/com/bartlomiejpluta/demo/util/ListUtil.java b/src/main/java/com/bartlomiejpluta/demo/util/ListUtil.java new file mode 100644 index 0000000..28633f3 --- /dev/null +++ b/src/main/java/com/bartlomiejpluta/demo/util/ListUtil.java @@ -0,0 +1,21 @@ +package com.bartlomiejpluta.demo.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.stream.IntStream; + +public class ListUtil { + private static final Random random = new Random(); + + public static T sample(List list) { + return list.get(random.nextInt(list.size())); + } + + public static List randomIntSequence(int startInclusive, int endExclusive) { + var ints = new ArrayList<>(IntStream.range(startInclusive, endExclusive).boxed().toList()); + Collections.shuffle(ints); + return ints; + } +} diff --git a/widgets/d78413cd-0dad-4b51-8dd1-54e33535fe53.xml b/widgets/d78413cd-0dad-4b51-8dd1-54e33535fe53.xml new file mode 100644 index 0000000..887729f --- /dev/null +++ b/widgets/d78413cd-0dad-4b51-8dd1-54e33535fe53.xml @@ -0,0 +1,27 @@ + + + + + + Loot + + + + + + + \ No newline at end of file