diff --git a/README.md b/README.md
index e00398f..e7dea83 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,39 @@
# Alone
-Single-player ASCII roguelike/roguelite focused on surviving, alone, on an island inhabited by animals.
+[![Latest Github release](https://img.shields.io/github/release/fabioticconi/alone-rl.svg)](https://github.com/fabioticconi/alone-rl/releases/latest)
+
+Single-player ASCII roguelike focused on surviving, alone, on an island inhabited by animals.
The main inspiration is from [Unreal World](http://unrealworld.fi) and [Wayward](http://www.waywardgame.com),
with a much simpler gameplay.
It's a real-time game but it defaults to a turn-based modality where the world only advances during player actions,
for as long as the player action runs. Pure real-time gameplay can be toggled.
-**NB: this is not even in alpha state.** Lurk freely if you like the concept, but *know* this is not playable,
-by far.
-Keep an eye on the [release](https://github.com/fabioticconi/alone-the-roguelite/releases) area, for the future.
+**NB: this is not ready yet.** Keep an eye on the [releases page](https://github.com/fabioticconi/alone-the-roguelite/releases)
+for a stable release.
## Controls
* **`directional arrows` to move** (hold two together for diagonal movement, eg UP+RIGHT to go north-east)
- Move into creatures to attack them (message/combat log coming soon), trees to cut them, boulders to crush them.
- For the last two you need proper tools (a cutting weapon for cutting three, not craftable yet, and a blunt weapon
- for crushing boulders: you can use a stone for that)
-
-* **`g` to get the first item** on the ground you are currently positioned on (stones, sticks, corpses, tree trunks..
- there's no inventory limit for now).
+ Move into creatures to attack them, trees to cut them, boulders to crush them.
+ For the last two you need proper tools (a cutting weapon for cutting three and a blunt weapon
+ for crushing boulders).
+* **`g` to get an item**. You must move onto it first. There is no inventory limit.
+
* **`d` to open the Drop screen**. You will be able to choose which item to drop.
* **`e` to open the Eat screen**. You can only eat corpses that you have taken from the ground, for now.
* **`w` to wear or wield an object** via the Equip screen. Stones and branches will do nicely for now.
-
+
* **`l` to open the Look screen**. Move around to choose a target (if you have Line of Sight) and press **`t`** to throw
an equipped weapon in that direction.
+* **`c` to open the Craft screen**. A list of recipes is loaded anew from a yaml file and displayed. See
+ [Crafting](https://github.com/fabioticconi/alone-rl#crafting) for details.
+
There are also some special commands:
* **`Ctrl+SPACE`** to toggle real-time/turn-based behaviour
@@ -38,21 +41,19 @@ There are also some special commands:
* **`SPACE`** to pause/unpause if you are on real-time mode; if you are on turn-based mode, keep `SPACE`
pressed to temporarily run the game in real-time (needed, for example, to recover stamina when you finish it,
or regenerate health).
-
+
* **`F1`** removes the speed delay of the player. Useful to test the game without having to suffer the movement delays.
Will be removed in the final version.
-
+
* **`F2`** restores the correct player speed.
Will be removed in the final version.
-
+
## Screenshots
This is how the game looks when run (**very** preliminary GUI):
![](screenshots/gameplay.gif)
-A few wolves are chasing rabbits, while pumas manage to bring a buffalo down.
-
### Map
![](screenshots/orig_map.png)
@@ -74,6 +75,8 @@ Later the terrain will be configurable by the user.
You can also see some rivers (still in-progress, not in the game yet) flowing from high to low places.
+Note: this is only temporary. In the final game the terrain will be randomised.
+
## Features
### Field of view
@@ -85,14 +88,14 @@ Pathfinding is both precise and efficient thanks to an AStar implementation that
to plan a course to a target position.
What this means for the end user is that the game doesn't cheat. It doesn't magically make creatures see you
-if they shouldn't. The other creatures can only do what *you* also can.
+if they shouldn't. The other creatures can only do what *you* can also do.
-### Simple Ecology Simulation
+### Simple Ecology
-Creatures don't "pop" or "spawn", they don't just appear when needed but they keep going even when the
+Creatures don't "pop" or "spawn", they don't just appear when needed but they *exist*; and they keep going even when the
player is not looking.
-This is the main difference between Alone and most roguelikes/roguelites. It makes it a simulation game, to an extent.
+This is the main difference between `AloneRL` and most roguelikes/roguelites. It makes it a simulation game, to an extent.
Different creatures have different set of behaviours:
@@ -116,5 +119,8 @@ Escape predators, steal their carcasses if you can, or kill any walking thing an
### Crafting
-**Not implemented yet**. It will cover the basic primitive technology of a Neolithic hunter, eg stone knives, spears and axes,
+It will cover the basic primitive technology of a Neolithic hunter, eg stone knives, spears and axes,
simple bark protection, a shelter, maybe rudimentary pit traps and extraction of parts from dead animals.
+
+All currently implemented recipes can be seen (and modified, added or removed) in the file
+[crafting.yml](data/crafting.yml).
diff --git a/alone-rl.iml b/alone-rl.iml
index 21b5596..a202107 100644
--- a/alone-rl.iml
+++ b/alone-rl.iml
@@ -20,10 +20,11 @@
-
+
-
-
+
+
+
\ No newline at end of file
diff --git a/data/crafting.yml b/data/crafting.yml
index 3aa4910..df53828 100644
--- a/data/crafting.yml
+++ b/data/crafting.yml
@@ -1,41 +1,20 @@
-branch:
- name: a branch
- #source:
- # external: tree
+sharp-stone:
+ sources: stone
+ tools: stone
-tree-trunk:
- name: a tree trunk
- #source:
- # external: tree
- tool: axe
+small-sharp-stone:
+ sources: sharp-stone
+ tools: stone
-stone:
- name: a stone
- #source:
- # external: boulder
- tool: stone
- n: 3
+stone-hammer:
+ sources: [stone, branch, vine]
+ tools: stone
-sharp-stone:
- name: a sharp stone
- source: stone
- tool: stone
+stone-axe:
+ sources: [sharp-stone, branch, vine]
+ tools: stone
-#small-sharp-stone:
-# name: a small, sharp stone
-# source: [sharp-stone]
-# tool: stone
-# n: 2
-#
-#stone-hammer:
-# name: a stone hammer
-# source: [stone, branch]
-#
-#stone-axe:
-# name: a stone axe
-# source: [sharp-stone, branch]
-#
-#stone-spear:
-# name: a stone spear
-# source: [small-sharp-stone, tree-trunk]
+stone-spear:
+ sources: [small-sharp-stone, trunk, vine]
+ tools: stone
diff --git a/data/items.yml b/data/items.yml
index f3ad73a..44e4576 100644
--- a/data/items.yml
+++ b/data/items.yml
@@ -1,12 +1,140 @@
stone:
- name: a stone
+ name: a round stone
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: BLUNT
+ damage: 2
+ sprite:
+ c: 7
+ col:
+ red: 88
+ green: 88
+ blue: 88
branch:
- name: a branch
+ name: a sturdy branch
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: BLUNT
+ damage: 1
+ sprite:
+ c: '/'
+ col:
+ red: 141
+ green: 104
+ blue: 21
+
+vine:
+ name: a thin, flexible branch
+ sprite:
+ c: 239
+ col:
+ red: 36
+ green: 122
+ blue: 7
trunk:
- name: a tree trunk
+ name: a fallen tree
+ sprite:
+ c: 22
+ col:
+ red: 141
+ green: 104
+ blue: 21
boulder:
- name: a boulder
+ name: a big boulder
+ sprite:
+ c: '#'
+ col:
+ red: 88
+ green: 88
+ blue: 88
+ shadowView: true
+ obstacle: {}
+ crushable: {}
+
+tree:
+ name: a mature tree
+ sprite:
+ c: T
+ col:
+ red: 0
+ green: 205
+ blue: 113
+ shadowView: true
+ obstacle: {}
+ cuttable: {}
+
+sharp-stone:
+ name: a sharp stone
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: SLASH
+ damage: 2
+ sprite:
+ c: 4
+ col:
+ red: 88
+ green: 88
+ blue: 88
+
+small-sharp-stone:
+ name: a small, sharp stone
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: POINT
+ damage: 2
+ sprite:
+ c: 17
+ col:
+ red: 88
+ green: 88
+ blue: 88
+
+stone-hammer:
+ name: a stone hammer
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: BLUNT
+ damage: 4
+ sprite:
+ c: 209
+ col:
+ red: 141
+ green: 104
+ blue: 21
+
+stone-axe:
+ name: a stone axe
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: SLASH
+ damage: 4
+ sprite:
+ c: 184
+ col:
+ red: 141
+ green: 104
+ blue: 21
+
+stone-spear:
+ name: a stone spear
+ wearable:
+ where: HANDS
+ weapon:
+ damageType: POINT
+ damage: 4
+ sprite:
+ c: 244
+ col:
+ red: 141
+ green: 104
+ blue: 21
diff --git a/pom.xml b/pom.xml
index a75def4..bfc9bf8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
com.github.fabioticconi
alone-rl
- 0.1.1
+ 0.2.0
jar
AloneRL
@@ -74,14 +74,31 @@
4.12
test
+
com.fasterxml.jackson.core
jackson-databind
- 2.3.1
+
+
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+
+
+
+ com.fasterxml.jackson
+ jackson-bom
+ 2.9.0
+ import
+ pom
+
+
+
+
diff --git a/screenshots/gameplay.gif b/screenshots/gameplay.gif
index e58f5df..1e0b896 100644
Binary files a/screenshots/gameplay.gif and b/screenshots/gameplay.gif differ
diff --git a/src/main/java/com/github/fabioticconi/alone/Main.java b/src/main/java/com/github/fabioticconi/alone/Main.java
index f835cb6..426d95c 100644
--- a/src/main/java/com/github/fabioticconi/alone/Main.java
+++ b/src/main/java/com/github/fabioticconi/alone/Main.java
@@ -25,6 +25,10 @@
import com.artemis.link.EntityLinkManager;
import com.artemis.managers.PlayerManager;
import com.artemis.utils.BitVector;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import com.github.fabioticconi.alone.behaviours.*;
import com.github.fabioticconi.alone.constants.Options;
import com.github.fabioticconi.alone.screens.*;
@@ -68,6 +72,10 @@ public Main() throws IOException
final Properties properties = new Properties();
properties.load(this.getClass().getResourceAsStream("/project.properties"));
+ final YAMLFactory factory = new YAMLFactory();
+ final ObjectMapper mapper = new ObjectMapper(factory).registerModule(new ParameterNamesModule())
+ .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+
final WorldConfiguration config;
config = new WorldConfiguration();
// first thing to be loaded
@@ -75,6 +83,7 @@ public Main() throws IOException
// POJO
config.register(new Random());
config.register(properties);
+ config.register(mapper);
// passive systems, one-timers, managers etc
config.setSystem(EntityLinkManager.class);
config.setSystem(BootstrapSystem.class);
@@ -139,12 +148,11 @@ public static void main(final String[] args) throws IOException
}
/**
- * In real-time mode, space means pause/unpause the game, while
- * in turn-based mode, space means unpause the game until SPACE is released.
+ * Makes sure that the game is paused
*/
public static void pause()
{
- Main.paused = Main.realtime && !Main.paused;
+ Main.paused = true;
}
public void loop()
diff --git a/src/main/java/com/github/fabioticconi/alone/components/Armour.java b/src/main/java/com/github/fabioticconi/alone/components/Armour.java
index 6ab8334..64850d4 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/Armour.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Armour.java
@@ -29,6 +29,8 @@
*/
public class Armour extends Component
{
+ // TODO we can improve this by using a primitive float array and WeaponType ordinals as indeces.
+ // which is what EnumMap does internally. We'd avoid autoboxing.
public final EnumMap defences;
public Armour()
diff --git a/src/main/java/com/github/fabioticconi/alone/components/Tree.java b/src/main/java/com/github/fabioticconi/alone/components/Cuttable.java
similarity index 95%
rename from src/main/java/com/github/fabioticconi/alone/components/Tree.java
rename to src/main/java/com/github/fabioticconi/alone/components/Cuttable.java
index 34529cc..2dce47b 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/Tree.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Cuttable.java
@@ -24,6 +24,6 @@
* Author: Fabio Ticconi
* Date: 07/10/17
*/
-public class Tree extends Component
+public class Cuttable extends Component
{
}
diff --git a/src/main/java/com/github/fabioticconi/alone/components/Name.java b/src/main/java/com/github/fabioticconi/alone/components/Name.java
index f21c1f2..d61d50a 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/Name.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Name.java
@@ -27,14 +27,17 @@
public class Name extends Component
{
public final String name;
+ public final String tag;
public Name()
{
this.name = "";
+ this.tag = "";
}
- public Name(final String name)
+ public Name(final String name, final String tag)
{
this.name = name;
+ this.tag = tag;
}
}
diff --git a/src/main/java/com/github/fabioticconi/alone/components/LightBlocker.java b/src/main/java/com/github/fabioticconi/alone/components/Obstacle.java
similarity index 86%
rename from src/main/java/com/github/fabioticconi/alone/components/LightBlocker.java
rename to src/main/java/com/github/fabioticconi/alone/components/Obstacle.java
index a539be7..be1736a 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/LightBlocker.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Obstacle.java
@@ -24,6 +24,7 @@
* Author: Fabio Ticconi
* Date: 10/09/17
*/
-public class LightBlocker extends Component
+public class Obstacle extends Component
{
+ // TODO eventually, we might need a parameter to say: "this only blocks light, not movement"
}
diff --git a/src/main/java/com/github/fabioticconi/alone/components/Path.java b/src/main/java/com/github/fabioticconi/alone/components/Path.java
index a3e7d6b..706e14a 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/Path.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Path.java
@@ -31,6 +31,7 @@ public class Path extends Component
{
public float cooldown;
public List steps;
+ public int i;
public Path()
{
@@ -44,6 +45,7 @@ public Path(final float cooldown, final List steps)
public void set(final float cooldown, final List steps)
{
+ this.i = 0;
this.cooldown = cooldown;
this.steps = steps;
}
diff --git a/src/main/java/com/github/fabioticconi/alone/components/Sprite.java b/src/main/java/com/github/fabioticconi/alone/components/Sprite.java
index 7db6f2d..c1743f4 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/Sprite.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Sprite.java
@@ -45,6 +45,11 @@ public Sprite(final char c, final Color col)
this(c, col, false);
}
+ public Sprite(final char c, final String hexCol)
+ {
+ this(c, Color.decode(hexCol), false);
+ }
+
public Sprite(final char c, final Color col, final boolean shadowView)
{
this.c = c;
diff --git a/src/main/java/com/github/fabioticconi/alone/components/Wearable.java b/src/main/java/com/github/fabioticconi/alone/components/Wearable.java
index e9292e0..55bd1e5 100644
--- a/src/main/java/com/github/fabioticconi/alone/components/Wearable.java
+++ b/src/main/java/com/github/fabioticconi/alone/components/Wearable.java
@@ -19,6 +19,7 @@
package com.github.fabioticconi.alone.components;
import com.artemis.Component;
+import com.github.fabioticconi.alone.constants.BodyPart;
/**
* Author: Fabio Ticconi
@@ -26,4 +27,22 @@
*/
public class Wearable extends Component
{
+ public BodyPart where;
+
+ public Wearable()
+ {
+
+ }
+
+ public Wearable(final BodyPart where)
+ {
+ this.where = where;
+ }
+
+ public Wearable set(final BodyPart where)
+ {
+ this.where = where;
+
+ return this;
+ }
}
diff --git a/src/main/java/com/github/fabioticconi/alone/constants/BodyPart.java b/src/main/java/com/github/fabioticconi/alone/constants/BodyPart.java
new file mode 100644
index 0000000..6bb7ed3
--- /dev/null
+++ b/src/main/java/com/github/fabioticconi/alone/constants/BodyPart.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 Fabio Ticconi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.github.fabioticconi.alone.constants;
+
+/**
+ * Author: Fabio Ticconi
+ * Date: 12/11/17
+ */
+public enum BodyPart
+{
+ HEAD,
+ BODY,
+ HANDS
+}
diff --git a/src/main/java/com/github/fabioticconi/alone/messages/CraftMsg.java b/src/main/java/com/github/fabioticconi/alone/messages/CraftMsg.java
new file mode 100644
index 0000000..33b7413
--- /dev/null
+++ b/src/main/java/com/github/fabioticconi/alone/messages/CraftMsg.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 Fabio Ticconi
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package com.github.fabioticconi.alone.messages;
+
+/**
+ * Author: Fabio Ticconi
+ * Date: 14/11/17
+ */
+public class CraftMsg extends AbstractMessage
+{
+ @Override
+ public String format()
+ {
+ return String.format("%s successfully %s %s", actor, thirdPerson ? "crafts" : "craft", target.toLowerCase());
+ }
+}
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java
index a5b7228..f5854d5 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/AbstractScreen.java
@@ -21,7 +21,7 @@
import asciiPanel.AsciiPanel;
import com.artemis.managers.PlayerManager;
import com.artemis.utils.BitVector;
-import com.github.fabioticconi.alone.components.Inventory;
+import com.github.fabioticconi.alone.utils.Util;
import net.mostlyoriginal.api.system.core.PassiveSystem;
import java.util.Collections;
@@ -36,43 +36,9 @@
*/
public abstract class AbstractScreen extends PassiveSystem implements Screen
{
- PlayerManager pManager;
+ public static final Letter[] ALL = Letter.values();
- public enum Letter
- {
- a,
- b,
- c,
- d,
- e,
- f,
- g,
- h,
- i,
- j,
- k,
- l,
- m,
- n,
- o,
- p,
- q,
- r,
- s,
- t,
- u,
- v,
- w,
- x,
- y,
- z;
-
- @Override
- public String toString()
- {
- return name() + ")";
- }
- }
+ PlayerManager pManager;
public abstract String header();
@@ -85,14 +51,18 @@ void drawHeader(final AsciiPanel terminal)
void drawList(final AsciiPanel terminal, final List list)
{
+ if (list.isEmpty())
+ return;
+
final int maxSize = AbstractScreen.Letter.values().length;
- final int size = Math.min(maxSize, list.size());
+ final int size = Math.min(maxSize, list.size());
+ final int space = Util.clamp(maxSize / size, 1, 4);
- for (int i = 0, starty = terminal.getHeightInCharacters() / 2 - size / 2; i < size; i++)
+ for (int i = 0, starty = terminal.getHeightInCharacters() / 2 - (size * space / 2); i < size; i++)
{
- final String entry = list.get(i);
+ final String entry = ALL[i] + " " + list.get(i);
- terminal.writeCenter(entry, starty + (size < maxSize / 2 ? i * 2 : i));
+ terminal.writeCenter(entry, starty + i * space);
}
}
@@ -115,4 +85,40 @@ int getTargetIndex(final BitVector keys)
return -1;
}
+
+ public enum Letter
+ {
+ a,
+ b,
+ c,
+ d,
+ e,
+ f,
+ g,
+ h,
+ i,
+ j,
+ k,
+ l,
+ m,
+ n,
+ o,
+ p,
+ q,
+ r,
+ s,
+ t,
+ u,
+ v,
+ w,
+ x,
+ y,
+ z;
+
+ @Override
+ public String toString()
+ {
+ return name() + ")";
+ }
+ }
}
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/CraftItemScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/CraftItemScreen.java
index 60f2024..fc813e1 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/CraftItemScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/CraftItemScreen.java
@@ -19,10 +19,17 @@
package com.github.fabioticconi.alone.screens;
import asciiPanel.AsciiPanel;
+import com.artemis.ComponentMapper;
import com.artemis.utils.BitVector;
+import com.github.fabioticconi.alone.components.Inventory;
+import com.github.fabioticconi.alone.messages.CannotMsg;
+import com.github.fabioticconi.alone.messages.CraftMsg;
+import com.github.fabioticconi.alone.systems.CraftSystem;
+import com.github.fabioticconi.alone.systems.MessageSystem;
import com.github.fabioticconi.alone.systems.ScreenSystem;
import java.awt.event.KeyEvent;
+import java.util.Arrays;
/**
* Author: Fabio Ticconi
@@ -30,20 +37,42 @@
*/
public class CraftItemScreen extends AbstractScreen
{
+ ComponentMapper mInventory;
+
ScreenSystem screen;
+ CraftSystem sCraft;
+ MessageSystem msg;
+
CraftScreen craftScreen;
@Override
public String header()
{
- return "Crafting item:";
+ return "Craft " + craftScreen.craftItem.tag + "?";
}
@Override
public float handleKeys(final BitVector keys)
{
+ final int playerId = pManager.getEntitiesOfPlayer("player").get(0).getId();
+
if (keys.get(KeyEvent.VK_ESCAPE))
+ screen.select(CraftScreen.class);
+ else if (keys.get(KeyEvent.VK_ENTER))
+ {
+ System.out.println(craftScreen.craftItem);
+ final int id = sCraft.craftItem(playerId, craftScreen.craftItem);
+ if (id >= 0)
+ {
+ final Inventory inv = mInventory.get(playerId);
+ inv.items.add(id);
+ msg.send(playerId, id, new CraftMsg());
+ }
+ else
+ msg.send(playerId, id, new CannotMsg("craft"));
+
screen.select(PlayScreen.class);
+ }
keys.clear();
@@ -53,10 +82,20 @@ public float handleKeys(final BitVector keys)
@Override
public void display(final AsciiPanel terminal)
{
+ terminal.clear();
+
drawHeader(terminal);
- terminal.writeCenter(craftScreen.craftItem, terminal.getHeightInCharacters()/2);
+ int height = terminal.getHeightInCharacters() / 3;
+
+ terminal.writeCenter("Consumes:", height);
+ terminal.writeCenter(Arrays.toString(craftScreen.craftItem.sources), height + 2);
+
+ height += 8;
+
+ terminal.writeCenter("Tools needed:", height);
+ terminal.writeCenter(Arrays.toString(craftScreen.craftItem.tools), height + 2);
- // TODO
+ terminal.writeCenter("[ type ENTER to confirm ]", terminal.getHeightInCharacters() - 2);
}
}
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/CraftScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/CraftScreen.java
index 8140563..267b119 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/CraftScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/CraftScreen.java
@@ -31,6 +31,7 @@
import com.github.fabioticconi.alone.systems.ScreenSystem;
import java.awt.event.KeyEvent;
+import java.util.List;
/**
* Author: Fabio Ticconi
@@ -49,19 +50,26 @@ public class CraftScreen extends AbstractScreen
PlayerManager pManager;
- String craftItem = "Test";
+ List recipeNames;
+ CraftSystem.CraftItem craftItem;
@Override
public float handleKeys(final BitVector keys)
{
if (keys.get(KeyEvent.VK_ESCAPE))
+ {
+ recipeNames = null;
screen.select(PlayScreen.class);
+ }
else
{
final int pos = getTargetIndex(keys);
- if (pos >= 0)
+ if (pos >= 0 && pos < recipeNames.size())
+ {
+ craftItem = sCraft.getRecipes().get(recipeNames.get(pos));
screen.select(CraftItemScreen.class);
+ }
}
keys.clear();
@@ -72,9 +80,14 @@ public float handleKeys(final BitVector keys)
@Override
public void display(final AsciiPanel terminal)
{
+ if (recipeNames == null)
+ recipeNames = sCraft.getRecipeNames();
+
+ terminal.clear(' ');
+
drawHeader(terminal);
- drawList(terminal, sCraft.getRecipeNames());
+ drawList(terminal, recipeNames);
}
@Override
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/DropScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/DropScreen.java
index 64bc51b..2ec4d09 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/DropScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/DropScreen.java
@@ -36,8 +36,6 @@ public float handleKeys(final BitVector keys)
if (targetId < 0)
return super.handleKeys(keys);
- screen.select(PlayScreen.class);
-
return sAction.act(sItems.drop(playerId, targetId));
}
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/EquipScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/EquipScreen.java
index 4a5d665..398a724 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/EquipScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/EquipScreen.java
@@ -40,8 +40,6 @@ public float handleKeys(final BitVector keys)
if (targetId < 0)
return super.handleKeys(keys);
- screen.select(PlayScreen.class);
-
return sAction.act(sItems.equip(playerId, targetId));
}
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/InventoryScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/InventoryScreen.java
index 364c4f6..0791542 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/InventoryScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/InventoryScreen.java
@@ -31,10 +31,6 @@
import java.awt.event.KeyEvent;
import java.util.ArrayList;
-import java.util.Collections;
-
-import static java.awt.event.KeyEvent.VK_A;
-import static java.awt.event.KeyEvent.VK_Z;
/**
* Author: Fabio Ticconi
@@ -81,10 +77,8 @@ public void display(final AsciiPanel terminal)
if (!canDraw(itemId))
continue;
- elements.add(String.format("%s %s %s",
- Letter.values()[i],
- mName.get(itemId).name.toLowerCase(),
- mEquip.has(itemId) ? " [WORN]" : ""));
+ elements
+ .add(String.format("%s %s", mName.get(itemId).name.toLowerCase(), mEquip.has(itemId) ? " [WORN]" : ""));
}
drawList(terminal, elements);
@@ -101,11 +95,21 @@ int getItem(final BitVector keys)
if (pos < 0)
return -1;
- final int playerId = pManager.getEntitiesOfPlayer("player").get(0).getId();
- final Inventory i = mInventory.get(playerId);
+ final int playerId = pManager.getEntitiesOfPlayer("player").get(0).getId();
+ final Inventory inv = mInventory.get(playerId);
- if (pos < i.items.size())
- return i.items.get(pos);
+ for (int i = 0, j = 0, size = inv.items.size(); i < size; i++)
+ {
+ final int itemId = inv.items.get(i);
+
+ if (!canDraw(itemId))
+ continue;
+
+ if (j == pos)
+ return itemId;
+
+ j++;
+ }
return -1;
}
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java
index 85af8dd..198d9d3 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/LookScreen.java
@@ -24,6 +24,7 @@
import com.github.fabioticconi.alone.components.Position;
import com.github.fabioticconi.alone.components.Target;
import com.github.fabioticconi.alone.components.attributes.Sight;
+import com.github.fabioticconi.alone.systems.ItemSystem;
import com.github.fabioticconi.alone.systems.MapSystem;
import com.github.fabioticconi.alone.systems.ThrowSystem;
import com.github.fabioticconi.alone.utils.Coords;
@@ -47,6 +48,7 @@ public class LookScreen extends PlayScreen
ThrowSystem sThrow;
MapSystem map;
+ ItemSystem sItem;
@Override
public float handleKeys(final BitVector keys)
diff --git a/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java
index cd00bdf..06a486f 100644
--- a/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java
+++ b/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java
@@ -28,9 +28,11 @@
import com.github.fabioticconi.alone.components.attributes.Sight;
import com.github.fabioticconi.alone.constants.Cell;
import com.github.fabioticconi.alone.constants.Side;
+import com.github.fabioticconi.alone.constants.WeaponType;
import com.github.fabioticconi.alone.map.SingleGrid;
import com.github.fabioticconi.alone.messages.AbstractMessage;
import com.github.fabioticconi.alone.messages.CannotMsg;
+import com.github.fabioticconi.alone.messages.Msg;
import com.github.fabioticconi.alone.systems.*;
import com.github.fabioticconi.alone.utils.LongBag;
import org.slf4j.Logger;
@@ -38,6 +40,7 @@
import java.awt.*;
import java.awt.event.KeyEvent;
+import java.util.EnumSet;
import java.util.Properties;
import java.util.Stack;
@@ -177,7 +180,7 @@ else if (keys.get(KeyEvent.VK_E))
return 0f;
}
- else if (keys.get(KeyEvent.VK_L) || keys.get(KeyEvent.VK_T))
+ else if (keys.get(KeyEvent.VK_L))
{
keys.clear();
@@ -187,6 +190,22 @@ else if (keys.get(KeyEvent.VK_L) || keys.get(KeyEvent.VK_T))
return 0f;
}
+ else if (keys.get(KeyEvent.VK_T))
+ {
+ keys.clear();
+
+ final int weaponId = sItems.getWeapon(playerId, EnumSet.allOf(WeaponType.class), true);
+
+ if (weaponId < 0)
+ {
+ // TODO: it would be cool if we could support proper screen chaining.
+ // Eg, here we should actually select the EquipScreen AND follow it with a LookScreen.
+
+ msg.send(playerId, new Msg("must equip a weapon first"));
+
+ return 0f;
+ }
+ }
else if (keys.get(KeyEvent.VK_W))
{
keys.clear();
@@ -197,6 +216,16 @@ else if (keys.get(KeyEvent.VK_W))
return 0f;
}
+ else if (keys.get(KeyEvent.VK_C))
+ {
+ keys.clear();
+
+ Main.pause();
+
+ screen.select(CraftScreen.class);
+
+ return 0f;
+ }
else if (keys.get(KeyEvent.VK_ESCAPE))
{
keys.clear();
@@ -231,12 +260,23 @@ else if (keys.get(KeyEvent.VK_SPACE))
Main.paused = !Main.paused;
keys.clear();
+
+ return 0f;
}
- else
+
+ keys.clear();
+
+ if (Main.realtime)
{
- keys.clear();
+ // in real-time mode, SPACE just means pausing (or unpausing)
- Main.paused = Main.realtime && !Main.paused;
+ Main.paused = !Main.paused;
+ }
+ else
+ {
+ // in turn-based mode, SPACE means "unpause", which we achieve by
+ // simply setting a player action equal to the world delta.
+ // this will
return world.delta;
}
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java
index d8afc13..c25b4c8 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/BootstrapSystem.java
@@ -21,7 +21,6 @@
import com.artemis.annotations.Wire;
import com.artemis.managers.PlayerManager;
import com.artemis.utils.IntBag;
-import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
import com.github.fabioticconi.alone.behaviours.*;
@@ -52,6 +51,7 @@ public class BootstrapSystem extends PassiveSystem
MapSystem sMap;
TreeSystem sTree;
CrushSystem sCrush;
+ ItemSystem sItems;
MapSystem map;
PlayerManager pManager;
@@ -87,7 +87,7 @@ protected void initialize()
map.obstacles.set(id, x, y);
pManager.setPlayer(world.getEntity(id), "player");
edit.create(Inventory.class);
- edit.add(new Name("You"));
+ edit.add(new Name("You", "you"));
// add a herd of buffalos
int groupId = sGroup.createGroup();
@@ -125,7 +125,7 @@ protected void initialize()
edit.create(Alertness.class).value = 0.0f;
edit.create(Sprite.class).set('b', Util.BROWN.darker().darker());
// edit.create(Sprite.class).set(Character.forDigit(id, 10), Util.BROWN.darker().darker());
- edit.add(new Name("A buffalo"));
+ edit.add(new Name("A big buffalo", "buffalo"));
map.obstacles.set(id, x, y);
}
@@ -160,7 +160,7 @@ protected void initialize()
edit.create(Position.class).set(x, y);
edit.create(Alertness.class).value = 0.0f;
edit.create(Sprite.class).set('r', Color.LIGHT_GRAY);
- edit.add(new Name("A rabbit"));
+ edit.add(new Name("A cute rabbit", "rabbit"));
map.obstacles.set(id, x, y);
}
@@ -201,7 +201,7 @@ protected void initialize()
edit.create(Alertness.class).value = 0.0f;
edit.create(Sprite.class).set('w', Color.DARK_GRAY);
// edit.create(Sprite.class).set(Character.forDigit(id, 10), Color.DARK_GRAY);
- edit.add(new Name("A wolf"));
+ edit.add(new Name("A ferocious wolf", "wolf"));
map.obstacles.set(id, x, y);
}
@@ -237,7 +237,7 @@ protected void initialize()
edit.create(Alertness.class).value = 0.0f;
edit.create(Sprite.class).set('p', Util.BROWN.darker().darker());
// edit.create(Sprite.class).set(Character.forDigit(id, 10), Util.BROWN.darker());
- edit.add(new Name("A puma"));
+ edit.add(new Name("A strong puma", "puma"));
map.obstacles.set(id, x, y);
}
@@ -276,7 +276,7 @@ protected void initialize()
edit.create(Position.class).set(x, y);
edit.create(Alertness.class).value = 0.0f;
edit.create(Sprite.class).set('f', Color.CYAN.darker());
- edit.add(new Name("A fish"));
+ edit.add(new Name("A colorful fish", "fish"));
map.obstacles.set(id, x, y);
}
@@ -297,7 +297,7 @@ protected void initialize()
if (!map.obstacles.isEmpty(x, y))
continue;
- sTree.makeTree(x, y);
+ sItems.makeItem("tree", x, y);
}
}
}
@@ -319,7 +319,7 @@ protected void initialize()
if (!map.obstacles.isEmpty(x, y))
continue;
- sCrush.makeBoulder(x, y);
+ sItems.makeItem("boulder", x, y);
}
}
}
@@ -341,7 +341,7 @@ protected void initialize()
if (!map.items.isEmpty(x, y))
continue;
- sCrush.makeStone(x, y);
+ sItems.makeItem("stone", x, y);
}
}
}
@@ -360,19 +360,9 @@ protected void initialize()
if (!map.items.isEmpty(x, y))
continue;
- switch (r.nextInt(3))
- {
- case 0:
- id = sTree.makeTrunk(x, y);
- map.items.set(id, x, y);
- break;
-
- case 1:
- case 2:
- id = sTree.makeBranch(x, y);
- map.items.set(id, x, y);
- break;
- }
+ sItems.makeItem("trunk", x, y);
+ sItems.makeItem("branch", x, y);
+ sItems.makeItem("vine", x, y);
}
}
}
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/BumpSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/BumpSystem.java
index d290ee5..43918a7 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/BumpSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/BumpSystem.java
@@ -38,12 +38,13 @@ public class BumpSystem extends PassiveSystem
static final Logger log = LoggerFactory.getLogger(BumpSystem.class);
ComponentMapper mHealth;
- ComponentMapper mTree;
+ ComponentMapper mCuttable;
ComponentMapper mPushable;
ComponentMapper mCrushable;
ComponentMapper mPos;
ComponentMapper mPlayer;
ComponentMapper mSight;
+ ComponentMapper mPath;
ActionSystem sAction;
AttackSystem sAttack;
@@ -53,12 +54,12 @@ public class BumpSystem extends PassiveSystem
MovementSystem sMove;
MapSystem map;
- public float bumpAction(final int entityId, final Side direction)
+ public float bumpAction(final int actorId, final Side direction)
{
if (direction.equals(Side.HERE))
return 0f;
- final Position p = mPos.get(entityId);
+ final Position p = mPos.get(actorId);
final int newX = p.x + direction.x;
final int newY = p.y + direction.y;
@@ -72,35 +73,44 @@ public float bumpAction(final int entityId, final Side direction)
if (targetId < 0)
{
- c = sMove.move(entityId, direction);
+ c = sMove.move(actorId, direction);
sAction.act(c);
return c.delay;
}
- if (mPlayer.has(entityId) && mTree.has(targetId))
+ // BUMPING!
+
+ // if we were path-moving, now, whatever happens next, we stop
+ mPath.remove(actorId);
+
+ if (mPlayer.has(actorId) && mCuttable.has(targetId))
{
- c = sTree.cut(entityId, targetId);
+ c = sTree.cut(actorId, targetId);
}
- // else if (mPlayer.has(entityId) && mPushable.has(targetId))
+ // else if (mPlayer.has(actorId) && mPushable.has(targetId))
// {
- // c = sPush.push(entityId, targetId);
+ // c = sPush.push(actorId, targetId);
// }
- else if (mPlayer.has(entityId) && mCrushable.has(targetId))
+ else if (mPlayer.has(actorId) && mCrushable.has(targetId))
{
- c = sCrush.crush(entityId, targetId);
+ c = sCrush.crush(actorId, targetId);
}
else if (mHealth.has(targetId))
{
- c = sAttack.attack(entityId, targetId);
+ c = sAttack.attack(actorId, targetId);
+ }
+ else
+ {
+ log.warn("{} bumped into {} but couldn't do anything", actorId, targetId);
}
return sAction.act(c);
}
- public float bumpAction(final int entityId, final Position target)
+ public float bumpAction(final int actorId, final Position target)
{
- final Position pos = mPos.get(entityId);
- final Sight sight = mSight.get(entityId);
+ final Position pos = mPos.get(actorId);
+ final Sight sight = mSight.get(actorId);
if (pos.equals(target))
{
@@ -111,7 +121,7 @@ public float bumpAction(final int entityId, final Position target)
if (Coords.distanceChebyshev(pos.x, pos.y, target.x, target.y) == 1)
{
// it's only one step away, no point calculating line of sight
- return bumpAction(entityId, Side.getSide(pos.x, pos.y, target.x, target.y));
+ return bumpAction(actorId, Side.getSide(pos.x, pos.y, target.x, target.y));
}
// let's give them the opportunity to plan a path even if the creature is at the border of the vision
@@ -123,7 +133,7 @@ public float bumpAction(final int entityId, final Position target)
// the target position is the same as the entity's position,
// or the target is not visible. Either way, we don't move.
- log.warn("{} cannot find a path from {} to {}", entityId, pos, target);
+ log.warn("{} cannot find a path from {} to {}", actorId, pos, target);
return 0f;
}
@@ -131,6 +141,6 @@ public float bumpAction(final int entityId, final Position target)
// position 0 is "HERE"
final Point p = path[1];
- return bumpAction(entityId, Side.getSide(pos.x, pos.y, p.x, p.y));
+ return bumpAction(actorId, Side.getSide(pos.x, pos.y, p.x, p.y));
}
}
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/CraftSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/CraftSystem.java
index a3bd464..cbfb10d 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/CraftSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/CraftSystem.java
@@ -18,12 +18,13 @@
package com.github.fabioticconi.alone.systems;
-import com.fasterxml.jackson.core.JsonParser;
+import com.artemis.ComponentMapper;
+import com.artemis.annotations.Wire;
+import com.artemis.utils.IntBag;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
-import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
-import com.github.fabioticconi.alone.screens.CraftItemScreen;
+import com.github.fabioticconi.alone.components.Inventory;
+import com.github.fabioticconi.alone.components.Name;
import net.mostlyoriginal.api.system.core.PassiveSystem;
import java.io.FileInputStream;
@@ -37,49 +38,180 @@
*/
public class CraftSystem extends PassiveSystem
{
+ ComponentMapper mInventory;
+ ComponentMapper mName;
+
+ ItemSystem sItems;
+
+ @Wire
+ ObjectMapper mapper;
+
HashMap recipes;
- public CraftSystem() throws IOException
+ @Override
+ protected void initialize()
{
- loadRecipes();
+ try
+ {
+ loadRecipes();
+ } catch (final IOException e)
+ {
+ e.printStackTrace();
+ }
}
public List getRecipeNames()
{
- if (recipes == null || recipes.isEmpty())
+ try
+ {
+ loadRecipes();
+
+ return new ArrayList<>(recipes.keySet());
+ } catch (final IOException e)
+ {
+ e.printStackTrace();
+
return List.of();
+ }
+ }
+
+ public HashMap getRecipes()
+ {
+ try
+ {
+ loadRecipes();
+ } catch (final IOException e)
+ {
+ e.printStackTrace();
+ }
- return new ArrayList<>(recipes.keySet());
+ return recipes;
}
- private void loadRecipes() throws IOException
+ public void loadRecipes() throws IOException
{
- // TODO we can actually instantiate the factory and mapper in the Main and inject/Wire them
+ final InputStream fileStream = new FileInputStream("data/crafting.yml");
- final InputStream fileStream = new FileInputStream("data/crafting.yml");
- final YAMLFactory factory = new YAMLFactory();
- final ObjectMapper mapper = new ObjectMapper(factory);
+ recipes = mapper.readValue(fileStream, new TypeReference>()
+ {
+ });
- recipes = mapper.readValue(fileStream, new TypeReference>(){});
+ // reload item templates
+ sItems.loadTemplates();
for (final Map.Entry entry : recipes.entrySet())
{
- System.out.println(entry.getKey() + " | " + entry.getValue());
+ final CraftItem temp = entry.getValue();
+ temp.tag = entry.getKey();
+
+ for (final String tempSource : temp.sources)
+ {
+ if (!sItems.templates.keySet().contains(tempSource))
+ throw new RuntimeException("unknown item in sources field: " + tempSource);
+ }
+
+ for (final String tempTool : temp.tools)
+ {
+ if (!sItems.templates.keySet().contains(tempTool))
+ throw new RuntimeException("unknown item in tools field: " + tempTool);
+ }
+ }
+ }
+
+ public int craftItem(final int entityId, final CraftItem itemRecipe)
+ {
+ final Inventory inv = mInventory.get(entityId);
+
+ if (inv == null)
+ return -1;
+
+ final IntBag tempSources = new IntBag(itemRecipe.sources.length);
+ Arrays.fill(tempSources.getData(), -1);
+ final IntBag tempTools = new IntBag(itemRecipe.tools.length);
+ Arrays.fill(tempTools.getData(), -1);
+
+ final int[] data = inv.items.getData();
+ for (int i = 0, size = inv.items.size(); i < size; i++)
+ {
+ final int itemId = data[i];
+
+ if (!mName.has(itemId))
+ continue;
+
+ final Name name = mName.get(itemId);
+
+ int ii = 0;
+ final int[] sources = tempSources.getData();
+ while (ii < itemRecipe.sources.length)
+ {
+ System.out.println("sources " + ii);
+ System.out.println(name.tag + " | " + itemRecipe.sources[ii]);
+ System.out.println(sources[ii]);
+ if (sources[ii] < 0 && name.tag.equals(itemRecipe.sources[ii]))
+ {
+ tempSources.set(ii, itemId);
+
+ break; // source items can only be "used" once
+ }
+
+ ii++;
+ }
+
+ // if ii == sources.length, then it means that the previous loop completed
+ // without setting itemId as a "source". So it's OK to evaluate whether it
+ // can be used as a "tool".
+ // conversely, if ii < sources.length then itemId is a "source" and we cannot use it
+ // as tool.
+ if (ii < sources.length)
+ continue;
+
+ ii = 0;
+ final int[] tools = tempTools.getData();
+ while (ii < itemRecipe.tools.length)
+ {
+ System.out.println("tools " + ii);
+ System.out.println(name.tag + " | " + itemRecipe.tools[ii]);
+ System.out.println(tools[ii]);
+ if (tools[ii] < 0 && name.tag.equals(itemRecipe.tools[ii]))
+ {
+ tempTools.set(ii, itemId);
+
+ break; // tool items can only be used once
+ }
+
+ ii++;
+ }
+ }
+
+ if (tempSources.size() < itemRecipe.sources.length || tempTools.size() < itemRecipe.tools.length)
+ return -1;
+
+ final int id = sItems.makeItem(itemRecipe.tag);
+
+ if (id < 0)
+ return -1;
+
+ for (final int sourceId : tempSources.getData())
+ {
+ // destroying source items
+ world.delete(sourceId);
+ inv.items.removeValue(sourceId);
}
+
+ return id;
}
public static class CraftItem
{
- public String name;
- public String source;
- public String tool;
- public int n = 1;
+ public String tag;
+ public String[] sources;
+ public String[] tools;
@Override
public String toString()
{
- return "CraftItem{" + "name='" + name + '\'' + ", source='" + source + '\'' + ", tool='" + tool + '\'' +
- ", n=" + n + '}';
+ return "CraftItem{" + "tag='" + tag + '\'' + ", sources=" + Arrays.toString(sources) + ", tools=" +
+ Arrays.toString(tools) + '}';
}
}
}
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/CrushSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/CrushSystem.java
index f2b00a7..41a628e 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/CrushSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/CrushSystem.java
@@ -19,8 +19,10 @@
package com.github.fabioticconi.alone.systems;
import com.artemis.ComponentMapper;
-import com.artemis.EntityEdit;
-import com.github.fabioticconi.alone.components.*;
+import com.github.fabioticconi.alone.components.Crushable;
+import com.github.fabioticconi.alone.components.Position;
+import com.github.fabioticconi.alone.components.Speed;
+import com.github.fabioticconi.alone.components.Weapon;
import com.github.fabioticconi.alone.components.actions.ActionContext;
import com.github.fabioticconi.alone.components.attributes.Strength;
import com.github.fabioticconi.alone.constants.WeaponType;
@@ -29,9 +31,7 @@
import net.mostlyoriginal.api.system.core.PassiveSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import rlforj.math.Point;
-import java.awt.Color;
import java.util.EnumSet;
/**
@@ -42,10 +42,10 @@ public class CrushSystem extends PassiveSystem
{
static final Logger log = LoggerFactory.getLogger(CrushSystem.class);
- ComponentMapper mCrushable;
+ ComponentMapper mCrush;
ComponentMapper mSpeed;
- ComponentMapper mStrength;
- ComponentMapper mPosition;
+ ComponentMapper mStr;
+ ComponentMapper mPos;
ComponentMapper mWeapon;
StaminaSystem sStamina;
@@ -64,49 +64,6 @@ public CrushAction crush(final int entityId, final int targetId)
return c;
}
- public int makeStone(final Point p)
- {
- return makeStone(p.x, p.y);
- }
-
- public int makeStone(final int x, final int y)
- {
- final int id = world.create();
-
- final EntityEdit edit = world.edit(id);
- edit.create(Position.class).set(x, y);
- edit.create(Sprite.class).set('o', Color.DARK_GRAY.brighter());
- edit.create(Weapon.class).set(WeaponType.BLUNT, 1);
- edit.create(Wearable.class);
- edit.add(new Name("A stone"));
-
- map.items.set(id, x, y);
-
- return id;
- }
-
- public int makeBoulder(final Point p)
- {
- return makeBoulder(p.x, p.y);
- }
-
- public int makeBoulder(final int x, final int y)
- {
- final int id = world.create();
- final EntityEdit edit = world.edit(id);
-
- edit.create(Position.class).set(x, y);
- edit.create(Sprite.class).set('#', Color.DARK_GRAY.brighter(), true);
- edit.create(LightBlocker.class);
- edit.create(Pushable.class);
- edit.create(Crushable.class);
- edit.add(new Name("A boulder"));
-
- map.obstacles.set(id, x, y);
-
- return id;
- }
-
public class CrushAction extends ActionContext
{
@Override
@@ -117,7 +74,7 @@ public boolean tryAction()
final int targetId = targets.get(0);
- if (targetId < 0 || !mCrushable.has(targetId))
+ if (targetId < 0 || !mCrush.has(targetId))
return false;
final int hammerId = sItem.getWeapon(actorId, EnumSet.of(WeaponType.BLUNT), true);
@@ -142,7 +99,7 @@ public boolean tryAction()
// FIXME further adjust delay and cost using the hammer power
delay = mSpeed.get(actorId).value;
- cost = delay / (mStrength.get(actorId).value + 3f);
+ cost = delay / (mStr.get(actorId).value + 3f);
return true;
}
@@ -157,7 +114,7 @@ public void doAction()
msg.send(actorId, targetId, new CrushMsg());
- final Position p = mPosition.get(targetId);
+ final Position p = mPos.get(targetId);
// from a tree we get a trunk and two branches
map.obstacles.del(p.x, p.y);
@@ -165,7 +122,7 @@ public void doAction()
for (int i = 0; i < 3; i++)
{
- makeStone(map.getFirstTotallyFree(p.x, p.y, -1));
+ sItem.makeItem("stone", p.x, p.y);
}
// consume a fixed amount of stamina
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/DeadSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/DeadSystem.java
index bece3eb..4f840c2 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/DeadSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/DeadSystem.java
@@ -25,7 +25,7 @@
import com.github.fabioticconi.alone.components.*;
import rlforj.math.Point;
-import java.awt.Color;
+import java.awt.*;
/**
* Author: Fabio Ticconi
@@ -65,7 +65,7 @@ protected void process(final int entityId)
edit.create(Sprite.class).set('$', Color.RED.darker().darker(), false);
edit.create(Corpse.class);
edit.create(Health.class).set(size.value + 3);
- edit.add(new Name(name.name + "'s corpse"));
+ edit.add(new Name(name.name + "'s corpse", "corpse"));
map.items.set(corpseId, p2.x, p2.y);
}
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java
index d51f467..8629a5d 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java
@@ -19,6 +19,10 @@
package com.github.fabioticconi.alone.systems;
import com.artemis.ComponentMapper;
+import com.artemis.EntityEdit;
+import com.artemis.annotations.Wire;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.fabioticconi.alone.components.*;
import com.github.fabioticconi.alone.components.actions.ActionContext;
import com.github.fabioticconi.alone.constants.WeaponType;
@@ -26,13 +30,17 @@
import com.github.fabioticconi.alone.messages.DropMsg;
import com.github.fabioticconi.alone.messages.EquipMsg;
import com.github.fabioticconi.alone.messages.GetMsg;
-import com.github.fabioticconi.alone.screens.AbstractScreen;
import net.mostlyoriginal.api.system.core.PassiveSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rlforj.math.Point;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
/**
* Author: Fabio Ticconi
@@ -48,10 +56,140 @@ public class ItemSystem extends PassiveSystem
ComponentMapper mEquip;
ComponentMapper mWearable;
ComponentMapper mArmour;
+ ComponentMapper mName;
+ ComponentMapper mObstacle;
MessageSystem msg;
MapSystem map;
+ @Wire
+ ObjectMapper mapper;
+
+ HashMap templates;
+
+ @Override
+ protected void initialize()
+ {
+ try
+ {
+ loadTemplates();
+ } catch (final IOException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public HashMap getTemplates()
+ {
+ try
+ {
+ loadTemplates();
+ } catch (final IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ return templates;
+ }
+
+ public void loadTemplates() throws IOException
+ {
+ final InputStream fileStream = new FileInputStream("data/items.yml");
+
+ templates = mapper.readValue(fileStream, new TypeReference>()
+ {
+ });
+
+ for (final Map.Entry entry : templates.entrySet())
+ {
+ final ItemTemplate temp = entry.getValue();
+ temp.tag = entry.getKey();
+ }
+ }
+
+ /**
+ * It instantiates an object of the given type and places at that Point.
+ *
+ * @param tag
+ * @param p
+ * @return
+ */
+ public int makeItem(final String tag, final Point p)
+ {
+ return makeItem(tag, p.x, p.y);
+ }
+
+ /**
+ * It instantiates an object of the given type and places at that position.
+ *
+ * @param tag
+ * @param x
+ * @param y
+ * @return
+ */
+ public int makeItem(final String tag, final int x, final int y)
+ {
+ final int id = makeItem(tag);
+
+ if (id < 0)
+ return id;
+
+ final Point p = map.getFirstTotallyFree(x, y, -1);
+
+ mPos.create(id).set(p.x, p.y);
+
+ if (mObstacle.has(id))
+ map.obstacles.set(id, p.x, p.y);
+ else
+ map.items.set(id, p.x, p.y);
+
+ return id;
+ }
+
+ public int makeItem(final String tag)
+ {
+ try
+ {
+ loadTemplates();
+
+ final ItemTemplate template = templates.get(tag);
+
+ if (template == null)
+ {
+ log.warn("Item named {} doesn't exist", tag);
+ return -1;
+ }
+
+ final int id = world.create();
+
+ final EntityEdit edit = world.edit(id);
+
+ edit.add(new Name(template.name, tag));
+
+ // TODO find a way to do this dynamically. Right now I cannot figure out a way
+ // of deserialising an array of Components, because it's an abstract class that I cannot annotate.
+ if (template.wearable != null)
+ edit.add(template.wearable);
+ if (template.weapon != null)
+ edit.add(template.weapon);
+ if (template.sprite != null)
+ edit.add(template.sprite);
+ if (template.obstacle != null)
+ edit.add(template.obstacle);
+ if (template.crushable != null)
+ edit.add(template.crushable);
+ if (template.cuttable != null)
+ edit.add(template.cuttable);
+
+ return id;
+ } catch (final IOException e)
+ {
+ e.printStackTrace();
+
+ return -1;
+ }
+ }
+
public GetAction get(final int actorId)
{
final GetAction a = new GetAction();
@@ -81,12 +219,12 @@ public EquipAction equip(final int actorId, final int targetId)
return a;
}
- int getArmour(final int entityId)
+ public int getArmour(final int entityId)
{
return getArmour(entityId, true);
}
- int getArmour(final int entityId, final boolean onlyEquipped)
+ public int getArmour(final int entityId, final boolean onlyEquipped)
{
final Inventory items = mInventory.get(entityId);
@@ -115,17 +253,42 @@ int getArmour(final int entityId, final boolean onlyEquipped)
return -1;
}
- int getWeapon(final int entityId)
+ public int getItem(final int entityId, final String tag, final boolean onlyEquipped)
+ {
+ final Inventory items = mInventory.get(entityId);
+
+ if (items == null)
+ return -1;
+
+ final int[] data = items.items.getData();
+ for (int i = 0, size = items.items.size(); i < size; i++)
+ {
+ final int itemId = data[i];
+
+ // we might only want an equipped item
+ if (!mName.has(itemId) || (onlyEquipped && !mEquip.has(itemId)))
+ continue;
+
+ final Name name = mName.get(itemId);
+
+ if (name.tag.equals(tag))
+ return itemId;
+ }
+
+ return -1;
+ }
+
+ public int getWeapon(final int entityId)
{
return getWeapon(entityId, true);
}
- int getWeapon(final int entityId, final boolean onlyEquipped)
+ public int getWeapon(final int entityId, final boolean onlyEquipped)
{
return getWeapon(entityId, EnumSet.allOf(WeaponType.class), onlyEquipped);
}
- int getWeapon(final int entityId, final EnumSet weaponTypes, final boolean onlyEquipped)
+ public int getWeapon(final int entityId, final EnumSet weaponTypes, final boolean onlyEquipped)
{
final Inventory items = mInventory.get(entityId);
@@ -137,13 +300,6 @@ int getWeapon(final int entityId, final EnumSet weaponTypes, final b
{
final int itemId = data[i];
- if (itemId < 0)
- {
- // TODO: we could flag inventory as "dirty", and then use a system for periodic cleanup.
-
- continue;
- }
-
// we might only want an equipped weapon
if (!mWeapon.has(itemId) || (onlyEquipped && !mEquip.has(itemId)))
continue;
@@ -157,6 +313,19 @@ int getWeapon(final int entityId, final EnumSet weaponTypes, final b
return -1;
}
+ public static class ItemTemplate
+ {
+ public String name;
+ public String tag;
+
+ public Wearable wearable;
+ public Weapon weapon;
+ public Sprite sprite;
+ public Obstacle obstacle;
+ public Crushable crushable;
+ public Cuttable cuttable;
+ }
+
public class GetAction extends ActionContext
{
@Override
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java
index c4b2905..02def06 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/MapSystem.java
@@ -18,7 +18,7 @@
package com.github.fabioticconi.alone.systems;
import com.artemis.ComponentMapper;
-import com.github.fabioticconi.alone.components.LightBlocker;
+import com.github.fabioticconi.alone.components.Obstacle;
import com.github.fabioticconi.alone.constants.Cell;
import com.github.fabioticconi.alone.constants.Options;
import com.github.fabioticconi.alone.constants.Side;
@@ -30,7 +30,10 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rlforj.IBoard;
-import rlforj.los.*;
+import rlforj.los.BresLos;
+import rlforj.los.IFovAlgorithm;
+import rlforj.los.ILosAlgorithm;
+import rlforj.los.ShadowCasting;
import rlforj.math.Point;
import rlforj.pathfinding.AStar;
@@ -54,14 +57,13 @@ public class MapSystem extends PassiveSystem implements IBoard
final Cell terrain[][];
/* FOV/LOS stuff */
- final LongBag lastVisited;
- final AStar astar;
+ final LongBag lastVisited;
+ final AStar astar;
final IFovAlgorithm fov;
final ILosAlgorithm los;
- ComponentMapper mLightBlocker;
-
final SingleGrid obstacles;
final SingleGrid items;
+ ComponentMapper mObstacle;
public MapSystem() throws IOException
{
@@ -82,6 +84,12 @@ public MapSystem() throws IOException
obstacles = new SingleGrid(Options.MAP_SIZE_X, Options.MAP_SIZE_Y);
items = new SingleGrid(Options.MAP_SIZE_X, Options.MAP_SIZE_Y);
+ // TODO: we should make Cell a class, and obviously use a pool.
+ // This way we can load the cells from a yaml file, with their thresholds, colours, characters etc,
+ // and then we aren't stuck anymore with a few discrete terrain types but we can colour with a gradient,
+ // for example.
+ // movement costs should also be part of this cell.
+
float value;
for (int x = 0; x < Options.MAP_SIZE_X; x++)
@@ -416,7 +424,7 @@ public boolean blocksLight(final int x, final int y)
// currently no tile blocks light by itself, so if there's no creature
// here we know that light passes.
- return entityId >= 0 && mLightBlocker.has(entityId);
+ return entityId >= 0 && mObstacle.has(entityId);
}
@Override
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java
index 8318f08..2b6da60 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/MovementSystem.java
@@ -18,6 +18,7 @@
package com.github.fabioticconi.alone.systems;
import com.artemis.ComponentMapper;
+import com.github.fabioticconi.alone.components.Path;
import com.github.fabioticconi.alone.components.Position;
import com.github.fabioticconi.alone.components.Speed;
import com.github.fabioticconi.alone.components.Underwater;
@@ -38,6 +39,7 @@ public class MovementSystem extends PassiveSystem
ComponentMapper mPosition;
ComponentMapper mSpeed;
ComponentMapper mUnderWater;
+ ComponentMapper mPath;
StaminaSystem sStamina;
MapSystem map;
@@ -76,6 +78,23 @@ public boolean tryAction()
final Cell cell = map.get(x2, y2);
+ if (mUnderWater.has(actorId))
+ {
+ cost = 0.25f;
+ delay = speed.value * 0.25f;
+
+ return true;
+ }
+
+ if (mPath.has(actorId))
+ {
+ // it's a thrown weapon, no cost whatsoever
+ cost = 0f;
+ delay = speed.value;
+
+ return true;
+ }
+
switch (cell)
{
case HILL:
@@ -93,17 +112,11 @@ public boolean tryAction()
break;
case WATER:
- if (mUnderWater.has(actorId))
- cost = 0.25f;
- else
- cost = 3f;
+ cost = 3f;
break;
case DEEP_WATER:
- if (mUnderWater.has(actorId))
- cost = 0.25f;
- else
- cost = 4f;
+ cost = 4f;
break;
default:
@@ -123,17 +136,7 @@ public void doAction()
final int x2 = p.x + direction.x;
final int y2 = p.y + direction.y;
- if (map.items.has(actorId, p.x, p.y))
- {
- // it's a moving item
- map.items.move(p.x, p.y, x2, y2);
-
- p.x = x2;
- p.y = y2;
-
- // it doesn't have stamina
- }
- else if (map.obstacles.has(actorId, p.x, p.y) && map.isFree(x2, y2))
+ if (map.isFree(x2, y2))
{
final int id = map.obstacles.move(p.x, p.y, x2, y2);
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/PathSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/PathSystem.java
index fa30cb9..2bc6d39 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/PathSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/PathSystem.java
@@ -25,6 +25,9 @@
import com.github.fabioticconi.alone.components.Position;
import com.github.fabioticconi.alone.components.Speed;
import com.github.fabioticconi.alone.constants.Side;
+import com.github.fabioticconi.alone.utils.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import rlforj.math.Point;
/**
@@ -33,14 +36,14 @@
*/
public class PathSystem extends DelayedIteratingSystem
{
+ static final Logger log = LoggerFactory.getLogger(PathSystem.class);
+
ComponentMapper mPath;
ComponentMapper mSpeed;
ComponentMapper mPos;
- MapSystem map;
-
- MovementSystem sMove;
- BumpSystem sBump;
+ MapSystem map;
+ BumpSystem sBump;
public PathSystem()
{
@@ -59,34 +62,54 @@ protected void processDelta(final int entityId, final float accumulatedDelta)
mPath.get(entityId).cooldown -= accumulatedDelta;
}
+ @Override
+ protected void removed(final int entityId)
+ {
+ // path was removed, so it stopped. Ergo, we place the object on the ground
+
+ final Position pos = mPos.get(entityId);
+
+ final Point p = map.getFirstTotallyFree(pos.x, pos.y, -1);
+
+ map.obstacles.del(pos.x, pos.y);
+ map.items.set(entityId, p.x, p.y);
+ }
+
@Override
protected void processExpired(final int entityId)
{
final Path path = mPath.get(entityId);
final Position p = mPos.get(entityId);
- final Point newP = path.steps.remove(0);
- final Side direction = Side.getSide(p.x, p.y, newP.x, newP.y);
+ if (path.i >= path.steps.size())
+ {
+ // for some reason the path has ended outside of us. Let's just terminate
+ mPath.remove(entityId);
+
+ log.warn("{} was moving via a Path but there are no steps left");
+
+ return;
+ }
- final float wait = sBump.bumpAction(entityId, direction);
+ final Point p2 = path.steps.get(path.i++);
+ final Side side = Side.getSide(p.x, p.y, p2.x, p2.y);
- // FIXME: if wait is zero, it usually means the bump failed somehow. If that's true than we need
- // to stop path-moving. Maybe we should reserve -1 for when the bump fails?
+ sBump.bumpAction(entityId, side);
- // in general, actually, we should stop whenever the bump did not do a movement. This
- // requires a more complicated handling which should, I believe, be completely performed by
- // BumpSystem.
+ // bump can remove the Path in case movement fails (eg, there's an obstacle and so we actually bump)
+ if (!mPath.has(entityId))
+ return;
- if (path.steps.isEmpty())
+ if (path.i == path.steps.size())
{
// we arrived!
mPath.remove(entityId);
}
else
{
- final float speed = mSpeed.get(entityId).value;
+ final float speed = mSpeed.get(entityId).value * Util.gain((float) path.i / path.steps.size(), 0.25f);
- path.cooldown = Math.max(speed, wait);
+ path.cooldown = Math.max(speed, 0.05f); // let's not go too fast
offerDelay(speed);
}
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/ThrowSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/ThrowSystem.java
index e9ae86d..a1d1508 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/ThrowSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/ThrowSystem.java
@@ -52,6 +52,7 @@ public class ThrowSystem extends PassiveSystem
ComponentMapper mStrength;
ComponentMapper mAgility;
ComponentMapper mName;
+ ComponentMapper mEquip;
StaminaSystem sStamina;
BumpSystem sBump;
@@ -151,9 +152,9 @@ public void doAction()
final int weaponId = targets.get(0);
- final Point newP = path.get(0);
+ final Point p2 = path.get(0);
- if (map.isFree(newP.x, newP.y))
+ if (map.isFree(p2.x, p2.y))
{
final Inventory inventory = mInventory.get(actorId);
@@ -166,17 +167,19 @@ public void doAction()
mSpeed.create(weaponId).set(cooldown);
mPath.create(weaponId).set(cooldown, path);
- mPos.create(weaponId).set(newP.x, newP.y);
+ mPos.create(weaponId).set(p2.x, p2.y);
+
+ // it's not equipped anymore
+ mEquip.remove(weaponId);
// strength and agility of thrower are passed on to the thrown weapon.
// effects: the weapon will hit more likely with high agility, and do more damage with high strength.
mStrength.create(weaponId).value = mStrength.get(actorId).value;
mAgility.create(weaponId).value = mAgility.get(actorId).value;
- // at this point it really happened: the weapon is flying at its new position
- // (it's not an obstacle, so there's not risk of someone interrupting it in mid-air)
- final Point p2 = map.getFirstTotallyFree(newP.x, newP.y, -1);
- map.items.set(weaponId, p2.x, p2.y);
+ // at this point it really happened: the weapon is flying at its new position.
+ // it's an obstacle, so it will bump against whatever it finds
+ map.obstacles.set(weaponId, p2.x, p2.y);
}
else
{
@@ -186,7 +189,7 @@ public void doAction()
final Position p = mPos.get(actorId);
- sBump.bumpAction(actorId, Side.getSide(p.x, p.y, newP.x, newP.y));
+ sBump.bumpAction(actorId, Side.getSide(p.x, p.y, p2.x, p2.y));
}
sStamina.consume(actorId, cost);
diff --git a/src/main/java/com/github/fabioticconi/alone/systems/TreeSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/TreeSystem.java
index 24506f8..4e3039a 100644
--- a/src/main/java/com/github/fabioticconi/alone/systems/TreeSystem.java
+++ b/src/main/java/com/github/fabioticconi/alone/systems/TreeSystem.java
@@ -19,20 +19,18 @@
package com.github.fabioticconi.alone.systems;
import com.artemis.ComponentMapper;
-import com.artemis.EntityEdit;
-import com.github.fabioticconi.alone.components.*;
+import com.github.fabioticconi.alone.components.Cuttable;
+import com.github.fabioticconi.alone.components.Position;
+import com.github.fabioticconi.alone.components.Speed;
import com.github.fabioticconi.alone.components.actions.ActionContext;
import com.github.fabioticconi.alone.components.attributes.Strength;
import com.github.fabioticconi.alone.constants.WeaponType;
import com.github.fabioticconi.alone.messages.CannotMsg;
import com.github.fabioticconi.alone.messages.CutMsg;
-import com.github.fabioticconi.alone.utils.Util;
import net.mostlyoriginal.api.system.core.PassiveSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import rlforj.math.Point;
-import java.awt.*;
import java.util.EnumSet;
/**
@@ -43,11 +41,10 @@ public class TreeSystem extends PassiveSystem
{
static final Logger log = LoggerFactory.getLogger(TreeSystem.class);
- ComponentMapper mTree;
+ ComponentMapper mCuttable;
ComponentMapper mSpeed;
ComponentMapper mStrength;
ComponentMapper mPosition;
- ComponentMapper mName;
StaminaSystem sStamina;
ItemSystem sItem;
@@ -65,62 +62,6 @@ public CutAction cut(final int entityId, final int treeId)
return c;
}
- public int makeTree(final int x, final int y)
- {
- final int id = world.create();
-
- final EntityEdit edit = world.edit(id);
- edit.create(Position.class).set(x, y);
- edit.create(Sprite.class).set('T', Color.GREEN.darker(), true);
- edit.create(LightBlocker.class);
- edit.create(Tree.class);
- edit.add(new Name("A tree"));
-
- map.obstacles.set(id, x, y);
-
- return id;
- }
-
- public int makeTrunk(final Point p)
- {
- return makeTrunk(p.x, p.y);
- }
-
- public int makeTrunk(final int x, final int y)
- {
- final int id = world.create();
-
- final EntityEdit edit = world.edit(id);
- edit.create(Position.class).set(x, y);
- edit.create(Sprite.class).set('-', Util.BROWN.brighter());
- edit.add(new Name("A tree trunk"));
-
- map.items.set(id, x, y);
-
- return id;
- }
-
- public int makeBranch(final Point p)
- {
- return makeBranch(p.x, p.y);
- }
-
- public int makeBranch(final int x, final int y)
- {
- final int id = world.create();
-
- final EntityEdit edit = world.edit(id);
- edit.create(Position.class).set(x, y);
- edit.create(Sprite.class).set('/', Util.BROWN.brighter());
- edit.create(Weapon.class).set(WeaponType.BLUNT, 1);
- edit.create(Wearable.class);
- edit.add(new Name("A branch"));
-
- map.items.set(id, x, y);
-
- return id;
- }
-
public class CutAction extends ActionContext
{
@Override
@@ -131,7 +72,7 @@ public boolean tryAction()
final int treeId = targets.get(0);
- if (!mTree.has(treeId))
+ if (!mCuttable.has(treeId))
return false;
final int axeId = sItem.getWeapon(actorId, EnumSet.of(WeaponType.SLASH), false);
@@ -169,9 +110,9 @@ public void doAction()
map.obstacles.del(p.x, p.y);
world.delete(treeId);
- makeTrunk(map.getFirstTotallyFree(p.x, p.y, -1));
- makeBranch(map.getFirstTotallyFree(p.x, p.y, -1));
- makeBranch(map.getFirstTotallyFree(p.x, p.y, -1));
+ sItem.makeItem("trunk", p.x, p.y);
+ sItem.makeItem("branch", p.x, p.y);
+ sItem.makeItem("vine", p.x, p.y);
// consume a fixed amount of stamina
sStamina.consume(actorId, cost);