From 2890e640e0383ea96253859ba35e4a8906ab2d8a Mon Sep 17 00:00:00 2001 From: Retera Date: Mon, 22 Mar 2021 08:34:44 -0400 Subject: [PATCH] Update to allow test menu to enter a campaign screen --- .../etheller/warsmash/SingleModelScreen.java | 5 + .../warsmash/WarsmashGdxMenuScreen.java | 94 ++++- .../warsmash/parsers/fdf/GameSkin.java | 22 + .../etheller/warsmash/parsers/fdf/GameUI.java | 142 +++++-- .../fdf/frames/AbstractRenderableFrame.java | 39 ++ .../parsers/fdf/frames/AbstractUIFrame.java | 5 + .../parsers/fdf/frames/BackdropFrame.java | 17 +- .../parsers/fdf/frames/ControlFrame.java | 35 ++ .../parsers/fdf/frames/EditBoxFrame.java | 164 ++++++++ .../parsers/fdf/frames/ListBoxFrame.java | 110 +++++ .../parsers/fdf/frames/StringFrame.java | 35 +- .../warsmash/parsers/fdf/frames/UIFrame.java | 8 + .../etheller/warsmash/parsers/jass/Jass2.java | 5 +- .../warsmash/util/WarsmashConstants.java | 2 +- .../viewer5/handlers/w3x/ui/MeleeUI.java | 4 +- .../viewer5/handlers/w3x/ui/MenuUI.java | 377 ++++++++++++++++-- .../w3x/ui/command/FocusableFrame.java | 17 + .../rms/parsers/mdlx/AnimationMap.java | 2 +- 18 files changed, 977 insertions(+), 106 deletions(-) create mode 100644 core/src/com/etheller/warsmash/SingleModelScreen.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/GameSkin.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/ControlFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/EditBoxFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/ListBoxFrame.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/FocusableFrame.java diff --git a/core/src/com/etheller/warsmash/SingleModelScreen.java b/core/src/com/etheller/warsmash/SingleModelScreen.java new file mode 100644 index 0000000..cf583e1 --- /dev/null +++ b/core/src/com/etheller/warsmash/SingleModelScreen.java @@ -0,0 +1,5 @@ +package com.etheller.warsmash; + +public interface SingleModelScreen { + void setModel(String path); +} diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java b/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java index 4788545..761a69c 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMenuScreen.java @@ -20,6 +20,8 @@ import com.badlogic.gdx.math.Quaternion; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.viewport.ExtendViewport; +import com.badlogic.gdx.utils.viewport.FitViewport; +import com.badlogic.gdx.utils.viewport.Viewport; import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener; @@ -44,10 +46,11 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer; import com.etheller.warsmash.viewer5.handlers.mdx.Sequence; import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode; +import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; import com.etheller.warsmash.viewer5.handlers.w3x.ui.MenuUI; -public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Screen { +public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Screen, SingleModelScreen { private static final boolean ENABLE_AUDIO = true; private static final boolean ENABLE_MUSIC = true; private DataSource codebase; @@ -59,7 +62,7 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc // libGDX stuff private OrthographicCamera uiCamera; private SpriteBatch batch; - private ExtendViewport uiViewport; + private Viewport uiViewport; private GlyphLayout glyphLayout; private final DataTable warsmashIni; @@ -68,6 +71,7 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc private MenuUI menuUI; private final WarsmashGdxMultiScreenGame game; private Music currentMusic; + private boolean hasPlayedStandHack = false; public WarsmashGdxMenuScreen(final DataTable warsmashIni, final WarsmashGdxMultiScreenGame game) { this.warsmashIni = warsmashIni; @@ -94,8 +98,8 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc this.viewer.addHandler(new MdxHandler()); this.viewer.enableAudio(); - final Scene scene = this.viewer.addSimpleScene(); - scene.enableAudio(); + this.scene = this.viewer.addSimpleScene(); + this.scene.enableAudio(); this.uiScene = this.viewer.addSimpleScene(); this.uiScene.alpha = true; @@ -121,10 +125,10 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc aspect3By4Width = (height * 4) / 3; aspect3By4Height = height; } - this.uiViewport = new ExtendViewport(aspect3By4Width, aspect3By4Height, this.uiCamera); + this.uiViewport = new FitViewport(aspect3By4Width, aspect3By4Height, this.uiCamera); this.uiViewport.update(width, height); - this.uiCamera.position.set(this.uiViewport.getMinWorldWidth() / 2, this.uiViewport.getMinWorldHeight() / 2, 0); + this.uiCamera.position.set(getMinWorldWidth() / 2, getMinWorldHeight() / 2, 0); this.uiCamera.update(); this.batch = new SpriteBatch(); @@ -137,7 +141,7 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc Gdx.input.setInputProcessor(this); this.cameraManager = new CameraManager(); - this.cameraManager.setupCamera(scene); + this.cameraManager.setupCamera(this.scene); // this.mainModel = (MdxModel) this.viewer.load("Doodads\\Cinematic\\ArthasIllidanFight\\ArthasIllidanFight.mdx", // this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\SinglePlayer\\NightElf_Exp\\NightElf_Exp.mdx", @@ -174,7 +178,7 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc System.out.println("Loaded"); Gdx.gl30.glClearColor(0.0f, 0.0f, 0.0f, 1); - this.menuUI = new MenuUI(this.viewer.dataSource, this.uiViewport, this.uiScene, this.viewer, this.game, + this.menuUI = new MenuUI(this.viewer.dataSource, this.uiViewport, this.uiScene, this.viewer, this.game, this, this.warsmashIni, new RootFrameListener() { @Override public void onCreate(final GameUI rootFrame) { @@ -193,7 +197,7 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc WarsmashGdxMenuScreen.this.currentMusic = music; } - singleModelScene(scene, + singleModelScene(WarsmashGdxMenuScreen.this.scene, War3MapViewer.mdx(rootFrame .getSkinField("GlueSpriteLayerBackground_V" + WarsmashConstants.GAME_VERSION)), "Stand"); @@ -213,6 +217,20 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc } + private float getMinWorldWidth() { + if (this.uiViewport instanceof ExtendViewport) { + return ((ExtendViewport) this.uiViewport).getMinWorldWidth(); + } + return this.uiViewport.getWorldWidth(); + } + + private float getMinWorldHeight() { + if (this.uiViewport instanceof ExtendViewport) { + return ((ExtendViewport) this.uiViewport).getMinWorldHeight(); + } + return this.uiViewport.getWorldHeight(); + } + private void updateUIScene() { this.tempRect.x = this.uiViewport.getScreenX(); this.tempRect.y = this.uiViewport.getScreenY(); @@ -221,8 +239,8 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc this.uiScene.camera.viewport(this.tempRect); final float worldWidth = this.uiViewport.getWorldWidth(); final float worldHeight = this.uiViewport.getWorldHeight(); - final float xScale = worldWidth / this.uiViewport.getMinWorldWidth(); - final float yScale = worldHeight / this.uiViewport.getMinWorldHeight(); + final float xScale = worldWidth / getMinWorldWidth(); + final float yScale = worldHeight / getMinWorldHeight(); final float uiSceneWidth = 0.8f * xScale; final float uiSceneHeight = 0.6f * yScale; final float uiSceneX = ((0.8f - uiSceneWidth) / 2); @@ -285,11 +303,27 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc } instance3.setSequence(animIndex); - instance3.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); + instance3.setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP); this.mainInstance = instance3; this.mainModel = model2; } + @Override + public void setModel(final String path) { + if (this.mainInstance != null) { + this.mainInstance.detach(); + } + singleModelScene(this.scene, War3MapViewer.mdx(path), "birth"); + WarsmashGdxMenuScreen.this.modelCamera = WarsmashGdxMenuScreen.this.mainModel.cameras.get(0); + // this hack is because we only have the queued animation system in RenderWidget + // which is stupid and back and needs to get moved to the model instance + // itself... our model instance class is a + // hacky replica of a model viewer tool with a bunch of irrelevant loop type + // settings instead of what it should be + this.hasPlayedStandHack = false; + + } + private void acolytesHarvestingScene(final Scene scene) { final MdxModel acolyteModel = (MdxModel) this.viewer.load("units\\undead\\acolyte\\acolyte.mdx", @@ -477,6 +511,7 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc private final float[] cameraPositionTemp = new float[3]; private final float[] cameraTargetTemp = new float[3]; private final boolean firstFrame = true; + private Scene scene; @Override public void render(final float delta) { @@ -486,6 +521,12 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO); this.cameraManager.updateCamera(); this.menuUI.update(deltaTime); + if (this.mainInstance.sequenceEnded + && (((this.mainModel.getSequences().get(this.mainInstance.sequence).getFlags() & 0x1) == 0) + || !this.hasPlayedStandHack)) { + SequenceUtils.randomStandSequence(this.mainInstance); + this.hasPlayedStandHack = true; + } this.viewer.updateAndRender(); Gdx.gl30.glDisable(GL30.GL_SCISSOR_TEST); @@ -516,12 +557,26 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc public void resize(final int width, final int height) { this.tempRect.width = width; this.tempRect.height = height; + final float fourThirdsHeight = (this.tempRect.height * 4) / 3; + if (fourThirdsHeight < this.tempRect.width) { + final float dx = this.tempRect.width - fourThirdsHeight; + this.tempRect.width = fourThirdsHeight; + this.tempRect.x = dx / 2; + } + else { + final float threeFourthsWidth = (this.tempRect.width * 3) / 4; + if (threeFourthsWidth < this.tempRect.height) { + final float dy = this.tempRect.height - threeFourthsWidth; + this.tempRect.height = threeFourthsWidth; + this.tempRect.y = dy; + } + } this.cameraManager.camera.viewport(this.tempRect); // super.resize(width, height); this.uiViewport.update(width, height); - this.uiCamera.position.set(this.uiViewport.getMinWorldWidth() / 2, this.uiViewport.getMinWorldHeight() / 2, 0); + this.uiCamera.position.set(getMinWorldWidth() / 2, getMinWorldHeight() / 2, 0); this.menuUI.resize(); updateUIScene(); @@ -612,8 +667,8 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc WarsmashGdxMenuScreen.this.cameraPositionTemp[2]); this.target.add(WarsmashGdxMenuScreen.this.cameraTargetTemp[0], WarsmashGdxMenuScreen.this.cameraTargetTemp[1], WarsmashGdxMenuScreen.this.cameraTargetTemp[2]); - this.camera.perspective(WarsmashGdxMenuScreen.this.modelCamera.fieldOfView * 0.75f, - Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(), + this.camera.perspective(WarsmashGdxMenuScreen.this.modelCamera.fieldOfView * 0.6f, + this.camera.rect.width / this.camera.rect.height, WarsmashGdxMenuScreen.this.modelCamera.nearClippingPlane, WarsmashGdxMenuScreen.this.modelCamera.farClippingPlane); } @@ -635,20 +690,17 @@ public class WarsmashGdxMenuScreen implements CanvasProvider, InputProcessor, Sc @Override public boolean keyDown(final int keycode) { - // TODO Auto-generated method stub - return false; + return this.menuUI.keyDown(keycode); } @Override public boolean keyUp(final int keycode) { - // TODO Auto-generated method stub - return false; + return this.menuUI.keyUp(keycode); } @Override public boolean keyTyped(final char character) { - // TODO Auto-generated method stub - return false; + return this.menuUI.keyTyped(character); } @Override diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameSkin.java b/core/src/com/etheller/warsmash/parsers/fdf/GameSkin.java new file mode 100644 index 0000000..ccbcf44 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameSkin.java @@ -0,0 +1,22 @@ +package com.etheller.warsmash.parsers.fdf; + +import com.etheller.warsmash.units.DataTable; +import com.etheller.warsmash.units.Element; + +public class GameSkin { + private final Element skin; + private final DataTable skinsTable; + + public GameSkin(final Element skin, final DataTable skinsTable) { + this.skin = skin; + this.skinsTable = skinsTable; + } + + public Element getSkin() { + return this.skin; + } + + public DataTable getSkinsTable() { + return this.skinsTable; + } +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java index 404020e..990bee2 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -34,9 +34,12 @@ import com.etheller.warsmash.parsers.fdf.datamodel.Vector2Definition; import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition; import com.etheller.warsmash.parsers.fdf.frames.AbstractUIFrame; import com.etheller.warsmash.parsers.fdf.frames.BackdropFrame; +import com.etheller.warsmash.parsers.fdf.frames.ControlFrame; +import com.etheller.warsmash.parsers.fdf.frames.EditBoxFrame; import com.etheller.warsmash.parsers.fdf.frames.FilterModeTextureFrame; import com.etheller.warsmash.parsers.fdf.frames.GlueButtonFrame; import com.etheller.warsmash.parsers.fdf.frames.GlueTextButtonFrame; +import com.etheller.warsmash.parsers.fdf.frames.ListBoxFrame; import com.etheller.warsmash.parsers.fdf.frames.SetPoint; import com.etheller.warsmash.parsers.fdf.frames.SimpleFrame; import com.etheller.warsmash.parsers.fdf.frames.SimpleStatusBarFrame; @@ -52,6 +55,7 @@ import com.etheller.warsmash.util.StringBundle; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.handlers.AbstractMdxModelViewer; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; +import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.FocusableFrame; import com.hiveworkshop.rms.parsers.mdlx.MdlxLayer.FilterMode; public final class GameUI extends AbstractUIFrame implements UIFrame { @@ -77,11 +81,11 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { private final BitmapFont font20; private final DynamicFontGeneratorHolder dynamicFontGeneratorHolder; - public GameUI(final DataSource dataSource, final Element skin, final Viewport viewport, final Scene uiScene, + public GameUI(final DataSource dataSource, final GameSkin skin, final Viewport viewport, final Scene uiScene, final AbstractMdxModelViewer modelViewer, final int racialCommandIndex, final WTS mapStrings) { super("GameUI", null); this.dataSource = dataSource; - this.skin = skin; + this.skin = skin.getSkin(); this.viewport = viewport; this.uiScene = uiScene; this.modelViewer = modelViewer; @@ -95,7 +99,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } this.templates = new FrameTemplateEnvironment(); - this.dynamicFontGeneratorHolder = new DynamicFontGeneratorHolder(this.modelViewer.dataSource, skin); + this.dynamicFontGeneratorHolder = new DynamicFontGeneratorHolder(this.modelViewer.dataSource, this.skin); this.fontGenerator = this.dynamicFontGeneratorHolder.getFontGenerator("MasterFont"); final FreeTypeFontParameter fontParam = new FreeTypeFontParameter(); fontParam.size = 32; @@ -104,32 +108,22 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { this.font20 = this.fontGenerator.generateFont(fontParam); this.fontParam = new FreeTypeFontParameter(); this.fdfCoordinateResolutionDummyViewport = new FitViewport(0.8f, 0.6f); - this.skinData = new DataTable(modelViewer.getWorldEditStrings()); - try { - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\CommandFunc.txt")) { - this.skinData.readTXT(miscDataTxtStream, true); - } - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\CommandStrings.txt")) { - this.skinData.readTXT(miscDataTxtStream, true); - } - if (this.dataSource.has("war3mapSkin.txt")) { - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("war3mapSkin.txt")) { - this.skinData.readTXT(miscDataTxtStream, true); - } - } - } - catch (final IOException e) { - throw new RuntimeException(e); - } + this.skinData = skin.getSkinsTable(); this.errorStrings = this.skinData.get("Errors"); this.glyphLayout = new GlyphLayout(); this.mapStrings = mapStrings; } - public static Element loadSkin(final DataSource dataSource, final String skin) { + public static GameSkin loadSkin(final DataSource dataSource, final String skin) { final DataTable skinsTable = new DataTable(StringBundle.EMPTY); try (InputStream stream = dataSource.getResourceAsStream("UI\\war3skins.txt")) { skinsTable.readTXT(stream, true); + try (InputStream miscDataTxtStream = dataSource.getResourceAsStream("Units\\CommandFunc.txt")) { + skinsTable.readTXT(miscDataTxtStream, true); + } + try (InputStream miscDataTxtStream = dataSource.getResourceAsStream("Units\\CommandStrings.txt")) { + skinsTable.readTXT(miscDataTxtStream, true); + } } catch (final IOException e) { throw new RuntimeException(e); @@ -159,10 +153,10 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { userSkin.setField(key, customSkin.getField(key)); } } - return userSkin; + return new GameSkin(userSkin, skinsTable); } - public static Element loadSkin(final DataSource dataSource, final int skinIndex) { + public static GameSkin loadSkin(final DataSource dataSource, final int skinIndex) { final DataTable skinsTable = new DataTable(StringBundle.EMPTY); try (InputStream stream = dataSource.getResourceAsStream("UI\\war3skins.txt")) { skinsTable.readTXT(stream, true); @@ -201,7 +195,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { userSkin.setField(key, customSkin.getField(key)); } } - return userSkin; + return new GameSkin(userSkin, skinsTable); } public void loadTOCFile(final String tocFilePath) throws IOException { @@ -246,7 +240,9 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { public UIFrame createFrame(final String name, final UIFrame owner, final int priority, final int createContext) { final FrameDefinition frameDefinition = this.templates.getFrame(name); final UIFrame inflatedFrame = inflate(frameDefinition, owner, null, frameDefinition.has("DecorateFileNames")); - add(inflatedFrame); + if (owner == this) { + add(inflatedFrame); + } return inflatedFrame; } @@ -344,10 +340,6 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { if (this.skin.hasField(backgroundArt)) { backgroundArt = this.skin.getField(backgroundArt); } - else { - throw new IllegalStateException( - "Decorated file name lookup not available: " + backgroundArt); - } } } if (backgroundArt != null) { @@ -373,7 +365,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { final Float textLength = frameDefinition.getFloat("TextLength"); TextJustify justifyH = frameDefinition.getTextJustify("FontJustificationH"); if (justifyH == null) { - justifyH = TextJustify.CENTER; + justifyH = TextJustify.LEFT; } TextJustify justifyV = frameDefinition.getTextJustify("FontJustificationV"); if (justifyV == null) { @@ -402,7 +394,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { final FontDefinition font = frameDefinition.getFont("FrameFont"); final Float height = frameDefinition.getFloat("Height"); this.fontParam.size = (int) convertY(viewport2, - font == null ? (height == null ? 0.06f : height) : font.getFontSize()); + (font == null ? (height == null ? 0.06f : height) : font.getFontSize())); if (this.fontParam.size == 0) { this.fontParam.size = 24; } @@ -524,6 +516,78 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } inflatedFrame = glueButtonFrame; } + else if ("EDITBOX".equals(frameDefinition.getFrameType())) { + final float editBorderSize = convertX(viewport2, frameDefinition.getFloat("EditBorderSize")); + final Vector4Definition editCursorColorDefinition = frameDefinition.getVector4("EditCursorColor"); + Color editCursorColor; + if (editCursorColorDefinition == null) { + editCursorColor = Color.WHITE; + } + else { + editCursorColor = new Color(editCursorColorDefinition.getX(), editCursorColorDefinition.getY(), + editCursorColorDefinition.getZ(), editCursorColorDefinition.getW()); + } + final EditBoxFrame editBoxFrame = new EditBoxFrame(frameDefinition.getName(), parent, editBorderSize, + editCursorColor); + // TODO: we should not need to put ourselves in this map 2x, but we do + // since there are nested inflate calls happening before the general case + // mapping + this.nameToFrame.put(frameDefinition.getName(), editBoxFrame); + final String controlBackdropKey = frameDefinition.getString("ControlBackdrop"); + final String editTextFrameKey = frameDefinition.getString("EditTextFrame"); + for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { + if (childDefinition.getName().equals(controlBackdropKey)) { + final UIFrame inflatedChild = inflate(childDefinition, editBoxFrame, frameDefinition, + inDecorateFileNames || childDefinition.has("DecorateFileNames")); + inflatedChild.setSetAllPoints(true); + editBoxFrame.setControlBackdrop(inflatedChild); + } + else if (childDefinition.getName().equals(editTextFrameKey)) { + final UIFrame inflatedChild = inflate(childDefinition, editBoxFrame, frameDefinition, + inDecorateFileNames || childDefinition.has("DecorateFileNames")); + final StringFrame editTextFrame = (StringFrame) inflatedChild; + inflatedChild.setSetAllPoints(true, editBorderSize); + editBoxFrame.setEditTextFrame(editTextFrame); + setText(editTextFrame, ""); + } + } + inflatedFrame = editBoxFrame; + } + else if ("CONTROL".equals(frameDefinition.getFrameType())) { + final ControlFrame controlFrame = new ControlFrame(frameDefinition.getName(), parent); + // TODO: we should not need to put ourselves in this map 2x, but we do + // since there are nested inflate calls happening before the general case + // mapping + this.nameToFrame.put(frameDefinition.getName(), controlFrame); + final String controlBackdropKey = frameDefinition.getString("ControlBackdrop"); + for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { + if (childDefinition.getName().equals(controlBackdropKey)) { + final UIFrame inflatedChild = inflate(childDefinition, controlFrame, frameDefinition, + inDecorateFileNames || childDefinition.has("DecorateFileNames")); + inflatedChild.setSetAllPoints(true); + controlFrame.setControlBackdrop(inflatedChild); + } + } + inflatedFrame = controlFrame; + } + else if ("LISTBOX".equals(frameDefinition.getFrameType())) { + // TODO advanced components here + final ListBoxFrame controlFrame = new ListBoxFrame(frameDefinition.getName(), parent, viewport2); + // TODO: we should not need to put ourselves in this map 2x, but we do + // since there are nested inflate calls happening before the general case + // mapping + this.nameToFrame.put(frameDefinition.getName(), controlFrame); + final String controlBackdropKey = frameDefinition.getString("ControlBackdrop"); + for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { + if (childDefinition.getName().equals(controlBackdropKey)) { + final UIFrame inflatedChild = inflate(childDefinition, controlFrame, frameDefinition, + inDecorateFileNames || childDefinition.has("DecorateFileNames")); + inflatedChild.setSetAllPoints(true); + controlFrame.setControlBackdrop(inflatedChild); + } + } + inflatedFrame = controlFrame; + } else if ("HIGHLIGHT".equals(frameDefinition.getFrameType())) { final String highlightType = frameDefinition.getString("HighlightType"); if (!"FILETEXTURE".equals(highlightType)) { @@ -542,6 +606,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } else if ("BACKDROP".equals(frameDefinition.getFrameType())) { final boolean tileBackground = frameDefinition.has("BackdropTileBackground"); + final boolean mirrored = frameDefinition.has("BackdropMirrored"); String backgroundString = frameDefinition.getString("BackdropBackground"); String cornerFlagsString = frameDefinition.getString("BackdropCornerFlags"); if (cornerFlagsString == null) { @@ -580,7 +645,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { final BackdropFrame backdropFrame = new BackdropFrame(frameDefinition.getName(), parent, decorateFileNames, tileBackground, background, cornerFlags, cornerSize, backgroundSize, - backgroundInsets, edgeFile); + backgroundInsets, edgeFile, mirrored); this.nameToFrame.put(frameDefinition.getName(), backdropFrame); for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { backdropFrame.add(inflate(childDefinition, backdropFrame, frameDefinition, decorateFileNames)); @@ -716,9 +781,15 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { final String inherits, final int createContext) { // TODO idk what inherits is doing yet, and I didn't implement createContext yet // even though it looked like just mapping/indexing on int + final FrameDefinition baseTemplateDef = this.templates.getFrame(name); final FrameDefinition frameDefinition = new FrameDefinition(FrameClass.Frame, typeName, name); + if (baseTemplateDef != null) { + frameDefinition.inheritFrom(baseTemplateDef, "WITHCHILDREN".equals(inherits)); + } final UIFrame inflatedFrame = inflate(frameDefinition, owner, null, frameDefinition.has("DecorateFileNames")); - add(inflatedFrame); + if (this == owner) { + add(inflatedFrame); + } return inflatedFrame; } @@ -830,4 +901,9 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { public void dispose() { this.dynamicFontGeneratorHolder.dispose(); } + + public FocusableFrame getNextFocusFrame() { + // TODO to support tabbing thru menus and stuff, we will have to implement this + return null; + } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java index ddd3c15..a89d357 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java @@ -47,6 +47,28 @@ public abstract class AbstractRenderableFrame implements UIFrame { } } + @Override + public void setSetAllPoints(final boolean setAllPoints, final float inset) { + this.framePointToAssignment.put(FramePoint.TOPLEFT, + new SetPoint(FramePoint.TOPLEFT, this.parent, FramePoint.TOPLEFT, inset, -inset)); + this.framePointToAssignment.put(FramePoint.LEFT, + new SetPoint(FramePoint.LEFT, this.parent, FramePoint.LEFT, inset, 0)); + this.framePointToAssignment.put(FramePoint.BOTTOMLEFT, + new SetPoint(FramePoint.BOTTOMLEFT, this.parent, FramePoint.BOTTOMLEFT, inset, inset)); + this.framePointToAssignment.put(FramePoint.BOTTOM, + new SetPoint(FramePoint.BOTTOM, this.parent, FramePoint.BOTTOM, 0, inset)); + this.framePointToAssignment.put(FramePoint.BOTTOMRIGHT, + new SetPoint(FramePoint.BOTTOMRIGHT, this.parent, FramePoint.BOTTOMRIGHT, -inset, inset)); + this.framePointToAssignment.put(FramePoint.RIGHT, + new SetPoint(FramePoint.RIGHT, this.parent, FramePoint.RIGHT, -inset, 0)); + this.framePointToAssignment.put(FramePoint.TOPRIGHT, + new SetPoint(FramePoint.TOPRIGHT, this.parent, FramePoint.TOPRIGHT, -inset, -inset)); + this.framePointToAssignment.put(FramePoint.TOP, + new SetPoint(FramePoint.TOP, this.parent, FramePoint.TOP, 0, -inset)); + this.framePointToAssignment.put(FramePoint.CENTER, + new SetPoint(FramePoint.CENTER, this.parent, FramePoint.CENTER, 0, 0)); + } + @Override public void setWidth(final float width) { this.assignedWidth = width; @@ -296,6 +318,7 @@ public abstract class AbstractRenderableFrame implements UIFrame { protected abstract void innerPositionBounds(GameUI gameUI, final Viewport viewport); + @Override public boolean isVisible() { return this.visible; } @@ -320,6 +343,22 @@ public abstract class AbstractRenderableFrame implements UIFrame { } } + @Override + public UIFrame getParent() { + return this.parent; + } + + @Override + public boolean isVisibleOnScreen() { + boolean visibleOnScreen = this.visible; + UIFrame ancestor = this.parent; + while (visibleOnScreen && (ancestor != null)) { + visibleOnScreen &= ancestor.isVisible(); + ancestor = ancestor.getParent(); + } + return visibleOnScreen; + } + protected abstract void internalRender(SpriteBatch batch, BitmapFont baseFont, GlyphLayout glyphLayout); @Override diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java index ff8756f..5727e28 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java @@ -30,6 +30,11 @@ public abstract class AbstractUIFrame extends AbstractRenderableFrame implements super(name, parent); } + @Override + public void setVisible(final boolean visible) { + super.setVisible(visible); + } + @Override protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { for (final UIFrame childFrame : this.childFrames) { diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/BackdropFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/BackdropFrame.java index 2b05c29..5d1f153 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/BackdropFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/BackdropFrame.java @@ -22,14 +22,15 @@ public class BackdropFrame extends AbstractUIFrame { private final float edgeUVWidth; private final float edgeFileHeight; private final float edgeUVHeight; + private final boolean mirrored; public BackdropFrame(final String name, final UIFrame parent, final boolean decorateFileNames, final boolean tileBackground, final Texture background, final EnumSet cornerFlags, final float cornerSize, final float backgroundSize, final Vector4Definition backgroundInsets, - final Texture edgeFile) { + final Texture edgeFile, final boolean mirrored) { super(name, parent); this.decorateFileNames = decorateFileNames; - this.tileBackground = tileBackground; + this.tileBackground = tileBackground && (backgroundSize > 0); this.background = background; this.cornerFlags = cornerFlags; this.cornerSize = cornerSize; @@ -40,6 +41,7 @@ public class BackdropFrame extends AbstractUIFrame { this.edgeFileHeight = edgeFile == null ? 0.0f : edgeFile.getHeight(); this.edgeUVWidth = 1f / 8f; this.edgeUVHeight = 1f; + this.mirrored = mirrored; } @Override @@ -83,7 +85,13 @@ public class BackdropFrame extends AbstractUIFrame { backgroundHeightRemainderRatio); } else { - batch.draw(this.background, backgroundX, backgroundY, backgroundWidth, backgroundHeight); + if (this.mirrored) { + batch.draw(this.background, backgroundX, backgroundY, backgroundWidth, backgroundHeight, 0, 0, + this.background.getWidth(), this.background.getHeight(), true, false); + } + else { + batch.draw(this.background, backgroundX, backgroundY, backgroundWidth, backgroundHeight); + } } } if (this.edgeFile != null) { @@ -181,4 +189,7 @@ public class BackdropFrame extends AbstractUIFrame { super.internalRender(batch, baseFont, glyphLayout); } + public float getCornerSize() { + return this.cornerSize; + } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/ControlFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/ControlFrame.java new file mode 100644 index 0000000..dd6a309 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/ControlFrame.java @@ -0,0 +1,35 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.GlyphLayout; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.parsers.fdf.GameUI; + +public class ControlFrame extends AbstractRenderableFrame { + + private UIFrame controlBackdrop; + + public ControlFrame(final String name, final UIFrame parent) { + super(name, parent); + } + + public void setControlBackdrop(final UIFrame controlBackdrop) { + this.controlBackdrop = controlBackdrop; + } + + public UIFrame getControlBackdrop() { + return this.controlBackdrop; + } + + @Override + protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) { + this.controlBackdrop.positionBounds(gameUI, viewport); + } + + @Override + protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { + this.controlBackdrop.render(batch, baseFont, glyphLayout); + } + +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/EditBoxFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/EditBoxFrame.java new file mode 100644 index 0000000..5ff574e --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/EditBoxFrame.java @@ -0,0 +1,164 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import com.badlogic.gdx.Input; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.GlyphLayout; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.parsers.fdf.GameUI; +import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; +import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.FocusableFrame; + +public class EditBoxFrame extends AbstractRenderableFrame implements FocusableFrame { + + private UIFrame controlBackdrop; + private final float editBorderSize; + private final Color editCursorColor; + private StringFrame editTextFrame; + private boolean focused = false; + private int cursorIndex; + + // TODO design in such a way that references to these are not held !! very bad + // code design here + private GameUI gameUI; + private Viewport viewport; + private GlyphLayout glyphLayout; + + public EditBoxFrame(final String name, final UIFrame parent, final float editBorderSize, + final Color editCursorColor) { + super(name, parent); + this.editBorderSize = editBorderSize; + this.editCursorColor = editCursorColor; + } + + public void setControlBackdrop(final UIFrame controlBackdrop) { + this.controlBackdrop = controlBackdrop; + } + + @Override + protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) { + this.gameUI = gameUI; + this.viewport = viewport; + this.controlBackdrop.positionBounds(gameUI, viewport); + this.editTextFrame.positionBounds(gameUI, viewport); + } + + @Override + protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { + this.glyphLayout = glyphLayout; + this.controlBackdrop.render(batch, baseFont, glyphLayout); + this.editTextFrame.render(batch, baseFont, glyphLayout); + if (this.focused) { + final long time = TimeUtils.millis(); + if ((time % 500) > 250) { + final BitmapFont frameFont = this.editTextFrame.getFrameFont(); + frameFont.setColor(this.editCursorColor); + final int cursorRenderPosition = Math.min(this.cursorIndex, this.editTextFrame.getText().length()); + this.cursorIndex = cursorRenderPosition; + glyphLayout.setText(frameFont, this.editTextFrame.getText().substring(0, cursorRenderPosition)); + final float cursorXOffset = glyphLayout.width; + glyphLayout.setText(frameFont, "|"); + frameFont.draw(batch, "|", + (this.editTextFrame.getFramePointX(FramePoint.LEFT) + cursorXOffset) - (glyphLayout.width / 2), + this.editTextFrame.getFramePointY(FramePoint.LEFT) + ((frameFont.getCapHeight()) / 2)); + } + } + } + + public void setEditTextFrame(final StringFrame editTextFrame) { + this.editTextFrame = editTextFrame; + } + + @Override + public boolean isFocusable() { + return true; + } + + @Override + public void onFocusGained() { + this.focused = true; + } + + @Override + public void onFocusLost() { + this.focused = false; + } + + @Override + public boolean keyDown(final int keycode) { + switch (keycode) { + case Input.Keys.LEFT: { + this.cursorIndex = Math.max(0, this.cursorIndex - 1); + break; + } + case Input.Keys.RIGHT: { + final String text = this.editTextFrame.getText(); + this.cursorIndex = Math.min(text.length(), this.cursorIndex + 1); + break; + } + case Input.Keys.BACKSPACE: { + final String prevText = this.editTextFrame.getText(); + final int prevTextLength = prevText.length(); + final int cursorIndex = Math.min(this.cursorIndex, prevTextLength); + if (cursorIndex >= 1) { + this.cursorIndex = cursorIndex - 1; + final String newText = prevText.substring(0, cursorIndex - 1) + + prevText.substring(cursorIndex, prevTextLength); + this.editTextFrame.setText(newText, this.gameUI, this.viewport); + } + break; + } + } + return false; + } + + @Override + public boolean keyUp(final int keycode) { + return false; + } + + @Override + public boolean keyTyped(final char character) { + if (Character.isAlphabetic(character) || Character.isDigit(character)) { + final String prevText = this.editTextFrame.getText(); + final int prevTextLength = prevText.length(); + final int cursorIndex = Math.min(this.cursorIndex, prevTextLength); + final String newText = prevText.substring(0, cursorIndex) + character + + prevText.substring(cursorIndex, prevTextLength); + this.editTextFrame.setText(newText, this.gameUI, this.viewport); + this.cursorIndex++; + } + return false; + } + + @Override + public UIFrame touchDown(final float screenX, final float screenY, final int button) { + if (isVisible() && this.renderBounds.contains(screenX, screenY)) { + + final String text = this.editTextFrame.getText(); + int indexFound = -1; + final float fpXOfEditText = this.editTextFrame.getFramePointX(FramePoint.LEFT); + float lastX = 0; + for (int i = 0; i < text.length(); i++) { + final BitmapFont frameFont = this.editTextFrame.getFrameFont(); + this.glyphLayout.setText(frameFont, this.editTextFrame.getText().substring(0, i)); + final float x = fpXOfEditText + this.glyphLayout.width; + if (((x + lastX) / 2) > screenX) { + indexFound = i - 1; + break; + } + lastX = x; + } + if (indexFound == -1) { + indexFound = text.length(); + } + this.cursorIndex = indexFound; + + return this; + } + return super.touchDown(screenX, screenY, button); + } + +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/ListBoxFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/ListBoxFrame.java new file mode 100644 index 0000000..50d250e --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/ListBoxFrame.java @@ -0,0 +1,110 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.GlyphLayout; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.parsers.fdf.GameUI; +import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; +import com.etheller.warsmash.parsers.fdf.datamodel.TextJustify; + +public class ListBoxFrame extends ControlFrame { + private final List listItems = new ArrayList<>(); + private final List stringFrames = new ArrayList<>(); + private BitmapFont frameFont; + private float listBoxBorder; + + public ListBoxFrame(final String name, final UIFrame parent, final Viewport viewport) { + super(name, parent); + this.listBoxBorder = GameUI.convertX(viewport, 0.01f); + } + + public void setListBoxBorder(final float listBoxBorder) { + this.listBoxBorder = listBoxBorder; + } + + public float getListBoxBorder() { + return this.listBoxBorder; + } + + public void setFrameFont(final BitmapFont frameFont) { + this.frameFont = frameFont; + } + + @Override + protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) { + super.innerPositionBounds(gameUI, viewport); + updateUI(gameUI, viewport); + } + + private void positionChildren(final GameUI gameUI, final Viewport viewport) { + for (final SingleStringFrame frame : this.stringFrames) { + frame.positionBounds(gameUI, viewport); + } + } + + @Override + protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { + super.internalRender(batch, baseFont, glyphLayout); + for (final SingleStringFrame frame : this.stringFrames) { + frame.render(batch, baseFont, glyphLayout); + } + } + + public void addItem(final String item, final GameUI gameUI, final Viewport viewport) { + this.listItems.add(item); +// updateUI(gameUI, viewport); + } + + public void setItems(final List items, final GameUI gameUI, final Viewport viewport) { + this.listItems.clear(); + this.listItems.addAll(items); +// updateUI(gameUI, viewport); + } + + public void removeItem(final String item, final GameUI gameUI, final Viewport viewport) { + this.listItems.remove(item); +// updateUI(gameUI, viewport); + } + + public void removeItem(final int index, final GameUI gameUI, final Viewport viewport) { + this.listItems.remove(index); +// updateUI(gameUI, viewport); + } + + private void updateUI(final GameUI gameUI, final Viewport viewport) { + this.stringFrames.clear(); + SingleStringFrame prev = null; + int i = 0; + for (final String string : this.listItems) { + final SingleStringFrame stringFrame = new SingleStringFrame("LISTY" + i++, this, Color.WHITE, + TextJustify.LEFT, TextJustify.MIDDLE, this.frameFont); + stringFrame.setText(string); + stringFrame.setWidth(this.renderBounds.width); + stringFrame.setHeight(this.frameFont.getLineHeight()); + if (prev != null) { + stringFrame.addSetPoint(new SetPoint(FramePoint.TOPLEFT, prev, FramePoint.BOTTOMLEFT, 0, 0)); + } + else { + stringFrame.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this, FramePoint.TOPLEFT, this.listBoxBorder, + -this.listBoxBorder)); + } + this.stringFrames.add(stringFrame); + prev = stringFrame; + } + positionChildren(gameUI, viewport); + } + + @Override + public UIFrame touchDown(final float screenX, final float screenY, final int button) { + if (isVisible() && this.renderBounds.contains(screenX, screenY)) { + + return this; + } + return super.touchDown(screenX, screenY, button); + } +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java index 24cef0f..72fe1ad 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java @@ -7,6 +7,8 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer; +import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; import com.badlogic.gdx.utils.viewport.Viewport; import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; @@ -14,6 +16,7 @@ import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; import com.etheller.warsmash.parsers.fdf.datamodel.TextJustify; public class StringFrame extends AbstractRenderableFrame { + private static final boolean DEBUG = false; private final List internalFrames = new ArrayList<>(); private Color color; private String text = "Default string"; @@ -27,6 +30,8 @@ public class StringFrame extends AbstractRenderableFrame { private final SimpleFrame internalFramesContainer; private float predictedViewportHeight; + static ShapeRenderer shapeRenderer = new ShapeRenderer(); + public StringFrame(final String name, final UIFrame parent, final Color color, final TextJustify justifyH, final TextJustify justifyV, final BitmapFont frameFont, final String text) { super(name, parent); @@ -38,6 +43,10 @@ public class StringFrame extends AbstractRenderableFrame { this.internalFramesContainer = new SimpleFrame(null, this); } + public String getText() { + return this.text; + } + public void setText(final String text, final GameUI gameUI, final Viewport viewport) { if (text == null) { throw new IllegalArgumentException(); @@ -77,11 +86,32 @@ public class StringFrame extends AbstractRenderableFrame { @Override protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { this.internalFramesContainer.render(batch, baseFont, glyphLayout); + + if (DEBUG) { + batch.end(); + shapeRenderer.setProjectionMatrix(batch.getProjectionMatrix()); + shapeRenderer.setColor(1f, 1f, 1f, 1f); + shapeRenderer.begin(ShapeType.Line); + shapeRenderer.rect(this.renderBounds.x, this.renderBounds.y, this.renderBounds.width, + this.renderBounds.height); + + shapeRenderer.end(); + + batch.begin(); + } + } + + @Override + public void positionBounds(final GameUI gameUI, final Viewport viewport) { + createInternalFrames(gameUI.getGlyphLayout()); + if (this.renderBounds.height == 0) { + this.renderBounds.height = getPredictedViewportHeight(); + } + super.positionBounds(gameUI, viewport); } @Override protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) { - createInternalFrames(gameUI.getGlyphLayout()); this.internalFramesContainer.positionBounds(gameUI, viewport); } @@ -436,4 +466,7 @@ public class StringFrame extends AbstractRenderableFrame { return this.predictedViewportHeight; } + public BitmapFont getFrameFont() { + return this.frameFont; + } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java index 003522a..3bb9f8c 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java @@ -31,8 +31,16 @@ public interface UIFrame { void setSetAllPoints(boolean setAllPoints); + void setSetAllPoints(boolean setAllPoints, float inset); + void setVisible(boolean visible); + UIFrame getParent(); + + boolean isVisible(); + + boolean isVisibleOnScreen(); + UIFrame touchDown(float screenX, float screenY, int button); UIFrame touchUp(float screenX, float screenY, int button); diff --git a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java index 5d1fe78..e08320d 100644 --- a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java +++ b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java @@ -35,6 +35,7 @@ import com.etheller.interpreter.ast.value.visitor.RealJassValueVisitor; import com.etheller.interpreter.ast.value.visitor.StringJassValueVisitor; import com.etheller.interpreter.ast.visitors.JassProgramVisitor; import com.etheller.warsmash.datasources.DataSource; +import com.etheller.warsmash.parsers.fdf.GameSkin; import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; @@ -227,11 +228,11 @@ public class Jass2 { public JassValue call(final List arguments, final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { final String skinArg = arguments.get(0).visit(StringJassValueVisitor.getInstance()); - final Element skin = GameUI.loadSkin(dataSource, skinArg); + final GameSkin skin = GameUI.loadSkin(dataSource, skinArg); final GameUI gameUI = new GameUI(dataSource, skin, uiViewport, uiScene, war3MapViewer, 0, war3MapViewer.getAllObjectData().getWts()); JUIEnvironment.this.gameUI = gameUI; - JUIEnvironment.this.skin = skin; + JUIEnvironment.this.skin = skin.getSkin(); rootFrameListener.onCreate(gameUI); return new HandleJassValue(frameHandleType, gameUI); } diff --git a/core/src/com/etheller/warsmash/util/WarsmashConstants.java b/core/src/com/etheller/warsmash/util/WarsmashConstants.java index 694d5b2..6b2e4ab 100644 --- a/core/src/com/etheller/warsmash/util/WarsmashConstants.java +++ b/core/src/com/etheller/warsmash/util/WarsmashConstants.java @@ -6,7 +6,7 @@ public class WarsmashConstants { * With version, we use 0 for RoC, 1 for TFT emulation, and probably 2+ or * whatever for custom mods and other stuff */ - public static int GAME_VERSION = 0; + public static int GAME_VERSION = 1; public static final int REPLACEABLE_TEXTURE_LIMIT = 64; public static final float SIMULATION_STEP_TIME = 1 / 20f; public static final int PORT_NUMBER = 6115; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java index 20020e4..eed8296 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUI.java @@ -715,9 +715,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.tooltipUberTipText = (StringFrame) this.rootFrame.getFrameByName("SmashUberTipText", 0); this.tooltipUberTipText.setWidth(GameUI.convertX(this.uiViewport, 0.274f)); this.uberTipNoResourcesSetPoint = new SetPoint(FramePoint.TOPLEFT, this.tooltipText, FramePoint.BOTTOMLEFT, 0, - GameUI.convertY(this.uiViewport, -0.014f)); + GameUI.convertY(this.uiViewport, -0.004f)); this.uberTipWithResourcesSetPoint = new SetPoint(FramePoint.TOPLEFT, this.tooltipText, FramePoint.BOTTOMLEFT, 0, - GameUI.convertY(this.uiViewport, -0.024f)); + GameUI.convertY(this.uiViewport, -0.014f)); this.tooltipUberTipText.addSetPoint(this.uberTipNoResourcesSetPoint); this.tooltipResourceFrames = new UIFrame[ResourceType.VALUES.length]; this.tooltipResourceIconFrames = new TextureFrame[ResourceType.VALUES.length]; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java index 58ad74f..b33da59 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java @@ -10,35 +10,43 @@ import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.viewport.ExtendViewport; -import com.etheller.warsmash.WarsmashGdxMapScreen; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.SingleModelScreen; import com.etheller.warsmash.WarsmashGdxMenuScreen; import com.etheller.warsmash.WarsmashGdxMultiScreenGame; import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; +import com.etheller.warsmash.parsers.fdf.frames.EditBoxFrame; import com.etheller.warsmash.parsers.fdf.frames.GlueButtonFrame; import com.etheller.warsmash.parsers.fdf.frames.GlueTextButtonFrame; +import com.etheller.warsmash.parsers.fdf.frames.ListBoxFrame; import com.etheller.warsmash.parsers.fdf.frames.SetPoint; +import com.etheller.warsmash.parsers.fdf.frames.SimpleFrame; import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame; import com.etheller.warsmash.parsers.fdf.frames.StringFrame; import com.etheller.warsmash.parsers.fdf.frames.UIFrame; import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener; import com.etheller.warsmash.units.DataTable; +import com.etheller.warsmash.units.Element; import com.etheller.warsmash.units.custom.WTS; +import com.etheller.warsmash.util.StringBundle; import com.etheller.warsmash.util.WarsmashConstants; import com.etheller.warsmash.util.WorldEditStrings; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.handlers.mdx.MdxViewer; import com.etheller.warsmash.viewer5.handlers.w3x.UnitSound; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.ClickableFrame; +import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.FocusableFrame; import com.etheller.warsmash.viewer5.handlers.w3x.ui.sound.KeyedSounds; public class MenuUI { private static final Vector2 screenCoordsVector = new Vector2(); + private static boolean ENABLE_NOT_YET_IMPLEMENTED_BUTTONS = false; private final DataSource dataSource; private final Scene uiScene; - private final ExtendViewport uiViewport; + private final Viewport uiViewport; private final MdxViewer viewer; private final RootFrameListener rootFrameListener; private final float widthRatioCorrection; @@ -48,6 +56,7 @@ public class MenuUI { private ClickableFrame mouseDownUIFrame; private ClickableFrame mouseOverUIFrame; + private FocusableFrame focusUIFrame; private UIFrame mainMenuFrame; @@ -74,8 +83,12 @@ public class MenuUI { private MenuState menuState; private UIFrame singlePlayerMenu; + private UIFrame singlePlayerMainPanel; + + private UIFrame skirmish; private UIFrame profilePanel; + private EditBoxFrame newProfileEditBox; private GlueButtonFrame profileButton; private GlueTextButtonFrame campaignButton; @@ -83,28 +96,61 @@ public class MenuUI { private GlueTextButtonFrame viewReplayButton; private GlueTextButtonFrame customCampaignButton; private GlueTextButtonFrame skirmishButton; - private GlueTextButtonFrame cancelButton; + private GlueTextButtonFrame singlePlayerCancelButton; private GlueButtonFrame editionButton; + private GlueTextButtonFrame skirmishCancelButton; + private final WarsmashGdxMultiScreenGame screenManager; private final DataTable warsmashIni; private UnitSound glueScreenLoop; - public MenuUI(final DataSource dataSource, final ExtendViewport uiViewport, final Scene uiScene, - final MdxViewer viewer, final WarsmashGdxMultiScreenGame screenManager, final DataTable warsmashIni, - final RootFrameListener rootFrameListener) { + private SpriteFrame warcraftIIILogo; + // Campaign + private UIFrame campaignMenu; + private SpriteFrame campaignFade; + private GlueTextButtonFrame campaignBackButton; + private UIFrame missionSelectFrame; + private UIFrame campaignSelectFrame; + private final DataTable campaignStrings; + private SpriteFrame campaignWarcraftIIILogo; + private final SingleModelScreen menuScreen; + + private String currentCampaignBackgroundModel; + private String currentCampaignAmbientSound; + private int currentCampaignCursor; + private String[] campaignList; + private Element[] campaignDatas; + private UnitSound mainMenuGlueScreenLoop; + private GlueTextButtonFrame addProfileButton; + private GlueTextButtonFrame deleteProfileButton; + private GlueTextButtonFrame selectProfileButton; + + public MenuUI(final DataSource dataSource, final Viewport uiViewport, final Scene uiScene, final MdxViewer viewer, + final WarsmashGdxMultiScreenGame screenManager, final SingleModelScreen menuScreen, + final DataTable warsmashIni, final RootFrameListener rootFrameListener) { this.dataSource = dataSource; this.uiViewport = uiViewport; this.uiScene = uiScene; this.viewer = viewer; this.screenManager = screenManager; + this.menuScreen = menuScreen; this.warsmashIni = warsmashIni; this.rootFrameListener = rootFrameListener; - this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f; - this.heightRatioCorrection = this.uiViewport.getMinWorldHeight() / 1200f; + this.widthRatioCorrection = getMinWorldWidth() / 1600f; + this.heightRatioCorrection = getMinWorldHeight() / 1200f; + + this.campaignStrings = new DataTable(StringBundle.EMPTY); + try (InputStream campaignStringStream = dataSource.getResourceAsStream( + "UI\\CampaignStrings" + (WarsmashConstants.GAME_VERSION == 1 ? "_exp" : "") + ".txt")) { + this.campaignStrings.readTXT(campaignStringStream, true); + } + catch (final IOException e) { + throw new RuntimeException(e); + } } public float getHeightRatioCorrection() { @@ -138,13 +184,13 @@ public class MenuUI { // Create main menu this.mainMenuFrame = this.rootFrame.createFrame("MainMenuFrame", this.rootFrame, 0, 0); - this.mainMenuFrame.setVisible(false); - final SpriteFrame warcraftIIILogo = (SpriteFrame) this.rootFrame.getFrameByName("WarCraftIIILogo", 0); - this.rootFrame.setSpriteFrameModel(warcraftIIILogo, + this.warcraftIIILogo = (SpriteFrame) this.rootFrame.getFrameByName("WarCraftIIILogo", 0); + this.rootFrame.setSpriteFrameModel(this.warcraftIIILogo, this.rootFrame.getSkinField("MainMenuLogo_V" + WarsmashConstants.GAME_VERSION)); - warcraftIIILogo.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.mainMenuFrame, FramePoint.TOPLEFT, + this.warcraftIIILogo.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.mainMenuFrame, FramePoint.TOPLEFT, GameUI.convertX(this.uiViewport, 0.13f), GameUI.convertY(this.uiViewport, -0.08f))); + setMainMenuVisible(false); this.rootFrame.getFrameByName("RealmSelect", 0).setVisible(false); this.glueSpriteLayerTopRight = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE", @@ -170,17 +216,7 @@ public class MenuUI { this.cursorFrame.setZDepth(-1.0f); Gdx.input.setCursorCatched(true); - // Create single player - this.singlePlayerMenu = this.rootFrame.createFrame("SinglePlayerMenu", this.rootFrame, 0, 0); - this.singlePlayerMenu.setVisible(false); - - this.profilePanel = this.rootFrame.getFrameByName("ProfilePanel", 0); - this.profilePanel.setVisible(false); - - // position all - this.rootFrame.positionBounds(this.rootFrame, this.uiViewport); - - // Main Menu + // Main Menu interactivity this.singlePlayerButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("SinglePlayerButton", 0); this.battleNetButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("BattleNetButton", 0); this.realmButton = (GlueButtonFrame) this.rootFrame.getFrameByName("RealmButton", 0); @@ -197,7 +233,7 @@ public class MenuUI { WarsmashConstants.GAME_VERSION = (WarsmashConstants.GAME_VERSION == 1 ? 0 : 1); MenuUI.this.glueSpriteLayerTopLeft.setSequence("MainMenu Death"); MenuUI.this.glueSpriteLayerTopRight.setSequence("MainMenu Death"); - MenuUI.this.mainMenuFrame.setVisible(false); + setMainMenuVisible(false); MenuUI.this.menuState = MenuState.RESTARTING; } }); @@ -214,7 +250,7 @@ public class MenuUI { public void run() { MenuUI.this.glueSpriteLayerTopLeft.setSequence("MainMenu Death"); MenuUI.this.glueSpriteLayerTopRight.setSequence("MainMenu Death"); - MenuUI.this.mainMenuFrame.setVisible(false); + setMainMenuVisible(false); MenuUI.this.menuState = MenuState.QUITTING; } }); @@ -224,12 +260,37 @@ public class MenuUI { public void run() { MenuUI.this.glueSpriteLayerTopLeft.setSequence("MainMenu Death"); MenuUI.this.glueSpriteLayerTopRight.setSequence("MainMenu Death"); - MenuUI.this.mainMenuFrame.setVisible(false); + setMainMenuVisible(false); MenuUI.this.menuState = MenuState.GOING_TO_SINGLE_PLAYER; } }); - // Single Player + // Create single player + this.singlePlayerMenu = this.rootFrame.createFrame("SinglePlayerMenu", this.rootFrame, 0, 0); + this.singlePlayerMenu.setVisible(false); + + this.profilePanel = this.rootFrame.getFrameByName("ProfilePanel", 0); + this.profilePanel.setVisible(false); + + this.newProfileEditBox = (EditBoxFrame) this.rootFrame.getFrameByName("NewProfileEditBox", 0); + final StringFrame profileListText = (StringFrame) this.rootFrame.getFrameByName("ProfileListText", 0); + final SimpleFrame profileListContainer = (SimpleFrame) this.rootFrame.getFrameByName("ProfileListContainer", 0); + final ListBoxFrame profileListBox = (ListBoxFrame) this.rootFrame.createFrameByType("LISTBOX", "ListBoxWar3", + profileListContainer, "WITHCHILDREN", 0); + profileListBox.setSetAllPoints(true); + profileListBox.setFrameFont(profileListText.getFrameFont()); + profileListBox.addItem("Test1", this.rootFrame, this.uiViewport); + profileListBox.addItem("Test2", this.rootFrame, this.uiViewport); + profileListBox.addItem("Test3", this.rootFrame, this.uiViewport); + profileListContainer.add(profileListBox); + + this.addProfileButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("AddProfileButton", 0); + this.deleteProfileButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("DeleteProfileButton", 0); + this.selectProfileButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("SelectProfileButton", 0); + + this.singlePlayerMainPanel = this.rootFrame.getFrameByName("MainPanel", 0); + + // Single Player Interactivity this.profileButton = (GlueButtonFrame) this.rootFrame.getFrameByName("ProfileButton", 0); this.campaignButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("CampaignButton", 0); this.loadSavedButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("LoadSavedButton", 0); @@ -237,16 +298,21 @@ public class MenuUI { this.customCampaignButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("CustomCampaignButton", 0); this.skirmishButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("SkirmishButton", 0); - this.cancelButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("CancelButton", 0); + this.singlePlayerCancelButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("CancelButton", 0); final StringFrame profileNameText = (StringFrame) this.rootFrame.getFrameByName("ProfileNameText", 0); this.rootFrame.setText(profileNameText, "WorldEdit"); - this.profileButton.setEnabled(false); - this.loadSavedButton.setEnabled(false); - this.viewReplayButton.setEnabled(false); - this.customCampaignButton.setEnabled(false); - this.skirmishButton.setEnabled(false); + setSinglePlayerButtonsEnabled(true); + + this.profileButton.setOnClick(new Runnable() { + @Override + public void run() { + MenuUI.this.glueSpriteLayerTopLeft.setSequence("RealmSelection Birth"); + setSinglePlayerButtonsEnabled(true); + MenuUI.this.menuState = MenuState.SINGLE_PLAYER_PROFILE; + } + }); this.campaignButton.setOnClick(new Runnable() { @Override @@ -254,30 +320,125 @@ public class MenuUI { MenuUI.this.glueSpriteLayerTopLeft.setSequence("SinglePlayer Death"); MenuUI.this.glueSpriteLayerTopRight.setSequence("SinglePlayer Death"); MenuUI.this.singlePlayerMenu.setVisible(false); + MenuUI.this.profilePanel.setVisible(false); MenuUI.this.menuState = MenuState.GOING_TO_CAMPAIGN; } }); - this.cancelButton.setOnClick(new Runnable() { + this.skirmishButton.setOnClick(new Runnable() { @Override public void run() { MenuUI.this.glueSpriteLayerTopLeft.setSequence("SinglePlayer Death"); MenuUI.this.glueSpriteLayerTopRight.setSequence("SinglePlayer Death"); MenuUI.this.singlePlayerMenu.setVisible(false); + MenuUI.this.profilePanel.setVisible(false); + MenuUI.this.menuState = MenuState.GOING_TO_SINGLE_PLAYER_SKIRMISH; + } + }); + + this.singlePlayerCancelButton.setOnClick(new Runnable() { + @Override + public void run() { + if (MenuUI.this.menuState == MenuState.SINGLE_PLAYER_PROFILE) { + MenuUI.this.glueSpriteLayerTopLeft.setSequence("RealmSelection Death"); + MenuUI.this.profilePanel.setVisible(false); + } + else { + MenuUI.this.glueSpriteLayerTopLeft.setSequence("SinglePlayer Death"); + } + MenuUI.this.glueSpriteLayerTopRight.setSequence("SinglePlayer Death"); + MenuUI.this.singlePlayerMenu.setVisible(false); MenuUI.this.menuState = MenuState.GOING_TO_MAIN_MENU; + } + }); + + // Create skirmish UI + this.skirmish = this.rootFrame.createFrame("Skirmish", this.rootFrame, 0, 0); + this.skirmish.setVisible(false); + + this.skirmishCancelButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("CancelButton", 0); + this.skirmishCancelButton.setOnClick(new Runnable() { + @Override + public void run() { + MenuUI.this.glueSpriteLayerTopLeft.setSequence("SinglePlayerSkirmish Death"); + MenuUI.this.glueSpriteLayerTopRight.setSequence("SinglePlayerSkirmish Death"); + MenuUI.this.skirmish.setVisible(false); + MenuUI.this.menuState = MenuState.GOING_TO_SINGLE_PLAYER; } }); - this.menuState = MenuState.MAIN_MENU; + // Create Campaign UI + + this.campaignMenu = this.rootFrame.createFrame("CampaignMenu", this.rootFrame, 0, 0); + this.campaignMenu.setVisible(false); + this.campaignFade = (SpriteFrame) this.rootFrame.getFrameByName("SlidingDoors", 0); + this.campaignFade.setVisible(false); + this.campaignBackButton = (GlueTextButtonFrame) this.rootFrame.getFrameByName("BackButton", 0); + this.campaignBackButton.setVisible(false); + this.missionSelectFrame = this.rootFrame.getFrameByName("MissionSelectFrame", 0); + this.missionSelectFrame.setVisible(false); + this.campaignSelectFrame = this.rootFrame.getFrameByName("CampaignSelectFrame", 0); + this.campaignSelectFrame.setVisible(false); + + this.campaignWarcraftIIILogo = (SpriteFrame) this.rootFrame.getFrameByName("WarCraftIIILogo", 0); + this.rootFrame.setSpriteFrameModel(this.campaignWarcraftIIILogo, + this.rootFrame.getSkinField("MainMenuLogo_V" + WarsmashConstants.GAME_VERSION)); + this.campaignWarcraftIIILogo.setVisible(false); + + this.campaignBackButton.setOnClick(new Runnable() { + @Override + public void run() { + MenuUI.this.campaignMenu.setVisible(false); + MenuUI.this.campaignBackButton.setVisible(false); + MenuUI.this.missionSelectFrame.setVisible(false); + MenuUI.this.campaignSelectFrame.setVisible(false); + MenuUI.this.campaignFade.setSequence("Birth"); + MenuUI.this.menuState = MenuState.LEAVING_CAMPAIGN; + } + }); + final Element campaignIndex = this.campaignStrings.get("Index"); + this.campaignList = campaignIndex.getField("CampaignList").split(","); + this.campaignDatas = new Element[this.campaignList.length]; + for (int i = 0; i < this.campaignList.length; i++) { + final String campaign = this.campaignList[i]; + this.campaignDatas[i] = this.campaignStrings.get(campaign); + if ((this.campaignDatas[i] != null) && (this.currentCampaignBackgroundModel == null)) { + this.currentCampaignBackgroundModel = this.rootFrame.getSkinField( + this.campaignDatas[i].getField("Background") + "_V" + WarsmashConstants.GAME_VERSION); + this.currentCampaignAmbientSound = this.rootFrame + .trySkinField(this.campaignDatas[i].getField("AmbientSound")); + this.currentCampaignCursor = this.campaignDatas[i].getFieldValue("Cursor"); + } + } + + // position all + this.rootFrame.positionBounds(this.rootFrame, this.uiViewport); + + this.menuState = MenuState.GOING_TO_MAIN_MENU; loadSounds(); final String glueLoopField = this.rootFrame.getSkinField("GlueScreenLoop_V" + WarsmashConstants.GAME_VERSION); - this.glueScreenLoop = this.uiSounds.getSound(glueLoopField); + this.mainMenuGlueScreenLoop = this.uiSounds.getSound(glueLoopField); + this.glueScreenLoop = this.mainMenuGlueScreenLoop; this.glueScreenLoop.play(this.uiScene.audioContext, 0f, 0f, 0f); } + protected void setSinglePlayerButtonsEnabled(final boolean b) { + this.profileButton.setEnabled(b); + this.campaignButton.setEnabled(b); + this.loadSavedButton.setEnabled(b && ENABLE_NOT_YET_IMPLEMENTED_BUTTONS); + this.viewReplayButton.setEnabled(b && ENABLE_NOT_YET_IMPLEMENTED_BUTTONS); + this.customCampaignButton.setEnabled(b && ENABLE_NOT_YET_IMPLEMENTED_BUTTONS); + this.skirmishButton.setEnabled(b); + } + + private void setMainMenuVisible(final boolean visible) { + this.mainMenuFrame.setVisible(visible); + this.warcraftIIILogo.setVisible(visible); + } + public void resize() { } @@ -288,12 +449,28 @@ public class MenuUI { font.setColor(Color.YELLOW); final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond(); glyphLayout.setText(font, fpsString); - font.draw(batch, fpsString, (this.uiViewport.getMinWorldWidth() - glyphLayout.width) / 2, - 1100 * this.heightRatioCorrection); + font.draw(batch, fpsString, (getMinWorldWidth() - glyphLayout.width) / 2, 1100 * this.heightRatioCorrection); this.rootFrame.render(batch, font20, glyphLayout); } + private float getMinWorldWidth() { + if (this.uiViewport instanceof ExtendViewport) { + return ((ExtendViewport) this.uiViewport).getMinWorldWidth(); + } + return this.uiViewport.getWorldWidth(); + } + + private float getMinWorldHeight() { + if (this.uiViewport instanceof ExtendViewport) { + return ((ExtendViewport) this.uiViewport).getMinWorldHeight(); + } + return this.uiViewport.getWorldHeight(); + } + public void update(final float deltaTime) { + if ((this.focusUIFrame != null) && !this.focusUIFrame.isVisibleOnScreen()) { + setFocusFrame(getNextFocusFrame()); + } final int baseMouseX = Gdx.input.getX(); int mouseX = baseMouseX; @@ -316,7 +493,8 @@ public class MenuUI { this.cursorFrame.setFramePointY(FramePoint.BOTTOM, screenCoordsVector.y); this.cursorFrame.setSequence("Normal"); - if (this.glueSpriteLayerTopRight.isSequenceEnded()) { + if (this.glueSpriteLayerTopRight.isSequenceEnded() && this.glueSpriteLayerTopLeft.isSequenceEnded() + && (!this.campaignFade.isVisible() || this.campaignFade.isSequenceEnded())) { switch (this.menuState) { case GOING_TO_MAIN_MENU: this.glueSpriteLayerTopLeft.setSequence("MainMenu Birth"); @@ -324,7 +502,7 @@ public class MenuUI { this.menuState = MenuState.MAIN_MENU; break; case MAIN_MENU: - this.mainMenuFrame.setVisible(true); + setMainMenuVisible(true); this.glueSpriteLayerTopLeft.setSequence("MainMenu Stand"); this.glueSpriteLayerTopRight.setSequence("MainMenu Stand"); break; @@ -333,14 +511,82 @@ public class MenuUI { this.glueSpriteLayerTopRight.setSequence("SinglePlayer Birth"); this.menuState = MenuState.SINGLE_PLAYER; break; + case LEAVING_CAMPAIGN: + this.glueSpriteLayerTopLeft.setSequence("Birth"); + this.glueSpriteLayerTopRight.setSequence("Birth"); + if (this.campaignFade.isVisible()) { + this.campaignFade.setSequence("Death"); + } + this.glueScreenLoop.stop(); + this.glueScreenLoop = this.mainMenuGlueScreenLoop; + this.glueScreenLoop.play(this.uiScene.audioContext, 0f, 0f, 0f); + this.menuScreen.setModel( + this.rootFrame.getSkinField("GlueSpriteLayerBackground_V" + WarsmashConstants.GAME_VERSION)); + this.rootFrame.setSpriteFrameModel(this.cursorFrame, this.rootFrame.getSkinField("Cursor")); + this.menuState = MenuState.GOING_TO_SINGLE_PLAYER; + break; case SINGLE_PLAYER: this.singlePlayerMenu.setVisible(true); + this.campaignFade.setVisible(false); + setSinglePlayerButtonsEnabled(true); this.glueSpriteLayerTopLeft.setSequence("SinglePlayer Stand"); this.glueSpriteLayerTopRight.setSequence("SinglePlayer Stand"); break; + case GOING_TO_SINGLE_PLAYER_SKIRMISH: + this.glueSpriteLayerTopLeft.setSequence("SinglePlayerSkirmish Birth"); + this.glueSpriteLayerTopRight.setSequence("SinglePlayerSkirmish Birth"); + this.menuState = MenuState.SINGLE_PLAYER_SKIRMISH; + break; + case SINGLE_PLAYER_SKIRMISH: + this.skirmish.setVisible(true); + this.glueSpriteLayerTopLeft.setSequence("SinglePlayerSkirmish Stand"); + this.glueSpriteLayerTopRight.setSequence("SinglePlayerSkirmish Stand"); + break; case GOING_TO_CAMPAIGN: - MenuUI.this.screenManager.setScreen(new WarsmashGdxMapScreen(MenuUI.this.warsmashIni, - this.warsmashIni.get("Map").getField("FilePath"))); + this.glueSpriteLayerTopLeft.setSequence("Death"); + this.glueSpriteLayerTopRight.setSequence("Death"); + this.campaignMenu.setVisible(true); + this.campaignFade.setVisible(true); + this.campaignFade.setSequence("Birth"); + this.menuState = MenuState.GOING_TO_CAMPAIGN_PART2; + break; + case GOING_TO_CAMPAIGN_PART2: + this.menuScreen.setModel(this.currentCampaignBackgroundModel); + this.glueScreenLoop.stop(); + this.glueScreenLoop = this.uiSounds.getSound(this.currentCampaignAmbientSound); + this.glueScreenLoop.play(this.uiScene.audioContext, 0f, 0f, 0f); + final DataTable skinData = this.rootFrame.getSkinData(); + final Element skinDataMain = skinData.get("Main"); + int currentCampaignCursor = this.currentCampaignCursor; + if (currentCampaignCursor == 3) { + currentCampaignCursor = 2; + } + else if (currentCampaignCursor == 2) { + currentCampaignCursor = 3; + } + final String cursorSkin = skinDataMain.getField("Skins", currentCampaignCursor); + this.rootFrame.setSpriteFrameModel(this.cursorFrame, skinData.get(cursorSkin).getField("Cursor")); + + this.campaignFade.setSequence("Death"); + this.menuState = MenuState.CAMPAIGN; + break; + case CAMPAIGN: + this.campaignBackButton.setVisible(true); + this.campaignWarcraftIIILogo.setVisible(true); + this.campaignSelectFrame.setVisible(true); + break; + case GOING_TO_SINGLE_PLAYER_PROFILE: + this.glueSpriteLayerTopLeft.setSequence("RealmSelection Birth"); + this.menuState = MenuState.SINGLE_PLAYER_PROFILE; + break; + case SINGLE_PLAYER_PROFILE: + this.profilePanel.setVisible(true); + this.glueSpriteLayerTopLeft.setSequence("RealmSelection Stand"); + // TODO the below should probably be some generic focusing thing when we enter a + // new view? + if ((this.newProfileEditBox != null) && this.newProfileEditBox.isFocusable()) { + setFocusFrame(this.newProfileEditBox); + } break; case QUITTING: Gdx.app.exit(); @@ -356,6 +602,10 @@ public class MenuUI { } + private FocusableFrame getNextFocusFrame() { + return this.rootFrame.getNextFocusFrame(); + } + public boolean touchDown(final int screenX, final int screenY, final float worldScreenY, final int button) { screenCoordsVector.set(screenX, screenY); this.uiViewport.unproject(screenCoordsVector); @@ -365,10 +615,26 @@ public class MenuUI { this.mouseDownUIFrame = (ClickableFrame) clickedUIFrame; this.mouseDownUIFrame.mouseDown(this.rootFrame, this.uiViewport); } + if (clickedUIFrame instanceof FocusableFrame) { + final FocusableFrame clickedFocusableFrame = (FocusableFrame) clickedUIFrame; + if (clickedFocusableFrame.isFocusable()) { + setFocusFrame(clickedFocusableFrame); + } + } } return false; } + private void setFocusFrame(final FocusableFrame clickedFocusableFrame) { + if (this.focusUIFrame != null) { + this.focusUIFrame.onFocusLost(); + } + this.focusUIFrame = clickedFocusableFrame; + if (this.focusUIFrame != null) { + this.focusUIFrame.onFocusGained(); + } + } + public boolean touchUp(final int screenX, final int screenY, final float worldScreenY, final int button) { screenCoordsVector.set(screenX, screenY); this.uiViewport.unproject(screenCoordsVector); @@ -437,9 +703,15 @@ public class MenuUI { GOING_TO_MAIN_MENU, MAIN_MENU, GOING_TO_SINGLE_PLAYER, + LEAVING_CAMPAIGN, SINGLE_PLAYER, + GOING_TO_SINGLE_PLAYER_SKIRMISH, + SINGLE_PLAYER_SKIRMISH, GOING_TO_CAMPAIGN, + GOING_TO_CAMPAIGN_PART2, CAMPAIGN, + GOING_TO_SINGLE_PLAYER_PROFILE, + SINGLE_PLAYER_PROFILE, QUITTING, RESTARTING; } @@ -453,4 +725,25 @@ public class MenuUI { this.rootFrame.dispose(); } } + + public boolean keyDown(final int keycode) { + if (this.focusUIFrame != null) { + this.focusUIFrame.keyDown(keycode); + } + return false; + } + + public boolean keyUp(final int keycode) { + if (this.focusUIFrame != null) { + this.focusUIFrame.keyUp(keycode); + } + return false; + } + + public boolean keyTyped(final char character) { + if (this.focusUIFrame != null) { + this.focusUIFrame.keyTyped(character); + } + return false; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/FocusableFrame.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/FocusableFrame.java new file mode 100644 index 0000000..0300fc3 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/FocusableFrame.java @@ -0,0 +1,17 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.ui.command; + +import com.etheller.warsmash.parsers.fdf.frames.UIFrame; + +public interface FocusableFrame extends UIFrame { + boolean isFocusable(); + + void onFocusGained(); + + void onFocusLost(); + + boolean keyDown(int keycode); + + boolean keyUp(int keycode); + + boolean keyTyped(char character); +} diff --git a/core/src/com/hiveworkshop/rms/parsers/mdlx/AnimationMap.java b/core/src/com/hiveworkshop/rms/parsers/mdlx/AnimationMap.java index 0fa82d4..9e88301 100644 --- a/core/src/com/hiveworkshop/rms/parsers/mdlx/AnimationMap.java +++ b/core/src/com/hiveworkshop/rms/parsers/mdlx/AnimationMap.java @@ -219,7 +219,7 @@ public enum AnimationMap { /** * Camera source rotation */ - KCRL(MdlUtils.TOKEN_ROTATION, MdlxTimelineDescriptor.UINT32_TIMELINE), + KCRL(MdlUtils.TOKEN_ROTATION, MdlxTimelineDescriptor.FLOAT_TIMELINE), // GenericObject /** * Generic object translation