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()); + } +}