From c3fcfc4f050dd74f38139d3cc7f3c1cdd734733a Mon Sep 17 00:00:00 2001 From: Collin Smith Date: Tue, 19 Mar 2019 03:03:25 -0700 Subject: [PATCH] Added SpellsQuickPanel Added SpellsQuickPanel Added HotkeyButton assigned to MappedKey Added Skill1-16 Keys Button now has a customizable disabled and highlight state Hacked together a couple skills for testing purposes Added animation overlay support (will expand in future) Added Entity.cast(int) ControlPanel left and right skill corresponds and changes based on active skill Mapped key now allows primary key as NOT_MAPPED Added Map from string to mode index Added Animation clamp function to play animation until over or stop at last frame Added additional fields to excel Skills Added excel Overlay --- core/src/com/riiablo/Colors.java | 2 + core/src/com/riiablo/Files.java | 3 + core/src/com/riiablo/Keys.java | 26 +++- core/src/com/riiablo/codec/Animation.java | 66 +++++---- core/src/com/riiablo/codec/excel/Overlay.java | 40 ++++++ core/src/com/riiablo/codec/excel/Skills.java | 2 + core/src/com/riiablo/entity/Entity.java | 103 ++++++++++++-- core/src/com/riiablo/entity/Object.java | 6 +- core/src/com/riiablo/entity/Player.java | 22 +++ core/src/com/riiablo/key/MappedKey.java | 9 +- core/src/com/riiablo/map/Map.java | 4 + core/src/com/riiablo/panel/ControlPanel.java | 35 +++-- .../com/riiablo/panel/SpellsQuickPanel.java | 127 ++++++++++++++++++ core/src/com/riiablo/screen/GameScreen.java | 35 ++++- core/src/com/riiablo/widget/Button.java | 32 ++++- core/src/com/riiablo/widget/HotkeyButton.java | 44 ++++++ 16 files changed, 495 insertions(+), 61 deletions(-) create mode 100644 core/src/com/riiablo/codec/excel/Overlay.java create mode 100644 core/src/com/riiablo/panel/SpellsQuickPanel.java create mode 100644 core/src/com/riiablo/widget/HotkeyButton.java diff --git a/core/src/com/riiablo/Colors.java b/core/src/com/riiablo/Colors.java index 218b556a..61341338 100644 --- a/core/src/com/riiablo/Colors.java +++ b/core/src/com/riiablo/Colors.java @@ -33,6 +33,8 @@ public class Colors { public Color c12 = C12.cpy(); public Color highlight = new Color(0.15f, 0.15f, 0.12f, 0); + public Color darken = new Color(0.40f, 0.40f, 0.40f, 0); + public Color darkenR = new Color(1.00f, 0.20f, 0.20f, 0); public Color invBlue = new Color(0.1f, 0.1f, 0.5f, 0.3f); public Color invGreen = new Color(0.1f, 0.5f, 0.1f, 0.3f); diff --git a/core/src/com/riiablo/Files.java b/core/src/com/riiablo/Files.java index d28ca1c4..bc81ad21 100644 --- a/core/src/com/riiablo/Files.java +++ b/core/src/com/riiablo/Files.java @@ -29,6 +29,7 @@ import com.riiablo.codec.excel.MonStats2; import com.riiablo.codec.excel.Obj; import com.riiablo.codec.excel.ObjMode; import com.riiablo.codec.excel.Objects; +import com.riiablo.codec.excel.Overlay; import com.riiablo.codec.excel.PlrMode; import com.riiablo.codec.excel.PlrType; import com.riiablo.codec.excel.QualityItems; @@ -70,6 +71,7 @@ public class Files { public final MonStats2 monstats2; public final Objects objects; public final ObjMode ObjMode; + public final Overlay Overlay; public final PlrMode PlrMode; public final PlrType PlrType; public final QualityItems QualityItems; @@ -115,6 +117,7 @@ public class Files { Runes = load(assets, Runes.class); objects = load(assets, Objects.class); ObjMode = load(assets, ObjMode.class); + Overlay = load(assets, Overlay.class, Excel.EXPANSION); PlrMode = load(assets, PlrMode.class); PlrType = load(assets, PlrType.class); QualityItems = load(assets, QualityItems.class); diff --git a/core/src/com/riiablo/Keys.java b/core/src/com/riiablo/Keys.java index b0cd23f1..83a66af6 100644 --- a/core/src/com/riiablo/Keys.java +++ b/core/src/com/riiablo/Keys.java @@ -3,13 +3,12 @@ package com.riiablo; import com.badlogic.gdx.Input; import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.Field; +import com.riiablo.key.KeyMapper; +import com.riiablo.key.MappedKey; import java.util.ArrayList; import java.util.Collection; -import com.riiablo.key.KeyMapper; -import com.riiablo.key.MappedKey; - public class Keys { public static Collection addTo(KeyMapper keyMapper) { return addTo(keyMapper, Keys.class, new ArrayList(0)); @@ -39,8 +38,29 @@ public class Keys { public static final MappedKey Esc = new MappedKey("Esc", "esc", Input.Keys.ESCAPE, Input.Keys.BACK); public static final MappedKey Inventory = new MappedKey("Inventory", "inventory", Input.Keys.I, Input.Keys.B); public static final MappedKey Character = new MappedKey("Character", "character", Input.Keys.C, Input.Keys.A); + public static final MappedKey Spells = new MappedKey("Spells", "spells", Input.Keys.S, Input.Keys.T); public static final MappedKey Stash = new MappedKey("Stash", "stash", Input.Keys.NUMPAD_1); public static final MappedKey SwapWeapons = new MappedKey("SwapWeapons", "swap", Input.Keys.W); public static final MappedKey Enter = new MappedKey("Enter", "enter", Input.Keys.ENTER); + public static final MappedKey Skill1 = new MappedKey("Skill 1", "skill1", Input.Keys.F1); + public static final MappedKey Skill2 = new MappedKey("Skill 2", "skill2", Input.Keys.F2); + public static final MappedKey Skill3 = new MappedKey("Skill 3", "skill3", Input.Keys.F3); + public static final MappedKey Skill4 = new MappedKey("Skill 4", "skill4", Input.Keys.F4); + public static final MappedKey Skill5 = new MappedKey("Skill 5", "skill5", Input.Keys.F5); + public static final MappedKey Skill6 = new MappedKey("Skill 6", "skill6", Input.Keys.F6); + public static final MappedKey Skill7 = new MappedKey("Skill 7", "skill7", Input.Keys.F7); + public static final MappedKey Skill8 = new MappedKey("Skill 8", "skill8", Input.Keys.F8); + public static final MappedKey Skill9 = new MappedKey("Skill 9", "skill9", MappedKey.NOT_MAPPED); + public static final MappedKey Skill10 = new MappedKey("Skill 10", "skill10", MappedKey.NOT_MAPPED); + public static final MappedKey Skill11 = new MappedKey("Skill 11", "skill11", MappedKey.NOT_MAPPED); + public static final MappedKey Skill12 = new MappedKey("Skill 12", "skill12", MappedKey.NOT_MAPPED); + public static final MappedKey Skill13 = new MappedKey("Skill 13", "skill13", MappedKey.NOT_MAPPED); + public static final MappedKey Skill14 = new MappedKey("Skill 14", "skill14", MappedKey.NOT_MAPPED); + public static final MappedKey Skill15 = new MappedKey("Skill 15", "skill15", MappedKey.NOT_MAPPED); + public static final MappedKey Skill16 = new MappedKey("Skill 16", "skill16", MappedKey.NOT_MAPPED); + public static final MappedKey Skill[] = new MappedKey[] { + Skill1, Skill2, Skill3, Skill4, Skill5, Skill6, Skill7, Skill8, + Skill9, Skill10, Skill11, Skill12, Skill13, Skill14, Skill15, Skill16 + }; } diff --git a/core/src/com/riiablo/codec/Animation.java b/core/src/com/riiablo/codec/Animation.java index 804e16fe..75bc36a4 100644 --- a/core/src/com/riiablo/codec/Animation.java +++ b/core/src/com/riiablo/codec/Animation.java @@ -23,8 +23,8 @@ public class Animation extends BaseDrawable { private static final int DEBUG_MODE = 1; // 0=off, 1=box, 2=layer box private static final int NUM_LAYERS = COF.Component.NUM_COMPONENTS; - private static final float FRAMES_PER_SECOND = 25f; - private static final float FRAME_DURATION = 1 / FRAMES_PER_SECOND; + public static final float FRAMES_PER_SECOND = 25f; + public static final float FRAME_DURATION = 1 / FRAMES_PER_SECOND; private static final Color SHADOW_TINT = Riiablo.colors.modal75; private static final Affine2 SHADOW_TRANSFORM = new Affine2(); @@ -36,6 +36,7 @@ public class Animation extends BaseDrawable { private int direction; private int frame; private boolean looping; + private boolean clamp; private float frameDuration; private float elapsedTime; private Layer layers[]; @@ -59,23 +60,28 @@ public class Animation extends BaseDrawable { numFrames = framesPerDir; this.layers = layers; looping = true; + clamp = true; frameDuration = FRAME_DURATION; box = new BBox(); animationListeners = EMPTY_MAP; } - public static Animation newAnimation(com.riiablo.codec.DC dc) { + public static Animation newAnimation(DC dc) { return Animation.builder().layer(dc).build(); } - public static Animation newAnimation(com.riiablo.codec.COF cof) { + public static Animation newAnimation(COF cof) { Animation animation = new Animation(); animation.reset(cof); return animation; } - public boolean reset(com.riiablo.codec.COF cof) { + public COF getCOF() { + return cof; + } + + public boolean reset(COF cof) { if (this.cof != cof) { this.cof = cof; numDirections = cof.getNumDirections(); @@ -83,10 +89,10 @@ public class Animation extends BaseDrawable { setFrameDelta(cof.getAnimRate()); if (direction >= numDirections) direction = 0; - if (frame >= numFrames) { + //if (frame >= numFrames) { frame = 0; elapsedTime = 0; - } + //} return true; } @@ -108,17 +114,17 @@ public class Animation extends BaseDrawable { for (Layer l : layers) if (l != null) l.load(d); } - public Animation setLayer(int component, com.riiablo.codec.DC dc) { + public Animation setLayer(int component, DC dc) { return setLayer(component, dc, true); } - public Animation setLayer(int component, com.riiablo.codec.DC dc, boolean updateBox) { + public Animation setLayer(int component, DC dc, boolean updateBox) { layers[component] = dc != null ? new Layer(dc).load(direction) : null; if (updateBox) updateBox(); return this; } - public Layer setLayer(com.riiablo.codec.COF.Layer cofLayer, com.riiablo.codec.DC dc, boolean updateBox) { + public Layer setLayer(COF.Layer cofLayer, DC dc, boolean updateBox) { setLayer(cofLayer.component, dc, updateBox); Layer layer = layers[cofLayer.component]; if (layer != null && cofLayer.overrideTransLvl != 0) { @@ -186,7 +192,7 @@ public class Animation extends BaseDrawable { Preconditions.checkArgument(0 <= f && f < numFrames, "Invalid frame: " + f); frame = f; elapsedTime = frameDuration * frame; - if (frame == numFrames - 1) notifyAnimationFinished(); + //if (frame == numFrames - 1) notifyAnimationFinished(); } } @@ -202,6 +208,14 @@ public class Animation extends BaseDrawable { looping = b; } + public boolean isClamped() { + return clamp; + } + + public void setClamp(boolean b) { + clamp = b; + } + public boolean isHighlighted() { return highlighted; } @@ -271,7 +285,7 @@ public class Animation extends BaseDrawable { int frameNumber = (int) (stateTime / frameDuration); return looping ? frameNumber % numFrames - : Math.min(numFrames - 1, frameNumber); + : Math.min(clamp ? numFrames - 1 : numFrames, frameNumber); } public void act() { @@ -305,8 +319,8 @@ public class Animation extends BaseDrawable { shapes.setColor(Color.GREEN); shapes.rect(x + box.xMin, y - box.yMax, box.width, box.height); if (reset) shapes.end(); - } else if (DEBUG_MODE == 2) { - int d = com.riiablo.codec.DC.Direction.toReadDir(direction, cof.getNumDirections()); + } else if (DEBUG_MODE == 2 && frame < numFrames) { + int d = DC.Direction.toReadDir(direction, cof.getNumDirections()); int f = frame; for (int l = 0; l < cof.getNumLayers(); l++) { int component = cof.getLayerOrder(d, f, l); @@ -326,15 +340,15 @@ public class Animation extends BaseDrawable { } public void draw(PaletteIndexedBatch batch, float x, float y) { - if (cof == null) { + if (cof == null && frame < numFrames) { for (Layer layer : layers) { if (layer == null) continue; drawLayer(batch, layer, x, y); } batch.resetBlendMode(); batch.resetColormap(); - } else { - int d = com.riiablo.codec.DC.Direction.toReadDir(direction, cof.getNumDirections()); + } else if (frame < numFrames) { + int d = DC.Direction.toReadDir(direction, cof.getNumDirections()); int f = frame; // TODO: Layer blend modes should correspond with the cof trans levels for (int l = 0; l < cof.getNumLayers(); l++) { @@ -360,8 +374,8 @@ public class Animation extends BaseDrawable { if (layer == null) continue; drawShadow(batch, layer, x, y); } - } else { - int d = com.riiablo.codec.DC.Direction.toReadDir(direction, cof.getNumDirections()); + } else if (frame < numFrames) { + int d = DC.Direction.toReadDir(direction, cof.getNumDirections()); int f = frame; for (int l = 0; l < cof.getNumLayers(); l++) { int component = cof.getLayerOrder(d, f, l); @@ -378,6 +392,10 @@ public class Animation extends BaseDrawable { } public void drawShadow(PaletteIndexedBatch batch, Layer layer, float x, float y) { + if (frame >= numFrames) { + return; + } + int d = direction; int f = frame; @@ -407,8 +425,8 @@ public class Animation extends BaseDrawable { if (layer == null) break; box.max(layer.dc.getBox(direction)); } - } else { - int d = com.riiablo.codec.DC.Direction.toReadDir(direction, cof.getNumDirections()); + } else if (frame < numFrames) { + int d = DC.Direction.toReadDir(direction, cof.getNumDirections()); int f = frame; box.reset(); for (int l = 0; l < cof.getNumLayers(); l++) { @@ -493,11 +511,11 @@ public class Animation extends BaseDrawable { Index transform; int transformColor; - Layer(com.riiablo.codec.DC dc) { + Layer(DC dc) { this(dc, BlendMode.ID); } - Layer(com.riiablo.codec.DC dc, int blendMode) { + Layer(DC dc, int blendMode) { this.dc = dc; this.blendMode = blendMode; tint = Color.WHITE; @@ -590,7 +608,7 @@ public class Animation extends BaseDrawable { int size = 0; Layer layers[] = new Layer[NUM_LAYERS]; - public Builder layer(com.riiablo.codec.DC dc) { + public Builder layer(DC dc) { return layer(new Layer(dc)); } diff --git a/core/src/com/riiablo/codec/excel/Overlay.java b/core/src/com/riiablo/codec/excel/Overlay.java new file mode 100644 index 00000000..24cd373b --- /dev/null +++ b/core/src/com/riiablo/codec/excel/Overlay.java @@ -0,0 +1,40 @@ +package com.riiablo.codec.excel; + +public class Overlay extends Excel { + public static class Entry extends Excel.Entry { + @Override + public String toString() { + return overlay; + } + + @Key + @Column + public String overlay; + @Column public String Filename; + @Column public int version; + @Column public int Frames; + @Column public String Character; + @Column public boolean PreDraw; + @Column(format = "1ofN") + public int _1ofN; + @Column public int Dir; + @Column public boolean Open; + @Column public boolean Beta; + @Column public int Xoffset; + @Column public int Yoffset; + @Column public int Height1; + @Column public int Height2; + @Column public int Height3; + @Column public int Height4; + @Column public int AnimRate; + @Column public int LoopWaitTime; + @Column public int Trans; + @Column public int InitRadius; + @Column public int Radius; + @Column public int Red; + @Column public int Green; + @Column public int Blue; + @Column public int NumDirections; + @Column public boolean LocalBlood; + } +} diff --git a/core/src/com/riiablo/codec/excel/Skills.java b/core/src/com/riiablo/codec/excel/Skills.java index ab860f1e..2fdd8e7b 100644 --- a/core/src/com/riiablo/codec/excel/Skills.java +++ b/core/src/com/riiablo/codec/excel/Skills.java @@ -28,5 +28,7 @@ public class Skills extends Excel { @Column public int lvlmana; @Column(startIndex = 1, endIndex = 9) public int Param[]; + @Column public boolean leftskill; + @Column public boolean passive; } } diff --git a/core/src/com/riiablo/entity/Entity.java b/core/src/com/riiablo/entity/Entity.java index 8c9f5506..3c4333fd 100644 --- a/core/src/com/riiablo/entity/Entity.java +++ b/core/src/com/riiablo/entity/Entity.java @@ -16,6 +16,8 @@ import com.riiablo.codec.Animation; import com.riiablo.codec.COF; import com.riiablo.codec.COFD2; import com.riiablo.codec.DCC; +import com.riiablo.codec.excel.Overlay; +import com.riiablo.codec.excel.Skills; import com.riiablo.codec.util.BBox; import com.riiablo.graphics.PaletteIndexedBatch; import com.riiablo.map.DS1; @@ -32,15 +34,16 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; +import gdx.diablo.BlendMode; import gdx.diablo.Diablo; -public abstract class Entity { +public abstract class Entity implements Animation.AnimationListener { private static final String TAG = "Entity"; private static final boolean DEBUG = true; - private static final boolean DEBUG_STATE = DEBUG && true; - private static final boolean DEBUG_DIRTY = DEBUG && true; - private static final boolean DEBUG_COF = DEBUG && true; + private static final boolean DEBUG_STATE = DEBUG && !true; + private static final boolean DEBUG_DIRTY = DEBUG && !true; + private static final boolean DEBUG_COF = DEBUG && !true; private static final boolean DEBUG_TARGET = DEBUG && true; private static final boolean DEBUG_PATH = DEBUG && !true; @@ -99,11 +102,14 @@ public abstract class Entity { public final String MODE[]; public final String COMP[]; + private ObjectIntMap MODES; private ObjectIntMap COMPS; Type(String path, String[] modes, String[] comps) { PATH = "data\\global\\" + path; MODE = modes; + MODES = new ObjectIntMap<>(); + for (int i = 0; i < modes.length; i++) MODES.put(modes[i].toLowerCase(), i); COMP = comps; COMPS = new ObjectIntMap<>(); for (int i = 0; i < comps.length; i++) COMPS.put(comps[i].toLowerCase(), i); @@ -113,6 +119,10 @@ public abstract class Entity { return Riiablo.cofs.active; } + public byte getMode(String mode) { + return (byte) MODES.get(mode.toLowerCase(), -1); + } + public int getComponent(String comp) { return COMPS.get(comp.toLowerCase(), -1); } @@ -232,6 +242,9 @@ public abstract class Entity { float walkSpeed = 6; float runSpeed = 9; + Overlay.Entry overlayEntry; + Animation overlay; + private static final Vector2 tmpVec2 = new Vector2(); public static Entity create(Map map, Map.Zone zone, DS1 ds1, DS1.Object object) { @@ -390,6 +403,7 @@ public abstract class Entity { private boolean updateAnimation(COF cof) { if (animation == null) { animation = Animation.newAnimation(cof); + animation.addAnimationListener(-1, this); updateDirection(); return true; } else { @@ -397,6 +411,16 @@ public abstract class Entity { } } + @Override + public void onTrigger(Animation animation, int frame) { + switch (frame) { + case -1: onAnimationFinished(animation); break; + default: // do nothing + } + } + + protected void onAnimationFinished(Animation animation) {} + public String getCOF() { return cof; } @@ -406,6 +430,7 @@ public abstract class Entity { } public void act(float delta) { + if (overlay != null) overlay.act(delta); if (animation != null) animation.act(delta); } @@ -413,12 +438,14 @@ public abstract class Entity { validate(); float x = +(position.x * Tile.SUBTILE_WIDTH50) - (position.y * Tile.SUBTILE_WIDTH50); float y = -(position.x * Tile.SUBTILE_HEIGHT50) - (position.y * Tile.SUBTILE_HEIGHT50); + if (overlayEntry != null && overlayEntry.PreDraw) overlay.draw(batch, x, y); animation.draw(batch, x, y); + if (overlayEntry != null && !overlayEntry.PreDraw) overlay.draw(batch, x, y); label.setPosition(x, y + getLabelOffset() + label.getHeight() / 2, Align.center); - if (nextMode >= 0 && animation.isFinished()) { - setMode(nextMode); - nextMode = -1; - } + //if (animation.isFinished() && nextMode >= 0) { + // setMode(nextMode); + // nextMode = -1; + //} } public void drawShadow(PaletteIndexedBatch batch) { @@ -523,6 +550,13 @@ public abstract class Entity { angle(MathUtils.atan2(tmpVec2.y, tmpVec2.x)); } + public void lookAt(float x, float y) { + float x2 = +(position.x * Tile.SUBTILE_WIDTH50) - (position.y * Tile.SUBTILE_WIDTH50); + float y2 = -(position.x * Tile.SUBTILE_HEIGHT50) - (position.y * Tile.SUBTILE_HEIGHT50); + tmpVec2.set(x, y).sub(x2, y2); + angle(MathUtils.atan2(tmpVec2.y, tmpVec2.x)); + } + public int direction() { int numDirs = animation.getNumDirections(); return Direction.radiansToDirection(angle, numDirs); @@ -565,9 +599,11 @@ public abstract class Entity { } } - public void animate(byte transition, byte mode) { + public boolean sequence(byte transition, byte mode) { + boolean changed = this.mode != transition; setMode(transition); nextMode = mode; + return changed; } public Vector2 target() { @@ -648,11 +684,16 @@ public abstract class Entity { } public void update(float delta) { + if (animation != null && animation.isFinished() && nextMode >= 0) { + setMode(nextMode); + nextMode = -1; + } + if (target.isZero()) return; if (position.epsilonEquals(target)) { if (!targets.hasNext()) { path.clear(); - if (mode == (running ? getRunMode() : getWalkMode())) setMode(getNeutralMode()); + if (isMoving(mode)) setMode(getNeutralMode()); return; } } @@ -677,4 +718,46 @@ public abstract class Entity { } } } + + public boolean isCasting(byte mode) { + return false; + } + + public boolean isMoving(byte mode) { + return false; + } + + public boolean isCastable(byte mode) { + return false; + } + + public boolean cast(final int spell) { + if (!isCastable(mode)) return false; + setPath(null, null); + //if (mode == getNeutralMode()) return; + //animating = true; + final Skills.Entry skill = Riiablo.files.skills.get(spell); + byte tm = mode; + boolean changed = sequence(type.getMode(skill.anim), getNeutralMode()); + if (!changed) return false; + + System.out.println("cast " + type.MODE[tm] + "->" + type.MODE[mode]); + Riiablo.audio.play(skill.stsound, true); + + if (!skill.castoverlay.isEmpty()) { + overlayEntry = Riiablo.files.Overlay.get(skill.castoverlay); + AssetDescriptor descriptor = new AssetDescriptor<>("data\\global\\overlays\\" + overlayEntry.Filename + ".dcc", DCC.class); + Riiablo.assets.load(descriptor); + Riiablo.assets.finishLoadingAsset(descriptor); + DCC dcc = Riiablo.assets.get(descriptor); + overlay = Animation.builder() + .layer(dcc, overlayEntry.Trans == 3 ? BlendMode.LUMINOSITY : BlendMode.ID) + .build(); + overlay.setLooping(false); + overlay.setClamp(false); + //overlay.setFrameDuration(1f / overlayEntry.AnimRate); + } + + return true; + } } diff --git a/core/src/com/riiablo/entity/Object.java b/core/src/com/riiablo/entity/Object.java index 0848cc02..d250268a 100644 --- a/core/src/com/riiablo/entity/Object.java +++ b/core/src/com/riiablo/entity/Object.java @@ -90,9 +90,9 @@ public class Object extends Entity { } @Override - public void animate(byte transition, byte mode) { + public boolean sequence(byte transition, byte mode) { assert !base.CycleAnim[transition]; - super.animate(transition, mode); + return super.sequence(transition, mode); } @Override @@ -158,7 +158,7 @@ public class Object extends Entity { break; case 23: // waypoint if (mode == MODE_NU) { - animate(MODE_OP, MODE_ON); + sequence(MODE_OP, MODE_ON); Riiablo.audio.play("object_waypoint_open", true); } break; diff --git a/core/src/com/riiablo/entity/Player.java b/core/src/com/riiablo/entity/Player.java index b4668e42..1e4af629 100644 --- a/core/src/com/riiablo/entity/Player.java +++ b/core/src/com/riiablo/entity/Player.java @@ -52,6 +52,10 @@ public class Player extends Entity { //public static final byte MODE_GH = 18; //public static final byte MODE_GH = 19; + private static final int MOVING_MODES = (1 << MODE_WL) | (1 << MODE_RN) | (1 << MODE_TW); + private static final int CASTING_MODES = (1 << MODE_SC); + private static final int CASTABLE_MODES = (1 << MODE_NU) | (1 << MODE_TN) | MOVING_MODES; + private static final String[] TOKENS = {"AM", "SO", "NE", "PA", "BA", "DZ", "AI"}; public static String getToken(int type) { @@ -87,6 +91,7 @@ public class Player extends Entity { boolean ignoreUpdate; public Stats stats; public Skills skills; + public int[] skillBar; public Map map; public Map.Zone curZone; public final CharacterClass charClass; @@ -100,12 +105,14 @@ public class Player extends Entity { this(name, characterClass.id); stats = new StatsImpl(name, characterClass.id); skills = new SkillsImpl(); + skillBar = new int[16]; } public Player(D2S d2s) { this(d2s.name, d2s.charClass); stats = new D2SStats(d2s); skills = new D2SSkills(d2s); + skillBar = d2s.skillBar; loadEquipped(d2s.items.equipped); loadInventory(d2s.items.inventory); } @@ -359,6 +366,21 @@ public class Player extends Entity { } } + @Override + public boolean isCasting(byte mode) { + return (CASTING_MODES & (1 << mode)) != 0; + } + + @Override + public boolean isCastable(byte mode) { + return (CASTABLE_MODES & (1 << mode)) != 0; + } + + @Override + public boolean isMoving(byte mode) { + return (MOVING_MODES & (1 << mode)) != 0; + } + private void notifySlotChanged(BodyLoc bodyLoc, Item oldItem, Item item) { for (SlotListener l : SLOT_LISTENERS) l.onChanged(this, bodyLoc, oldItem, item); } diff --git a/core/src/com/riiablo/key/MappedKey.java b/core/src/com/riiablo/key/MappedKey.java index 49d5be21..c6e61696 100644 --- a/core/src/com/riiablo/key/MappedKey.java +++ b/core/src/com/riiablo/key/MappedKey.java @@ -53,6 +53,10 @@ public class MappedKey implements Iterable { private final Set ASSIGNMENT_LISTENERS = new CopyOnWriteArraySet<>(); private final Set STATE_LISTENERS = new CopyOnWriteArraySet<>(); + public MappedKey(String name, String alias) { + this(name, alias, NOT_MAPPED, NOT_MAPPED); + } + public MappedKey(String name, String alias, @Keycode int primary) { this(name, alias, primary, NOT_MAPPED); } @@ -60,8 +64,9 @@ public class MappedKey implements Iterable { public MappedKey(String name, String alias, @Keycode int primary, @Keycode int secondary) { Preconditions.checkArgument(!name.isEmpty(), "name cannot be empty"); Preconditions.checkArgument(!alias.isEmpty(), "alias cannot be empty"); - Preconditions.checkArgument(primary != NOT_MAPPED, "primary key mapping must be mapped"); - Preconditions.checkArgument(primary != secondary, "key mappings must be unique"); + //Preconditions.checkArgument(primary != NOT_MAPPED, "primary key mapping must be mapped"); + //Preconditions.checkArgument(primary != secondary, "key mappings must be unique"); + Preconditions.checkArgument(primary == NOT_MAPPED || primary != secondary, "key mappings must be unique"); NAME = name; ALIAS = alias; diff --git a/core/src/com/riiablo/map/Map.java b/core/src/com/riiablo/map/Map.java index a8146b91..7d10b936 100644 --- a/core/src/com/riiablo/map/Map.java +++ b/core/src/com/riiablo/map/Map.java @@ -555,6 +555,10 @@ public class Map implements Disposable { return null; } + public Zone getZone(Vector2 pos) { + return getZone(Map.round(pos.x), Map.round(pos.y)); + } + Zone addZone(Levels.Entry level, int diff, LvlPrest.Entry preset, int ds1) { assert preset.LevelId != 0 : "presets should have an assigned level id"; Zone zone = addZone(level, diff, level.SizeX[diff], level.SizeY[diff]); diff --git a/core/src/com/riiablo/panel/ControlPanel.java b/core/src/com/riiablo/panel/ControlPanel.java index bc49f6c8..f1b99fcd 100644 --- a/core/src/com/riiablo/panel/ControlPanel.java +++ b/core/src/com/riiablo/panel/ControlPanel.java @@ -21,6 +21,7 @@ import com.riiablo.Riiablo; import com.riiablo.codec.DC6; import com.riiablo.screen.GameScreen; import com.riiablo.widget.Button; +import com.riiablo.widget.HotkeyButton; public class ControlPanel extends Table implements Disposable { private static final String TAG = "ControlPanel"; @@ -39,11 +40,11 @@ public class ControlPanel extends Table implements Disposable { final AssetDescriptor SkilliconDescriptor = new AssetDescriptor<>("data\\global\\ui\\SPELLS\\Skillicon.DC6", DC6.class); DC6 Skillicon; - Button leftSkill, rightSkill; + HotkeyButton leftSkill, rightSkill; GameScreen gameScreen; - public ControlPanel(GameScreen gameScreen) { + public ControlPanel(final GameScreen gameScreen) { this.gameScreen = gameScreen; Riiablo.assets.load(hlthmanaDescriptor); Riiablo.assets.finishLoadingAsset(hlthmanaDescriptor); @@ -66,14 +67,20 @@ public class ControlPanel extends Table implements Disposable { manaWidget = new ManaWidget(ctrlpnl.getTexture(numFrames - 2)); if (!DEBUG_MOBILE && Gdx.app.getType() == Application.ApplicationType.Desktop) { - leftSkill = new Button(new Button.ButtonStyle() {{ - up = new TextureRegionDrawable(Skillicon.getTexture(0)); - down = new TextureRegionDrawable(Skillicon.getTexture(1)); - }}); - rightSkill = new Button(new Button.ButtonStyle() {{ - up = new TextureRegionDrawable(Skillicon.getTexture(0)); - down = new TextureRegionDrawable(Skillicon.getTexture(1)); - }}); + leftSkill = new HotkeyButton(Skillicon, 0); + leftSkill.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + gameScreen.spellsQuickPanelL.setVisible(!gameScreen.spellsQuickPanelL.isVisible()); + } + }); + rightSkill = new HotkeyButton(Skillicon, 0); + rightSkill.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + gameScreen.spellsQuickPanelR.setVisible(!gameScreen.spellsQuickPanelR.isVisible()); + } + }); int width = 0; int height = Integer.MIN_VALUE; @@ -108,6 +115,14 @@ public class ControlPanel extends Table implements Disposable { //setDebug(true, true); } + public HotkeyButton getLeftSkill() { + return leftSkill; + } + + public HotkeyButton getRightSkill() { + return rightSkill; + } + @Override public void dispose() { Riiablo.assets.unload(ctrlpnlDescriptor.fileName); diff --git a/core/src/com/riiablo/panel/SpellsQuickPanel.java b/core/src/com/riiablo/panel/SpellsQuickPanel.java new file mode 100644 index 00000000..13de7112 --- /dev/null +++ b/core/src/com/riiablo/panel/SpellsQuickPanel.java @@ -0,0 +1,127 @@ +package com.riiablo.panel; + +import com.badlogic.gdx.assets.AssetDescriptor; +import com.badlogic.gdx.scenes.scene2d.InputEvent; +import com.badlogic.gdx.scenes.scene2d.ui.Table; +import com.badlogic.gdx.scenes.scene2d.utils.ClickListener; +import com.badlogic.gdx.utils.Align; +import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.utils.ObjectMap; +import com.riiablo.CharacterClass; +import com.riiablo.Keys; +import com.riiablo.Riiablo; +import com.riiablo.codec.DC6; +import com.riiablo.codec.excel.SkillDesc; +import com.riiablo.codec.excel.Skills; +import com.riiablo.entity.Player; +import com.riiablo.key.MappedKey; +import com.riiablo.key.MappedKeyStateAdapter; +import com.riiablo.screen.GameScreen; +import com.riiablo.widget.HotkeyButton; + +import org.apache.commons.lang3.ArrayUtils; + +public class SpellsQuickPanel extends Table implements Disposable { + final AssetDescriptor SkilliconDescriptor = new AssetDescriptor<>("data\\global\\ui\\SPELLS\\Skillicon.DC6", DC6.class); + DC6 Skillicon; + + final AssetDescriptor CharSkilliconDescriptor; + DC6 CharSkillicon; + + GameScreen gameScreen; + ObjectMap keyMappings; + MappedKeyStateAdapter mappedKeyListener; + + public SpellsQuickPanel(final GameScreen gameScreen, final boolean leftSkills) { + this.gameScreen = gameScreen; + + Riiablo.assets.load(SkilliconDescriptor); + Riiablo.assets.finishLoadingAsset(SkilliconDescriptor); + Skillicon = Riiablo.assets.get(SkilliconDescriptor); + + Player player = gameScreen.player; + CharacterClass charClass = player.charClass; + CharSkilliconDescriptor = new AssetDescriptor<>("data\\global\\ui\\SPELLS\\" + charClass.spellIcons + ".DC6", DC6.class); + Riiablo.assets.load(CharSkilliconDescriptor); + Riiablo.assets.finishLoadingAsset(CharSkilliconDescriptor); + CharSkillicon = Riiablo.assets.get(CharSkilliconDescriptor); + + keyMappings = new ObjectMap<>(31); + Table top = new Table() {{ + add(new HotkeyButton(Skillicon, 14)); + add(new HotkeyButton(Skillicon, 18)); + pack(); + }}; + Table[] tables = new Table[5]; + for (int i = charClass.firstSpell; i < charClass.lastSpell; i++) { + if (player.skills.getLevel(i) <= 0) continue; + + final Skills.Entry skill = Riiablo.files.skills.get(i); + if (leftSkills && !skill.leftskill) continue; + if (skill.passive) continue; + + final SkillDesc.Entry desc = Riiablo.files.skilldesc.get(skill.skilldesc); + Table table = tables[desc.ListRow]; + if (table == null) table = tables[desc.ListRow] = new Table(); + final HotkeyButton button = new HotkeyButton(CharSkillicon, desc.IconCel); + + int index = ArrayUtils.indexOf(player.skillBar, i); + if (index != ArrayUtils.INDEX_NOT_FOUND) { + MappedKey mapping = Keys.Skill[index]; + button.map(mapping); + keyMappings.put(mapping, button); + } + + button.addListener(new ClickListener() { + @Override + public void clicked(InputEvent event, float x, float y) { + ControlPanel controlPanel = gameScreen.controlPanel; + if (leftSkills) { + controlPanel.getLeftSkill().copy(button); + } else { + controlPanel.getRightSkill().copy(button); + } + } + }); + table.add(button); + } + Table bottom = new Table() {{ + add(new HotkeyButton(Skillicon, 4)); + add(new HotkeyButton(Skillicon, 6)); + add(new HotkeyButton(Skillicon, 2)); + pack(); + }}; + add(top).align(leftSkills ? Align.left : Align.right).row(); + for (int i = tables.length - 1; i >= 0; i--) { + if (tables[i] != null) { + add(tables[i]).align(leftSkills ? Align.left : Align.right).row(); + } + } + add(bottom).align(leftSkills ? Align.left : Align.right).row(); + pack(); + //setDebug(true, true); + + mappedKeyListener = new MappedKeyStateAdapter() { + @Override + public void onPressed(MappedKey key, int keycode) { + HotkeyButton button = keyMappings.get(key); + if (button == null) return; + // TODO: Assign + ControlPanel controlPanel = gameScreen.controlPanel; + if (leftSkills) { + controlPanel.getLeftSkill().copy(button); + } else { + controlPanel.getRightSkill().copy(button); + } + } + }; + for (MappedKey Skill : Keys.Skill) Skill.addStateListener(mappedKeyListener); + } + + @Override + public void dispose() { + for (MappedKey Skill : Keys.Skill) Skill.removeStateListener(mappedKeyListener); + Riiablo.assets.unload(SkilliconDescriptor.fileName); + Riiablo.assets.unload(CharSkilliconDescriptor.fileName); + } +} diff --git a/core/src/com/riiablo/screen/GameScreen.java b/core/src/com/riiablo/screen/GameScreen.java index 6848261b..2f6d4a54 100644 --- a/core/src/com/riiablo/screen/GameScreen.java +++ b/core/src/com/riiablo/screen/GameScreen.java @@ -55,6 +55,7 @@ import com.riiablo.panel.InventoryPanel; import com.riiablo.panel.MobileControls; import com.riiablo.panel.MobilePanel; import com.riiablo.panel.SpellsPanel; +import com.riiablo.panel.SpellsQuickPanel; import com.riiablo.panel.StashPanel; import com.riiablo.server.Connect; import com.riiablo.server.ConnectResponse; @@ -93,13 +94,15 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable Actor left; Actor right; - ControlPanel controlPanel; + public ControlPanel controlPanel; MobilePanel mobilePanel; MobileControls mobileControls; public InventoryPanel inventoryPanel; public CharacterPanel characterPanel; public SpellsPanel spellsPanel; public StashPanel stashPanel; + public SpellsQuickPanel spellsQuickPanelL; + public SpellsQuickPanel spellsQuickPanelR; MappedKeyStateAdapter mappedKeyStateListener; Stage stage; @@ -232,6 +235,14 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable 0, stage.getHeight() - stashPanel.getHeight()); + spellsQuickPanelL = new SpellsQuickPanel(this, true); + spellsQuickPanelL.setPosition(0, 100, Align.bottomLeft); + spellsQuickPanelL.setVisible(false); + + spellsQuickPanelR = new SpellsQuickPanel(this, false); + spellsQuickPanelR.setPosition(stage.getWidth(), 100, Align.bottomRight); + spellsQuickPanelR.setVisible(false); + //stage.setDebugAll(true); if (mobilePanel != null) stage.addActor(mobilePanel); if (mobileControls != null) stage.addActor(mobileControls); @@ -243,6 +254,8 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable stage.addActor(spellsPanel); stage.addActor(characterPanel); stage.addActor(stashPanel); + stage.addActor(spellsQuickPanelL); + stage.addActor(spellsQuickPanelR); controlPanel.toFront(); if (mobilePanel != null) mobilePanel.toFront(); output.toFront(); @@ -493,6 +506,22 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable } else {*/ mapListener.update(); //} + + if (Gdx.input.isKeyPressed(Input.Keys.F1) || Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) { + mapRenderer.unproject(tmpVec2.set(Gdx.input.getX(), Gdx.input.getY())); + player.lookAt(tmpVec2.x, tmpVec2.y); + boolean cast = player.cast(54); + if (cast) { + GridPoint2 dst = mapRenderer.coords(tmpVec2.x, tmpVec2.y); + player.position().set(dst.x, dst.y); + player.target().setZero(); + player.setPath(null, null); + } + } else if (Gdx.input.isKeyPressed(Input.Keys.F2)) { + mapRenderer.unproject(tmpVec2.set(Gdx.input.getX(), Gdx.input.getY())); + player.lookAt(tmpVec2.x, tmpVec2.y); + boolean cast = player.cast(36); + } } else if (DEBUG_HIT) Gdx.app.debug(TAG, hit.toString()); } @@ -781,6 +810,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable } private void displayEntry() { + if (curZone == null) return; if (enteringImage == null) { enteringImage = new DCWrapper(); enteringImage.setScaling(Scaling.none); @@ -795,8 +825,7 @@ public class GameScreen extends ScreenAdapter implements LoadingScreen.Loadable Riiablo.assets.load(entryDescriptor); Riiablo.assets.finishLoadingAsset(entryDescriptor); enteringImage.setDrawable(Riiablo.assets.get(entryDescriptor)); - enteringImage.setPosition(stage.getWidth() / 2, stage.getHeight() * 0.75f, Align.center); - System.out.println(enteringImage.getWidth() + ", " + enteringImage.getHeight()); + enteringImage.setPosition(stage.getWidth() / 2, stage.getHeight() * 0.8f, Align.center); enteringImage.clearActions(); enteringImage.addAction(Actions.sequence( Actions.show(), diff --git a/core/src/com/riiablo/widget/Button.java b/core/src/com/riiablo/widget/Button.java index 06ee2f1e..28ac263c 100644 --- a/core/src/com/riiablo/widget/Button.java +++ b/core/src/com/riiablo/widget/Button.java @@ -17,6 +17,12 @@ public class Button extends com.badlogic.gdx.scenes.scene2d.ui.Button implements private static final AssetDescriptor buttonDescriptor = new AssetDescriptor<>("data\\global\\sfx\\cursor\\button.wav", Sound.class); + int disabledBlendMode = BlendMode.DARKEN; + Color disabledColor = Riiablo.colors.darken; + + int highlightedBlendMode = BlendMode.BRIGHTEN; + Color highlightedColor = Riiablo.colors.highlight; + @Override public void setStyle(com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle style) { super.setStyle(style); @@ -47,6 +53,16 @@ public class Button extends com.badlogic.gdx.scenes.scene2d.ui.Button implements }); } + public void setDisabledBlendMode(int blendMode, Color color) { + disabledBlendMode = blendMode; + disabledColor = color; + } + + public void setHighlightedBlendMode(int blendMode, Color color) { + highlightedBlendMode = blendMode; + highlightedColor = color; + } + @Override public void dispose() { Riiablo.assets.unload(buttonDescriptor.fileName); @@ -63,15 +79,19 @@ public class Button extends com.badlogic.gdx.scenes.scene2d.ui.Button implements // FIXME: super.draw(Batch,float) sets color and applies to batch, circumventing my own color -- workaround is to set actor's color public void draw(PaletteIndexedBatch batch, float parentAlpha) { - final boolean over = isOver() && !isDisabled(); - if (isDisabled()) { - setColor(Riiablo.colors.trans50); + final boolean disabled = isDisabled(); + final boolean over = isOver(); + if (disabled) { + setColor(disabledColor); + batch.setBlendMode(disabledBlendMode); + } else if (over) { + setColor(highlightedColor); + batch.setBlendMode(highlightedBlendMode); } else { - setColor(over ? Riiablo.colors.highlight : Color.WHITE); + setColor(Color.WHITE); } - if (over) batch.setBlendMode(BlendMode.BRIGHTEN); super.draw(batch, parentAlpha); - if (over) batch.resetBlendMode(); + if (disabled || over) batch.resetBlendMode(); } public static class ButtonStyle extends com.badlogic.gdx.scenes.scene2d.ui.Button.ButtonStyle { diff --git a/core/src/com/riiablo/widget/HotkeyButton.java b/core/src/com/riiablo/widget/HotkeyButton.java new file mode 100644 index 00000000..b31cd309 --- /dev/null +++ b/core/src/com/riiablo/widget/HotkeyButton.java @@ -0,0 +1,44 @@ +package com.riiablo.widget; + +import com.badlogic.gdx.Input; +import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable; +import com.badlogic.gdx.utils.Align; +import com.riiablo.Riiablo; +import com.riiablo.codec.DC; +import com.riiablo.graphics.BlendMode; +import com.riiablo.key.MappedKey; + +public class HotkeyButton extends Button { + MappedKey mapping; + Label label; + + public HotkeyButton(final DC dc, final int index) { + super(new ButtonStyle() {{ + up = new TextureRegionDrawable(dc.getTexture(index)); + down = new TextureRegionDrawable(dc.getTexture(index + 1)); + disabled = up; + pressedOffsetX = pressedOffsetY = -2; + }}); + + add(label = new Label("", Riiablo.fonts.font16, Riiablo.colors.gold)); + align(Align.topRight); + pad(2); + pack(); + + setDisabledBlendMode(BlendMode.DARKEN, Riiablo.colors.darkenR); + } + + public void map(MappedKey mapping) { + this.mapping = mapping; + label.setText(Input.Keys.toString(mapping.getPrimaryAssignment())); + } + + public MappedKey getMapping() { + return mapping; + } + + public void copy(HotkeyButton other) { + setStyle(other.getStyle()); + label.setText(other.label.getText()); + } +}