From 5c329e86362d380befe0418482c6c73a363eaa81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 10 May 2014 20:12:39 +0200 Subject: [PATCH] Player can now attack; +base of inventory system --- .../gamecore/util/math/algo/Coord.java | 6 + src/mightypork/rogue/world/Inventory.java | 155 ++++++++++++++++++ src/mightypork/rogue/world/PlayerControl.java | 25 ++- src/mightypork/rogue/world/PlayerInfo.java | 40 ++++- src/mightypork/rogue/world/World.java | 23 +++ src/mightypork/rogue/world/WorldRenderer.java | 4 +- src/mightypork/rogue/world/entity/Entity.java | 1 + .../world/entity/entities/MonsterAi.java | 56 ++----- .../world/entity/entities/PlayerEntity.java | 3 +- .../rogue/world/entity/entities/RatAi.java | 6 +- .../world/entity/entities/RatEntity.java | 3 +- .../entity/modules/EntityModuleHealth.java | 14 ++ .../entity/render/EntityRendererMobLR.java | 2 +- src/mightypork/rogue/world/gui/MapView.java | 3 +- .../rogue/world/gui/interaction/MIPMouse.java | 21 ++- src/mightypork/rogue/world/item/Item.java | 95 +++++++++++ src/mightypork/rogue/world/item/ItemType.java | 7 + .../rogue/world/item/items/ItemBaseFood.java | 37 +++++ .../world/item/items/ItemBaseWeapon.java | 37 +++++ .../rogue/world/item/items/ItemMeat.java | 10 +- src/mightypork/rogue/world/level/Level.java | 47 +++++- 21 files changed, 522 insertions(+), 73 deletions(-) create mode 100644 src/mightypork/rogue/world/Inventory.java create mode 100644 src/mightypork/rogue/world/item/ItemType.java create mode 100644 src/mightypork/rogue/world/item/items/ItemBaseFood.java create mode 100644 src/mightypork/rogue/world/item/items/ItemBaseWeapon.java diff --git a/src/mightypork/gamecore/util/math/algo/Coord.java b/src/mightypork/gamecore/util/math/algo/Coord.java index 9a33e62..a09e839 100644 --- a/src/mightypork/gamecore/util/math/algo/Coord.java +++ b/src/mightypork/gamecore/util/math/algo/Coord.java @@ -201,4 +201,10 @@ public class Coord implements IonObjBundled, IonObjBinary { { return ION_MARK; } + + + public static Coord fromVect(Vect vect) + { + return make((int) Math.floor(vect.x()), (int) Math.floor(vect.y())); + } } diff --git a/src/mightypork/rogue/world/Inventory.java b/src/mightypork/rogue/world/Inventory.java new file mode 100644 index 0000000..4f6227a --- /dev/null +++ b/src/mightypork/rogue/world/Inventory.java @@ -0,0 +1,155 @@ +package mightypork.rogue.world; + + +import java.io.IOException; + +import mightypork.gamecore.util.ion.IonInput; +import mightypork.gamecore.util.ion.IonObjBinary; +import mightypork.gamecore.util.ion.IonOutput; +import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.item.Items; + + +public class Inventory implements IonObjBinary { + + private static final short ION_MARK = 0; + private Item[] items; + + + public Inventory(int size) + { + this.items = new Item[size]; + } + + + public Inventory() + { + // ION constructor + } + + + @Override + public void load(IonInput in) throws IOException + { + final int size = in.readIntByte(); + items = new Item[size]; + + // for all items in sequence + while (in.hasNextEntry()) { + + // load item index + final int i = in.readIntByte(); + + // load item + setItem(i, Items.loadItem(in)); + } + } + + + @Override + public void save(IonOutput out) throws IOException + { + // write length + out.writeIntByte(getSize()); + + // find items that are writable + for (int i = 0; i < getSize(); i++) { + final Item item = getItem(i); + if (item != null && !item.isEmpty()) { + + // start sequence entry + out.startEntry(); + + // write index + out.writeIntByte(i); + + // write item at index + Items.saveItem(out, item); + } + } + // close sequence + out.endSequence(); + } + + + @Override + public short getIonMark() + { + return ION_MARK; + } + + + /** + * Get item in a slot + * + * @param i slot number + * @return item in the slot; can be null. + */ + public Item getItem(int i) + { + verifyIndex(i); + final Item itm = items[i]; + if (itm == null || itm.isEmpty()) return null; + return itm; + } + + + private void verifyIndex(int i) + { + if (i < 0 || i > getSize()) { + throw new IndexOutOfBoundsException("Invalid inventory index: " + i + ", size: " + getSize()); + } + } + + + /** + * Put item in a slot + * + * @param i slot number + * @param item item to store + */ + public void setItem(int i, Item item) + { + verifyIndex(i); + items[i] = item; + } + + + /** + * @return inventory size + */ + public int getSize() + { + return items.length; + } + + + /** + * Add an item, try to merge first. + * + * @param stored stored item + * @return true if the item was entirely added, and is now empty. + */ + public boolean addItem(Item stored) + { + // try to merge with another item + for (int i = 0; i < getSize(); i++) { + final Item itm = getItem(i); + if (itm != null && itm.canStackWith(stored)) { + if (itm.addItem(stored)) return true; + } + } + + // try to place in a free slot + for (int i = 0; i < getSize(); i++) { + final Item itm = getItem(i); + if (itm == null) { + setItem(i, stored.split(stored.getAmount())); // store a copy, empty the original item. + return true; + } + } + + // could not insert. + return false; + } +} diff --git a/src/mightypork/rogue/world/PlayerControl.java b/src/mightypork/rogue/world/PlayerControl.java index c7809fe..b2657e3 100644 --- a/src/mightypork/rogue/world/PlayerControl.java +++ b/src/mightypork/rogue/world/PlayerControl.java @@ -6,7 +6,10 @@ import java.util.Set; import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.algo.Step; +import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.world.World.PlayerFacade; +import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.entity.EntityType; import mightypork.rogue.world.entity.modules.EntityMoveListener; import mightypork.rogue.world.level.Level; @@ -96,15 +99,29 @@ public abstract class PlayerControl { */ public boolean clickTile(Step side) { - return clickTile(getPlayer().getCoord().add(side)); + return clickTile(getPlayer().getCoord().add(side).toVect().add(0.5, 0.5)); } - public boolean clickTile(Coord pos) + public boolean clickTile(Vect pos) { - if (pos.dist(getPlayer().getCoord()) > 8) return false; // too far + //if (pos.dist(getPlayer().getVisualPos()).value() > 8) return false; // too far - return getLevel().getTile(pos).onClick(); + if (pos.dist(getPlayer().getVisualPos().add(0.5, 0.5)).value() < 1.5) { + + // 1st try to hit entity + final Entity prey = getLevel().getClosestEntity(pos, EntityType.MONSTER, 1.2); + if (prey != null) { + prey.receiveAttack(getPlayer().getEntity(), getPlayer().getAttackStrength()); + return true; + } + + //2nd try to click tile + System.out.println("do click: " + Coord.fromVect(pos)); + return getLevel().getTile(Coord.fromVect(pos)).onClick(); + } + + return false; } diff --git a/src/mightypork/rogue/world/PlayerInfo.java b/src/mightypork/rogue/world/PlayerInfo.java index e09675f..c87aa4c 100644 --- a/src/mightypork/rogue/world/PlayerInfo.java +++ b/src/mightypork/rogue/world/PlayerInfo.java @@ -2,21 +2,35 @@ package mightypork.rogue.world; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import mightypork.gamecore.util.ion.IonBundle; import mightypork.gamecore.util.ion.IonObjBundled; import mightypork.rogue.world.item.Item; +/** + * Player information stored in world. + * + * @author MightyPork + */ public class PlayerInfo implements IonObjBundled { + /** Player inventory size */ + private static final int INV_SIZE = 8; + + /** Constant indicating that no weapon is selected. */ + private static final int NO_WEAPON = -1; + + /** Attack str with bare hands */ + public static final int BARE_ATTACK = 1; + + private int eid = -1; // marks not initialized private int level; - private final List inventory = new ArrayList<>(); - private int selectedWeapon = -1; + private Inventory inventory = new Inventory(INV_SIZE); + + private int selectedWeapon = NO_WEAPON; @Override @@ -25,7 +39,8 @@ public class PlayerInfo implements IonObjBundled { eid = bundle.get("eid", eid); level = bundle.get("floor", level); selectedWeapon = bundle.get("weapon", selectedWeapon); - bundle.loadSequence("inv", inventory); + + inventory = bundle.get("inv", inventory); } @@ -35,7 +50,7 @@ public class PlayerInfo implements IonObjBundled { bundle.put("eid", eid); bundle.put("floor", level); bundle.put("weapon", selectedWeapon); - bundle.putSequence("inv", inventory); + bundle.put("inv", inventory); } @@ -69,4 +84,17 @@ public class PlayerInfo implements IonObjBundled { return eid != -1; } + + public Inventory getInventory() + { + return inventory; + } + + + public Item getEquippedWeapon() + { + if (selectedWeapon == NO_WEAPON) return null; + return inventory.getItem(selectedWeapon); + } + } diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java index 89ba5c2..153e016 100644 --- a/src/mightypork/rogue/world/World.java +++ b/src/mightypork/rogue/world/World.java @@ -17,6 +17,7 @@ import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.gamecore.util.math.timing.Pauseable; import mightypork.rogue.world.entity.Entities; import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.item.Item; import mightypork.rogue.world.level.Level; @@ -155,6 +156,28 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea return playerEntity.health.getMaxHealth(); } + + public Inventory getInventory() + { + return playerInfo.getInventory(); + } + + + public Entity getEntity() + { + return playerEntity; + } + + + public int getAttackStrength() + { + final Item weapon = playerInfo.getEquippedWeapon(); + + if (weapon == null) return PlayerInfo.BARE_ATTACK; + + return Math.min(weapon.getAttackPoints(), playerInfo.BARE_ATTACK); + } + } // not saved stuffs diff --git a/src/mightypork/rogue/world/WorldRenderer.java b/src/mightypork/rogue/world/WorldRenderer.java index cb5ba1f..5457ed4 100644 --- a/src/mightypork/rogue/world/WorldRenderer.java +++ b/src/mightypork/rogue/world/WorldRenderer.java @@ -153,11 +153,11 @@ public class WorldRenderer extends RectProxy { } - public Coord getClickedTile(Vect clickPos) + public Vect getClickedTile(Vect clickPos) { final int ts = (int) tileSize.value(); final Vect v = clickPos.sub(center().add(getOffset().mul(ts))); - return new Coord(v.xi() / ts, v.yi() / ts); + return Vect.make(v.x() / ts, v.y() / ts); } } diff --git a/src/mightypork/rogue/world/entity/Entity.java b/src/mightypork/rogue/world/entity/Entity.java index e87e7da..25304ca 100644 --- a/src/mightypork/rogue/world/entity/Entity.java +++ b/src/mightypork/rogue/world/entity/Entity.java @@ -203,6 +203,7 @@ public abstract class Entity implements IonObjBundled, Updateable { @DefaultImpl public void onKilled() { + getLevel().freeTile(getCoord()); } diff --git a/src/mightypork/rogue/world/entity/entities/MonsterAi.java b/src/mightypork/rogue/world/entity/entities/MonsterAi.java index 1dbc24c..c8691ae 100644 --- a/src/mightypork/rogue/world/entity/entities/MonsterAi.java +++ b/src/mightypork/rogue/world/entity/entities/MonsterAi.java @@ -29,7 +29,6 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { public void run() { if (chasing) return; - //System.out.println("Mob looks around."); lookForTarget(); } }; @@ -40,7 +39,6 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { public void run() { if (chasing) return; - //System.out.println("Mob going to sleep"); sleeping = true; } }; @@ -54,12 +52,8 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { final Entity prey = getPreyEntity(); - if (prey == null || prey.isDead()) { - //System.out.println("prey dead?"); - return; - } + if (prey == null || prey.isDead()) return; - //System.out.println("Timed prey attack"); attackPrey(prey); } }; @@ -98,12 +92,13 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { @Override public void onStepFinished() { + if (entity.isDead()) return; + //System.out.println("monster ai step finished."); if (chasing) { //System.out.println("chasing.."); final Entity prey = getPreyEntity(); if (!isPreyValid(prey)) { - //System.out.println("prey dead or null, stop chasing: " + prey + ", prey.isdead " + prey.isDead()); stopChasing(); return; } @@ -113,14 +108,9 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { return; } - if (isPreyInAttackRange(prey)) { - //System.out.println("prey in attack range"); - return; // attacking - } else { + if (!isPreyInAttackRange(prey)) { stepTowardsPrey(prey); } - } else { - //System.out.println("not chasing."); } } @@ -175,6 +165,8 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { @Override public void update(double delta) { + if (entity.isDead()) return; + timerFindPrey.update(delta); timerSleepStart.update(delta); timerAttack.update(delta); @@ -188,7 +180,6 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { } if (!isPreyInAttackRange(prey)) { - //System.out.println("-upd STEP--"); stepTowardsPrey(prey); } } @@ -203,22 +194,17 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { private void lookForTarget() { - if (shouldSkipScan()) return; // not hungry right now + if (entity.isDead()) return; - //System.out.println("- Lookin for prey, r=" + getScanRadius()); + if (shouldSkipScan()) return; // not hungry right now - final Entity prey = entity.getLevel().getClosestEntity(entity, EntityType.PLAYER, getScanRadius()); + final Entity prey = entity.getLevel().getClosestEntity(entity.pos.getVisualPos(), EntityType.PLAYER, getScanRadius()); if (prey != null) { - //System.out.println("-- Prey in sight: " + prey); - //System.out.println("-- path would be: " + entity.getCoord() + "->" + prey.getCoord()); // check if reachable without leaving room final List noDoorPath = noDoorPf.findPath(entity.getCoord(), prey.getCoord()); - if (noDoorPath == null) { - //System.out.println("-- Could not navigate to prey, aborting."); - return; // cant reach, give up - } + if (noDoorPath == null) return; // cant reach, give up startChasing(prey); } @@ -245,7 +231,7 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { private void startChasing(Entity prey) { - //System.out.println("start chasing"); + if (entity.isDead()) return; preyId = prey.getEntityId(); chasing = true; @@ -263,7 +249,6 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { private void stopChasing() { - //System.out.println("stop chasing."); chasing = false; preyId = -1; timerSleepStart.restart(); @@ -281,15 +266,12 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { private void stepTowardsPrey(Entity prey) { - //System.out.println("stepTowardsPrey"); - if (!isPreyValid(prey)) { - //System.out.println("prey dead?"); - return; - } + if (entity.isDead()) return; + + if (!isPreyValid(prey)) return; // if close enough if (isPreyInAttackRange(prey)) { - //System.out.println("attack"); attackPrey(prey); return; } @@ -297,7 +279,6 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { final List preyPath = getPathToPrey(prey); if (preyPath == null || preyPath.size() > getPreyAbandonDistance()) { - //System.out.println("no path"); stopChasing(); return; } @@ -309,10 +290,9 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { private void attackPrey(Entity prey) { - if (!isPreyInAttackRange(prey)) { - //System.out.println("prey out of attack range, cant attack"); - return; - } + if (entity.isDead()) return; + + if (!isPreyInAttackRange(prey)) return; prey.receiveAttack(entity, getAttackStrength()); } @@ -353,7 +333,7 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { @DefaultImpl protected double getAttackDistance() { - return 1.6; + return 1; } diff --git a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java index c6f519c..380fb3c 100644 --- a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java +++ b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java @@ -22,10 +22,11 @@ public class PlayerEntity extends Entity { public PlayerAi(Entity entity) { super(entity); - setDespawnDelay(3); + setDespawnDelay(2); health.setMaxHealth(12); health.fill(); // fill health bar to max + health.setHitCooldownTime(0.5); } diff --git a/src/mightypork/rogue/world/entity/entities/RatAi.java b/src/mightypork/rogue/world/entity/entities/RatAi.java index 3af9d6a..2ebe289 100644 --- a/src/mightypork/rogue/world/entity/entities/RatAi.java +++ b/src/mightypork/rogue/world/entity/entities/RatAi.java @@ -10,8 +10,8 @@ public class RatAi extends MonsterAi { { super(entity); - setAttackTime(1); - setScanTime(1); + setAttackTime(1.2); + setScanTime(1.5); setSleepTime(10); } @@ -26,7 +26,7 @@ public class RatAi extends MonsterAi { @Override protected double getAttackDistance() { - return 1.42; + return 1.43; } diff --git a/src/mightypork/rogue/world/entity/entities/RatEntity.java b/src/mightypork/rogue/world/entity/entities/RatEntity.java index 7eb6058..dda6f41 100644 --- a/src/mightypork/rogue/world/entity/entities/RatEntity.java +++ b/src/mightypork/rogue/world/entity/entities/RatEntity.java @@ -28,10 +28,11 @@ public class RatEntity extends Entity { pos.addMoveListener(ai); pos.setStepTime(0.5); - setDespawnDelay(3); + setDespawnDelay(2); health.setMaxHealth(3 + rand.nextInt(3)); health.fill(); // fill health bar to max + health.setHitCooldownTime(0.2); } diff --git a/src/mightypork/rogue/world/entity/modules/EntityModuleHealth.java b/src/mightypork/rogue/world/entity/modules/EntityModuleHealth.java index 6c10706..2d26d79 100644 --- a/src/mightypork/rogue/world/entity/modules/EntityModuleHealth.java +++ b/src/mightypork/rogue/world/entity/modules/EntityModuleHealth.java @@ -19,6 +19,7 @@ public class EntityModuleHealth extends EntityModule { protected int health = 1; protected int maxHealth = 1; + private double hitCooldownTime = 0.3; protected boolean dead = false; private double timeSinceLastDamage = Integer.MAX_VALUE; @@ -94,6 +95,8 @@ public class EntityModuleHealth extends EntityModule { public void receiveDamage(int attackStrength) { + if (timeSinceLastDamage < hitCooldownTime) return; + setHealth(health - attackStrength); timeSinceLastDamage = 0; } @@ -119,4 +122,15 @@ public class EntityModuleHealth extends EntityModule { { return timeSinceLastDamage; } + + + /** + * Set how long after hit another hit can be received. + * + * @param secs + */ + public void setHitCooldownTime(double secs) + { + this.hitCooldownTime = secs; + } } diff --git a/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java b/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java index 8675dba..c99263e 100644 --- a/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java +++ b/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java @@ -70,7 +70,7 @@ public class EntityRendererMobLR extends EntityRenderer { final double hw = spriteRect.width().half().value(); - Render.quadTextured(Vect.ZERO.expand(hw, hw, hw, hw), q, hue.withAlpha(entity.isDead() ? 1 - hurtTime / 3 : 1)); + Render.quadTextured(Vect.ZERO.expand(hw, hw, hw, hw), q, hue.withAlpha(entity.isDead() ? 1 - hurtTime / 2 : 1)); Render.popMatrix(); } } diff --git a/src/mightypork/rogue/world/gui/MapView.java b/src/mightypork/rogue/world/gui/MapView.java index 0f87a1d..03d0398 100644 --- a/src/mightypork/rogue/world/gui/MapView.java +++ b/src/mightypork/rogue/world/gui/MapView.java @@ -15,7 +15,6 @@ import mightypork.gamecore.input.events.MouseButtonEvent; import mightypork.gamecore.input.events.MouseButtonListener; import mightypork.gamecore.render.Render; import mightypork.gamecore.util.math.Easing; -import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.color.Color; import mightypork.gamecore.util.math.color.pal.RGB; import mightypork.gamecore.util.math.constraints.num.Num; @@ -121,7 +120,7 @@ public class MapView extends InputComponent implements DelegatingClient, KeyList * @param pos position on screen (px) * @return position on map (tiles) */ - public Coord toWorldPos(Vect pos) + public Vect toWorldPos(Vect pos) { return worldRenderer.getClickedTile(pos); } diff --git a/src/mightypork/rogue/world/gui/interaction/MIPMouse.java b/src/mightypork/rogue/world/gui/interaction/MIPMouse.java index 644eee9..30ea859 100644 --- a/src/mightypork/rogue/world/gui/interaction/MIPMouse.java +++ b/src/mightypork/rogue/world/gui/interaction/MIPMouse.java @@ -45,13 +45,19 @@ public class MIPMouse extends MapInteractionPlugin implements PlayerStepEndListe { if (isImmobile()) return false; - final Coord pos = mapView.toWorldPos(mouse); - final Tile t = mapView.plc.getLevel().getTile(pos); - - if (button == BTN && !down && t.onClick()) { - return true; + final Vect pos = mapView.toWorldPos(mouse); + + if (button == BTN && !down) { + // try to click tile + + System.out.println("---"); + System.out.println("Standing at: " + getPlayer().getCoord() + ", visual " + getPlayer().getVisualPos()); + System.out.println("Click tile: " + pos + ", floored: " + pos.floor()); + + if (mapView.plc.clickTile(pos)) return true; } + final Tile t = mapView.plc.getLevel().getTile(Coord.fromVect(pos)); if (button == 1 && !down && t.isWalkable()) { if (troToNav(mouse)) return true; return mouseWalk(mouse); @@ -66,7 +72,8 @@ public class MIPMouse extends MapInteractionPlugin implements PlayerStepEndListe if (isImmobile()) return false; final Coord plpos = mapView.plc.getPlayer().getCoord(); - final Coord clicked = mapView.toWorldPos(mouse); + + final Coord clicked = Coord.fromVect(mapView.toWorldPos(mouse)); if (clicked.equals(plpos)) return false; final Tile t = mapView.plc.getLevel().getTile(clicked); @@ -82,7 +89,7 @@ public class MIPMouse extends MapInteractionPlugin implements PlayerStepEndListe if (isImmobile()) return false; final Coord plpos = mapView.plc.getPlayer().getCoord(); - final Coord clicked = mapView.toWorldPos(pos); + final Coord clicked = Coord.fromVect(mapView.toWorldPos(pos)); if (clicked.equals(plpos)) return false; final Polar p = Polar.fromCoord(clicked.x - plpos.x, clicked.y - plpos.y); diff --git a/src/mightypork/rogue/world/item/Item.java b/src/mightypork/rogue/world/item/Item.java index 2fb41bf..f11545d 100644 --- a/src/mightypork/rogue/world/item/Item.java +++ b/src/mightypork/rogue/world/item/Item.java @@ -14,6 +14,7 @@ public abstract class Item implements IonObjBlob { private final ItemModel model; private ItemRenderer renderer; + private int amount = 1; public Item(ItemModel model) @@ -39,6 +40,7 @@ public abstract class Item implements IonObjBlob { @DefaultImpl public void save(IonOutput out) throws IOException { + out.writeIntShort(amount); } @@ -46,6 +48,7 @@ public abstract class Item implements IonObjBlob { @DefaultImpl public void load(IonInput in) throws IOException { + amount = in.readIntShort(); } @@ -53,4 +56,96 @@ public abstract class Item implements IonObjBlob { { return model; } + + + @DefaultImpl + protected int getMaxStackSize() + { + return isStackable() ? 1 : 65535; + } + + + public boolean canStackWith(Item other) + { + return (getModel().id == other.getModel().id) && isStackable(); + } + + + public abstract boolean isStackable(); + + + /** + * Add another item to this item + * + * @param added added item + * @return if items are compatible and the added item was completely moved + * to this item, returns true and the added item should be + * discarded. + */ + public boolean addItem(Item added) + { + if (!canStackWith(added)) return false; + + final int room = getMaxStackSize() - this.amount; + final int avail = added.amount; + + final int moved = Math.min(room, avail); + this.amount += moved; + added.amount -= moved; + + return added.isEmpty(); + } + + + /** + * @return item amount in the stack + */ + public int getAmount() + { + return Math.max(0, amount); + } + + + /** + * Consume one item. + * + * @return true if the item is fully consumed and should be removed. + */ + public boolean consume() + { + if (isEmpty()) throw new RuntimeException("Item is empty, cannot consume."); + + amount--; + + return isEmpty(); + } + + + public boolean isEmpty() + { + return getAmount() == 0; + } + + + public Item split(int removed) + { + if (isEmpty()) throw new RuntimeException("Item is empty, cannot split."); + + final int realRemoved = Math.min(removed, amount); + + final Item newItm = model.createItem(); + newItm.amount = realRemoved; + this.amount -= realRemoved; + + return newItm; + } + + + public abstract int getAttackPoints(); + + + public abstract int getFoodPoints(); + + + public abstract ItemType getType(); } diff --git a/src/mightypork/rogue/world/item/ItemType.java b/src/mightypork/rogue/world/item/ItemType.java new file mode 100644 index 0000000..bc36cd9 --- /dev/null +++ b/src/mightypork/rogue/world/item/ItemType.java @@ -0,0 +1,7 @@ +package mightypork.rogue.world.item; + + +public enum ItemType +{ + FOOD, WEAPON; +} diff --git a/src/mightypork/rogue/world/item/items/ItemBaseFood.java b/src/mightypork/rogue/world/item/items/ItemBaseFood.java new file mode 100644 index 0000000..dcfa2d8 --- /dev/null +++ b/src/mightypork/rogue/world/item/items/ItemBaseFood.java @@ -0,0 +1,37 @@ +package mightypork.rogue.world.item.items; + + +import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemType; + + +public abstract class ItemBaseFood extends Item { + + public ItemBaseFood(ItemModel model) + { + super(model); + } + + + @Override + public boolean isStackable() + { + return true; + } + + + @Override + public int getAttackPoints() + { + return 0; + } + + + @Override + public ItemType getType() + { + return ItemType.FOOD; + } + +} diff --git a/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java b/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java new file mode 100644 index 0000000..1b6e474 --- /dev/null +++ b/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java @@ -0,0 +1,37 @@ +package mightypork.rogue.world.item.items; + + +import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemType; + + +public abstract class ItemBaseWeapon extends Item { + + public ItemBaseWeapon(ItemModel model) + { + super(model); + } + + + @Override + public boolean isStackable() + { + return false; + } + + + @Override + public int getFoodPoints() + { + return 0; + } + + + @Override + public ItemType getType() + { + return ItemType.WEAPON; + } + +} diff --git a/src/mightypork/rogue/world/item/items/ItemMeat.java b/src/mightypork/rogue/world/item/items/ItemMeat.java index 688a81c..24758c0 100644 --- a/src/mightypork/rogue/world/item/items/ItemMeat.java +++ b/src/mightypork/rogue/world/item/items/ItemMeat.java @@ -2,13 +2,12 @@ package mightypork.rogue.world.item.items; import mightypork.rogue.Res; -import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.ItemModel; import mightypork.rogue.world.item.ItemRenderer; import mightypork.rogue.world.item.render.QuadItemRenderer; -public class ItemMeat extends Item { +public class ItemMeat extends ItemBaseFood { public ItemMeat(ItemModel model) { @@ -22,4 +21,11 @@ public class ItemMeat extends Item { return new QuadItemRenderer(Res.txq("item.meat")); } + + @Override + public int getFoodPoints() + { + return 4; + } + } diff --git a/src/mightypork/rogue/world/level/Level.java b/src/mightypork/rogue/world/level/Level.java index 2046f9c..5c5dcbc 100644 --- a/src/mightypork/rogue/world/level/Level.java +++ b/src/mightypork/rogue/world/level/Level.java @@ -19,6 +19,7 @@ import mightypork.gamecore.util.math.algo.Sides; import mightypork.gamecore.util.math.algo.Step; import mightypork.gamecore.util.math.algo.floodfill.FillContext; import mightypork.gamecore.util.math.algo.floodfill.FloodFill; +import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.gamecore.util.math.noise.NoiseGen; import mightypork.rogue.world.World; import mightypork.rogue.world.entity.Entities; @@ -37,7 +38,29 @@ import mightypork.rogue.world.tile.Tiles; */ public class Level implements BusAccess, Updateable, DelegatingClient, ToggleableClient, IonObjBinary { + private static class EntityRenderComparator implements Comparator { + + @Override + public int compare(Entity o1, Entity o2) + { + if (o1.isDead() && !o2.isDead()) { + return -1; + } + + if (!o1.isDead() && o2.isDead()) { + return 1; + } + + int c = Double.compare(o1.pos.getVisualPos().y(), o1.pos.getVisualPos().y()); + if (c == 0) c = Double.compare(o1.pos.getVisualPos().x(), o1.pos.getVisualPos().x()); + + return c; + } + + } + public static final int ION_MARK = 53; + private static final Comparator ENTITY_RENDER_CMP = new EntityRenderComparator(); private final Coord size = Coord.zero(); private World world; @@ -49,7 +72,7 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl private Tile[][] tiles; private final Map entityMap = new HashMap<>(); - private final Set entitySet = new HashSet<>(); + private final List entitySet = new LinkedList<>(); private int playerCount = 0; @@ -57,6 +80,7 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl public long seed; private transient NoiseGen noiseGen; + private double timeSinceLastEntitySort; public Level() @@ -231,6 +255,13 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl @Override public void update(double delta) { + timeSinceLastEntitySort += delta; + + if (timeSinceLastEntitySort > 0.2) { + Collections.sort(entitySet, ENTITY_RENDER_CMP); + timeSinceLastEntitySort = 0; + } + // just update them all for (final Coord c = Coord.zero(); c.x < size.x; c.x++) { for (c.y = 0; c.y < size.y; c.y++) { @@ -505,23 +536,27 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl /** * Get entity of type closest to coord * - * @param self the querying entity - to provide position, and to be excluded - * from the search. + * @param pos the attack origin * @param type wanted entity type * @param radius search radius; -1 for unlimited. * @return */ - public Entity getClosestEntity(Entity self, EntityType type, double radius) + public Entity getClosestEntity(Vect pos, EntityType type, double radius) { Entity closest = null; double minDist = Double.MAX_VALUE; + if (type == EntityType.MONSTER) System.out.println("Finding entity in range " + radius + " of " + pos); + for (final Entity e : entitySet) { - if (e == self) continue; if (e.isDead()) continue; if (e.getType() == type) { - final double dist = e.getCoord().dist(self.getCoord()); + final double dist = e.pos.getVisualPos().dist(pos).value(); + + if (type == EntityType.MONSTER && dist < radius * 2) { + System.out.println("Entity " + e + ", dist: " + dist + ", standing at: " + e.pos.getCoord() + ", visual: " + e.pos.getVisualPos()); + } if (dist <= radius && dist < minDist) { minDist = dist;