From 5731cfc357c7765a57f68e75ea35092384763d03 Mon Sep 17 00:00:00 2001 From: "notzed@gmail.com" Date: Sun, 15 Sep 2013 00:09:58 +0000 Subject: [PATCH] Checkpoint of "work as is". But I did add tile animations. git-svn-id: file:///home/notzed/svn/duskz/trunk@14 b8b59bfb-1aa4-4687-8f88-a62eeb14c21e --- DuskZ/README | 44 ++++++++++- DuskZ/src/duskz/client/ClientMap.java | 2 +- DuskZ/src/duskz/client/Dusk.java | 79 +++++++------------- DuskZ/src/duskz/client/Entity.java | 52 ++++++++++--- DuskZ/src/duskz/client/Equipment.java | 6 +- DuskZ/src/duskz/client/Status.java | 5 +- DuskZ/src/duskz/client/fx/DataManagerFX.java | 28 ++++++- DuskZ/src/duskz/client/fx/EquipmentPane.java | 3 +- DuskZ/src/duskz/client/fx/MainFrameFX.java | 50 +++++++++---- DuskZ/src/duskz/client/fx/TileAnimator.java | 54 +++++++++++++ 10 files changed, 238 insertions(+), 85 deletions(-) create mode 100644 DuskZ/src/duskz/client/fx/TileAnimator.java diff --git a/DuskZ/README b/DuskZ/README index 5ab7274..51bd46a 100644 --- a/DuskZ/README +++ b/DuskZ/README @@ -1,4 +1,16 @@ +CODE STATUS 11/13 +----------------- + +As most of the 'smarts' in Dusk happens in the server the client is +fairly simple - it basically handles some local input and rendering +the tiles. + +This should be feature complete against the latest DuskServer, but +needs some aesthetic and usability work. + +Original README follows. + README ------ This is the client frontend to DuskZ, it uses JavaFX. It connects @@ -9,7 +21,7 @@ circa 2000. Currently a recent Oracle JRE is required to execute this application. -This is currently in an alpha state and in very active development. +This is currently in an alpha state. ... to be completed ... @@ -42,3 +54,33 @@ LICENSE You should have received a copy of the GNU General Public License along with DuskZ. If not, see . + + + DuskZ also uses the ListSpinner widget from JFXExtras. + +/** + * Copyright (c) 2011, JFXtras + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ \ No newline at end of file diff --git a/DuskZ/src/duskz/client/ClientMap.java b/DuskZ/src/duskz/client/ClientMap.java index 7e4b131..8856b24 100644 --- a/DuskZ/src/duskz/client/ClientMap.java +++ b/DuskZ/src/duskz/client/ClientMap.java @@ -149,7 +149,7 @@ public class ClientMap { public synchronized void addEntity(Entity e) { if (entityByID.containsKey(e.ID)) { - throw new RuntimeException("entity already in table " + e.ID + " " + e.strName); + throw new RuntimeException("entity already in table " + e.ID + " " + e.name); } entityByID.put(e.ID, e); diff --git a/DuskZ/src/duskz/client/Dusk.java b/DuskZ/src/duskz/client/Dusk.java index 1a9d237..fbc0264 100644 --- a/DuskZ/src/duskz/client/Dusk.java +++ b/DuskZ/src/duskz/client/Dusk.java @@ -35,8 +35,6 @@ package duskz.client; import duskz.protocol.*; import duskz.protocol.DuskMessage.*; -import static duskz.protocol.DuskProtocol.FIELD_AUTH_NEWPLAYER; -import static duskz.protocol.DuskProtocol.FIELD_AUTH_REASON; import java.io.*; import java.net.*; import java.util.ArrayList; @@ -194,12 +192,12 @@ public class Dusk implements Runnable, DuskProtocol { /** * FIXME: all needs a rewrite * - * @param entStore + * @param e */ // add the thing, this renumbers any that have the same name - void addEntity(Entity entStore) { - System.out.println("Add entity: " + entStore.ID + " " + entStore.strName); - map.addEntity(entStore); + void addEntity(Entity e) { + System.out.println("Add entity: " + e.ID + " " + e.name + " " + e.locx + "x" + e.locy); + map.addEntity(e); // FIXME: call rement if it was out of range so the server knows } @@ -302,7 +300,7 @@ public class Dusk implements Runnable, DuskProtocol { case MSG_CLEAR_FLAGS: synchronized (map) { for (Entity e : map.getEntities()) { - e.intFlag = 0; + e.flags = 0; } } update(); @@ -334,7 +332,7 @@ public class Dusk implements Runnable, DuskProtocol { //frame.info.setText("HP: " + inthp + "/" + intmaxhp + " MP: " + intsp + "/" + intmaxsp + " Loc: " + LocX + "/" + LocY); buyList.clear(); reloadChoiceLookGetAttack(); - + // Perhaps i need a status message to go from 'starting' to 'ready' loaded = true; break; @@ -356,7 +354,10 @@ public class Dusk implements Runnable, DuskProtocol { for (DuskMessage m : el.value) { switch (m.name) { case FIELD_ENTITY_FLAGS: - e.intFlag = ((IntegerMessage) m).value; + e.flags = ((IntegerMessage) m).value; + break; + case FIELD_ENTITY_CONDITIONS: + e.conditions = ((StringListMessage) m).value; break; } } @@ -519,7 +520,7 @@ public class Dusk implements Runnable, DuskProtocol { e.intMoveDirection = dir; } } - reloadChoiceAttack(); + reloadChoiceLookGetAttack(); // TODO: find out what this is for //if (addit) { // thrGraphics.addEntityToMove(ent, dir); @@ -557,7 +558,6 @@ public class Dusk implements Runnable, DuskProtocol { nextQuery(); } - // TBD - use the individual functions public void reloadChoiceLookGetAttack() { final ArrayList looks = new ArrayList<>(); final ArrayList gets = new ArrayList<>(); @@ -581,42 +581,6 @@ public class Dusk implements Runnable, DuskProtocol { frame.setTakeList(gets); } - public void reloadChoiceLook() { - final ArrayList looks = new ArrayList<>(); - - synchronized (map) { - for (Entity e : map.getEntities()) { - if (status.canLook(e)) - looks.add(e); - } - } - frame.setLookList(looks); - } - - public void reloadChoiceAttack() { - final ArrayList attacks = new ArrayList<>(); - - synchronized (map) { - for (Entity e : map.getEntities()) { - if (status.canAttack(e)) - attacks.add(e); - } - } - frame.setAttackList(attacks); - } - - public void reloadChoiceGet() { - final ArrayList gets = new ArrayList<>(); - - synchronized (map) { - for (Entity e : map.getEntities()) { - if (status.canTake(e)) - gets.add(e); - } - } - frame.setTakeList(gets); - } - public boolean isConnected() { return connected; } @@ -739,8 +703,15 @@ public class Dusk implements Runnable, DuskProtocol { docmd(what); } - public void command(String what, String params) { - docmd(what + " " + params); + public void command(String what, String... params) { + StringBuilder sb = new StringBuilder(what); + for (String s : params) { + sb.append(' '); + sb.append('"'); + sb.append(s); + sb.append('"'); + } + docmd(sb.toString()); } public void move(Direction dir) { @@ -757,11 +728,11 @@ public class Dusk implements Runnable, DuskProtocol { } public void buy(TransactionItem item, int quantity) { - command("buy " + quantity + " " + item.name); + command("buy", String.valueOf(quantity), item.name); } public void sell(TransactionItem item, int quantity) { - command("sell " + quantity + " " + item.name); + command("sell", String.valueOf(quantity), item.name); } private void ping() { @@ -781,14 +752,14 @@ public class Dusk implements Runnable, DuskProtocol { } public void drop(String what) { - command("drop " + what); + command("drop", what); } public void wear(String what) { - command("wear " + what); + command("wear", what); } public void unwear(String what) { - command("unwear " + what); + command("unwear", what); } } diff --git a/DuskZ/src/duskz/client/Entity.java b/DuskZ/src/duskz/client/Entity.java index 2e3bdbd..2fa519f 100644 --- a/DuskZ/src/duskz/client/Entity.java +++ b/DuskZ/src/duskz/client/Entity.java @@ -24,11 +24,14 @@ */ package duskz.client; +import duskz.protocol.DuskProtocol; import duskz.protocol.EntityUpdateMessage; +import java.util.ArrayList; +import java.util.List; public class Entity { - public String strName; + public String name; public int locx, locy; public int intType, @@ -43,8 +46,9 @@ public class Entity { 2 west 3 east */ - intFlag = 0; - /*intFlag + flags = 0; + List conditions = new ArrayList<>(0); + /*flags 0 none 1 ally 2 enemy @@ -53,7 +57,7 @@ public class Entity { Entity entNext = null; public Entity(String instrName, long inID, int inintLocX, int inintLocY, int inImage, int inStep, int inintType) { - strName = instrName; + name = instrName; ID = inID; locx = inintLocX; locy = inintLocY; @@ -63,7 +67,7 @@ public class Entity { } public Entity(EntityUpdateMessage msg) { - strName = msg.entityName; + name = msg.entityName; locx = msg.x; locy = msg.y; ID = msg.id; @@ -78,21 +82,49 @@ public class Entity { * @return */ public String getSimpleName() { - int i = strName.lastIndexOf(">"); + int i = name.lastIndexOf(">"); if (i != -1) { - return strName.substring(i + 1); + return name.substring(i + 1); } else { - return strName; + return name; } } public String getIndexedName() { - return intNum == 0 ? strName : intNum + "." + strName; + return intNum == 0 ? name : intNum + "." + name; } @Override public String toString() { - return "[Entity " + ID + ", " + strName + ", " + locx + ", " + locy + "]"; + return "[Entity " + ID + ", " + name + ", " + locx + ", " + locy + "]"; + } + + public String getTitle() { + if (conditions.isEmpty() && flags == 0) + return name; + + StringBuilder sb = new StringBuilder(name); + + sb.append('<'); + boolean first = true; + for (String c : conditions) { + if (!first) { + sb.append(", "); + } + first = false; + sb.append(c); + } + + if ((flags & DuskProtocol.ENTITY_FLAG_SLEEPING) != 0) { + if (!first) { + sb.append(", "); + } + sb.append("sleeping"); + } + + sb.append('>'); + + return sb.toString(); } } diff --git a/DuskZ/src/duskz/client/Equipment.java b/DuskZ/src/duskz/client/Equipment.java index 7472473..d63476c 100644 --- a/DuskZ/src/duskz/client/Equipment.java +++ b/DuskZ/src/duskz/client/Equipment.java @@ -25,7 +25,7 @@ import duskz.protocol.DuskMessage; import duskz.protocol.DuskMessage.StringMessage; import duskz.protocol.ListMessage; import duskz.protocol.TransactionItem; -import duskz.protocol.Wearing; +import static duskz.protocol.Wearing.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -36,7 +36,7 @@ import java.util.List; * * @author notzed */ -public class Equipment implements Wearing { +public class Equipment { /** * Worn items @@ -102,7 +102,7 @@ public class Equipment implements Wearing { StringMessage sm = (StringMessage) dm; // remap to local id; - int i = sm.name - 1; + int i = sm.name; //sm.name - 1; if (i >= 0 && i < available.length) { available[i].add(sm.value); wearableAt.put(sm.value, i); diff --git a/DuskZ/src/duskz/client/Status.java b/DuskZ/src/duskz/client/Status.java index 35d4ce9..c87b162 100644 --- a/DuskZ/src/duskz/client/Status.java +++ b/DuskZ/src/duskz/client/Status.java @@ -92,8 +92,11 @@ public class Status implements DuskProtocol { } public boolean canAttack(Entity e) { + // Ugh, i'm not sure what the types were, but i think it just meant 'any living thing'. + // Dunno why the server doesn't send this message to the client anyway return e.ID != id - && ((e.intType == 0 || e.intType == 1 || e.intType == 4) +// && ((e.intType == 0 || e.intType == 1 || e.intType == 4) + && ((e.intType == 0 || e.intType == 1 || e.intType == 2) && (distance(e) <= range)); } } diff --git a/DuskZ/src/duskz/client/fx/DataManagerFX.java b/DuskZ/src/duskz/client/fx/DataManagerFX.java index 5720ed2..041ee6c 100644 --- a/DuskZ/src/duskz/client/fx/DataManagerFX.java +++ b/DuskZ/src/duskz/client/fx/DataManagerFX.java @@ -45,6 +45,21 @@ public class DataManagerFX extends DataManager { return new ImageSetFX(); } + public void updateTile(ImageView iv, int tileid, int tilewidth, int tileheight) { + for (int i = 0; i < tilesets.length; i++) { + ImageSetFX is = (ImageSetFX) tilesets[i]; + if (tileid >= is.gid && tileid < is.gid + is.count) { + tileid -= is.gid; + iv.setImage(is.image); + //iv.setViewport(new Rectangle2D(tileid * is.width, 0, is.width, is.height)); + iv.setViewport(is.getViewport(tileid)); + iv.setTranslateY(-(is.height - tileheight)); + //iv.relocate(tilex * tilewidth, tiley * tileheight - (is.height - tileheight)); + return; + } + } + } + /** * Create a tile image and align it to the baseline of the map tile. * @@ -65,7 +80,8 @@ public class DataManagerFX extends DataManager { ImageView iv = new ImageView(is.image); tileid -= is.gid; - iv.setViewport(new Rectangle2D(tileid * is.width, 0, is.width, is.height)); + //iv.setViewport(new Rectangle2D(tileid * is.width, 0, is.width, is.height)); + iv.setViewport(is.getViewport(tileid)); iv.relocate(tilex * tilewidth, tiley * tileheight - (is.height - tileheight)); return iv; @@ -82,6 +98,7 @@ public class DataManagerFX extends DataManager { public class ImageSetFX extends ImageSet { Image image; + Rectangle2D tiles[]; public ImageSetFX() { } @@ -91,10 +108,19 @@ public class DataManagerFX extends DataManager { try (InputStream s = getInputStream()) { image = new Image(s); } + + tiles = new Rectangle2D[count]; + for (int i = 0; i < count; i++) { + tiles[i] = new Rectangle2D(i * width, 0, width, height); + } } public Image getImage() { return image; } + + public Rectangle2D getViewport(int tileid) { + return tiles[tileid]; + } } } diff --git a/DuskZ/src/duskz/client/fx/EquipmentPane.java b/DuskZ/src/duskz/client/fx/EquipmentPane.java index f515d48..b57b98b 100644 --- a/DuskZ/src/duskz/client/fx/EquipmentPane.java +++ b/DuskZ/src/duskz/client/fx/EquipmentPane.java @@ -22,6 +22,7 @@ package duskz.client.fx; import duskz.client.Equipment; +import duskz.protocol.Wearing; import java.util.ArrayList; import java.util.List; import javafx.beans.value.ChangeListener; @@ -127,7 +128,7 @@ public class EquipmentPane extends HBox { @Override public String toString() { - return wornAt == -1 ? name : name + " [worn: " + Equipment.titles[wornAt] + "]"; + return wornAt == -1 ? name : name + " [worn: " + Wearing.wornTitles[wornAt] + "]"; } } diff --git a/DuskZ/src/duskz/client/fx/MainFrameFX.java b/DuskZ/src/duskz/client/fx/MainFrameFX.java index cf7a40f..c82dc54 100644 --- a/DuskZ/src/duskz/client/fx/MainFrameFX.java +++ b/DuskZ/src/duskz/client/fx/MainFrameFX.java @@ -35,6 +35,7 @@ import duskz.client.Equipment; import duskz.client.GUI; import duskz.client.Status; import duskz.protocol.TransactionItem; +import duskz.protocol.Wearing; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; @@ -43,6 +44,7 @@ import java.util.List; import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; +import javafx.animation.Animation; import javafx.animation.FadeTransition; import javafx.animation.FadeTransitionBuilder; import javafx.animation.Interpolator; @@ -621,7 +623,7 @@ public class MainFrameFX extends StackPane implements GUI { equip.unwear.setOnAction(new EventHandler() { public void handle(ActionEvent t) { EquipmentPane.ItemInfo ii = equip.getItem(); - game.unwear(Equipment.names[ii.wornAt]); + game.unwear(Wearing.wornNames[ii.wornAt]); } }); equip.drop.setOnAction(new EventHandler() { @@ -654,7 +656,7 @@ public class MainFrameFX extends StackPane implements GUI { public void visitFile(String file, String text, boolean canSave) { throw new UnsupportedOperationException("Not supported yet."); // FIXME: implement edit text, - // FIXME: sent using: appParent.outstream.writeBytes("submit "+strName+"\n"); + // FIXME: sent using: appParent.outstream.writeBytes("submit "+name+"\n"); // appParent.outstream.writeBytes(txtEdit.getText()+"\n--EOF--\n"); } //Accept key input @@ -886,6 +888,7 @@ public class MainFrameFX extends StackPane implements GUI { Logger.getLogger(MainFrameFX.class.getName()).log(Level.SEVERE, null, ex); } } + TileAnimator animator; @Override public void updateMap(ClientMap map) { @@ -893,6 +896,8 @@ public class MainFrameFX extends StackPane implements GUI { // Since we get a whole update for the map every time, there isn't much we // can practically do apart from simply build a whole new page to display. + // However ... this is jused for every type of changed thing, and that + // isn't neccessary. System.out.println("update map"); if (data == null) { //if (tileImage == null) { @@ -904,17 +909,27 @@ public class MainFrameFX extends StackPane implements GUI { final ArrayList upper = new ArrayList<>(); int levelCount = map.getLevelCount(); - // Build map + // Animated tiles hack test + final Rectangle2D[] anims = new Rectangle2D[2]; + anims[0] = data.createTile(305, 0, 0, tileSize, tileSize).getViewport(); + anims[1] = data.createTile(304, 0, 0, tileSize, tileSize).getViewport(); + final List animated = new ArrayList<>(); + + // Build map for (int l = 0; l < levelCount; l++) { for (int y = 0; y < map.rows; y++) { // Draw tiles first for whole row for (int x = 0; x < map.cols; x++) { int tileid = map.getTile(l, x, y); if (tileid != 0) { - children.add(data.createTile(tileid, x, y, tileSize, tileSize)); + ImageView iv = data.createTile(tileid, x, y, tileSize, tileSize); + children.add(iv); + + if (tileid == 305) + animated.add(iv); } } - + // Now check for entities over this layer row if (l == map.getGroundLevel()) { for (int x = 0; x < map.cols; x++) { @@ -939,6 +954,14 @@ public class MainFrameFX extends StackPane implements GUI { public void run() { graphics.getChildren().setAll(children); graphics.getChildren().addAll(upper); + + System.out.println("animated node count = " + animated.size()); + if (animator == null) { + animator = new TileAnimator(animated, Duration.seconds(0.25), anims); + animator.setCycleCount(Animation.INDEFINITE); + animator.play(); + } else + animator.setNodes(animated); } }); } @@ -965,28 +988,29 @@ public class MainFrameFX extends StackPane implements GUI { ImageView iv = new ImageView(playerImage); iv.setViewport(new Rectangle2D((e.intImage * 8 + e.intStep) * spriteSize, 0, spriteSize, spriteSize)); - iv.relocate((x * tileSize) - tileSize / 2, (y * tileSize) - tileSize / 2); - iv.setScaleX(0.5); - iv.setScaleY(0.5); + iv.relocate((x * tileSize) + tileSize / 2 - spriteSize / 2, (y * tileSize) - spriteSize / 2); + iv.setScaleX(1); + iv.setScaleY(1); children.add(iv); } + // FIXME: intnum not used anymore if (e.intNum == 0) { - Text t = new Text(e.strName); + Text t = new Text(e.getTitle()); t.setId("entity-label"); t.relocate((x * tileSize) + tileSize / 2 - t.getLayoutBounds().getWidth() / 2, ((y + 1) * tileSize)); upper.add(t); } else { - Text t = new Text(e.intNum + "." + e.strName); + Text t = new Text(e.intNum + "." + e.name); t.setId("entity-label"); t.relocate((x * tileSize) + tileSize / 2 - t.getLayoutBounds().getWidth() / 2, ((y + 1) * tileSize)); upper.add(t); } //Draw flag - if (e.intFlag != 0) { + if (e.flags != 0) { Rectangle r = new Rectangle(1, 1, tileSize - 2, tileSize - 2); - if (e.intFlag == 1) { + if ((e.flags & 3) == 1) { r.setStroke(Color.GREEN); - } else if (e.intFlag == 2) { + } else if ((e.flags & 3) == 2) { r.setStroke(Color.RED); } r.setStrokeWidth(2); diff --git a/DuskZ/src/duskz/client/fx/TileAnimator.java b/DuskZ/src/duskz/client/fx/TileAnimator.java new file mode 100644 index 0000000..6a73323 --- /dev/null +++ b/DuskZ/src/duskz/client/fx/TileAnimator.java @@ -0,0 +1,54 @@ +/* + * This file is part of DuskZ, a graphical mud engine. + * + * Copyright (C) 2013 Michael Zucchi + * + * DuskZ is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DuskZ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with DuskZ. If not, see . + */ +package duskz.client.fx; + +import java.util.List; +import javafx.animation.Transition; +import javafx.geometry.Rectangle2D; +import javafx.scene.image.ImageView; +import javafx.util.Duration; + +/** + * Animates a set of tiles by switching the viewport on the texture. + * + * @author Michael Zucchi + */ +public class TileAnimator extends Transition { + + Rectangle2D[] viewports; + List nodes; + + public TileAnimator(List nodes, Duration duration, Rectangle2D[] images) { + setCycleDuration(duration); + this.viewports = images; + this.nodes = nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + @Override + protected void interpolate(double d) { + int index = Math.min(viewports.length - 1, (int) (d * viewports.length)); + + for (ImageView node : nodes) + node.setViewport(viewports[index]); + } +} -- 2.39.5