From e919f359049a5103375b7e8fb0e1b57737f5dd2d Mon Sep 17 00:00:00 2001 From: Retera Date: Tue, 20 Oct 2020 08:39:35 -0400 Subject: [PATCH] Update with fixes for ui --- core/assets/warsmash.ini | 8 +- .../etheller/warsmash/WarsmashGdxMapGame.java | 528 +++++------------- .../etheller/warsmash/parsers/fdf/GameUI.java | 63 ++- .../fdf/frames/AbstractRenderableFrame.java | 23 +- .../parsers/fdf/frames/AbstractUIFrame.java | 11 + .../parsers/fdf/frames/SpriteFrame.java | 61 +- .../warsmash/parsers/fdf/frames/UIFrame.java | 4 + .../warsmash/viewer5/ModelInstance.java | 2 + .../com/etheller/warsmash/viewer5/Scene.java | 3 +- .../etheller/warsmash/viewer5/Shaders.java | 7 +- .../viewer5/handlers/mdx/AnimatedObject.java | 2 +- .../viewer5/handlers/mdx/BatchGroup.java | 3 +- .../viewer5/handlers/mdx/GenericObject.java | 2 +- .../viewer5/handlers/mdx/LightInstance.java | 14 +- .../handlers/mdx/MdxComplexInstance.java | 7 + .../viewer5/handlers/mdx/MdxShaders.java | 8 +- .../handlers/mdx/MdxSimpleInstance.java | 4 + .../viewer5/handlers/mdx/Particle2.java | 7 +- .../handlers/mdx/TextureAnimation.java | 2 +- .../viewer5/handlers/w3x/AnimationTokens.java | 2 +- ...tructable.java => RenderDestructable.java} | 4 +- .../w3x/{Doodad.java => RenderDoodad.java} | 4 +- .../viewer5/handlers/w3x/SequenceUtils.java | 4 + .../w3x/W3xScenePortraitLightManager.java | 40 +- .../viewer5/handlers/w3x/W3xShaders.java | 10 +- .../viewer5/handlers/w3x/War3MapViewer.java | 111 ++-- .../handlers/w3x/camera/CameraManager.java | 62 ++ .../w3x/camera/CameraPanControls.java | 10 + .../handlers/w3x/camera/CameraPreset.java | 85 +++ .../handlers/w3x/camera/CameraRates.java | 20 + .../w3x/camera/GameCameraManager.java | 154 +++++ .../w3x/camera/PortraitCameraManager.java | 52 ++ .../handlers/w3x/environment/Terrain.java | 14 +- .../w3x/environment/TerrainShaders.java | 14 +- .../w3x/rendersim/CommandCardIcon.java | 33 -- .../handlers/w3x/rendersim/RenderUnit.java | 93 ++- .../w3x/rendersim/RenderUnitTypeData.java | 25 + .../w3x/rendersim/ability/AbilityDataUI.java | 112 ++++ .../w3x/rendersim/ability/AbilityIconUI.java | 25 + .../w3x/rendersim/ability/IconUI.java | 34 ++ .../commandbuttons/AbilityCommandButton.java | 94 ++++ .../commandbuttons/BasicCommandButton.java | 95 ++++ .../commandbuttons/CommandButton.java | 38 ++ .../commandbuttons/CommandButtonListener.java | 38 ++ .../CommandCardPopulatingAbilityVisitor.java | 51 ++ .../handlers/w3x/simulation/CSimulation.java | 25 +- .../handlers/w3x/simulation/CUnit.java | 9 +- .../w3x/simulation/abilities/CAbility.java | 8 +- .../simulation/abilities/CAbilityAttack.java | 58 +- .../abilities/CAbilityHoldPosition.java | 69 --- .../simulation/abilities/CAbilityMove.java | 80 ++- .../simulation/abilities/CAbilityPatrol.java | 70 --- .../simulation/abilities/CAbilityStop.java | 69 --- .../abilities/CAbilityToggleableView.java | 5 + .../simulation/abilities/CAbilityView.java | 13 +- .../simulation/abilities/CAbilityVisitor.java | 15 + .../w3x/simulation/data/CUnitData.java | 22 +- .../w3x/simulation/orders/CAttackOrder.java | 11 +- .../w3x/simulation/orders/CMoveOrder.java | 10 +- .../w3x/simulation/orders/CPatrolOrder.java | 58 ++ .../w3x/simulation/orders/OrderIds.java | 457 +++++++++++++++ .../util/AbilityTargetCheckReceiver.java | 2 + .../CWidgetAbilityTargetCheckReceiver.java | 5 + .../util/PointAbilityTargetCheckReceiver.java | 6 +- .../handlers/w3x/ui/CommandCardIcon.java | 88 +++ .../viewer5/handlers/w3x/ui/MeleeUI.java | 270 +++++++-- .../handlers/w3x/ui/MeleeUIMinimap.java | 62 ++ 67 files changed, 2516 insertions(+), 879 deletions(-) rename core/src/com/etheller/warsmash/viewer5/handlers/w3x/{Destructable.java => RenderDestructable.java} (82%) rename core/src/com/etheller/warsmash/viewer5/handlers/w3x/{Doodad.java => RenderDoodad.java} (97%) create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraManager.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPanControls.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPreset.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraRates.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/PortraitCameraManager.java delete mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/CommandCardIcon.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityIconUI.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/IconUI.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/AbilityCommandButton.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/BasicCommandButton.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButton.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButtonListener.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java delete mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityHoldPosition.java delete mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityPatrol.java delete mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityStop.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityToggleableView.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CPatrolOrder.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/OrderIds.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/CommandCardIcon.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java diff --git a/core/assets/warsmash.ini b/core/assets/warsmash.ini index e2a1669..ec2d2f3 100644 --- a/core/assets/warsmash.ini +++ b/core/assets/warsmash.ini @@ -24,5 +24,9 @@ Path06="." //FilePath="FireLord.w3x" //FilePath="Maps\Campaign\NightElf03.w3m" //FilePath="PhoenixAttack.w3x" -FilePath="LightEnvironmentTest.w3x" -//FilePath="TorchLight2.w3x" \ No newline at end of file +//FilePath="LightEnvironmentTest.w3x" +//FilePath="TorchLight2.w3x" +//FilePath="OrcAssault.w3x" +//FilePath="FrostyVsFarm.w3m" +//FilePath="ModelTest.w3x" +FilePath="SpinningSample.w3x" \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java index 41bef64..71542ff 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java @@ -1,15 +1,13 @@ package com.etheller.warsmash; import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; -import javax.imageio.ImageIO; - import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; @@ -26,8 +24,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; import com.badlogic.gdx.graphics.glutils.ShapeRenderer; -import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType; -import com.badlogic.gdx.math.Quaternion; +import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; @@ -43,27 +40,29 @@ import com.etheller.warsmash.units.DataTable; import com.etheller.warsmash.units.Element; import com.etheller.warsmash.util.DataSourceFileHandle; import com.etheller.warsmash.util.ImageUtils; -import com.etheller.warsmash.util.WarsmashConstants; -import com.etheller.warsmash.viewer5.Camera; import com.etheller.warsmash.viewer5.CanvasProvider; +import com.etheller.warsmash.viewer5.Model; +import com.etheller.warsmash.viewer5.ModelInstance; +import com.etheller.warsmash.viewer5.ModelViewer; +import com.etheller.warsmash.viewer5.PathSolver; +import com.etheller.warsmash.viewer5.RenderBatch; import com.etheller.warsmash.viewer5.Scene; -import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; +import com.etheller.warsmash.viewer5.TextureMapper; +import com.etheller.warsmash.viewer5.handlers.ModelHandler; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; -import com.etheller.warsmash.viewer5.handlers.mdx.ReplaceableIds; -import com.etheller.warsmash.viewer5.handlers.tga.TgaFile; import com.etheller.warsmash.viewer5.handlers.w3x.UnitSound; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; +import com.etheller.warsmash.viewer5.handlers.w3x.camera.CameraPreset; +import com.etheller.warsmash.viewer5.handlers.w3x.camera.CameraRates; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI; public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor { - private static final double HORIZONTAL_ANGLE_INCREMENT = Math.PI / 60; - + private static final boolean ENABLE_MUSIC = false; private static final Vector3 clickLocationTemp = new Vector3(); private static final Vector2 clickLocationTemp2 = new Vector2(); private DataSource codebase; private War3MapViewer viewer; - private GameCameraManager cameraManager; private final Rectangle tempRect = new Rectangle(); // libGDX stuff @@ -74,22 +73,18 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv private ExtendViewport uiViewport; private GlyphLayout glyphLayout; - private Texture consoleUITexture; private int selectedSoundCount = 0; - private Rectangle minimap; - private Rectangle minimapFilledArea; - - private final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS]; private Texture solidGreenTexture; private ShapeRenderer shapeRenderer; - private boolean showTalentTree; - private final List messages = new LinkedList<>(); private MdxModel timeIndicator; private final DataTable warsmashIni; + private Scene uiScene; + private MeleeUI meleeUI; + public WarsmashGdxMapGame(final DataTable warsmashIni) { this.warsmashIni = warsmashIni; } @@ -158,14 +153,12 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv cameraListenerData.getFieldFloatValue("ListenerDistance", i), cameraListenerData.getFieldFloatValue("ListenerAOA", i)); } - this.cameraManager = new GameCameraManager(cameraPresets); - - this.cameraManager.setupCamera(this.viewer.worldScene); System.out.println("Loaded"); Gdx.gl30.glClearColor(0.0f, 0.0f, 0.0f, 1); // TODO remove white background Gdx.gl30.glEnable(GL30.GL_SCISSOR_TEST); + final Scene portraitScene = this.viewer.addSimpleScene(); this.uiScene = this.viewer.addSimpleScene(); this.uiScene.alpha = true; @@ -197,56 +190,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.batch = new SpriteBatch(); // this.consoleUITexture = new Texture(new DataSourceFileHandle(this.viewer.dataSource, "AlphaUi.png")); - if (this.viewer.dataSource.has("war3mapMap.tga")) { - try { - this.minimapTexture = ImageUtils.getTextureNoColorCorrection(TgaFile.readTGA("war3mapMap.tga", - this.viewer.dataSource.getResourceAsStream("war3mapMap.tga"))); - } - catch (final IOException e) { - System.err.println("Could not load minimap TGA file"); - e.printStackTrace(); - } - } - else if (this.viewer.dataSource.has("war3mapMap.blp")) { - try { - this.minimapTexture = ImageUtils - .getTexture(ImageIO.read(this.viewer.dataSource.getResourceAsStream("war3mapMap.blp"))); - } - catch (final IOException e) { - System.err.println("Could not load minimap BLP file"); - e.printStackTrace(); - } - } - - for (int i = 0; i < this.teamColors.length; i++) { - this.teamColors[i] = ImageUtils.getBLPTexture(this.viewer.dataSource, - "ReplaceableTextures\\" + ReplaceableIds.getPathString(1) + ReplaceableIds.getIdString(i) + ".blp"); - } this.solidGreenTexture = ImageUtils.getBLPTexture(this.viewer.dataSource, "ReplaceableTextures\\TeamColor\\TeamColor06.blp"); Gdx.input.setInputProcessor(this); - this.minimap = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f); - final Rectangle playableMapArea = this.viewer.terrain.getPlayableMapArea(); - final float worldWidth = playableMapArea.getWidth(); - final float worldHeight = playableMapArea.getHeight(); - final float worldSize = Math.max(worldWidth, worldHeight); - final float minimapFilledWidth = (worldWidth / worldSize) * this.minimap.width; - final float minimapFilledHeight = (worldHeight / worldSize) * this.minimap.height; - - this.minimapFilledArea = new Rectangle(this.minimap.x + ((this.minimap.width - minimapFilledWidth) / 2), - this.minimap.y + ((this.minimap.height - minimapFilledHeight) / 2), minimapFilledWidth, - minimapFilledHeight); - - if (this.viewer.startLocations[0] != null) { - this.cameraManager.target.x = this.viewer.startLocations[0].x; - this.cameraManager.target.y = this.viewer.startLocations[0].y; - } - this.shapeRenderer = new ShapeRenderer(); - this.talentTreeWindow = new Rectangle(100, 300, 1400, 800); // Jass2.loadJUI(this.codebase, this.uiViewport, fontGenerator, this.uiScene, this.viewer, // new RootFrameListener() { @@ -255,30 +205,45 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv // WarsmashGdxMapGame.this.gameUI = rootFrame; // } // }, "Scripts\\common.jui", "Scripts\\melee.jui"); - - this.meleeUI = new MeleeUI(this.codebase, this.uiViewport, fontGenerator, this.uiScene, this.viewer, - new RootFrameListener() { + final Element cameraRatesElement = this.viewer.miscData.get("CameraRates"); + final CameraRates cameraRates = new CameraRates(cameraRatesElement.getFieldFloatValue("AOA"), + cameraRatesElement.getFieldFloatValue("FOV"), cameraRatesElement.getFieldFloatValue("Rotation"), + cameraRatesElement.getFieldFloatValue("Distance"), cameraRatesElement.getFieldFloatValue("Forward"), + cameraRatesElement.getFieldFloatValue("Strafe")); + this.meleeUI = new MeleeUI(this.codebase, this.uiViewport, fontGenerator, this.uiScene, portraitScene, + cameraPresets, cameraRates, this.viewer, new RootFrameListener() { @Override public void onCreate(final GameUI rootFrame) { WarsmashGdxMapGame.this.viewer.setGameUI(rootFrame); - final String musicField = rootFrame.getSkinField("Music_V1"); - final String[] musics = musicField.split(";"); - final String musicPath = musics[(int) (Math.random() * musics.length)]; - final Music music = Gdx.audio.newMusic( - new DataSourceFileHandle(WarsmashGdxMapGame.this.viewer.dataSource, musicPath)); - music.setVolume(0.2f); - music.setLooping(true); - music.play(); + if (ENABLE_MUSIC) { + final String musicField = rootFrame.getSkinField("Music_V1"); + final String[] musics = musicField.split(";"); + final String musicPath = musics[(int) (Math.random() * musics.length)]; + final Music music = Gdx.audio.newMusic( + new DataSourceFileHandle(WarsmashGdxMapGame.this.viewer.dataSource, musicPath)); + music.setVolume(0.2f); + music.setLooping(true); + music.play(); + } } }); + final ModelInstance libgdxContentInstance = new LibGDXContentLayerModel(null, this.viewer, "", + this.viewer.mapPathSolver, "").addInstance(); + libgdxContentInstance.setScene(this.uiScene); this.meleeUI.main(); fontGenerator.dispose(); updateUIScene(); - this.meleeUI.resize(); resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + + try { + this.viewer.loadAfterUI(); + } + catch (final IOException e) { + throw new RuntimeException(e); + } } private void updateUIScene() { @@ -304,21 +269,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv Gdx.gl30.glEnable(GL30.GL_SCISSOR_TEST); final float deltaTime = Gdx.graphics.getDeltaTime(); Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO); - this.cameraManager.target.add(this.cameraVelocity.x * deltaTime, this.cameraVelocity.y * deltaTime, 0); - this.cameraManager.target.z = (Math.max( - this.viewer.terrain.getGroundHeight(this.cameraManager.target.x, this.cameraManager.target.y), - this.viewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y)) - 256) - + this.cameraManager.presets[this.cameraManager.currentPreset].height + 256; - this.cameraManager.updateCamera(); - this.meleeUI.updatePortrait(); + this.meleeUI.update(deltaTime); this.viewer.updateAndRender(); -// gl.glDrawElements(GL20.GL_TRIANGLES, this.elements, GL20.GL_UNSIGNED_SHORT, this.faceOffset); - -// this.batch.begin(); -// this.font.draw(this.batch, Integer.toString(Gdx.graphics.getFramesPerSecond()), 0, 0); -// this.batch.end(); - Gdx.gl30.glDisable(GL30.GL_SCISSOR_TEST); Gdx.gl30.glDisable(GL30.GL_CULL_FACE); @@ -326,6 +279,18 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.viewer.webGL.useShaderProgram(null); Gdx.gl30.glActiveTexture(GL30.GL_TEXTURE0); + } + + private void renderLibGDXContent() { + + Gdx.gl30.glDisable(GL30.GL_SCISSOR_TEST); + + Gdx.gl30.glDisable(GL30.GL_CULL_FACE); + + this.viewer.webGL.useShaderProgram(null); + + Gdx.gl30.glActiveTexture(GL30.GL_TEXTURE0); + this.uiViewport.apply(); this.batch.setProjectionMatrix(this.uiCamera.combined); this.batch.begin(); @@ -334,54 +299,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond(); this.glyphLayout.setText(this.font, fpsString); this.font.draw(this.batch, fpsString, (this.uiViewport.getMinWorldWidth() - this.glyphLayout.width) / 2, 1100); - - this.batch.draw(this.minimapTexture, this.minimap.x, this.minimap.y, this.minimap.width, this.minimap.height); - - final Rectangle playableMapArea = this.viewer.terrain.getPlayableMapArea(); - for (final RenderUnit unit : this.viewer.units) { - if (unit.playerIndex >= WarsmashConstants.MAX_PLAYERS) { - System.err.println(unit.row.getName() + " at ( " + unit.location[0] + ", " + unit.location[1] + " )" - + " with " + unit.playerIndex); - unit.playerIndex -= 12; - } - final Texture minimapIcon = this.teamColors[unit.playerIndex]; - this.batch.draw(minimapIcon, - this.minimapFilledArea.x - + (((unit.location[0] - playableMapArea.getX()) / (playableMapArea.getWidth())) - * this.minimapFilledArea.width), - this.minimapFilledArea.y - + (((unit.location[1] - playableMapArea.getY()) / (playableMapArea.getHeight())) - * this.minimapFilledArea.height), - 4, 4); - } this.batch.end(); - if (this.showTalentTree) { - this.shapeRenderer.setProjectionMatrix(this.uiCamera.combined); - - this.shapeRenderer.setColor(Color.BLACK); - this.shapeRenderer.begin(ShapeType.Filled); - this.shapeRenderer.rect(this.talentTreeWindow.x, this.talentTreeWindow.y, this.talentTreeWindow.width, - this.talentTreeWindow.height); - this.shapeRenderer.end(); - - this.shapeRenderer.setColor(Color.YELLOW); - this.shapeRenderer.begin(ShapeType.Line); - this.shapeRenderer.rect(100, 300, 1400, 800); - this.shapeRenderer.end(); - - this.batch.begin(); - - this.font.setColor(Color.YELLOW); - final String title = "Mage Talent Tree"; - this.glyphLayout.setText(this.font, title); - this.font.draw(this.batch, title, - this.talentTreeWindow.x + ((this.talentTreeWindow.width - this.glyphLayout.width) / 2), - (this.talentTreeWindow.y + this.talentTreeWindow.height) - 45); - - this.batch.end(); - } - + Gdx.gl30.glEnable(GL30.GL_SCISSOR_TEST); + Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO); } @Override @@ -401,212 +322,34 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv @Override public void resize(final int width, final int height) { super.resize(width, height); + + this.uiViewport.update(width, height); + this.uiCamera.position.set(this.uiViewport.getMinWorldWidth() / 2, this.uiViewport.getMinWorldHeight() / 2, 0); + + this.meleeUI.resize(setupWorldFrameViewport(width, height)); + updateUIScene(); + + } + + private Rectangle setupWorldFrameViewport(final int width, final int height) { this.tempRect.x = 0; this.tempRect.width = width; final float topHeight = 0.02666f * height; final float bottomHeight = 0.21333f * height; this.tempRect.y = (int) bottomHeight; this.tempRect.height = height - (int) (topHeight + bottomHeight); - this.cameraManager.camera.viewport(this.tempRect); - final float portraitTestWidth = (100 / 640f) * width; - final float portraitTestHeight = (100 / 480f) * height; - - this.uiViewport.update(width, height); - this.uiCamera.position.set(this.uiViewport.getMinWorldWidth() / 2, this.uiViewport.getMinWorldHeight() / 2, 0); - - updateUIScene(); - - this.meleeUI.resize(); + return this.tempRect; } - public static abstract class CameraManager { - protected final float[] cameraPositionTemp = new float[3]; - protected final float[] cameraTargetTemp = new float[3]; - protected CanvasProvider canvas; - protected Camera camera; - protected float moveSpeed; - protected float rotationSpeed; - protected float zoomFactor; - protected float horizontalAngle; - protected float verticalAngle; - protected float distance; - protected Vector3 position; - protected Vector3 target; - protected Vector3 worldUp; - protected Vector3 vecHeap; - protected Quaternion quatHeap; - protected Quaternion quatHeap2; - - public CameraManager() { - } - - // An orbit camera setup example. - // Left mouse button controls the orbit itself. - // The right mouse button allows to move the camera and the point it's looking - // at on the XY plane. - // Scrolling zooms in and out. - public void setupCamera(final Scene scene) { - this.canvas = scene.viewer.canvas; - this.camera = scene.camera; - this.moveSpeed = 2; - this.rotationSpeed = (float) HORIZONTAL_ANGLE_INCREMENT; - this.zoomFactor = 0.1f; - this.horizontalAngle = 0;// (float) (Math.PI / 2); - this.verticalAngle = (float) Math.toRadians(34); - this.distance = 1650; - this.position = new Vector3(); - this.target = new Vector3(0, 0, 0); - this.worldUp = new Vector3(0, 0, 1); - this.vecHeap = new Vector3(); - this.quatHeap = new Quaternion(); - this.quatHeap2 = new Quaternion(); - - updateCamera(); - -// cameraUpdate(); - } - - public abstract void updateCamera(); - -// private void cameraUpdate() { -// -// } - } - - public static final class GameCameraManager extends CameraManager { - private final CameraPreset[] presets; - private int currentPreset = 0; - - protected boolean insertDown; - protected boolean deleteDown; - - public GameCameraManager(final CameraPreset[] presets) { - this.presets = presets; - } - - @Override - public void updateCamera() { - this.quatHeap2.idt(); - final CameraPreset cameraPreset = this.presets[this.currentPreset]; - this.quatHeap.idt(); - this.horizontalAngle = (float) Math - .toRadians(cameraPreset.getRotation(this.insertDown, this.deleteDown) - 90); - this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle); - this.distance = Math.max(1200, cameraPreset.distance); - this.verticalAngle = (float) Math.toRadians(Math.min(335, cameraPreset.aoa) - 270); - this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle); - this.quatHeap.mul(this.quatHeap2); - - this.position.set(0, 0, 1); - this.quatHeap.transform(this.position); - this.position.nor(); - this.position.scl(this.distance); - this.position = this.position.add(this.target); - this.camera.perspective((float) Math.toRadians(cameraPreset.fov / 2), this.camera.getAspect(), - cameraPreset.nearZ, cameraPreset.farZ); - - this.camera.moveToAndFace(this.position, this.target, this.worldUp); - } - } - - public static final class PortraitCameraManager extends CameraManager { - public com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera; - protected MdxComplexInstance modelInstance; - - @Override - public void updateCamera() { - this.quatHeap.idt(); - this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle); - this.quatHeap2.idt(); - this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle); - this.quatHeap.mul(this.quatHeap2); - - this.position.set(0, 0, 1); - this.quatHeap.transform(this.position); - this.position.scl(this.distance); - this.position = this.position.add(this.target); - if (this.modelCamera != null) { - this.modelCamera.getPositionTranslation(this.cameraPositionTemp, this.modelInstance.sequence, - this.modelInstance.frame, this.modelInstance.counter); - this.modelCamera.getTargetTranslation(this.cameraTargetTemp, this.modelInstance.sequence, - this.modelInstance.frame, this.modelInstance.counter); - - this.position.set(this.modelCamera.position); - this.target.set(this.modelCamera.targetPosition); - - this.position.add(this.cameraPositionTemp[0], this.cameraPositionTemp[1], this.cameraPositionTemp[2]); - this.target.add(this.cameraTargetTemp[0], this.cameraTargetTemp[1], this.cameraTargetTemp[2]); - this.camera.perspective(this.modelCamera.fieldOfView * 0.75f, this.camera.getAspect(), - this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane); - } - else { - this.camera.perspective(70, this.camera.getAspect(), 100, 5000); - } - - this.camera.moveToAndFace(this.position, this.target, this.worldUp); - } - - public void setModelInstance(final MdxComplexInstance modelInstance, final MdxModel portraitModel) { - this.modelInstance = modelInstance; - if (modelInstance == null) { - this.modelCamera = null; - } - else if ((portraitModel != null) && (portraitModel.getCameras().size() > 0)) { - this.modelCamera = portraitModel.getCameras().get(0); - } - } - } - - private final float cameraSpeed = 4096.0f; // per second - private final Vector2 cameraVelocity = new Vector2(); - private Texture minimapTexture; - private Rectangle talentTreeWindow; - private Scene uiScene; - private MeleeUI meleeUI; - @Override public boolean keyDown(final int keycode) { - if (keycode == Input.Keys.LEFT) { - this.cameraVelocity.x = -this.cameraSpeed; - } - else if (keycode == Input.Keys.RIGHT) { - this.cameraVelocity.x = this.cameraSpeed; - } - else if (keycode == Input.Keys.DOWN) { - this.cameraVelocity.y = -this.cameraSpeed; - } - else if (keycode == Input.Keys.UP) { - this.cameraVelocity.y = this.cameraSpeed; - } - else if (keycode == Input.Keys.INSERT) { - this.cameraManager.insertDown = true; - } - else if (keycode == Input.Keys.FORWARD_DEL) { - this.cameraManager.deleteDown = true; - } + this.meleeUI.keyDown(keycode); return true; } @Override public boolean keyUp(final int keycode) { - if (keycode == Input.Keys.LEFT) { - this.cameraVelocity.x = 0; - } - else if (keycode == Input.Keys.RIGHT) { - this.cameraVelocity.x = 0; - } - else if (keycode == Input.Keys.DOWN) { - this.cameraVelocity.y = 0; - } - else if (keycode == Input.Keys.UP) { - this.cameraVelocity.y = 0; - } - else if (keycode == Input.Keys.INSERT) { - this.cameraManager.insertDown = false; - } - else if (keycode == Input.Keys.FORWARD_DEL) { - this.cameraManager.deleteDown = false; - } + this.meleeUI.keyUp(keycode); return true; } @@ -624,13 +367,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv clickLocationTemp2.y = screenY; this.uiViewport.unproject(clickLocationTemp2); - if (this.minimapFilledArea.contains(clickLocationTemp2.x, clickLocationTemp2.y)) { - final float clickX = (clickLocationTemp2.x - this.minimapFilledArea.x) / this.minimapFilledArea.width; - final float clickY = (clickLocationTemp2.y - this.minimapFilledArea.y) / this.minimapFilledArea.height; - this.cameraManager.target.x = (clickX * this.viewer.terrain.columns * 128) - + this.viewer.terrain.centerOffset[0]; - this.cameraManager.target.y = (clickY * this.viewer.terrain.rows * 128) - + this.viewer.terrain.centerOffset[1]; + if (this.meleeUI.touchDown(clickLocationTemp2.x, clickLocationTemp2.y, button)) { return false; } if (button == Input.Buttons.RIGHT) { @@ -723,13 +460,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv @Override public boolean scrolled(final int amount) { - this.cameraManager.currentPreset -= amount; - if (this.cameraManager.currentPreset < 0) { - this.cameraManager.currentPreset = 0; - } - if (this.cameraManager.currentPreset >= this.cameraManager.presets.length) { - this.cameraManager.currentPreset = this.cameraManager.presets.length - 1; - } + this.meleeUI.scrolled(amount); return true; } @@ -743,43 +474,88 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv } } - private static class CameraPreset { - private final float aoa; - private final float fov; - private final float rotation; - private final float rotationInsert; - private final float rotationDelete; - private final float distance; - private final float farZ; - private final float nearZ; - private final float height; - private final float listenerDistance; - private final float listenerAOA; + private class LibGDXContentLayerModelInstance extends ModelInstance { - public CameraPreset(final float aoa, final float fov, final float rotation, final float rotationInsert, - final float rotationDelete, final float distance, final float farZ, final float nearZ, - final float height, final float listenerDistance, final float listenerAOA) { - this.aoa = aoa; - this.fov = fov; - this.rotation = rotation; - this.rotationInsert = rotationInsert; - this.rotationDelete = rotationDelete; - this.distance = distance; - this.farZ = farZ; - this.nearZ = nearZ; - this.height = height; - this.listenerDistance = listenerDistance; - this.listenerAOA = listenerAOA; + public LibGDXContentLayerModelInstance(final Model model) { + super(model); } - public float getRotation(final boolean insertDown, final boolean deleteDown) { - if (insertDown && !deleteDown) { - return this.rotationInsert; - } - if (!insertDown && deleteDown) { - return this.rotationDelete; - } - return this.rotation; + @Override + public void updateAnimations(final float dt) { + } + + @Override + public void clearEmittedObjects() { + + } + + @Override + protected void updateLights(final Scene scene2) { + + } + + @Override + public void renderOpaque(final Matrix4 mvp) { + + } + + @Override + public void renderTranslucent() { + renderLibGDXContent(); + } + + @Override + public void load() { + } + + @Override + protected RenderBatch getBatch(final TextureMapper textureMapper2) { + throw new UnsupportedOperationException("NOT API"); + } + + @Override + public void setReplaceableTexture(final int replaceableTextureId, final String replaceableTextureFile) { + + } + + @Override + public boolean isBatched() { + return super.isBatched(); + } + + @Override + protected void removeLights(final Scene scene2) { + // TODO Auto-generated method stub + + } + + } + + private class LibGDXContentLayerModel extends Model { + + public LibGDXContentLayerModel(final ModelHandler handler, final ModelViewer viewer, final String extension, + final PathSolver pathSolver, final String fetchUrl) { + super(handler, viewer, extension, pathSolver, fetchUrl); + this.ok = true; + } + + @Override + protected ModelInstance createInstance(final int type) { + return new LibGDXContentLayerModelInstance(this); + } + + @Override + protected void lateLoad() { + } + + @Override + protected void load(final InputStream src, final Object options) { + } + + @Override + protected void error(final Exception e) { + } + } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java index 4d05e0c..1d0be0c 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -164,6 +164,13 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { return file; } + public String trySkinField(String file) { + if ((file != null) && this.skin.hasField(file)) { + file = this.skin.getField(file); + } + return file; + } + public DataTable getSkinData() { return this.skinData; } @@ -196,6 +203,14 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { return null; } + public TextureFrame createTextureFrame(final String name, final UIFrame parent, final boolean decorateFileNames, + final Vector4Definition texCoord) { + final TextureFrame textureFrame = new TextureFrame(name, parent, decorateFileNames, texCoord); + this.nameToFrame.put(name, textureFrame); + add(textureFrame); + return textureFrame; + } + public UIFrame inflate(final FrameDefinition frameDefinition, final UIFrame parent, final FrameDefinition parentDefinitionIfAvailable) { UIFrame inflatedFrame = null; @@ -213,7 +228,8 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { inflatedFrame = simpleFrame; } else if ("SPRITE".equals(frameDefinition.getFrameType())) { - final SpriteFrame spriteFrame = new SpriteFrame(frameDefinition.getName(), parent, this.uiScene); + final SpriteFrame spriteFrame = new SpriteFrame(frameDefinition.getName(), parent, this.uiScene, + viewport2); String backgroundArt = frameDefinition.getString("BackgroundArt"); if (frameDefinition.has("DecorateFileNames") || ((parentDefinitionIfAvailable != null) && parentDefinitionIfAvailable.has("DecorateFileNames"))) { @@ -224,13 +240,9 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { throw new IllegalStateException("Decorated file name lookup not available: " + backgroundArt); } } - if (backgroundArt.toLowerCase().endsWith(".mdl") || backgroundArt.toLowerCase().endsWith(".mdx")) { - backgroundArt = backgroundArt.substring(0, backgroundArt.length() - 4); + if (backgroundArt != null) { + setSpriteFrameModel(spriteFrame, backgroundArt); } - backgroundArt += ".mdx"; - final MdxModel model = (MdxModel) this.modelViewer.load(backgroundArt, this.modelViewer.mapPathSolver, - this.modelViewer.solverParams); - spriteFrame.setModel(model); viewport2 = this.fdfCoordinateResolutionDummyViewport; inflatedFrame = spriteFrame; } @@ -330,9 +342,24 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { return inflatedFrame; } + public void setSpriteFrameModel(final SpriteFrame spriteFrame, String backgroundArt) { + if (backgroundArt.toLowerCase().endsWith(".mdl") || backgroundArt.toLowerCase().endsWith(".mdx")) { + backgroundArt = backgroundArt.substring(0, backgroundArt.length() - 4); + } + backgroundArt += ".mdx"; + final MdxModel model = (MdxModel) this.modelViewer.load(backgroundArt, this.modelViewer.mapPathSolver, + this.modelViewer.solverParams); + spriteFrame.setModel(model); + } + public UIFrame createFrameByType(final String typeName, final String name, final UIFrame owner, final String inherits, final int createContext) { - throw new UnsupportedOperationException("Not yet implemented"); + // 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 frameDefinition = new FrameDefinition(FrameClass.Frame, typeName, name); + final UIFrame inflatedFrame = inflate(frameDefinition, owner, null); + add(inflatedFrame); + return inflatedFrame; } public UIFrame getFrameByName(final String name, final int createContext) { @@ -353,6 +380,20 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { return (fdfY / 0.6f) * viewport.getWorldHeight(); } + public static float unconvertX(final Viewport viewport, final float nonFdfX) { + if (viewport instanceof ExtendViewport) { + return (nonFdfX / ((ExtendViewport) viewport).getMinWorldWidth()) * 0.8f; + } + return (nonFdfX / viewport.getWorldWidth()) * 0.8f; + } + + public static float unconvertY(final Viewport viewport, final float nonFdfY) { + if (viewport instanceof ExtendViewport) { + return (nonFdfY / ((ExtendViewport) viewport).getMinWorldHeight()) * 0.6f; + } + return (nonFdfY / viewport.getWorldHeight()) * 0.6f; + } + public Texture loadTexture(String path) { if (!path.contains(".")) { path = path + ".blp"; @@ -374,4 +415,10 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { public final void positionBounds(final Viewport viewport) { innerPositionBounds(viewport); } + + @Override + public void add(final UIFrame childFrame) { + super.add(childFrame); + this.nameToFrame.put(childFrame.getName(), childFrame); + } } 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 46af7b3..893401f 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java @@ -1,6 +1,7 @@ package com.etheller.warsmash.parsers.fdf.frames; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import com.badlogic.gdx.graphics.g2d.BitmapFont; @@ -252,6 +253,15 @@ public abstract class AbstractRenderableFrame implements UIFrame { @Override public void addSetPoint(final SetPoint setPointDefinition) { + // TODO this is O(N) in the number of SetPoints, and that + // is not good performance. + final Iterator iterator = this.setPoints.iterator(); + while (iterator.hasNext()) { + final SetPoint setPoint = iterator.next(); + if (setPoint.getMyPoint() == setPointDefinition.getMyPoint()) { + iterator.remove(); + } + } this.setPoints.add(setPointDefinition); } @@ -261,9 +271,6 @@ public abstract class AbstractRenderableFrame implements UIFrame { // TODO this is a bit of a hack, remove later return; } - if ("ResourceBarFrame".equals(this.name)) { - System.out.println("doing resource bar"); - } if (this.anchors.isEmpty() && this.setPoints.isEmpty()) { this.renderBounds.x = this.parent.getFramePointX(FramePoint.LEFT); this.renderBounds.y = this.parent.getFramePointY(FramePoint.BOTTOM); @@ -334,4 +341,14 @@ public abstract class AbstractRenderableFrame implements UIFrame { protected abstract void internalRender(SpriteBatch batch, BitmapFont baseFont, GlyphLayout glyphLayout); + @Override + public UIFrame touchDown(final float screenX, final float screenY, final int button) { + return null; + } + + @Override + public String getName() { + return this.name; + } + } 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 eb2889d..8a7bbb7 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java @@ -35,4 +35,15 @@ public abstract class AbstractUIFrame extends AbstractRenderableFrame implements childFrame.positionBounds(viewport); } } + + @Override + public UIFrame touchDown(final float screenX, final float screenY, final int button) { + for (final UIFrame childFrame : this.childFrames) { + final UIFrame clickedChild = childFrame.touchDown(screenX, screenY, button); + if (clickedChild != null) { + return clickedChild; + } + } + return super.touchDown(screenX, screenY, button); + } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java index 28fb028..f569e66 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java @@ -4,19 +4,26 @@ 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.viewer5.Scene; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode; +import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag; +import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils; public class SpriteFrame extends AbstractRenderableFrame { private final Scene scene; private MdxComplexInstance instance; + private float zDepth; + private final Viewport uiViewport; - public SpriteFrame(final String name, final UIFrame parent, final Scene scene) { + public SpriteFrame(final String name, final UIFrame parent, final Scene scene, final Viewport uiViewport) { super(name, parent); this.scene = scene; + this.uiViewport = uiViewport; } public void setModel(final MdxModel model) { @@ -27,21 +34,44 @@ public class SpriteFrame extends AbstractRenderableFrame { this.instance = (MdxComplexInstance) model.addInstance(); this.instance.setSequenceLoopMode(SequenceLoopMode.MODEL_LOOP); this.instance.setScene(this.scene); - this.instance.setLocation(this.renderBounds.x, this.renderBounds.y, 0); + this.instance.setLocation(this.renderBounds.x, this.renderBounds.y, this.zDepth); } else { this.instance = null; } } + @Override + public void setVisible(final boolean visible) { + super.setVisible(visible); + updateInstanceLocation(this.uiViewport); + } + @Override protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { } @Override - protected void innerPositionBounds(final Viewport viewport) { + public void setFramePointX(final FramePoint framePoint, final float x) { + super.setFramePointX(framePoint, x); + updateInstanceLocation(this.uiViewport); + } + @Override + public void setFramePointY(final FramePoint framePoint, final float y) { + super.setFramePointY(framePoint, y); + updateInstanceLocation(this.uiViewport); + } + + public void setZDepth(final float depth) { + this.zDepth = depth; + updateInstanceLocation(this.uiViewport); + } + + @Override + protected void innerPositionBounds(final Viewport viewport) { + updateInstanceLocation(viewport); } public void setSequence(final int index) { @@ -50,6 +80,18 @@ public class SpriteFrame extends AbstractRenderableFrame { } } + public void setSequence(final String animationName) { + if (this.instance != null) { + SequenceUtils.randomSequence(this.instance, animationName.toLowerCase()); + } + } + + public void setSequence(final PrimaryTag animationName) { + if (this.instance != null) { + SequenceUtils.randomSequence(this.instance, animationName); + } + } + public void setAnimationSpeed(final float speedRatio) { if (this.instance != null) { this.instance.setAnimationSpeed(speedRatio); @@ -68,4 +110,17 @@ public class SpriteFrame extends AbstractRenderableFrame { } } + private void updateInstanceLocation(final Viewport viewport) { + if (this.instance != null) { + this.instance.setLocation(GameUI.unconvertX(viewport, this.renderBounds.x), + GameUI.unconvertY(viewport, this.renderBounds.y), this.zDepth); + if (isVisible()) { + this.instance.show(); + } + else { + this.instance.hide(); + } + } + } + } 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 0615caa..b6ceca7 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java @@ -31,4 +31,8 @@ public interface UIFrame { void setSetAllPoints(boolean setAllPoints); void setVisible(boolean visible); + + UIFrame touchDown(float screenX, float screenY, int button); + + String getName(); } diff --git a/core/src/com/etheller/warsmash/viewer5/ModelInstance.java b/core/src/com/etheller/warsmash/viewer5/ModelInstance.java index d9d527e..9d75fae 100644 --- a/core/src/com/etheller/warsmash/viewer5/ModelInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/ModelInstance.java @@ -145,4 +145,6 @@ public abstract class ModelInstance extends Node { protected abstract RenderBatch getBatch(TextureMapper textureMapper2); public abstract void setReplaceableTexture(int replaceableTextureId, String replaceableTextureFile); + + protected abstract void removeLights(Scene scene2); } diff --git a/core/src/com/etheller/warsmash/viewer5/Scene.java b/core/src/com/etheller/warsmash/viewer5/Scene.java index 0896e5e..c34aac5 100644 --- a/core/src/com/etheller/warsmash/viewer5/Scene.java +++ b/core/src/com/etheller/warsmash/viewer5/Scene.java @@ -147,6 +147,7 @@ public abstract class Scene { public boolean removeInstance(final ModelInstance instance) { if (instance.scene == this) { + instance.removeLights(this); innerRemove(instance); instance.scene = null; @@ -219,7 +220,6 @@ public abstract class Scene { this.emitterObjectUpdater.update(dt); this.updatedParticles = this.emitterObjectUpdater.objects.size(); - this.lightManager.update(); } protected abstract void innerUpdate(float dt, int frame); @@ -239,6 +239,7 @@ public abstract class Scene { gl.glDepthMask(true); gl.glClear(GL20.GL_DEPTH_BUFFER_BIT | GL20.GL_COLOR_BUFFER_BIT); } + this.lightManager.update(); } public void renderOpaque() { diff --git a/core/src/com/etheller/warsmash/viewer5/Shaders.java b/core/src/com/etheller/warsmash/viewer5/Shaders.java index 2adfbf8..6171fb3 100644 --- a/core/src/com/etheller/warsmash/viewer5/Shaders.java +++ b/core/src/com/etheller/warsmash/viewer5/Shaders.java @@ -57,11 +57,10 @@ public class Shaders { " "; public static String lightSystem(final String normalName, final String positionName, final String lightTexture, - final String lightCount, final boolean terrain) { + final String lightTextureHeight, final String lightCount, final boolean terrain) { return " vec3 lightFactor = vec3(0.0,0.0,0.0);\r\n" + // - " float lightIndex = 0.0;\r\n" + // - " for(float lightIndex = 0.0; lightIndex < " + lightCount + "; lightIndex += 1.0) {\r\n" + // - " float rowPos = (lightIndex + 0.5) / " + lightCount + ";\r\n" + // + " for(float lightIndex = 0.5; lightIndex < " + lightCount + "; lightIndex += 1.0) {\r\n" + // + " float rowPos = (lightIndex) / " + lightTextureHeight + ";\r\n" + // " vec4 lightPosition = texture2D(" + lightTexture + ", vec2(0.125, rowPos));\r\n" + // " vec3 lightExtra = texture2D(" + lightTexture + ", vec2(0.375, rowPos)).xyz;\r\n" + // " vec4 lightColor = texture2D(" + lightTexture + ", vec2(0.625, rowPos));\r\n" + // diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/AnimatedObject.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/AnimatedObject.java index 6a43a20..ce47a6c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/AnimatedObject.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/AnimatedObject.java @@ -69,7 +69,7 @@ public class AnimatedObject { return -1; } - public int getQuadValue(final float[] out, final War3ID name, final int sequence, final int frame, + public int getQuatValue(final float[] out, final War3ID name, final int sequence, final int frame, final int counter, final float[] defaultValue) { if (sequence != -1) { final Sd animation = this.timelines.get(name); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java index df93d5e..3fe6d19 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/BatchGroup.java @@ -65,7 +65,8 @@ public class BatchGroup extends GenericGroup { unitLightsTexture.bind(14); shader.setUniformi("u_lightTexture", 14); - shader.setUniformf("u_lightCount", unitLightsTexture.getHeight() - 0.5f); + shader.setUniformf("u_lightCount", lightManager.getUnitLightCount()); + shader.setUniformf("u_lightTextureHeight", unitLightsTexture.getHeight()); // Instances of models with no bones don't have a bone texture. if (boneTexture != null) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GenericObject.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GenericObject.java index 952a124..849e85c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GenericObject.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GenericObject.java @@ -128,7 +128,7 @@ public class GenericObject extends AnimatedObject implements GenericIndexed { } public int getRotation(final float[] out, final int sequence, final int frame, final int counter) { - return this.getQuadValue(out, AnimationMap.KGRT.getWar3id(), sequence, frame, counter, + return this.getQuatValue(out, AnimationMap.KGRT.getWar3id(), sequence, frame, counter, RenderMathUtils.FLOAT_QUAT_DEFAULT); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/LightInstance.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/LightInstance.java index aade9ac..4e5a26f 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/LightInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/LightInstance.java @@ -88,15 +88,23 @@ public class LightInstance implements UpdatableObject, SceneLightInstance { public void update(final Scene scene) { this.light.getVisibility(scalarHeap, this.instance.sequence, this.instance.frame, this.instance.counter); this.visible = scalarHeap[0] > 0; + updateVisibility(scene, this.visible); + } + + public void remove(final Scene scene) { + updateVisibility(scene, false); + } + + private void updateVisibility(final Scene scene, final boolean visible) { if (scene != null) { - if (this.loadedInScene != this.visible) { - if (this.visible) { + if (this.loadedInScene != visible) { + if (visible) { scene.addLight(this); } else { scene.removeLight(this); } - this.loadedInScene = this.visible; + this.loadedInScene = visible; } } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java index b08c9de..b9e3b8f 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java @@ -614,6 +614,13 @@ public class MdxComplexInstance extends ModelInstance { } } + @Override + protected void removeLights(final Scene scene2) { + for (final LightInstance light : this.lights) { + light.remove(this.scene); + } + } + /** * Set the team color of this instance. */ diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java index 4cea40e..7e7cb8c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxShaders.java @@ -83,7 +83,9 @@ public class MdxShaders { " gl_FragColor = color;\r\n" + // " }\r\n"; - public static final String vsComplex = " uniform mat4 u_mvp;\r\n" + // + public static final String vsComplex = "\r\n" + // + "\r\n" + // + " uniform mat4 u_mvp;\r\n" + // " uniform vec4 u_vertexColor;\r\n" + // " uniform vec4 u_geosetColor;\r\n" + // " uniform float u_layerAlpha;\r\n" + // @@ -106,6 +108,7 @@ public class MdxShaders { " varying float v_uvScale;\r\n" + // " uniform sampler2D u_lightTexture;\r\n" + // " uniform float u_lightCount;\r\n" + // + " uniform float u_lightTextureHeight;\r\n" + // Shaders.boneTexture + "\r\n" + // " void transform(inout vec3 position, inout vec3 normal) {\r\n" + // " // For the broken models out there, since the game supports this.\r\n" + // @@ -150,7 +153,8 @@ public class MdxShaders { " v_uvScale = u_uvScale;\r\n" + // " gl_Position = u_mvp * vec4(position, 1.0);\r\n" + // " if(!u_unshaded) {\r\n" + // - Shaders.lightSystem("normal", "position", "u_lightTexture", "u_lightCount", false) + "\r\n" + // + Shaders.lightSystem("normal", "position", "u_lightTexture", "u_lightTextureHeight", "u_lightCount", false) + + "\r\n" + // " v_color.xyz *= clamp(lightFactor, 0.0, 1.0);\r\n" + // " }\r\n" + // " }"; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java index b176f1e..f56d476 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxSimpleInstance.java @@ -37,6 +37,10 @@ public class MdxSimpleInstance extends BatchedInstance { protected void updateLights(final Scene scene2) { } + @Override + protected void removeLights(final Scene scene2) { + } + @Override public void load() { } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Particle2.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Particle2.java index b125d8a..91f5e6f 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Particle2.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Particle2.java @@ -67,13 +67,12 @@ public class Particle2 extends EmittedObject lights; private FloatBuffer lightDataCopyHeap; private final DataTexture unitLightsTexture; - private final War3MapViewer viewer; private int unitLightCount; - public W3xScenePortraitLightManager(final War3MapViewer viewer) { + public W3xScenePortraitLightManager(final War3MapViewer viewer, final Vector3 lightDirection) { this.viewer = viewer; + this.hardcodedLightDirection = lightDirection; this.lights = new ArrayList<>(); this.unitLightsTexture = new DataTexture(viewer.gl, 4, 4, 1); this.lightDataCopyHeap = ByteBuffer.allocateDirect(16 * 1 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); @@ -52,13 +56,35 @@ public class W3xScenePortraitLightManager implements SceneLightManager, W3xScene this.unitLightCount = 0; this.lightDataCopyHeap.clear(); int offset = 0; - if (this.viewer.dncUnitDay != null) { - if (!this.viewer.dncUnitDay.lights.isEmpty()) { - this.viewer.dncUnitDay.lights.get(0).bind(0, this.lightDataCopyHeap); - offset += 16; - this.unitLightCount++; + if (this.hardcodedLightDirection == null) { + if (this.viewer.dncTarget != null) { + if (!this.viewer.dncTarget.lights.isEmpty()) { + this.viewer.dncTarget.lights.get(0).bind(0, this.lightDataCopyHeap); + offset += 16; + this.unitLightCount++; + } } } + else { + this.lightDataCopyHeap.put(offset, this.hardcodedLightDirection.y); + this.lightDataCopyHeap.put(offset + 1, -this.hardcodedLightDirection.x); + this.lightDataCopyHeap.put(offset + 2, -this.hardcodedLightDirection.z); + this.lightDataCopyHeap.put(offset + 3, -this.hardcodedLightDirection.z); + this.lightDataCopyHeap.put(offset + 4, Light.Type.DIRECTIONAL.ordinal()); + this.lightDataCopyHeap.put(offset + 5, 1); + this.lightDataCopyHeap.put(offset + 6, 2); + this.lightDataCopyHeap.put(offset + 7, 0); + this.lightDataCopyHeap.put(offset + 8, 1); + this.lightDataCopyHeap.put(offset + 9, 1); + this.lightDataCopyHeap.put(offset + 10, 1); + this.lightDataCopyHeap.put(offset + 11, 1); + this.lightDataCopyHeap.put(offset + 12, 1); + this.lightDataCopyHeap.put(offset + 13, 1); + this.lightDataCopyHeap.put(offset + 14, 1); + this.lightDataCopyHeap.put(offset + 15, 0.3f); + offset += 16; + this.unitLightCount++; + } for (final LightInstance light : this.lights) { light.bind(offset, this.lightDataCopyHeap); offset += 16; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java index 6b04d75..c56fd50 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/W3xShaders.java @@ -8,14 +8,16 @@ public class W3xShaders { } public static final String vert = "\r\n" + // + "\r\n" + // " uniform mat4 u_mvp;\r\n" + // " uniform sampler2D u_heightMap;\r\n" + // " uniform vec2 u_pixel;\r\n" + // " uniform vec2 u_size;\r\n" + // " uniform vec2 u_shadowPixel;\r\n" + // " uniform vec2 u_centerOffset;\r\n" + // - " uniform sampler2D lightTexture;\r\n" + // - " uniform float lightCount;\r\n" + // + " uniform sampler2D u_lightTexture;\r\n" + // + " uniform float u_lightCount;\r\n" + // + " uniform float u_lightTextureHeight;\r\n" + // " attribute vec3 a_position;\r\n" + // " attribute vec2 a_uv;\r\n" + // " varying vec2 v_uv;\r\n" + // @@ -42,7 +44,9 @@ public class W3xShaders { " vec3 myposition = vec3(a_position.xy, height * 128.0 + a_position.z);\r\n" + // " gl_Position = u_mvp * vec4(myposition.xyz, 1.0);\r\n" + // " a_positionHeight = a_position.z;\r\n" + // - Shaders.lightSystem("v_normal", "myposition", "lightTexture", "lightCount", true) + "\r\n" + // + Shaders.lightSystem("v_normal", "myposition", "u_lightTexture", "u_lightTextureHeight", "u_lightCount", + true) + + "\r\n" + // " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // " }\r\n" + // " "; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java index eeda60f..1a797b9 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -76,6 +76,8 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderAttackProjecti import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderEffect; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderItem; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnitTypeData; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; @@ -86,6 +88,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityM import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.CAttackProjectile; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.CWidgetAbilityTargetCheckReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.PointAbilityTargetCheckReceiver; @@ -109,6 +112,9 @@ public class War3MapViewer extends ModelViewer { private static final War3ID ITEM_FILE = War3ID.fromString("ifil"); private static final War3ID UNIT_PATHING = War3ID.fromString("upat"); private static final War3ID DESTRUCTABLE_PATHING = War3ID.fromString("bptx"); + private static final War3ID ELEVATION_SAMPLE_RADIUS = War3ID.fromString("uerd"); + private static final War3ID MAX_PITCH = War3ID.fromString("umxp"); + private static final War3ID MAX_ROLL = War3ID.fromString("umxr"); private static final War3ID sloc = War3ID.fromString("sloc"); private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation(); private static final float[] rayHeap = new float[6]; @@ -131,7 +137,7 @@ public class War3MapViewer extends ModelViewer { public MappedData doodadsData = new MappedData(); public MappedData doodadMetaData = new MappedData(); public MappedData destructableMetaData = new MappedData(); - public List doodads = new ArrayList<>(); + public List doodads = new ArrayList<>(); public List terrainDoodads = new ArrayList<>(); public boolean doodadsReady; public boolean unitsAndItemsLoaded; @@ -164,6 +170,7 @@ public class War3MapViewer extends ModelViewer { public CSimulation simulation; private float updateTime; + // for World Editor, I think public Vector2[] startLocations = new Vector2[WarsmashConstants.MAX_PLAYERS]; private final DynamicShadowManager dynamicShadowManager = new DynamicShadowManager(); @@ -175,7 +182,9 @@ public class War3MapViewer extends ModelViewer { private final List selectionCircleSizes = new ArrayList<>(); private final Map unitToRenderPeer = new HashMap<>(); + private final Map unitIdToTypeData = new HashMap<>(); private GameUI gameUI; + private Vector3 lightDirection; public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) { super(dataSource, canvas); @@ -269,6 +278,11 @@ public class War3MapViewer extends ModelViewer { this.miscData.readTXT(miscDataTxtStream, true); } } + final Element light = this.miscData.get("Light"); + final float lightX = light.getFieldFloatValue("Direction", 0); + final float lightY = light.getFieldFloatValue("Direction", 1); + final float lightZ = light.getFieldFloatValue("Direction", 2); + this.lightDirection = new Vector3(lightX, lightY, lightZ).nor(); this.unitGlobalStrings = new DataTable(worldEditStrings); try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\UnitGlobalStrings.txt")) { this.unitGlobalStrings.readTXT(miscDataTxtStream, true); @@ -381,9 +395,9 @@ public class War3MapViewer extends ModelViewer { this.confirmationInstance.setSequence(0); this.confirmationInstance.setScene(this.worldScene); - final Warcraft3MapObjectData modifications = this.mapMpq.readModifications(); - this.simulation = new CSimulation(this.miscData, modifications.getUnits(), modifications.getAbilities(), - new SimulationRenderController() { + this.allObjectData = this.mapMpq.readModifications(); + this.simulation = new CSimulation(this.miscData, this.allObjectData.getUnits(), + this.allObjectData.getAbilities(), new SimulationRenderController() { private final Map keyToCombatSound = new HashMap<>(); @Override @@ -493,25 +507,39 @@ public class War3MapViewer extends ModelViewer { this.seededRandom, w3iFile.getPlayers()); if (this.doodadsAndDestructiblesLoaded) { - this.loadDoodadsAndDestructibles(modifications); + this.loadDoodadsAndDestructibles(this.allObjectData); } else { throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises"); } - if (this.unitsAndItemsLoaded) { - this.loadUnitsAndItems(modifications); - } - else { - throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises"); - } - - this.terrain.initShadows(); this.terrain.createWaves(); loadLightsAndShading(tileset); } + /** + * Loads the map information that should be loaded after UI, such as units, who + * need to be able to setup their UI counterparts (icons, etc) for their + * abilities while loading. This allows the dynamic creation of units while the + * game is playing to better share code with the startup sequence's creation of + * units. + * + * @throws IOException + */ + public void loadAfterUI() throws IOException { + if (this.unitsAndItemsLoaded) { + this.loadUnitsAndItems(this.allObjectData); + } + else { + throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises"); + } + + // After we finish loading units, we need to update & create the stored shadow + // information for all unit shadows + this.terrain.initShadows(); + } + private void loadLightsAndShading(final char tileset) { // TODO this should be set by the war3map.j actually, not by the tileset, so the // call to set day night models is just for testing to make the test look pretty @@ -605,11 +633,11 @@ public class War3MapViewer extends ModelViewer { } if (type == WorldEditorDataType.DESTRUCTIBLES) { - this.doodads - .add(new Destructable(this, model, row, doodad, type, maxPitch, maxRoll, doodad.getLife())); + this.doodads.add(new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll, + doodad.getLife())); } else { - this.doodads.add(new Doodad(this, model, row, doodad, type, maxPitch, maxRoll)); + this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll)); } } } @@ -841,8 +869,9 @@ public class War3MapViewer extends ModelViewer { final float angle = (float) Math.toDegrees(unit.getAngle()); final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), unit.getPlayer(), unit.getLocation()[0], unit.getLocation()[1], angle, buildingPathingPixelMap); + final RenderUnitTypeData typeData = getUnitTypeData(unit.getId(), row); final RenderUnit renderUnit = new RenderUnit(this, model, row, unit, soundset, portraitModel, - simulationUnit); + simulationUnit, typeData); this.unitToRenderPeer.put(simulationUnit, renderUnit); this.units.add(renderUnit); if (unitShadowSplat != null) { @@ -879,6 +908,16 @@ public class War3MapViewer extends ModelViewer { this.anyReady = true; } + private RenderUnitTypeData getUnitTypeData(final War3ID key, final MutableGameObject row) { + RenderUnitTypeData unitTypeData = this.unitIdToTypeData.get(key); + if (unitTypeData == null) { + unitTypeData = new RenderUnitTypeData(row.getFieldAsFloat(MAX_PITCH, 0), row.getFieldAsFloat(MAX_ROLL, 0), + row.getFieldAsFloat(ELEVATION_SAMPLE_RADIUS, 0)); + this.unitIdToTypeData.put(key, unitTypeData); + } + return unitTypeData; + } + @Override public void update() { if (this.anyReady) { @@ -903,13 +942,15 @@ public class War3MapViewer extends ModelViewer { SequenceUtils.randomStandSequence(mdxComplexInstance); } } - for (final Doodad item : this.doodads) { + for (final RenderDoodad item : this.doodads) { final ModelInstance instance = item.instance; - if ((instance instanceof MdxComplexInstance) && (instance != this.confirmationInstance)) { + if (instance instanceof MdxComplexInstance) { final MdxComplexInstance mdxComplexInstance = (MdxComplexInstance) instance; if ((mdxComplexInstance.sequence == -1) - || (mdxComplexInstance.sequenceEnded && (((MdxModel) mdxComplexInstance.model).sequences - .get(mdxComplexInstance.sequence).getFlags() == 0))) { + || (mdxComplexInstance.sequenceEnded/* + * && (((MdxModel) mdxComplexInstance.model).sequences + * .get(mdxComplexInstance.sequence).getFlags() == 0) + */)) { SequenceUtils.randomSequence(mdxComplexInstance, item.getAnimation(), SequenceUtils.EMPTY, true); } @@ -1178,6 +1219,8 @@ public class War3MapViewer extends ModelViewer { private float selectionCircleScaleFactor; private DataTable worldEditData; private WorldEditStrings worldEditStrings; + private Warcraft3MapObjectData allObjectData; + private AbilityDataUI abilityDataUI; /** * Returns a power of two size for the given target capacity. @@ -1199,14 +1242,15 @@ public class War3MapViewer extends ModelViewer { for (final RenderUnit unit : this.selected) { for (final CAbility ability : unit.getSimulationUnit().getAbilities()) { if (ability instanceof CAbilityMove) { - ability.checkCanUse(this.simulation, unit.getSimulationUnit(), + ability.checkCanUse(this.simulation, unit.getSimulationUnit(), OrderIds.smart, BooleanAbilityActivationReceiver.INSTANCE); if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) { - ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), mousePosHeap, + ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap, PointAbilityTargetCheckReceiver.INSTANCE); final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget(); if (target != null) { - ability.onOrder(this.simulation, unit.getSimulationUnit(), mousePosHeap, false); + ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap, + false); ordered = true; } else { @@ -1231,14 +1275,15 @@ public class War3MapViewer extends ModelViewer { for (final RenderUnit unit : this.selected) { for (final CAbility ability : unit.getSimulationUnit().getAbilities()) { if (ability instanceof CAbilityAttack) { - ability.checkCanUse(this.simulation, unit.getSimulationUnit(), + ability.checkCanUse(this.simulation, unit.getSimulationUnit(), OrderIds.smart, BooleanAbilityActivationReceiver.INSTANCE); if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) { - ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), target.getSimulationUnit(), - CWidgetAbilityTargetCheckReceiver.INSTANCE); + ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), OrderIds.smart, + target.getSimulationUnit(), CWidgetAbilityTargetCheckReceiver.INSTANCE); final CWidget targetWidget = CWidgetAbilityTargetCheckReceiver.INSTANCE.getTarget(); if (targetWidget != null) { - ability.onOrder(this.simulation, unit.getSimulationUnit(), targetWidget, false); + ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, targetWidget, + false); ordered = true; } else { @@ -1308,7 +1353,7 @@ public class War3MapViewer extends ModelViewer { @Override public SceneLightManager createLightManager(final boolean simple) { if (simple) { - return new W3xScenePortraitLightManager(this); + return new W3xScenePortraitLightManager(this, this.lightDirection); } else { return new W3xSceneWorldLightManager(this); @@ -1321,12 +1366,14 @@ public class War3MapViewer extends ModelViewer { public void setGameUI(final GameUI gameUI) { this.gameUI = gameUI; - for (final RenderUnit unit : this.units) { - unit.initAbilityUI(this); - } + this.abilityDataUI = new AbilityDataUI(this.allObjectData.getAbilities(), gameUI); } public GameUI getGameUI() { return this.gameUI; } + + public AbilityDataUI getAbilityDataUI() { + return this.abilityDataUI; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraManager.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraManager.java new file mode 100644 index 0000000..dc84859 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraManager.java @@ -0,0 +1,62 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.camera; + +import com.badlogic.gdx.math.Quaternion; +import com.badlogic.gdx.math.Vector3; +import com.etheller.warsmash.viewer5.Camera; +import com.etheller.warsmash.viewer5.CanvasProvider; +import com.etheller.warsmash.viewer5.Scene; + +public abstract class CameraManager { + private static final double HORIZONTAL_ANGLE_INCREMENT = Math.PI / 60; + protected final float[] cameraPositionTemp = new float[3]; + protected final float[] cameraTargetTemp = new float[3]; + protected CanvasProvider canvas; + protected Camera camera; + protected float moveSpeed; + protected float rotationSpeed; + protected float zoomFactor; + protected float horizontalAngle; + protected float verticalAngle; + protected float distance; + protected Vector3 position; + public Vector3 target; + protected Vector3 worldUp; + protected Vector3 vecHeap; + protected Quaternion quatHeap; + protected Quaternion quatHeap2; + + public CameraManager() { + } + + // An orbit camera setup example. + // Left mouse button controls the orbit itself. + // The right mouse button allows to move the camera and the point it's looking + // at on the XY plane. + // Scrolling zooms in and out. + public void setupCamera(final Scene scene) { + this.canvas = scene.viewer.canvas; + this.camera = scene.camera; + this.moveSpeed = 2; + this.rotationSpeed = (float) HORIZONTAL_ANGLE_INCREMENT; + this.zoomFactor = 0.1f; + this.horizontalAngle = 0;// (float) (Math.PI / 2); + this.verticalAngle = (float) Math.toRadians(34); + this.distance = 1650; + this.position = new Vector3(); + this.target = new Vector3(0, 0, 0); + this.worldUp = new Vector3(0, 0, 1); + this.vecHeap = new Vector3(); + this.quatHeap = new Quaternion(); + this.quatHeap2 = new Quaternion(); + + updateCamera(); + +// cameraUpdate(); + } + + public abstract void updateCamera(); + +// private void cameraUpdate() { +// +// } +} \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPanControls.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPanControls.java new file mode 100644 index 0000000..964dbb9 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPanControls.java @@ -0,0 +1,10 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.camera; + +public final class CameraPanControls { + protected boolean down; + protected boolean up; + protected boolean left; + protected boolean right; + protected boolean insertDown; + protected boolean deleteDown; +} \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPreset.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPreset.java new file mode 100644 index 0000000..92ed43c --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraPreset.java @@ -0,0 +1,85 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.camera; + +public class CameraPreset { + private final float aoa; + private final float fov; + private final float rotation; + private final float rotationInsert; + private final float rotationDelete; + private final float distance; + private final float farZ; + private final float nearZ; + private final float height; + private final float listenerDistance; + private final float listenerAOA; + + public CameraPreset(final float aoa, final float fov, final float rotation, final float rotationInsert, + final float rotationDelete, final float distance, final float farZ, final float nearZ, final float height, + final float listenerDistance, final float listenerAOA) { + this.aoa = aoa; + this.fov = fov; + this.rotation = rotation; + this.rotationInsert = rotationInsert; + this.rotationDelete = rotationDelete; + this.distance = distance; + this.farZ = farZ; + this.nearZ = nearZ; + this.height = height; + this.listenerDistance = listenerDistance; + this.listenerAOA = listenerAOA; + } + + public float getRotation(final boolean insertDown, final boolean deleteDown) { + if (insertDown && !deleteDown) { + return this.rotationInsert; + } + if (!insertDown && deleteDown) { + return this.rotationDelete; + } + return this.rotation; + } + + public float getHeight() { + return this.height; + } + + public float getAoa() { + return this.aoa; + } + + public float getFov() { + return this.fov; + } + + public float getRotation() { + return this.rotation; + } + + public float getRotationInsert() { + return this.rotationInsert; + } + + public float getRotationDelete() { + return this.rotationDelete; + } + + public float getDistance() { + return this.distance; + } + + public float getFarZ() { + return this.farZ; + } + + public float getNearZ() { + return this.nearZ; + } + + public float getListenerDistance() { + return this.listenerDistance; + } + + public float getListenerAOA() { + return this.listenerAOA; + } +} \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraRates.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraRates.java new file mode 100644 index 0000000..430d4e8 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/CameraRates.java @@ -0,0 +1,20 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.camera; + +public class CameraRates { + public final float aoa; + public final float fov; + public final float rotation; + public final float distance; + public final float forward; + public final float strafe; + + public CameraRates(final float aoa, final float fov, final float rotation, final float distance, + final float forward, final float strafe) { + this.aoa = aoa; + this.fov = fov; + this.rotation = rotation; + this.distance = distance; + this.forward = forward; + this.strafe = strafe; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java new file mode 100644 index 0000000..013b276 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/GameCameraManager.java @@ -0,0 +1,154 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.camera; + +import com.badlogic.gdx.Input; +import com.badlogic.gdx.math.Rectangle; + +public final class GameCameraManager extends CameraManager { + private final CameraPreset[] presets; + private final CameraRates cameraRates; + public final CameraPanControls cameraPanControls; + private int currentPreset = 0; + + public GameCameraManager(final CameraPreset[] presets, final CameraRates cameraRates) { + this.presets = presets; + this.cameraRates = cameraRates; + this.cameraPanControls = new CameraPanControls(); + } + + @Override + public void updateCamera() { + this.quatHeap2.idt(); + final CameraPreset cameraPreset = this.presets[this.currentPreset]; + this.quatHeap.idt(); + this.horizontalAngle = (float) Math.toRadians( + cameraPreset.getRotation(this.cameraPanControls.insertDown, this.cameraPanControls.deleteDown) - 90); + this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle); + this.distance = Math.max(1200, cameraPreset.getDistance()); + this.verticalAngle = (float) Math.toRadians(Math.min(335, cameraPreset.getAoa()) - 270); + this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle); + this.quatHeap.mul(this.quatHeap2); + + this.position.set(0, 0, 1); + this.quatHeap.transform(this.position); + this.position.nor(); + this.position.scl(this.distance); + this.position = this.position.add(this.target); + this.camera.perspective((float) Math.toRadians(cameraPreset.getFov() / 2), this.camera.getAspect(), + cameraPreset.getNearZ(), cameraPreset.getFarZ()); + + this.camera.moveToAndFace(this.position, this.target, this.worldUp); + } + + public void resize(final Rectangle viewport) { + this.camera.viewport(viewport); + } + + public void applyVelocity(final float deltaTime, boolean up, boolean down, boolean left, boolean right) { + final float velocityX; + final float velocityY; + up |= this.cameraPanControls.up; + down |= this.cameraPanControls.down; + left |= this.cameraPanControls.left; + right |= this.cameraPanControls.right; + if (up) { + if (down) { + velocityY = 0; + } + else { + velocityY = this.cameraRates.forward; + } + } + else if (down) { + velocityY = -this.cameraRates.forward; + } + else { + velocityY = 0; + } + if (right) { + if (left) { + velocityX = 0; + } + else { + velocityX = this.cameraRates.strafe; + } + } + else if (left) { + velocityX = -this.cameraRates.strafe; + } + else { + velocityX = 0; + } + this.target.add(velocityX * deltaTime, velocityY * deltaTime, 0); + + } + + public void updateTargetZ(final float groundHeight) { + this.target.z = groundHeight + this.presets[this.currentPreset].getHeight(); + } + + public void scrolled(final int amount) { + this.currentPreset -= amount; + if (this.currentPreset < 0) { + this.currentPreset = 0; + } + if (this.currentPreset >= this.presets.length) { + this.currentPreset = this.presets.length - 1; + } + } + + public boolean keyDown(final int keycode) { + if (keycode == Input.Keys.LEFT) { + this.cameraPanControls.left = true; + return true; + } + else if (keycode == Input.Keys.RIGHT) { + this.cameraPanControls.right = true; + return true; + } + else if (keycode == Input.Keys.DOWN) { + this.cameraPanControls.down = true; + return true; + } + else if (keycode == Input.Keys.UP) { + this.cameraPanControls.up = true; + return true; + } + else if (keycode == Input.Keys.INSERT) { + this.cameraPanControls.insertDown = true; + return true; + } + else if (keycode == Input.Keys.FORWARD_DEL) { + this.cameraPanControls.deleteDown = true; + return true; + } + return false; + } + + public boolean keyUp(final int keycode) { + if (keycode == Input.Keys.LEFT) { + this.cameraPanControls.left = false; + return true; + } + else if (keycode == Input.Keys.RIGHT) { + this.cameraPanControls.right = false; + return true; + } + else if (keycode == Input.Keys.DOWN) { + this.cameraPanControls.down = false; + return true; + } + else if (keycode == Input.Keys.UP) { + this.cameraPanControls.up = false; + return true; + } + else if (keycode == Input.Keys.INSERT) { + this.cameraPanControls.insertDown = false; + return true; + } + else if (keycode == Input.Keys.FORWARD_DEL) { + this.cameraPanControls.deleteDown = false; + return true; + } + return false; + } +} \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/PortraitCameraManager.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/PortraitCameraManager.java new file mode 100644 index 0000000..4ecf106 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/camera/PortraitCameraManager.java @@ -0,0 +1,52 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.camera; + +import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; +import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; + +public final class PortraitCameraManager extends CameraManager { + public com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera; + protected MdxComplexInstance modelInstance; + + @Override + public void updateCamera() { + this.quatHeap.idt(); + this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle); + this.quatHeap2.idt(); + this.quatHeap2.setFromAxisRad(1, 0, 0, this.verticalAngle); + this.quatHeap.mul(this.quatHeap2); + + this.position.set(0, 0, 1); + this.quatHeap.transform(this.position); + this.position.scl(this.distance); + this.position = this.position.add(this.target); + if (this.modelCamera != null) { + this.modelCamera.getPositionTranslation(this.cameraPositionTemp, this.modelInstance.sequence, + this.modelInstance.frame, this.modelInstance.counter); + this.modelCamera.getTargetTranslation(this.cameraTargetTemp, this.modelInstance.sequence, + this.modelInstance.frame, this.modelInstance.counter); + + this.position.set(this.modelCamera.position); + this.target.set(this.modelCamera.targetPosition); + + this.position.add(this.cameraPositionTemp[0], this.cameraPositionTemp[1], this.cameraPositionTemp[2]); + this.target.add(this.cameraTargetTemp[0], this.cameraTargetTemp[1], this.cameraTargetTemp[2]); + this.camera.perspective(this.modelCamera.fieldOfView * 0.75f, this.camera.getAspect(), + this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane); + } + else { + this.camera.perspective(70, this.camera.getAspect(), 100, 5000); + } + + this.camera.moveToAndFace(this.position, this.target, this.worldUp); + } + + public void setModelInstance(final MdxComplexInstance modelInstance, final MdxModel portraitModel) { + this.modelInstance = modelInstance; + if (modelInstance == null) { + this.modelCamera = null; + } + else if ((portraitModel != null) && (portraitModel.getCameras().size() > 0)) { + this.modelCamera = portraitModel.getCameras().get(0); + } + } +} \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java index e9a17f2..08edfec 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java @@ -890,7 +890,8 @@ public class Terrain { unitLightsTexture.bind(21); gl.glUniform1i(this.groundShader.getUniformLocation("lightTexture"), 21); - gl.glUniform1f(this.groundShader.getUniformLocation("lightCount"), unitLightsTexture.getHeight() - 0.5f); + gl.glUniform1f(this.groundShader.getUniformLocation("lightCount"), lightManager.getTerrainLightCount()); + gl.glUniform1f(this.groundShader.getUniformLocation("lightTextureHeight"), unitLightsTexture.getHeight()); gl.glUniformMatrix4fv(this.groundShader.getUniformLocation("DepthBiasMVP"), 1, false, dynamicShadowManager.getDepthBiasMVP().val, 0); @@ -989,8 +990,9 @@ public class Terrain { final DataTexture terrainLightsTexture = lightManager.getTerrainLightsTexture(); terrainLightsTexture.bind(21); - gl.glUniform1i(shader.getUniformLocation("lightTexture"), 21); - gl.glUniform1f(shader.getUniformLocation("lightCount"), terrainLightsTexture.getHeight() - 0.5f); + gl.glUniform1i(shader.getUniformLocation("u_lightTexture"), 21); + gl.glUniform1f(shader.getUniformLocation("u_lightCount"), lightManager.getTerrainLightCount()); + gl.glUniform1f(shader.getUniformLocation("u_lightTextureHeight"), terrainLightsTexture.getHeight()); // Render the cliffs for (final SplatModel splat : this.uberSplatModels) { @@ -1024,7 +1026,8 @@ public class Terrain { unitLightsTexture.bind(21); gl.glUniform1i(this.waterShader.getUniformLocation("lightTexture"), 21); - gl.glUniform1f(this.waterShader.getUniformLocation("lightCount"), unitLightsTexture.getHeight() - 0.5f); + gl.glUniform1f(this.waterShader.getUniformLocation("lightCount"), lightManager.getTerrainLightCount()); + gl.glUniform1f(this.waterShader.getUniformLocation("lightCountHeight"), unitLightsTexture.getHeight()); gl.glActiveTexture(GL30.GL_TEXTURE0); gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterHeight); @@ -1080,7 +1083,8 @@ public class Terrain { unitLightsTexture.bind(21); gl.glUniform1i(this.cliffShader.getUniformLocation("lightTexture"), 21); - gl.glUniform1f(this.cliffShader.getUniformLocation("lightCount"), unitLightsTexture.getHeight() - 0.5f); + gl.glUniform1f(this.cliffShader.getUniformLocation("lightCount"), lightManager.getTerrainLightCount()); + gl.glUniform1f(this.cliffShader.getUniformLocation("lightTextureHeight"), unitLightsTexture.getHeight()); this.cliffShader.setUniformi("shadowMap", 2); gl.glActiveTexture(GL30.GL_TEXTURE2); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java index 43897a2..3771d07 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/TerrainShaders.java @@ -25,6 +25,7 @@ public class TerrainShaders { "layout (location = 4) uniform float centerOffsetY;\r\n" + // "layout (location = 5) uniform sampler2D lightTexture;\r\n" + // "layout (location = 6) uniform float lightCount;\r\n" + // + "layout (location = 7) uniform float lightTextureHeight;\r\n" + // "\r\n" + // "layout (location = 0) out vec3 UV;\r\n" + // "layout (location = 1) out vec3 Normal;\r\n" + // @@ -60,7 +61,9 @@ public class TerrainShaders { " vec3 terrain_normal = normalize(vNormal);//vec3(hL - hR, hD - hU, 2.0)+vNormal);\r\n" + // "\r\n" + // " Normal = terrain_normal;\r\n" + // - Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightCount", true) + "\r\n" + // + Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightTextureHeight", "lightCount", + true) + + "\r\n" + // " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // "}"; @@ -135,6 +138,7 @@ public class TerrainShaders { "layout (location = 5) uniform float centerOffsetY;\r\n" + // "layout (location = 7) uniform sampler2D lightTexture;\r\n" + // "layout (location = 8) uniform float lightCount;\r\n" + // + "layout (location = 9) uniform float lightTextureHeight;\r\n" + // "\r\n" + // "layout (location = 0) out vec2 UV;\r\n" + // "layout (location = 1) out flat uvec4 texture_indices;\r\n" + // @@ -173,7 +177,8 @@ public class TerrainShaders { " v_suv = (vPosition + pos) / size;\r\n" + // " position.x = (position.x - centerOffsetX) / (size.x * 128.0);\r\n" + // " position.y = (position.y - centerOffsetY) / (size.y * 128.0);\r\n" + // - Shaders.lightSystem("normal", "positionWorld", "lightTexture", "lightCount", true) + "\r\n" + // + Shaders.lightSystem("normal", "positionWorld", "lightTexture", "lightTextureHeight", "lightCount", true) + + "\r\n" + // " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // "}"; @@ -425,6 +430,7 @@ public class TerrainShaders { "layout (location = 5) uniform float water_offset;\r\n" + // "layout (location = 10) uniform sampler2D lightTexture;\r\n" + // "layout (location = 11) uniform float lightCount;\r\n" + // + "layout (location = 12) uniform float lightTextureHeight;\r\n" + // "\r\n" + // "out vec2 UV;\r\n" + // "out vec4 Color;\r\n" + // @@ -463,7 +469,9 @@ public class TerrainShaders { " value = clamp(value - deeplevel, 0.f, maxdepth - deeplevel) / (maxdepth - deeplevel);\r\n" + // " Color = deep_color_min * (1.f - value) + deep_color_max * value;\r\n" + // " }\r\n" + // - Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightCount", true) + "\r\n" + // + Shaders.lightSystem("Normal", "myposition.xyz", "lightTexture", "lightTextureHeight", "lightCount", + true) + + "\r\n" + // " shadeColor = clamp(lightFactor, 0.0, 1.0);\r\n" + // " }"; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/CommandCardIcon.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/CommandCardIcon.java deleted file mode 100644 index 4d702a6..0000000 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/CommandCardIcon.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.etheller.warsmash.viewer5.handlers.w3x.rendersim; - -import com.badlogic.gdx.graphics.Texture; - -public class CommandCardIcon { - private final int x; - private final int y; - private final Texture texture; - private final int orderId; - - public CommandCardIcon(final int x, final int y, final Texture texture, final int orderId) { - this.x = x; - this.y = y; - this.texture = texture; - this.orderId = orderId; - } - - public int getX() { - return this.x; - } - - public int getY() { - return this.y; - } - - public Texture getTexture() { - return this.texture; - } - - public int getOrderId() { - return this.orderId; - } -} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java index 5812b73..8fc6aed 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java @@ -1,18 +1,13 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim; -import java.util.ArrayList; import java.util.EnumSet; import java.util.LinkedList; -import java.util.List; import java.util.Queue; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.math.Quaternion; -import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.mdlx.Sequence; -import com.etheller.warsmash.units.Element; import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; -import com.etheller.warsmash.util.ImageUtils; import com.etheller.warsmash.util.RenderMathUtils; import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; @@ -26,22 +21,14 @@ import com.etheller.warsmash.viewer5.handlers.w3x.UnitSoundset; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid; import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.MovementType; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButtonListener; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandCardPopulatingAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitAnimationListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityHoldPosition; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityPatrol; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityStop; public class RenderUnit { - private static final War3ID ABILITY_MOVE = War3ID.fromString("Amov"); - private static final War3ID ABILITY_STOP = War3ID.fromString("Astp"); - private static final War3ID ABILITY_HOLD_POSITION = War3ID.fromString("Ahol"); - private static final War3ID ABILITY_PATROL = War3ID.fromString("Apat"); - private static final War3ID ABILITY_ATTACK = War3ID.fromString("Aatk"); - private static final Quaternion tempQuat = new Quaternion(); private static final War3ID RED = War3ID.fromString("uclr"); private static final War3ID GREEN = War3ID.fromString("uclg"); @@ -61,7 +48,6 @@ public class RenderUnit { private final CUnit simulationUnit; public SplatMover shadow; public SplatMover selectionCircle; - private final List commandCardIcons = new ArrayList<>(); private float x; private float y; @@ -77,12 +63,14 @@ public class RenderUnit { public long lastUnitResponseEndTimeMillis; private boolean corpse; private boolean boneCorpse; + private final RenderUnitTypeData typeData; public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit, final UnitSoundset soundset, - final MdxModel portraitModel, final CUnit simulationUnit) { + final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData) { this.portraitModel = portraitModel; this.simulationUnit = simulationUnit; + this.typeData = typeData; final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance(); final float[] location = unit.getLocation(); @@ -143,43 +131,10 @@ public class RenderUnit { } - public void initAbilityUI(final War3MapViewer map) { + public void populateCommandCard(final CommandButtonListener commandButtonListener, + final AbilityDataUI abilityDataUI) { for (final CAbility ability : this.simulationUnit.getAbilities()) { - if (ability instanceof CAbilityMove) { - final GameUI gameUI = map.getGameUI(); - final Element moveCommand = gameUI.getSkinData().get("CmdMove"); - final String moveCommandArt = gameUI.getSkinField(moveCommand.getField("Art")); - this.commandCardIcons.add(new CommandCardIcon(0, 0, - ImageUtils.getBLPTexture(map.dataSource, moveCommandArt), ability.getOrderId())); - } - else if (ability instanceof CAbilityAttack) { - final GameUI gameUI = map.getGameUI(); - final Element command = gameUI.getSkinData().get("CmdAttack"); - final String commandArt = gameUI.getSkinField(command.getField("Art")); - this.commandCardIcons.add(new CommandCardIcon(3, 0, - ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId())); - } - else if (ability instanceof CAbilityHoldPosition) { - final GameUI gameUI = map.getGameUI(); - final Element command = gameUI.getSkinData().get("CmdHoldPos"); - final String commandArt = gameUI.getSkinField(command.getField("Art")); - this.commandCardIcons.add(new CommandCardIcon(2, 0, - ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId())); - } - else if (ability instanceof CAbilityPatrol) { - final GameUI gameUI = map.getGameUI(); - final Element command = gameUI.getSkinData().get("CmdPatrol"); - final String commandArt = gameUI.getSkinField(command.getField("Art")); - this.commandCardIcons.add(new CommandCardIcon(0, 1, - ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId())); - } - else if (ability instanceof CAbilityStop) { - final GameUI gameUI = map.getGameUI(); - final Element command = gameUI.getSkinData().get("CmdStop"); - final String commandArt = gameUI.getSkinField(command.getField("Art")); - this.commandCardIcons.add(new CommandCardIcon(1, 0, - ImageUtils.getBLPTexture(map.dataSource, commandArt), ability.getOrderId())); - } + ability.visit(CommandCardPopulatingAbilityVisitor.INSTANCE.reset(commandButtonListener, abilityDataUI)); } } @@ -298,6 +253,32 @@ public class RenderUnit { } this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360; this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing)); + + final float facingRadians = (float) Math.toRadians(this.facing); + final float maxPitch = this.typeData.getMaxPitch(); + final float maxRoll = this.typeData.getMaxRoll(); + final float sampleRadius = this.typeData.getElevationSampleRadius(); + float pitch, roll; + final float pitchSampleForwardX = x + (sampleRadius * (float) Math.cos(facingRadians)); + final float pitchSampleForwardY = y + (sampleRadius * (float) Math.sin(facingRadians)); + final float pitchSampleBackwardX = x - (sampleRadius * (float) Math.cos(facingRadians)); + final float pitchSampleBackwardY = y - (sampleRadius * (float) Math.sin(facingRadians)); + final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY); + final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY); + pitch = Math.max(-maxPitch, Math.min(maxPitch, + (float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, sampleRadius * 2))); + final double leftOfFacingAngle = facingRadians + (Math.PI / 2); + final float rollSampleForwardX = x + (sampleRadius * (float) Math.cos(leftOfFacingAngle)); + final float rollSampleForwardY = y + (sampleRadius * (float) Math.sin(leftOfFacingAngle)); + final float rollSampleBackwardX = x - (sampleRadius * (float) Math.cos(leftOfFacingAngle)); + final float rollSampleBackwardY = y - (sampleRadius * (float) Math.sin(leftOfFacingAngle)); + final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY); + final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY); + roll = Math.max(-maxRoll, Math.min(maxRoll, + (float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, sampleRadius * 2))); + this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch)); + this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll)); + map.worldScene.instanceMoved(this.instance, this.location[0], this.location[1]); if (this.shadow != null) { this.shadow.move(dx, dy, map.terrain.centerOffset); @@ -312,10 +293,6 @@ public class RenderUnit { return this.simulationUnit; } - public List getCommandCardIcons() { - return this.commandCardIcons; - } - private static final class UnitAnimationListenerImpl implements CUnitAnimationListener { private final MdxComplexInstance instance; private final EnumSet secondaryAnimationTags = EnumSet diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java new file mode 100644 index 0000000..649ae3b --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java @@ -0,0 +1,25 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim; + +public class RenderUnitTypeData { + private final float maxPitch; + private final float maxRoll; + private final float sampleRadius; + + public RenderUnitTypeData(final float maxPitch, final float maxRoll, final float sampleRadius) { + this.maxPitch = maxPitch; + this.maxRoll = maxRoll; + this.sampleRadius = sampleRadius; + } + + public float getMaxPitch() { + return this.maxPitch; + } + + public float getMaxRoll() { + return this.maxRoll; + } + + public float getElevationSampleRadius() { + return this.sampleRadius; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java new file mode 100644 index 0000000..9871acf --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java @@ -0,0 +1,112 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability; + +import java.util.HashMap; +import java.util.Map; + +import com.badlogic.gdx.graphics.Texture; +import com.etheller.warsmash.parsers.fdf.GameUI; +import com.etheller.warsmash.units.Element; +import com.etheller.warsmash.units.manager.MutableObjectData; +import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; +import com.etheller.warsmash.util.War3ID; + +public class AbilityDataUI { + // Standard ability icon fields + private static final War3ID ICON_NORMAL_X = War3ID.fromString("abpx"); + private static final War3ID ICON_NORMAL_Y = War3ID.fromString("abpy"); + private static final War3ID ICON_NORMAL = War3ID.fromString("aart"); + private static final War3ID ICON_TURN_OFF = War3ID.fromString("auar"); + private static final War3ID ICON_TURN_OFF_X = War3ID.fromString("aubx"); + private static final War3ID ICON_TURN_OFF_Y = War3ID.fromString("auby"); + private static final War3ID ICON_RESEARCH = War3ID.fromString("arar"); + private static final War3ID ICON_RESEARCH_X = War3ID.fromString("arpx"); + private static final War3ID ICON_RESEARCH_Y = War3ID.fromString("arpy"); + + private final Map rawcodeToUI = new HashMap<>(); + private final IconUI moveUI; + private final IconUI stopUI; + private final IconUI holdPosUI; + private final IconUI patrolUI; + private final IconUI attackUI; + private final IconUI attackGroundUI; + + public AbilityDataUI(final MutableObjectData abilityData, final GameUI gameUI) { + final String disabledPrefix = gameUI.getSkinField("CommandButtonDisabledArtPath"); + for (final War3ID alias : abilityData.keySet()) { + final MutableGameObject abilityTypeData = abilityData.get(alias); + final String iconResearchPath = gameUI.trySkinField(abilityTypeData.getFieldAsString(ICON_RESEARCH, 0)); + final String iconNormalPath = gameUI.trySkinField(abilityTypeData.getFieldAsString(ICON_NORMAL, 0)); + final String iconTurnOffPath = gameUI.trySkinField(abilityTypeData.getFieldAsString(ICON_TURN_OFF, 0)); + final int iconResearchX = abilityTypeData.getFieldAsInteger(ICON_RESEARCH_X, 0); + final int iconResearchY = abilityTypeData.getFieldAsInteger(ICON_RESEARCH_Y, 0); + final int iconNormalX = abilityTypeData.getFieldAsInteger(ICON_NORMAL_X, 0); + final int iconNormalY = abilityTypeData.getFieldAsInteger(ICON_NORMAL_Y, 0); + final int iconTurnOffX = abilityTypeData.getFieldAsInteger(ICON_TURN_OFF_X, 0); + final int iconTurnOffY = abilityTypeData.getFieldAsInteger(ICON_TURN_OFF_Y, 0); + final Texture iconResearch = gameUI.loadTexture(iconResearchPath); + final Texture iconResearchDisabled = gameUI.loadTexture(disable(iconResearchPath, disabledPrefix)); + final Texture iconNormal = gameUI.loadTexture(iconNormalPath); + final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix)); + final Texture iconTurnOff = gameUI.loadTexture(iconTurnOffPath); + final Texture iconTurnOffDisabled = gameUI.loadTexture(disable(iconTurnOffPath, disabledPrefix)); + this.rawcodeToUI.put(alias, + new AbilityIconUI(new IconUI(iconResearch, iconResearchDisabled, iconResearchX, iconResearchY), + new IconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY), + new IconUI(iconTurnOff, iconTurnOffDisabled, iconTurnOffX, iconTurnOffY))); + } + this.moveUI = createBuiltInIconUI(gameUI, "CmdMove", disabledPrefix); + this.stopUI = createBuiltInIconUI(gameUI, "CmdStop", disabledPrefix); + this.holdPosUI = createBuiltInIconUI(gameUI, "CmdHoldPos", disabledPrefix); + this.patrolUI = createBuiltInIconUI(gameUI, "CmdPatrol", disabledPrefix); + this.attackUI = createBuiltInIconUI(gameUI, "CmdAttack", disabledPrefix); + this.attackGroundUI = createBuiltInIconUI(gameUI, "CmdAttackGround", disabledPrefix); + } + + private IconUI createBuiltInIconUI(final GameUI gameUI, final String key, final String disabledPrefix) { + final Element builtInAbility = gameUI.getSkinData().get(key); + final String iconPath = gameUI.trySkinField(builtInAbility.getField("Art")); + final Texture icon = gameUI.loadTexture(iconPath); + final Texture iconDisabled = gameUI.loadTexture(disable(iconPath, disabledPrefix)); + final int buttonPositionX = builtInAbility.getFieldValue("Buttonpos", 0); + final int buttonPositionY = builtInAbility.getFieldValue("Buttonpos", 1); + return new IconUI(icon, iconDisabled, buttonPositionX, buttonPositionY); + } + + public AbilityIconUI getUI(final War3ID rawcode) { + return this.rawcodeToUI.get(rawcode); + } + + private static String disable(final String path, final String disabledPrefix) { + final int slashIndex = path.lastIndexOf('\\'); + String name = path; + if (slashIndex != -1) { + name = path.substring(slashIndex + 1); + } + return disabledPrefix + name; + } + + public IconUI getMoveUI() { + return this.moveUI; + } + + public IconUI getStopUI() { + return this.stopUI; + } + + public IconUI getHoldPosUI() { + return this.holdPosUI; + } + + public IconUI getPatrolUI() { + return this.patrolUI; + } + + public IconUI getAttackUI() { + return this.attackUI; + } + + public IconUI getAttackGroundUI() { + return this.attackGroundUI; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityIconUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityIconUI.java new file mode 100644 index 0000000..d75e395 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityIconUI.java @@ -0,0 +1,25 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability; + +public class AbilityIconUI { + private final IconUI learnIconUI; + private final IconUI onIconUI; + private final IconUI offIconUI; + + public AbilityIconUI(final IconUI learnIconUI, final IconUI onIconUI, final IconUI offIconUI) { + this.learnIconUI = learnIconUI; + this.onIconUI = onIconUI; + this.offIconUI = offIconUI; + } + + public IconUI getLearnIconUI() { + return this.learnIconUI; + } + + public IconUI getOnIconUI() { + return this.onIconUI; + } + + public IconUI getOffIconUI() { + return this.offIconUI; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/IconUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/IconUI.java new file mode 100644 index 0000000..1cfbdb3 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/IconUI.java @@ -0,0 +1,34 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability; + +import com.badlogic.gdx.graphics.Texture; + +public class IconUI { + private final Texture icon; + private final Texture iconDisabled; + private final int buttonPositionX; + private final int buttonPositionY; + + public IconUI(final Texture icon, final Texture iconDisabled, final int buttonPositionX, + final int buttonPositionY) { + this.icon = icon; + this.iconDisabled = iconDisabled; + this.buttonPositionX = buttonPositionX; + this.buttonPositionY = buttonPositionY; + } + + public Texture getIcon() { + return this.icon; + } + + public Texture getIconDisabled() { + return this.iconDisabled; + } + + public int getButtonPositionX() { + return this.buttonPositionX; + } + + public int getButtonPositionY() { + return this.buttonPositionY; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/AbilityCommandButton.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/AbilityCommandButton.java new file mode 100644 index 0000000..82b19aa --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/AbilityCommandButton.java @@ -0,0 +1,94 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons; + +import com.badlogic.gdx.graphics.Texture; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityIconUI; + +public class AbilityCommandButton implements CommandButton { + private final AbilityIconUI abilityIconUI; + private final int orderId; + + public AbilityCommandButton(final AbilityIconUI abilityIconUI, final int orderId) { + this.abilityIconUI = abilityIconUI; + this.orderId = orderId; + } + + @Override + public String getToolTip() { + return null; + } + + @Override + public String getUberTip() { + return null; + } + + @Override + public int getLumberCost() { + return 0; + } + + @Override + public int getGoldCost() { + return 0; + } + + @Override + public int getManaCost() { + return 0; + } + + @Override + public int getFoodCost() { + return 0; + } + + @Override + public Texture getIcon() { + return this.abilityIconUI.getOnIconUI().getIcon(); + } + + @Override + public Texture getDisabledIcon() { + return this.abilityIconUI.getOnIconUI().getIconDisabled(); + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public float getCooldown() { + return 0; + } + + @Override + public float getCooldownRemaining() { + return 0; + } + + @Override + public boolean isAutoCastCapable() { + return false; + } + + @Override + public boolean isAutoCastActive() { + return false; + } + + @Override + public int getButtonPositionX() { + return this.abilityIconUI.getOnIconUI().getButtonPositionX(); + } + + @Override + public int getButtonPositionY() { + return this.abilityIconUI.getOnIconUI().getButtonPositionY(); + } + + @Override + public int getOrderId() { + return this.orderId; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/BasicCommandButton.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/BasicCommandButton.java new file mode 100644 index 0000000..c9cc906 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/BasicCommandButton.java @@ -0,0 +1,95 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons; + +import com.badlogic.gdx.graphics.Texture; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI; + +public class BasicCommandButton implements CommandButton { + private final IconUI iconUI; + private final int orderId; + + public BasicCommandButton(final IconUI iconUI, final int orderId) { + this.iconUI = iconUI; + this.orderId = orderId; + } + + @Override + public String getToolTip() { + return null; + } + + @Override + public String getUberTip() { + return null; + } + + @Override + public int getLumberCost() { + return 0; + } + + @Override + public int getGoldCost() { + return 0; + } + + @Override + public int getManaCost() { + return 0; + } + + @Override + public int getFoodCost() { + return 0; + } + + @Override + public Texture getIcon() { + return this.iconUI.getIcon(); + } + + @Override + public Texture getDisabledIcon() { + return this.iconUI.getIconDisabled(); + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public float getCooldown() { + return 0; + } + + @Override + public float getCooldownRemaining() { + return 0; + } + + @Override + public boolean isAutoCastCapable() { + return false; + } + + @Override + public boolean isAutoCastActive() { + return false; + } + + @Override + public int getButtonPositionX() { + return this.iconUI.getButtonPositionX(); + } + + @Override + public int getButtonPositionY() { + return this.iconUI.getButtonPositionY(); + } + + @Override + public int getOrderId() { + return this.orderId; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButton.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButton.java new file mode 100644 index 0000000..af4e9c9 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButton.java @@ -0,0 +1,38 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons; + +import com.badlogic.gdx.graphics.Texture; + +public interface CommandButton { + String getToolTip(); + + String getUberTip(); + + int getLumberCost(); + + int getGoldCost(); + + int getManaCost(); + + int getFoodCost(); + + Texture getIcon(); + + Texture getDisabledIcon(); + + boolean isEnabled(); + + float getCooldown(); + + float getCooldownRemaining(); + + boolean isAutoCastCapable(); + + boolean isAutoCastActive(); + + int getButtonPositionX(); + + int getButtonPositionY(); + + int getOrderId(); + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButtonListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButtonListener.java new file mode 100644 index 0000000..04c2414 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandButtonListener.java @@ -0,0 +1,38 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons; + +import com.badlogic.gdx.graphics.Texture; + +public interface CommandButtonListener { +// String getToolTip(); +// +// String getUberTip(); +// +// int getLumberCost(); +// +// int getGoldCost(); +// +// int getManaCost(); +// +// int getFoodCost(); +// +// Texture getIcon(); +// +// Texture getDisabledIcon(); +// +// boolean isEnabled(); +// +// float getCooldown(); +// +// float getCooldownRemaining(); +// +// boolean isAutoCastCapable(); +// +// boolean isAutoCastActive(); +// +// int getButtonPositionX(); +// +// int getButtonPositionY(); +// +// int getOrderId(); + void commandButton(int buttonPositionX, int buttonPositionY, Texture icon, int orderId); +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java new file mode 100644 index 0000000..a919955 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java @@ -0,0 +1,51 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons; + +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; + +public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor { + public static final CommandCardPopulatingAbilityVisitor INSTANCE = new CommandCardPopulatingAbilityVisitor(); + + private CommandButtonListener commandButtonListener; + private AbilityDataUI abilityDataUI; + private boolean hasStop; + + public CommandCardPopulatingAbilityVisitor reset(final CommandButtonListener commandButtonListener, + final AbilityDataUI abilityDataUI) { + this.commandButtonListener = commandButtonListener; + this.abilityDataUI = abilityDataUI; + this.hasStop = false; + return this; + } + + @Override + public Void accept(final CAbilityAttack ability) { + addCommandButton(this.abilityDataUI.getAttackUI(), ability.getHandleId(), OrderIds.attack); + if (!this.hasStop) { + this.hasStop = true; + addCommandButton(this.abilityDataUI.getStopUI(), -1, OrderIds.stop); + } + return null; + } + + @Override + public Void accept(final CAbilityMove ability) { + addCommandButton(this.abilityDataUI.getMoveUI(), ability.getHandleId(), OrderIds.move); + addCommandButton(this.abilityDataUI.getHoldPosUI(), ability.getHandleId(), OrderIds.holdposition); + addCommandButton(this.abilityDataUI.getPatrolUI(), ability.getHandleId(), OrderIds.patrol); + if (!this.hasStop) { + this.hasStop = true; + addCommandButton(this.abilityDataUI.getStopUI(), -1, OrderIds.stop); + } + return null; + } + + private void addCommandButton(final IconUI iconUI, final int handleId, final int orderId) { + this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(), + iconUI.getIcon(), orderId); + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java index 3e95c5e..5d214c3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java @@ -20,6 +20,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.C import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CAbilityData; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CUnitData; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindingProcessor; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CMapControl; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace; @@ -59,15 +60,17 @@ public class CSimulation { this.pathfindingProcessor = new CPathfindingProcessor(pathingGrid, this.worldCollision); this.seededRandom = seededRandom; this.players = new ArrayList<>(); - for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { + for (int i = 0; i < (WarsmashConstants.MAX_PLAYERS - 4); i++) { + CPlayer newPlayer; if (i < playerInfos.size()) { final Player playerInfo = playerInfos.get(i); - this.players.add(new CPlayer(playerInfo.getId(), CMapControl.values()[playerInfo.getType()], - playerInfo.getName(), CRace.parseRace(playerInfo.getRace()), playerInfo.getStartLocation())); + newPlayer = new CPlayer(playerInfo.getId(), CMapControl.values()[playerInfo.getType()], + playerInfo.getName(), CRace.parseRace(playerInfo.getRace()), playerInfo.getStartLocation()); } else { - this.players.add(new CPlayer(i, CMapControl.NONE, "Default string", CRace.OTHER, new float[] { 0, 0 })); + newPlayer = new CPlayer(i, CMapControl.NONE, "Default string", CRace.OTHER, new float[] { 0, 0 }); } + this.players.add(newPlayer); } this.players.add(new CPlayer(this.players.size(), CMapControl.NEUTRAL, miscData.getLocalizedString("WESTRING_PLAYER_NA"), CRace.OTHER, new float[] { 0, 0 })); @@ -75,8 +78,14 @@ public class CSimulation { miscData.getLocalizedString("WESTRING_PLAYER_NV"), CRace.OTHER, new float[] { 0, 0 })); this.players.add(new CPlayer(this.players.size(), CMapControl.NEUTRAL, miscData.getLocalizedString("WESTRING_PLAYER_NE"), CRace.OTHER, new float[] { 0, 0 })); - this.players.add(new CPlayer(this.players.size(), CMapControl.NEUTRAL, - miscData.getLocalizedString("WESTRING_PLAYER_NP"), CRace.OTHER, new float[] { 0, 0 })); + final CPlayer neutralPassive = new CPlayer(this.players.size(), CMapControl.NEUTRAL, + miscData.getLocalizedString("WESTRING_PLAYER_NP"), CRace.OTHER, new float[] { 0, 0 }); + this.players.add(neutralPassive); + for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { + final CPlayer cPlayer = this.players.get(i); + cPlayer.setAlliance(neutralPassive, CAllianceType.PASSIVE, true); + neutralPassive.setAlliance(cPlayer, CAllianceType.PASSIVE, true); + } } @@ -94,8 +103,8 @@ public class CSimulation { public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y, final float facing, final BufferedImage buildingPathingPixelMap) { - final CUnit unit = this.unitData.create(this, playerIndex, this.handleIdAllocator.createId(), typeId, x, y, - facing, buildingPathingPixelMap); + final CUnit unit = this.unitData.create(this, playerIndex, typeId, x, y, facing, buildingPathingPixelMap, + this.simulationRenderController, this.handleIdAllocator); this.units.add(unit); this.worldCollision.addUnit(unit); return unit; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java index 9f6f623..a5a7d1a 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java @@ -18,6 +18,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CAttackOrder; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; @@ -389,7 +390,7 @@ public class CUnit extends CWidget { CAllianceType.PASSIVE)) { for (final CUnitAttack attack : this.unitType.getAttacks()) { if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) { - this.order(new CAttackOrder(this, attack, source), false); + this.order(new CAttackOrder(this, attack, OrderIds.attack, source), false); break; } } @@ -527,6 +528,10 @@ public class CUnit extends CWidget { return this.unitType.isBuilding(); } + public float getAcquisitionRange() { + return this.acquisitionRange; + } + private static final class AutoAttackTargetFinderEnum implements CUnitEnumFunction { private CSimulation game; private CUnit source; @@ -545,7 +550,7 @@ public class CUnit extends CWidget { if (this.source.canReach(unit, this.source.acquisitionRange) && unit.canBeTargetedBy(this.game, this.source, attack.getTargetsAllowed()) && (this.source.distance(unit) >= this.source.getUnitType().getMinimumAttackRange())) { - this.source.order(new CAttackOrder(this.source, attack, unit), false); + this.source.order(new CAttackOrder(this.source, attack, OrderIds.attack, unit), false); return true; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java index ac615e7..9b5db10 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java @@ -12,11 +12,9 @@ public interface CAbility extends CAbilityView { /* should fire when ability removed from unit */ void onRemove(CSimulation game, CUnit unit); - void onOrder(CSimulation game, CUnit caster, CWidget target, boolean queue); + void onOrder(CSimulation game, CUnit caster, int orderId, CWidget target, boolean queue); - void onOrder(CSimulation game, CUnit caster, Vector2 point, boolean queue); + void onOrder(CSimulation game, CUnit caster, int orderId, Vector2 point, boolean queue); - void onOrderNoTarget(CSimulation game, CUnit caster, boolean queue); - - int getOrderId(); + void onOrderNoTarget(CSimulation game, CUnit caster, int orderId, boolean queue); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java index f4ad9ef..26d07c3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java @@ -9,33 +9,56 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.StringsToExternaliz import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CAttackOrder; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CMoveOrder; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver.TargetType; public class CAbilityAttack implements CAbility { - public static final int ORDER_ID = 860000; // fake, later will use WC3 one probably - public static final CAbilityAttack INSTANCE = new CAbilityAttack(); + private final int handleId; + + public CAbilityAttack(final int handleId) { + this.handleId = handleId; + } @Override - public void checkCanUse(final CSimulation game, final CUnit unit, final AbilityActivationReceiver receiver) { + public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId, + final AbilityActivationReceiver receiver) { receiver.useOk(); } @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final CWidget target, + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target, final AbilityTargetCheckReceiver receiver) { - receiver.targetOk(target); + if ((orderId == OrderIds.smart) || (orderId == OrderIds.attack)) { + boolean canTarget = false; + for (final CUnitAttack attack : unit.getUnitType().getAttacks()) { + if (target.canBeTargetedBy(game, unit, attack.getTargetsAllowed())) { + canTarget = true; + break; + } + } + if (canTarget) { + receiver.targetOk(target); + } + else { + // TODO obviously we should later support better warnings here + receiver.mustTargetType(TargetType.UNIT); + } + } + else { + receiver.orderIdNotAccepted(); + } } @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final Vector2 target, + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target, final AbilityTargetCheckReceiver receiver) { receiver.mustTargetType(TargetType.UNIT); } @Override - public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, + public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId, final AbilityTargetCheckReceiver receiver) { receiver.mustTargetType(TargetType.UNIT); } @@ -49,33 +72,40 @@ public class CAbilityAttack implements CAbility { } @Override - public void onOrder(final CSimulation game, final CUnit caster, final CWidget target, final boolean queue) { + public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final CWidget target, + final boolean queue) { COrder order = null; for (final CUnitAttack attack : caster.getUnitType().getAttacks()) { if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) { - order = new CAttackOrder(caster, attack, target); + order = new CAttackOrder(caster, attack, orderId, target); break; } } if (order == null) { - order = new CMoveOrder(caster, target.getX(), target.getY()); + order = new CMoveOrder(caster, orderId, target.getX(), target.getY()); } caster.order(order, queue); } @Override - public void onOrder(final CSimulation game, final CUnit caster, final Vector2 target, final boolean queue) { + public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final Vector2 target, + final boolean queue) { throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_WIDGET); } @Override - public void onOrderNoTarget(final CSimulation game, final CUnit caster, final boolean queue) { + public void onOrderNoTarget(final CSimulation game, final CUnit caster, final int orderId, final boolean queue) { throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_WIDGET); } @Override - public int getOrderId() { - return ORDER_ID; + public T visit(final CAbilityVisitor visitor) { + return visitor.accept(this); + } + + @Override + public int getHandleId() { + return this.handleId; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityHoldPosition.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityHoldPosition.java deleted file mode 100644 index 0ede105..0000000 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityHoldPosition.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities; - -import com.badlogic.gdx.math.Vector2; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CDoNothingOrder; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver.TargetType; - -public class CAbilityHoldPosition implements CAbility { - public static final int ORDER_ID = 860002; // fake, later will use WC3 one probably - public static CAbilityHoldPosition INSTANCE = new CAbilityHoldPosition(); - - @Override - public void checkCanUse(final CSimulation game, final CUnit unit, final AbilityActivationReceiver receiver) { - receiver.useOk(); - } - - @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final CWidget target, - final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.NO_TARGET); - } - - @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final Vector2 target, - final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.NO_TARGET); - } - - @Override - public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, - final AbilityTargetCheckReceiver receiver) { - receiver.targetOk(null); - } - - @Override - public void onAdd(final CSimulation game, final CUnit unit) { - - } - - @Override - public void onRemove(final CSimulation game, final CUnit unit) { - - } - - @Override - public void onOrder(final CSimulation game, final CUnit caster, final CWidget target, final boolean queue) { - caster.order(new CDoNothingOrder(getOrderId()), queue); - } - - @Override - public void onOrder(final CSimulation game, final CUnit caster, final Vector2 target, final boolean queue) { - caster.order(new CDoNothingOrder(getOrderId()), queue); - } - - @Override - public void onOrderNoTarget(final CSimulation game, final CUnit caster, final boolean queue) { - caster.order(new CDoNothingOrder(getOrderId()), queue); - } - - @Override - public int getOrderId() { - return ORDER_ID; - } - -} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java index 2fb4d21..6b71c20 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java @@ -6,35 +6,70 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.StringsToExternalizeLater; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CMoveOrder; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CPatrolOrder; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver.TargetType; public class CAbilityMove implements CAbility { - public static final int ORDER_ID = 859999; // fake, later will use WC3 one probably - public static CAbilityMove INSTANCE = new CAbilityMove(); + private final int handleId; + + public CAbilityMove(final int handleId) { + this.handleId = handleId; + } @Override - public void checkCanUse(final CSimulation game, final CUnit unit, final AbilityActivationReceiver receiver) { + public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId, + final AbilityActivationReceiver receiver) { receiver.useOk(); } @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final CWidget target, + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target, final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.POINT); + switch (orderId) { + case OrderIds.smart: + case OrderIds.patrol: + if (target instanceof CUnit) { + receiver.targetOk(target); + } + else { + receiver.mustTargetType(TargetType.UNIT_OR_POINT); + } + break; + default: + receiver.orderIdNotAccepted(); + break; + } } @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final Vector2 target, + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target, final AbilityTargetCheckReceiver receiver) { - receiver.targetOk(target); + switch (orderId) { + case OrderIds.smart: + case OrderIds.move: + case OrderIds.patrol: + receiver.targetOk(target); + break; + default: + receiver.orderIdNotAccepted(); + break; + } } @Override - public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, + public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId, final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.POINT); + switch (orderId) { + case OrderIds.holdposition: + receiver.targetOk(null); + break; + default: + receiver.orderIdNotAccepted(); + break; + } } @Override @@ -48,23 +83,30 @@ public class CAbilityMove implements CAbility { } @Override - public void onOrder(final CSimulation game, final CUnit caster, final CWidget target, final boolean queue) { + public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final CWidget target, + final boolean queue) { + caster.order(new CPatrolOrder(caster, orderId, (CUnit) target), queue); + } + + @Override + public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final Vector2 target, + final boolean queue) { + caster.order(new CMoveOrder(caster, orderId, target.x, target.y), queue); + } + + @Override + public void onOrderNoTarget(final CSimulation game, final CUnit caster, final int orderId, final boolean queue) { throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_POINT); } @Override - public void onOrder(final CSimulation game, final CUnit caster, final Vector2 target, final boolean queue) { - caster.order(new CMoveOrder(caster, target.x, target.y), queue); + public T visit(final CAbilityVisitor visitor) { + return visitor.accept(this); } @Override - public void onOrderNoTarget(final CSimulation game, final CUnit caster, final boolean queue) { - throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_POINT); - } - - @Override - public int getOrderId() { - return ORDER_ID; + public int getHandleId() { + return this.handleId; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityPatrol.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityPatrol.java deleted file mode 100644 index 5d08daa..0000000 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityPatrol.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities; - -import com.badlogic.gdx.math.Vector2; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.StringsToExternalizeLater; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CMoveOrder; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver.TargetType; - -public class CAbilityPatrol implements CAbility { - public static final int ORDER_ID = 860001; // fake, later will use WC3 one probably - public static CAbilityPatrol INSTANCE = new CAbilityPatrol(); - - @Override - public void checkCanUse(final CSimulation game, final CUnit unit, final AbilityActivationReceiver receiver) { - receiver.useOk(); - } - - @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final CWidget target, - final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.POINT); - } - - @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final Vector2 target, - final AbilityTargetCheckReceiver receiver) { - receiver.targetOk(target); - } - - @Override - public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, - final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.POINT); - } - - @Override - public void onAdd(final CSimulation game, final CUnit unit) { - - } - - @Override - public void onRemove(final CSimulation game, final CUnit unit) { - - } - - @Override - public void onOrder(final CSimulation game, final CUnit caster, final CWidget target, final boolean queue) { - throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_POINT); - } - - @Override - public void onOrder(final CSimulation game, final CUnit caster, final Vector2 target, final boolean queue) { - caster.order(new CMoveOrder(caster, target.x, target.y), queue); - } - - @Override - public void onOrderNoTarget(final CSimulation game, final CUnit caster, final boolean queue) { - throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_POINT); - } - - @Override - public int getOrderId() { - return ORDER_ID; - } - -} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityStop.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityStop.java deleted file mode 100644 index 352d47b..0000000 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityStop.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities; - -import com.badlogic.gdx.math.Vector2; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CDoNothingOrder; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver.TargetType; - -public class CAbilityStop implements CAbility { - public static final int ORDER_ID = 860003; // fake, later will use WC3 one probably - public static CAbilityStop INSTANCE = new CAbilityStop(); - - @Override - public void checkCanUse(final CSimulation game, final CUnit unit, final AbilityActivationReceiver receiver) { - receiver.useOk(); - } - - @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final CWidget target, - final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.NO_TARGET); - } - - @Override - public void checkCanTarget(final CSimulation game, final CUnit unit, final Vector2 target, - final AbilityTargetCheckReceiver receiver) { - receiver.mustTargetType(TargetType.NO_TARGET); - } - - @Override - public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, - final AbilityTargetCheckReceiver receiver) { - receiver.targetOk(null); - } - - @Override - public void onAdd(final CSimulation game, final CUnit unit) { - - } - - @Override - public void onRemove(final CSimulation game, final CUnit unit) { - - } - - @Override - public void onOrder(final CSimulation game, final CUnit caster, final CWidget target, final boolean queue) { - caster.order(new CDoNothingOrder(getOrderId()), queue); - } - - @Override - public void onOrder(final CSimulation game, final CUnit caster, final Vector2 target, final boolean queue) { - caster.order(new CDoNothingOrder(getOrderId()), queue); - } - - @Override - public void onOrderNoTarget(final CSimulation game, final CUnit caster, final boolean queue) { - caster.order(new CDoNothingOrder(getOrderId()), queue); - } - - @Override - public int getOrderId() { - return ORDER_ID; - } - -} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityToggleableView.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityToggleableView.java new file mode 100644 index 0000000..810dd9f --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityToggleableView.java @@ -0,0 +1,5 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities; + +public interface CAbilityToggleableView extends CAbilityView { + boolean isActive(); +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityView.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityView.java index ad90fd0..8ef4b1a 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityView.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityView.java @@ -8,12 +8,17 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivat import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; public interface CAbilityView { - void checkCanUse(CSimulation game, CUnit unit, AbilityActivationReceiver receiver); + void checkCanUse(CSimulation game, CUnit unit, int orderId, AbilityActivationReceiver receiver); - void checkCanTarget(CSimulation game, CUnit unit, CWidget target, AbilityTargetCheckReceiver receiver); + void checkCanTarget(CSimulation game, CUnit unit, int orderId, CWidget target, + AbilityTargetCheckReceiver receiver); - void checkCanTarget(CSimulation game, CUnit unit, Vector2 target, AbilityTargetCheckReceiver receiver); + void checkCanTarget(CSimulation game, CUnit unit, int orderId, Vector2 target, + AbilityTargetCheckReceiver receiver); - void checkCanTargetNoTarget(CSimulation game, CUnit unit, AbilityTargetCheckReceiver receiver); + void checkCanTargetNoTarget(CSimulation game, CUnit unit, int orderId, AbilityTargetCheckReceiver receiver); + int getHandleId(); + + T visit(CAbilityVisitor visitor); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java new file mode 100644 index 0000000..626aec9 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java @@ -0,0 +1,15 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities; + +/** + * A visitor for the lowest level inherent types of an ability. It's a bit of a + * design clash to have the notion of an ability visitor pattern while also + * having any arbitrary number of "ability types" defined in config files. But + * the way that we will handle this for now will be with the notion of a generic + * ability (one whose UI information and behaviors come from a rawcode) versus + * abilities with engine-level type information (move, stop, attack). + */ +public interface CAbilityVisitor { + T accept(CAbilityAttack ability); + + T accept(CAbilityMove ability); +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java index a790f44..a9ebc8d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java @@ -15,11 +15,9 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.HandleIdAllocator; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityHoldPosition; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityPatrol; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityStop; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; @@ -31,6 +29,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUni import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissileLine; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissileSplash; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackNormal; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController; public class CUnitData { private static final War3ID MANA_INITIAL_AMOUNT = War3ID.fromString("umpi"); @@ -126,9 +125,11 @@ public class CUnitData { this.unitData = unitData; } - public CUnit create(final CSimulation simulation, final int playerIndex, final int handleId, final War3ID typeId, - final float x, final float y, final float facing, final BufferedImage buildingPathingPixelMap) { + public CUnit create(final CSimulation simulation, final int playerIndex, final War3ID typeId, final float x, + final float y, final float facing, final BufferedImage buildingPathingPixelMap, + final SimulationRenderController simulationRenderController, final HandleIdAllocator handleIdAllocator) { final MutableGameObject unitType = this.unitData.get(typeId); + final int handleId = handleIdAllocator.createId(); final int life = unitType.getFieldAsInteger(HIT_POINT_MAXIMUM, 0); final int manaInitial = unitType.getFieldAsInteger(MANA_INITIAL_AMOUNT, 0); final int manaMaximum = unitType.getFieldAsInteger(MANA_MAXIMUM, 0); @@ -140,15 +141,10 @@ public class CUnitData { final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum, speed, defense, unitTypeInstance); if (speed > 0) { - unit.add(simulation, CAbilityMove.INSTANCE); - unit.add(simulation, CAbilityPatrol.INSTANCE); - unit.add(simulation, CAbilityHoldPosition.INSTANCE); - unit.add(simulation, CAbilityStop.INSTANCE); + unit.add(simulation, new CAbilityMove(handleIdAllocator.createId())); } - final int dmgDice1 = unitType.getFieldAsInteger(ATTACK1_DMG_DICE, 0); - final int dmgDice2 = unitType.getFieldAsInteger(ATTACK2_DMG_DICE, 0); - if ((dmgDice1 != 0) || (dmgDice2 != 0)) { - unit.add(simulation, CAbilityAttack.INSTANCE); + if (!unitTypeInstance.getAttacks().isEmpty()) { + unit.add(simulation, new CAbilityAttack(handleIdAllocator.createId())); } return unit; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CAttackOrder.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CAttackOrder.java index 29d4887..7bc942d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CAttackOrder.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CAttackOrder.java @@ -7,11 +7,11 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; public class CAttackOrder implements COrder { private final CUnit unit; + private final int orderId; private boolean wasWithinPropWindow = false; private final CUnitAttack unitAttack; private final CWidget target; @@ -21,9 +21,10 @@ public class CAttackOrder implements COrder { private int thisOrderCooldownEndTime; private boolean wasInRange = false; - public CAttackOrder(final CUnit unit, final CUnitAttack unitAttack, final CWidget target) { + public CAttackOrder(final CUnit unit, final CUnitAttack unitAttack, final int orderId, final CWidget target) { this.unit = unit; this.unitAttack = unitAttack; + this.orderId = orderId; this.target = target; createMoveOrder(unit, target); } @@ -31,10 +32,10 @@ public class CAttackOrder implements COrder { private void createMoveOrder(final CUnit unit, final CWidget target) { if (!unit.isMovementDisabled()) { // TODO: Check mobility instead if ((target instanceof CUnit) && !(((CUnit) target).getUnitType().isBuilding())) { - this.moveOrder = new CMoveOrder(unit, (CUnit) target); + this.moveOrder = new CMoveOrder(unit, this.orderId, (CUnit) target); } else { - this.moveOrder = new CMoveOrder(unit, target.getX(), target.getY()); + this.moveOrder = new CMoveOrder(unit, this.orderId, target.getX(), target.getY()); } } else { @@ -172,7 +173,7 @@ public class CAttackOrder implements COrder { @Override public int getOrderId() { - return CAbilityAttack.ORDER_ID; + return this.orderId; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CMoveOrder.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CMoveOrder.java index 83677cd..57b0e38 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CMoveOrder.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CMoveOrder.java @@ -14,12 +14,12 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWorldCollision; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindingProcessor; public class CMoveOrder implements COrder { private static final Rectangle tempRect = new Rectangle(); private final CUnit unit; + private final int orderId; private boolean wasWithinPropWindow = false; private List path = null; private final CPathfindingProcessor.GridMapping gridMapping; @@ -27,16 +27,18 @@ public class CMoveOrder implements COrder { private int searchCycles = 0; private CUnit followUnit; - public CMoveOrder(final CUnit unit, final float targetX, final float targetY) { + public CMoveOrder(final CUnit unit, final int orderId, final float targetX, final float targetY) { this.unit = unit; + this.orderId = orderId; this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners( unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS : CPathfindingProcessor.GridMapping.CELLS; this.target = new Point2D.Float(targetX, targetY); } - public CMoveOrder(final CUnit unit, final CUnit followUnit) { + public CMoveOrder(final CUnit unit, final int orderId, final CUnit followUnit) { this.unit = unit; + this.orderId = orderId; this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners( unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS : CPathfindingProcessor.GridMapping.CELLS; @@ -313,7 +315,7 @@ public class CMoveOrder implements COrder { @Override public int getOrderId() { - return CAbilityMove.ORDER_ID; + return this.orderId; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CPatrolOrder.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CPatrolOrder.java new file mode 100644 index 0000000..078e83f --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/CPatrolOrder.java @@ -0,0 +1,58 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; + +public class CPatrolOrder implements COrder { + private final CUnit unit; + private final int orderId; + private final CWidget target; + private COrder moveOrder; + + public CPatrolOrder(final CUnit unit, final int orderId, final CUnit target) { + this.unit = unit; + this.orderId = orderId; + this.target = target; + createMoveOrder(unit, target); + } + + private void createMoveOrder(final CUnit unit, final CUnit target) { + if (!unit.isMovementDisabled()) { // TODO: Check mobility instead + if ((target instanceof CUnit) && !(target.getUnitType().isBuilding())) { + this.moveOrder = new CMoveOrder(unit, this.orderId, target); + } + else { + this.moveOrder = new CMoveOrder(unit, this.orderId, target.getX(), target.getY()); + } + } + else { + this.moveOrder = null; + } + } + + @Override + public boolean update(final CSimulation simulation) { + if (this.target.isDead()) { + return true; + } + final float range = this.unit.getAcquisitionRange(); + if (!this.unit.canReach(this.target, range)) { + if (this.moveOrder == null) { + return true; + } + if (this.moveOrder.update(simulation)) { + return true; // we just cant reach them + } + return false; + } + return false; + } + + @Override + public int getOrderId() { + return this.orderId; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/OrderIds.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/OrderIds.java new file mode 100644 index 0000000..ee9022d --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/OrderIds.java @@ -0,0 +1,457 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders; + +/** + * Thanks to the Wurst guys for this list of ids, taken from this link: + * https://github.com/wurstscript/WurstStdlib2/blob/master/wurst/_wurst/assets/Orders.wurst + * + * The original code ported to create this Java file is licensed under the + * Apache License; you can read more at the link above. + * + */ +public class OrderIds { + ;/** + * This is an order with no target that opens up the build menu of a unit that + * can build structures. + */ + ; + public static final int buildmenu = 851994; + /** + * 851976 (cancel): This is an order with no target that is like a click on a + * cancel button. We used to be able to catch cancel clicks with this id back + * then but this id doesn't seem to work any more. + */ + public static final int cancel = 851976; + /** + * An item targeted order that move the target item to a certain inventory slot + * of the ordered hero. + */ + public static final int itemdrag00 = 852002; + /** + * An item targeted order that move the target item to a certain inventory slot + * of the ordered hero. + */ + public static final int itemdrag01 = 852003; + /** + * An item targeted order that move the target item to a certain inventory slot + * of the ordered hero. + */ + public static final int itemdrag02 = 852004; + /** + * An item targeted order that move the target item to a certain inventory slot + * of the ordered hero. + */ + public static final int itemdrag03 = 852005; + /** + * An item targeted order that move the target item to a certain inventory slot + * of the ordered hero. + */ + public static final int itemdrag04 = 852006; + /** + * An item targeted order that move the target item to a certain inventory slot + * of the ordered hero. + */ + public static final int itemdrag05 = 852007; + /** + * An order that will make the ordered hero use the item in a certain inventory + * slot. If it's an order with no target or object or point targeted depends on + * the type of item. + */ + public static final int itemuse00 = 852008; + /** + * An order that will make the ordered hero use the item in a certain inventory + * slot. If it's an order with no target or object or point targeted depends on + * the type of item. + */ + public static final int itemuse01 = 852009; + /** + * An order that will make the ordered hero use the item in a certain inventory + * slot. If it's an order with no target or object or point targeted depends on + * the type of item. + */ + public static final int itemuse02 = 852010; + /** + * An order that will make the ordered hero use the item in a certain inventory + * slot. If it's an order with no target or object or point targeted depends on + * the type of item. + */ + public static final int itemuse03 = 852011; + /** + * An order that will make the ordered hero use the item in a certain inventory + * slot. If it's an order with no target or object or point targeted depends on + * the type of item. + */ + public static final int itemuse04 = 852012; + /** + * An order that will make the ordered hero use the item in a certain inventory + * slot. If it's an order with no target or object or point targeted depends on + * the type of item. + */ + public static final int itemuse05 = 852013; + /** + * Order for AIaa ability, which blizzard made for tome of attack, but never + * used it. But it can actually change caster's base attack! + */ + public static final int tomeOfAttack = 852259; + /** This is a point or object targeted order that is like a right click. */ + public static final int smart = 851971; + /** + * This is an order with no target that opens the skill menu of heroes. If it is + * issued for a normal unit with triggers it will black out the command card for + * this unit, the command card will revert to normal after reselecting the unit. + */ + public static final int skillmenu = 852000; + /** + * This order is issued to units that get stunned by a spell, for example War + * Stomp (AOws). This is probably a hold position + hold fire order. The ordered + * unit will be unable to move and attack. + */ + public static final int stunned = 851973; + public static final int wandOfIllusion = 852274; + public static final int absorb = 852529; + public static final int acidbomb = 852662; + public static final int acolyteharvest = 852185; + public static final int ambush = 852131; + public static final int ancestralspirit = 852490; + public static final int ancestralspirittarget = 852491; + public static final int animatedead = 852217; + public static final int antimagicshell = 852186; + public static final int attack = 851983; + public static final int attackground = 851984; + public static final int attackonce = 851985; + public static final int attributemodskill = 852576; + public static final int auraunholy = 852215; + public static final int auravampiric = 852216; + public static final int autodispel = 852132; + public static final int autodispeloff = 852134; + public static final int autodispelon = 852133; + public static final int autoentangle = 852505; + public static final int autoentangleinstant = 852506; + public static final int autoharvestgold = 852021; + public static final int autoharvestlumber = 852022; + public static final int avatar = 852086; + public static final int avengerform = 852531; + public static final int awaken = 852466; + public static final int banish = 852486; + public static final int barkskin = 852135; + public static final int barkskinoff = 852137; + public static final int barkskinon = 852136; + public static final int battleroar = 852599; + public static final int battlestations = 852099; + public static final int bearform = 852138; + public static final int berserk = 852100; + public static final int blackarrow = 852577; + public static final int blackarrowoff = 852579; + public static final int blackarrowon = 852578; + public static final int blight = 852187; + public static final int blink = 852525; + public static final int blizzard = 852089; + public static final int bloodlust = 852101; + public static final int bloodlustoff = 852103; + public static final int bloodluston = 852102; + public static final int board = 852043; + public static final int breathoffire = 852580; + public static final int breathoffrost = 852560; + public static final int build = 851994; + public static final int burrow = 852533; + public static final int cannibalize = 852188; + public static final int carrionscarabs = 852551; + public static final int carrionscarabsinstant = 852554; + public static final int carrionscarabsoff = 852553; + public static final int carrionscarabson = 852552; + public static final int carrionswarm = 852218; + public static final int chainlightning = 852119; + public static final int channel = 852600; + public static final int charm = 852581; + public static final int chemicalrage = 852663; + public static final int cloudoffog = 852473; + public static final int clusterrockets = 852652; + public static final int coldarrows = 852244; + public static final int coldarrowstarg = 852243; + public static final int controlmagic = 852474; + public static final int corporealform = 852493; + public static final int corrosivebreath = 852140; + public static final int coupleinstant = 852508; + public static final int coupletarget = 852507; + public static final int creepanimatedead = 852246; + public static final int creepdevour = 852247; + public static final int creepheal = 852248; + public static final int creephealoff = 852250; + public static final int creephealon = 852249; + public static final int creepthunderbolt = 852252; + public static final int creepthunderclap = 852253; + public static final int cripple = 852189; + public static final int curse = 852190; + public static final int curseoff = 852192; + public static final int curseon = 852191; + public static final int cyclone = 852144; + public static final int darkconversion = 852228; + public static final int darkportal = 852229; + public static final int darkritual = 852219; + public static final int darksummoning = 852220; + public static final int deathanddecay = 852221; + public static final int deathcoil = 852222; + public static final int deathpact = 852223; + public static final int decouple = 852509; + public static final int defend = 852055; + public static final int detectaoe = 852015; + public static final int detonate = 852145; + public static final int devour = 852104; + public static final int devourmagic = 852536; + public static final int disassociate = 852240; + public static final int disenchant = 852495; + public static final int dismount = 852470; + public static final int dispel = 852057; + public static final int divineshield = 852090; + public static final int doom = 852583; + public static final int drain = 852487; + public static final int dreadlordinferno = 852224; + public static final int dropitem = 852001; + public static final int drunkenhaze = 852585; + public static final int earthquake = 852121; + public static final int eattree = 852146; + public static final int elementalfury = 852586; + public static final int ensnare = 852106; + public static final int ensnareoff = 852108; + public static final int ensnareon = 852107; + public static final int entangle = 852147; + public static final int entangleinstant = 852148; + public static final int entanglingroots = 852171; + public static final int etherealform = 852496; + public static final int evileye = 852105; + public static final int faeriefire = 852149; + public static final int faeriefireoff = 852151; + public static final int faeriefireon = 852150; + public static final int fanofknives = 852526; + public static final int farsight = 852122; + public static final int fingerofdeath = 852230; + public static final int firebolt = 852231; + public static final int flamestrike = 852488; + public static final int flamingarrows = 852174; + public static final int flamingarrowstarg = 852173; + public static final int flamingattack = 852540; + public static final int flamingattacktarg = 852539; + public static final int flare = 852060; + public static final int forceboard = 852044; + public static final int forceofnature = 852176; + public static final int forkedlightning = 852587; + public static final int freezingbreath = 852195; + public static final int frenzy = 852561; + public static final int frenzyoff = 852563; + public static final int frenzyon = 852562; + public static final int frostarmor = 852225; + public static final int frostarmoroff = 852459; + public static final int frostarmoron = 852458; + public static final int frostnova = 852226; + public static final int getitem = 851981; + public static final int gold2lumber = 852233; + public static final int grabtree = 852511; + public static final int harvest = 852018; + public static final int heal = 852063; + public static final int healingspray = 852664; + public static final int healingward = 852109; + public static final int healingwave = 852501; + public static final int healoff = 852065; + public static final int healon = 852064; + public static final int hex = 852502; + public static final int holdposition = 851993; + public static final int holybolt = 852092; + public static final int howlofterror = 852588; + public static final int humanbuild = 851995; + public static final int immolation = 852177; + public static final int impale = 852555; + public static final int incineratearrow = 852670; + public static final int incineratearrowoff = 852672; + public static final int incineratearrowon = 852671; + public static final int inferno = 852232; + public static final int innerfire = 852066; + public static final int innerfireoff = 852068; + public static final int innerfireon = 852067; + public static final int instant = 852200; + public static final int invisibility = 852069; + public static final int lavamonster = 852667; + public static final int lightningshield = 852110; + public static final int load = 852046; + public static final int loadarcher = 852142; + public static final int loadcorpse = 852050; + public static final int loadcorpseinstant = 852053; + public static final int locustswarm = 852556; + public static final int lumber2gold = 852234; + public static final int magicdefense = 852478; + public static final int magicleash = 852480; + public static final int magicundefense = 852479; + public static final int manaburn = 852179; + public static final int manaflareoff = 852513; + public static final int manaflareon = 852512; + public static final int manashieldoff = 852590; + public static final int manashieldon = 852589; + public static final int massteleport = 852093; + public static final int mechanicalcritter = 852564; + public static final int metamorphosis = 852180; + public static final int militia = 852072; + public static final int militiaconvert = 852071; + public static final int militiaoff = 852073; + public static final int militiaunconvert = 852651; + public static final int mindrot = 852565; + public static final int mirrorimage = 852123; + public static final int monsoon = 852591; + public static final int mount = 852469; + public static final int mounthippogryph = 852143; + public static final int move = 851986; + public static final int moveAI = 851988; + public static final int nagabuild = 852467; + public static final int neutraldetectaoe = 852023; + public static final int neutralinteract = 852566; + public static final int neutralspell = 852630; + public static final int nightelfbuild = 851997; + public static final int orcbuild = 851996; + public static final int parasite = 852601; + public static final int parasiteoff = 852603; + public static final int parasiteon = 852602; + public static final int patrol = 851990; + public static final int phaseshift = 852514; + public static final int phaseshiftinstant = 852517; + public static final int phaseshiftoff = 852516; + public static final int phaseshifton = 852515; + public static final int phoenixfire = 852481; + public static final int phoenixmorph = 852482; + public static final int poisonarrows = 852255; + public static final int poisonarrowstarg = 852254; + public static final int polymorph = 852074; + public static final int possession = 852196; + public static final int preservation = 852568; + public static final int purge = 852111; + public static final int rainofchaos = 852237; + public static final int rainoffire = 852238; + public static final int raisedead = 852197; + public static final int raisedeadoff = 852199; + public static final int raisedeadon = 852198; + public static final int ravenform = 852155; + public static final int recharge = 852157; + public static final int rechargeoff = 852159; + public static final int rechargeon = 852158; + public static final int rejuvination = 852160; + public static final int renew = 852161; + public static final int renewoff = 852163; + public static final int renewon = 852162; + public static final int repair = 852024; + public static final int repairoff = 852026; + public static final int repairon = 852025; + public static final int replenish = 852542; + public static final int replenishlife = 852545; + public static final int replenishlifeoff = 852547; + public static final int replenishlifeon = 852546; + public static final int replenishmana = 852548; + public static final int replenishmanaoff = 852550; + public static final int replenishmanaon = 852549; + public static final int replenishoff = 852544; + public static final int replenishon = 852543; + public static final int request_hero = 852239; + public static final int requestsacrifice = 852201; + public static final int restoration = 852202; + public static final int restorationoff = 852204; + public static final int restorationon = 852203; + public static final int resumebuild = 851999; + public static final int resumeharvesting = 852017; + public static final int resurrection = 852094; + public static final int returnresources = 852020; + public static final int revenge = 852241; + public static final int revive = 852039; + public static final int roar = 852164; + public static final int robogoblin = 852656; + public static final int root = 852165; + public static final int sacrifice = 852205; + public static final int sanctuary = 852569; + public static final int scout = 852181; + public static final int selfdestruct = 852040; + public static final int selfdestructoff = 852042; + public static final int selfdestructon = 852041; + public static final int sentinel = 852182; + public static final int setrally = 851980; + public static final int shadowsight = 852570; + public static final int shadowstrike = 852527; + public static final int shockwave = 852125; + public static final int silence = 852592; + public static final int sleep = 852227; + public static final int slow = 852075; + public static final int slowoff = 852077; + public static final int slowon = 852076; + public static final int soulburn = 852668; + public static final int soulpreservation = 852242; + public static final int spellshield = 852571; + public static final int spellshieldaoe = 852572; + public static final int spellsteal = 852483; + public static final int spellstealoff = 852485; + public static final int spellstealon = 852484; + public static final int spies = 852235; + public static final int spiritlink = 852499; + public static final int spiritofvengeance = 852528; + public static final int spirittroll = 852573; + public static final int spiritwolf = 852126; + public static final int stampede = 852593; + public static final int standdown = 852113; + public static final int starfall = 852183; + public static final int stasistrap = 852114; + public static final int steal = 852574; + public static final int stomp = 852127; + public static final int stoneform = 852206; + public static final int stop = 851972; + public static final int submerge = 852604; + public static final int summonfactory = 852658; + public static final int summongrizzly = 852594; + public static final int summonphoenix = 852489; + public static final int summonquillbeast = 852595; + public static final int summonwareagle = 852596; + public static final int tankdroppilot = 852079; + public static final int tankloadpilot = 852080; + public static final int tankpilot = 852081; + public static final int taunt = 852520; + public static final int thunderbolt = 852095; + public static final int thunderclap = 852096; + public static final int tornado = 852597; + public static final int townbelloff = 852083; + public static final int townbellon = 852082; + public static final int tranquility = 852184; + public static final int transmute = 852665; + public static final int unavatar = 852087; + public static final int unavengerform = 852532; + public static final int unbearform = 852139; + public static final int unburrow = 852534; + public static final int uncoldarrows = 852245; + public static final int uncorporealform = 852494; + public static final int undeadbuild = 851998; + public static final int undefend = 852056; + public static final int undivineshield = 852091; + public static final int unetherealform = 852497; + public static final int unflamingarrows = 852175; + public static final int unflamingattack = 852541; + public static final int unholyfrenzy = 852209; + public static final int unimmolation = 852178; + public static final int unload = 852047; + public static final int unloadall = 852048; + public static final int unloadallcorpses = 852054; + public static final int unloadallinstant = 852049; + public static final int unpoisonarrows = 852256; + public static final int unravenform = 852156; + public static final int unrobogoblin = 852657; + public static final int unroot = 852166; + public static final int unstableconcoction = 852500; + public static final int unstoneform = 852207; + public static final int unsubmerge = 852605; + public static final int unsummon = 852210; + public static final int unwindwalk = 852130; + public static final int vengeance = 852521; + public static final int vengeanceinstant = 852524; + public static final int vengeanceoff = 852523; + public static final int vengeanceon = 852522; + public static final int volcano = 852669; + public static final int voodoo = 852503; + public static final int ward = 852504; + public static final int waterelemental = 852097; + public static final int wateryminion = 852598; + public static final int web = 852211; + public static final int weboff = 852213; + public static final int webon = 852212; + public static final int whirlwind = 852128; + public static final int windwalk = 852129; + public static final int wispharvest = 852214; +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityTargetCheckReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityTargetCheckReceiver.java index 0faccd5..2d11d29 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityTargetCheckReceiver.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityTargetCheckReceiver.java @@ -17,6 +17,8 @@ public interface AbilityTargetCheckReceiver { void targetNotInPlayableMap(); + void orderIdNotAccepted(); + public static enum TeamType { ALLIED, ENEMY, diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java index 94bce87..aa960e4 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java @@ -48,6 +48,11 @@ public class CWidgetAbilityTargetCheckReceiver implements AbilityTargetCheckRece this.target = null; } + @Override + public void orderIdNotAccepted() { + this.target = null; + } + public CWidget getTarget() { return this.target; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java index 6c4721a..ad86eb7 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java @@ -20,7 +20,6 @@ public class PointAbilityTargetCheckReceiver implements AbilityTargetCheckReceiv @Override public void mustTargetType(final TargetType correctType) { this.target = null; - } @Override @@ -48,6 +47,11 @@ public class PointAbilityTargetCheckReceiver implements AbilityTargetCheckReceiv this.target = null; } + @Override + public void orderIdNotAccepted() { + this.target = null; + } + public Vector2 getTarget() { return this.target; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/CommandCardIcon.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/CommandCardIcon.java new file mode 100644 index 0000000..2449cb6 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/CommandCardIcon.java @@ -0,0 +1,88 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.ui; + +import com.badlogic.gdx.graphics.Texture; +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.frames.AbstractRenderableFrame; +import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame; +import com.etheller.warsmash.parsers.fdf.frames.TextureFrame; +import com.etheller.warsmash.parsers.fdf.frames.UIFrame; +import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButton; + +public class CommandCardIcon extends AbstractRenderableFrame { + + private TextureFrame iconFrame; + private SpriteFrame cooldownFrame; + private SpriteFrame autocastFrame; + private CommandButton commandButton; + + public CommandCardIcon(final String name, final UIFrame parent) { + super(name, parent); + } + + public void set(final TextureFrame iconFrame, final SpriteFrame cooldownFrame, final SpriteFrame autocastFrame) { + this.iconFrame = iconFrame; + this.cooldownFrame = cooldownFrame; + this.autocastFrame = autocastFrame; + } + + public void setCommandButton(final CommandButton commandButton) { + this.commandButton = commandButton; + if (commandButton == null) { + this.iconFrame.setVisible(false); + this.cooldownFrame.setVisible(false); + this.autocastFrame.setVisible(false); + } + else { + if (commandButton.isEnabled()) { + this.iconFrame.setTexture(commandButton.getIcon()); + } + else { + this.iconFrame.setTexture(commandButton.getDisabledIcon()); + } + if (commandButton.getCooldownRemaining() <= 0) { + this.cooldownFrame.setVisible(false); + } + else { + this.cooldownFrame.setVisible(true); + this.cooldownFrame.setSequence(PrimaryTag.STAND); + this.cooldownFrame.setAnimationSpeed(commandButton.getCooldown()); + this.cooldownFrame + .setFrameByRatio(1 - (commandButton.getCooldownRemaining() / commandButton.getCooldown())); + } + this.autocastFrame.setVisible(commandButton.isAutoCastActive()); + } + } + + public void setCommandButtonData(final Texture texture, final int orderId) { + this.iconFrame.setVisible(true); + this.cooldownFrame.setVisible(false); + this.autocastFrame.setVisible(false); + this.iconFrame.setTexture(texture); + } + + @Override + protected void innerPositionBounds(final Viewport viewport) { + this.iconFrame.positionBounds(viewport); + this.cooldownFrame.positionBounds(viewport); + this.autocastFrame.positionBounds(viewport); + } + + @Override + protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { + this.iconFrame.render(batch, baseFont, glyphLayout); + this.cooldownFrame.render(batch, baseFont, glyphLayout); + this.autocastFrame.render(batch, baseFont, glyphLayout); + } + + @Override + public UIFrame touchDown(final float screenX, final float screenY, final int button) { + if (this.renderBounds.contains(screenX, screenY)) { + return this; + } + return null; + } +} 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 4a0e87d..1c57b9a 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 @@ -2,8 +2,10 @@ package com.etheller.warsmash.viewer5.handlers.w3x.ui; import java.io.IOException; +import javax.imageio.ImageIO; + +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.GlyphLayout; @@ -12,7 +14,6 @@ import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.viewport.Viewport; -import com.etheller.warsmash.WarsmashGdxMapGame; import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; @@ -25,28 +26,39 @@ import com.etheller.warsmash.parsers.fdf.frames.UIFrame; import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener; import com.etheller.warsmash.util.FastNumberFormat; import com.etheller.warsmash.util.ImageUtils; +import com.etheller.warsmash.util.WarsmashConstants; import com.etheller.warsmash.viewer5.Scene; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; +import com.etheller.warsmash.viewer5.handlers.mdx.ReplaceableIds; import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode; +import com.etheller.warsmash.viewer5.handlers.tga.TgaFile; import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; -import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.CommandCardIcon; +import com.etheller.warsmash.viewer5.handlers.w3x.camera.CameraPreset; +import com.etheller.warsmash.viewer5.handlers.w3x.camera.CameraRates; +import com.etheller.warsmash.viewer5.handlers.w3x.camera.GameCameraManager; +import com.etheller.warsmash.viewer5.handlers.w3x.camera.PortraitCameraManager; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButtonListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityStop; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CodeKeyType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; -public class MeleeUI implements CUnitStateListener { +public class MeleeUI implements CUnitStateListener, CommandButtonListener { + private static final int COMMAND_CARD_WIDTH = 4; + private static final int COMMAND_CARD_HEIGHT = 3; + + private static final Vector2 screenCoordsVector = new Vector2(); private final DataSource dataSource; private final Viewport uiViewport; private final FreeTypeFontGenerator fontGenerator; private final Scene uiScene; + private final Scene portraitScene; + private final GameCameraManager cameraManager; private final War3MapViewer war3MapViewer; private final RootFrameListener rootFrameListener; private GameUI rootFrame; @@ -82,23 +94,72 @@ public class MeleeUI implements CUnitStateListener { private StringFrame armorInfoPanelIconLevel; private InfoPanelIconBackdrops damageBackdrops; private InfoPanelIconBackdrops defenseBackdrops; + + private final CommandCardIcon[][] commandCard = new CommandCardIcon[COMMAND_CARD_HEIGHT][COMMAND_CARD_WIDTH]; + private RenderUnit selectedUnit; // TODO remove this & replace with FDF private final Texture activeButtonTexture; private UIFrame inventoryCover; + private SpriteFrame cursorFrame; + private MeleeUIMinimap meleeUIMinimap; public MeleeUI(final DataSource dataSource, final Viewport uiViewport, final FreeTypeFontGenerator fontGenerator, - final Scene uiScene, final War3MapViewer war3MapViewer, final RootFrameListener rootFrameListener) { + final Scene uiScene, final Scene portraitScene, final CameraPreset[] cameraPresets, + final CameraRates cameraRates, final War3MapViewer war3MapViewer, + final RootFrameListener rootFrameListener) { this.dataSource = dataSource; this.uiViewport = uiViewport; this.fontGenerator = fontGenerator; this.uiScene = uiScene; + this.portraitScene = portraitScene; this.war3MapViewer = war3MapViewer; this.rootFrameListener = rootFrameListener; + this.cameraManager = new GameCameraManager(cameraPresets, cameraRates); + + this.cameraManager.setupCamera(war3MapViewer.worldScene); + if (this.war3MapViewer.startLocations[0] != null) { + this.cameraManager.target.x = this.war3MapViewer.startLocations[0].x; + this.cameraManager.target.y = this.war3MapViewer.startLocations[0].y; + } + this.activeButtonTexture = ImageUtils.getBLPTexture(war3MapViewer.mapMpq, "UI\\Widgets\\Console\\Human\\CommandButton\\human-activebutton.blp"); + + } + + private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) { + final Rectangle minimapDisplayArea = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f); + Texture minimapTexture = null; + if (war3MapViewer.dataSource.has("war3mapMap.tga")) { + try { + minimapTexture = ImageUtils.getTextureNoColorCorrection(TgaFile.readTGA("war3mapMap.tga", + war3MapViewer.dataSource.getResourceAsStream("war3mapMap.tga"))); + } + catch (final IOException e) { + System.err.println("Could not load minimap TGA file"); + e.printStackTrace(); + } + } + else if (war3MapViewer.dataSource.has("war3mapMap.blp")) { + try { + minimapTexture = ImageUtils + .getTexture(ImageIO.read(war3MapViewer.dataSource.getResourceAsStream("war3mapMap.blp"))); + } + catch (final IOException e) { + System.err.println("Could not load minimap BLP file"); + e.printStackTrace(); + } + } + final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS]; + for (int i = 0; i < teamColors.length; i++) { + teamColors[i] = ImageUtils.getBLPTexture(war3MapViewer.dataSource, + "ReplaceableTextures\\" + ReplaceableIds.getPathString(1) + ReplaceableIds.getIdString(i) + ".blp"); + } + final Rectangle playableMapArea = war3MapViewer.terrain.getPlayableMapArea(); + return new MeleeUIMinimap(minimapDisplayArea, playableMapArea, minimapTexture, teamColors); } /** @@ -109,7 +170,7 @@ public class MeleeUI implements CUnitStateListener { // ================================= // Load skins and templates // ================================= - this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 0), this.uiViewport, + this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 3), this.uiViewport, this.fontGenerator, this.uiScene, this.war3MapViewer); this.rootFrameListener.onCreate(this.rootFrame); try { @@ -155,7 +216,7 @@ public class MeleeUI implements CUnitStateListener { this.timeIndicator.setAnimationSpeed(0.0f); // do not advance automatically // Create the unit portrait stuff - this.portrait = new Portrait(this.war3MapViewer); + this.portrait = new Portrait(this.war3MapViewer, this.portraitScene); positionPortrait(); this.unitPortrait = this.rootFrame.createSimpleFrame("UnitPortrait", this.consoleUI, 0); this.unitLifeText = (StringFrame) this.rootFrame.getFrameByName("UnitPortraitHitPointText", 0); @@ -198,36 +259,122 @@ public class MeleeUI implements CUnitStateListener { this.inventoryCover = this.rootFrame.createSimpleFrame("SmashConsoleInventoryCover", this.rootFrame, 0); + int commandButtonIndex = 0; + for (int j = 0; j < COMMAND_CARD_HEIGHT; j++) { + for (int i = 0; i < COMMAND_CARD_WIDTH; i++) { + final CommandCardIcon commandCardIcon = new CommandCardIcon("SmashCommandButton_" + commandButtonIndex, + this.rootFrame); + this.rootFrame.add(commandCardIcon); + final TextureFrame iconFrame = this.rootFrame.createTextureFrame( + "SmashCommandButton_" + (commandButtonIndex) + "_Icon", this.rootFrame, false, null); + final SpriteFrame cooldownFrame = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE", + "SmashCommandButton_" + (commandButtonIndex) + "_Cooldown", this.rootFrame, "", 0); + final SpriteFrame autocastFrame = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE", + "SmashCommandButton_" + (commandButtonIndex) + "_Autocast", this.rootFrame, "", 0); + iconFrame.addAnchor(new AnchorDefinition(FramePoint.BOTTOMLEFT, + GameUI.convertX(this.uiViewport, 0.6175f + (0.0434f * i)), + GameUI.convertY(this.uiViewport, 0.095f - (0.044f * j)))); + iconFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f)); + iconFrame.setHeight(GameUI.convertY(this.uiViewport, 0.039f)); + iconFrame.setTexture(ImageUtils.DEFAULT_ICON_PATH, this.rootFrame); + cooldownFrame.addSetPoint(new SetPoint(FramePoint.CENTER, iconFrame, FramePoint.CENTER, 0, 0)); + this.rootFrame.setSpriteFrameModel(cooldownFrame, this.rootFrame.getSkinField("CommandButtonCooldown")); + cooldownFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f)); + cooldownFrame.setHeight(GameUI.convertY(this.uiViewport, 0.039f)); + autocastFrame.addSetPoint(new SetPoint(FramePoint.CENTER, iconFrame, FramePoint.CENTER, 0, 0)); + this.rootFrame.setSpriteFrameModel(autocastFrame, this.rootFrame.getSkinField("CommandButtonAutocast")); + autocastFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f)); + autocastFrame.setHeight(GameUI.convertY(this.uiViewport, 0.039f)); + commandCardIcon.set(iconFrame, cooldownFrame, autocastFrame); + this.commandCard[j][i] = commandCardIcon; + commandCardIcon.setCommandButton(null); + commandButtonIndex++; + } + } + + this.cursorFrame = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE", "SmashCursorFrame", this.rootFrame, + "", 0); + this.rootFrame.setSpriteFrameModel(this.cursorFrame, this.rootFrame.getSkinField("Cursor")); + this.cursorFrame.setSequence("Normal"); + this.cursorFrame.setZDepth(1.0f); + Gdx.input.setCursorCatched(true); + + this.meleeUIMinimap = createMinimap(this.war3MapViewer); + this.rootFrame.positionBounds(this.uiViewport); selectUnit(null); } - public void updatePortrait() { + public void update(final float deltaTime) { this.portrait.update(); + + int mouseX = Gdx.input.getX(); + int mouseY = Gdx.input.getY(); + final int minX = this.uiViewport.getScreenX(); + final int maxX = minX + this.uiViewport.getScreenWidth(); + final int minY = this.uiViewport.getScreenY(); + final int maxY = minY + this.uiViewport.getScreenHeight(); + final boolean left = mouseX <= (minX + 3); + final boolean right = mouseX >= (maxX - 3); + final boolean up = mouseY <= (minY + 3); + final boolean down = mouseY >= (maxY - 3); + this.cameraManager.applyVelocity(deltaTime, up, down, left, right); + + mouseX = Math.max(minX, Math.min(maxX, mouseX)); + mouseY = Math.max(minY, Math.min(maxY, mouseY)); + Gdx.input.setCursorPosition(mouseX, mouseY); + + screenCoordsVector.set(mouseX, mouseY); + this.uiViewport.unproject(screenCoordsVector); + this.cursorFrame.setFramePointX(FramePoint.LEFT, screenCoordsVector.x); + this.cursorFrame.setFramePointY(FramePoint.BOTTOM, screenCoordsVector.y); + + if (down) { + if (left) { + this.cursorFrame.setSequence("Scroll Down Left"); + } + else if (right) { + this.cursorFrame.setSequence("Scroll Down Right"); + } + else { + this.cursorFrame.setSequence("Scroll Down"); + } + } + else if (up) { + if (left) { + this.cursorFrame.setSequence("Scroll Up Left"); + } + else if (right) { + this.cursorFrame.setSequence("Scroll Up Right"); + } + else { + this.cursorFrame.setSequence("Scroll Up"); + } + } + else if (left) { + this.cursorFrame.setSequence("Scroll Left"); + } + else if (right) { + this.cursorFrame.setSequence("Scroll Right"); + } + else { + this.cursorFrame.setSequence("Normal"); + } + final float groundHeight = Math.max( + this.war3MapViewer.terrain.getGroundHeight(this.cameraManager.target.x, this.cameraManager.target.y), + this.war3MapViewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y)); + this.cameraManager.updateTargetZ(groundHeight); + this.cameraManager.updateCamera(); } public void render(final SpriteBatch batch, final BitmapFont font20, final GlyphLayout glyphLayout) { this.rootFrame.render(batch, font20, glyphLayout); - if (this.selectedUnit != null) { font20.setColor(Color.WHITE); - final COrder currentOrder = this.selectedUnit.getSimulationUnit().getCurrentOrder(); - for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) { - batch.draw(commandCardIcon.getTexture(), 1235 + (86.8f * commandCardIcon.getX()), - 190 - (88 * commandCardIcon.getY()), 78f, 78f); - if (((currentOrder != null) && (currentOrder.getOrderId() == commandCardIcon.getOrderId())) - || ((currentOrder == null) && (commandCardIcon.getOrderId() == CAbilityStop.ORDER_ID))) { - final int blendDstFunc = batch.getBlendDstFunc(); - final int blendSrcFunc = batch.getBlendSrcFunc(); - batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE); - batch.draw(this.activeButtonTexture, 1235 + (86.8f * commandCardIcon.getX()), - 190 - (88 * commandCardIcon.getY()), 78f, 78f); - batch.setBlendFunction(blendSrcFunc, blendDstFunc); - } - } } + this.meleeUIMinimap.render(batch, this.war3MapViewer.units); this.timeIndicator.setFrameByRatio(this.war3MapViewer.simulation.getGameTimeOfDay() / this.war3MapViewer.simulation.getGameplayConstants().getGameDayHours()); } @@ -238,12 +385,12 @@ public class MeleeUI implements CUnitStateListener { private static final class Portrait { private MdxComplexInstance modelInstance; - private final WarsmashGdxMapGame.PortraitCameraManager portraitCameraManager; + private final PortraitCameraManager portraitCameraManager; private final Scene portraitScene; - public Portrait(final War3MapViewer war3MapViewer) { - this.portraitScene = war3MapViewer.addSimpleScene(); - this.portraitCameraManager = new WarsmashGdxMapGame.PortraitCameraManager(); + public Portrait(final War3MapViewer war3MapViewer, final Scene portraitScene) { + this.portraitScene = portraitScene; + this.portraitCameraManager = new PortraitCameraManager(); this.portraitCameraManager.setupCamera(this.portraitScene); this.portraitScene.camera.viewport(new Rectangle(100, 0, 6400, 48)); } @@ -294,6 +441,11 @@ public class MeleeUI implements CUnitStateListener { } this.portrait.setSelectedUnit(unit); this.selectedUnit = unit; + for (int j = 0; j < COMMAND_CARD_HEIGHT; j++) { + for (int i = 0; i < COMMAND_CARD_WIDTH; i++) { + this.commandCard[j][i].setCommandButton(null); + } + } if (unit == null) { this.simpleNameValue.setText(""); this.unitLifeText.setText(""); @@ -340,9 +492,9 @@ public class MeleeUI implements CUnitStateListener { this.simpleBuildingActionLabel.setText(""); final boolean anyAttacks = unit.getSimulationUnit().getUnitType().getAttacks().size() > 0; - final UIFrame localArmorIcon; - final TextureFrame localArmorIconBackdrop; - final StringFrame localArmorInfoPanelIconValue; + final UIFrame localArmorIcon = this.armorIcon; + final TextureFrame localArmorIconBackdrop = this.armorIconBackdrop; + final StringFrame localArmorInfoPanelIconValue = this.armorInfoPanelIconValue; if (anyAttacks) { final CUnitAttack attackOne = unit.getSimulationUnit().getUnitType().getAttacks().get(0); this.attack1Icon.setVisible(attackOne.isShowUI()); @@ -358,17 +510,18 @@ public class MeleeUI implements CUnitStateListener { this.attack2Icon.setVisible(false); } - localArmorIcon = this.armorIcon; - localArmorIconBackdrop = this.armorIconBackdrop; - localArmorInfoPanelIconValue = this.armorInfoPanelIconValue; + this.armorIcon.addSetPoint( + new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT, + GameUI.convertX(this.uiViewport, 0f), GameUI.convertY(this.uiViewport, -0.06025f))); + this.armorIcon.positionBounds(this.uiViewport); } else { - this.armorIcon.setVisible(false); + this.attack1Icon.setVisible(false); this.attack2Icon.setVisible(false); - localArmorIcon = this.attack1Icon; - localArmorIconBackdrop = this.attack1IconBackdrop; - localArmorInfoPanelIconValue = this.attack1InfoPanelIconValue; + this.armorIcon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, + FramePoint.TOPLEFT, 0, GameUI.convertY(this.uiViewport, -0.030125f))); + this.armorIcon.positionBounds(this.uiViewport); } localArmorIcon.setVisible(true); @@ -380,10 +533,21 @@ public class MeleeUI implements CUnitStateListener { } localArmorIconBackdrop.setTexture(defenseTexture); localArmorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense())); + unit.populateCommandCard(this, this.war3MapViewer.getAbilityDataUI()); } } - public void resize() { + @Override + public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon, + final int orderId) { + final int x = Math.max(0, Math.min(COMMAND_CARD_WIDTH - 1, buttonPositionX)); + final int y = Math.max(0, Math.min(COMMAND_CARD_HEIGHT - 1, buttonPositionY)); + this.commandCard[y][x].setCommandButtonData(icon, orderId); + + } + + public void resize(final Rectangle viewport) { + this.cameraManager.resize(viewport); positionPortrait(); } @@ -472,4 +636,30 @@ public class MeleeUI implements CUnitStateListener { public RenderUnit getSelectedUnit() { return this.selectedUnit; } + + public boolean keyDown(final int keycode) { + return this.cameraManager.keyDown(keycode); + } + + public boolean keyUp(final int keycode) { + return this.cameraManager.keyUp(keycode); + } + + public void scrolled(final int amount) { + this.cameraManager.scrolled(amount); + } + + public boolean touchDown(final float screenX, final float screenY, final int button) { + if (this.meleeUIMinimap.containsMouse(screenX, screenY)) { + final Vector2 worldPoint = this.meleeUIMinimap.getWorldPointFromScreen(screenX, screenY); + this.cameraManager.target.x = worldPoint.x; + this.cameraManager.target.y = worldPoint.y; + return true; + } + screenCoordsVector.set(screenX, screenY); + this.uiViewport.unproject(screenCoordsVector); + this.rootFrame.touchDown(GameUI.unconvertX(this.uiViewport, screenCoordsVector.x), + GameUI.unconvertY(this.uiViewport, screenCoordsVector.y), button); + return false; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java new file mode 100644 index 0000000..fcaf00f --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java @@ -0,0 +1,62 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.ui; + +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector2; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; + +public class MeleeUIMinimap { + private final Rectangle minimap; + private final Rectangle minimapFilledArea; + private final Texture minimapTexture; + private final Rectangle playableMapArea; + private final Texture[] teamColors; + + public MeleeUIMinimap(final Rectangle displayArea, final Rectangle playableMapArea, final Texture minimapTexture, + final Texture[] teamColors) { + this.playableMapArea = playableMapArea; + this.minimapTexture = minimapTexture; + this.teamColors = teamColors; + this.minimap = displayArea; + final float worldWidth = playableMapArea.getWidth(); + final float worldHeight = playableMapArea.getHeight(); + final float worldSize = Math.max(worldWidth, worldHeight); + final float minimapFilledWidth = (worldWidth / worldSize) * this.minimap.width; + final float minimapFilledHeight = (worldHeight / worldSize) * this.minimap.height; + + this.minimapFilledArea = new Rectangle(this.minimap.x + ((this.minimap.width - minimapFilledWidth) / 2), + this.minimap.y + ((this.minimap.height - minimapFilledHeight) / 2), minimapFilledWidth, + minimapFilledHeight); + } + + public void render(final SpriteBatch batch, final Iterable units) { + batch.draw(this.minimapTexture, this.minimap.x, this.minimap.y, this.minimap.width, this.minimap.height); + + for (final RenderUnit unit : units) { + final Texture minimapIcon = this.teamColors[unit.playerIndex]; + batch.draw(minimapIcon, + this.minimapFilledArea.x + + (((unit.location[0] - this.playableMapArea.getX()) / (this.playableMapArea.getWidth())) + * this.minimapFilledArea.width), + this.minimapFilledArea.y + + (((unit.location[1] - this.playableMapArea.getY()) / (this.playableMapArea.getHeight())) + * this.minimapFilledArea.height), + 4, 4); + } + } + + public Vector2 getWorldPointFromScreen(final float screenX, final float screenY) { + final Rectangle filledArea = this.minimapFilledArea; + final float clickX = (screenX - filledArea.x) / filledArea.width; + final float clickY = (screenY - filledArea.y) / filledArea.height; + final float worldX = (clickX * this.playableMapArea.width) + this.playableMapArea.x; + final float worldY = (clickY * this.playableMapArea.height) + this.playableMapArea.y; + return new Vector2(worldX, worldY); + + } + + public boolean containsMouse(final float x, final float y) { + return this.minimapFilledArea.contains(x, y); + } +}