diff --git a/alone-rl.iml b/alone-rl.iml index 942698b..e8ac48a 100644 --- a/alone-rl.iml +++ b/alone-rl.iml @@ -15,7 +15,7 @@ - + diff --git a/data/crafting.yml b/data/crafting.yml index df53828..31f287f 100644 --- a/data/crafting.yml +++ b/data/crafting.yml @@ -3,7 +3,7 @@ sharp-stone: sources: stone tools: stone -small-sharp-stone: +stone-knife: sources: sharp-stone tools: stone @@ -16,5 +16,17 @@ stone-axe: tools: stone stone-spear: - sources: [small-sharp-stone, trunk, vine] - tools: stone + sources: [stone-knife, trunk, vine] + tools: [stone-axe, stone-knife] + +arrow: + sources: branch + tools: stone-knife + +stone-arrow: + sources: [stone-knife, branch, vine] + tools: stone-knife + +bow: + sources: [trunk, vine] + tools: [stone-axe, stone-knife] diff --git a/data/items.yml b/data/items.yml index 13bc993..0855fe6 100644 --- a/data/items.yml +++ b/data/items.yml @@ -90,8 +90,8 @@ sharp-stone: blue: 88 alpha: 255 -small-sharp-stone: - name: a small, sharp stone +stone-knife: + name: a stone knife wearable: where: HANDS weapon: @@ -143,7 +143,52 @@ stone-spear: damageType: POINT damage: 4 sprite: - c: 244 + c: 124 + col: + red: 141 + green: 104 + blue: 21 + alpha: 255 + +arrow: + name: an arrow + weapon: + damageType: POINT + damage: 2 + ammo: + usableBy: bow + sprite: + c: 196 + col: + red: 141 + green: 104 + blue: 21 + alpha: 255 + +stone-arrow: + name: a stone-tip arrow + weapon: + damageType: POINT + damage: 3 + ammo: + usableBy: bow + sprite: + c: 26 + col: + red: 141 + green: 104 + blue: 21 + alpha: 255 + +bow: + name: a simple bow + wearable: + where: HANDS + weapon: + damageType: SHOOTER + damage: 1 + sprite: + c: '(' col: red: 141 green: 104 diff --git a/pom.xml b/pom.xml index 76493e4..f7ef509 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ com.github.fabio-t alone-rl - 0.2.6 + 0.2.7 jar AloneRL @@ -59,7 +59,7 @@ com.github.fabio-t terrain-generator - 0.1.0 + 0.1.1 diff --git a/src/main/java/com/github/fabioticconi/alone/components/Ammo.java b/src/main/java/com/github/fabioticconi/alone/components/Ammo.java new file mode 100644 index 0000000..6724db5 --- /dev/null +++ b/src/main/java/com/github/fabioticconi/alone/components/Ammo.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015-2018 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.components; + +import com.artemis.Component; + +/** + * Author: Fabio Ticconi + * Date: 02/03/18 + */ +public class Ammo extends Component +{ + public String usableBy; + + public Ammo() + { + + } + + public Ammo(final String usableBy) + { + this.usableBy = usableBy; + } +} 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 12577b5..ff2dce4 100644 --- a/src/main/java/com/github/fabioticconi/alone/components/Armour.java +++ b/src/main/java/com/github/fabioticconi/alone/components/Armour.java @@ -19,7 +19,7 @@ package com.github.fabioticconi.alone.components; import com.artemis.Component; -import com.github.fabioticconi.alone.constants.WeaponType; +import com.github.fabioticconi.alone.constants.DamageType; import java.util.EnumMap; @@ -29,22 +29,22 @@ */ public class Armour extends Component { - // TODO we can improve this by using a primitive float array and WeaponType ordinals as indeces. + // TODO we can improve this by using a primitive float array and DamageType ordinals as indeces. // which is what EnumMap does internally. We'd avoid autoboxing. - public final EnumMap defences; + public final EnumMap defences; public Armour() { - this.defences = new EnumMap<>(WeaponType.class); + this.defences = new EnumMap<>(DamageType.class); } public void set(final float slash, final float point, final float blunt, final float natural) { - this.defences.put(WeaponType.SLASH, slash); - this.defences.put(WeaponType.POINT, point); - this.defences.put(WeaponType.BLUNT, blunt); + this.defences.put(DamageType.SLASH, slash); + this.defences.put(DamageType.POINT, point); + this.defences.put(DamageType.BLUNT, blunt); // a generic type for animal attacks - this.defences.put(WeaponType.NATURAL, natural); + this.defences.put(DamageType.NATURAL, natural); } } diff --git a/src/main/java/com/github/fabioticconi/alone/components/Weapon.java b/src/main/java/com/github/fabioticconi/alone/components/Weapon.java index ffae2e5..981c090 100644 --- a/src/main/java/com/github/fabioticconi/alone/components/Weapon.java +++ b/src/main/java/com/github/fabioticconi/alone/components/Weapon.java @@ -19,7 +19,7 @@ package com.github.fabioticconi.alone.components; import com.artemis.Component; -import com.github.fabioticconi.alone.constants.WeaponType; +import com.github.fabioticconi.alone.constants.DamageType; /** * Author: Fabio Ticconi @@ -27,7 +27,7 @@ */ public class Weapon extends Component { - public WeaponType damageType; + public DamageType damageType; public float damage; public Weapon() @@ -35,12 +35,12 @@ public Weapon() } - public Weapon(final WeaponType damageType, final float damage) + public Weapon(final DamageType damageType, final float damage) { set(damageType, damage); } - public void set(final WeaponType damageType, final float damage) + public void set(final DamageType damageType, final float damage) { this.damageType = damageType; this.damage = damage; diff --git a/src/main/java/com/github/fabioticconi/alone/constants/WeaponType.java b/src/main/java/com/github/fabioticconi/alone/constants/DamageType.java similarity index 94% rename from src/main/java/com/github/fabioticconi/alone/constants/WeaponType.java rename to src/main/java/com/github/fabioticconi/alone/constants/DamageType.java index 8861d7d..cf3e8fe 100644 --- a/src/main/java/com/github/fabioticconi/alone/constants/WeaponType.java +++ b/src/main/java/com/github/fabioticconi/alone/constants/DamageType.java @@ -22,10 +22,11 @@ * Author: Fabio Ticconi * Date: 04/11/17 */ -public enum WeaponType +public enum DamageType { SLASH, POINT, BLUNT, - NATURAL + NATURAL, + SHOOTER } diff --git a/src/main/java/com/github/fabioticconi/alone/messages/ShootMsg.java b/src/main/java/com/github/fabioticconi/alone/messages/ShootMsg.java new file mode 100644 index 0000000..99c3a93 --- /dev/null +++ b/src/main/java/com/github/fabioticconi/alone/messages/ShootMsg.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015-2018 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; + +import com.github.fabioticconi.alone.constants.Side; + +import java.awt.*; + +/** + * Author: Fabio Ticconi + * Date: 01/11/17 + */ +public class ShootMsg extends AbstractMessage +{ + public final String item; + public final Side at; + + public ShootMsg(final String item, final Side at) + { + this.item = item; + this.at = at; + } + + @Override + public String format() + { + fgCol = Color.RED; + + return String.format("%s %s %s towards %s", + actor, + thirdPerson ? "shoots" : "shoot", + item.toLowerCase(), + at.toString()); + } +} diff --git a/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java b/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java index b3becb2..adf2dc1 100644 --- a/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java +++ b/src/main/java/com/github/fabioticconi/alone/screens/MapScreen.java @@ -57,9 +57,10 @@ void regenerate() final int seed = (int) (System.currentTimeMillis() / 1000); final float freq = 3f / Math.max(Options.MAP_SIZE_X, Options.MAP_SIZE_Y); - final HeightMap heightMap = new HeightMap().size(Options.MAP_SIZE_X, Options.MAP_SIZE_Y) - .island(0.85f) - .rivers(0.8f, 0.03f, 0.001f); + final HeightMap heightMap = new HeightMap() + .size(Options.MAP_SIZE_X, Options.MAP_SIZE_Y) + .island(0.85f) + .rivers(0.8f, 0.03f, 0.001f, 1); heightMap.fractalNoise .seed(seed) 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 cb00f04..f56eb45 100644 --- a/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java +++ b/src/main/java/com/github/fabioticconi/alone/screens/PlayScreen.java @@ -26,8 +26,8 @@ import com.github.fabioticconi.alone.Main; import com.github.fabioticconi.alone.components.*; import com.github.fabioticconi.alone.components.attributes.Sight; +import com.github.fabioticconi.alone.constants.DamageType; import com.github.fabioticconi.alone.constants.Side; -import com.github.fabioticconi.alone.constants.WeaponType; import com.github.fabioticconi.alone.messages.AbstractMessage; import com.github.fabioticconi.alone.messages.CannotMsg; import com.github.fabioticconi.alone.messages.Msg; @@ -193,7 +193,7 @@ else if (keys.get(KeyEvent.VK_T)) { keys.clear(); - final int weaponId = sItems.getWeapon(playerId, EnumSet.allOf(WeaponType.class), true); + final int weaponId = sItems.getWeapon(playerId, EnumSet.allOf(DamageType.class), true); if (weaponId < 0) { @@ -204,6 +204,10 @@ else if (keys.get(KeyEvent.VK_T)) return 0f; } + + Main.pause(); + + screen.select(LookScreen.class); } else if (keys.get(KeyEvent.VK_W)) { diff --git a/src/main/java/com/github/fabioticconi/alone/systems/AttackSystem.java b/src/main/java/com/github/fabioticconi/alone/systems/AttackSystem.java index cf7f624..cc8dfc9 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/AttackSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/AttackSystem.java @@ -25,7 +25,7 @@ import com.github.fabioticconi.alone.components.attributes.Agility; import com.github.fabioticconi.alone.components.attributes.Skin; import com.github.fabioticconi.alone.components.attributes.Strength; -import com.github.fabioticconi.alone.constants.WeaponType; +import com.github.fabioticconi.alone.constants.DamageType; import com.github.fabioticconi.alone.messages.DamageMsg; import com.github.fabioticconi.alone.messages.KillMsg; import com.github.fabioticconi.alone.messages.MissMsg; @@ -35,6 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.EnumSet; import java.util.Random; /** @@ -137,23 +138,28 @@ public void doAction() if (r.nextFloat() < toHit) { - float damage = cStrength.value + 2f; - float armour = tSkin.value; - - // assuming the actor is unharmed, we use a generic "natural" attack - WeaponType dmgType = WeaponType.NATURAL; + float damage = cStrength.value + 2f; + float armour = tSkin.value; + DamageType dmgType = DamageType.NATURAL; // the weapon damage is added to the strength-based one, so that creatures // wielding weapons can overcome stronger, unharmed creatures - final int weaponId = sItem.getWeapon(actorId); + final int weaponId = sItem.getWeapon(actorId, EnumSet.allOf(DamageType.class), true); if (weaponId >= 0) { final Weapon w = mWeapon.get(weaponId); damage += w.damage; dmgType = w.damageType; } + // or maybe we ARE a weapon (eg, if thrown or shot) + else if (mWeapon.has(actorId)) + { + final Weapon w = mWeapon.get(actorId); + damage += w.damage; + dmgType = w.damageType; + } - final int armourId = sItem.getArmour(targetId); + final int armourId = sItem.getArmour(targetId, true); if (armourId >= 0) { final Armour a = mArmour.get(armourId); 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 bcab54e..7d4e288 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/CrushSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/CrushSystem.java @@ -25,7 +25,7 @@ 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; +import com.github.fabioticconi.alone.constants.DamageType; import com.github.fabioticconi.alone.messages.CannotMsg; import com.github.fabioticconi.alone.messages.CrushMsg; import net.mostlyoriginal.api.system.core.PassiveSystem; @@ -77,7 +77,7 @@ public boolean tryAction() if (targetId < 0 || !mCrush.has(targetId)) return false; - final int hammerId = sItem.getWeapon(actorId, EnumSet.of(WeaponType.BLUNT), true); + final int hammerId = sItem.getWeapon(actorId, EnumSet.of(DamageType.BLUNT), true); if (hammerId < 0) { 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 6d675e8..c85da37 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/ItemSystem.java @@ -25,7 +25,7 @@ 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; +import com.github.fabioticconi.alone.constants.DamageType; import com.github.fabioticconi.alone.messages.CannotMsg; import com.github.fabioticconi.alone.messages.DropMsg; import com.github.fabioticconi.alone.messages.EquipMsg; @@ -58,6 +58,7 @@ public class ItemSystem extends PassiveSystem ComponentMapper mArmour; ComponentMapper mName; ComponentMapper mObstacle; + ComponentMapper mAmmo; MessageSystem msg; MapSystem map; @@ -176,6 +177,8 @@ public int makeItem(final String tag) edit.add(template.crushable); if (template.cuttable != null) edit.add(template.cuttable); + if (template.ammo != null) + edit.add(template.ammo); return id; } @@ -209,11 +212,6 @@ public EquipAction equip(final int actorId, final int targetId) return a; } - public int getArmour(final int entityId) - { - return getArmour(entityId, true); - } - public int getArmour(final int entityId, final boolean onlyEquipped) { final Inventory items = mInventory.get(entityId); @@ -233,7 +231,7 @@ public int getArmour(final int entityId, final boolean onlyEquipped) continue; } - // we might only want an equipped weapon + // we might only want a worn armour if (!mArmour.has(itemId) || (onlyEquipped && !mEquip.has(itemId))) continue; @@ -268,17 +266,36 @@ public int getItem(final int entityId, final String tag, final boolean onlyEquip return -1; } - public int getWeapon(final int entityId) + public int getAmmo(final int entityId, final String usableBy) { - return getWeapon(entityId, true); + 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]; + + if (!mAmmo.has(itemId)) + continue; + + final Ammo ammo = mAmmo.get(itemId); + + if (ammo.usableBy.equals(usableBy)) + return itemId; + } + + return -1; } public int getWeapon(final int entityId, final boolean onlyEquipped) { - return getWeapon(entityId, EnumSet.allOf(WeaponType.class), onlyEquipped); + return getWeapon(entityId, EnumSet.allOf(DamageType.class), onlyEquipped); } - public int getWeapon(final int entityId, final EnumSet weaponTypes, final boolean onlyEquipped) + public int getWeapon(final int entityId, final EnumSet damageTypes, final boolean onlyEquipped) { final Inventory items = mInventory.get(entityId); @@ -296,7 +313,7 @@ public int getWeapon(final int entityId, final EnumSet weaponTypes, final Weapon weapon = mWeapon.get(itemId); - if (weaponTypes.contains(weapon.damageType)) + if (damageTypes.contains(weapon.damageType)) return itemId; } @@ -314,6 +331,7 @@ public static class ItemTemplate public Obstacle obstacle; public Crushable crushable; public Cuttable cuttable; + public Ammo ammo; } public class GetAction extends ActionContext 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 0f84a67..973095a 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/ThrowSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/ThrowSystem.java @@ -24,7 +24,7 @@ import com.github.fabioticconi.alone.components.attributes.Agility; import com.github.fabioticconi.alone.components.attributes.Strength; import com.github.fabioticconi.alone.constants.Side; -import com.github.fabioticconi.alone.constants.WeaponType; +import com.github.fabioticconi.alone.constants.DamageType; import com.github.fabioticconi.alone.messages.CannotMsg; import com.github.fabioticconi.alone.messages.ThrowMsg; import com.github.fabioticconi.alone.utils.Coords; @@ -53,6 +53,7 @@ public class ThrowSystem extends PassiveSystem ComponentMapper mAgility; ComponentMapper mName; ComponentMapper mEquip; + ComponentMapper mWeapon; StaminaSystem sStamina; BumpSystem sBump; @@ -73,6 +74,8 @@ public class ThrowAction extends ActionContext { public List path; + float cooldown; + @Override public boolean tryAction() { @@ -99,7 +102,7 @@ public boolean tryAction() if (inventory == null) return false; - final int weaponId = sItem.getWeapon(actorId, EnumSet.allOf(WeaponType.class), true); + int weaponId = sItem.getWeapon(actorId, EnumSet.allOf(DamageType.class), true); if (weaponId < 0) { @@ -108,6 +111,27 @@ public boolean tryAction() return false; } + cooldown = 0.06f; + + final Weapon weapon = mWeapon.get(weaponId); + + if (weapon.damageType == DamageType.SHOOTER) + { + final Name bowName = mName.get(weaponId); + + // the ammo will be thrown at the next step, not the bow + weaponId = sItem.getAmmo(actorId, bowName.tag); + + cooldown = 0.04f; + + if (weaponId < 0) + { + msg.send(actorId, new CannotMsg("shoot", "with only ".concat(bowName.name))); + + return false; + } + } + // it's close enough to strike, so we transform this in a bump action if (Coords.distanceChebyshev(p.x, p.y, t.pos.x, t.pos.y) == 1) { @@ -163,7 +187,6 @@ public void doAction() // how long does it take the object to move one step? // TODO should be based on thrower's strength and weapon characteristics, maybe - final float cooldown = 0.05f; mSpeed.create(weaponId).set(cooldown); mPath.create(weaponId).set(cooldown, path); 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 0945322..ccde487 100644 --- a/src/main/java/com/github/fabioticconi/alone/systems/TreeSystem.java +++ b/src/main/java/com/github/fabioticconi/alone/systems/TreeSystem.java @@ -24,7 +24,7 @@ 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.constants.DamageType; import com.github.fabioticconi.alone.messages.CannotMsg; import com.github.fabioticconi.alone.messages.CutMsg; import net.mostlyoriginal.api.system.core.PassiveSystem; @@ -75,7 +75,7 @@ public boolean tryAction() if (!mCuttable.has(treeId)) return false; - final int axeId = sItem.getWeapon(actorId, EnumSet.of(WeaponType.SLASH), false); + final int axeId = sItem.getWeapon(actorId, EnumSet.of(DamageType.SLASH), false); if (axeId < 0) {