diff --git a/core/src/gdx/diablo/ai/Npc.java b/core/src/gdx/diablo/ai/Npc.java index e1147bc3..1e40a56b 100644 --- a/core/src/gdx/diablo/ai/Npc.java +++ b/core/src/gdx/diablo/ai/Npc.java @@ -3,6 +3,8 @@ package gdx.diablo.ai; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.scenes.scene2d.InputEvent; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; import org.apache.commons.lang3.ArrayUtils; @@ -10,20 +12,25 @@ import gdx.diablo.Diablo; import gdx.diablo.entity.Monster; import gdx.diablo.map.DS1; import gdx.diablo.screen.GameScreen; +import gdx.diablo.widget.NpcMenu; public class Npc extends AI { private static final String TAG = "Npc"; + // data\\global\\ui\\MENU\\boxpieces.DC6 + int targetId = ArrayUtils.INDEX_NOT_FOUND; float actionTimer = 0; boolean actionPerformed = false; + NpcMenu menu; + int activeAudio; public Npc(Monster entity) { super(entity); } @Override - public void interact(GameScreen gameScreen) { + public void interact(final GameScreen gameScreen) { String name = entity.getName().toLowerCase(); String id = name + "_greeting_1"; int index = Diablo.audio.play(id, false); @@ -32,10 +39,53 @@ public class Npc extends AI { Diablo.audio.play(id, false); } - actionTimer = 4; + actionTimer = Float.POSITIVE_INFINITY; actionPerformed = false; entity.target().set(entity.position()); entity.lookAt(gameScreen.player); + entity.update(0); + + if (menu == null) { + menu = new NpcMenu(entity, gameScreen, entity.getName()); + menu + // Talk + .addItem(3381, new NpcMenu(3381) + // Introduction + .addItem(3399, new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + String name = entity.getName().toLowerCase(); + String id = name + "_act1_intro"; + Diablo.audio.play(id, false); + } + }) + // Gossip + .addItem(3395, new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + String name = entity.getName().toLowerCase(); + String id = name + "_act1_gossip_1"; + Diablo.audio.play(id, false); + } + }) + .addCancel(new NpcMenu.CancellationListener() { + @Override + public void onCancelled() { + // TODO: stop audio + } + }) + .build()) + .addCancel(new NpcMenu.CancellationListener() { + @Override + public void onCancelled() { + actionTimer = 4; + entity.target().setZero(); + } + }) + .build(); + } + + gameScreen.setMenu(menu, entity); } public void update(float delta) { diff --git a/core/src/gdx/diablo/entity/Entity.java b/core/src/gdx/diablo/entity/Entity.java index 6a92319e..898e4c0c 100644 --- a/core/src/gdx/diablo/entity/Entity.java +++ b/core/src/gdx/diablo/entity/Entity.java @@ -534,7 +534,11 @@ public class Entity { } protected void updateLabel(Label label, float x, float y) { - label.setPosition(x, y + animation.getMinHeight() + label.getHeight() / 2, Align.center); + label.setPosition(x, y + getLabelOffset() + label.getHeight() / 2, Align.center); + } + + public float getLabelOffset() { + return animation.getMinHeight(); } /* diff --git a/core/src/gdx/diablo/entity/Monster.java b/core/src/gdx/diablo/entity/Monster.java index a183283e..f37d9c8b 100644 --- a/core/src/gdx/diablo/entity/Monster.java +++ b/core/src/gdx/diablo/entity/Monster.java @@ -20,7 +20,6 @@ import gdx.diablo.map.DS1; import gdx.diablo.map.DT1.Tile; import gdx.diablo.map.Map; import gdx.diablo.screen.GameScreen; -import gdx.diablo.widget.Label; public class Monster extends Entity { private static final String TAG = "Monster"; @@ -123,7 +122,7 @@ public class Monster extends Entity { point = path.points[i]; p1x = +(point.x * Tile.SUBTILE_WIDTH50) - (point.y * Tile.SUBTILE_WIDTH50); p1y = -(point.x * Tile.SUBTILE_HEIGHT50) - (point.y * Tile.SUBTILE_HEIGHT50); - Diablo.fonts.consolas16.draw(batch, Integer.toString(point.action), p1x, p1y - BOX_SIZE, 0, Align.center, false); + Diablo.fonts.consolas12.draw(batch, Integer.toString(point.action), p1x, p1y - BOX_SIZE, 0, Align.center, false); } batch.end(); batch.setShader(Diablo.shader); @@ -131,8 +130,8 @@ public class Monster extends Entity { } @Override - protected void updateLabel(Label label, float x, float y) { - label.setPosition(x, y + monstats2.pixHeight + label.getHeight() / 2, Align.center); + public float getLabelOffset() { + return monstats2.pixHeight; } @Override diff --git a/core/src/gdx/diablo/entity/StaticEntity.java b/core/src/gdx/diablo/entity/StaticEntity.java index ec45e107..34d07334 100644 --- a/core/src/gdx/diablo/entity/StaticEntity.java +++ b/core/src/gdx/diablo/entity/StaticEntity.java @@ -4,7 +4,6 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.g2d.Batch; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.Align; import gdx.diablo.Diablo; import gdx.diablo.codec.excel.Objects; @@ -12,7 +11,6 @@ import gdx.diablo.graphics.PaletteIndexedBatch; import gdx.diablo.map.DS1; import gdx.diablo.map.Map; import gdx.diablo.screen.GameScreen; -import gdx.diablo.widget.Label; public class StaticEntity extends Entity { private static final String TAG = "StaticEntity"; @@ -76,8 +74,8 @@ public class StaticEntity extends Entity { } @Override - protected void updateLabel(Label label, float x, float y) { - label.setPosition(x, y - base.NameOffset + label.getHeight() / 2, Align.center); + public float getLabelOffset() { + return -base.NameOffset; } @Override diff --git a/core/src/gdx/diablo/map/MapListener.java b/core/src/gdx/diablo/map/MapListener.java index 4053b524..2b214a21 100644 --- a/core/src/gdx/diablo/map/MapListener.java +++ b/core/src/gdx/diablo/map/MapListener.java @@ -2,7 +2,6 @@ package gdx.diablo.map; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; -import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; @@ -11,7 +10,7 @@ import com.badlogic.gdx.utils.Array; import gdx.diablo.entity.Entity; import gdx.diablo.screen.GameScreen; -public class MapListener extends InputAdapter { +public class MapListener { private final Vector2 tmpVec2 = new Vector2(); private final Vector3 tmpVec3 = new Vector3(); private final GridPoint2 tmpVec2i = new GridPoint2(); @@ -27,35 +26,30 @@ public class MapListener extends InputAdapter { this.mapRenderer = mapRenderer; } - @Override - public boolean mouseMoved(int x, int y) { - mapRenderer.unproject(x, y, tmpVec2); + // TODO: assert only 1 entity can be selected at once, once found, deselect other and set new and return + private void updateLabel(Vector2 position) { gameScreen.clearLabels(); for (Map.Zone zone : map.zones) { for (Entity entity : zone.entities) { - entity.over = entity.contains(tmpVec2); + entity.over = entity.contains(position); if (entity.over) gameScreen.addLabel(entity.getLabel()); } } - - return false; } - @Override - public boolean touchDown(int x, int y, int pointer, int button) { - setTarget(null); - mapRenderer.unproject(x, y, tmpVec2); + private boolean touchDown() { + //setTarget(null); for (Map.Zone zone : new Array.ArrayIterator<>(map.zones)) { for (Entity entity : zone.entities) { if (entity.over) { if (entity.position().dst(gameScreen.player.position()) <= entity.getInteractRange()) { setTarget(null); entity.interact(gameScreen); - return true; } else { setTarget(entity); - return true; } + + return true; } } } @@ -64,23 +58,25 @@ public class MapListener extends InputAdapter { } public void update() { - if (target != null) { - if (target.position().dst(gameScreen.player.position()) <= target.getInteractRange()) { - Entity entity = target; - setTarget(null); - entity.interact(gameScreen); + mapRenderer.unproject(tmpVec2.set(Gdx.input.getX(), Gdx.input.getY())); + updateLabel(tmpVec2); + if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) { + if (gameScreen.getMenu() != null) gameScreen.setMenu(null, null); + boolean touched = touchDown(); + if (!touched) { + mapRenderer.coords(tmpVec2.x, tmpVec2.y, tmpVec2i); + tmpVec3.set(tmpVec2i.x, tmpVec2i.y, 0); + gameScreen.player.setPath(map, tmpVec3); + } + } else { + if (target != null) { + if (target.position().dst(gameScreen.player.position()) <= target.getInteractRange()) { + Entity entity = target; + setTarget(null); + entity.interact(gameScreen); + } } - - return; } - - if (!Gdx.input.isButtonPressed(Input.Buttons.LEFT)) return; - int x = Gdx.input.getX(); - int y = Gdx.input.getY(); - mapRenderer.unproject(x, y, tmpVec2); - mapRenderer.coords(tmpVec2.x, tmpVec2.y, tmpVec2i); - tmpVec3.set(tmpVec2i.x, tmpVec2i.y, 0); - gameScreen.player.setPath(map, tmpVec3); } private void setTarget(Entity entity) { diff --git a/core/src/gdx/diablo/map/MapRenderer.java b/core/src/gdx/diablo/map/MapRenderer.java index 5755746d..821fa180 100644 --- a/core/src/gdx/diablo/map/MapRenderer.java +++ b/core/src/gdx/diablo/map/MapRenderer.java @@ -1032,12 +1032,12 @@ public class MapRenderer { batch.begin(); batch.setShader(null); - BitmapFont font = Diablo.fonts.consolas16; + BitmapFont font = Diablo.fonts.consolas12; String str = String.format(String.format("%s%n%08x", Map.ID.getName(tile.cell.id), tile.cell.value)); GlyphLayout layout = new GlyphLayout(font, str, 0, str.length(), font.getColor(), 0, Align.center, false, null); font.draw(batch, layout, px + Tile.WIDTH50, - py + Tile.HEIGHT50 + layout.height / 2); + py + Tile.HEIGHT50 + font.getLineHeight() / 4); batch.end(); batch.setShader(Diablo.shader); diff --git a/core/src/gdx/diablo/screen/GameScreen.java b/core/src/gdx/diablo/screen/GameScreen.java index 77843b83..b62ff4f2 100644 --- a/core/src/gdx/diablo/screen/GameScreen.java +++ b/core/src/gdx/diablo/screen/GameScreen.java @@ -37,12 +37,13 @@ import java.io.PrintWriter; import gdx.diablo.Diablo; import gdx.diablo.Keys; +import gdx.diablo.entity.Entity; import gdx.diablo.entity.Player; import gdx.diablo.graphics.PaletteIndexedBatch; import gdx.diablo.graphics.PaletteIndexedColorDrawable; import gdx.diablo.key.MappedKey; import gdx.diablo.key.MappedKeyStateAdapter; -import gdx.diablo.map.DT1; +import gdx.diablo.map.DT1.Tile; import gdx.diablo.map.Map; import gdx.diablo.map.MapListener; import gdx.diablo.map.MapLoader; @@ -61,6 +62,7 @@ import gdx.diablo.server.MoveTo; import gdx.diablo.server.Packet; import gdx.diablo.server.Packets; import gdx.diablo.server.PipedSocket; +import gdx.diablo.widget.NpcMenu; import gdx.diablo.widget.TextArea; public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable { @@ -93,6 +95,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable MapListener mapListener; InputProcessor inputProcessorTest; final Array labels = new Array<>(); + Actor menu; public TextArea input; TextArea output; @@ -381,7 +384,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable //player.setPath(map, new Vector3(x, y, 0).add(player.position()), 3); Vector2 position = new Vector2(player.position().x, player.position().y); - Vector2 target = new Vector2(x, y).scl(DT1.Tile.WIDTH).add(mapRenderer.project(position.x, position.y, new Vector2())); + Vector2 target = new Vector2(x, y).scl(Tile.WIDTH).add(mapRenderer.project(position.x, position.y, new Vector2())); GridPoint2 coords = mapRenderer.coords(target.x, target.y, new GridPoint2()); target.set(coords.x, coords.y); Ray ray = new Ray<>(position, target); @@ -421,9 +424,10 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable //System.out.println("hit " + hit + "; " + collision.point + "; " + collision.normal); } } else { + // TODO: this requires a bit more thorough checking - touchable flags need to be checked on each panel and unset on output/input areas stage.screenToStageCoordinates(tmpVec2.set(Gdx.input.getX(), Gdx.input.getY())); Actor hit = stage.hit(tmpVec2.x, tmpVec2.y, false); - if (hit == null) mapListener.update(); + if (hit == null || hit == output || hit == input) mapListener.update(); //if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) { // GridPoint2 coords = mapRenderer.coords(); // player.setPath(map, new Vector3(coords.x, coords.y, 0)); @@ -465,12 +469,12 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable } b.end();*/ - layoutLabels(); - //mapRenderer.prepare(b); - b.begin(); - for (Actor label : labels) label.draw(b, 1); - b.end(); - b.setProjectionMatrix(Diablo.viewport.getCamera().combined); + if (menu == null && !labels.isEmpty()) { + layoutLabels(); + b.begin(); + for (Actor label : labels) label.draw(b, 1); + b.end(); + } } @Override @@ -500,7 +504,6 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable Keys.Enter.addStateListener(mappedKeyStateListener); Diablo.input.addProcessor(stage); Diablo.input.addProcessor(inputProcessorTest); - Diablo.input.addProcessor(mapListener); if (socket != null && socket.isConnected()) { Gdx.app.log(TAG, "connecting to " + socket.getRemoteAddress() + "..."); @@ -545,7 +548,6 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable Keys.Enter.removeStateListener(mappedKeyStateListener); Diablo.input.removeProcessor(stage); Diablo.input.removeProcessor(inputProcessorTest); - Diablo.input.removeProcessor(mapListener); //updateTask.cancel(); } @@ -593,4 +595,34 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable label.setPosition(tmp.x, tmp.y); } } + + public Actor getMenu() { + return menu; + } + + public void setMenu(Actor menu, Entity owner) { + if (this.menu != menu) { + if (this.menu != null) { + if (this.menu instanceof NpcMenu) ((NpcMenu) this.menu).cancel(); + stage.getRoot().removeActor(this.menu); + } + this.menu = menu; + if (menu != null && owner != null) { + stage.addActor(menu); + + Vector3 position = owner.position(); + float x = +(position.x * Tile.SUBTILE_WIDTH50) - (position.y * Tile.SUBTILE_WIDTH50); + float y = -(position.x * Tile.SUBTILE_HEIGHT50) - (position.y * Tile.SUBTILE_HEIGHT50); + menu.setPosition(x, y + owner.getLabelOffset(), Align.center | Align.bottom); + + Vector2 tmp = new Vector2(); + tmp.x = menu.getX(); + tmp.y = menu.getY(); + mapRenderer.projectScaled(tmp); + tmp.x = MathUtils.clamp(tmp.x, 0, Diablo.VIRTUAL_WIDTH - menu.getWidth()); + tmp.y = MathUtils.clamp(tmp.y, 0, Diablo.VIRTUAL_HEIGHT - menu.getHeight()); + menu.setPosition(tmp.x, tmp.y); + } + } + } } diff --git a/core/src/gdx/diablo/widget/LabelButton.java b/core/src/gdx/diablo/widget/LabelButton.java new file mode 100644 index 00000000..fec2eca6 --- /dev/null +++ b/core/src/gdx/diablo/widget/LabelButton.java @@ -0,0 +1,42 @@ +package gdx.diablo.widget; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; + +import gdx.diablo.Diablo; +import gdx.diablo.graphics.PaletteIndexedBatch; + +public class LabelButton extends Label { + ClickListener clickListener; + + public LabelButton(int id, BitmapFont font) { + super(id, font); + init(); + } + + public LabelButton(int id, BitmapFont font, Color color) { + super(id, font, color); + init(); + } + + public LabelButton(String text, BitmapFont font) { + super(text, font); + init(); + } + + public LabelButton(BitmapFont font) { + super(font); + init(); + } + + private void init() { + addListener(clickListener = new ClickListener()); + } + + @Override + public void draw(PaletteIndexedBatch batch, float a) { + setColor(clickListener.isOver() ? Diablo.colors.blue : Diablo.colors.white); + super.draw(batch, a); + } +} diff --git a/core/src/gdx/diablo/widget/NpcMenu.java b/core/src/gdx/diablo/widget/NpcMenu.java new file mode 100644 index 00000000..b7fd1744 --- /dev/null +++ b/core/src/gdx/diablo/widget/NpcMenu.java @@ -0,0 +1,85 @@ +package gdx.diablo.widget; + +import com.badlogic.gdx.scenes.scene2d.InputEvent; +import com.badlogic.gdx.scenes.scene2d.ui.Table; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; + +import gdx.diablo.Diablo; +import gdx.diablo.entity.Entity; +import gdx.diablo.graphics.PaletteIndexedColorDrawable; +import gdx.diablo.screen.GameScreen; + +public class NpcMenu extends Table { + + private static final float PADDING = 8; + private static final float SPACING = 2; + + Entity owner; + NpcMenu parent; + GameScreen gameScreen; + + CancellationListener cancellationListener; + + public NpcMenu(Entity owner, GameScreen gameScreen, String header) { + parent = null; + this.owner = owner; + this.gameScreen = gameScreen; + setBackground(new PaletteIndexedColorDrawable(Diablo.colors.modal50)); + pad(PADDING); + add(new Label(header, Diablo.fonts.font16) {{ + setColor(Diablo.colors.gold); + }}).space(SPACING).row(); + } + + public NpcMenu(int id) { + setBackground(new PaletteIndexedColorDrawable(Diablo.colors.modal50)); + pad(PADDING); + add(new Label(id, Diablo.fonts.font16, Diablo.colors.gold)).space(SPACING).row(); + } + + public NpcMenu addItem(int id, ClickListener clickListener) { + LabelButton button = new LabelButton(id, Diablo.fonts.font16); + button.addListener(clickListener); + add(button).space(SPACING).row(); + return this; + } + + public NpcMenu addItem(int id, final NpcMenu menu) { + menu.parent = this; + menu.owner = owner; + menu.gameScreen = gameScreen; + addItem(id, new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + gameScreen.setMenu(menu, owner); + } + }); + return this; + } + + public NpcMenu addCancel(CancellationListener cancellationListener) { + this.cancellationListener = cancellationListener; + addItem(3400, new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + cancel(); + gameScreen.setMenu(parent, owner); + } + }); + return this; + } + + public NpcMenu build() { + pack(); + return this; + } + + public void cancel() { + if (cancellationListener != null) cancellationListener.onCancelled(); + + } + + public interface CancellationListener { + void onCancelled(); + } +}