From c13a624520dc328e6d865963a9e47bf9179c3c0e Mon Sep 17 00:00:00 2001 From: ondra Date: Sun, 11 May 2014 18:47:16 +0200 Subject: [PATCH] More items, bugfixes --- res/font/TinyUnicode.ttf | Bin 0 -> 23296 bytes res/font/TinyUnicode2.ttf | Bin 0 -> 15852 bytes res/font/battlenet.ttf | Bin 0 -> 21004 bytes res/img/items.png | Bin 519 -> 2136 bytes res/img/items.xcf | Bin 20810 -> 4939 bytes .../gamecore/eventbus/clients/BusNode.java | 2 +- ...isualComponent.java => BaseComponent.java} | 28 ++- .../gamecore/gui/components/Component.java | 24 +- .../gui/components/InputComponent.java | 21 +- .../gui/components/LayoutComponent.java | 31 ++- .../gui/components/painters/ImagePainter.java | 4 +- .../gui/components/painters/QuadPainter.java | 4 +- .../gui/components/painters/TextPainter.java | 40 ++- .../gamecore/gui/screens/LayeredScreen.java | 5 + .../gamecore/gui/screens/Overlay.java | 22 +- .../resources/fonts/FontRenderer.java | 13 +- .../resources/fonts/impl/CachedFont.java | 26 +- .../resources/fonts/impl/DeferredFont.java | 1 + src/mightypork/gamecore/util/math/Calc.java | 47 ++++ .../util/math/constraints/rect/Rect.java | 2 +- .../gamecore/util/strings/StringUtils.java | 39 --- src/mightypork/rogue/Config.java | 6 + src/mightypork/rogue/Res.java | 18 +- .../rogue/screens/game/HeartBar.java | 17 +- .../rogue/screens/game/HudLayer.java | 54 +++-- .../rogue/screens/game/InvLayer.java | 56 +++-- .../rogue/screens/game/InvSlot.java | 69 ++++-- .../rogue/screens/game/NavButton.java | 2 + .../rogue/screens/game/ScreenGame.java | 29 ++- .../screens/game/WorldConsoleRenderer.java | 62 +++++ .../screens/test_bouncyboxes/BouncyBox.java | 4 +- src/mightypork/rogue/world/Inventory.java | 25 +- src/mightypork/rogue/world/PlayerControl.java | 31 +-- src/mightypork/rogue/world/PlayerInfo.java | 6 +- src/mightypork/rogue/world/World.java | 229 ++++++++++++++++-- src/mightypork/rogue/world/WorldConsole.java | 103 ++++++++ src/mightypork/rogue/world/WorldCreator.java | 2 +- src/mightypork/rogue/world/entity/Entity.java | 7 +- .../entity/{render => }/EntityRenderer.java | 2 +- .../world/entity/entities/MonsterAi.java | 3 - .../world/entity/entities/PlayerEntity.java | 25 +- .../world/entity/entities/RatEntity.java | 34 ++- .../entity/render/EntityRendererMobLR.java | 1 + .../rogue/world/gen/LevelGenerator.java | 40 +-- .../rogue/world/gen/ScratchMap.java | 50 ++-- .../rogue/world/gen/rooms/SecretRoom.java | 29 ++- src/mightypork/rogue/world/gui/MapView.java | 2 + .../rogue/world/gui/interaction/MIPMouse.java | 19 +- src/mightypork/rogue/world/item/Item.java | 34 ++- .../rogue/world/item/ItemModel.java | 12 +- .../rogue/world/item/ItemRenderer.java | 10 + src/mightypork/rogue/world/item/Items.java | 16 +- .../rogue/world/item/items/ItemBaseFood.java | 11 + .../world/item/items/ItemBaseWeapon.java | 6 + .../world/item/items/food/ItemCheese.java | 38 +++ .../world/item/items/{ => food}/ItemMeat.java | 13 +- .../world/item/items/food/ItemSandwich.java | 38 +++ .../world/item/items/weapons/ItemBone.java | 39 +++ .../world/item/items/weapons/ItemClub.java | 39 +++ .../world/item/items/weapons/ItemHammer.java | 39 +++ .../world/item/items/weapons/ItemStone.java | 39 +++ .../world/item/items/weapons/ItemSword.java | 39 +++ .../world/item/render/QuadItemRenderer.java | 37 ++- src/mightypork/rogue/world/level/Level.java | 13 +- src/mightypork/rogue/world/tile/Tile.java | 3 +- .../rogue/world/tile/TileRenderer.java | 3 + .../world/tile/tiles/TileBaseSecretDoor.java | 5 +- .../rogue/world/tile/tiles/TileWithItems.java | 34 +-- 68 files changed, 1330 insertions(+), 372 deletions(-) create mode 100644 res/font/TinyUnicode.ttf create mode 100644 res/font/TinyUnicode2.ttf create mode 100644 res/font/battlenet.ttf rename src/mightypork/gamecore/gui/components/{VisualComponent.java => BaseComponent.java} (80%) create mode 100644 src/mightypork/rogue/screens/game/WorldConsoleRenderer.java create mode 100644 src/mightypork/rogue/world/WorldConsole.java rename src/mightypork/rogue/world/entity/{render => }/EntityRenderer.java (78%) create mode 100644 src/mightypork/rogue/world/item/items/food/ItemCheese.java rename src/mightypork/rogue/world/item/items/{ => food}/ItemMeat.java (61%) create mode 100644 src/mightypork/rogue/world/item/items/food/ItemSandwich.java create mode 100644 src/mightypork/rogue/world/item/items/weapons/ItemBone.java create mode 100644 src/mightypork/rogue/world/item/items/weapons/ItemClub.java create mode 100644 src/mightypork/rogue/world/item/items/weapons/ItemHammer.java create mode 100644 src/mightypork/rogue/world/item/items/weapons/ItemStone.java create mode 100644 src/mightypork/rogue/world/item/items/weapons/ItemSword.java diff --git a/res/font/TinyUnicode.ttf b/res/font/TinyUnicode.ttf new file mode 100644 index 0000000000000000000000000000000000000000..524ec80170ff2b105710fb1f4ac27bbcb47057d0 GIT binary patch literal 23296 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 literal 0 HcmV?d00001 diff --git a/res/font/battlenet.ttf b/res/font/battlenet.ttf new file mode 100644 index 0000000000000000000000000000000000000000..18f85a79f49dcaa050d4d646f97bddd9052d421b GIT binary patch literal 21004 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 diff --git a/res/img/items.xcf b/res/img/items.xcf index df5c4c7c76f70c30b34f57e497fb2a0cdf7b44db..73eb70f11141dc2eae21e42f46d8bdbcd11d4a51 100644 GIT binary patch literal 4939 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 0) disableLevel--; + } else { + disableLevel++; + } + } + + + @Override + public boolean isEnabled() + { + return disableLevel == 0; + } } diff --git a/src/mightypork/gamecore/gui/components/Component.java b/src/mightypork/gamecore/gui/components/Component.java index b101316..2e3f9a6 100644 --- a/src/mightypork/gamecore/gui/components/Component.java +++ b/src/mightypork/gamecore/gui/components/Component.java @@ -1,9 +1,8 @@ package mightypork.gamecore.gui.components; +import mightypork.gamecore.gui.Enableable; import mightypork.gamecore.gui.Hideable; -import mightypork.gamecore.util.math.constraints.rect.Rect; -import mightypork.gamecore.util.math.constraints.rect.proxy.RectBound; /** @@ -11,26 +10,7 @@ import mightypork.gamecore.util.math.constraints.rect.proxy.RectBound; * * @author MightyPork */ -public interface Component extends Hideable, PluggableRenderable { - - /** - * Set visible. When not visible, the component should not render. - */ - @Override - void setVisible(boolean yes); - - - @Override - boolean isVisible(); - - - @Override - Rect getRect(); - - - @Override - void setRect(RectBound rect); - +public interface Component extends Enableable, Hideable, PluggableRenderable { /** * Render the component, if it is visible. diff --git a/src/mightypork/gamecore/gui/components/InputComponent.java b/src/mightypork/gamecore/gui/components/InputComponent.java index b1f6ea9..d4844a1 100644 --- a/src/mightypork/gamecore/gui/components/InputComponent.java +++ b/src/mightypork/gamecore/gui/components/InputComponent.java @@ -5,25 +5,8 @@ import mightypork.gamecore.eventbus.clients.ToggleableClient; import mightypork.gamecore.gui.Enableable; -public abstract class InputComponent extends VisualComponent implements Enableable, ToggleableClient { - - private boolean enabled = true; - - - @Override - public void enable(boolean yes) - { - this.enabled = yes; - } - - - @Override - public boolean isEnabled() - { - return enabled && isVisible(); - } - - +public abstract class InputComponent extends BaseComponent implements Enableable, ToggleableClient { + @Override public boolean isListening() { diff --git a/src/mightypork/gamecore/gui/components/LayoutComponent.java b/src/mightypork/gamecore/gui/components/LayoutComponent.java index bcec65a..b2b0bb5 100644 --- a/src/mightypork/gamecore/gui/components/LayoutComponent.java +++ b/src/mightypork/gamecore/gui/components/LayoutComponent.java @@ -8,14 +8,13 @@ import mightypork.gamecore.app.AppAccess; import mightypork.gamecore.app.AppSubModule; import mightypork.gamecore.eventbus.EventBus; import mightypork.gamecore.eventbus.clients.ClientHub; -import mightypork.gamecore.gui.Enableable; import mightypork.gamecore.input.InputSystem; import mightypork.gamecore.render.DisplaySystem; import mightypork.gamecore.resources.audio.SoundSystem; import mightypork.gamecore.util.math.constraints.rect.proxy.RectBound; -public abstract class LayoutComponent extends VisualComponent implements Enableable, ClientHub, AppAccess { +public abstract class LayoutComponent extends BaseComponent implements ClientHub, AppAccess { private boolean enabled; @@ -106,21 +105,29 @@ public abstract class LayoutComponent extends VisualComponent implements Enablea subModule.removeChildClient(client); } - @Override public void enable(boolean yes) { - subModule.setDelegating(yes); - subModule.setListening(yes); - enabled = yes; + super.enable(yes); + for(Component c : components) { + c.enable(yes); + } } - - @Override - public boolean isEnabled() - { - return enabled; - } +// @Override +// public void enable(boolean yes) +// { +// subModule.setDelegating(yes); +// subModule.setListening(yes); +// enabled = yes; +// } +// +// +// @Override +// public boolean isEnabled() +// { +// return enabled; +// } /** diff --git a/src/mightypork/gamecore/gui/components/painters/ImagePainter.java b/src/mightypork/gamecore/gui/components/painters/ImagePainter.java index d676fd8..95733cb 100644 --- a/src/mightypork/gamecore/gui/components/painters/ImagePainter.java +++ b/src/mightypork/gamecore/gui/components/painters/ImagePainter.java @@ -1,7 +1,7 @@ package mightypork.gamecore.gui.components.painters; -import mightypork.gamecore.gui.components.VisualComponent; +import mightypork.gamecore.gui.components.BaseComponent; import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; import mightypork.gamecore.util.math.constraints.num.Num; @@ -13,7 +13,7 @@ import mightypork.gamecore.util.math.constraints.rect.Rect; * * @author MightyPork */ -public class ImagePainter extends VisualComponent { +public class ImagePainter extends BaseComponent { private final TxQuad txQuad; private boolean aspratio = false; diff --git a/src/mightypork/gamecore/gui/components/painters/QuadPainter.java b/src/mightypork/gamecore/gui/components/painters/QuadPainter.java index 0bceed9..e64f090 100644 --- a/src/mightypork/gamecore/gui/components/painters/QuadPainter.java +++ b/src/mightypork/gamecore/gui/components/painters/QuadPainter.java @@ -1,7 +1,7 @@ package mightypork.gamecore.gui.components.painters; -import mightypork.gamecore.gui.components.VisualComponent; +import mightypork.gamecore.gui.components.BaseComponent; import mightypork.gamecore.render.Render; import mightypork.gamecore.util.annot.FactoryMethod; import mightypork.gamecore.util.math.color.Color; @@ -12,7 +12,7 @@ import mightypork.gamecore.util.math.color.Color; * * @author MightyPork */ -public class QuadPainter extends VisualComponent { +public class QuadPainter extends BaseComponent { @FactoryMethod public static QuadPainter gradH(Color colorLeft, Color colorRight) diff --git a/src/mightypork/gamecore/gui/components/painters/TextPainter.java b/src/mightypork/gamecore/gui/components/painters/TextPainter.java index 20df900..902c98a 100644 --- a/src/mightypork/gamecore/gui/components/painters/TextPainter.java +++ b/src/mightypork/gamecore/gui/components/painters/TextPainter.java @@ -2,15 +2,18 @@ package mightypork.gamecore.gui.components.painters; import mightypork.gamecore.gui.AlignX; -import mightypork.gamecore.gui.components.VisualComponent; +import mightypork.gamecore.gui.components.BaseComponent; +import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.fonts.FontRenderer; import mightypork.gamecore.resources.fonts.GLFont; import mightypork.gamecore.util.math.color.Color; import mightypork.gamecore.util.math.color.pal.RGB; +import mightypork.gamecore.util.math.constraints.num.Num; import mightypork.gamecore.util.math.constraints.rect.Rect; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.gamecore.util.strings.StringProvider; import mightypork.gamecore.util.strings.StringWrapper; +import mightypork.rogue.Config; /** @@ -20,7 +23,7 @@ import mightypork.gamecore.util.strings.StringWrapper; * * @author MightyPork */ -public class TextPainter extends VisualComponent { +public class TextPainter extends BaseComponent { private final FontRenderer font; private Color color; @@ -28,6 +31,9 @@ public class TextPainter extends VisualComponent { private StringProvider text; private boolean shadow; + private double yPaddingPerc = 0; + private double xPaddingPerc = 0; + private Color shadowColor = RGB.BLACK; private Vect shadowOffset = Vect.make(2, 2); @@ -35,8 +41,7 @@ public class TextPainter extends VisualComponent { /** * @param font font to use */ - public TextPainter(GLFont font) - { + public TextPainter(GLFont font) { this(font, AlignX.LEFT, RGB.WHITE); } @@ -49,8 +54,7 @@ public class TextPainter extends VisualComponent { * @param color default color * @param text drawn text */ - public TextPainter(GLFont font, AlignX align, Color color, String text) - { + public TextPainter(GLFont font, AlignX align, Color color, String text) { this(font, align, color, new StringWrapper(text)); } @@ -63,8 +67,7 @@ public class TextPainter extends VisualComponent { * @param color default color * @param text text provider */ - public TextPainter(GLFont font, AlignX align, Color color, StringProvider text) - { + public TextPainter(GLFont font, AlignX align, Color color, StringProvider text) { this.font = new FontRenderer(font); this.color = color; this.align = align; @@ -77,8 +80,7 @@ public class TextPainter extends VisualComponent { * @param align text align * @param color default color */ - public TextPainter(GLFont font, AlignX align, Color color) - { + public TextPainter(GLFont font, AlignX align, Color color) { this(font, align, color, (StringProvider) null); } @@ -89,13 +91,20 @@ public class TextPainter extends VisualComponent { if (text == null) return; final String str = text.getString(); - final Rect rect = getRect(); + + Num shrX = height().perc(xPaddingPerc); + Num shrY = height().perc(yPaddingPerc); + + final Rect rect = getRect().shrink(shrX, shrY); if (shadow) { font.draw(str, rect.round(), align, shadowColor); } - font.draw(str, rect.move(shadowOffset.neg()).round(), align, color); + Rect r = (shadow ? rect.move(shadowOffset.neg()) : rect).round(); + font.draw(str, r, align, color); + + if (Config.DEBUG_FONT_RENDER) Render.quadColor(r, RGB.PINK.withAlpha(0.4)); } @@ -147,4 +156,11 @@ public class TextPainter extends VisualComponent { { this.text = text; } + + + public void setPaddingHPerc(double percX, double percY) + { + xPaddingPerc = percX; + yPaddingPerc = percY; + } } diff --git a/src/mightypork/gamecore/gui/screens/LayeredScreen.java b/src/mightypork/gamecore/gui/screens/LayeredScreen.java index 79957a4..ab948b4 100644 --- a/src/mightypork/gamecore/gui/screens/LayeredScreen.java +++ b/src/mightypork/gamecore/gui/screens/LayeredScreen.java @@ -16,6 +16,11 @@ import mightypork.gamecore.eventbus.clients.DelegatingClient; */ public abstract class LayeredScreen extends Screen { + /** + * Wrapper for delegating client, to use custom client ordering. + * + * @author MightyPork + */ private class LayersClient implements DelegatingClient { @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/src/mightypork/gamecore/gui/screens/Overlay.java b/src/mightypork/gamecore/gui/screens/Overlay.java index 0059c3d..d6e8632 100644 --- a/src/mightypork/gamecore/gui/screens/Overlay.java +++ b/src/mightypork/gamecore/gui/screens/Overlay.java @@ -7,6 +7,7 @@ import java.util.LinkedHashSet; import mightypork.gamecore.app.AppAccess; import mightypork.gamecore.app.AppSubModule; import mightypork.gamecore.eventbus.events.Updateable; +import mightypork.gamecore.gui.Enableable; import mightypork.gamecore.gui.Hideable; import mightypork.gamecore.gui.components.layout.ConstraintLayout; import mightypork.gamecore.gui.events.LayoutChangeListener; @@ -24,9 +25,11 @@ import mightypork.gamecore.util.math.constraints.vect.Vect; * * @author MightyPork */ -public abstract class Overlay extends AppSubModule implements Comparable, Updateable, Renderable, KeyBinder, Hideable, LayoutChangeListener { +public abstract class Overlay extends AppSubModule implements Comparable, Updateable, Renderable, KeyBinder, Hideable, Enableable, LayoutChangeListener { private boolean visible = true; + private boolean enabled = true; + private final KeyBindingPool keybindings = new KeyBindingPool(); /** Root layout, rendered and attached to the event bus. */ @@ -81,8 +84,21 @@ public abstract class Overlay extends AppSubModule implements Comparable 13) { + if (number % 10 == 1) return number + "st"; + if (number % 10 == 2) return number + "nd"; + if (number % 10 == 3) return number + "rd"; + } + return number + "th"; + } + + + /** + * Format number with thousands separated by a dot. + * + * @param number number + * @return string 12.004.225 + */ + public static String formatInt(long number) + { + final String num = number + ""; + String out = ""; + final String dot = "."; + int cnt = 1; + for (int i = num.length() - 1; i >= 0; i--) { + out = num.charAt(i) + out; + if (cnt % 3 == 0 && i > 0) out = dot + out; + cnt++; + } + + return out; + } } diff --git a/src/mightypork/gamecore/util/math/constraints/rect/Rect.java b/src/mightypork/gamecore/util/math/constraints/rect/Rect.java index e05b351..8b7ad8f 100644 --- a/src/mightypork/gamecore/util/math/constraints/rect/Rect.java +++ b/src/mightypork/gamecore/util/math/constraints/rect/Rect.java @@ -474,7 +474,7 @@ public abstract class Rect implements RectBound, Digestable { public Rect shrinkRight(final double shrink) { - return growLeft(-shrink); + return growRight(-shrink); } diff --git a/src/mightypork/gamecore/util/strings/StringUtils.java b/src/mightypork/gamecore/util/strings/StringUtils.java index 12360a5..26c7e70 100644 --- a/src/mightypork/gamecore/util/strings/StringUtils.java +++ b/src/mightypork/gamecore/util/strings/StringUtils.java @@ -100,45 +100,6 @@ public class StringUtils { } - /** - * Get ordinal version of numbers (1 = 1st, 5 = 5th etc.) - * - * @param number number - * @return ordinal, string - */ - public static String numberToOrdinal(int number) - { - if (number % 100 < 4 || number % 100 > 13) { - if (number % 10 == 1) return number + "st"; - if (number % 10 == 2) return number + "nd"; - if (number % 10 == 3) return number + "rd"; - } - return number + "th"; - } - - - /** - * Format number with thousands separated by a dot. - * - * @param number number - * @return string 12.004.225 - */ - public static String formatInt(long number) - { - final String num = number + ""; - String out = ""; - final String dot = "."; - int cnt = 1; - for (int i = num.length() - 1; i >= 0; i--) { - out = num.charAt(i) + out; - if (cnt % 3 == 0 && i > 0) out = dot + out; - cnt++; - } - - return out; - } - - public static boolean isValidFilenameChar(char ch) { return isValidFilenameString(Character.toString(ch)); diff --git a/src/mightypork/rogue/Config.java b/src/mightypork/rogue/Config.java index ea28057..470af55 100644 --- a/src/mightypork/rogue/Config.java +++ b/src/mightypork/rogue/Config.java @@ -24,6 +24,7 @@ public final class Config { // property keys private static final String PK_LAST_RUN_VERSION = "status.last_run_version"; private static final String PK_START_IN_FS = "cfg.start_in_fullscreen"; + /** @@ -76,4 +77,9 @@ public final class Config { public static boolean LOG_TO_STDOUT = true; public static boolean SINGLE_INSTANCE = true; + /** Render dark in unknown area & skip invisible stuff */ + public static boolean RENDER_UFOG = true; + + /** Render a font bounding box in text painters. */ + public static boolean DEBUG_FONT_RENDER = false; } diff --git a/src/mightypork/rogue/Res.java b/src/mightypork/rogue/Res.java index 8f57b4a..76261f4 100644 --- a/src/mightypork/rogue/Res.java +++ b/src/mightypork/rogue/Res.java @@ -53,13 +53,18 @@ public final class Res { //fonts.loadFont("polygon_pixel", new DeferredFont("/res/font/PolygonPixel5x7Standard.ttf", Glyphs.basic, 16)); fonts.loadFont("press_start", font = new DeferredFont("/res/font/PressStart2P.ttf", Glyphs.basic, 16)); - fonts.loadFont("polygon_pixel", font = new DeferredFont("/res/font/Simpleton.ttf", Glyphs.basic, 16)); - font.setDiscardRatio(5 / 16D, 2 / 16D); + + fonts.loadFont("battlenet", font = new DeferredFont("/res/font/battlenet.ttf", Glyphs.basic, 16)); + font.setDiscardRatio(3 / 16D, 2 / 16D); + + fonts.loadFont("tinyutf", font = new DeferredFont("/res/font/TinyUnicode2.ttf", Glyphs.basic, 16)); + font.setDiscardRatio(6 / 16D, 2 / 16D); // aliases based on concrete usage fonts.addAlias("thick", "press_start"); - fonts.addAlias("thin", "polygon_pixel"); + fonts.addAlias("thin", "battlenet"); + fonts.addAlias("tiny", "tinyutf"); } @@ -152,6 +157,13 @@ public final class Res { texture = textures.loadTexture("items", "/res/img/items.png", FilterMode.NEAREST, WrapMode.CLAMP); grid = texture.grid(8, 8); textures.add("item.meat", grid.makeQuad(0, 0)); + textures.add("item.club", grid.makeQuad(1, 0)); + textures.add("item.sword", grid.makeQuad(2, 0)); + textures.add("item.hammer", grid.makeQuad(3, 0)); + textures.add("item.stone", grid.makeQuad(4, 0)); + textures.add("item.bone", grid.makeQuad(5, 0)); + textures.add("item.cheese", grid.makeQuad(6, 0)); + textures.add("item.sandwich", grid.makeQuad(7, 0)); } diff --git a/src/mightypork/rogue/screens/game/HeartBar.java b/src/mightypork/rogue/screens/game/HeartBar.java index 348bffa..3bb2533 100644 --- a/src/mightypork/rogue/screens/game/HeartBar.java +++ b/src/mightypork/rogue/screens/game/HeartBar.java @@ -2,15 +2,16 @@ package mightypork.rogue.screens.game; import mightypork.gamecore.gui.AlignX; -import mightypork.gamecore.gui.components.VisualComponent; +import mightypork.gamecore.gui.components.BaseComponent; import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; +import mightypork.gamecore.util.math.color.pal.RGB; import mightypork.gamecore.util.math.constraints.num.Num; import mightypork.gamecore.util.math.constraints.num.mutable.NumVar; import mightypork.gamecore.util.math.constraints.rect.Rect; -public class HeartBar extends VisualComponent { +public class HeartBar extends BaseComponent { private final TxQuad img_on; private final TxQuad img_off; @@ -18,8 +19,8 @@ public class HeartBar extends VisualComponent { private final Num total; private final Num active; - NumVar index = new NumVar(0); - Rect heart; + private final NumVar index = new NumVar(0); + private final Rect heart; /** @@ -30,8 +31,7 @@ public class HeartBar extends VisualComponent { * @param img_off * @param align */ - public HeartBar(Num total, Num active, TxQuad img_on, TxQuad img_half, TxQuad img_off, AlignX align) - { + public HeartBar(Num total, Num active, TxQuad img_on, TxQuad img_half, TxQuad img_off, AlignX align) { super(); this.total = total; this.active = active; @@ -52,6 +52,8 @@ public class HeartBar extends VisualComponent { case CENTER: heart = leftEdge().moveX(w.half().add(h.mul(total.half().neg()))).growRight(h).moveX(index.mul(h)); break; + default: + heart = null; // impossible } } @@ -59,12 +61,11 @@ public class HeartBar extends VisualComponent { @Override protected void renderComponent() - { + { for (int i = 0; i < total.value(); i++) { index.setTo(i); final double rem = active.value() - i; - Render.quadTextured(heart, (rem > 0.6 ? img_on : rem > 0.25 ? img_half : img_off)); } } diff --git a/src/mightypork/rogue/screens/game/HudLayer.java b/src/mightypork/rogue/screens/game/HudLayer.java index eb1bc99..6af2bb9 100644 --- a/src/mightypork/rogue/screens/game/HudLayer.java +++ b/src/mightypork/rogue/screens/game/HudLayer.java @@ -39,8 +39,7 @@ public class HudLayer extends ScreenLayer { private final ScreenGame gameScreen; - public HudLayer(ScreenGame screen) - { + public HudLayer(ScreenGame screen) { super(screen); this.gameScreen = screen; @@ -49,6 +48,22 @@ public class HudLayer extends ScreenLayer { buildDisplays(); buildMinimap(); + + buildConsole(); + } + + + private void buildConsole() + { + Num rh = root.height(); + Num rw = root.width(); + Rect consoleRect = root.shrink(rw.perc(2), Num.ZERO, rh.perc(6), rh.perc(16)); + + Num perRow = consoleRect.height().div(20).max(12).min(32); + + WorldConsoleRenderer wcr = new WorldConsoleRenderer(perRow); + wcr.setRect(consoleRect); + root.add(wcr); } @@ -64,8 +79,6 @@ public class HudLayer extends ScreenLayer { { final Num h = root.height(); - final Num displays_height = h.perc(6); - //@formatter:off final HeartBar hearts = new HeartBar( playerHealthTotal, @@ -76,13 +89,11 @@ public class HudLayer extends ScreenLayer { AlignX.LEFT); //@formatter:on - - final Rect hearts_box = root.shrink(h.perc(3)).topLeft().startRect().growDown(displays_height); + final Rect hearts_box = root.shrink(h.perc(3)).topEdge().growDown(h.perc(6)); hearts.setRect(hearts_box); root.add(hearts); - - final TextPainter lvl = new TextPainter(Res.getFont("thick"), AlignX.RIGHT, RGB.WHITE, new StringProvider() { + final TextPainter levelText = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE, new StringProvider() { @Override public String getString() @@ -92,15 +103,8 @@ public class HudLayer extends ScreenLayer { } }); - final Rect rr = root.shrink(h.perc(3)).topEdge().growDown(displays_height); - lvl.setRect(rr.shrink(Num.ZERO, rr.height().perc(20))); - root.add(lvl); - - - /*final HeartBar experience = new HeartBar(6, 2, Res.getTxQuad("xp_on"), Res.getTxQuad("xp_off"), AlignX.RIGHT); - final Rect xp_box = shrunk.topRight().startRect().growDown(displays_height); - experience.setRect(xp_box); - root.add(experience);*/ + levelText.setRect(hearts_box.moveY(hearts_box.height().mul(1/7D))); + root.add(levelText); } @@ -112,24 +116,24 @@ public class HudLayer extends ScreenLayer { NavButton btn; - // ltr - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.inventory"))); + nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.inventory"))); btn.setAction(gameScreen.actionInv); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.eat"))); + nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.eat"))); btn.setAction(gameScreen.actionEat); - nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.pause"))); + nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.pause"))); btn.setAction(gameScreen.actionTogglePause); + // TODO actions - nav.addRight(new NavButton(Res.txq("nav.button.fg.options"))); - nav.addRight(new NavButton(Res.txq("nav.button.fg.help"))); + nav.addLeft(new NavButton(Res.txq("nav.button.fg.options"))); + nav.addLeft(new NavButton(Res.txq("nav.button.fg.help"))); - nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.map"))); + nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.map"))); btn.setAction(gameScreen.actionToggleMinimap); - nav.addRight(btn = new NavButton(Res.txq("nav.button.fg.magnify"))); + nav.addLeft(btn = new NavButton(Res.txq("nav.button.fg.magnify"))); btn.setAction(gameScreen.actionToggleZoom); } diff --git a/src/mightypork/rogue/screens/game/InvLayer.java b/src/mightypork/rogue/screens/game/InvLayer.java index 243f2df..750f959 100644 --- a/src/mightypork/rogue/screens/game/InvLayer.java +++ b/src/mightypork/rogue/screens/game/InvLayer.java @@ -30,7 +30,7 @@ public class InvLayer extends ScreenLayer { @Override public String getString() { - String s = "Esc - close"; + String s = "ESC-close"; final int selected = getSelectedSlot(); if (selected != -1) { @@ -39,16 +39,18 @@ public class InvLayer extends ScreenLayer { final Item itm = pl.getInventory().getItem(selected); if (itm != null && !itm.isEmpty()) { - s = "D - drop, " + s; + s = "D-drop," + s; if (itm.getType() == ItemType.FOOD) { - s = "E - eat, " + s; + s = "E-eat," + s; } if (itm.getType() == ItemType.WEAPON) { - s = "E - equip, " + s; + s = "E-equip," + s; } } + }else { + s = "Click-select,"+s; } return s; @@ -76,18 +78,19 @@ public class InvLayer extends ScreenLayer { final Rect fg = root.shrink(root.height().perc(15)); - final QuadPainter qp = new QuadPainter(Color.rgba(0, 0, 0, 0.5)); + final QuadPainter qp = new QuadPainter(RGB.BLACK_30, RGB.BLACK_30, RGB.BLACK_80, RGB.BLACK_80); qp.setRect(root); root.add(qp); int pos = 0; - final GridLayout gl = new GridLayout(root, fg, 21, 1); + final GridLayout gl = new GridLayout(root, fg, 10, 1); root.add(gl); final TextPainter txp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Inventory"); - gl.put(txp, pos, 0, 2, 1); - pos += 3; + gl.put(txp, pos, 0, 1, 1); + txp.setPaddingHPerc(0, 5); + pos += 1; final HorizontalFixedFlowLayout row1 = new HorizontalFixedFlowLayout(root, null, AlignX.LEFT); row1.setElementWidth(row1.height()); @@ -95,8 +98,8 @@ public class InvLayer extends ScreenLayer { row1.setRect(cl1.axisV().grow(cl1.height().mul(2), Num.ZERO)); cl1.add(row1); - gl.put(cl1, pos, 0, 8, 1); - pos += 8; + gl.put(cl1, pos, 0, 4, 1); + pos += 4; row1.add(slots[0] = new InvSlot(0, slots)); row1.add(slots[1] = new InvSlot(1, slots)); @@ -109,25 +112,24 @@ public class InvLayer extends ScreenLayer { final ConstraintLayout cl2 = new ConstraintLayout(root); row2.setRect(cl2.axisV().grow(cl2.height().mul(2), Num.ZERO)); cl2.add(row2); - gl.put(cl2, pos, 0, 8, 1); - pos += 8; + gl.put(cl2, pos, 0, 4, 1); + pos += 4; row2.add(slots[4] = new InvSlot(4, slots)); row2.add(slots[5] = new InvSlot(5, slots)); row2.add(slots[6] = new InvSlot(6, slots)); row2.add(slots[7] = new InvSlot(7, slots)); - final TextPainter txp2 = new TextPainter(Res.getFont("thin"), AlignX.CENTER, RGB.WHITE, contextStrProv); - gl.put(txp2, pos + 1, 0, 1, 1); - - setVisible(false); + final TextPainter txp2 = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, contextStrProv); + gl.put(txp2, pos, 0, 1, 1); + txp2.setPaddingHPerc(0, 25); bindKey(new KeyStroke(Keys.ESCAPE), new Runnable() { @Override public void run() { - if (!isVisible()) return; + if(WorldProvider.get().getPlayer().isDead()) return; screen.setState(GScrState.WORLD); } @@ -137,8 +139,8 @@ public class InvLayer extends ScreenLayer { @Override public void run() - { - if (!isVisible()) return; + { + if(WorldProvider.get().getPlayer().isDead()) return; final int selected = getSelectedSlot(); if (selected != -1) { @@ -147,15 +149,14 @@ public class InvLayer extends ScreenLayer { if (itm != null && !itm.isEmpty()) { if (itm.getType() == ItemType.FOOD) { - if (pl.eatFood(itm)) { - if (itm.consume()) { - pl.getInventory().setItem(selected, null); - } + if (pl.eatFood(itm)) { + pl.getInventory().clean(); } } if (itm.getType() == ItemType.WEAPON) { pl.selectWeapon(selected); + WorldProvider.get().getWorld().msgEquipWeapon(itm); } } } @@ -194,4 +195,13 @@ public class InvLayer extends ScreenLayer { return 200; } + @Override + public void onLayoutChanged() + { + // TODO Auto-generated method stub + super.onLayoutChanged(); + + System.out.println("LayoutChange @ invlayer"); + } + } diff --git a/src/mightypork/rogue/screens/game/InvSlot.java b/src/mightypork/rogue/screens/game/InvSlot.java index 9247872..d6c28f1 100644 --- a/src/mightypork/rogue/screens/game/InvSlot.java +++ b/src/mightypork/rogue/screens/game/InvSlot.java @@ -7,12 +7,17 @@ import mightypork.gamecore.gui.components.ClickableComponent; import mightypork.gamecore.gui.components.painters.TextPainter; import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; +import mightypork.gamecore.util.math.Calc; +import mightypork.gamecore.util.math.color.Color; import mightypork.gamecore.util.math.color.pal.RGB; +import mightypork.gamecore.util.math.constraints.num.Num; import mightypork.gamecore.util.math.constraints.rect.caching.RectCache; import mightypork.rogue.Res; import mightypork.rogue.world.World.PlayerFacade; +import mightypork.rogue.world.PlayerInfo; import mightypork.rogue.world.WorldProvider; import mightypork.rogue.world.item.Item; +import mightypork.rogue.world.item.ItemType; /** @@ -28,16 +33,18 @@ public class InvSlot extends ClickableComponent { protected int index; private final RectCache itemRect; + private final RectCache uiRect; private final InvSlot[] slots; - private final TextPainter txt; + private final TextPainter rbTxP; + private final TextPainter rtTxP; - private final RectCache txtRect; + private final RectCache rbTxRect; + private final RectCache rtTxRect; - public InvSlot(int index, InvSlot[] allSlots) - { + public InvSlot(int index, InvSlot[] allSlots) { super(); this.txBase = Res.txq("inv.slot.base"); this.txSelected = Res.txq("inv.slot.selected"); @@ -45,17 +52,27 @@ public class InvSlot extends ClickableComponent { this.index = index; this.slots = allSlots; - this.itemRect = getRect().shrink(height().perc(16)).cached(); + this.uiRect = getRect().shrink(height().perc(16)).cached(); + this.itemRect = uiRect.shrink(Num.ZERO, height().perc(14), height().perc(14), Num.ZERO).cached(); + + //@formatter:off + this.rbTxRect = uiRect.bottomEdge() + .moveY(uiRect.height().perc(1*(30/7D))) + .growUp(uiRect.height().perc(30)).cached(); + //@formatter:on + + rbTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE); + rbTxP.setRect(rbTxRect); + rbTxP.setShadow(RGB.BLACK_70, rbTxP.getRect().height().div(7).toVectXY()); //@formatter:off - this.txtRect = itemRect.bottomEdge() - .move(itemRect.height().perc(5).neg(), itemRect.height().perc(10).neg()) - .growUp(itemRect.height().perc(35)).cached(); + this.rtTxRect = uiRect.topEdge() + .growDown(uiRect.height().perc(30)).cached(); //@formatter:on - txt = new TextPainter(Res.getFont("thin"), AlignX.RIGHT, RGB.WHITE); - txt.setRect(txtRect); - txt.setShadow(RGB.BLACK_60, txt.getRect().height().div(8).toVectXY()); + rtTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.GREEN); + rtTxP.setRect(rtTxRect); + rtTxP.setShadow(RGB.BLACK_70, rtTxP.getRect().height().div(7).toVectXY()); setAction(new Action() { @@ -76,8 +93,10 @@ public class InvSlot extends ClickableComponent { @Override public void updateLayout() { + uiRect.poll(); itemRect.poll(); - txtRect.poll(); + rbTxRect.poll(); + rtTxRect.poll(); } @@ -100,18 +119,28 @@ public class InvSlot extends ClickableComponent { if (itm != null && !itm.isEmpty()) { itm.render(itemRect); if (itm.getAmount() > 1) { - txt.setText("" + itm.getAmount()); - txt.setColor(RGB.WHITE); - txt.render(); + rbTxP.setText("" + itm.getAmount()); + rbTxP.setColor(RGB.WHITE); + rbTxP.render(); } - if (pl.getSelectedWeapon() == index) { - txt.setText("*"); - txt.setColor(RGB.YELLOW); - txt.render(); + if (pl.getSelectedWeaponIndex() == index) { + rbTxP.setText("*"); + rbTxP.setColor(RGB.YELLOW); + rbTxP.render(); } - + if (itm.getType() == ItemType.FOOD) { + rtTxP.setText(Calc.toString(itm.getFoodPoints() / 2D)); + rbTxP.setColor(RGB.GREEN); + rtTxP.render(); + } else if (itm.getType() == ItemType.WEAPON) { + + int atk = itm.getAttackPoints(); + rtTxP.setText((atk >= 0 ? "+" : "") + atk); + rtTxP.setColor(RGB.CYAN); + rtTxP.render(); + } } } diff --git a/src/mightypork/rogue/screens/game/NavButton.java b/src/mightypork/rogue/screens/game/NavButton.java index f6263f5..188dd98 100644 --- a/src/mightypork/rogue/screens/game/NavButton.java +++ b/src/mightypork/rogue/screens/game/NavButton.java @@ -40,6 +40,8 @@ public class NavButton extends ClickableComponent { bg = base; } + if(!isEnabled()) bg = base; // override effects + Render.quadTextured(this, bg); Render.quadTextured(this, fg); } diff --git a/src/mightypork/rogue/screens/game/ScreenGame.java b/src/mightypork/rogue/screens/game/ScreenGame.java index da2fae2..f3fa98e 100644 --- a/src/mightypork/rogue/screens/game/ScreenGame.java +++ b/src/mightypork/rogue/screens/game/ScreenGame.java @@ -9,6 +9,7 @@ import mightypork.gamecore.gui.ActionGroup; import mightypork.gamecore.gui.screens.LayeredScreen; import mightypork.gamecore.input.KeyStroke; import mightypork.gamecore.input.Keys; +import mightypork.rogue.Config; import mightypork.rogue.world.WorldProvider; import mightypork.rogue.world.events.WorldPauseRequest; import mightypork.rogue.world.events.WorldPauseRequest.PauseAction; @@ -26,7 +27,6 @@ public class ScreenGame extends LayeredScreen { WORLD, INV; } - private final Random rand = new Random(); private InvLayer invLayer; private HudLayer hudLayer; @@ -102,12 +102,14 @@ public class ScreenGame extends LayeredScreen { getEventBus().send(new WorldPauseRequest(PauseAction.RESUME)); invLayer.setVisible(false); // hide all extra layers + invLayer.enable(false); worldActions.enable(true); } if (nstate == GScrState.INV) { invLayer.setVisible(true); + invLayer.enable(true); } this.state = nstate; @@ -120,13 +122,20 @@ public class ScreenGame extends LayeredScreen { } - public ScreenGame(AppAccess app) - { + public ScreenGame(AppAccess app) { super(app); addLayer(invLayer = new InvLayer(this)); + invLayer.enable(false); + invLayer.setVisible(false); + addLayer(hudLayer = new HudLayer(this)); + hudLayer.enable(true); + hudLayer.setVisible(true); + addLayer(worldLayer = new WorldLayer(this)); + worldLayer.enable(true); + worldLayer.setVisible(true); // TODO temporary here ↓ bindKey(new KeyStroke(Keys.L_CONTROL, Keys.N), new Runnable() { @@ -146,6 +155,10 @@ public class ScreenGame extends LayeredScreen { bindKey(new KeyStroke(Keys.E), actionEat); bindKey(new KeyStroke(Keys.M), actionToggleMinimap); bindKey(new KeyStroke(Keys.Z), actionToggleZoom); + + // add as actions - enableables. + worldActions.add(worldLayer); + worldActions.add(hudLayer); worldActions.add(actionEat); worldActions.add(actionInv); @@ -154,6 +167,16 @@ public class ScreenGame extends LayeredScreen { worldActions.add(actionToggleZoom); worldActions.enable(true); + + // TMP TODO remove + bindKey(new KeyStroke(Keys.X), new Runnable() { + + @Override + public void run() + { + Config.RENDER_UFOG ^= true; + } + }); } diff --git a/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java b/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java new file mode 100644 index 0000000..0cbb391 --- /dev/null +++ b/src/mightypork/rogue/screens/game/WorldConsoleRenderer.java @@ -0,0 +1,62 @@ +package mightypork.rogue.screens.game; + + +import java.util.Collection; + +import mightypork.gamecore.gui.AlignX; +import mightypork.gamecore.gui.components.BaseComponent; +import mightypork.gamecore.gui.components.painters.TextPainter; +import mightypork.gamecore.resources.fonts.FontRenderer; +import mightypork.gamecore.util.math.color.Color; +import mightypork.gamecore.util.math.color.pal.RGB; +import mightypork.gamecore.util.math.constraints.num.Num; +import mightypork.gamecore.util.math.constraints.num.mutable.NumVar; +import mightypork.gamecore.util.math.constraints.rect.Rect; +import mightypork.rogue.Res; +import mightypork.rogue.world.WorldConsole; +import mightypork.rogue.world.WorldConsole.Entry; +import mightypork.rogue.world.WorldProvider; + + +public class WorldConsoleRenderer extends BaseComponent { + + private final Num rowHeight; + private final FontRenderer fr; + + + public WorldConsoleRenderer(Num rowHeight) { + this.rowHeight = rowHeight; + this.fr = new FontRenderer(Res.getFont("tiny")); + } + + + @Override + protected void renderComponent() + { + double rh = rowHeight.value(); + + Rect lowRow = bottomEdge().growUp(rowHeight); + + Collection entries = WorldProvider.get().getWorld().getConsole().getEntries(); + int cnt = 0; + + NumVar alph = Num.makeVar(); + + Color.pushAlpha(alph); + + for (WorldConsole.Entry entry : entries) { + + alph.setTo(entry.getAlpha()); + + Rect rrr = lowRow.moveY(-rh * cnt); + + fr.draw(entry.getMessage(), rrr.move(rh / 12, rh / 12), AlignX.LEFT, RGB.BLACK_60); + fr.draw(entry.getMessage(), rrr, AlignX.LEFT, RGB.WHITE); + + cnt++; + } + + Color.popAlpha(); + } + +} diff --git a/src/mightypork/rogue/screens/test_bouncyboxes/BouncyBox.java b/src/mightypork/rogue/screens/test_bouncyboxes/BouncyBox.java index 48c9817..b309629 100644 --- a/src/mightypork/rogue/screens/test_bouncyboxes/BouncyBox.java +++ b/src/mightypork/rogue/screens/test_bouncyboxes/BouncyBox.java @@ -4,7 +4,7 @@ package mightypork.rogue.screens.test_bouncyboxes; import java.util.Random; import mightypork.gamecore.eventbus.events.Updateable; -import mightypork.gamecore.gui.components.VisualComponent; +import mightypork.gamecore.gui.components.BaseComponent; import mightypork.gamecore.render.Render; import mightypork.gamecore.util.math.Easing; import mightypork.gamecore.util.math.color.pal.RGB; @@ -14,7 +14,7 @@ import mightypork.gamecore.util.math.constraints.rect.Rect; import mightypork.gamecore.util.math.constraints.rect.caching.RectCache; -public class BouncyBox extends VisualComponent implements Updateable { +public class BouncyBox extends BaseComponent implements Updateable { private final Random rand = new Random(); diff --git a/src/mightypork/rogue/world/Inventory.java b/src/mightypork/rogue/world/Inventory.java index ad4a97f..ed0bd6e 100644 --- a/src/mightypork/rogue/world/Inventory.java +++ b/src/mightypork/rogue/world/Inventory.java @@ -16,14 +16,12 @@ public class Inventory implements IonObjBinary { private Item[] items; - public Inventory(int size) - { + public Inventory(int size) { this.items = new Item[size]; } - public Inventory() - { + public Inventory() { // ION constructor } @@ -154,6 +152,19 @@ public class Inventory implements IonObjBinary { } + /** + * Clean empty items + */ + public void clean() + { + for (int i = 0; i < getSize(); i++) { + Item itm = getItem(i); + if (itm == null) continue; + if (itm.isEmpty()) setItem(i, null); + } + } + + @Override public String toString() { @@ -164,8 +175,10 @@ public class Inventory implements IonObjBinary { s += i + ": "; final Item itm = getItem(i); - if (itm == null) s += ""; - else s += itm; + if (itm == null) + s += ""; + else + s += itm; } s += "]"; return s; diff --git a/src/mightypork/rogue/world/PlayerControl.java b/src/mightypork/rogue/world/PlayerControl.java index 54d9db0..829327f 100644 --- a/src/mightypork/rogue/world/PlayerControl.java +++ b/src/mightypork/rogue/world/PlayerControl.java @@ -99,31 +99,34 @@ public abstract class PlayerControl { */ public boolean clickTile(Step side) { - return clickTile(getPlayer().getCoord().add(side).toVect().add(0.5, 0.5)); + return doClickTile(getPlayer().getCoord().add(side).toVect()); } public boolean clickTile(Vect pos) - { - //if (pos.dist(getPlayer().getVisualPos()).value() > 8) return false; // too far - + { 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); - if (prey != null) { - prey.receiveAttack(getPlayer().getEntity(), getPlayer().getAttackStrength()); - return true; - } - - //2nd try to click tile - return getLevel().getTile(Coord.fromVect(pos)).onClick(); + return doClickTile(pos); } return false; } + private boolean doClickTile(Vect pos) + { + // 1st try to hit entity + final Entity prey = getLevel().getClosestEntity(pos, EntityType.MONSTER, 1); + if (prey != null) { + getPlayer().attack(prey); + return true; + } + + //2nd try to click tile + return getLevel().getTile(Coord.fromVect(pos)).onClick(); + } + + public void go(Step side) { getPlayer().cancelPath(); diff --git a/src/mightypork/rogue/world/PlayerInfo.java b/src/mightypork/rogue/world/PlayerInfo.java index 9cdb72c..4b5c9b7 100644 --- a/src/mightypork/rogue/world/PlayerInfo.java +++ b/src/mightypork/rogue/world/PlayerInfo.java @@ -19,7 +19,7 @@ public class PlayerInfo implements IonObjBundled { private static final int INV_SIZE = 8; /** Constant indicating that no weapon is selected. */ - private static final int NO_WEAPON = -1; + public static final int NO_WEAPON = -1; /** Attack str with bare hands */ public static final int BARE_ATTACK = 1; @@ -106,6 +106,10 @@ public class PlayerInfo implements IonObjBundled { public void selectWeapon(int selectedWeapon) { + if(selectedWeapon<0||selectedWeapon>=getInventory().getSize()) { + selectedWeapon = NO_WEAPON; + } + this.selectedWeapon = selectedWeapon; } diff --git a/src/mightypork/rogue/world/World.java b/src/mightypork/rogue/world/World.java index ddee98a..ac4a6e0 100644 --- a/src/mightypork/rogue/world/World.java +++ b/src/mightypork/rogue/world/World.java @@ -2,15 +2,15 @@ package mightypork.rogue.world; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Random; +import java.util.*; import mightypork.gamecore.eventbus.BusAccess; import mightypork.gamecore.eventbus.EventBus; import mightypork.gamecore.eventbus.clients.DelegatingClient; +import mightypork.gamecore.eventbus.events.Updateable; import mightypork.gamecore.util.ion.IonBundle; import mightypork.gamecore.util.ion.IonObjBundled; +import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.algo.Step; import mightypork.gamecore.util.math.constraints.vect.Vect; @@ -27,7 +27,7 @@ import mightypork.rogue.world.level.Level; * * @author MightyPork */ -public class World implements DelegatingClient, BusAccess, IonObjBundled, Pauseable { +public class World implements DelegatingClient, BusAccess, IonObjBundled, Pauseable, Updateable { /** * Convenient access to player-related methods and data @@ -36,7 +36,6 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea */ public class PlayerFacade { - public boolean canAscend() { return playerInfo.getLevelNumber() > 0; @@ -52,8 +51,11 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea playerInfo.setLevelNumber(lvl_num + 1); + getLevel().forceFreeTile(getLevel().getEnterPoint()); getLevel().addEntity(playerEntity, getLevel().getEnterPoint()); getLevel().explore(getCoord()); + + msgEnterFloor(getLevelNumber()); } @@ -72,8 +74,11 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea playerInfo.setLevelNumber(lvl_num - 1); + getLevel().forceFreeTile(getLevel().getExitPoint()); getLevel().addEntity(playerEntity, getLevel().getExitPoint()); getLevel().explore(getCoord()); + + msgEnterFloor(getLevelNumber()); } @@ -176,10 +181,16 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea if (weapon == null) return PlayerInfo.BARE_ATTACK; - return Math.min(weapon.getAttackPoints(), playerInfo.BARE_ATTACK); + return PlayerInfo.BARE_ATTACK + weapon.getAttackPoints(); } + /** + * Eat food. + * + * @param itm food item + * @return if something was eaten + */ public boolean eatFood(Item itm) { if (itm == null || itm.isEmpty() || itm.getType() != ItemType.FOOD) return false; @@ -187,11 +198,13 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea if (getHealth() < getHealthMax()) { playerEntity.health.addHealth(itm.getFoodPoints()); + itm.consume(); + + msgEat(itm); return true; } - return false; } @@ -202,7 +215,13 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea } - public int getSelectedWeapon() + public Item getSelectedWeapon() + { + return playerInfo.getSelectedWeapon(); + } + + + public int getSelectedWeaponIndex() { return playerInfo.getSelectedWeaponIndex(); } @@ -210,17 +229,122 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea public void tryToEatSomeFood() { + List foods = new ArrayList<>(); for (int i = 0; i < getInventory().getSize(); i++) { final Item itm = getInventory().getItem(i); - if (itm == null || itm.isEmpty()) continue; + if (itm != null && itm.getType() == ItemType.FOOD) { + foods.add(itm); + } + } + + // sort from smallest to biggest foods + Collections.sort(foods, new Comparator() { - final Item slice = itm.split(1); - if (!eatFood(slice)) itm.addItem(slice); - if (itm.isEmpty()) getInventory().setItem(i, null); - return; + @Override + public int compare(Item o1, Item o2) + { + return (o1.getFoodPoints() - o2.getFoodPoints()); + } + }); + + for (Item itm : foods) { + if (eatFood(itm)) { + getInventory().clean(); + return; + } } + + msgNoMoreFood(); + } + + + public void attack(Entity prey) + { + int attackPoints = getAttackStrength(); + + prey.receiveAttack(getPlayer().getEntity(), attackPoints); + + if (prey.isDead()) { + msgKill(prey); + } + + Item wpn = getSelectedWeapon(); + + if (wpn != null) { + wpn.use(); + if (wpn.isEmpty()) { + msgWeaponBreak(wpn); + + getInventory().clean(); + selectWeapon(-1); + + pickBestWeaponIfNoneSelected(); + } + } + } + + private void pickBestWeaponIfNoneSelected() + { + if (getSelectedWeapon() != null) return; + + List wpns = new ArrayList<>(); + for (int i = 0; i < getInventory().getSize(); i++) { + final Item itm = getInventory().getItem(i); + if (itm != null && itm.getType() == ItemType.WEAPON) { + wpns.add(itm); + } + } + + // sort from smallest to biggest foods + Collections.sort(wpns, new Comparator() { + + @Override + public int compare(Item o1, Item o2) + { + return (o2.getAttackPoints() - o1.getAttackPoints()); + } + }); + + for (Item itm : wpns) { + for (int i = 0; i < getInventory().getSize(); i++) { + final Item itm2 = getInventory().getItem(i); + if (itm2 == itm) { + selectWeapon(i); + break; + } + } + break; // just one cycle + } + + msgEquipWeapon(getSelectedWeapon()); + } + + + public boolean addItem(Item item) + { + if (!getInventory().addItem(item)) { + + msgCannotPick(); + + return false; + } + + msgPick(item); + + if (item.getType() == ItemType.WEAPON) { + if (getSelectedWeapon() != null) { + if (item.getAttackPoints() > getSelectedWeapon().getAttackPoints()) { + selectWeapon(-1); // unselect to grab the best one + } + } + + pickBestWeaponIfNoneSelected(); + } + + return true; + } } // not saved stuffs @@ -229,10 +353,10 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea private BusAccess bus; private int pauseDepth = 0; - private final ArrayList levels = new ArrayList<>(); private final PlayerInfo playerInfo = new PlayerInfo(); + private final WorldConsole console = new WorldConsole(); /** World seed */ private long seed; @@ -342,6 +466,8 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea playerInfo.setLevelNumber(0); playerInfo.setEID(playerEid); + + msgEnterFloor(0); } @@ -390,4 +516,79 @@ public class World implements DelegatingClient, BusAccess, IonObjBundled, Pausea return player; } + + @Override + public void update(double delta) + { + if (isPaused()) return; + + // update console timing + console.update(delta); + } + + + public WorldConsole getConsole() + { + return console; + } + + + public void msgPick(Item item) + { + console.addMessage("You've picked a " + item.getVisualName() + "."); + } + + + public void msgWeaponBreak(Item item) + { + console.addMessage("Your " + item.getVisualName() + " has broken!"); + } + + + public void msgEquipWeapon(Item item) + { + console.addMessage("You're now wielding " + (item == null ? "NOTHING" : "a " + item.getVisualName()) + "."); + } + + + public void msgEat(Item item) + { + console.addMessage("You've eaten a " + item.getVisualName() + "."); + } + + + public void msgKill(Entity prey) + { + console.addMessage("You've killed a " + prey.getVisualName() + "."); + } + + + public void msgDie(Entity attacker) + { + console.addMessage("You've been defeated by a " + attacker.getVisualName() + "!"); + } + + + public void msgDiscoverSecretDoor() + { + console.addMessage("You've discovered a secret door."); + } + + + public void msgNoMoreFood() + { + console.addMessage("You have no more food!"); + } + + + public void msgCannotPick() + { + console.addMessage("Inventory is full."); + } + + + public void msgEnterFloor(int floor) + { + console.addMessage("~ " + Calc.ordinal(floor + 1) + " floor ~"); + } } diff --git a/src/mightypork/rogue/world/WorldConsole.java b/src/mightypork/rogue/world/WorldConsole.java new file mode 100644 index 0000000..0c00fbf --- /dev/null +++ b/src/mightypork/rogue/world/WorldConsole.java @@ -0,0 +1,103 @@ +package mightypork.rogue.world; + + +import java.util.*; + +import mightypork.gamecore.eventbus.events.Updateable; +import mightypork.gamecore.util.math.Easing; +import mightypork.gamecore.util.math.constraints.num.mutable.NumAnimated; + + +public class WorldConsole implements Updateable { + + private static final double DURATION = 5; + + public class Entry implements Updateable { + + private final String text; + private final NumAnimated fadeout; + private boolean fading = false; + private double elapsed = 0; + + + private Entry(String text) { + this.text = text; + this.fadeout = new NumAnimated(1, Easing.LINEAR); + this.fadeout.setDefaultDuration(0.5); + } + + + @Override + public void update(double delta) + { + elapsed += delta; + + if (fading) { + fadeout.update(delta); + return; + } + + if (elapsed > DURATION) { + fading = true; + fadeout.fadeOut(); + } + } + + + private boolean canRemove() + { + return fading && fadeout.isFinished(); + } + + + public double getAlpha() + { + return !fading ? 1 : fadeout.value(); + } + + + public String getMessage() + { + return text; + } + + + public double getAge() + { + return elapsed; + } + } + + private final Deque entries = new LinkedList<>(); + + + @Override + public void update(double delta) + { + for (Iterator iter = entries.iterator(); iter.hasNext();) { + Entry e = iter.next(); + + e.update(delta); + + if (e.canRemove()) { + iter.remove(); + } + } + } + + + public void addMessage(String message) + { + entries.addFirst(new Entry(message)); + } + + + public Collection getEntries() + { + return entries; + } + + public void clear() { + entries.clear(); + } +} diff --git a/src/mightypork/rogue/world/WorldCreator.java b/src/mightypork/rogue/world/WorldCreator.java index 873427a..71247a9 100644 --- a/src/mightypork/rogue/world/WorldCreator.java +++ b/src/mightypork/rogue/world/WorldCreator.java @@ -20,7 +20,7 @@ public class WorldCreator { final World w = new World(); w.setSeed(seed); - final int count = 6; + final int count = 7; for (int i = 1; i <= count; i++) { final Level l = LevelGenerator.build(w, rand.nextLong(), i, LevelGenerator.DUNGEON_THEME, i == count); diff --git a/src/mightypork/rogue/world/entity/Entity.java b/src/mightypork/rogue/world/entity/Entity.java index e87e7da..209bf80 100644 --- a/src/mightypork/rogue/world/entity/Entity.java +++ b/src/mightypork/rogue/world/entity/Entity.java @@ -14,10 +14,10 @@ import mightypork.gamecore.util.ion.IonBundle; import mightypork.gamecore.util.ion.IonObjBundled; import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; +import mightypork.rogue.Config; import mightypork.rogue.world.World; import mightypork.rogue.world.entity.modules.EntityModuleHealth; import mightypork.rogue.world.entity.modules.EntityModulePosition; -import mightypork.rogue.world.entity.render.EntityRenderer; import mightypork.rogue.world.level.Level; import mightypork.rogue.world.level.render.MapRenderContext; @@ -43,6 +43,7 @@ public abstract class Entity implements IonObjBundled, Updateable { public final EntityModulePosition pos = new EntityModulePosition(this); public final EntityModuleHealth health = new EntityModuleHealth(this); private double despawnDelay = 1; + protected Entity lastAttacker; public Entity(EntityModel model, int eid) @@ -154,7 +155,7 @@ public abstract class Entity implements IonObjBundled, Updateable { @DefaultImpl public final void render(MapRenderContext context) { - if (context.getTile(getCoord()).isExplored()) { + if (context.getTile(getCoord()).isExplored() || !Config.RENDER_UFOG) { getRenderer().render(context); } } @@ -244,6 +245,7 @@ public abstract class Entity implements IonObjBundled, Updateable { */ public void receiveAttack(Entity attacker, int attackStrength) { + this.lastAttacker = attacker; health.receiveDamage(attackStrength); } @@ -264,4 +266,5 @@ public abstract class Entity implements IonObjBundled, Updateable { return despawnDelay; } + public abstract String getVisualName(); } diff --git a/src/mightypork/rogue/world/entity/render/EntityRenderer.java b/src/mightypork/rogue/world/entity/EntityRenderer.java similarity index 78% rename from src/mightypork/rogue/world/entity/render/EntityRenderer.java rename to src/mightypork/rogue/world/entity/EntityRenderer.java index c35243b..19705ee 100644 --- a/src/mightypork/rogue/world/entity/render/EntityRenderer.java +++ b/src/mightypork/rogue/world/entity/EntityRenderer.java @@ -1,4 +1,4 @@ -package mightypork.rogue.world.entity.render; +package mightypork.rogue.world.entity; import mightypork.rogue.world.level.render.MapRenderContext; diff --git a/src/mightypork/rogue/world/entity/entities/MonsterAi.java b/src/mightypork/rogue/world/entity/entities/MonsterAi.java index fc1fb68..fb9791a 100644 --- a/src/mightypork/rogue/world/entity/entities/MonsterAi.java +++ b/src/mightypork/rogue/world/entity/entities/MonsterAi.java @@ -94,9 +94,7 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { { if (entity.isDead()) return; - //System.out.println("monster ai step finished."); if (chasing) { - //System.out.println("chasing.."); final Entity prey = getPreyEntity(); if (!isPreyValid(prey)) { stopChasing(); @@ -283,7 +281,6 @@ public class MonsterAi extends EntityModule implements EntityMoveListener { return; } - //System.out.println("step to prey"); entity.pos.cancelPath(); entity.pos.addSteps(preyPath); } diff --git a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java index 61af7c8..9e9183d 100644 --- a/src/mightypork/rogue/world/entity/entities/PlayerEntity.java +++ b/src/mightypork/rogue/world/entity/entities/PlayerEntity.java @@ -3,13 +3,8 @@ package mightypork.rogue.world.entity.entities; import mightypork.gamecore.util.math.algo.Coord; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; -import mightypork.rogue.world.entity.Entity; -import mightypork.rogue.world.entity.EntityModel; -import mightypork.rogue.world.entity.EntityModule; -import mightypork.rogue.world.entity.EntityPathFinder; -import mightypork.rogue.world.entity.EntityType; +import mightypork.rogue.world.entity.*; import mightypork.rogue.world.entity.modules.EntityMoveListener; -import mightypork.rogue.world.entity.render.EntityRenderer; import mightypork.rogue.world.entity.render.EntityRendererMobLR; import mightypork.rogue.world.events.PlayerKilledEvent; import mightypork.rogue.world.events.PlayerStepEndEvent; @@ -21,8 +16,7 @@ public class PlayerEntity extends Entity { class PlayerAi extends EntityModule implements EntityMoveListener { - public PlayerAi(Entity entity) - { + public PlayerAi(Entity entity) { super(entity); setDespawnDelay(2); @@ -43,7 +37,7 @@ public class PlayerEntity extends Entity { final Tile t = getLevel().getTile(getCoord()); if (t.hasItem()) { final Item item = t.pickItem(); - if (getWorld().getPlayer().getInventory().addItem(item)) { + if (getWorld().getPlayer().addItem(item)) { // player picked item } else { t.dropItem(item); // put back. @@ -85,8 +79,7 @@ public class PlayerEntity extends Entity { private final PlayerAi ai = new PlayerAi(this); - public PlayerEntity(EntityModel model, int eid) - { + public PlayerEntity(EntityModel model, int eid) { super(model, eid); pos.setStepTime(0.25); @@ -141,5 +134,15 @@ public class PlayerEntity extends Entity { { // send kill event to listeners, after the entity has despawned (disappeared) getWorld().getEventBus().sendDelayed(new PlayerKilledEvent(), getDespawnDelay()); + + getWorld().msgDie(lastAttacker); + + } + + + @Override + public String getVisualName() + { + return "Player"; } } diff --git a/src/mightypork/rogue/world/entity/entities/RatEntity.java b/src/mightypork/rogue/world/entity/entities/RatEntity.java index 93ace9f..8754400 100644 --- a/src/mightypork/rogue/world/entity/entities/RatEntity.java +++ b/src/mightypork/rogue/world/entity/entities/RatEntity.java @@ -1,14 +1,14 @@ package mightypork.rogue.world.entity.entities; +import mightypork.gamecore.util.math.Calc; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.entity.EntityModel; import mightypork.rogue.world.entity.EntityPathFinder; +import mightypork.rogue.world.entity.EntityRenderer; import mightypork.rogue.world.entity.EntityType; -import mightypork.rogue.world.entity.render.EntityRenderer; import mightypork.rogue.world.entity.render.EntityRendererMobLR; -import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.Items; @@ -22,8 +22,7 @@ public class RatEntity extends Entity { private EntityRenderer renderer; - public RatEntity(EntityModel model, int eid) - { + public RatEntity(EntityModel model, int eid) { super(model, eid); addModule("ai", ai); @@ -32,8 +31,8 @@ public class RatEntity extends Entity { pos.setStepTime(0.5); setDespawnDelay(1); - health.setMaxHealth(3 + rand.nextInt(3)); - health.fill(); // fill health bar to max + health.setMaxHealth(5); + health.setHealth(Calc.randInt(rand, 3, 5)); // fill health bar to max health.setHitCooldownTime(0.3); } @@ -77,10 +76,27 @@ public class RatEntity extends Entity { @Override public void onCorpseRemoved() { - // drop rat meat - final Item meat = Items.MEAT.createItem(); + // drop rat stuff + + if(rand.nextInt(7) == 0) { + getLevel().dropNear(getCoord(), Items.BONE.createItem()); + return; + } + + if(rand.nextInt(3) == 0) { + getLevel().dropNear(getCoord(), Items.MEAT.createItem()); + return; + } - getLevel().dropNear(getCoord(), meat); + if(rand.nextInt(2) == 0) { + getLevel().dropNear(getCoord(), Items.CHEESE.createItem()); + return; + } } + @Override + public String getVisualName() + { + return "Gray Rat"; + } } diff --git a/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java b/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java index a41d240..b26884e 100644 --- a/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java +++ b/src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java @@ -13,6 +13,7 @@ import mightypork.gamecore.util.math.constraints.rect.Rect; import mightypork.gamecore.util.math.constraints.vect.Vect; import mightypork.rogue.Res; import mightypork.rogue.world.entity.Entity; +import mightypork.rogue.world.entity.EntityRenderer; import mightypork.rogue.world.level.render.MapRenderContext; diff --git a/src/mightypork/rogue/world/gen/LevelGenerator.java b/src/mightypork/rogue/world/gen/LevelGenerator.java index dff8ee7..7f18215 100644 --- a/src/mightypork/rogue/world/gen/LevelGenerator.java +++ b/src/mightypork/rogue/world/gen/LevelGenerator.java @@ -10,6 +10,7 @@ import mightypork.rogue.world.entity.Entities; import mightypork.rogue.world.entity.Entity; import mightypork.rogue.world.gen.rooms.Rooms; import mightypork.rogue.world.gen.themes.ThemeBrick; +import mightypork.rogue.world.item.Items; import mightypork.rogue.world.level.Level; @@ -18,6 +19,7 @@ public class LevelGenerator { public static final MapTheme DUNGEON_THEME = new ThemeBrick(); + @SuppressWarnings("fallthrough") public static Level build(World world, long seed, int complexity, MapTheme theme, boolean lastLevel) { Log.f3("Generating level of complexity: " + complexity); @@ -31,16 +33,34 @@ public class LevelGenerator { // start map.addRoom(Rooms.ENTRANCE, true); - for (int i = 0; i < 1 + complexity / 2 + rand.nextInt((int) (1 + complexity * 0.3)); i++) { + for (int i = 0; i < 1 + complexity / 2 + rand.nextInt((int) (1 + complexity * 0.2)); i++) { map.addRoom(Rooms.BASIC, false); if (rand.nextInt(7) > 0) map.addRoom(Rooms.SECRET, false); - if (rand.nextInt(6) > 0) map.addRoom(Rooms.DEAD_END, false); + if (rand.nextInt(7) > 0) map.addRoom(Rooms.DEAD_END, false); } if (!lastLevel) map.addRoom(Rooms.EXIT, true); map.buildCorridors(); + switch(complexity) { + default: + case 3: + case 2: + if(rand.nextInt(2)==0) map.dropInMap(Items.CLUB.createItem(), 50); + case 1: + case 0: + if(rand.nextInt(2)==0) map.dropInMap(Items.ROCK.createItem(), 50); + } + + if(complexity == 6) { + map.dropInMap(Items.SWORD.createItem(), 200); + } + + if(complexity == 5) { + map.dropInMap(Items.HAMMER.createItem(), 100); + } + final Coord size = map.getNeededSize(); final Level lvl = new Level(size.x, size.y); @@ -49,6 +69,8 @@ public class LevelGenerator { // TODO tmp // spawn rats + // TODO entities in ScratchMap itself + final Coord pos = Coord.make(0, 0); for (int i = 0; i < 3 + complexity + rand.nextInt(1 + complexity); i++) { @@ -62,20 +84,6 @@ public class LevelGenerator { } } -// for (int i = 0; i < 4 + complexity + rand.nextInt(1 + complexity); i++) { -// -// final Item meat = Items.MEAT.createItem(); -// -// for (int j = 0; j < 20; j++) { -// pos.x = rand.nextInt(lvl.getWidth()); -// pos.y = rand.nextInt(lvl.getHeight()); -// -// final Tile t = lvl.getTile(pos); -// if (t.dropItem(meat)) break; -// } -// } - - return lvl; } } diff --git a/src/mightypork/rogue/world/gen/ScratchMap.java b/src/mightypork/rogue/world/gen/ScratchMap.java index cbbd0a4..1d976c5 100644 --- a/src/mightypork/rogue/world/gen/ScratchMap.java +++ b/src/mightypork/rogue/world/gen/ScratchMap.java @@ -14,6 +14,7 @@ import mightypork.gamecore.util.math.algo.Sides; import mightypork.gamecore.util.math.algo.Step; import mightypork.gamecore.util.math.algo.pathfinding.Heuristic; import mightypork.gamecore.util.math.algo.pathfinding.PathFinder; +import mightypork.rogue.world.item.Item; import mightypork.rogue.world.level.Level; import mightypork.rogue.world.tile.Tile; import mightypork.rogue.world.tile.TileModel; @@ -42,7 +43,7 @@ public class ScratchMap { public boolean isAccessible(Coord pos) { if (!isIn(pos)) return false; - final Tile t = get(pos); + final Tile t = getTile(pos); if (t.isStairs()) return false; return t.isPotentiallyWalkable() || (t.genData.protection != TileProtectLevel.STRONG); } @@ -51,7 +52,7 @@ public class ScratchMap { @Override public int getCost(Coord last, Coord pos) { - final Tile t = get(pos); + final Tile t = getTile(pos); switch (t.getType()) { case NULL: @@ -173,7 +174,7 @@ public class ScratchMap { clampBounds(); nodes.add(center); -// Log.f3("placed room: " + rd.min + " -> " + rd.max); + Log.f3("Placed room on " + Calc.ordinal(1+failed_total) + " try."); return; } else { @@ -222,7 +223,7 @@ public class ScratchMap { } - public Tile get(Coord pos) + public Tile getTile(Coord pos) { if (!isIn(pos)) { throw new IndexOutOfBoundsException("Tile not in map: " + pos); @@ -267,7 +268,7 @@ public class ScratchMap { final Coord c = Coord.make(0, 0); for (c.x = min.x; c.x <= max.x; c.x++) for (c.y = min.y; c.y <= max.y; c.y++) - get(c).genData.protection = prot; + getTile(c).genData.protection = prot; } @@ -357,7 +358,7 @@ public class ScratchMap { genMax.y = Math.max(genMax.y, c.y); clampBounds(); - final Tile current = get(c); + final Tile current = getTile(c); if (!current.isNull() && (current.isPotentiallyWalkable() || current.isStairs())) continue; // floor already, let it be if (i == 0 && j == 0) { @@ -394,7 +395,7 @@ public class ScratchMap { final Coord cc = pos.add(Sides.get(i)); if (!isIn(cc)) continue; - if (get(cc).isWall()) { + if (getTile(cc).isWall()) { walls |= Sides.bit(i); } } @@ -409,7 +410,7 @@ public class ScratchMap { final Coord cc = pos.add(Sides.get(i)); if (!isIn(cc)) continue; - if (get(cc).isFloor()) { + if (getTile(cc).isFloor()) { floors |= Sides.bit(i); } } @@ -424,7 +425,7 @@ public class ScratchMap { final Coord cc = pos.add(Sides.get(i)); if (!isIn(cc)) continue; - if (get(cc).isDoor()) { + if (getTile(cc).isDoor()) { doors |= Sides.bit(i); } } @@ -438,7 +439,7 @@ public class ScratchMap { for (int i = 0; i <= 7; i++) { final Coord cc = pos.add(Sides.get(i)); - if (!isIn(cc) || get(cc).isNull()) { + if (!isIn(cc) || getTile(cc).isNull()) { nils |= Sides.bit(i); } } @@ -459,7 +460,7 @@ public class ScratchMap { for (c.x = 0; c.x < width; c.x++) { for (c.y = 0; c.y < height; c.y++) { - final Tile t = get(c); + final Tile t = getTile(c); final boolean isNull = t.isNull(); final boolean isDoor = !isNull && t.isDoor(); @@ -533,14 +534,13 @@ public class ScratchMap { for (c.x = genMin.x, c1.x = 0; c.x <= genMax.x; c.x++, c1.x++) { for (c.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) { - level.setTile(c1, get(c)); + level.setTile(c1, getTile(c)); } } final Coord entrance = new Coord(enterPoint.x - genMin.x, enterPoint.y - genMin.y); level.setEnterPoint(entrance); -// System.out.println("Entrance = " + entrance + ", original: " + enterPoint + ", minG=" + genMin + ", maxG=" + genMax); - + final Coord exit = new Coord(exitPoint.x - genMin.x, exitPoint.y - genMin.y); level.setExitPoint(exit); } @@ -556,4 +556,26 @@ public class ScratchMap { { exitPoint.setTo(pos); } + + + public boolean dropInArea(Item item, Coord min, Coord max, int tries) + { + Coord pos = Coord.zero(); + + for(int i=0; i0) uses--; + if(uses==0) consume(); + } + + + public abstract boolean isDamageable(); + + + public abstract String getVisualName(); } diff --git a/src/mightypork/rogue/world/item/ItemModel.java b/src/mightypork/rogue/world/item/ItemModel.java index b04dd05..173d69e 100644 --- a/src/mightypork/rogue/world/item/ItemModel.java +++ b/src/mightypork/rogue/world/item/ItemModel.java @@ -19,8 +19,7 @@ public final class ItemModel { public final Class itemClass; - public ItemModel(int id, Class item) - { + public ItemModel(int id, Class item) { Items.register(id, this); this.id = id; this.itemClass = item; @@ -30,10 +29,15 @@ public final class ItemModel { /** * @return new item instance of this type */ - public T createItem() + public Item createItem() { try { - return (T) itemClass.getConstructor(ItemModel.class).newInstance(this); + Item itm = itemClass.getConstructor(ItemModel.class).newInstance(this); + + itm.setRemainingUses(itm.getMaxUses()); + + return itm; + } catch (final Exception e) { throw new RuntimeException("Could not instantiate an item.", e); } diff --git a/src/mightypork/rogue/world/item/ItemRenderer.java b/src/mightypork/rogue/world/item/ItemRenderer.java index 61b936d..d4a26e3 100644 --- a/src/mightypork/rogue/world/item/ItemRenderer.java +++ b/src/mightypork/rogue/world/item/ItemRenderer.java @@ -6,6 +6,16 @@ import mightypork.gamecore.util.math.constraints.rect.Rect; public abstract class ItemRenderer { + protected final Item item; + + + + public ItemRenderer(Item item) { + this.item = item; + } + + + public abstract void render(Rect r); } diff --git a/src/mightypork/rogue/world/item/Items.java b/src/mightypork/rogue/world/item/Items.java index ecc872e..a7ebd0b 100644 --- a/src/mightypork/rogue/world/item/Items.java +++ b/src/mightypork/rogue/world/item/Items.java @@ -6,7 +6,14 @@ import java.util.Collection; import mightypork.gamecore.util.ion.IonInput; import mightypork.gamecore.util.ion.IonOutput; -import mightypork.rogue.world.item.items.ItemMeat; +import mightypork.rogue.world.item.items.food.ItemCheese; +import mightypork.rogue.world.item.items.food.ItemMeat; +import mightypork.rogue.world.item.items.food.ItemSandwich; +import mightypork.rogue.world.item.items.weapons.ItemBone; +import mightypork.rogue.world.item.items.weapons.ItemClub; +import mightypork.rogue.world.item.items.weapons.ItemHammer; +import mightypork.rogue.world.item.items.weapons.ItemStone; +import mightypork.rogue.world.item.items.weapons.ItemSword; /** @@ -19,6 +26,13 @@ public final class Items { private static final ItemModel[] items = new ItemModel[256]; public static final ItemModel MEAT = new ItemModel(1, ItemMeat.class); + public static final ItemModel CHEESE = new ItemModel(2, ItemCheese.class); + public static final ItemModel BONE = new ItemModel(3, ItemBone.class); + public static final ItemModel SANDWICH = new ItemModel(4, ItemSandwich.class); + public static final ItemModel CLUB = new ItemModel(5, ItemClub.class); + public static final ItemModel HAMMER = new ItemModel(6, ItemHammer.class); + public static final ItemModel SWORD = new ItemModel(7, ItemSword.class); + public static final ItemModel ROCK = new ItemModel(8, ItemStone.class); public static void register(int id, ItemModel model) diff --git a/src/mightypork/rogue/world/item/items/ItemBaseFood.java b/src/mightypork/rogue/world/item/items/ItemBaseFood.java index dcfa2d8..a539334 100644 --- a/src/mightypork/rogue/world/item/items/ItemBaseFood.java +++ b/src/mightypork/rogue/world/item/items/ItemBaseFood.java @@ -34,4 +34,15 @@ public abstract class ItemBaseFood extends Item { return ItemType.FOOD; } + @Override + public boolean isDamageable() + { + return false; + } + + @Override + public int getMaxUses() + { + return 1; + } } diff --git a/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java b/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java index 1b6e474..394a25e 100644 --- a/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java +++ b/src/mightypork/rogue/world/item/items/ItemBaseWeapon.java @@ -34,4 +34,10 @@ public abstract class ItemBaseWeapon extends Item { return ItemType.WEAPON; } + @Override + public boolean isDamageable() + { + return true; + } + } diff --git a/src/mightypork/rogue/world/item/items/food/ItemCheese.java b/src/mightypork/rogue/world/item/items/food/ItemCheese.java new file mode 100644 index 0000000..d90ab8b --- /dev/null +++ b/src/mightypork/rogue/world/item/items/food/ItemCheese.java @@ -0,0 +1,38 @@ +package mightypork.rogue.world.item.items.food; + + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseFood; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemCheese extends ItemBaseFood { + + public ItemCheese(ItemModel model) + { + super(model); + } + + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.cheese")); + } + + + @Override + public int getFoodPoints() + { + return 2; + } + + + @Override + public String getVisualName() + { + return "Cheese"; + } +} diff --git a/src/mightypork/rogue/world/item/items/ItemMeat.java b/src/mightypork/rogue/world/item/items/food/ItemMeat.java similarity index 61% rename from src/mightypork/rogue/world/item/items/ItemMeat.java rename to src/mightypork/rogue/world/item/items/food/ItemMeat.java index c4e74bf..d5d56e4 100644 --- a/src/mightypork/rogue/world/item/items/ItemMeat.java +++ b/src/mightypork/rogue/world/item/items/food/ItemMeat.java @@ -1,9 +1,10 @@ -package mightypork.rogue.world.item.items; +package mightypork.rogue.world.item.items.food; import mightypork.rogue.Res; import mightypork.rogue.world.item.ItemModel; import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseFood; import mightypork.rogue.world.item.render.QuadItemRenderer; @@ -18,14 +19,20 @@ public class ItemMeat extends ItemBaseFood { @Override protected ItemRenderer makeRenderer() { - return new QuadItemRenderer(Res.txq("item.meat")); + return new QuadItemRenderer(this, Res.txq("item.meat")); } @Override public int getFoodPoints() { - return 2; + return 3; } + + @Override + public String getVisualName() + { + return "Chunk Of Meat"; + } } diff --git a/src/mightypork/rogue/world/item/items/food/ItemSandwich.java b/src/mightypork/rogue/world/item/items/food/ItemSandwich.java new file mode 100644 index 0000000..6378c06 --- /dev/null +++ b/src/mightypork/rogue/world/item/items/food/ItemSandwich.java @@ -0,0 +1,38 @@ +package mightypork.rogue.world.item.items.food; + + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseFood; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemSandwich extends ItemBaseFood { + + public ItemSandwich(ItemModel model) + { + super(model); + } + + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.sandwich")); + } + + + @Override + public int getFoodPoints() + { + return 6; + } + + + @Override + public String getVisualName() + { + return "Sandwich"; + } +} diff --git a/src/mightypork/rogue/world/item/items/weapons/ItemBone.java b/src/mightypork/rogue/world/item/items/weapons/ItemBone.java new file mode 100644 index 0000000..ca34b9a --- /dev/null +++ b/src/mightypork/rogue/world/item/items/weapons/ItemBone.java @@ -0,0 +1,39 @@ +package mightypork.rogue.world.item.items.weapons; + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemBone extends ItemBaseWeapon { + + public ItemBone(ItemModel model) { + super(model); + } + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.bone")); + } + + @Override + public int getAttackPoints() + { + return 1; + } + + @Override + public int getMaxUses() + { + return 15; + } + + @Override + public String getVisualName() + { + return "Bone"; + } +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/item/items/weapons/ItemClub.java b/src/mightypork/rogue/world/item/items/weapons/ItemClub.java new file mode 100644 index 0000000..96e6be8 --- /dev/null +++ b/src/mightypork/rogue/world/item/items/weapons/ItemClub.java @@ -0,0 +1,39 @@ +package mightypork.rogue.world.item.items.weapons; + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemClub extends ItemBaseWeapon { + + public ItemClub(ItemModel model) { + super(model); + } + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.club")); + } + + @Override + public int getAttackPoints() + { + return 3; + } + + @Override + public int getMaxUses() + { + return 25; + } + + @Override + public String getVisualName() + { + return "Wooden Club"; + } +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/item/items/weapons/ItemHammer.java b/src/mightypork/rogue/world/item/items/weapons/ItemHammer.java new file mode 100644 index 0000000..d7de95e --- /dev/null +++ b/src/mightypork/rogue/world/item/items/weapons/ItemHammer.java @@ -0,0 +1,39 @@ +package mightypork.rogue.world.item.items.weapons; + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemHammer extends ItemBaseWeapon { + + public ItemHammer(ItemModel model) { + super(model); + } + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.hammer")); + } + + @Override + public int getAttackPoints() + { + return 4; + } + + @Override + public int getMaxUses() + { + return 50; + } + + @Override + public String getVisualName() + { + return "Hammer"; + } +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/item/items/weapons/ItemStone.java b/src/mightypork/rogue/world/item/items/weapons/ItemStone.java new file mode 100644 index 0000000..d680dac --- /dev/null +++ b/src/mightypork/rogue/world/item/items/weapons/ItemStone.java @@ -0,0 +1,39 @@ +package mightypork.rogue.world.item.items.weapons; + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemStone extends ItemBaseWeapon { + + public ItemStone(ItemModel model) { + super(model); + } + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.stone")); + } + + @Override + public int getAttackPoints() + { + return 2; + } + + @Override + public int getMaxUses() + { + return 20; + } + + @Override + public String getVisualName() + { + return "Rock"; + } +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/item/items/weapons/ItemSword.java b/src/mightypork/rogue/world/item/items/weapons/ItemSword.java new file mode 100644 index 0000000..efbfea5 --- /dev/null +++ b/src/mightypork/rogue/world/item/items/weapons/ItemSword.java @@ -0,0 +1,39 @@ +package mightypork.rogue.world.item.items.weapons; + +import mightypork.rogue.Res; +import mightypork.rogue.world.item.ItemModel; +import mightypork.rogue.world.item.ItemRenderer; +import mightypork.rogue.world.item.items.ItemBaseWeapon; +import mightypork.rogue.world.item.render.QuadItemRenderer; + + +public class ItemSword extends ItemBaseWeapon { + + public ItemSword(ItemModel model) { + super(model); + } + + @Override + protected ItemRenderer makeRenderer() + { + return new QuadItemRenderer(this, Res.txq("item.sword")); + } + + @Override + public int getAttackPoints() + { + return 6; + } + + @Override + public int getMaxUses() + { + return 100; + } + + @Override + public String getVisualName() + { + return "Wooden Club"; + } +} \ No newline at end of file diff --git a/src/mightypork/rogue/world/item/render/QuadItemRenderer.java b/src/mightypork/rogue/world/item/render/QuadItemRenderer.java index 2d59ee5..7940487 100644 --- a/src/mightypork/rogue/world/item/render/QuadItemRenderer.java +++ b/src/mightypork/rogue/world/item/render/QuadItemRenderer.java @@ -3,8 +3,12 @@ package mightypork.rogue.world.item.render; import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; -import mightypork.gamecore.resources.textures.TxSheet; +import mightypork.gamecore.util.math.color.Color; +import mightypork.gamecore.util.math.color.pal.RGB; +import mightypork.gamecore.util.math.constraints.num.Num; import mightypork.gamecore.util.math.constraints.rect.Rect; +import mightypork.gamecore.util.math.constraints.rect.mutable.RectVar; +import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.ItemRenderer; @@ -12,16 +16,14 @@ public class QuadItemRenderer extends ItemRenderer { private final TxQuad txq; + private final RectVar rrect = Rect.makeVar(); + private final Rect usesRect = rrect.topLeft().startRect().grow(Num.ZERO, rrect.width().perc(40), Num.ZERO, rrect.height().perc(8)); + private final Num hAlpha = Num.make(0.7); - public QuadItemRenderer(TxQuad txq) - { - this.txq = txq; - } - - public QuadItemRenderer(TxSheet txs) - { - this.txq = txs.getQuad(0); + public QuadItemRenderer(Item item, TxQuad txq) { + super(item); + this.txq = txq; } @@ -29,6 +31,23 @@ public class QuadItemRenderer extends ItemRenderer { public void render(Rect r) { Render.quadTextured(r, txq); + + if (item.isDamageable()) { + Color.pushAlpha(hAlpha); + + rrect.setTo(r); + + Render.quadColor(usesRect, RGB.BLACK); + + double useRatio = (item.getRemainingUses() / (double)item.getMaxUses()); + + Color barColor = (useRatio > 0.6 ? RGB.GREEN : useRatio > 0.2 ? RGB.ORANGE : RGB.RED); + + Render.quadColor(usesRect.shrinkRight(usesRect.width().value() * (1 - useRatio)), barColor); + + Color.popAlpha(); + } + } } diff --git a/src/mightypork/rogue/world/level/Level.java b/src/mightypork/rogue/world/level/Level.java index 5ef4c88..8bfb203 100644 --- a/src/mightypork/rogue/world/level/Level.java +++ b/src/mightypork/rogue/world/level/Level.java @@ -86,13 +86,11 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl private double timeSinceLastEntitySort; - public Level() - { + public Level() { } - public Level(int width, int height) - { + public Level(int width, int height) { size.setTo(width, height); buildArray(); } @@ -128,7 +126,7 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl public final Tile getTile(Coord pos) { if (!pos.isInRange(0, 0, size.x - 1, size.y - 1)) return Tiles.NULL.createTile(); // out of range - + return tiles[pos.y][pos.x]; } @@ -589,6 +587,11 @@ public class Level implements BusAccess, Updateable, DelegatingClient, Toggleabl } } + if (!getTile(pos).isWalkable()) { + // this should never happen. + setTile(pos, Tiles.BRICK_FLOOR.createTile()); + } + } diff --git a/src/mightypork/rogue/world/tile/Tile.java b/src/mightypork/rogue/world/tile/Tile.java index 6177872..a5dc18e 100644 --- a/src/mightypork/rogue/world/tile/Tile.java +++ b/src/mightypork/rogue/world/tile/Tile.java @@ -12,6 +12,7 @@ import mightypork.gamecore.util.ion.IonInput; import mightypork.gamecore.util.ion.IonObjBlob; import mightypork.gamecore.util.ion.IonOutput; import mightypork.gamecore.util.math.color.Color; +import mightypork.rogue.Config; import mightypork.rogue.world.World; import mightypork.rogue.world.item.Item; import mightypork.rogue.world.level.Level; @@ -56,7 +57,7 @@ public abstract class Tile implements BusAccess, IonObjBlob { @DefaultImpl public void renderTile(TileRenderContext context) { - if (!isExplored()) return; + if (!isExplored() && Config.RENDER_UFOG) return; initRenderer(); diff --git a/src/mightypork/rogue/world/tile/TileRenderer.java b/src/mightypork/rogue/world/tile/TileRenderer.java index d8a8c7f..cb6a14d 100644 --- a/src/mightypork/rogue/world/tile/TileRenderer.java +++ b/src/mightypork/rogue/world/tile/TileRenderer.java @@ -6,6 +6,7 @@ import mightypork.gamecore.render.Render; import mightypork.gamecore.resources.textures.TxQuad; import mightypork.gamecore.util.math.algo.Sides; import mightypork.gamecore.util.math.constraints.rect.Rect; +import mightypork.rogue.Config; import mightypork.rogue.Res; import mightypork.rogue.world.level.render.TileRenderContext; import mightypork.rogue.world.tile.render.NullTileRenderer; @@ -104,6 +105,8 @@ public abstract class TileRenderer implements Updateable { public void renderUnexploredFog(TileRenderContext context) { + if(!Config.RENDER_UFOG) return; + // TODO cache values, update neighbouring tiles upon "explored" flag changed. byte ufog = 0; diff --git a/src/mightypork/rogue/world/tile/tiles/TileBaseSecretDoor.java b/src/mightypork/rogue/world/tile/tiles/TileBaseSecretDoor.java index ebfaa4d..aa67b49 100644 --- a/src/mightypork/rogue/world/tile/tiles/TileBaseSecretDoor.java +++ b/src/mightypork/rogue/world/tile/tiles/TileBaseSecretDoor.java @@ -30,7 +30,10 @@ public abstract class TileBaseSecretDoor extends TileBaseDoor { if (clicks > 0) clicks--; - if (clicks == 0) locked = false; + if (clicks == 0) { + locked = false; + getWorld().msgDiscoverSecretDoor(); + } return true; } diff --git a/src/mightypork/rogue/world/tile/tiles/TileWithItems.java b/src/mightypork/rogue/world/tile/tiles/TileWithItems.java index 8e2bf3c..caff12d 100644 --- a/src/mightypork/rogue/world/tile/tiles/TileWithItems.java +++ b/src/mightypork/rogue/world/tile/tiles/TileWithItems.java @@ -6,6 +6,7 @@ import java.util.Stack; import mightypork.gamecore.util.ion.IonInput; import mightypork.gamecore.util.ion.IonOutput; +import mightypork.rogue.Config; import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.Items; import mightypork.rogue.world.level.render.TileRenderContext; @@ -21,8 +22,7 @@ public abstract class TileWithItems extends Tile { protected final Stack items = new Stack<>(); - public TileWithItems(TileModel model) - { + public TileWithItems(TileModel model) { super(model); } @@ -30,7 +30,7 @@ public abstract class TileWithItems extends Tile { @Override public void renderExtra(TileRenderContext context) { - if (isExplored() && !items.isEmpty()) { + if ((isExplored() || !Config.RENDER_UFOG) && !items.isEmpty()) { itemRenderer.render(items.peek(), context); } } @@ -98,18 +98,18 @@ public abstract class TileWithItems extends Tile { } - @Override - public boolean onClick() - { - if (hasItem()) { - final Item item = pickItem(); - if (getWorld().getPlayer().getInventory().addItem(item)) { - // player picked item - } else { - dropItem(item); // put back. - } - return true; - } - return false; - } +// @Override +// public boolean onClick() +// { +// if (hasItem()) { +// final Item item = pickItem(); +// if (getWorld().getPlayer().addItem(item)) { +// // player picked item +// } else { +// dropItem(item); // put back. +// } +// return true; +// } +// return false; +// } }