diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java index ac61fe3..7944cc3 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java @@ -130,7 +130,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.viewer.enableAudio(); } try { - this.viewer.loadMap(this.warsmashIni.get("Map").getField("FilePath")); + this.viewer.loadMap(this.warsmashIni.get("Map").getField("FilePath"), 0); } catch (final IOException e) { throw new RuntimeException(e); @@ -222,7 +222,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv 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, + this.meleeUI = new MeleeUI(this.viewer.mapMpq, this.uiViewport, fontGenerator, this.uiScene, portraitScene, cameraPresets, cameraRates, this.viewer, new RootFrameListener() { @Override public void onCreate(final GameUI rootFrame) { diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java index 2bdc4dd..24edd47 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -30,6 +30,7 @@ import com.etheller.warsmash.parsers.fdf.frames.AbstractUIFrame; import com.etheller.warsmash.parsers.fdf.frames.FilterModeTextureFrame; import com.etheller.warsmash.parsers.fdf.frames.SetPoint; import com.etheller.warsmash.parsers.fdf.frames.SimpleFrame; +import com.etheller.warsmash.parsers.fdf.frames.SimpleStatusBarFrame; import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame; import com.etheller.warsmash.parsers.fdf.frames.StringFrame; import com.etheller.warsmash.parsers.fdf.frames.TextureFrame; @@ -104,16 +105,31 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { catch (final IOException e) { throw new RuntimeException(e); } + // TODO eliminate duplicate read of skin TXT!! + if (dataSource.has("war3mapSkin.txt")) { + try (InputStream miscDataTxtStream = dataSource.getResourceAsStream("war3mapSkin.txt")) { + skinsTable.readTXT(miscDataTxtStream, true); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + } // final Element main = skinsTable.get("Main"); // final String skinsField = main.getField("Skins"); // final String[] skins = skinsField.split(","); final Element defaultSkin = skinsTable.get("Default"); final Element userSkin = skinsTable.get(skin); + final Element customSkin = skinsTable.get("CustomSkin"); for (final String key : defaultSkin.keySet()) { if (!userSkin.hasField(key)) { userSkin.setField(key, defaultSkin.getField(key)); } } + if (customSkin != null) { + for (final String key : customSkin.keySet()) { + userSkin.setField(key, customSkin.getField(key)); + } + } return userSkin; } @@ -125,16 +141,31 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { catch (final IOException e) { throw new RuntimeException(e); } + // TODO eliminate duplicate read of skin TXT!! + if (dataSource.has("war3mapSkin.txt")) { + try (InputStream miscDataTxtStream = dataSource.getResourceAsStream("war3mapSkin.txt")) { + skinsTable.readTXT(miscDataTxtStream, true); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + } final Element main = skinsTable.get("Main"); final String skinsField = main.getField("Skins"); final String[] skins = skinsField.split(","); final Element defaultSkin = skinsTable.get("Default"); final Element userSkin = skinsTable.get(skins[skinIndex]); + final Element customSkin = skinsTable.get("CustomSkin"); for (final String key : defaultSkin.keySet()) { if (!userSkin.hasField(key)) { userSkin.setField(key, defaultSkin.getField(key)); } } + if (customSkin != null) { + for (final String key : customSkin.keySet()) { + userSkin.setField(key, customSkin.getField(key)); + } + } return userSkin; } @@ -252,6 +283,17 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } inflatedFrame = simpleFrame; } + else if ("SIMPLESTATUSBAR".equals(frameDefinition.getFrameType())) { + final boolean decorateFileNames = frameDefinition.has("DecorateFileNames") + || ((parentDefinitionIfAvailable != null) + && parentDefinitionIfAvailable.has("DecorateFileNames")); + final SimpleStatusBarFrame simpleStatusBarFrame = new SimpleStatusBarFrame(frameDefinition.getName(), + parent, decorateFileNames); + for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { + simpleStatusBarFrame.add(inflate(childDefinition, simpleStatusBarFrame, frameDefinition)); + } + inflatedFrame = simpleStatusBarFrame; + } else if ("SPRITE".equals(frameDefinition.getFrameType())) { final SpriteFrame spriteFrame = new SpriteFrame(frameDefinition.getName(), parent, this.uiScene, viewport2); @@ -448,6 +490,10 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } public Scene getUiScene() { - return uiScene; + return this.uiScene; + } + + public FrameTemplateEnvironment getTemplates() { + return this.templates; } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java new file mode 100644 index 0000000..6fcef72 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleStatusBarFrame.java @@ -0,0 +1,39 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; +import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition; + +public class SimpleStatusBarFrame extends AbstractUIFrame { + private final boolean decorateFileNames; + private final TextureFrame barFrame; + private final TextureFrame borderFrame; + + public SimpleStatusBarFrame(final String name, final UIFrame parent, final boolean decorateFileNames) { + super(name, parent); + this.decorateFileNames = decorateFileNames; + this.barFrame = new TextureFrame(name + "Bar", this, decorateFileNames, new Vector4Definition(0, 1, 0, 1)); + this.borderFrame = new TextureFrame(name + "Border", this, decorateFileNames, + new Vector4Definition(0, 1, 0, 1)); + this.borderFrame.setSetAllPoints(true); + this.barFrame.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this, FramePoint.TOPLEFT, 0, 0)); + this.barFrame.addSetPoint(new SetPoint(FramePoint.BOTTOMLEFT, this, FramePoint.BOTTOMLEFT, 0, 0)); + add(this.barFrame); + add(this.borderFrame); + } + + public boolean isDecorateFileNames() { + return this.decorateFileNames; + } + + public void setValue(final float value) { + this.barFrame.setWidth(this.renderBounds.width * value); + } + + public TextureFrame getBarFrame() { + return this.barFrame; + } + + public TextureFrame getBorderFrame() { + return this.borderFrame; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/AudioBufferSource.java b/core/src/com/etheller/warsmash/viewer5/AudioBufferSource.java index 7a2976f..8e40532 100644 --- a/core/src/com/etheller/warsmash/viewer5/AudioBufferSource.java +++ b/core/src/com/etheller/warsmash/viewer5/AudioBufferSource.java @@ -9,9 +9,9 @@ public class AudioBufferSource { } - public void start(final int value) { + public void start(final int value, final float volume, final float pitch) { if (this.buffer != null) { - this.buffer.play(1); + this.buffer.play(volume, pitch, 0.0f); } } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectEmitterObject.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectEmitterObject.java index 68d07a5..38396ff 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectEmitterObject.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectEmitterObject.java @@ -22,328 +22,348 @@ import com.etheller.warsmash.viewer5.Texture; import com.etheller.warsmash.viewer5.handlers.EmitterObject; public class EventObjectEmitterObject extends GenericObject implements EmitterObject { - private static final class LoadGenericSoundCallback implements LoadGenericCallback { - private final String filename; + private static final class LoadGenericSoundCallback implements LoadGenericCallback { + private final String filename; - public LoadGenericSoundCallback(final String filename) { - this.filename = filename; - } + public LoadGenericSoundCallback(final String filename) { + this.filename = filename; + } - @Override - public Object call(final InputStream data) { - final FileHandle temp = new FileHandle(this.filename) { - @Override - public InputStream read() { - return data; - } + @Override + public Object call(final InputStream data) { + final FileHandle temp = new FileHandle(this.filename) { + @Override + public InputStream read() { + return data; + } - ; - }; - if (data != null) { - return Gdx.audio.newSound(temp); - } else { - System.err.println("Warning: missing sound file: " + this.filename); - return null; - } - } - } + ; + }; + if (data != null) { + return Gdx.audio.newSound(temp); + } + else { + System.err.println("Warning: missing sound file: " + this.filename); + return null; + } + } + } - private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() { + private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() { - @Override - public Object call(final InputStream data) { - final StringBuilder stringBuilder = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) { - String line; - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - stringBuilder.append("\n"); - } - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (final IOException e) { - throw new RuntimeException(e); - } - return new MappedData(stringBuilder.toString()); - } - }; + @Override + public Object call(final InputStream data) { + final StringBuilder stringBuilder = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) { + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append("\n"); + } + } + catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + return new MappedData(stringBuilder.toString()); + } + }; - private int geometryEmitterType = -1; - public final String type; - private final String id; - public final long[] keyFrames; - private long globalSequence = -1; - private final long[] defval = {1}; - public MdxModel internalModel; - public Texture internalTexture; - public float[][] colors; - public float[] intervalTimes; - public float scale; - public int columns; - public int rows; - public float lifeSpan; - public int blendSrc; - public int blendDst; - public float[][] intervals; - public float distanceCutoff; - private float maxDistance; - public float minDistance; - private float pitch; - private float pitchVariance; - private float volume; - public List decodedBuffers = new ArrayList<>(); - /** - * If this is an SPL/UBR emitter object, ok will be set to true if the tables - * are loaded. - *

- * This is because, like the other geometry emitters, it is fine to use them - * even if the textures don't load. - *

- * The particles will simply be black. - */ - private boolean ok = false; + private int geometryEmitterType = -1; + public final String type; + private final String id; + public final long[] keyFrames; + private long globalSequence = -1; + private final long[] defval = { 1 }; + public MdxModel internalModel; + public Texture internalTexture; + public float[][] colors; + public float[] intervalTimes; + public float scale; + public int columns; + public int rows; + public float lifeSpan; + public int blendSrc; + public int blendDst; + public float[][] intervals; + public float distanceCutoff; + private float maxDistance; + public float minDistance; + public float pitch; + public float pitchVariance; + public float volume; + public List decodedBuffers = new ArrayList<>(); + /** + * If this is an SPL/UBR emitter object, ok will be set to true if the tables + * are loaded. + *

+ * This is because, like the other geometry emitters, it is fine to use them + * even if the textures don't load. + *

+ * The particles will simply be black. + */ + private boolean ok = false; - public EventObjectEmitterObject(final MdxModel model, - final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) { - super(model, eventObject, index); + public EventObjectEmitterObject(final MdxModel model, + final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) { + super(model, eventObject, index); - final ModelViewer viewer = model.viewer; - final String name = eventObject.getName(); - String type = name.substring(0, 3); - final String id = name.substring(4); + final ModelViewer viewer = model.viewer; + final String name = eventObject.getName(); + String type = name.substring(0, 3); + final String id = name.substring(4); - // Same thing - if ("FPT".equals(type)) { - type = "SPL"; - } + // Same thing + if ("FPT".equals(type)) { + type = "SPL"; + } - if ("SPL".equals(type)) { - this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT; - } else if ("UBR".equals(type)) { - this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT; - } else if ("SPN".equals(type)) { - this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN; - } + if ("SPL".equals(type)) { + this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT; + } + else if ("UBR".equals(type)) { + this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT; + } + else if ("SPN".equals(type)) { + this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN; + } - this.type = type; - this.id = id; - this.keyFrames = eventObject.getKeyFrames(); + this.type = type; + this.id = id; + this.keyFrames = eventObject.getKeyFrames(); - final int globalSequenceId = eventObject.getGlobalSequenceId(); - if (globalSequenceId != -1) { - this.globalSequence = model.getGlobalSequences().get(globalSequenceId); - } + final int globalSequenceId = eventObject.getGlobalSequenceId(); + if (globalSequenceId != -1) { + this.globalSequence = model.getGlobalSequences().get(globalSequenceId); + } - final List tables = new ArrayList<>(); - final PathSolver pathSolver = model.pathSolver; - final Object solverParams = model.solverParams; + final List tables = new ArrayList<>(); + final PathSolver pathSolver = model.pathSolver; + final Object solverParams = model.solverParams; - if ("SPN".equals(type)) { - tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc, - FetchDataTypeName.SLK, mappedDataCallback)); - } else if ("SPL".equals(type)) { - tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc, - FetchDataTypeName.SLK, mappedDataCallback)); - } else if ("UBR".equals(type)) { - tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc, - FetchDataTypeName.SLK, mappedDataCallback)); - } else if ("SND".equals(type)) { - if (!model.reforged) { - tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc, - FetchDataTypeName.SLK, mappedDataCallback)); - } - tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc, - FetchDataTypeName.SLK, mappedDataCallback)); - } else { - // Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named - // "Point01". - return; - } + if ("SPN".equals(type)) { + tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc, + FetchDataTypeName.SLK, mappedDataCallback)); + } + else if ("SPL".equals(type)) { + tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc, + FetchDataTypeName.SLK, mappedDataCallback)); + } + else if ("UBR".equals(type)) { + tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc, + FetchDataTypeName.SLK, mappedDataCallback)); + } + else if ("SND".equals(type)) { + if (!model.reforged) { + tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc, + FetchDataTypeName.SLK, mappedDataCallback)); + } + tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc, + FetchDataTypeName.SLK, mappedDataCallback)); + } + else { + // Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named + // "Point01". + return; + } - // TODO I am scrapping some async stuff with promises here from the JS and - // calling load - this.load(tables); - } + // TODO I am scrapping some async stuff with promises here from the JS and + // calling load + this.load(tables); + } - private float getFloat(final MappedDataRow row, final String name) { - final Float x = (Float) row.get(name); - if (x == null) { - return Float.NaN; - } else { - return x.floatValue(); - } - } + private float getFloat(final MappedDataRow row, final String name) { + final Float x = (Float) row.get(name); + if (x == null) { + return Float.NaN; + } + else { + return x.floatValue(); + } + } - private int getInt(final MappedDataRow row, final String name) { - return getInt(row, name, Integer.MIN_VALUE); - } + private int getInt(final MappedDataRow row, final String name) { + return getInt(row, name, Integer.MIN_VALUE); + } - private int getInt(final MappedDataRow row, final String name, final int defaultValue) { - final Number x = (Number) row.get(name); - if (x == null) { - return defaultValue; - } else { - return x.intValue(); - } - } + private int getInt(final MappedDataRow row, final String name, final int defaultValue) { + final Number x = (Number) row.get(name); + if (x == null) { + return defaultValue; + } + else { + return x.intValue(); + } + } - private void load(final List tables) { - final MappedData firstTable = (MappedData) tables.get(0).data; - if (firstTable == null) { - return; - } - final MappedDataRow row = firstTable.getRow(this.id.trim()); + private void load(final List tables) { + final MappedData firstTable = (MappedData) tables.get(0).data; + if (firstTable == null) { + return; + } + final MappedDataRow row = firstTable.getRow(this.id.trim()); - if (row != null) { - final MdxModel model = this.model; - final ModelViewer viewer = model.viewer; - final PathSolver pathSolver = model.pathSolver; + if (row != null) { + final MdxModel model = this.model; + final ModelViewer viewer = model.viewer; + final PathSolver pathSolver = model.pathSolver; - if ("SPN".equals(this.type)) { - this.internalModel = (MdxModel) viewer.load(((String) row.get("Model")).replace(".mdl", ".mdx"), - pathSolver, model.solverParams); + if ("SPN".equals(this.type)) { + this.internalModel = (MdxModel) viewer.load(((String) row.get("Model")).replace(".mdl", ".mdx"), + pathSolver, model.solverParams); - if (this.internalModel != null) { - // TODO javascript async code removed here + if (this.internalModel != null) { + // TODO javascript async code removed here // this.internalModel.whenLoaded((model) => this.ok = model.ok) - this.ok = this.internalModel.ok; - } - } else if ("SPL".equals(this.type) || "UBR".equals(this.type)) { - final String texturesExt = model.reforged ? ".dds" : ".blp"; + this.ok = this.internalModel.ok; + } + } + else if ("SPL".equals(this.type) || "UBR".equals(this.type)) { + final String texturesExt = model.reforged ? ".dds" : ".blp"; - this.internalTexture = (Texture) viewer.load( - "ReplaceableTextures\\Splats\\" + row.get("file") + texturesExt, pathSolver, - model.solverParams); + this.internalTexture = (Texture) viewer.load( + "ReplaceableTextures\\Splats\\" + row.get("file") + texturesExt, pathSolver, + model.solverParams); - this.scale = getFloat(row, "Scale"); - this.colors = new float[][]{ - {getFloat(row, "StartR"), getFloat(row, "StartG"), getFloat(row, "StartB"), - getFloat(row, "StartA")}, - {getFloat(row, "MiddleR"), getFloat(row, "MiddleG"), getFloat(row, "MiddleB"), - getFloat(row, "MiddleA")}, - {getFloat(row, "EndR"), getFloat(row, "EndG"), getFloat(row, "EndB"), - getFloat(row, "EndA")}}; + this.scale = getFloat(row, "Scale"); + this.colors = new float[][] { + { getFloat(row, "StartR"), getFloat(row, "StartG"), getFloat(row, "StartB"), + getFloat(row, "StartA") }, + { getFloat(row, "MiddleR"), getFloat(row, "MiddleG"), getFloat(row, "MiddleB"), + getFloat(row, "MiddleA") }, + { getFloat(row, "EndR"), getFloat(row, "EndG"), getFloat(row, "EndB"), + getFloat(row, "EndA") } }; - if ("SPL".equals(this.type)) { - this.columns = getInt(row, "Columns"); - this.rows = getInt(row, "Rows"); - this.lifeSpan = getFloat(row, "Lifespan") + getFloat(row, "Decay"); - this.intervalTimes = new float[]{getFloat(row, "Lifespan"), getFloat(row, "Decay")}; - this.intervals = new float[][]{ - {getFloat(row, "UVLifespanStart"), getFloat(row, "UVLifespanEnd"), - getFloat(row, "LifespanRepeat")}, - {getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"), - getFloat(row, "DecayRepeat")},}; - } else { - this.columns = 1; - this.rows = 1; - this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay"); - this.intervalTimes = new float[]{getFloat(row, "BirthTime"), getFloat(row, "PauseTime"), - getFloat(row, "Decay")}; - } + if ("SPL".equals(this.type)) { + this.columns = getInt(row, "Columns"); + this.rows = getInt(row, "Rows"); + this.lifeSpan = getFloat(row, "Lifespan") + getFloat(row, "Decay"); + this.intervalTimes = new float[] { getFloat(row, "Lifespan"), getFloat(row, "Decay") }; + this.intervals = new float[][] { + { getFloat(row, "UVLifespanStart"), getFloat(row, "UVLifespanEnd"), + getFloat(row, "LifespanRepeat") }, + { getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"), + getFloat(row, "DecayRepeat") }, }; + } + else { + this.columns = 1; + this.rows = 1; + this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay"); + this.intervalTimes = new float[] { getFloat(row, "BirthTime"), getFloat(row, "PauseTime"), + getFloat(row, "Decay") }; + } - final int[] blendModes = FilterMode - .emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode - .fromId(getInt(row, "BlendMode"))); + final int[] blendModes = FilterMode + .emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode + .fromId(getInt(row, "BlendMode"))); - this.blendSrc = blendModes[0]; - this.blendDst = blendModes[1]; + this.blendSrc = blendModes[0]; + this.blendDst = blendModes[1]; - this.ok = true; - } else if ("SND".equals(this.type)) { - // Only load sounds if audio is enabled. - // This is mostly to save on bandwidth and loading time, especially when loading - // full maps. - if (viewer.audioEnabled) { - final MappedData animSounds = (MappedData) tables.get(1).data; + this.ok = true; + } + else if ("SND".equals(this.type)) { + // Only load sounds if audio is enabled. + // This is mostly to save on bandwidth and loading time, especially when loading + // full maps. + if (viewer.audioEnabled) { + final MappedData animSounds = (MappedData) tables.get(1).data; - final MappedDataRow animSoundsRow = animSounds.getRow((String) row.get("SoundLabel")); + final MappedDataRow animSoundsRow = animSounds.getRow((String) row.get("SoundLabel")); - if (animSoundsRow != null) { - this.distanceCutoff = getFloat(animSoundsRow, "DistanceCutoff"); - this.maxDistance = getFloat(animSoundsRow, "MaxDistance"); - this.minDistance = getFloat(animSoundsRow, "MinDistance"); - this.pitch = getFloat(animSoundsRow, "Pitch"); - this.pitchVariance = getFloat(animSoundsRow, "PitchVariance"); - this.volume = getFloat(animSoundsRow, "Volume"); + if (animSoundsRow != null) { + this.distanceCutoff = getFloat(animSoundsRow, "DistanceCutoff"); + this.maxDistance = getFloat(animSoundsRow, "MaxDistance"); + this.minDistance = getFloat(animSoundsRow, "MinDistance"); + this.pitch = getFloat(animSoundsRow, "Pitch"); + this.pitchVariance = getFloat(animSoundsRow, "PitchVariance"); + this.volume = getFloat(animSoundsRow, "Volume") / 127f; - final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(","); - final GenericResource[] resources = new GenericResource[fileNames.length]; - for (int i = 0; i < fileNames.length; i++) { - final String path = ((String) animSoundsRow.get("DirectoryBase")) + fileNames[i]; - try { - final String pathString = pathSolver.solve(path, model.solverParams).finalSrc; - final GenericResource genericResource = viewer.loadGeneric(pathString, - FetchDataTypeName.ARRAY_BUFFER, new LoadGenericSoundCallback(pathString)); - if (genericResource == null) { - System.err.println("Null sound: " + fileNames[i]); - } - resources[i] = genericResource; - } catch (final Exception exc) { - System.err.println("Failed to load sound: " + path); - exc.printStackTrace(); - } - } + final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(","); + final GenericResource[] resources = new GenericResource[fileNames.length]; + for (int i = 0; i < fileNames.length; i++) { + final String path = ((String) animSoundsRow.get("DirectoryBase")) + fileNames[i]; + try { + final String pathString = pathSolver.solve(path, model.solverParams).finalSrc; + final GenericResource genericResource = viewer.loadGeneric(pathString, + FetchDataTypeName.ARRAY_BUFFER, new LoadGenericSoundCallback(pathString)); + if (genericResource == null) { + System.err.println("Null sound: " + fileNames[i]); + } + resources[i] = genericResource; + } + catch (final Exception exc) { + System.err.println("Failed to load sound: " + path); + exc.printStackTrace(); + } + } - // TODO JS async removed - for (final GenericResource resource : resources) { - if (resource != null) { - this.decodedBuffers.add((Sound) resource.data); - } - } - this.ok = true; - } - } - } else { - System.err.println("Unknown event object type: " + this.type + this.id); - } - } else { - System.err.println("Unknown event object ID: " + this.type + this.id); - } - } + // TODO JS async removed + for (final GenericResource resource : resources) { + if (resource != null) { + this.decodedBuffers.add((Sound) resource.data); + } + } + this.ok = true; + } + } + } + else { + System.err.println("Unknown event object type: " + this.type + this.id); + } + } + else { + System.err.println("Unknown event object ID: " + this.type + this.id); + } + } - public int getValue(final long[] out, final MdxComplexInstance instance) { - if (this.globalSequence != -1) { + public int getValue(final long[] out, final MdxComplexInstance instance) { + if (this.globalSequence != -1) { - return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence); - } else if (instance.sequence != -1) { - final long[] interval = this.model.getSequences().get(instance.sequence).getInterval(); + return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence); + } + else if (instance.sequence != -1) { + final long[] interval = this.model.getSequences().get(instance.sequence).getInterval(); - return this.getValueAtTime(out, instance.frame, interval[0], interval[1]); - } else { - out[0] = this.defval[0]; + return this.getValueAtTime(out, instance.frame, interval[0], interval[1]); + } + else { + out[0] = this.defval[0]; - return -1; - } - } + return -1; + } + } - public int getValueAtTime(final long[] out, final long frame, final long start, final long end) { - if ((frame >= start) && (frame <= end)) { - for (int i = this.keyFrames.length - 1; i > -1; i--) { - if (this.keyFrames[i] < start) { - out[0] = 0; + public int getValueAtTime(final long[] out, final long frame, final long start, final long end) { + if ((frame >= start) && (frame <= end)) { + for (int i = this.keyFrames.length - 1; i > -1; i--) { + if (this.keyFrames[i] < start) { + out[0] = 0; - return i; - } else if (this.keyFrames[i] <= frame) { - out[0] = 1; + return i; + } + else if (this.keyFrames[i] <= frame) { + out[0] = 1; - return i; - } - } - } + return i; + } + } + } - out[0] = 0; + out[0] = 0; - return -1; - } + return -1; + } - @Override - public boolean ok() { - return this.ok; - } + @Override + public boolean ok() { + return this.ok; + } - @Override - public int getGeometryEmitterType() { - return this.geometryEmitterType; - } + @Override + public int getGeometryEmitterType() { + return this.geometryEmitterType; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectSnd.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectSnd.java index 575fea3..8c38305 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectSnd.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EventObjectSnd.java @@ -47,7 +47,9 @@ public class EventObjectSnd extends EmittedObject sounds = new ArrayList<>(); private final float volume; private final float pitch; - private final float pitchVariation; + private final float pitchVariance; private final float minDistance; private final float maxDistance; private final float distanceCutoff; private Sound lastPlayedSound; - public static UnitAckSound create(final DataSource dataSource, final DataTable unitAckSounds, final String soundName, - final String soundType) { + public static UnitAckSound create(final DataSource dataSource, final DataTable unitAckSounds, + final String soundName, final String soundType) { final Element row = unitAckSounds.get(soundName + soundType); if (row == null) { return SILENT; @@ -40,13 +40,17 @@ public final class UnitAckSound { if ((directoryBase.length() > 1) && !directoryBase.endsWith("\\")) { directoryBase += "\\"; } - final float volume = row.getFieldFloatValue("Volume"); + final float volume = row.getFieldFloatValue("Volume") / 127f; final float pitch = row.getFieldFloatValue("Pitch"); - final float pitchVariation = row.getFieldFloatValue("PitchVariance"); + float pitchVariance = row.getFieldFloatValue("PitchVariance"); + if (pitchVariance == 1.0f) { + pitchVariance = 0.0f; + } final float minDistance = row.getFieldFloatValue("MinDistance"); final float maxDistance = row.getFieldFloatValue("MaxDistance"); final float distanceCutoff = row.getFieldFloatValue("DistanceCutoff"); - final UnitAckSound sound = new UnitAckSound(volume, pitch, pitchVariation, minDistance, maxDistance, distanceCutoff); + final UnitAckSound sound = new UnitAckSound(volume, pitch, pitchVariance, minDistance, maxDistance, + distanceCutoff); for (final String fileName : fileNames.split(",")) { String filePath = directoryBase + fileName; if (!filePath.toLowerCase().endsWith(".wav")) { @@ -63,7 +67,7 @@ public final class UnitAckSound { final float maxDistance, final float distanceCutoff) { this.volume = volume; this.pitch = pitch; - this.pitchVariation = pitchVariation; + this.pitchVariance = pitchVariation; this.minDistance = minDistance; this.maxDistance = maxDistance; this.distanceCutoff = distanceCutoff; @@ -96,7 +100,8 @@ public final class UnitAckSound { source.connect(panner); // Make a sound. - source.start(0); + source.start(0, this.volume, + (this.pitch + ((float) Math.random() * this.pitchVariance * 2)) - this.pitchVariance); this.lastPlayedSound = source.buffer; final float duration = Extensions.soundLengthExtension.getDuration(this.lastPlayedSound); unit.lastUnitResponseEndTimeMillis = millisTime + (long) (1000 * duration); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/UnitSound.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/UnitSound.java index f0f0585..62b910f 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/UnitSound.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/UnitSound.java @@ -22,7 +22,7 @@ public final class UnitSound { private final List sounds = new ArrayList<>(); private final float volume; private final float pitch; - private final float pitchVariation; + private final float pitchVariance; private final float minDistance; private final float maxDistance; private final float distanceCutoff; @@ -40,13 +40,16 @@ public final class UnitSound { if ((directoryBase.length() > 1) && !directoryBase.endsWith("\\")) { directoryBase += "\\"; } - final float volume = row.getFieldFloatValue("Volume"); + final float volume = row.getFieldFloatValue("Volume") / 127f; final float pitch = row.getFieldFloatValue("Pitch"); - final float pitchVariation = row.getFieldFloatValue("PitchVariance"); + float pitchVariance = row.getFieldFloatValue("PitchVariance"); + if (pitchVariance == 1.0f) { + pitchVariance = 0.0f; + } final float minDistance = row.getFieldFloatValue("MinDistance"); final float maxDistance = row.getFieldFloatValue("MaxDistance"); final float distanceCutoff = row.getFieldFloatValue("DistanceCutoff"); - final UnitSound sound = new UnitSound(volume, pitch, pitchVariation, minDistance, maxDistance, distanceCutoff); + final UnitSound sound = new UnitSound(volume, pitch, pitchVariance, minDistance, maxDistance, distanceCutoff); for (final String fileName : fileNames.split(",")) { String filePath = directoryBase + fileName; if (!filePath.toLowerCase().endsWith(".wav")) { @@ -63,7 +66,7 @@ public final class UnitSound { final float maxDistance, final float distanceCutoff) { this.volume = volume; this.pitch = pitch; - this.pitchVariation = pitchVariation; + this.pitchVariance = pitchVariation; this.minDistance = minDistance; this.maxDistance = maxDistance; this.distanceCutoff = distanceCutoff; @@ -112,7 +115,8 @@ public final class UnitSound { source.connect(panner); // Make a sound. - source.start(0); + source.start(0, this.volume, + (this.pitch + ((float) Math.random() * this.pitchVariance * 2)) - this.pitchVariance); this.lastPlayedSound = source.buffer; return true; } 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 c148071..fd1ade4 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -96,1402 +96,1446 @@ import mpq.MPQArchive; import mpq.MPQException; public class War3MapViewer extends ModelViewer { - private static final War3ID UNIT_FILE = War3ID.fromString("umdl"); - private static final War3ID UBER_SPLAT = War3ID.fromString("uubs"); - private static final War3ID UNIT_SHADOW = War3ID.fromString("ushu"); - private static final War3ID UNIT_SHADOW_X = War3ID.fromString("ushx"); - private static final War3ID UNIT_SHADOW_Y = War3ID.fromString("ushy"); - private static final War3ID UNIT_SHADOW_W = War3ID.fromString("ushw"); - private static final War3ID UNIT_SHADOW_H = War3ID.fromString("ushh"); - private static final War3ID BUILDING_SHADOW = War3ID.fromString("ushb"); - public static final War3ID UNIT_SELECT_SCALE = War3ID.fromString("ussc"); - private static final War3ID UNIT_SELECT_HEIGHT = War3ID.fromString("uslz"); - private static final War3ID UNIT_SOUNDSET = War3ID.fromString("usnd"); - 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]; - public static final Ray gdxRayHeap = new Ray(); - private static final Vector2 mousePosHeap = new Vector2(); - private static final Vector3 normalHeap = new Vector3(); - public static final Vector3 intersectionHeap = new Vector3(); - private static final Rectangle rectangleHeap = new Rectangle(); - public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation(); - - public PathSolver wc3PathSolver = PathSolver.DEFAULT; - public SolverParams solverParams = new SolverParams(); - public WorldScene worldScene; - public boolean anyReady; - public MappedData terrainData = new MappedData(); - public MappedData cliffTypesData = new MappedData(); - public MappedData waterData = new MappedData(); - public boolean terrainReady; - public boolean cliffsReady; - public boolean doodadsAndDestructiblesLoaded; - public MappedData doodadsData = new MappedData(); - public MappedData doodadMetaData = new MappedData(); - public MappedData destructableMetaData = new MappedData(); - public List doodads = new ArrayList<>(); - public List terrainDoodads = new ArrayList<>(); - public boolean doodadsReady; - public boolean unitsAndItemsLoaded; - public MappedData unitsData = new MappedData(); - public MappedData unitMetaData = new MappedData(); - public List units = new ArrayList<>(); - public List items = new ArrayList<>(); - public List projectiles = new ArrayList<>(); - public boolean unitsReady; - public War3Map mapMpq; - public PathSolver mapPathSolver = PathSolver.DEFAULT; - - private final DataSource gameDataSource; - - public Terrain terrain; - public int renderPathing = 0; - public int renderLighting = 1; - - public List selModels = new ArrayList<>(); - public List selected = new ArrayList<>(); - private DataTable unitAckSoundsTable; - private DataTable unitCombatSoundsTable; - public DataTable miscData; - private DataTable unitGlobalStrings; - public DataTable uiSoundsTable; - private MdxComplexInstance confirmationInstance; - public MdxComplexInstance dncUnit; - public MdxComplexInstance dncUnitDay; - public MdxComplexInstance dncTerrain; - public MdxComplexInstance dncTarget; - 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(); - - private final Random seededRandom = new Random(1337L); - - private final Map filePathToPathingMap = new HashMap<>(); - - private final List selectionCircleSizes = new ArrayList<>(); - - private final Map unitToRenderPeer = new HashMap<>(); - private final Map unitIdToTypeData = new HashMap<>(); - private GameUI gameUI; - private Vector3 lightDirection; - - private Quadtree walkableObjectsTree; - private final QuadtreeIntersectorFindsWalkableRenderHeight walkablesIntersector = new QuadtreeIntersectorFindsWalkableRenderHeight(); - private final QuadtreeIntersectorFindsHitPoint walkablesIntersectionFinder = new QuadtreeIntersectorFindsHitPoint(); - private final QuadtreeIntersectorFindsHighestWalkable intersectorFindsHighestWalkable = new QuadtreeIntersectorFindsHighestWalkable(); - - private KeyedSounds uiSounds; - - public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) { - super(dataSource, canvas); - this.gameDataSource = dataSource; - - final WebGL webGL = this.webGL; - - this.addHandler(new MdxHandler()); - - this.wc3PathSolver = PathSolver.DEFAULT; - - this.worldScene = this.addWorldScene(); - - if (!this.dynamicShadowManager.setup(webGL)) { - throw new IllegalStateException("FrameBuffer setup failed"); - } - } - - public void loadSLKs(final WorldEditStrings worldEditStrings) throws IOException { - final GenericResource terrain = this.loadMapGeneric("TerrainArt\\Terrain.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource cliffTypes = this.loadMapGeneric("TerrainArt\\CliffTypes.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource water = this.loadMapGeneric("TerrainArt\\Water.slk", FetchDataTypeName.SLK, - stringDataCallback); - - // == when loaded, which is always in our system == - this.terrainData.load(terrain.data.toString()); - this.cliffTypesData.load(cliffTypes.data.toString()); - this.waterData.load(water.data.toString()); - // emit terrain loaded?? - - final GenericResource doodads = this.loadMapGeneric("Doodads\\Doodads.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource doodadMetaData = this.loadMapGeneric("Doodads\\DoodadMetaData.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource destructableData = this.loadMapGeneric("Units\\DestructableData.slk", - FetchDataTypeName.SLK, stringDataCallback); - final GenericResource destructableMetaData = this.loadMapGeneric("Units\\DestructableMetaData.slk", - FetchDataTypeName.SLK, stringDataCallback); - - // == when loaded, which is always in our system == - this.doodadsAndDestructiblesLoaded = true; - this.doodadsData.load(doodads.data.toString()); - this.doodadMetaData.load(doodadMetaData.data.toString()); - this.doodadsData.load(destructableData.data.toString()); - this.destructableMetaData.load(destructableData.data.toString()); - // emit doodads loaded - - final GenericResource unitData = this.loadMapGeneric("Units\\UnitData.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource unitUi = this.loadMapGeneric("Units\\unitUI.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource itemData = this.loadMapGeneric("Units\\ItemData.slk", FetchDataTypeName.SLK, - stringDataCallback); - final GenericResource unitMetaData = this.loadMapGeneric("Units\\UnitMetaData.slk", FetchDataTypeName.SLK, - stringDataCallback); - - // == when loaded, which is always in our system == - this.unitsAndItemsLoaded = true; - this.unitsData.load(unitData.data.toString()); - this.unitsData.load(unitUi.data.toString()); - this.unitsData.load(itemData.data.toString()); - this.unitMetaData.load(unitMetaData.data.toString()); - // emit loaded - - this.unitAckSoundsTable = new DataTable(worldEditStrings); - try (InputStream terrainSlkStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UnitAckSounds.slk")) { - this.unitAckSoundsTable.readSLK(terrainSlkStream); - } - this.unitCombatSoundsTable = new DataTable(worldEditStrings); - try (InputStream terrainSlkStream = this.dataSource - .getResourceAsStream("UI\\SoundInfo\\UnitCombatSounds.slk")) { - this.unitCombatSoundsTable.readSLK(terrainSlkStream); - } - this.miscData = new DataTable(worldEditStrings); - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\MiscData.txt")) { - this.miscData.readTXT(miscDataTxtStream, true); - } - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\MiscData.txt")) { - this.miscData.readTXT(miscDataTxtStream, true); - } - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\MiscGame.txt")) { - this.miscData.readTXT(miscDataTxtStream, true); - } - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\MiscData.txt")) { - this.miscData.readTXT(miscDataTxtStream, true); - } - if (this.dataSource.has("war3mapMisc.txt")) { - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("war3mapMisc.txt")) { - 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); - } - final Element categories = this.unitGlobalStrings.get("Categories"); - for (final CUnitClassification unitClassification : CUnitClassification.values()) { - if (unitClassification.getLocaleKey() != null) { - final String displayName = categories.getField(unitClassification.getLocaleKey()); - unitClassification.setDisplayName(displayName); - } - } - this.selectionCircleSizes.clear(); - final Element selectionCircleData = this.miscData.get("SelectionCircle"); - final int selectionCircleNumSizes = selectionCircleData.getFieldValue("NumSizes"); - for (int i = 0; i < selectionCircleNumSizes; i++) { - final String indexString = i < 10 ? "0" + i : Integer.toString(i); - final float size = selectionCircleData.getFieldFloatValue("Size" + indexString); - final String texture = selectionCircleData.getField("Texture" + indexString); - final String textureDotted = selectionCircleData.getField("TextureDotted" + indexString); - this.selectionCircleSizes.add(new SelectionCircleSize(size, texture, textureDotted)); - } - this.selectionCircleScaleFactor = selectionCircleData.getFieldFloatValue("ScaleFactor"); - - this.uiSoundsTable = new DataTable(worldEditStrings); - try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UISounds.slk")) { - this.uiSoundsTable.readSLK(miscDataTxtStream); - } - } - - public GenericResource loadMapGeneric(final String path, final FetchDataTypeName dataType, - final LoadGenericCallback callback) { - if (this.mapMpq == null) { - return loadGeneric(path, dataType, callback); - } else { - return loadGeneric(path, dataType, callback, this.dataSource); - } - } - - public void loadMap(final String mapFilePath) throws IOException { - final War3Map war3Map = new War3Map(this.gameDataSource, mapFilePath); - - this.mapMpq = war3Map; - - final PathSolver wc3PathSolver = this.wc3PathSolver; - - char tileset = 'A'; - - final War3MapW3i w3iFile = this.mapMpq.readMapInformation(); - - tileset = w3iFile.getTileset(); - - DataSource tilesetSource; - try { - // Slightly complex. Here's the theory: - // 1.) Copy map into RAM - // 2.) Setup a Data Source that will read assets - // from either the map or the game, giving the map priority. - SeekableByteChannel sbc; - final CompoundDataSource compoundDataSource = war3Map.getCompoundDataSource(); - try (InputStream mapStream = compoundDataSource.getResourceAsStream(tileset + ".mpq")) { - if (mapStream == null) { - tilesetSource = new CompoundDataSource(Arrays.asList(compoundDataSource, - new SubdirDataSource(compoundDataSource, tileset + ".mpq/"), - new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/"))); - } else { - final byte[] mapData = IOUtils.toByteArray(mapStream); - sbc = new SeekableInMemoryByteChannel(mapData); - final DataSource internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc); - tilesetSource = new CompoundDataSource( - Arrays.asList(compoundDataSource, internalMpqContentsDataSource)); - } - } catch (final IOException exc) { - tilesetSource = new CompoundDataSource( - Arrays.asList(compoundDataSource, new SubdirDataSource(compoundDataSource, tileset + ".mpq/"), - new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/"))); - } - } catch (final MPQException e) { - throw new RuntimeException(e); - } - setDataSource(tilesetSource); - this.worldEditStrings = new WorldEditStrings(this.dataSource); - loadSLKs(this.worldEditStrings); - - this.solverParams.tileset = Character.toLowerCase(tileset); - - final War3MapW3e terrainData = this.mapMpq.readEnvironment(); - - final War3MapWpm terrainPathing = this.mapMpq.readPathing(); - - final StandardObjectData standardObjectData = new StandardObjectData(this.dataSource); - this.worldEditData = standardObjectData.getWorldEditData(); - - this.terrain = new Terrain(terrainData, terrainPathing, w3iFile, this.webGL, this.dataSource, - this.worldEditStrings, this, this.worldEditData); - - final float[] centerOffset = terrainData.getCenterOffset(); - final int[] mapSize = terrainData.getMapSize(); - - this.terrainReady = true; - this.anyReady = true; - this.cliffsReady = true; - - // Override the grid based on the map. - this.worldScene.grid = new Grid(centerOffset[0], centerOffset[1], (mapSize[0] * 128) - 128, - (mapSize[1] * 128) - 128, 16 * 128, 16 * 128); - - final MdxModel confirmation = (MdxModel) load("UI\\Feedback\\Confirmation\\Confirmation.mdx", - PathSolver.DEFAULT, null); - this.confirmationInstance = (MdxComplexInstance) confirmation.addInstance(); - this.confirmationInstance.setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP_AND_HIDE_WHEN_DONE); - this.confirmationInstance.setSequence(0); - this.confirmationInstance.setScene(this.worldScene); - - 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 - public CAttackProjectile createAttackProjectile(final CSimulation simulation, final float launchX, - final float launchY, final float launchFacing, final CUnit source, - final CUnitAttackMissile unitAttack, final CWidget target, final float damage, - final int bounceIndex) { - final War3ID typeId = source.getTypeId(); - final int projectileSpeed = unitAttack.getProjectileSpeed(); - final float projectileArc = unitAttack.getProjectileArc(); - String missileArt = unitAttack.getProjectileArt(); - final float projectileLaunchX = simulation.getUnitData().getProjectileLaunchX(typeId); - final float projectileLaunchY = simulation.getUnitData().getProjectileLaunchY(typeId); - final float projectileLaunchZ = simulation.getUnitData().getProjectileLaunchZ(typeId); - - missileArt = mdx(missileArt); - final float facing = launchFacing; - final float sinFacing = (float) Math.sin(facing); - final float cosFacing = (float) Math.cos(facing); - final float x = (launchX + (projectileLaunchY * cosFacing)) + (projectileLaunchX * sinFacing); - final float y = (launchY + (projectileLaunchY * sinFacing)) - (projectileLaunchX * cosFacing); - - final float height = War3MapViewer.this.terrain.getGroundHeight(x, y) + source.getFlyHeight() - + projectileLaunchZ; - final CAttackProjectile simulationAttackProjectile = new CAttackProjectile(x, y, - projectileSpeed, target, source, damage, unitAttack, bounceIndex); - - final MdxModel model = (MdxModel) load(missileArt, War3MapViewer.this.mapPathSolver, - War3MapViewer.this.solverParams); - final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance(); - modelInstance.setTeamColor(source.getPlayerIndex()); - modelInstance.setScene(War3MapViewer.this.worldScene); - if (bounceIndex == 0) { - SequenceUtils.randomBirthSequence(modelInstance); - } else { - SequenceUtils.randomStandSequence(modelInstance); - } - modelInstance.setLocation(x, y, height); - final RenderAttackProjectile renderAttackProjectile = new RenderAttackProjectile( - simulationAttackProjectile, modelInstance, height, projectileArc, War3MapViewer.this); - - War3MapViewer.this.projectiles.add(renderAttackProjectile); - - return simulationAttackProjectile; - } - - @Override - public void createInstantAttackEffect(final CSimulation cSimulation, final CUnit source, - final CUnitAttackInstant unitAttack, final CWidget target) { - final War3ID typeId = source.getTypeId(); - - String missileArt = unitAttack.getProjectileArt(); - final float projectileLaunchX = War3MapViewer.this.simulation.getUnitData() - .getProjectileLaunchX(typeId); - final float projectileLaunchY = War3MapViewer.this.simulation.getUnitData() - .getProjectileLaunchY(typeId); - missileArt = mdx(missileArt); - final float facing = (float) Math.toRadians(source.getFacing()); - final float sinFacing = (float) Math.sin(facing); - final float cosFacing = (float) Math.cos(facing); - final float x = (source.getX() + (projectileLaunchY * cosFacing)) - + (projectileLaunchX * sinFacing); - final float y = (source.getY() + (projectileLaunchY * sinFacing)) - - (projectileLaunchX * cosFacing); - - final float targetX = target.getX(); - final float targetY = target.getY(); - final float angleToTarget = (float) Math.atan2(targetY - y, targetX - x); - - final float height = War3MapViewer.this.terrain.getGroundHeight(targetX, targetY) - + target.getFlyHeight() + target.getImpactZ(); - - final MdxModel model = (MdxModel) load(missileArt, War3MapViewer.this.mapPathSolver, - War3MapViewer.this.solverParams); - final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance(); - modelInstance.setTeamColor(source.getPlayerIndex()); - modelInstance.setScene(War3MapViewer.this.worldScene); - SequenceUtils.randomBirthSequence(modelInstance); - modelInstance.setLocation(targetX, targetY, height); - War3MapViewer.this.projectiles - .add(new RenderAttackInstant(modelInstance, War3MapViewer.this, angleToTarget)); - } - - @Override - public void spawnUnitDamageSound(final CUnit damagedUnit, final String weaponSound, - final String armorType) { - final String key = weaponSound + armorType; - UnitSound combatSound = this.keyToCombatSound.get(key); - if (combatSound == null) { - combatSound = UnitSound.create(War3MapViewer.this.dataSource, - War3MapViewer.this.unitCombatSoundsTable, weaponSound, armorType); - this.keyToCombatSound.put(key, combatSound); - } - combatSound.play(War3MapViewer.this.worldScene.audioContext, damagedUnit.getX(), - damagedUnit.getY()); - } - - @Override - public void spawnUnitConstructionSound(CUnit constructingUnit, CUnit constructedStructure) { - UnitSound constructingBuilding = uiSounds.getSound(gameUI.getSkinField("ConstructingBuilding")); - if (constructingBuilding != null) { - constructingBuilding.play(worldScene.audioContext, constructedStructure.getX(), constructedStructure.getY()); - } - } - - @Override - public void removeUnit(final CUnit unit) { - final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.remove(unit); - War3MapViewer.this.units.remove(renderUnit); - War3MapViewer.this.worldScene.removeInstance(renderUnit.instance); - } - - @Override - public BufferedImage getBuildingPathingPixelMap(final War3ID rawcode) { - return War3MapViewer.this - .getBuildingPathingPixelMap(War3MapViewer.this.allObjectData.getUnits().get(rawcode)); - } - - @Override - public void spawnUnitConstructionFinishSound(CUnit constructedStructure) { - UnitSound constructingBuilding = uiSounds.getSound(gameUI.getSkinField("JobDoneSound")); - if (constructingBuilding != null) { - constructingBuilding.play(worldScene.audioContext, constructedStructure.getX(), constructedStructure.getY()); - } - } - - @Override - public CUnit createUnit(final CSimulation simulation, final War3ID typeId, final int playerIndex, - final float x, final float y, final float facing) { - return createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, playerIndex, - (float) Math.toRadians(facing)); - } - }, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers()); - - this.walkableObjectsTree = new Quadtree<>(this.terrain.getEntireMap()); - if (this.doodadsAndDestructiblesLoaded) { - this.loadDoodadsAndDestructibles(this.allObjectData); - } else { - throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises"); - } - - loadSounds(); - - this.terrain.createWaves(); - - loadLightsAndShading(tileset); - } - - private void loadSounds() { - this.uiSounds = new KeyedSounds(this.uiSoundsTable, this.mapMpq); - } - - /** - * 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 - final Element defaultTerrainLights = this.worldEditData.get("TerrainLights"); - final Element defaultUnitLights = this.worldEditData.get("UnitLights"); - setDayNightModels(defaultTerrainLights.getField(Character.toString(tileset)), - defaultUnitLights.getField(Character.toString(tileset))); - - } - - private void loadDoodadsAndDestructibles(final Warcraft3MapObjectData modifications) throws IOException { - this.applyModificationFile(this.doodadsData, this.doodadMetaData, modifications.getDoodads(), - WorldEditorDataType.DOODADS); - this.applyModificationFile(this.doodadsData, this.destructableMetaData, modifications.getDestructibles(), - WorldEditorDataType.DESTRUCTIBLES); - - final War3MapDoo doo = this.mapMpq.readDoodads(); - - for (final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad : doo.getDoodads()) { - WorldEditorDataType type = WorldEditorDataType.DOODADS; - MutableGameObject row = modifications.getDoodads().get(doodad.getId()); - if (row == null) { - row = modifications.getDestructibles().get(doodad.getId()); - type = WorldEditorDataType.DESTRUCTIBLES; - } - if (row != null) { - String file = row.readSLKTag("file"); - final int numVar = row.readSLKTagInt("numVar"); - - if (file.endsWith(".mdx") || file.endsWith(".mdl")) { - file = file.substring(0, file.length() - 4); - } - - String fileVar = file; - - file += ".mdx"; - - if (numVar > 1) { - fileVar += Math.min(doodad.getVariation(), numVar - 1); - } - - fileVar += ".mdx"; - - final float maxPitch = row.readSLKTagFloat("maxPitch"); - final float maxRoll = row.readSLKTagFloat("maxRoll"); - if (type == WorldEditorDataType.DESTRUCTIBLES) { - final String shadowString = row.readSLKTag("shadow"); - if ((shadowString != null) && (shadowString.length() > 0) && !"_".equals(shadowString)) { - this.terrain.addShadow(shadowString, doodad.getLocation()[0], doodad.getLocation()[1]); - } - - final String pathingTexture = row.readSLKTag("pathTex"); - if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) { - - BufferedImage bufferedImage = this.filePathToPathingMap.get(pathingTexture.toLowerCase()); - if (bufferedImage == null) { - if (this.mapMpq.has(pathingTexture)) { - try { - bufferedImage = TgaFile.readTGA(pathingTexture, - this.mapMpq.getResourceAsStream(pathingTexture)); - this.filePathToPathingMap.put(pathingTexture.toLowerCase(), bufferedImage); - } catch (final Exception exc) { - exc.printStackTrace(); - } - } - } - if (bufferedImage != null) { - this.terrain.pathingGrid.blitPathingOverlayTexture(doodad.getLocation()[0], - doodad.getLocation()[1], (int) Math.toDegrees(doodad.getAngle()), bufferedImage); - } - } - } - // First see if the model is local. - // Doodads referring to local models may have invalid variations, so if the - // variation doesn't exist, try without a variation. - - String path; - if (this.mapMpq.has(fileVar)) { - path = fileVar; - } else { - path = file; - } - MdxModel model; - if (this.mapMpq.has(path)) { - model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams); - } else { - model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams); - } - - if (type == WorldEditorDataType.DESTRUCTIBLES) { - final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type, - maxPitch, maxRoll, doodad.getLife()); - if (row.readSLKTagBoolean("walkable")) { - final float x = doodad.getLocation()[0]; - final float y = doodad.getLocation()[1]; - final BoundingBox boundingBox = model.bounds.getBoundingBox(); - final float minX = boundingBox.min.x + x; - final float minY = boundingBox.min.y + y; - final Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(), - boundingBox.getHeight()); - this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance, - renderDestructableBounds); - } - this.doodads.add(renderDestructable); - } else { - this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll)); - } - } - } - - // Cliff/Terrain doodads. - for (final com.etheller.warsmash.parsers.w3x.doo.TerrainDoodad doodad : doo.getTerrainDoodads()) { - final MutableGameObject row = modifications.getDoodads().get(doodad.getId()); - String file = row.readSLKTag("file");// - if ("".equals(file)) { - final String blaBla = row.readSLKTag("file"); - System.out.println("bla"); - } - if (file.toLowerCase().endsWith(".mdl")) { - file = file.substring(0, file.length() - 4); - } - if (!file.toLowerCase().endsWith(".mdx")) { - file += ".mdx"; - } - final MdxModel model = (MdxModel) this.load(file, this.mapPathSolver, this.solverParams); - - final String pathingTexture = row.readSLKTag("pathTex"); - BufferedImage pathingTextureImage; - if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) { - - pathingTextureImage = this.filePathToPathingMap.get(pathingTexture.toLowerCase()); - if (pathingTextureImage == null) { - if (this.mapMpq.has(pathingTexture)) { - try { - pathingTextureImage = TgaFile.readTGA(pathingTexture, - this.mapMpq.getResourceAsStream(pathingTexture)); - this.filePathToPathingMap.put(pathingTexture.toLowerCase(), pathingTextureImage); - } catch (final Exception exc) { - exc.printStackTrace(); - } - } - } - } else { - pathingTextureImage = null; - } - if (pathingTextureImage != null) { - // blit out terrain cells under this TerrainDoodad - final int textureWidth = pathingTextureImage.getWidth(); - final int textureHeight = pathingTextureImage.getHeight(); - final int textureWidthTerrainCells = textureWidth / 4; - final int textureHeightTerrainCells = textureHeight / 4; - final int minCellX = ((int) doodad.getLocation()[0]); - final int minCellY = ((int) doodad.getLocation()[1]); - final int maxCellX = (minCellX + textureWidthTerrainCells) - 1; - final int maxCellY = (minCellY + textureHeightTerrainCells) - 1; - for (int j = minCellY; j <= maxCellY; j++) { - for (int i = minCellX; i <= maxCellX; i++) { - this.terrain.removeTerrainCellWithoutFlush(i, j); - } - } - this.terrain.flushRemovedTerrainCells(); - } - - System.out.println("Loading terrain doodad: " + file); - this.terrainDoodads.add(new TerrainDoodad(this, model, row, doodad, pathingTextureImage)); - } - - this.doodadsReady = true; - this.anyReady = true; - } - - private void applyModificationFile(final MappedData doodadsData2, final MappedData doodadMetaData2, - final MutableObjectData destructibles, final WorldEditorDataType dataType) { - // TODO condense ported MappedData from Ghostwolf and MutableObjectData from - // Retera - - } - - private void loadUnitsAndItems(final Warcraft3MapObjectData modifications) throws IOException { - final War3Map mpq = this.mapMpq; - this.unitsReady = false; - - this.soundsetNameToSoundset = new HashMap<>(); - - if (this.dataSource.has("war3mapUnits.doo")) { - final War3MapUnitsDoo dooFile = mpq.readUnits(); - - // Collect the units and items data. - for (final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit : dooFile.getUnits()) { - final War3ID unitId = unit.getId(); - final float unitX = unit.getLocation()[0]; - final float unitY = unit.getLocation()[1]; - final float unitZ = unit.getLocation()[2]; - final int playerIndex = unit.getPlayer(); - final float unitAngle = unit.getAngle(); - - createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, unitAngle); - } - } - - this.terrain.loadSplats(); - - this.unitsReady = true; - this.anyReady = true; - } - - private CUnit createNewUnit(final Warcraft3MapObjectData modifications, final War3ID unitId, float unitX, - float unitY, final float unitZ, final int playerIndex, final float unitAngle) { - UnitSoundset soundset = null; - MutableGameObject row = null; - String path = null; - Splat unitShadowSplat = null; - BufferedImage buildingPathingPixelMap = null; - final float unitVertexScale = 1.0f; - - // Hardcoded? - WorldEditorDataType type = null; - if (sloc.equals(unitId)) { + private static final War3ID UNIT_FILE = War3ID.fromString("umdl"); + private static final War3ID UBER_SPLAT = War3ID.fromString("uubs"); + private static final War3ID UNIT_SHADOW = War3ID.fromString("ushu"); + private static final War3ID UNIT_SHADOW_X = War3ID.fromString("ushx"); + private static final War3ID UNIT_SHADOW_Y = War3ID.fromString("ushy"); + private static final War3ID UNIT_SHADOW_W = War3ID.fromString("ushw"); + private static final War3ID UNIT_SHADOW_H = War3ID.fromString("ushh"); + private static final War3ID BUILDING_SHADOW = War3ID.fromString("ushb"); + public static final War3ID UNIT_SELECT_SCALE = War3ID.fromString("ussc"); + private static final War3ID UNIT_SELECT_HEIGHT = War3ID.fromString("uslz"); + private static final War3ID UNIT_SOUNDSET = War3ID.fromString("usnd"); + 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]; + public static final Ray gdxRayHeap = new Ray(); + private static final Vector2 mousePosHeap = new Vector2(); + private static final Vector3 normalHeap = new Vector3(); + public static final Vector3 intersectionHeap = new Vector3(); + private static final Rectangle rectangleHeap = new Rectangle(); + public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation(); + + public PathSolver wc3PathSolver = PathSolver.DEFAULT; + public SolverParams solverParams = new SolverParams(); + public WorldScene worldScene; + public boolean anyReady; + public MappedData terrainData = new MappedData(); + public MappedData cliffTypesData = new MappedData(); + public MappedData waterData = new MappedData(); + public boolean terrainReady; + public boolean cliffsReady; + public boolean doodadsAndDestructiblesLoaded; + public MappedData doodadsData = new MappedData(); + public MappedData doodadMetaData = new MappedData(); + public MappedData destructableMetaData = new MappedData(); + public List doodads = new ArrayList<>(); + public List terrainDoodads = new ArrayList<>(); + public boolean doodadsReady; + public boolean unitsAndItemsLoaded; + public MappedData unitsData = new MappedData(); + public MappedData unitMetaData = new MappedData(); + public List units = new ArrayList<>(); + public List items = new ArrayList<>(); + public List projectiles = new ArrayList<>(); + public boolean unitsReady; + public War3Map mapMpq; + public PathSolver mapPathSolver = PathSolver.DEFAULT; + + private final DataSource gameDataSource; + + public Terrain terrain; + public int renderPathing = 0; + public int renderLighting = 1; + + public List selModels = new ArrayList<>(); + public List selected = new ArrayList<>(); + private DataTable unitAckSoundsTable; + private DataTable unitCombatSoundsTable; + public DataTable miscData; + private DataTable unitGlobalStrings; + public DataTable uiSoundsTable; + private MdxComplexInstance confirmationInstance; + public MdxComplexInstance dncUnit; + public MdxComplexInstance dncUnitDay; + public MdxComplexInstance dncTerrain; + public MdxComplexInstance dncTarget; + 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(); + + private final Random seededRandom = new Random(1337L); + + private final Map filePathToPathingMap = new HashMap<>(); + + private final List selectionCircleSizes = new ArrayList<>(); + + private final Map unitToRenderPeer = new HashMap<>(); + private final Map unitIdToTypeData = new HashMap<>(); + private GameUI gameUI; + private Vector3 lightDirection; + + private Quadtree walkableObjectsTree; + private final QuadtreeIntersectorFindsWalkableRenderHeight walkablesIntersector = new QuadtreeIntersectorFindsWalkableRenderHeight(); + private final QuadtreeIntersectorFindsHitPoint walkablesIntersectionFinder = new QuadtreeIntersectorFindsHitPoint(); + private final QuadtreeIntersectorFindsHighestWalkable intersectorFindsHighestWalkable = new QuadtreeIntersectorFindsHighestWalkable(); + + private KeyedSounds uiSounds; + private int localPlayerIndex; + + public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) { + super(dataSource, canvas); + this.gameDataSource = dataSource; + + final WebGL webGL = this.webGL; + + this.addHandler(new MdxHandler()); + + this.wc3PathSolver = PathSolver.DEFAULT; + + this.worldScene = this.addWorldScene(); + + if (!this.dynamicShadowManager.setup(webGL)) { + throw new IllegalStateException("FrameBuffer setup failed"); + } + } + + public void loadSLKs(final WorldEditStrings worldEditStrings) throws IOException { + final GenericResource terrain = this.loadMapGeneric("TerrainArt\\Terrain.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource cliffTypes = this.loadMapGeneric("TerrainArt\\CliffTypes.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource water = this.loadMapGeneric("TerrainArt\\Water.slk", FetchDataTypeName.SLK, + stringDataCallback); + + // == when loaded, which is always in our system == + this.terrainData.load(terrain.data.toString()); + this.cliffTypesData.load(cliffTypes.data.toString()); + this.waterData.load(water.data.toString()); + // emit terrain loaded?? + + final GenericResource doodads = this.loadMapGeneric("Doodads\\Doodads.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource doodadMetaData = this.loadMapGeneric("Doodads\\DoodadMetaData.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource destructableData = this.loadMapGeneric("Units\\DestructableData.slk", + FetchDataTypeName.SLK, stringDataCallback); + final GenericResource destructableMetaData = this.loadMapGeneric("Units\\DestructableMetaData.slk", + FetchDataTypeName.SLK, stringDataCallback); + + // == when loaded, which is always in our system == + this.doodadsAndDestructiblesLoaded = true; + this.doodadsData.load(doodads.data.toString()); + this.doodadMetaData.load(doodadMetaData.data.toString()); + this.doodadsData.load(destructableData.data.toString()); + this.destructableMetaData.load(destructableData.data.toString()); + // emit doodads loaded + + final GenericResource unitData = this.loadMapGeneric("Units\\UnitData.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource unitUi = this.loadMapGeneric("Units\\unitUI.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource itemData = this.loadMapGeneric("Units\\ItemData.slk", FetchDataTypeName.SLK, + stringDataCallback); + final GenericResource unitMetaData = this.loadMapGeneric("Units\\UnitMetaData.slk", FetchDataTypeName.SLK, + stringDataCallback); + + // == when loaded, which is always in our system == + this.unitsAndItemsLoaded = true; + this.unitsData.load(unitData.data.toString()); + this.unitsData.load(unitUi.data.toString()); + this.unitsData.load(itemData.data.toString()); + this.unitMetaData.load(unitMetaData.data.toString()); + // emit loaded + + this.unitAckSoundsTable = new DataTable(worldEditStrings); + try (InputStream terrainSlkStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UnitAckSounds.slk")) { + this.unitAckSoundsTable.readSLK(terrainSlkStream); + } + this.unitCombatSoundsTable = new DataTable(worldEditStrings); + try (InputStream terrainSlkStream = this.dataSource + .getResourceAsStream("UI\\SoundInfo\\UnitCombatSounds.slk")) { + this.unitCombatSoundsTable.readSLK(terrainSlkStream); + } + this.miscData = new DataTable(worldEditStrings); + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\MiscData.txt")) { + this.miscData.readTXT(miscDataTxtStream, true); + } + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\MiscData.txt")) { + this.miscData.readTXT(miscDataTxtStream, true); + } + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("Units\\MiscGame.txt")) { + this.miscData.readTXT(miscDataTxtStream, true); + } + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\MiscData.txt")) { + this.miscData.readTXT(miscDataTxtStream, true); + } + if (this.dataSource.has("war3mapMisc.txt")) { + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("war3mapMisc.txt")) { + 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); + } + final Element categories = this.unitGlobalStrings.get("Categories"); + for (final CUnitClassification unitClassification : CUnitClassification.values()) { + if (unitClassification.getLocaleKey() != null) { + final String displayName = categories.getField(unitClassification.getLocaleKey()); + unitClassification.setDisplayName(displayName); + } + } + this.selectionCircleSizes.clear(); + final Element selectionCircleData = this.miscData.get("SelectionCircle"); + final int selectionCircleNumSizes = selectionCircleData.getFieldValue("NumSizes"); + for (int i = 0; i < selectionCircleNumSizes; i++) { + final String indexString = i < 10 ? "0" + i : Integer.toString(i); + final float size = selectionCircleData.getFieldFloatValue("Size" + indexString); + final String texture = selectionCircleData.getField("Texture" + indexString); + final String textureDotted = selectionCircleData.getField("TextureDotted" + indexString); + this.selectionCircleSizes.add(new SelectionCircleSize(size, texture, textureDotted)); + } + this.selectionCircleScaleFactor = selectionCircleData.getFieldFloatValue("ScaleFactor"); + + this.uiSoundsTable = new DataTable(worldEditStrings); + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UISounds.slk")) { + this.uiSoundsTable.readSLK(miscDataTxtStream); + } + try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\AmbienceSounds.slk")) { + this.uiSoundsTable.readSLK(miscDataTxtStream); + } + } + + public GenericResource loadMapGeneric(final String path, final FetchDataTypeName dataType, + final LoadGenericCallback callback) { + if (this.mapMpq == null) { + return loadGeneric(path, dataType, callback); + } + else { + return loadGeneric(path, dataType, callback, this.dataSource); + } + } + + public void loadMap(final String mapFilePath, final int localPlayerIndex) throws IOException { + this.localPlayerIndex = localPlayerIndex; + final War3Map war3Map = new War3Map(this.gameDataSource, mapFilePath); + + this.mapMpq = war3Map; + + final PathSolver wc3PathSolver = this.wc3PathSolver; + + char tileset = 'A'; + + final War3MapW3i w3iFile = this.mapMpq.readMapInformation(); + + tileset = w3iFile.getTileset(); + + DataSource tilesetSource; + try { + // Slightly complex. Here's the theory: + // 1.) Copy map into RAM + // 2.) Setup a Data Source that will read assets + // from either the map or the game, giving the map priority. + SeekableByteChannel sbc; + final CompoundDataSource compoundDataSource = war3Map.getCompoundDataSource(); + try (InputStream mapStream = compoundDataSource.getResourceAsStream(tileset + ".mpq")) { + if (mapStream == null) { + tilesetSource = new CompoundDataSource(Arrays.asList(compoundDataSource, + new SubdirDataSource(compoundDataSource, tileset + ".mpq/"), + new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/"))); + } + else { + final byte[] mapData = IOUtils.toByteArray(mapStream); + sbc = new SeekableInMemoryByteChannel(mapData); + final DataSource internalMpqContentsDataSource = new MpqDataSource(new MPQArchive(sbc), sbc); + tilesetSource = new CompoundDataSource( + Arrays.asList(compoundDataSource, internalMpqContentsDataSource)); + } + } + catch (final IOException exc) { + tilesetSource = new CompoundDataSource( + Arrays.asList(compoundDataSource, new SubdirDataSource(compoundDataSource, tileset + ".mpq/"), + new SubdirDataSource(compoundDataSource, "_tilesets/" + tileset + ".w3mod/"))); + } + } + catch (final MPQException e) { + throw new RuntimeException(e); + } + setDataSource(tilesetSource); + this.worldEditStrings = new WorldEditStrings(this.dataSource); + loadSLKs(this.worldEditStrings); + + this.solverParams.tileset = Character.toLowerCase(tileset); + + final War3MapW3e terrainData = this.mapMpq.readEnvironment(); + + final War3MapWpm terrainPathing = this.mapMpq.readPathing(); + + final StandardObjectData standardObjectData = new StandardObjectData(this.dataSource); + this.worldEditData = standardObjectData.getWorldEditData(); + + this.terrain = new Terrain(terrainData, terrainPathing, w3iFile, this.webGL, this.dataSource, + this.worldEditStrings, this, this.worldEditData); + + final float[] centerOffset = terrainData.getCenterOffset(); + final int[] mapSize = terrainData.getMapSize(); + + this.terrainReady = true; + this.anyReady = true; + this.cliffsReady = true; + + // Override the grid based on the map. + this.worldScene.grid = new Grid(centerOffset[0], centerOffset[1], (mapSize[0] * 128) - 128, + (mapSize[1] * 128) - 128, 16 * 128, 16 * 128); + + final MdxModel confirmation = (MdxModel) load("UI\\Feedback\\Confirmation\\Confirmation.mdx", + PathSolver.DEFAULT, null); + this.confirmationInstance = (MdxComplexInstance) confirmation.addInstance(); + this.confirmationInstance.setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP_AND_HIDE_WHEN_DONE); + this.confirmationInstance.setSequence(0); + this.confirmationInstance.setScene(this.worldScene); + + 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 + public CAttackProjectile createAttackProjectile(final CSimulation simulation, final float launchX, + final float launchY, final float launchFacing, final CUnit source, + final CUnitAttackMissile unitAttack, final CWidget target, final float damage, + final int bounceIndex) { + final War3ID typeId = source.getTypeId(); + final int projectileSpeed = unitAttack.getProjectileSpeed(); + final float projectileArc = unitAttack.getProjectileArc(); + String missileArt = unitAttack.getProjectileArt(); + final float projectileLaunchX = simulation.getUnitData().getProjectileLaunchX(typeId); + final float projectileLaunchY = simulation.getUnitData().getProjectileLaunchY(typeId); + final float projectileLaunchZ = simulation.getUnitData().getProjectileLaunchZ(typeId); + + missileArt = mdx(missileArt); + final float facing = launchFacing; + final float sinFacing = (float) Math.sin(facing); + final float cosFacing = (float) Math.cos(facing); + final float x = (launchX + (projectileLaunchY * cosFacing)) + (projectileLaunchX * sinFacing); + final float y = (launchY + (projectileLaunchY * sinFacing)) - (projectileLaunchX * cosFacing); + + final float height = War3MapViewer.this.terrain.getGroundHeight(x, y) + source.getFlyHeight() + + projectileLaunchZ; + final CAttackProjectile simulationAttackProjectile = new CAttackProjectile(x, y, + projectileSpeed, target, source, damage, unitAttack, bounceIndex); + + final MdxModel model = (MdxModel) load(missileArt, War3MapViewer.this.mapPathSolver, + War3MapViewer.this.solverParams); + final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance(); + modelInstance.setTeamColor(source.getPlayerIndex()); + modelInstance.setScene(War3MapViewer.this.worldScene); + if (bounceIndex == 0) { + SequenceUtils.randomBirthSequence(modelInstance); + } + else { + SequenceUtils.randomStandSequence(modelInstance); + } + modelInstance.setLocation(x, y, height); + final RenderAttackProjectile renderAttackProjectile = new RenderAttackProjectile( + simulationAttackProjectile, modelInstance, height, projectileArc, War3MapViewer.this); + + War3MapViewer.this.projectiles.add(renderAttackProjectile); + + return simulationAttackProjectile; + } + + @Override + public void createInstantAttackEffect(final CSimulation cSimulation, final CUnit source, + final CUnitAttackInstant unitAttack, final CWidget target) { + final War3ID typeId = source.getTypeId(); + + String missileArt = unitAttack.getProjectileArt(); + final float projectileLaunchX = War3MapViewer.this.simulation.getUnitData() + .getProjectileLaunchX(typeId); + final float projectileLaunchY = War3MapViewer.this.simulation.getUnitData() + .getProjectileLaunchY(typeId); + missileArt = mdx(missileArt); + final float facing = (float) Math.toRadians(source.getFacing()); + final float sinFacing = (float) Math.sin(facing); + final float cosFacing = (float) Math.cos(facing); + final float x = (source.getX() + (projectileLaunchY * cosFacing)) + + (projectileLaunchX * sinFacing); + final float y = (source.getY() + (projectileLaunchY * sinFacing)) + - (projectileLaunchX * cosFacing); + + final float targetX = target.getX(); + final float targetY = target.getY(); + final float angleToTarget = (float) Math.atan2(targetY - y, targetX - x); + + final float height = War3MapViewer.this.terrain.getGroundHeight(targetX, targetY) + + target.getFlyHeight() + target.getImpactZ(); + + final MdxModel model = (MdxModel) load(missileArt, War3MapViewer.this.mapPathSolver, + War3MapViewer.this.solverParams); + final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance(); + modelInstance.setTeamColor(source.getPlayerIndex()); + modelInstance.setScene(War3MapViewer.this.worldScene); + SequenceUtils.randomBirthSequence(modelInstance); + modelInstance.setLocation(targetX, targetY, height); + War3MapViewer.this.projectiles + .add(new RenderAttackInstant(modelInstance, War3MapViewer.this, angleToTarget)); + } + + @Override + public void spawnUnitDamageSound(final CUnit damagedUnit, final String weaponSound, + final String armorType) { + final String key = weaponSound + armorType; + UnitSound combatSound = this.keyToCombatSound.get(key); + if (combatSound == null) { + combatSound = UnitSound.create(War3MapViewer.this.dataSource, + War3MapViewer.this.unitCombatSoundsTable, weaponSound, armorType); + this.keyToCombatSound.put(key, combatSound); + } + combatSound.play(War3MapViewer.this.worldScene.audioContext, damagedUnit.getX(), + damagedUnit.getY()); + } + + @Override + public void spawnUnitConstructionSound(final CUnit constructingUnit, + final CUnit constructedStructure) { + final UnitSound constructingBuilding = War3MapViewer.this.uiSounds + .getSound(War3MapViewer.this.gameUI.getSkinField("ConstructingBuilding")); + if (constructingBuilding != null) { + constructingBuilding.playUnitResponse(War3MapViewer.this.worldScene.audioContext, + War3MapViewer.this.unitToRenderPeer.get(constructingUnit)); + } + } + + @Override + public void removeUnit(final CUnit unit) { + final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.remove(unit); + War3MapViewer.this.units.remove(renderUnit); + War3MapViewer.this.worldScene.removeInstance(renderUnit.instance); + } + + @Override + public BufferedImage getBuildingPathingPixelMap(final War3ID rawcode) { + return War3MapViewer.this + .getBuildingPathingPixelMap(War3MapViewer.this.allObjectData.getUnits().get(rawcode)); + } + + @Override + public void spawnUnitConstructionFinishSound(final CUnit constructedStructure) { + final UnitSound constructingBuilding = War3MapViewer.this.uiSounds + .getSound(War3MapViewer.this.gameUI.getSkinField("JobDoneSound")); + if (constructingBuilding != null) { + constructingBuilding.play(War3MapViewer.this.worldScene.audioContext, + constructedStructure.getX(), constructedStructure.getY()); + } + } + + @Override + public CUnit createUnit(final CSimulation simulation, final War3ID typeId, final int playerIndex, + final float x, final float y, final float facing) { + return createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, playerIndex, + (float) Math.toRadians(facing)); + } + }, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers()); + + this.walkableObjectsTree = new Quadtree<>(this.terrain.getEntireMap()); + if (this.doodadsAndDestructiblesLoaded) { + this.loadDoodadsAndDestructibles(this.allObjectData); + } + else { + throw new IllegalStateException("transcription of JS has not loaded a map and has no JS async promises"); + } + + loadSounds(); + + this.terrain.createWaves(); + + loadLightsAndShading(tileset); + } + + private void loadSounds() { + this.uiSounds = new KeyedSounds(this.uiSoundsTable, this.mapMpq); + } + + /** + * 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 + final Element defaultTerrainLights = this.worldEditData.get("TerrainLights"); + final Element defaultUnitLights = this.worldEditData.get("UnitLights"); + setDayNightModels(defaultTerrainLights.getField(Character.toString(tileset)), + defaultUnitLights.getField(Character.toString(tileset))); + + } + + private void loadDoodadsAndDestructibles(final Warcraft3MapObjectData modifications) throws IOException { + this.applyModificationFile(this.doodadsData, this.doodadMetaData, modifications.getDoodads(), + WorldEditorDataType.DOODADS); + this.applyModificationFile(this.doodadsData, this.destructableMetaData, modifications.getDestructibles(), + WorldEditorDataType.DESTRUCTIBLES); + + final War3MapDoo doo = this.mapMpq.readDoodads(); + + for (final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad : doo.getDoodads()) { + WorldEditorDataType type = WorldEditorDataType.DOODADS; + MutableGameObject row = modifications.getDoodads().get(doodad.getId()); + if (row == null) { + row = modifications.getDestructibles().get(doodad.getId()); + type = WorldEditorDataType.DESTRUCTIBLES; + } + if (row != null) { + String file = row.readSLKTag("file"); + final int numVar = row.readSLKTagInt("numVar"); + + if (file.endsWith(".mdx") || file.endsWith(".mdl")) { + file = file.substring(0, file.length() - 4); + } + + String fileVar = file; + + file += ".mdx"; + + if (numVar > 1) { + fileVar += Math.min(doodad.getVariation(), numVar - 1); + } + + fileVar += ".mdx"; + + final float maxPitch = row.readSLKTagFloat("maxPitch"); + final float maxRoll = row.readSLKTagFloat("maxRoll"); + if (type == WorldEditorDataType.DESTRUCTIBLES) { + final String shadowString = row.readSLKTag("shadow"); + if ((shadowString != null) && (shadowString.length() > 0) && !"_".equals(shadowString)) { + this.terrain.addShadow(shadowString, doodad.getLocation()[0], doodad.getLocation()[1]); + } + + final String pathingTexture = row.readSLKTag("pathTex"); + if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) { + + BufferedImage bufferedImage = this.filePathToPathingMap.get(pathingTexture.toLowerCase()); + if (bufferedImage == null) { + if (this.mapMpq.has(pathingTexture)) { + try { + bufferedImage = TgaFile.readTGA(pathingTexture, + this.mapMpq.getResourceAsStream(pathingTexture)); + this.filePathToPathingMap.put(pathingTexture.toLowerCase(), bufferedImage); + } + catch (final Exception exc) { + exc.printStackTrace(); + } + } + } + if (bufferedImage != null) { + this.terrain.pathingGrid.blitPathingOverlayTexture(doodad.getLocation()[0], + doodad.getLocation()[1], (int) Math.toDegrees(doodad.getAngle()), bufferedImage); + } + } + } + // First see if the model is local. + // Doodads referring to local models may have invalid variations, so if the + // variation doesn't exist, try without a variation. + + String path; + if (this.mapMpq.has(fileVar)) { + path = fileVar; + } + else { + path = file; + } + MdxModel model; + if (this.mapMpq.has(path)) { + model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams); + } + else { + model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams); + } + + if (type == WorldEditorDataType.DESTRUCTIBLES) { + final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type, + maxPitch, maxRoll, doodad.getLife()); + if (row.readSLKTagBoolean("walkable")) { + final float x = doodad.getLocation()[0]; + final float y = doodad.getLocation()[1]; + final BoundingBox boundingBox = model.bounds.getBoundingBox(); + final float minX = boundingBox.min.x + x; + final float minY = boundingBox.min.y + y; + final Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(), + boundingBox.getHeight()); + this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance, + renderDestructableBounds); + } + this.doodads.add(renderDestructable); + } + else { + this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll)); + } + } + } + + // Cliff/Terrain doodads. + for (final com.etheller.warsmash.parsers.w3x.doo.TerrainDoodad doodad : doo.getTerrainDoodads()) { + final MutableGameObject row = modifications.getDoodads().get(doodad.getId()); + String file = row.readSLKTag("file");// + if ("".equals(file)) { + final String blaBla = row.readSLKTag("file"); + System.out.println("bla"); + } + if (file.toLowerCase().endsWith(".mdl")) { + file = file.substring(0, file.length() - 4); + } + if (!file.toLowerCase().endsWith(".mdx")) { + file += ".mdx"; + } + final MdxModel model = (MdxModel) this.load(file, this.mapPathSolver, this.solverParams); + + final String pathingTexture = row.readSLKTag("pathTex"); + BufferedImage pathingTextureImage; + if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) { + + pathingTextureImage = this.filePathToPathingMap.get(pathingTexture.toLowerCase()); + if (pathingTextureImage == null) { + if (this.mapMpq.has(pathingTexture)) { + try { + pathingTextureImage = TgaFile.readTGA(pathingTexture, + this.mapMpq.getResourceAsStream(pathingTexture)); + this.filePathToPathingMap.put(pathingTexture.toLowerCase(), pathingTextureImage); + } + catch (final Exception exc) { + exc.printStackTrace(); + } + } + } + } + else { + pathingTextureImage = null; + } + if (pathingTextureImage != null) { + // blit out terrain cells under this TerrainDoodad + final int textureWidth = pathingTextureImage.getWidth(); + final int textureHeight = pathingTextureImage.getHeight(); + final int textureWidthTerrainCells = textureWidth / 4; + final int textureHeightTerrainCells = textureHeight / 4; + final int minCellX = ((int) doodad.getLocation()[0]); + final int minCellY = ((int) doodad.getLocation()[1]); + final int maxCellX = (minCellX + textureWidthTerrainCells) - 1; + final int maxCellY = (minCellY + textureHeightTerrainCells) - 1; + for (int j = minCellY; j <= maxCellY; j++) { + for (int i = minCellX; i <= maxCellX; i++) { + this.terrain.removeTerrainCellWithoutFlush(i, j); + } + } + this.terrain.flushRemovedTerrainCells(); + } + + System.out.println("Loading terrain doodad: " + file); + this.terrainDoodads.add(new TerrainDoodad(this, model, row, doodad, pathingTextureImage)); + } + + this.doodadsReady = true; + this.anyReady = true; + } + + private void applyModificationFile(final MappedData doodadsData2, final MappedData doodadMetaData2, + final MutableObjectData destructibles, final WorldEditorDataType dataType) { + // TODO condense ported MappedData from Ghostwolf and MutableObjectData from + // Retera + + } + + private void loadUnitsAndItems(final Warcraft3MapObjectData modifications) throws IOException { + final War3Map mpq = this.mapMpq; + this.unitsReady = false; + + this.soundsetNameToSoundset = new HashMap<>(); + + if (this.dataSource.has("war3mapUnits.doo")) { + final War3MapUnitsDoo dooFile = mpq.readUnits(); + + // Collect the units and items data. + for (final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit : dooFile.getUnits()) { + final War3ID unitId = unit.getId(); + final float unitX = unit.getLocation()[0]; + final float unitY = unit.getLocation()[1]; + final float unitZ = unit.getLocation()[2]; + final int playerIndex = unit.getPlayer(); + final float unitAngle = unit.getAngle(); + + createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, unitAngle); + } + } + + this.terrain.loadSplats(); + + this.unitsReady = true; + this.anyReady = true; + } + + private CUnit createNewUnit(final Warcraft3MapObjectData modifications, final War3ID unitId, float unitX, + float unitY, final float unitZ, final int playerIndex, final float unitAngle) { + UnitSoundset soundset = null; + MutableGameObject row = null; + String path = null; + Splat unitShadowSplat = null; + BufferedImage buildingPathingPixelMap = null; + final float unitVertexScale = 1.0f; + + // Hardcoded? + WorldEditorDataType type = null; + if (sloc.equals(unitId)) { // path = "Objects\\StartLocation\\StartLocation.mdx"; - type = null; /// ?????? - this.startLocations[playerIndex] = new Vector2(unitX, unitY); - } else { - row = modifications.getUnits().get(unitId); - if (row == null) { - row = modifications.getItems().get(unitId); - if (row != null) { - type = WorldEditorDataType.ITEM; - path = row.getFieldAsString(ITEM_FILE, 0); + type = null; /// ?????? + this.startLocations[playerIndex] = new Vector2(unitX, unitY); + } + else { + row = modifications.getUnits().get(unitId); + if (row == null) { + row = modifications.getItems().get(unitId); + if (row != null) { + type = WorldEditorDataType.ITEM; + path = row.getFieldAsString(ITEM_FILE, 0); - if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { - path = path.substring(0, path.length() - 4); - } + if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { + path = path.substring(0, path.length() - 4); + } - Element misc = miscData.get("Misc"); - String itemShadowFile = misc.getField("ItemShadowFile"); - int itemShadowWidth = misc.getFieldValue("ItemShadowSize", 0); - int itemShadowHeight = misc.getFieldValue("ItemShadowSize", 1); - int itemShadowX = misc.getFieldValue("ItemShadowOffset", 0); - int itemShadowY = misc.getFieldValue("ItemShadowOffset", 1); - if ((itemShadowFile != null) && !"_".equals(itemShadowFile)) { - final String texture = "ReplaceableTextures\\Shadows\\" + itemShadowFile + ".blp"; - final float shadowX = itemShadowX; - final float shadowY = itemShadowY; - final float shadowWidth = itemShadowWidth; - final float shadowHeight = itemShadowHeight; - if (!this.terrain.splats.containsKey(texture)) { - final Splat splat = new Splat(); - splat.opacity = 0.5f; - this.terrain.splats.put(texture, splat); - } - final float x = unitX - shadowX; - final float y = unitY - shadowY; - this.terrain.splats.get(texture).locations - .add(new float[]{x, y, x + shadowWidth, y + shadowHeight, 3}); - unitShadowSplat = this.terrain.splats.get(texture); - } + final Element misc = this.miscData.get("Misc"); + final String itemShadowFile = misc.getField("ItemShadowFile"); + final int itemShadowWidth = misc.getFieldValue("ItemShadowSize", 0); + final int itemShadowHeight = misc.getFieldValue("ItemShadowSize", 1); + final int itemShadowX = misc.getFieldValue("ItemShadowOffset", 0); + final int itemShadowY = misc.getFieldValue("ItemShadowOffset", 1); + if ((itemShadowFile != null) && !"_".equals(itemShadowFile)) { + final String texture = "ReplaceableTextures\\Shadows\\" + itemShadowFile + ".blp"; + final float shadowX = itemShadowX; + final float shadowY = itemShadowY; + final float shadowWidth = itemShadowWidth; + final float shadowHeight = itemShadowHeight; + if (!this.terrain.splats.containsKey(texture)) { + final Splat splat = new Splat(); + splat.opacity = 0.5f; + this.terrain.splats.put(texture, splat); + } + final float x = unitX - shadowX; + final float y = unitY - shadowY; + this.terrain.splats.get(texture).locations + .add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 }); + unitShadowSplat = this.terrain.splats.get(texture); + } - path += ".mdx"; - } - } else { - type = WorldEditorDataType.UNITS; - path = getUnitModelPath(row); + path += ".mdx"; + } + } + else { + type = WorldEditorDataType.UNITS; + path = getUnitModelPath(row); - buildingPathingPixelMap = getBuildingPathingPixelMap(row); - if (buildingPathingPixelMap != null) { - unitX = Math.round(unitX / 64f) * 64f; - unitY = Math.round(unitY / 64f) * 64f; - this.terrain.pathingGrid.blitPathingOverlayTexture(unitX, unitY, (int) Math.toDegrees(unitAngle), - buildingPathingPixelMap); - } + buildingPathingPixelMap = getBuildingPathingPixelMap(row); + if (buildingPathingPixelMap != null) { + unitX = Math.round(unitX / 64f) * 64f; + unitY = Math.round(unitY / 64f) * 64f; + this.terrain.pathingGrid.blitPathingOverlayTexture(unitX, unitY, (int) Math.toDegrees(unitAngle), + buildingPathingPixelMap); + } - final String uberSplat = row.getFieldAsString(UBER_SPLAT, 0); - if (uberSplat != null) { - final Element uberSplatInfo = this.terrain.uberSplatTable.get(uberSplat); - if (uberSplatInfo != null) { - final String texturePath = uberSplatInfo.getField("Dir") + "\\" + uberSplatInfo.getField("file") - + ".blp"; - final float s = uberSplatInfo.getFieldFloatValue("Scale"); - if (unitsReady) { - this.terrain.addUberSplat(texturePath, unitX, unitY, 1, s); - } else { - if (!this.terrain.splats.containsKey(texturePath)) { - this.terrain.splats.put(texturePath, new Splat()); - } - final float x = unitX; - final float y = unitY; - this.terrain.splats.get(texturePath).locations - .add(new float[]{x - s, y - s, x + s, y + s, 1}); - } - } - } + final String uberSplat = row.getFieldAsString(UBER_SPLAT, 0); + if (uberSplat != null) { + final Element uberSplatInfo = this.terrain.uberSplatTable.get(uberSplat); + if (uberSplatInfo != null) { + final String texturePath = uberSplatInfo.getField("Dir") + "\\" + uberSplatInfo.getField("file") + + ".blp"; + final float s = uberSplatInfo.getFieldFloatValue("Scale"); + if (this.unitsReady) { + this.terrain.addUberSplat(texturePath, unitX, unitY, 1, s); + } + else { + if (!this.terrain.splats.containsKey(texturePath)) { + this.terrain.splats.put(texturePath, new Splat()); + } + final float x = unitX; + final float y = unitY; + this.terrain.splats.get(texturePath).locations + .add(new float[] { x - s, y - s, x + s, y + s, 1 }); + } + } + } - final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0); - if ((unitShadow != null) && !"_".equals(unitShadow)) { - final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp"; - final float shadowX = row.getFieldAsFloat(UNIT_SHADOW_X, 0); - final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0); - final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0); - final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0); - if (this.mapMpq.has(texture)) { - if (!this.terrain.splats.containsKey(texture)) { - final Splat splat = new Splat(); - splat.opacity = 0.5f; - this.terrain.splats.put(texture, splat); - } - final float x = unitX - shadowX; - final float y = unitY - shadowY; - this.terrain.splats.get(texture).locations - .add(new float[]{x, y, x + shadowWidth, y + shadowHeight, 3}); - unitShadowSplat = this.terrain.splats.get(texture); - } - } + final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0); + if ((unitShadow != null) && !"_".equals(unitShadow)) { + final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp"; + final float shadowX = row.getFieldAsFloat(UNIT_SHADOW_X, 0); + final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0); + final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0); + final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0); + if (this.mapMpq.has(texture)) { + if (!this.terrain.splats.containsKey(texture)) { + final Splat splat = new Splat(); + splat.opacity = 0.5f; + this.terrain.splats.put(texture, splat); + } + final float x = unitX - shadowX; + final float y = unitY - shadowY; + this.terrain.splats.get(texture).locations + .add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 }); + unitShadowSplat = this.terrain.splats.get(texture); + } + } - final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0); - if ((buildingShadow != null) && !"_".equals(buildingShadow)) { - this.terrain.addShadow(buildingShadow, unitX, unitY); - } + final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0); + if ((buildingShadow != null) && !"_".equals(buildingShadow)) { + this.terrain.addShadow(buildingShadow, unitX, unitY); + } - final String soundName = row.getFieldAsString(UNIT_SOUNDSET, 0); - UnitSoundset unitSoundset = this.soundsetNameToSoundset.get(soundName); - if (unitSoundset == null) { - unitSoundset = new UnitSoundset(this.dataSource, this.unitAckSoundsTable, soundName); - this.soundsetNameToSoundset.put(soundName, unitSoundset); - } - soundset = unitSoundset; + final String soundName = row.getFieldAsString(UNIT_SOUNDSET, 0); + UnitSoundset unitSoundset = this.soundsetNameToSoundset.get(soundName); + if (unitSoundset == null) { + unitSoundset = new UnitSoundset(this.dataSource, this.unitAckSoundsTable, soundName); + this.soundsetNameToSoundset.put(soundName, unitSoundset); + } + soundset = unitSoundset; - } - } + } + } - if (path != null) { - final MdxModel model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams); - MdxModel portraitModel; - final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx"; - if (this.dataSource.has(portraitPath)) { - portraitModel = (MdxModel) this.load(portraitPath, this.mapPathSolver, this.solverParams); - } else { - portraitModel = model; - } - if (type == WorldEditorDataType.UNITS) { - final float angle = (float) Math.toDegrees(unitAngle); - final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), playerIndex, unitX, unitY, - angle, buildingPathingPixelMap); - final RenderUnitTypeData typeData = getUnitTypeData(unitId, row); - final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, playerIndex, - soundset, portraitModel, simulationUnit, typeData); - this.unitToRenderPeer.put(simulationUnit, renderUnit); - this.units.add(renderUnit); - if (unitShadowSplat != null) { - unitShadowSplat.unitMapping.add(new Consumer() { - @Override - public void accept(final SplatMover t) { - renderUnit.shadow = t; - } - }); - } - return simulationUnit; - } else { - this.items - .add(new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset, portraitModel)); // TODO - // store - // somewhere - if (unitShadowSplat != null) { - unitShadowSplat.unitMapping.add(new Consumer() { - @Override - public void accept(final SplatMover t) { + if (path != null) { + final MdxModel model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams); + MdxModel portraitModel; + final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx"; + if (this.dataSource.has(portraitPath)) { + portraitModel = (MdxModel) this.load(portraitPath, this.mapPathSolver, this.solverParams); + } + else { + portraitModel = model; + } + if (type == WorldEditorDataType.UNITS) { + final float angle = (float) Math.toDegrees(unitAngle); + final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), playerIndex, unitX, unitY, + angle, buildingPathingPixelMap); + final RenderUnitTypeData typeData = getUnitTypeData(unitId, row); + final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, playerIndex, + soundset, portraitModel, simulationUnit, typeData); + this.unitToRenderPeer.put(simulationUnit, renderUnit); + this.units.add(renderUnit); + if (unitShadowSplat != null) { + unitShadowSplat.unitMapping.add(new Consumer() { + @Override + public void accept(final SplatMover t) { + renderUnit.shadow = t; + } + }); + } + return simulationUnit; + } + else { + this.items + .add(new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset, portraitModel)); // TODO + // store + // somewhere + if (unitShadowSplat != null) { + unitShadowSplat.unitMapping.add(new Consumer() { + @Override + public void accept(final SplatMover t) { - } - }); - } - } - } else { - System.err.println("Unknown unit ID: " + unitId); - } - return null; - } + } + }); + } + } + } + else { + System.err.println("Unknown unit ID: " + unitId); + } + return null; + } - public String getUnitModelPath(final MutableGameObject row) { - String path; - path = row.getFieldAsString(UNIT_FILE, 0); + public String getUnitModelPath(final MutableGameObject row) { + String path; + path = row.getFieldAsString(UNIT_FILE, 0); - if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { - path = path.substring(0, path.length() - 4); - } - if ((row.readSLKTagInt("fileVerFlags") == 2) && this.dataSource.has(path + "_V1.mdx")) { - path += "_V1"; - } + if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { + path = path.substring(0, path.length() - 4); + } + if ((row.readSLKTagInt("fileVerFlags") == 2) && this.dataSource.has(path + "_V1.mdx")) { + path += "_V1"; + } - path += ".mdx"; - return path; - } + path += ".mdx"; + return path; + } - private BufferedImage getBuildingPathingPixelMap(final MutableGameObject row) { - BufferedImage buildingPathingPixelMap = null; - final String pathingTexture = row.getFieldAsString(UNIT_PATHING, 0); - if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) { - buildingPathingPixelMap = this.filePathToPathingMap.get(pathingTexture.toLowerCase()); - if (buildingPathingPixelMap == null) { - try { - if (pathingTexture.toLowerCase().endsWith(".tga")) { - buildingPathingPixelMap = TgaFile.readTGA(pathingTexture, - this.mapMpq.getResourceAsStream(pathingTexture)); - } else { - try (InputStream stream = this.mapMpq.getResourceAsStream(pathingTexture)) { - buildingPathingPixelMap = ImageIO.read(stream); - System.out.println("LOADING BLP PATHING: " + pathingTexture); - } - } - this.filePathToPathingMap.put(pathingTexture.toLowerCase(), buildingPathingPixelMap); - } catch (final IOException exc) { - System.err.println("Failure to get pathing: " + exc.getClass() + ":" + exc.getMessage()); - } - } - } - return buildingPathingPixelMap; - } + private BufferedImage getBuildingPathingPixelMap(final MutableGameObject row) { + BufferedImage buildingPathingPixelMap = null; + final String pathingTexture = row.getFieldAsString(UNIT_PATHING, 0); + if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) { + buildingPathingPixelMap = this.filePathToPathingMap.get(pathingTexture.toLowerCase()); + if (buildingPathingPixelMap == null) { + try { + if (pathingTexture.toLowerCase().endsWith(".tga")) { + buildingPathingPixelMap = TgaFile.readTGA(pathingTexture, + this.mapMpq.getResourceAsStream(pathingTexture)); + } + else { + try (InputStream stream = this.mapMpq.getResourceAsStream(pathingTexture)) { + buildingPathingPixelMap = ImageIO.read(stream); + System.out.println("LOADING BLP PATHING: " + pathingTexture); + } + } + this.filePathToPathingMap.put(pathingTexture.toLowerCase(), buildingPathingPixelMap); + } + catch (final IOException exc) { + System.err.println("Failure to get pathing: " + exc.getClass() + ":" + exc.getMessage()); + } + } + } + return buildingPathingPixelMap; + } - public 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; - } + public 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) { - this.terrain.update(); + @Override + public void update() { + if (this.anyReady) { + this.terrain.update(); - super.update(); + super.update(); - for (final RenderUnit unit : this.units) { - unit.updateAnimations(this); - } - final Iterator projectileIterator = this.projectiles.iterator(); - while (projectileIterator.hasNext()) { - final RenderEffect projectile = projectileIterator.next(); - if (projectile.updateAnimations(this, Gdx.graphics.getDeltaTime())) { - projectileIterator.remove(); - } - } - for (final RenderItem item : this.items) { - final MdxComplexInstance instance = item.instance; - final MdxComplexInstance mdxComplexInstance = instance; - if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1)) { - SequenceUtils.randomStandSequence(mdxComplexInstance); - } - } - for (final RenderDoodad item : this.doodads) { - final ModelInstance instance = item.instance; - if (instance instanceof MdxComplexInstance) { - final MdxComplexInstance mdxComplexInstance = (MdxComplexInstance) instance; - if ((mdxComplexInstance.sequence == -1) || (mdxComplexInstance.sequenceEnded - && ((item.getAnimation() != AnimationTokens.PrimaryTag.DEATH) - || (((MdxModel) mdxComplexInstance.model).sequences.get(mdxComplexInstance.sequence) - .getFlags() == 0)))) { - SequenceUtils.randomSequence(mdxComplexInstance, item.getAnimation(), SequenceUtils.EMPTY, - true); + for (final RenderUnit unit : this.units) { + unit.updateAnimations(this); + } + final Iterator projectileIterator = this.projectiles.iterator(); + while (projectileIterator.hasNext()) { + final RenderEffect projectile = projectileIterator.next(); + if (projectile.updateAnimations(this, Gdx.graphics.getDeltaTime())) { + projectileIterator.remove(); + } + } + for (final RenderItem item : this.items) { + final MdxComplexInstance instance = item.instance; + final MdxComplexInstance mdxComplexInstance = instance; + if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1)) { + SequenceUtils.randomStandSequence(mdxComplexInstance); + } + } + for (final RenderDoodad item : this.doodads) { + final ModelInstance instance = item.instance; + if (instance instanceof MdxComplexInstance) { + final MdxComplexInstance mdxComplexInstance = (MdxComplexInstance) instance; + if ((mdxComplexInstance.sequence == -1) || (mdxComplexInstance.sequenceEnded + && ((item.getAnimation() != AnimationTokens.PrimaryTag.DEATH) + || (((MdxModel) mdxComplexInstance.model).sequences.get(mdxComplexInstance.sequence) + .getFlags() == 0)))) { + SequenceUtils.randomSequence(mdxComplexInstance, item.getAnimation(), SequenceUtils.EMPTY, + true); - } - } - } + } + } + } - final float rawDeltaTime = Gdx.graphics.getRawDeltaTime(); - this.updateTime += rawDeltaTime; - while (this.updateTime >= WarsmashConstants.SIMULATION_STEP_TIME) { - this.updateTime -= WarsmashConstants.SIMULATION_STEP_TIME; - this.simulation.update(); - } - this.dncTerrain.setFrameByRatio( - this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours()); - this.dncTerrain.update(rawDeltaTime, null); - this.dncUnit.setFrameByRatio( - this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours()); - this.dncUnit.update(rawDeltaTime, null); - this.dncUnitDay.setFrameByRatio(0.5f); - this.dncUnitDay.update(rawDeltaTime, null); - this.dncTarget.setFrameByRatio( - this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours()); - this.dncTarget.update(rawDeltaTime, null); - } - } + final float rawDeltaTime = Gdx.graphics.getRawDeltaTime(); + this.updateTime += rawDeltaTime; + while (this.updateTime >= WarsmashConstants.SIMULATION_STEP_TIME) { + this.updateTime -= WarsmashConstants.SIMULATION_STEP_TIME; + this.simulation.update(); + } + this.dncTerrain.setFrameByRatio( + this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours()); + this.dncTerrain.update(rawDeltaTime, null); + this.dncUnit.setFrameByRatio( + this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours()); + this.dncUnit.update(rawDeltaTime, null); + this.dncUnitDay.setFrameByRatio(0.5f); + this.dncUnitDay.update(rawDeltaTime, null); + this.dncTarget.setFrameByRatio( + this.simulation.getGameTimeOfDay() / this.simulation.getGameplayConstants().getGameDayHours()); + this.dncTarget.update(rawDeltaTime, null); + } + } - @Override - public void render() { - if (this.anyReady) { - final Scene worldScene = this.worldScene; + @Override + public void render() { + if (this.anyReady) { + final Scene worldScene = this.worldScene; - startFrame(); - worldScene.startFrame(); - worldScene.renderOpaque(this.dynamicShadowManager, this.webGL); - this.terrain.renderGround(this.dynamicShadowManager); - this.terrain.renderCliffs(); - worldScene.renderOpaque(); - this.terrain.renderUberSplats(); - this.terrain.renderWater(); - worldScene.renderTranslucent(); + startFrame(); + worldScene.startFrame(); + worldScene.renderOpaque(this.dynamicShadowManager, this.webGL); + this.terrain.renderGround(this.dynamicShadowManager); + this.terrain.renderCliffs(); + worldScene.renderOpaque(); + this.terrain.renderUberSplats(); + this.terrain.renderWater(); + worldScene.renderTranslucent(); - final List scenes = this.scenes; - for (final Scene scene : scenes) { - if (scene != worldScene) { - scene.startFrame(); - scene.renderOpaque(); - scene.renderTranslucent(); - } - } - } - } + final List scenes = this.scenes; + for (final Scene scene : scenes) { + if (scene != worldScene) { + scene.startFrame(); + scene.renderOpaque(); + scene.renderTranslucent(); + } + } + } + } - public void deselect() { - if (!this.selModels.isEmpty()) { - for (final SplatModel model : this.selModels) { - this.terrain.removeSplatBatchModel("selection"); - } - this.selModels.clear(); - for (final RenderUnit unit : this.selected) { - unit.selectionCircle = null; - } - } - this.selected.clear(); - } + public void deselect() { + if (!this.selModels.isEmpty()) { + for (final SplatModel model : this.selModels) { + this.terrain.removeSplatBatchModel("selection"); + } + this.selModels.clear(); + for (final RenderUnit unit : this.selected) { + unit.selectionCircle = null; + } + } + this.selected.clear(); + } - public void doSelectUnit(final List units) { - deselect(); - if (units.isEmpty()) { - return; - } + public void doSelectUnit(final List units) { + deselect(); + if (units.isEmpty()) { + return; + } - final Map splats = new HashMap(); - for (final RenderUnit unit : units) { - if (unit.row != null) { - if (unit.selectionScale > 0) { - final float selectionSize = unit.selectionScale * this.selectionCircleScaleFactor; - String path = null; - for (int i = 0; i < this.selectionCircleSizes.size(); i++) { - final SelectionCircleSize selectionCircleSize = this.selectionCircleSizes.get(i); - if ((selectionSize < selectionCircleSize.size) - || (i == (this.selectionCircleSizes.size() - 1))) { - path = selectionCircleSize.texture; - break; - } - } - if (!path.toLowerCase().endsWith(".blp")) { - path += ".blp"; - } - if (!splats.containsKey(path)) { - splats.put(path, new Splat()); - } - final float x = unit.location[0]; - final float y = unit.location[1]; - System.out.println("Selecting a unit at " + x + "," + y); - final float z = unit.row.getFieldAsFloat(UNIT_SELECT_HEIGHT, 0); - splats.get(path).locations.add(new float[]{x - (selectionSize / 2), y - (selectionSize / 2), - x + (selectionSize / 2), y + (selectionSize / 2), z + 5}); - splats.get(path).unitMapping.add(new Consumer() { - @Override - public void accept(final SplatMover t) { - unit.selectionCircle = t; - } - }); - } - this.selected.add(unit); - } - } - this.selModels.clear(); - for (final Map.Entry entry : splats.entrySet()) { - final String path = entry.getKey(); - final Splat locations = entry.getValue(); - final SplatModel model = new SplatModel(Gdx.gl30, (Texture) load(path, PathSolver.DEFAULT, null), - locations.locations, this.terrain.centerOffset, locations.unitMapping, true); - model.color[0] = 0; - model.color[1] = 1; - model.color[2] = 0; - model.color[3] = 1; - this.selModels.add(model); - this.terrain.addSplatBatchModel("selection", model); - } - } + final Map splats = new HashMap(); + for (final RenderUnit unit : units) { + if (unit.row != null) { + if (unit.selectionScale > 0) { + final float selectionSize = unit.selectionScale * this.selectionCircleScaleFactor; + String path = null; + for (int i = 0; i < this.selectionCircleSizes.size(); i++) { + final SelectionCircleSize selectionCircleSize = this.selectionCircleSizes.get(i); + if ((selectionSize < selectionCircleSize.size) + || (i == (this.selectionCircleSizes.size() - 1))) { + path = selectionCircleSize.texture; + break; + } + } + if (!path.toLowerCase().endsWith(".blp")) { + path += ".blp"; + } + if (!splats.containsKey(path)) { + splats.put(path, new Splat()); + } + final float x = unit.location[0]; + final float y = unit.location[1]; + System.out.println("Selecting a unit at " + x + "," + y); + final float z = unit.row.getFieldAsFloat(UNIT_SELECT_HEIGHT, 0); + splats.get(path).locations.add(new float[] { x - (selectionSize / 2), y - (selectionSize / 2), + x + (selectionSize / 2), y + (selectionSize / 2), z + 5 }); + splats.get(path).unitMapping.add(new Consumer() { + @Override + public void accept(final SplatMover t) { + unit.selectionCircle = t; + } + }); + } + this.selected.add(unit); + } + } + this.selModels.clear(); + for (final Map.Entry entry : splats.entrySet()) { + final String path = entry.getKey(); + final Splat locations = entry.getValue(); + final SplatModel model = new SplatModel(Gdx.gl30, (Texture) load(path, PathSolver.DEFAULT, null), + locations.locations, this.terrain.centerOffset, locations.unitMapping, true); + model.color[0] = 0; + model.color[1] = 1; + model.color[2] = 0; + model.color[3] = 1; + this.selModels.add(model); + this.terrain.addSplatBatchModel("selection", model); + } + } - public void getClickLocation(final Vector3 out, final int screenX, final int screenY) { - final float[] ray = rayHeap; - mousePosHeap.set(screenX, screenY); - this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); - gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]); - gdxRayHeap.direction.nor();// needed for libgdx - RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices, - this.terrain.softwareGroundMesh.indices, 3, out); - rectangleHeap.set(Math.min(out.x, gdxRayHeap.origin.x), Math.min(out.y, gdxRayHeap.origin.y), - Math.abs(out.x - gdxRayHeap.origin.x), Math.abs(out.y - gdxRayHeap.origin.y)); - this.walkableObjectsTree.intersect(rectangleHeap, this.walkablesIntersectionFinder.reset(gdxRayHeap)); - if (this.walkablesIntersectionFinder.found) { - out.set(this.walkablesIntersectionFinder.intersection); - } else { - out.z = Math.max(getWalkableRenderHeight(out.x, out.y), this.terrain.getGroundHeight(out.x, out.y)); - } - } + public void getClickLocation(final Vector3 out, final int screenX, final int screenY) { + final float[] ray = rayHeap; + mousePosHeap.set(screenX, screenY); + this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); + gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]); + gdxRayHeap.direction.nor();// needed for libgdx + RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices, + this.terrain.softwareGroundMesh.indices, 3, out); + rectangleHeap.set(Math.min(out.x, gdxRayHeap.origin.x), Math.min(out.y, gdxRayHeap.origin.y), + Math.abs(out.x - gdxRayHeap.origin.x), Math.abs(out.y - gdxRayHeap.origin.y)); + this.walkableObjectsTree.intersect(rectangleHeap, this.walkablesIntersectionFinder.reset(gdxRayHeap)); + if (this.walkablesIntersectionFinder.found) { + out.set(this.walkablesIntersectionFinder.intersection); + } + else { + out.z = Math.max(getWalkableRenderHeight(out.x, out.y), this.terrain.getGroundHeight(out.x, out.y)); + } + } - public void showConfirmation(final Vector3 position, final float red, final float green, final float blue) { - this.confirmationInstance.show(); - this.confirmationInstance.setSequence(0); - this.confirmationInstance.setLocation(position); - this.worldScene.instanceMoved(this.confirmationInstance, position.x, position.y); - this.confirmationInstance.vertexColor[0] = red; - this.confirmationInstance.vertexColor[1] = green; - this.confirmationInstance.vertexColor[2] = blue; - } + public void showConfirmation(final Vector3 position, final float red, final float green, final float blue) { + this.confirmationInstance.show(); + this.confirmationInstance.setSequence(0); + this.confirmationInstance.setLocation(position); + this.worldScene.instanceMoved(this.confirmationInstance, position.x, position.y); + this.confirmationInstance.vertexColor[0] = red; + this.confirmationInstance.vertexColor[1] = green; + this.confirmationInstance.vertexColor[2] = blue; + } - public List selectUnit(final float x, final float y, final boolean toggle) { - System.out.println("world: " + x + "," + y); - final float[] ray = rayHeap; - mousePosHeap.set(x, y); - this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); - gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]); - gdxRayHeap.direction.nor();// needed for libgdx + public List selectUnit(final float x, final float y, final boolean toggle) { + System.out.println("world: " + x + "," + y); + final float[] ray = rayHeap; + mousePosHeap.set(x, y); + this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); + gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]); + gdxRayHeap.direction.nor();// needed for libgdx - RenderUnit entity = null; - for (final RenderUnit unit : this.units) { - final MdxComplexInstance instance = unit.instance; - if (instance.isVisible(this.worldScene.camera) - && instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap, - unit.getSimulationUnit().getUnitType().isBuilding(), false) - && !unit.getSimulationUnit().isDead()) { - if ((entity == null) || (entity.instance.depth > instance.depth)) { - entity = unit; - } - } - } - List sel; - if (entity != null) { - if (toggle) { - sel = new ArrayList<>(this.selected); - final int idx = sel.indexOf(entity); - if (idx >= 0) { - sel.remove(idx); - } else { - sel.add(entity); - } - } else { - sel = Arrays.asList(entity); - } - } else { - sel = Collections.emptyList(); - } - this.doSelectUnit(sel); - return sel; - } + RenderUnit entity = null; + for (final RenderUnit unit : this.units) { + final MdxComplexInstance instance = unit.instance; + if (instance.isVisible(this.worldScene.camera) + && instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap, + unit.getSimulationUnit().getUnitType().isBuilding(), false) + && !unit.getSimulationUnit().isDead()) { + if ((entity == null) || (entity.instance.depth > instance.depth)) { + entity = unit; + } + } + } + List sel; + if (entity != null) { + if (toggle) { + sel = new ArrayList<>(this.selected); + final int idx = sel.indexOf(entity); + if (idx >= 0) { + sel.remove(idx); + } + else { + sel.add(entity); + } + } + else { + sel = Arrays.asList(entity); + } + } + else { + sel = Collections.emptyList(); + } + this.doSelectUnit(sel); + return sel; + } - public RenderUnit rayPickUnit(final float x, final float y) { - return rayPickUnit(x, y, CUnitFilterFunction.ACCEPT_ALL); - } + public RenderUnit rayPickUnit(final float x, final float y) { + return rayPickUnit(x, y, CUnitFilterFunction.ACCEPT_ALL); + } - public RenderUnit rayPickUnit(final float x, final float y, final CUnitFilterFunction filter) { - final float[] ray = rayHeap; - mousePosHeap.set(x, y); - this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); - gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]); - gdxRayHeap.direction.nor();// needed for libgdx + public RenderUnit rayPickUnit(final float x, final float y, final CUnitFilterFunction filter) { + final float[] ray = rayHeap; + mousePosHeap.set(x, y); + this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); + gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]); + gdxRayHeap.direction.nor();// needed for libgdx - RenderUnit entity = null; - for (final RenderUnit unit : this.units) { - final MdxComplexInstance instance = unit.instance; - if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap, - intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) { - if (filter.call(unit.getSimulationUnit()) && (intersectionHeap.z > this.terrain - .getGroundHeight(intersectionHeap.x, intersectionHeap.y))) { - if ((entity == null) || (entity.instance.depth > instance.depth)) { - entity = unit; - } - } - } - } - return entity; - } + RenderUnit entity = null; + for (final RenderUnit unit : this.units) { + final MdxComplexInstance instance = unit.instance; + if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap, + intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) { + if (filter.call(unit.getSimulationUnit()) && (intersectionHeap.z > this.terrain + .getGroundHeight(intersectionHeap.x, intersectionHeap.y))) { + if ((entity == null) || (entity.instance.depth > instance.depth)) { + entity = unit; + } + } + } + } + return entity; + } - private static final class MappedDataCallbackImplementation implements LoadGenericCallback { - @Override - public Object call(final InputStream data) { - final StringBuilder stringBuilder = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) { - String line; - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - stringBuilder.append("\n"); - } - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (final IOException e) { - throw new RuntimeException(e); - } - return new MappedData(stringBuilder.toString()); - } - } + private static final class MappedDataCallbackImplementation implements LoadGenericCallback { + @Override + public Object call(final InputStream data) { + final StringBuilder stringBuilder = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) { + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append("\n"); + } + } + catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + return new MappedData(stringBuilder.toString()); + } + } - private static final class StringDataCallbackImplementation implements LoadGenericCallback { - @Override - public Object call(final InputStream data) { - if (data == null) { - System.err.println("data null"); - } - final StringBuilder stringBuilder = new StringBuilder(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) { - String line; - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - stringBuilder.append("\n"); - } - } catch (final UnsupportedEncodingException e) { - throw new RuntimeException(e); - } catch (final IOException e) { - throw new RuntimeException(e); - } - return stringBuilder.toString(); - } - } + private static final class StringDataCallbackImplementation implements LoadGenericCallback { + @Override + public Object call(final InputStream data) { + if (data == null) { + System.err.println("data null"); + } + final StringBuilder stringBuilder = new StringBuilder(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) { + String line; + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append("\n"); + } + } + catch (final UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + return stringBuilder.toString(); + } + } - private static final class StreamDataCallbackImplementation implements LoadGenericCallback { - @Override - public Object call(final InputStream data) { - return data; - } - } + private static final class StreamDataCallbackImplementation implements LoadGenericCallback { + @Override + public Object call(final InputStream data) { + return data; + } + } - public static final class SolverParams { - public char tileset; - public boolean reforged; - public boolean hd; - } + public static final class SolverParams { + public char tileset; + public boolean reforged; + public boolean hd; + } - public static final class CliffInfo { - public List locations = new ArrayList<>(); - public List textures = new ArrayList<>(); - } + public static final class CliffInfo { + public List locations = new ArrayList<>(); + public List textures = new ArrayList<>(); + } - private static final int MAXIMUM_ACCEPTED = 1 << 30; - private float selectionCircleScaleFactor; - private DataTable worldEditData; - private WorldEditStrings worldEditStrings; - private Warcraft3MapObjectData allObjectData; - private AbilityDataUI abilityDataUI; - private Map soundsetNameToSoundset; + private static final int MAXIMUM_ACCEPTED = 1 << 30; + private float selectionCircleScaleFactor; + private DataTable worldEditData; + private WorldEditStrings worldEditStrings; + private Warcraft3MapObjectData allObjectData; + private AbilityDataUI abilityDataUI; + private Map soundsetNameToSoundset; - /** - * Returns a power of two size for the given target capacity. - */ - private static final int pow2GreaterThan(final int capacity) { - int numElements = capacity - 1; - numElements |= numElements >>> 1; - numElements |= numElements >>> 2; - numElements |= numElements >>> 4; - numElements |= numElements >>> 8; - numElements |= numElements >>> 16; - return (numElements < 0) ? 1 : (numElements >= MAXIMUM_ACCEPTED) ? MAXIMUM_ACCEPTED : numElements + 1; - } + /** + * Returns a power of two size for the given target capacity. + */ + private static final int pow2GreaterThan(final int capacity) { + int numElements = capacity - 1; + numElements |= numElements >>> 1; + numElements |= numElements >>> 2; + numElements |= numElements >>> 4; + numElements |= numElements >>> 8; + numElements |= numElements >>> 16; + return (numElements < 0) ? 1 : (numElements >= MAXIMUM_ACCEPTED) ? MAXIMUM_ACCEPTED : numElements + 1; + } - public void standOnRepeat(final MdxComplexInstance instance) { - instance.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); - SequenceUtils.randomStandSequence(instance); - } + public void standOnRepeat(final MdxComplexInstance instance) { + instance.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); + SequenceUtils.randomStandSequence(instance); + } - private static final class SelectionCircleSize { - private final float size; - private final String texture; - private final String textureDotted; + private static final class SelectionCircleSize { + private final float size; + private final String texture; + private final String textureDotted; - public SelectionCircleSize(final float size, final String texture, final String textureDotted) { - this.size = size; - this.texture = texture; - this.textureDotted = textureDotted; - } - } + public SelectionCircleSize(final float size, final String texture, final String textureDotted) { + this.size = size; + this.texture = texture; + this.textureDotted = textureDotted; + } + } - public void setDayNightModels(final String terrainDNCFile, final String unitDNCFile) { - final MdxModel terrainDNCModel = (MdxModel) load(mdx(terrainDNCFile), PathSolver.DEFAULT, null); - this.dncTerrain = (MdxComplexInstance) terrainDNCModel.addInstance(); - this.dncTerrain.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); - this.dncTerrain.setSequence(0); - final MdxModel unitDNCModel = (MdxModel) load(mdx(unitDNCFile), PathSolver.DEFAULT, null); - this.dncUnit = (MdxComplexInstance) unitDNCModel.addInstance(); - this.dncUnit.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); - this.dncUnit.setSequence(0); - this.dncUnitDay = (MdxComplexInstance) unitDNCModel.addInstance(); - this.dncUnitDay.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); - this.dncUnitDay.setSequence(0); - final MdxModel targetDNCModel = (MdxModel) load( - mdx("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTarget\\DNCLordaeronTarget.mdl"), PathSolver.DEFAULT, - null); - this.dncTarget = (MdxComplexInstance) targetDNCModel.addInstance(); - this.dncTarget.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); - this.dncTarget.setSequence(0); - } + public void setDayNightModels(final String terrainDNCFile, final String unitDNCFile) { + final MdxModel terrainDNCModel = (MdxModel) load(mdx(terrainDNCFile), PathSolver.DEFAULT, null); + this.dncTerrain = (MdxComplexInstance) terrainDNCModel.addInstance(); + this.dncTerrain.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); + this.dncTerrain.setSequence(0); + final MdxModel unitDNCModel = (MdxModel) load(mdx(unitDNCFile), PathSolver.DEFAULT, null); + this.dncUnit = (MdxComplexInstance) unitDNCModel.addInstance(); + this.dncUnit.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); + this.dncUnit.setSequence(0); + this.dncUnitDay = (MdxComplexInstance) unitDNCModel.addInstance(); + this.dncUnitDay.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); + this.dncUnitDay.setSequence(0); + final MdxModel targetDNCModel = (MdxModel) load( + mdx("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTarget\\DNCLordaeronTarget.mdl"), PathSolver.DEFAULT, + null); + this.dncTarget = (MdxComplexInstance) targetDNCModel.addInstance(); + this.dncTarget.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); + this.dncTarget.setSequence(0); + } - private static String mdx(String mdxPath) { - if (mdxPath.toLowerCase().endsWith(".mdl")) { - mdxPath = mdxPath.substring(0, mdxPath.length() - 4); - } - if (!mdxPath.toLowerCase().endsWith(".mdx")) { - mdxPath += ".mdx"; - } - return mdxPath; - } + private static String mdx(String mdxPath) { + if (mdxPath.toLowerCase().endsWith(".mdl")) { + mdxPath = mdxPath.substring(0, mdxPath.length() - 4); + } + if (!mdxPath.toLowerCase().endsWith(".mdx")) { + mdxPath += ".mdx"; + } + return mdxPath; + } - @Override - public SceneLightManager createLightManager(final boolean simple) { - if (simple) { - return new W3xScenePortraitLightManager(this, this.lightDirection); - } else { - return new W3xSceneWorldLightManager(this); - } - } + @Override + public SceneLightManager createLightManager(final boolean simple) { + if (simple) { + return new W3xScenePortraitLightManager(this, this.lightDirection); + } + else { + return new W3xSceneWorldLightManager(this); + } + } - public WorldEditStrings getWorldEditStrings() { - return this.worldEditStrings; - } + public WorldEditStrings getWorldEditStrings() { + return this.worldEditStrings; + } - public void setGameUI(final GameUI gameUI) { - this.gameUI = gameUI; - this.abilityDataUI = new AbilityDataUI(this.allObjectData.getAbilities(), this.allObjectData.getUnits(), - gameUI); - } + public void setGameUI(final GameUI gameUI) { + this.gameUI = gameUI; + this.abilityDataUI = new AbilityDataUI(this.allObjectData.getAbilities(), this.allObjectData.getUnits(), + gameUI); + } - public GameUI getGameUI() { - return this.gameUI; - } + public GameUI getGameUI() { + return this.gameUI; + } - public AbilityDataUI getAbilityDataUI() { - return this.abilityDataUI; - } + public AbilityDataUI getAbilityDataUI() { + return this.abilityDataUI; + } - public KeyedSounds getUiSounds() { - return this.uiSounds; - } + public KeyedSounds getUiSounds() { + return this.uiSounds; + } - public Warcraft3MapObjectData getAllObjectData() { - return this.allObjectData; - } + public Warcraft3MapObjectData getAllObjectData() { + return this.allObjectData; + } - public float getWalkableRenderHeight(final float x, final float y) { - this.walkableObjectsTree.intersect(x, y, this.walkablesIntersector.reset(x, y)); - return this.walkablesIntersector.z; - } + public float getWalkableRenderHeight(final float x, final float y) { + this.walkableObjectsTree.intersect(x, y, this.walkablesIntersector.reset(x, y)); + return this.walkablesIntersector.z; + } - public MdxComplexInstance getHighestWalkableUnder(final float x, final float y) { - this.walkableObjectsTree.intersect(x, y, this.intersectorFindsHighestWalkable.reset(x, y)); - return this.intersectorFindsHighestWalkable.highestInstance; - } + public MdxComplexInstance getHighestWalkableUnder(final float x, final float y) { + this.walkableObjectsTree.intersect(x, y, this.intersectorFindsHighestWalkable.reset(x, y)); + return this.intersectorFindsHighestWalkable.highestInstance; + } - private static final class QuadtreeIntersectorFindsWalkableRenderHeight - implements QuadtreeIntersector { - private float z; - private final Ray ray = new Ray(); - private final Vector3 intersection = new Vector3(); + public int getLocalPlayerIndex() { + return this.localPlayerIndex; + } - private QuadtreeIntersectorFindsWalkableRenderHeight reset(final float x, final float y) { - this.z = -Float.MAX_VALUE; - this.ray.set(x, y, 4096, 0, 0, -8192); - return this; - } + private static final class QuadtreeIntersectorFindsWalkableRenderHeight + implements QuadtreeIntersector { + private float z; + private final Ray ray = new Ray(); + private final Vector3 intersection = new Vector3(); - @Override - public boolean onIntersect(final MdxComplexInstance intersectingObject) { - if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) { - this.z = Math.max(this.z, this.intersection.z); - } - return false; - } - } + private QuadtreeIntersectorFindsWalkableRenderHeight reset(final float x, final float y) { + this.z = -Float.MAX_VALUE; + this.ray.set(x, y, 4096, 0, 0, -8192); + return this; + } - private static final class QuadtreeIntersectorFindsHighestWalkable - implements QuadtreeIntersector { - private float z; - private final Ray ray = new Ray(); - private final Vector3 intersection = new Vector3(); - private MdxComplexInstance highestInstance; + @Override + public boolean onIntersect(final MdxComplexInstance intersectingObject) { + if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) { + this.z = Math.max(this.z, this.intersection.z); + } + return false; + } + } - private QuadtreeIntersectorFindsHighestWalkable reset(final float x, final float y) { - this.z = -Float.MAX_VALUE; - this.ray.set(x, y, 4096, 0, 0, -8192); - this.highestInstance = null; - return this; - } + private static final class QuadtreeIntersectorFindsHighestWalkable + implements QuadtreeIntersector { + private float z; + private final Ray ray = new Ray(); + private final Vector3 intersection = new Vector3(); + private MdxComplexInstance highestInstance; - @Override - public boolean onIntersect(final MdxComplexInstance intersectingObject) { - if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) { - if (this.intersection.z > this.z) { - this.z = this.intersection.z; - this.highestInstance = intersectingObject; - } - } - return false; - } - } + private QuadtreeIntersectorFindsHighestWalkable reset(final float x, final float y) { + this.z = -Float.MAX_VALUE; + this.ray.set(x, y, 4096, 0, 0, -8192); + this.highestInstance = null; + return this; + } - private static final class QuadtreeIntersectorFindsHitPoint implements QuadtreeIntersector { - private Ray ray; - private final Vector3 intersection = new Vector3(); - private boolean found; + @Override + public boolean onIntersect(final MdxComplexInstance intersectingObject) { + if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) { + if (this.intersection.z > this.z) { + this.z = this.intersection.z; + this.highestInstance = intersectingObject; + } + } + return false; + } + } - private QuadtreeIntersectorFindsHitPoint reset(final Ray ray) { - this.ray = ray; - this.found = false; - return this; - } + private static final class QuadtreeIntersectorFindsHitPoint implements QuadtreeIntersector { + private Ray ray; + private final Vector3 intersection = new Vector3(); + private boolean found; - @Override - public boolean onIntersect(final MdxComplexInstance intersectingObject) { - if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) { - this.found = true; - return true; - } - return false; - } - } + private QuadtreeIntersectorFindsHitPoint reset(final Ray ray) { + this.ray = ray; + this.found = false; + return this; + } + + @Override + public boolean onIntersect(final MdxComplexInstance intersectingObject) { + if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) { + this.found = true; + return true; + } + return false; + } + } } 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 8affe9f..cafeff8 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 @@ -12,10 +12,13 @@ import com.etheller.warsmash.util.RenderMathUtils; import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; -import com.etheller.warsmash.viewer5.handlers.w3x.*; +import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens; import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag; import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag; +import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils; import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover; +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; @@ -64,7 +67,6 @@ public class RenderUnit { private boolean corpse; private boolean boneCorpse; private final RenderUnitTypeData typeData; - public UnitSound buildSound; public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final float x, final float y, final float z, final int playerIndex, final UnitSoundset soundset, @@ -127,7 +129,6 @@ public class RenderUnit { final float blendTime = row.getFieldAsFloat(BLEND_TIME, 0); instance.setBlendTime(blendTime * 1000.0f); - buildSound = map.getUiSounds().getSound(row.getFieldAsString(BUILD_SOUND_LABEL, 0)); } this.instance = instance; @@ -350,8 +351,9 @@ public class RenderUnit { this.selectionCircle.move(dx, dy, map.terrain.centerOffset); } this.unitAnimationListenerImpl.update(); - if(!dead && simulationUnit.isConstructing()) { - instance.setFrameByRatio(simulationUnit.getConstructionProgress() / simulationUnit.getUnitType().getBuildTime()); + if (!dead && this.simulationUnit.isConstructing()) { + this.instance.setFrameByRatio( + this.simulationUnit.getConstructionProgress() / this.simulationUnit.getUnitType().getBuildTime()); } } 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 index 12cc1d8..034e7fd 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/CommandCardIcon.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/CommandCardIcon.java @@ -50,6 +50,7 @@ public class CommandCardIcon extends AbstractRenderableFrame { this.activeHighlightFrame.setVisible(false); this.cooldownFrame.setVisible(false); this.autocastFrame.setVisible(false); + setVisible(false); } else { if (commandButton.isEnabled()) { @@ -74,6 +75,7 @@ public class CommandCardIcon extends AbstractRenderableFrame { public void setCommandButtonData(final Texture texture, final int abilityHandleId, final int orderId, final int autoCastOrderId, final boolean active, final boolean autoCastActive, final boolean menuButton) { this.menuButton = menuButton; + setVisible(true); this.iconFrame.setVisible(true); this.activeHighlightFrame.setVisible(active); this.cooldownFrame.setVisible(false); @@ -113,7 +115,7 @@ public class CommandCardIcon extends AbstractRenderableFrame { @Override public UIFrame touchDown(final float screenX, final float screenY, final int button) { - if (this.renderBounds.contains(screenX, screenY)) { + if (isVisible() && this.renderBounds.contains(screenX, screenY)) { return this; } return super.touchDown(screenX, screenY, button); @@ -121,7 +123,7 @@ public class CommandCardIcon extends AbstractRenderableFrame { @Override public UIFrame touchUp(final float screenX, final float screenY, final int button) { - if (this.renderBounds.contains(screenX, screenY)) { + if (isVisible() && this.renderBounds.contains(screenX, screenY)) { return this; } return super.touchUp(screenX, screenY, button); 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 0b0a887..51ca702 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 @@ -25,6 +25,7 @@ import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; import com.etheller.warsmash.parsers.fdf.datamodel.TextJustify; import com.etheller.warsmash.parsers.fdf.frames.SetPoint; +import com.etheller.warsmash.parsers.fdf.frames.SimpleStatusBarFrame; import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame; import com.etheller.warsmash.parsers.fdf.frames.StringFrame; import com.etheller.warsmash.parsers.fdf.frames.TextureFrame; @@ -79,1037 +80,1087 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgAbili import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandCardCommandListener; public class MeleeUI implements CUnitStateListener, CommandButtonListener, CommandCardCommandListener { - public static final float DEFAULT_COMMAND_CARD_ICON_WIDTH = 0.039f; - public static final float DEFAULT_COMMAND_CARD_ICON_PRESSED_WIDTH = 0.037f; - private static final int COMMAND_CARD_WIDTH = 4; - private static final int COMMAND_CARD_HEIGHT = 3; - - private static final Vector2 screenCoordsVector = new Vector2(); - private static final Vector3 clickLocationTemp = new Vector3(); - private static final Vector2 clickLocationTemp2 = new Vector2(); - private final DataSource dataSource; - private final ExtendViewport 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; - private UIFrame consoleUI; - private UIFrame resourceBar; - private StringFrame resourceBarGoldText; - private StringFrame resourceBarLumberText; - private StringFrame resourceBarSupplyText; - private StringFrame resourceBarUpkeepText; - private SpriteFrame timeIndicator; - private UIFrame unitPortrait; - private StringFrame unitLifeText; - private StringFrame unitManaText; - private Portrait portrait; - private final Rectangle tempRect = new Rectangle(); - private final Vector2 projectionTemp1 = new Vector2(); - private final Vector2 projectionTemp2 = new Vector2(); - private UIFrame simpleInfoPanelUnitDetail; - private StringFrame simpleNameValue; - private StringFrame simpleClassValue; - private StringFrame simpleBuildingActionLabel; - private UIFrame attack1Icon; - private TextureFrame attack1IconBackdrop; - private StringFrame attack1InfoPanelIconValue; - private StringFrame attack1InfoPanelIconLevel; - private UIFrame attack2Icon; - private TextureFrame attack2IconBackdrop; - private StringFrame attack2InfoPanelIconValue; - private StringFrame attack2InfoPanelIconLevel; - private UIFrame armorIcon; - private TextureFrame armorIconBackdrop; - private StringFrame armorInfoPanelIconValue; - private StringFrame armorInfoPanelIconLevel; - private InfoPanelIconBackdrops damageBackdrops; - private InfoPanelIconBackdrops defenseBackdrops; - private SpriteFrame buildTimeIndicator; - - private final CommandCardIcon[][] commandCard = new CommandCardIcon[COMMAND_CARD_HEIGHT][COMMAND_CARD_WIDTH]; - - private RenderUnit selectedUnit; - private final List subMenuOrderIdStack = new ArrayList<>(); - - // TODO remove this & replace with FDF - private final Texture activeButtonTexture; - private UIFrame inventoryCover; - private SpriteFrame cursorFrame; - private MeleeUIMinimap meleeUIMinimap; - private final CPlayerUnitOrderListener unitOrderListener; - private StringFrame errorMessageFrame; - - private CAbilityView activeCommand; - private int activeCommandOrderId; - private RenderUnit activeCommandUnit; - private MdxComplexInstance cursorModelInstance = null; - private BufferedImage cursorModelPathing; - - private int selectedSoundCount = 0; - private final ActiveCommandUnitTargetFilter activeCommandUnitTargetFilter; - - // TODO these corrections are used for old hardcoded UI stuff, we should - // probably remove them later - private final float widthRatioCorrection; - private final float heightRatioCorrection; - private CommandCardIcon mouseDownUIFrame; - - public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport, - final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final Scene portraitScene, - final CameraPreset[] cameraPresets, final CameraRates cameraRates, final War3MapViewer war3MapViewer, - final RootFrameListener rootFrameListener, final CPlayerUnitOrderListener unitOrderListener) { - this.dataSource = dataSource; - this.uiViewport = uiViewport; - this.fontGenerator = fontGenerator; - this.uiScene = uiScene; - this.portraitScene = portraitScene; - this.war3MapViewer = war3MapViewer; - this.rootFrameListener = rootFrameListener; - this.unitOrderListener = unitOrderListener; - - 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"); - this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter(); - this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f; - this.heightRatioCorrection = this.uiViewport.getMinWorldHeight() / 1200f; - - } - - private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) { - final Rectangle minimapDisplayArea = new Rectangle(18.75f * this.widthRatioCorrection, - 13.75f * this.heightRatioCorrection, 278.75f * this.widthRatioCorrection, - 276.25f * this.heightRatioCorrection); - 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); - } - - /** - * Called "main" because this was originally written in JASS so that maps could - * override it, and I may convert it back to the JASS at some point. - */ - public void main() { - // ================================= - // Load skins and templates - // ================================= - this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 0), this.uiViewport, - this.fontGenerator, this.uiScene, this.war3MapViewer); - this.rootFrameListener.onCreate(this.rootFrame); - try { - this.rootFrame.loadTOCFile("UI\\FrameDef\\FrameDef.toc"); - } catch (final IOException exc) { - throw new IllegalStateException("Unable to load FrameDef.toc", exc); - } - try { - this.rootFrame.loadTOCFile("UI\\FrameDef\\SmashFrameDef.toc"); - } catch (final IOException exc) { - throw new IllegalStateException("Unable to load SmashFrameDef.toc", exc); - } - this.damageBackdrops = new InfoPanelIconBackdrops(CAttackType.values(), this.rootFrame, "Damage", "Neutral"); - this.defenseBackdrops = new InfoPanelIconBackdrops(CDefenseType.values(), this.rootFrame, "Armor", "Neutral"); - - // ================================= - // Load major UI components - // ================================= - // Console UI is the background with the racial theme - this.consoleUI = this.rootFrame.createSimpleFrame("ConsoleUI", this.rootFrame, 0); - this.consoleUI.setSetAllPoints(true); - - // Resource bar is a 3 part bar with Gold, Lumber, and Food. - // Its template does not specify where to put it, so we must - // put it in the "TOPRIGHT" corner. - this.resourceBar = this.rootFrame.createSimpleFrame("ResourceBarFrame", this.consoleUI, 0); - this.resourceBar.addSetPoint(new SetPoint(FramePoint.TOPRIGHT, this.consoleUI, FramePoint.TOPRIGHT, 0, 0)); - this.resourceBarGoldText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarGoldText", 0); - this.resourceBarGoldText.setText("500"); - this.resourceBarLumberText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarLumberText", 0); - this.resourceBarLumberText.setText("150"); - this.resourceBarSupplyText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarSupplyText", 0); - this.resourceBarSupplyText.setText("12/100"); - this.resourceBarUpkeepText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarUpkeepText", 0); - this.resourceBarUpkeepText.setText("No Upkeep"); - this.resourceBarUpkeepText.setColor(Color.GREEN); - - // Create the Time Indicator (clock) - this.timeIndicator = (SpriteFrame) this.rootFrame.createFrame("TimeOfDayIndicator", this.rootFrame, 0, 0); - this.timeIndicator.setSequence(0); // play the stand - this.timeIndicator.setAnimationSpeed(0.0f); // do not advance automatically - - // Create the unit portrait stuff - 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); - this.unitManaText = (StringFrame) this.rootFrame.getFrameByName("UnitPortraitManaPointText", 0); - - this.simpleInfoPanelUnitDetail = this.rootFrame.createSimpleFrame("SimpleInfoPanelUnitDetail", this.consoleUI, - 0); - this.simpleInfoPanelUnitDetail - .addAnchor(new AnchorDefinition(FramePoint.BOTTOM, 0, GameUI.convertY(this.uiViewport, 0.0f))); - this.simpleInfoPanelUnitDetail.setWidth(GameUI.convertY(this.uiViewport, 0.180f)); - this.simpleInfoPanelUnitDetail.setHeight(GameUI.convertY(this.uiViewport, 0.105f)); - this.simpleNameValue = (StringFrame) this.rootFrame.getFrameByName("SimpleNameValue", 0); - this.simpleClassValue = (StringFrame) this.rootFrame.getFrameByName("SimpleClassValue", 0); - this.simpleBuildingActionLabel = (StringFrame) this.rootFrame.getFrameByName("SimpleBuildingActionLabel", 0); - - this.attack1Icon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconDamage", this.simpleInfoPanelUnitDetail, - 0); - this.attack1Icon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, - FramePoint.TOPLEFT, 0, GameUI.convertY(this.uiViewport, -0.030125f))); - this.attack1IconBackdrop = (TextureFrame) this.rootFrame.getFrameByName("InfoPanelIconBackdrop", 0); - this.attack1InfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 0); - this.attack1InfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 0); - - this.attack2Icon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconDamage", this.simpleInfoPanelUnitDetail, - 1); - this.attack2Icon - .addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT, - GameUI.convertX(this.uiViewport, 0.1f), GameUI.convertY(this.uiViewport, -0.030125f))); - this.attack2IconBackdrop = (TextureFrame) this.rootFrame.getFrameByName("InfoPanelIconBackdrop", 1); - this.attack2InfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 1); - this.attack2InfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 1); - - this.armorIcon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconArmor", this.simpleInfoPanelUnitDetail, - 1); - this.armorIcon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT, - GameUI.convertX(this.uiViewport, 0f), GameUI.convertY(this.uiViewport, -0.06025f))); - this.armorIconBackdrop = (TextureFrame) this.rootFrame.getFrameByName("InfoPanelIconBackdrop", 0); - this.armorInfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 0); - this.armorInfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 0); - - this.inventoryCover = this.rootFrame.createSimpleFrame("SmashConsoleInventoryCover", this.rootFrame, 0); - - this.errorMessageFrame = this.rootFrame.createStringFrame("SmashErrorMessageFrame", this.consoleUI, - new Color(0xFFFFCC00), TextJustify.LEFT, TextJustify.MIDDLE, 0.014f); - this.errorMessageFrame.addAnchor(new AnchorDefinition(FramePoint.BOTTOMLEFT, - GameUI.convertX(this.uiViewport, 0.275f), GameUI.convertY(this.uiViewport, 0.275f))); - this.errorMessageFrame.setWidth(GameUI.convertX(this.uiViewport, 0.25f)); - - 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); - this.rootFrame.add(commandCardIcon); - final TextureFrame iconFrame = this.rootFrame.createTextureFrame( - "SmashCommandButton_" + (commandButtonIndex) + "_Icon", this.rootFrame, false, null); - final TextureFrame activeHighlightFrame = this.rootFrame.createTextureFrame( - "SmashCommandButton_" + (commandButtonIndex) + "_ActiveHighlight", this.rootFrame, true, null, - FilterMode.ADDALPHA); - 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); - commandCardIcon.addAnchor(new AnchorDefinition(FramePoint.BOTTOMLEFT, - GameUI.convertX(this.uiViewport, 0.6175f + (0.0434f * i)), - GameUI.convertY(this.uiViewport, 0.095f - (0.044f * j)))); - commandCardIcon.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - commandCardIcon.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - iconFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); - iconFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - iconFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - iconFrame.setTexture(ImageUtils.DEFAULT_ICON_PATH, this.rootFrame); - activeHighlightFrame - .addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); - activeHighlightFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - activeHighlightFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - activeHighlightFrame.setTexture("CommandButtonActiveHighlight", this.rootFrame); - cooldownFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); - this.rootFrame.setSpriteFrameModel(cooldownFrame, this.rootFrame.getSkinField("CommandButtonCooldown")); - cooldownFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - cooldownFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - autocastFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); - this.rootFrame.setSpriteFrameModel(autocastFrame, this.rootFrame.getSkinField("CommandButtonAutocast")); - autocastFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - autocastFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); - commandCardIcon.set(iconFrame, activeHighlightFrame, cooldownFrame, autocastFrame); - this.commandCard[j][i] = commandCardIcon; - commandCardIcon.setCommandButton(null); - commandButtonIndex++; - } - } - buildTimeIndicator = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE", - "SmashBuildProgressBar", this.rootFrame, "", 0); - buildTimeIndicator.addSetPoint(new SetPoint(FramePoint.CENTER, simpleClassValue, FramePoint.CENTER, - GameUI.convertX(this.uiViewport, -0.04f), - GameUI.convertY(this.uiViewport, -0.025f))); - this.rootFrame.setSpriteFrameModel(buildTimeIndicator, this.rootFrame.getSkinField("BuildTimeIndicator")); - buildTimeIndicator.setAnimationSpeed(0); - - 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); - } - - @Override - public void startUsingAbility(final int abilityHandleId, final int orderId, final boolean rightClick) { - // TODO not O(N) - CAbilityView abilityToUse = null; - for (final CAbility ability : this.selectedUnit.getSimulationUnit().getAbilities()) { - if (ability.getHandleId() == abilityHandleId) { - abilityToUse = ability; - break; - } - } - if (abilityToUse != null) { - final StringMsgAbilityActivationReceiver stringMsgActivationReceiver = StringMsgAbilityActivationReceiver - .getInstance().reset(); - abilityToUse.checkCanUse(this.war3MapViewer.simulation, this.selectedUnit.getSimulationUnit(), orderId, - stringMsgActivationReceiver); - if (!stringMsgActivationReceiver.isUseOk()) { - showCommandError(stringMsgActivationReceiver.getMessage()); - } else { - final BooleanAbilityTargetCheckReceiver noTargetReceiver = BooleanAbilityTargetCheckReceiver - .getInstance().reset(); - abilityToUse.checkCanTargetNoTarget(this.war3MapViewer.simulation, - this.selectedUnit.getSimulationUnit(), orderId, noTargetReceiver); - if (noTargetReceiver.isTargetable()) { - this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), - abilityHandleId, orderId, isShiftDown()); - } else { - this.activeCommand = abilityToUse; - this.activeCommandOrderId = orderId; - this.activeCommandUnit = this.selectedUnit; - clearAndRepopulateCommandCard(); - } - } - } else { - this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), - abilityHandleId, orderId, isShiftDown()); - } - if (rightClick) { - this.war3MapViewer.getUiSounds().getSound("AutoCastButtonClick").play(this.uiScene.audioContext, 0, 0); - } - } - - @Override - public void openMenu(final int orderId) { - if (orderId == 0) { - this.subMenuOrderIdStack.clear(); - this.activeCommandUnit = null; - this.activeCommand = null; - this.activeCommandOrderId = -1; - } else { - this.subMenuOrderIdStack.add(orderId); - } - clearAndRepopulateCommandCard(); - } - - public void showCommandError(final String message) { - this.errorMessageFrame.setText(message); - } - - public void update(final float deltaTime) { - this.portrait.update(); - - final int baseMouseX = Gdx.input.getX(); - int mouseX = baseMouseX; - final int baseMouseY = Gdx.input.getY(); - int mouseY = baseMouseY; - 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)); - if (Gdx.input.isCursorCatched()) { - 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 (this.activeCommand != null) { - if (this.activeCommand instanceof AbstractCAbilityBuild) { - boolean justLoaded = false; - if (this.cursorModelInstance == null) { - final MutableObjectData unitData = this.war3MapViewer.getAllObjectData().getUnits(); - final War3ID buildingTypeId = new War3ID(this.activeCommandOrderId); - final String unitModelPath = this.war3MapViewer.getUnitModelPath(unitData.get(buildingTypeId)); - final MdxModel model = (MdxModel) this.war3MapViewer.load(unitModelPath, - this.war3MapViewer.mapPathSolver, this.war3MapViewer.solverParams); - this.cursorModelInstance = (MdxComplexInstance) model.addInstance(); - this.cursorModelInstance.setVertexColor(new float[]{1, 1, 1, 0.5f}); - this.cursorModelInstance.rotate(RenderUnit.tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, - this.war3MapViewer.simulation.getGameplayConstants().getBuildingAngle())); - this.cursorModelInstance.setAnimationSpeed(0f); - justLoaded = true; - final CUnitType buildingUnitType = this.war3MapViewer.simulation.getUnitData() - .getUnitType(buildingTypeId); - this.cursorModelPathing = buildingUnitType.getBuildingPathingPixelMap(); - } - this.war3MapViewer.getClickLocation(clickLocationTemp, baseMouseX, - Gdx.graphics.getHeight() - baseMouseY); - if (this.cursorModelPathing != null) { - clickLocationTemp.x = Math.round(clickLocationTemp.x / 64f) * 64f; - clickLocationTemp.y = Math.round(clickLocationTemp.y / 64f) * 64f; - clickLocationTemp.z = this.war3MapViewer.terrain.getGroundHeight(clickLocationTemp.x, - clickLocationTemp.y); - } - this.cursorModelInstance.setLocation(clickLocationTemp); - SequenceUtils.randomSequence(this.cursorModelInstance, PrimaryTag.STAND); - this.cursorFrame.setVisible(false); - if (justLoaded) { - this.cursorModelInstance.setScene(this.war3MapViewer.worldScene); - } - } else { - if (this.cursorModelInstance != null) { - this.cursorModelInstance.detach(); - this.cursorModelInstance = null; - this.cursorFrame.setVisible(true); - } - this.cursorFrame.setSequence("Target"); - } - } else { - if (this.cursorModelInstance != null) { - this.cursorModelInstance.detach(); - this.cursorModelInstance = null; - this.cursorFrame.setVisible(true); - } - 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"); - } - } - if (this.buildTimeIndicator.isVisible() && this.selectedUnit != null) { - buildTimeIndicator.setFrameByRatio(Math.min(this.selectedUnit.getSimulationUnit().getConstructionProgress() / - this.selectedUnit.getSimulationUnit().getUnitType().getBuildTime(), 0.99f)); - } - 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); - - } - - this.meleeUIMinimap.render(batch, this.war3MapViewer.units); - this.timeIndicator.setFrameByRatio(this.war3MapViewer.simulation.getGameTimeOfDay() - / this.war3MapViewer.simulation.getGameplayConstants().getGameDayHours()); - } - - public void portraitTalk() { - this.portrait.talk(); - } - - private final class ActiveCommandUnitTargetFilter implements CUnitFilterFunction { - @Override - public boolean call(final CUnit unit) { - final BooleanAbilityTargetCheckReceiver targetReceiver = BooleanAbilityTargetCheckReceiver - .getInstance(); - MeleeUI.this.activeCommand.checkCanTarget(MeleeUI.this.war3MapViewer.simulation, - MeleeUI.this.activeCommandUnit.getSimulationUnit(), MeleeUI.this.activeCommandOrderId, unit, - targetReceiver); - return targetReceiver.isTargetable(); - } - } - - private static final class Portrait { - private MdxComplexInstance modelInstance; - private final PortraitCameraManager portraitCameraManager; - private final Scene portraitScene; - - 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)); - } - - public void update() { - this.portraitCameraManager.updateCamera(); - if ((this.modelInstance != null) - && (this.modelInstance.sequenceEnded || (this.modelInstance.sequence == -1))) { - SequenceUtils.randomSequence(this.modelInstance, PrimaryTag.PORTRAIT, SequenceUtils.EMPTY, true); - } - } - - public void talk() { - SequenceUtils.randomSequence(this.modelInstance, PrimaryTag.PORTRAIT, SequenceUtils.TALK, true); - } - - public void setSelectedUnit(final RenderUnit unit) { - if (unit == null) { - if (this.modelInstance != null) { - this.portraitScene.removeInstance(this.modelInstance); - } - this.modelInstance = null; - this.portraitCameraManager.setModelInstance(null, null); - } else { - final MdxModel portraitModel = unit.portraitModel; - if (portraitModel != null) { - if (this.modelInstance != null) { - this.portraitScene.removeInstance(this.modelInstance); - } - this.modelInstance = (MdxComplexInstance) portraitModel.addInstance(); - this.portraitCameraManager.setModelInstance(this.modelInstance, portraitModel); - this.modelInstance.setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP); - this.modelInstance.setScene(this.portraitScene); - this.modelInstance.setVertexColor(unit.instance.vertexColor); - this.modelInstance.setTeamColor(unit.playerIndex); - } - } - } - } - - public void selectUnit(RenderUnit unit) { - this.subMenuOrderIdStack.clear(); - if ((unit != null) && unit.getSimulationUnit().isDead()) { - unit = null; - } - if (this.selectedUnit != null) { - this.selectedUnit.getSimulationUnit().removeStateListener(this); - } - this.portrait.setSelectedUnit(unit); - this.selectedUnit = unit; - if (unit == null) { - clearCommandCard(); - this.simpleNameValue.setText(""); - this.unitLifeText.setText(""); - this.unitManaText.setText(""); - this.simpleClassValue.setText(""); - this.simpleBuildingActionLabel.setText(""); - this.attack1Icon.setVisible(false); - this.attack2Icon.setVisible(false); - this.attack1InfoPanelIconLevel.setText(""); - this.attack2InfoPanelIconLevel.setText(""); - this.armorIcon.setVisible(false); - this.armorInfoPanelIconLevel.setText(""); - this.buildTimeIndicator.setVisible(false); - } else { - unit.getSimulationUnit().addStateListener(this); - this.simpleNameValue.setText(unit.getSimulationUnit().getUnitType().getName()); - String classText = null; - for (final CUnitClassification classification : unit.getSimulationUnit().getClassifications()) { - if ((classification == CUnitClassification.MECHANICAL) - && unit.getSimulationUnit().getUnitType().isBuilding()) { - // buildings dont display MECHANICAL - continue; - } - if (classification.getDisplayName() != null) { - classText = classification.getDisplayName(); - } - } - if (classText != null) { - this.simpleClassValue.setText(classText); - } else { - this.simpleClassValue.setText(""); - } - this.unitLifeText.setText(FastNumberFormat.formatWholeNumber(unit.getSimulationUnit().getLife()) + " / " - + unit.getSimulationUnit().getMaximumLife()); - final int maximumMana = unit.getSimulationUnit().getMaximumMana(); - if (maximumMana > 0) { - this.unitManaText.setText( - FastNumberFormat.formatWholeNumber(unit.getSimulationUnit().getMana()) + " / " + maximumMana); - } else { - this.unitManaText.setText(""); - } - this.simpleBuildingActionLabel.setText(""); - - final boolean anyAttacks = unit.getSimulationUnit().getUnitType().getAttacks().size() > 0; - boolean constructing = unit.getSimulationUnit().isConstructing(); - final UIFrame localArmorIcon = this.armorIcon; - final TextureFrame localArmorIconBackdrop = this.armorIconBackdrop; - final StringFrame localArmorInfoPanelIconValue = this.armorInfoPanelIconValue; - if (anyAttacks && !constructing) { - final CUnitAttack attackOne = unit.getSimulationUnit().getUnitType().getAttacks().get(0); - this.attack1Icon.setVisible(attackOne.isShowUI()); - this.attack1IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackOne.getAttackType())); - this.attack1InfoPanelIconValue.setText(attackOne.getMinDamage() + " - " + attackOne.getMaxDamage()); - if (unit.getSimulationUnit().getUnitType().getAttacks().size() > 1) { - final CUnitAttack attackTwo = unit.getSimulationUnit().getUnitType().getAttacks().get(1); - this.attack2Icon.setVisible(attackTwo.isShowUI()); - this.attack2IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackTwo.getAttackType())); - this.attack2InfoPanelIconValue.setText(attackTwo.getMinDamage() + " - " + attackTwo.getMaxDamage()); - } else { - this.attack2Icon.setVisible(false); - } - - 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.attack1Icon.setVisible(false); - this.attack2Icon.setVisible(false); - - 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(!constructing); - buildTimeIndicator.setVisible(constructing); - if (constructing) { - buildTimeIndicator.setSequence(0); - } - final Texture defenseTexture = this.defenseBackdrops - .getTexture(unit.getSimulationUnit().getUnitType().getDefenseType()); - if (defenseTexture == null) { - throw new RuntimeException( - unit.getSimulationUnit().getUnitType().getDefenseType() + " can't find texture!"); - } - localArmorIconBackdrop.setTexture(defenseTexture); - localArmorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense())); - clearAndRepopulateCommandCard(); - } - } - - private void clearCommandCard() { - for (int j = 0; j < COMMAND_CARD_HEIGHT; j++) { - for (int i = 0; i < COMMAND_CARD_WIDTH; i++) { - this.commandCard[j][i].setCommandButton(null); - } - } - } - - @Override - public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon, - final int abilityHandleId, final int orderId, final int autoCastId, final boolean active, - final boolean autoCastActive, final boolean menuButton) { - 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, abilityHandleId, orderId, autoCastId, active, autoCastActive, - menuButton); - } - - public void resize(final Rectangle viewport) { - this.cameraManager.resize(viewport); - positionPortrait(); - } - - public void positionPortrait() { - this.projectionTemp1.x = 422 * this.widthRatioCorrection; - this.projectionTemp1.y = 57 * this.heightRatioCorrection; - this.projectionTemp2.x = (422 + 167) * this.widthRatioCorrection; - this.projectionTemp2.y = (57 + 170) * this.heightRatioCorrection; - this.uiViewport.project(this.projectionTemp1); - this.uiViewport.project(this.projectionTemp2); - - this.tempRect.x = this.projectionTemp1.x + this.uiViewport.getScreenX(); - this.tempRect.y = this.projectionTemp1.y + this.uiViewport.getScreenY(); - this.tempRect.width = this.projectionTemp2.x - this.projectionTemp1.x; - this.tempRect.height = this.projectionTemp2.y - this.projectionTemp1.y; - this.portrait.portraitScene.camera.viewport(this.tempRect); - } - - private static final class InfoPanelIconBackdrops { - private final Texture[] damageBackdropTextures; - - public InfoPanelIconBackdrops(final CodeKeyType[] attackTypes, final GameUI gameUI, final String prefix, - final String suffix) { - this.damageBackdropTextures = new Texture[attackTypes.length]; - for (int index = 0; index < attackTypes.length; index++) { - final CodeKeyType attackType = attackTypes[index]; - String skinLookupKey = "InfoPanelIcon" + prefix + attackType.getCodeKey() + suffix; - final Texture suffixTexture = gameUI.loadTexture(gameUI.getSkinField(skinLookupKey)); - if (suffixTexture != null) { - this.damageBackdropTextures[index] = suffixTexture; - } else { - skinLookupKey = "InfoPanelIcon" + prefix + attackType.getCodeKey(); - this.damageBackdropTextures[index] = gameUI.loadTexture(gameUI.getSkinField(skinLookupKey)); - } - } - } - - public Texture getTexture(final CodeKeyType attackType) { - if (attackType != null) { - final int ordinal = attackType.ordinal(); - if ((ordinal >= 0) && (ordinal < this.damageBackdropTextures.length)) { - return this.damageBackdropTextures[ordinal]; - } - } - return this.damageBackdropTextures[0]; - } - - private static String getSuffix(final CAttackType attackType) { - switch (attackType) { - case CHAOS: - return "Chaos"; - case HERO: - return "Hero"; - case MAGIC: - return "Magic"; - case NORMAL: - return "Normal"; - case PIERCE: - return "Pierce"; - case SIEGE: - return "Siege"; - case SPELLS: - return "Magic"; - case UNKNOWN: - return "Unknown"; - default: - throw new IllegalArgumentException("Unknown attack type: " + attackType); - } - - } - } - - @Override - public void lifeChanged() { - if (this.selectedUnit.getSimulationUnit().isDead()) { - selectUnit(null); - } else { - this.unitLifeText - .setText(FastNumberFormat.formatWholeNumber(this.selectedUnit.getSimulationUnit().getLife()) + " / " - + this.selectedUnit.getSimulationUnit().getMaximumLife()); - } - } - - @Override - public void ordersChanged(final int abilityHandleId, final int orderId) { - selectUnit(selectedUnit); - } - - private void clearAndRepopulateCommandCard() { - clearCommandCard(); - final AbilityDataUI abilityDataUI = this.war3MapViewer.getAbilityDataUI(); - final int menuOrderId = getSubMenuOrderId(); - if (this.activeCommand != null) { - final IconUI cancelUI = abilityDataUI.getCancelUI(); - this.commandButton(cancelUI.getButtonPositionX(), cancelUI.getButtonPositionY(), cancelUI.getIcon(), 0, - menuOrderId, 0, false, false, true); - } else if (this.selectedUnit.getSimulationUnit().isConstructing()) { - - } else { - if (menuOrderId != 0) { - final int exitOrderId = this.subMenuOrderIdStack.size() > 1 - ? this.subMenuOrderIdStack.get(this.subMenuOrderIdStack.size() - 2) - : 0; - final IconUI cancelUI = abilityDataUI.getCancelUI(); - this.commandButton(cancelUI.getButtonPositionX(), cancelUI.getButtonPositionY(), cancelUI.getIcon(), 0, - exitOrderId, 0, false, false, true); - } - this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this, abilityDataUI, menuOrderId); - } - } - - private int getSubMenuOrderId() { - return this.subMenuOrderIdStack.isEmpty() ? 0 - : this.subMenuOrderIdStack.get(this.subMenuOrderIdStack.size() - 1); - } - - 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 int screenX, final int screenY, final float worldScreenY, final int button) { - screenCoordsVector.set(screenX, screenY); - this.uiViewport.unproject(screenCoordsVector); - if (this.meleeUIMinimap.containsMouse(screenCoordsVector.x, screenCoordsVector.y)) { - final Vector2 worldPoint = this.meleeUIMinimap.getWorldPointFromScreen(screenCoordsVector.x, - screenCoordsVector.y); - this.cameraManager.target.x = worldPoint.x; - this.cameraManager.target.y = worldPoint.y; - return true; - } - final UIFrame clickedUIFrame = this.rootFrame.touchDown(screenCoordsVector.x, screenCoordsVector.y, button); - if (clickedUIFrame == null) { - // try to interact with world - if (this.activeCommand != null) { - if (button == Input.Buttons.RIGHT) { - this.activeCommandUnit = null; - this.activeCommand = null; - this.activeCommandOrderId = -1; - clearAndRepopulateCommandCard(); - } else { - final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY, - this.activeCommandUnitTargetFilter); - final boolean shiftDown = isShiftDown(); - if (rayPickUnit != null) { - this.unitOrderListener.issueTargetOrder( - this.activeCommandUnit.getSimulationUnit().getHandleId(), - this.activeCommand.getHandleId(), this.activeCommandOrderId, - rayPickUnit.getSimulationUnit().getHandleId(), shiftDown); - if (getSelectedUnit().soundset.yesAttack - .playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { - portraitTalk(); - } - this.selectedSoundCount = 0; - if (!shiftDown) { - this.subMenuOrderIdStack.clear(); - this.activeCommandUnit = null; - this.activeCommand = null; - this.activeCommandOrderId = -1; - clearAndRepopulateCommandCard(); - } - } else { - this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY); - clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y); - - this.activeCommand.checkCanTarget(this.war3MapViewer.simulation, - this.activeCommandUnit.getSimulationUnit(), this.activeCommandOrderId, - clickLocationTemp2, PointAbilityTargetCheckReceiver.INSTANCE); - final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget(); - if (target != null) { - if (this.activeCommand instanceof CAbilityAttack) { - this.war3MapViewer.showConfirmation(clickLocationTemp, 1, 0, 0); - } else { - this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); - } - this.unitOrderListener.issuePointOrder( - this.activeCommandUnit.getSimulationUnit().getHandleId(), - this.activeCommand.getHandleId(), this.activeCommandOrderId, clickLocationTemp2.x, - clickLocationTemp2.y, shiftDown); - if (getSelectedUnit().soundset.yes - .playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { - portraitTalk(); - } - this.selectedSoundCount = 0; - if (this.activeCommand instanceof AbstractCAbilityBuild) { - this.war3MapViewer.getUiSounds().getSound("PlaceBuildingDefault") - .play(this.uiScene.audioContext, 0, 0); - } - if (!shiftDown) { - this.subMenuOrderIdStack.clear(); - this.activeCommandUnit = null; - this.activeCommand = null; - this.activeCommandOrderId = -1; - clearAndRepopulateCommandCard(); - } - - } - } - } - } else { - if (button == Input.Buttons.RIGHT) { - if (getSelectedUnit() != null) { - final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY); - if ((rayPickUnit != null) && !rayPickUnit.getSimulationUnit().isDead()) { - boolean ordered = false; - for (final RenderUnit unit : this.war3MapViewer.selected) { - for (final CAbility ability : unit.getSimulationUnit().getAbilities()) { - ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(), - OrderIds.smart, rayPickUnit.getSimulationUnit(), - CWidgetAbilityTargetCheckReceiver.INSTANCE); - final CWidget targetWidget = CWidgetAbilityTargetCheckReceiver.INSTANCE.getTarget(); - if (targetWidget != null) { - this.unitOrderListener.issueTargetOrder(unit.getSimulationUnit().getHandleId(), - ability.getHandleId(), OrderIds.smart, targetWidget.getHandleId(), - isShiftDown()); - ordered = true; - } - } - - } - if (ordered) { - if (getSelectedUnit().soundset.yesAttack.playUnitResponse( - this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { - portraitTalk(); - } - this.selectedSoundCount = 0; - } - } else { - this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY); - this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); - clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y); - - boolean ordered = false; - for (final RenderUnit unit : this.war3MapViewer.selected) { - for (final CAbility ability : unit.getSimulationUnit().getAbilities()) { - ability.checkCanUse(this.war3MapViewer.simulation, unit.getSimulationUnit(), - OrderIds.smart, BooleanAbilityActivationReceiver.INSTANCE); - if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) { - ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(), - OrderIds.smart, clickLocationTemp2, - PointAbilityTargetCheckReceiver.INSTANCE); - final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget(); - if (target != null) { - this.unitOrderListener.issuePointOrder( - unit.getSimulationUnit().getHandleId(), ability.getHandleId(), - OrderIds.smart, clickLocationTemp2.x, clickLocationTemp2.y, - isShiftDown()); - ordered = true; - } - } - } - - } - - if (ordered) { - if (getSelectedUnit().soundset.yes.playUnitResponse( - this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { - portraitTalk(); - } - this.selectedSoundCount = 0; - } - } - } - } else { - final List selectedUnits = this.war3MapViewer.selectUnit(screenX, worldScreenY, false); - if (!selectedUnits.isEmpty()) { - final RenderUnit unit = selectedUnits.get(0); - final boolean selectionChanged = getSelectedUnit() != unit; - boolean playedNewSound = false; - if (selectionChanged) { - this.selectedSoundCount = 0; - } - if (unit.soundset != null) { - UnitSound ackSoundToPlay = unit.soundset.what; - int soundIndex; - final int pissedSoundCount = unit.soundset.pissed.getSoundCount(); - if (unit.getSimulationUnit().isConstructing()) { - ackSoundToPlay = unit.buildSound; - soundIndex = 0; - } else { - if ((this.selectedSoundCount >= 3) && (pissedSoundCount > 0)) { - soundIndex = this.selectedSoundCount - 3; - ackSoundToPlay = unit.soundset.pissed; - } else { - soundIndex = (int) (Math.random() * ackSoundToPlay.getSoundCount()); - } - } - if (ackSoundToPlay != null && ackSoundToPlay.playUnitResponse(this.war3MapViewer.worldScene.audioContext, unit, - soundIndex)) { - this.selectedSoundCount++; - if ((this.selectedSoundCount - 3) >= pissedSoundCount) { - this.selectedSoundCount = 0; - } - playedNewSound = true; - } - } - if (selectionChanged) { - selectUnit(unit); - } - if (playedNewSound) { - portraitTalk(); - } - } else { - selectUnit(null); - } - } - } - } else { - if (clickedUIFrame instanceof CommandCardIcon) { - this.mouseDownUIFrame = (CommandCardIcon) clickedUIFrame; - this.mouseDownUIFrame.mouseDown(this.uiViewport); - } - } - return false; - } - - public boolean touchUp(final int screenX, final int screenY, final float worldScreenY, final int button) { - screenCoordsVector.set(screenX, screenY); - this.uiViewport.unproject(screenCoordsVector); - final UIFrame clickedUIFrame = this.rootFrame.touchUp(screenCoordsVector.x, screenCoordsVector.y, button); - if (this.mouseDownUIFrame != null) { - if (clickedUIFrame == this.mouseDownUIFrame) { - this.mouseDownUIFrame.onClick(button); - this.war3MapViewer.getUiSounds().getSound("InterfaceClick").play(this.uiScene.audioContext, 0, 0); - } - this.mouseDownUIFrame.mouseUp(this.uiViewport); - } - this.mouseDownUIFrame = null; - return false; - } - - private static boolean isShiftDown() { - return Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT); - } - - public boolean touchDragged(final int screenX, final int screenY, final float worldScreenY, final int pointer) { - screenCoordsVector.set(screenX, screenY); - this.uiViewport.unproject(screenCoordsVector); - - if (this.meleeUIMinimap.containsMouse(screenCoordsVector.x, screenCoordsVector.y)) { - final Vector2 worldPoint = this.meleeUIMinimap.getWorldPointFromScreen(screenCoordsVector.x, - screenCoordsVector.y); - this.cameraManager.target.x = worldPoint.x; - this.cameraManager.target.y = worldPoint.y; - } - return false; - } - - public float getHeightRatioCorrection() { - return this.heightRatioCorrection; - } + public static final float DEFAULT_COMMAND_CARD_ICON_WIDTH = 0.039f; + public static final float DEFAULT_COMMAND_CARD_ICON_PRESSED_WIDTH = 0.037f; + private static final int COMMAND_CARD_WIDTH = 4; + private static final int COMMAND_CARD_HEIGHT = 3; + + private static final Vector2 screenCoordsVector = new Vector2(); + private static final Vector3 clickLocationTemp = new Vector3(); + private static final Vector2 clickLocationTemp2 = new Vector2(); + private final DataSource dataSource; + private final ExtendViewport 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; + private UIFrame consoleUI; + private UIFrame resourceBar; + private StringFrame resourceBarGoldText; + private StringFrame resourceBarLumberText; + private StringFrame resourceBarSupplyText; + private StringFrame resourceBarUpkeepText; + private SpriteFrame timeIndicator; + private UIFrame unitPortrait; + private StringFrame unitLifeText; + private StringFrame unitManaText; + private Portrait portrait; + private final Rectangle tempRect = new Rectangle(); + private final Vector2 projectionTemp1 = new Vector2(); + private final Vector2 projectionTemp2 = new Vector2(); + private UIFrame simpleInfoPanelUnitDetail; + private StringFrame simpleNameValue; + private StringFrame simpleClassValue; + private StringFrame simpleBuildingActionLabel; + private SimpleStatusBarFrame simpleBuildTimeIndicator; + private UIFrame attack1Icon; + private TextureFrame attack1IconBackdrop; + private StringFrame attack1InfoPanelIconValue; + private StringFrame attack1InfoPanelIconLevel; + private UIFrame attack2Icon; + private TextureFrame attack2IconBackdrop; + private StringFrame attack2InfoPanelIconValue; + private StringFrame attack2InfoPanelIconLevel; + private UIFrame armorIcon; + private TextureFrame armorIconBackdrop; + private StringFrame armorInfoPanelIconValue; + private StringFrame armorInfoPanelIconLevel; + private InfoPanelIconBackdrops damageBackdrops; + private InfoPanelIconBackdrops defenseBackdrops; + + private final CommandCardIcon[][] commandCard = new CommandCardIcon[COMMAND_CARD_HEIGHT][COMMAND_CARD_WIDTH]; + + private RenderUnit selectedUnit; + private final List subMenuOrderIdStack = new ArrayList<>(); + + // TODO remove this & replace with FDF + private final Texture activeButtonTexture; + private UIFrame inventoryCover; + private SpriteFrame cursorFrame; + private MeleeUIMinimap meleeUIMinimap; + private final CPlayerUnitOrderListener unitOrderListener; + private StringFrame errorMessageFrame; + + private CAbilityView activeCommand; + private int activeCommandOrderId; + private RenderUnit activeCommandUnit; + private MdxComplexInstance cursorModelInstance = null; + private BufferedImage cursorModelPathing; + + private int selectedSoundCount = 0; + private final ActiveCommandUnitTargetFilter activeCommandUnitTargetFilter; + + // TODO these corrections are used for old hardcoded UI stuff, we should + // probably remove them later + private final float widthRatioCorrection; + private final float heightRatioCorrection; + private CommandCardIcon mouseDownUIFrame; + + public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport, + final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final Scene portraitScene, + final CameraPreset[] cameraPresets, final CameraRates cameraRates, final War3MapViewer war3MapViewer, + final RootFrameListener rootFrameListener, final CPlayerUnitOrderListener unitOrderListener) { + this.dataSource = dataSource; + this.uiViewport = uiViewport; + this.fontGenerator = fontGenerator; + this.uiScene = uiScene; + this.portraitScene = portraitScene; + this.war3MapViewer = war3MapViewer; + this.rootFrameListener = rootFrameListener; + this.unitOrderListener = unitOrderListener; + + this.cameraManager = new GameCameraManager(cameraPresets, cameraRates); + + this.cameraManager.setupCamera(war3MapViewer.worldScene); + final float[] startLocation = this.war3MapViewer.simulation.getPlayer(war3MapViewer.getLocalPlayerIndex()) + .getStartLocation(); + this.cameraManager.target.x = startLocation[0]; + this.cameraManager.target.y = startLocation[1]; + + this.activeButtonTexture = ImageUtils.getBLPTexture(war3MapViewer.mapMpq, + "UI\\Widgets\\Console\\Human\\CommandButton\\human-activebutton.blp"); + this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter(); + this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f; + this.heightRatioCorrection = this.uiViewport.getMinWorldHeight() / 1200f; + + } + + private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) { + final Rectangle minimapDisplayArea = new Rectangle(18.75f * this.widthRatioCorrection, + 13.75f * this.heightRatioCorrection, 278.75f * this.widthRatioCorrection, + 276.25f * this.heightRatioCorrection); + 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); + } + + /** + * Called "main" because this was originally written in JASS so that maps could + * override it, and I may convert it back to the JASS at some point. + */ + public void main() { + // ================================= + // Load skins and templates + // ================================= + this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 0), this.uiViewport, + this.fontGenerator, this.uiScene, this.war3MapViewer); + this.rootFrameListener.onCreate(this.rootFrame); + try { + this.rootFrame.loadTOCFile("UI\\FrameDef\\FrameDef.toc"); + } + catch (final IOException exc) { + throw new IllegalStateException("Unable to load FrameDef.toc", exc); + } + try { + this.rootFrame.loadTOCFile("UI\\FrameDef\\SmashFrameDef.toc"); + } + catch (final IOException exc) { + throw new IllegalStateException("Unable to load SmashFrameDef.toc", exc); + } + this.damageBackdrops = new InfoPanelIconBackdrops(CAttackType.values(), this.rootFrame, "Damage", "Neutral"); + this.defenseBackdrops = new InfoPanelIconBackdrops(CDefenseType.values(), this.rootFrame, "Armor", "Neutral"); + + // ================================= + // Load major UI components + // ================================= + // Console UI is the background with the racial theme + this.consoleUI = this.rootFrame.createSimpleFrame("ConsoleUI", this.rootFrame, 0); + this.consoleUI.setSetAllPoints(true); + + // Resource bar is a 3 part bar with Gold, Lumber, and Food. + // Its template does not specify where to put it, so we must + // put it in the "TOPRIGHT" corner. + this.resourceBar = this.rootFrame.createSimpleFrame("ResourceBarFrame", this.consoleUI, 0); + this.resourceBar.addSetPoint(new SetPoint(FramePoint.TOPRIGHT, this.consoleUI, FramePoint.TOPRIGHT, 0, 0)); + this.resourceBarGoldText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarGoldText", 0); + this.resourceBarGoldText.setText("500"); + this.resourceBarLumberText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarLumberText", 0); + this.resourceBarLumberText.setText("150"); + this.resourceBarSupplyText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarSupplyText", 0); + this.resourceBarSupplyText.setText("12/100"); + this.resourceBarUpkeepText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarUpkeepText", 0); + this.resourceBarUpkeepText.setText("No Upkeep"); + this.resourceBarUpkeepText.setColor(Color.GREEN); + + // Create the Time Indicator (clock) + this.timeIndicator = (SpriteFrame) this.rootFrame.createFrame("TimeOfDayIndicator", this.rootFrame, 0, 0); + this.timeIndicator.setSequence(0); // play the stand + this.timeIndicator.setAnimationSpeed(0.0f); // do not advance automatically + + // Create the unit portrait stuff + 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); + this.unitManaText = (StringFrame) this.rootFrame.getFrameByName("UnitPortraitManaPointText", 0); + + this.simpleInfoPanelUnitDetail = this.rootFrame.createSimpleFrame("SimpleInfoPanelUnitDetail", this.consoleUI, + 0); + this.simpleInfoPanelUnitDetail + .addAnchor(new AnchorDefinition(FramePoint.BOTTOM, 0, GameUI.convertY(this.uiViewport, 0.0f))); + this.simpleInfoPanelUnitDetail.setWidth(GameUI.convertY(this.uiViewport, 0.180f)); + this.simpleInfoPanelUnitDetail.setHeight(GameUI.convertY(this.uiViewport, 0.105f)); + this.simpleNameValue = (StringFrame) this.rootFrame.getFrameByName("SimpleNameValue", 0); + this.simpleClassValue = (StringFrame) this.rootFrame.getFrameByName("SimpleClassValue", 0); + this.simpleBuildingActionLabel = (StringFrame) this.rootFrame.getFrameByName("SimpleBuildingActionLabel", 0); + this.simpleBuildTimeIndicator = (SimpleStatusBarFrame) this.rootFrame.getFrameByName("SimpleBuildTimeIndicator", + 0); + final TextureFrame simpleBuildTimeIndicatorBar = this.simpleBuildTimeIndicator.getBarFrame(); + simpleBuildTimeIndicatorBar.setTexture("SimpleBuildTimeIndicator", this.rootFrame); + final TextureFrame simpleBuildTimeIndicatorBorder = this.simpleBuildTimeIndicator.getBorderFrame(); + simpleBuildTimeIndicatorBorder.setTexture("SimpleBuildTimeIndicatorBorder", this.rootFrame); + this.simpleBuildTimeIndicator.setWidth(GameUI.convertX(this.uiViewport, 0.10538f)); + this.simpleBuildTimeIndicator.setHeight(GameUI.convertY(this.uiViewport, 0.0103f)); + + this.attack1Icon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconDamage", this.simpleInfoPanelUnitDetail, + 0); + this.attack1Icon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, + FramePoint.TOPLEFT, 0, GameUI.convertY(this.uiViewport, -0.030125f))); + this.attack1IconBackdrop = (TextureFrame) this.rootFrame.getFrameByName("InfoPanelIconBackdrop", 0); + this.attack1InfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 0); + this.attack1InfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 0); + + this.attack2Icon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconDamage", this.simpleInfoPanelUnitDetail, + 1); + this.attack2Icon + .addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT, + GameUI.convertX(this.uiViewport, 0.1f), GameUI.convertY(this.uiViewport, -0.030125f))); + this.attack2IconBackdrop = (TextureFrame) this.rootFrame.getFrameByName("InfoPanelIconBackdrop", 1); + this.attack2InfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 1); + this.attack2InfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 1); + + this.armorIcon = this.rootFrame.createSimpleFrame("SimpleInfoPanelIconArmor", this.simpleInfoPanelUnitDetail, + 1); + this.armorIcon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.simpleInfoPanelUnitDetail, FramePoint.TOPLEFT, + GameUI.convertX(this.uiViewport, 0f), GameUI.convertY(this.uiViewport, -0.06025f))); + this.armorIconBackdrop = (TextureFrame) this.rootFrame.getFrameByName("InfoPanelIconBackdrop", 0); + this.armorInfoPanelIconValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconValue", 0); + this.armorInfoPanelIconLevel = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconLevel", 0); + + this.inventoryCover = this.rootFrame.createSimpleFrame("SmashConsoleInventoryCover", this.rootFrame, 0); + + this.errorMessageFrame = this.rootFrame.createStringFrame("SmashErrorMessageFrame", this.consoleUI, + new Color(0xFFFFCC00), TextJustify.LEFT, TextJustify.MIDDLE, 0.014f); + this.errorMessageFrame.addAnchor(new AnchorDefinition(FramePoint.BOTTOMLEFT, + GameUI.convertX(this.uiViewport, 0.275f), GameUI.convertY(this.uiViewport, 0.275f))); + this.errorMessageFrame.setWidth(GameUI.convertX(this.uiViewport, 0.25f)); + + 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); + this.rootFrame.add(commandCardIcon); + final TextureFrame iconFrame = this.rootFrame.createTextureFrame( + "SmashCommandButton_" + (commandButtonIndex) + "_Icon", this.rootFrame, false, null); + final TextureFrame activeHighlightFrame = this.rootFrame.createTextureFrame( + "SmashCommandButton_" + (commandButtonIndex) + "_ActiveHighlight", this.rootFrame, true, null, + FilterMode.ADDALPHA); + 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); + commandCardIcon.addAnchor(new AnchorDefinition(FramePoint.BOTTOMLEFT, + GameUI.convertX(this.uiViewport, 0.6175f + (0.0434f * i)), + GameUI.convertY(this.uiViewport, 0.095f - (0.044f * j)))); + commandCardIcon.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + commandCardIcon.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + iconFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); + iconFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + iconFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + iconFrame.setTexture(ImageUtils.DEFAULT_ICON_PATH, this.rootFrame); + activeHighlightFrame + .addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); + activeHighlightFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + activeHighlightFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + activeHighlightFrame.setTexture("CommandButtonActiveHighlight", this.rootFrame); + cooldownFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); + this.rootFrame.setSpriteFrameModel(cooldownFrame, this.rootFrame.getSkinField("CommandButtonCooldown")); + cooldownFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + cooldownFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + autocastFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); + this.rootFrame.setSpriteFrameModel(autocastFrame, this.rootFrame.getSkinField("CommandButtonAutocast")); + autocastFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + autocastFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_COMMAND_CARD_ICON_WIDTH)); + commandCardIcon.set(iconFrame, activeHighlightFrame, 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); + } + + @Override + public void startUsingAbility(final int abilityHandleId, final int orderId, final boolean rightClick) { + // TODO not O(N) + if (this.selectedUnit == null) { + return; + } + CAbilityView abilityToUse = null; + for (final CAbility ability : this.selectedUnit.getSimulationUnit().getAbilities()) { + if (ability.getHandleId() == abilityHandleId) { + abilityToUse = ability; + break; + } + } + if (abilityToUse != null) { + final StringMsgAbilityActivationReceiver stringMsgActivationReceiver = StringMsgAbilityActivationReceiver + .getInstance().reset(); + abilityToUse.checkCanUse(this.war3MapViewer.simulation, this.selectedUnit.getSimulationUnit(), orderId, + stringMsgActivationReceiver); + if (!stringMsgActivationReceiver.isUseOk()) { + showCommandError(stringMsgActivationReceiver.getMessage()); + } + else { + final BooleanAbilityTargetCheckReceiver noTargetReceiver = BooleanAbilityTargetCheckReceiver + .getInstance().reset(); + abilityToUse.checkCanTargetNoTarget(this.war3MapViewer.simulation, + this.selectedUnit.getSimulationUnit(), orderId, noTargetReceiver); + if (noTargetReceiver.isTargetable()) { + this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), + abilityHandleId, orderId, isShiftDown()); + } + else { + this.activeCommand = abilityToUse; + this.activeCommandOrderId = orderId; + this.activeCommandUnit = this.selectedUnit; + clearAndRepopulateCommandCard(); + } + } + } + else { + this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), + abilityHandleId, orderId, isShiftDown()); + } + if (rightClick) { + this.war3MapViewer.getUiSounds().getSound("AutoCastButtonClick").play(this.uiScene.audioContext, 0, 0); + } + } + + @Override + public void openMenu(final int orderId) { + if (orderId == 0) { + this.subMenuOrderIdStack.clear(); + this.activeCommandUnit = null; + this.activeCommand = null; + this.activeCommandOrderId = -1; + } + else { + this.subMenuOrderIdStack.add(orderId); + } + clearAndRepopulateCommandCard(); + } + + public void showCommandError(final String message) { + this.errorMessageFrame.setText(message); + } + + public void update(final float deltaTime) { + this.portrait.update(); + + final int baseMouseX = Gdx.input.getX(); + int mouseX = baseMouseX; + final int baseMouseY = Gdx.input.getY(); + int mouseY = baseMouseY; + 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)); + if (Gdx.input.isCursorCatched()) { + 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 (this.activeCommand != null) { + if (this.activeCommand instanceof AbstractCAbilityBuild) { + boolean justLoaded = false; + if (this.cursorModelInstance == null) { + final MutableObjectData unitData = this.war3MapViewer.getAllObjectData().getUnits(); + final War3ID buildingTypeId = new War3ID(this.activeCommandOrderId); + final String unitModelPath = this.war3MapViewer.getUnitModelPath(unitData.get(buildingTypeId)); + final MdxModel model = (MdxModel) this.war3MapViewer.load(unitModelPath, + this.war3MapViewer.mapPathSolver, this.war3MapViewer.solverParams); + this.cursorModelInstance = (MdxComplexInstance) model.addInstance(); + this.cursorModelInstance.setVertexColor(new float[] { 1, 1, 1, 0.5f }); + this.cursorModelInstance.rotate(RenderUnit.tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, + this.war3MapViewer.simulation.getGameplayConstants().getBuildingAngle())); + this.cursorModelInstance.setAnimationSpeed(0f); + justLoaded = true; + final CUnitType buildingUnitType = this.war3MapViewer.simulation.getUnitData() + .getUnitType(buildingTypeId); + this.cursorModelPathing = buildingUnitType.getBuildingPathingPixelMap(); + } + this.war3MapViewer.getClickLocation(clickLocationTemp, baseMouseX, + Gdx.graphics.getHeight() - baseMouseY); + if (this.cursorModelPathing != null) { + clickLocationTemp.x = Math.round(clickLocationTemp.x / 64f) * 64f; + clickLocationTemp.y = Math.round(clickLocationTemp.y / 64f) * 64f; + clickLocationTemp.z = this.war3MapViewer.terrain.getGroundHeight(clickLocationTemp.x, + clickLocationTemp.y); + } + this.cursorModelInstance.setLocation(clickLocationTemp); + SequenceUtils.randomSequence(this.cursorModelInstance, PrimaryTag.STAND); + this.cursorFrame.setVisible(false); + if (justLoaded) { + this.cursorModelInstance.setScene(this.war3MapViewer.worldScene); + } + } + else { + if (this.cursorModelInstance != null) { + this.cursorModelInstance.detach(); + this.cursorModelInstance = null; + this.cursorFrame.setVisible(true); + } + this.cursorFrame.setSequence("Target"); + } + } + else { + if (this.cursorModelInstance != null) { + this.cursorModelInstance.detach(); + this.cursorModelInstance = null; + this.cursorFrame.setVisible(true); + } + 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"); + } + } + if (this.simpleBuildTimeIndicator.isVisible() && (this.selectedUnit != null)) { + this.simpleBuildTimeIndicator + .setValue(Math.min(this.selectedUnit.getSimulationUnit().getConstructionProgress() + / this.selectedUnit.getSimulationUnit().getUnitType().getBuildTime(), 0.99f)); + } + 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); + + } + + this.meleeUIMinimap.render(batch, this.war3MapViewer.units); + this.timeIndicator.setFrameByRatio(this.war3MapViewer.simulation.getGameTimeOfDay() + / this.war3MapViewer.simulation.getGameplayConstants().getGameDayHours()); + } + + public void portraitTalk() { + this.portrait.talk(); + } + + private final class ActiveCommandUnitTargetFilter implements CUnitFilterFunction { + @Override + public boolean call(final CUnit unit) { + final BooleanAbilityTargetCheckReceiver targetReceiver = BooleanAbilityTargetCheckReceiver + .getInstance(); + MeleeUI.this.activeCommand.checkCanTarget(MeleeUI.this.war3MapViewer.simulation, + MeleeUI.this.activeCommandUnit.getSimulationUnit(), MeleeUI.this.activeCommandOrderId, unit, + targetReceiver); + return targetReceiver.isTargetable(); + } + } + + private static final class Portrait { + private MdxComplexInstance modelInstance; + private final PortraitCameraManager portraitCameraManager; + private final Scene portraitScene; + + 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)); + } + + public void update() { + this.portraitCameraManager.updateCamera(); + if ((this.modelInstance != null) + && (this.modelInstance.sequenceEnded || (this.modelInstance.sequence == -1))) { + SequenceUtils.randomSequence(this.modelInstance, PrimaryTag.PORTRAIT, SequenceUtils.EMPTY, true); + } + } + + public void talk() { + SequenceUtils.randomSequence(this.modelInstance, PrimaryTag.PORTRAIT, SequenceUtils.TALK, true); + } + + public void setSelectedUnit(final RenderUnit unit) { + if (unit == null) { + if (this.modelInstance != null) { + this.portraitScene.removeInstance(this.modelInstance); + } + this.modelInstance = null; + this.portraitCameraManager.setModelInstance(null, null); + } + else { + final MdxModel portraitModel = unit.portraitModel; + if (portraitModel != null) { + if (this.modelInstance != null) { + this.portraitScene.removeInstance(this.modelInstance); + } + this.modelInstance = (MdxComplexInstance) portraitModel.addInstance(); + this.portraitCameraManager.setModelInstance(this.modelInstance, portraitModel); + this.modelInstance.setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP); + this.modelInstance.setScene(this.portraitScene); + this.modelInstance.setVertexColor(unit.instance.vertexColor); + this.modelInstance.setTeamColor(unit.playerIndex); + } + } + } + } + + public void selectUnit(RenderUnit unit) { + this.subMenuOrderIdStack.clear(); + if ((unit != null) && unit.getSimulationUnit().isDead()) { + unit = null; + } + if (this.selectedUnit != null) { + this.selectedUnit.getSimulationUnit().removeStateListener(this); + } + this.portrait.setSelectedUnit(unit); + this.selectedUnit = unit; + if (unit == null) { + clearCommandCard(); + this.simpleNameValue.setText(""); + this.unitLifeText.setText(""); + this.unitManaText.setText(""); + this.simpleClassValue.setText(""); + this.simpleBuildingActionLabel.setText(""); + this.attack1Icon.setVisible(false); + this.attack2Icon.setVisible(false); + this.attack1InfoPanelIconLevel.setText(""); + this.attack2InfoPanelIconLevel.setText(""); + this.simpleBuildingActionLabel.setText(""); + this.armorIcon.setVisible(false); + this.armorInfoPanelIconLevel.setText(""); + this.simpleBuildTimeIndicator.setVisible(false); + } + else { + unit.getSimulationUnit().addStateListener(this); + reloadSelectedUnitUI(unit); + } + } + + private void reloadSelectedUnitUI(final RenderUnit unit) { + this.simpleNameValue.setText(unit.getSimulationUnit().getUnitType().getName()); + String classText = null; + for (final CUnitClassification classification : unit.getSimulationUnit().getClassifications()) { + if ((classification == CUnitClassification.MECHANICAL) + && unit.getSimulationUnit().getUnitType().isBuilding()) { + // buildings dont display MECHANICAL + continue; + } + if (classification.getDisplayName() != null) { + classText = classification.getDisplayName(); + } + } + if (classText != null) { + this.simpleClassValue.setText(classText); + } + else { + this.simpleClassValue.setText(""); + } + this.unitLifeText.setText(FastNumberFormat.formatWholeNumber(unit.getSimulationUnit().getLife()) + " / " + + unit.getSimulationUnit().getMaximumLife()); + final int maximumMana = unit.getSimulationUnit().getMaximumMana(); + if (maximumMana > 0) { + this.unitManaText.setText( + FastNumberFormat.formatWholeNumber(unit.getSimulationUnit().getMana()) + " / " + maximumMana); + } + else { + this.unitManaText.setText(""); + } + this.simpleBuildingActionLabel.setText(""); + + final boolean anyAttacks = unit.getSimulationUnit().getUnitType().getAttacks().size() > 0; + final boolean constructing = unit.getSimulationUnit().isConstructing(); + final UIFrame localArmorIcon = this.armorIcon; + final TextureFrame localArmorIconBackdrop = this.armorIconBackdrop; + final StringFrame localArmorInfoPanelIconValue = this.armorInfoPanelIconValue; + if (anyAttacks && !constructing) { + final CUnitAttack attackOne = unit.getSimulationUnit().getUnitType().getAttacks().get(0); + this.attack1Icon.setVisible(attackOne.isShowUI()); + this.attack1IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackOne.getAttackType())); + this.attack1InfoPanelIconValue.setText(attackOne.getMinDamage() + " - " + attackOne.getMaxDamage()); + if (unit.getSimulationUnit().getUnitType().getAttacks().size() > 1) { + final CUnitAttack attackTwo = unit.getSimulationUnit().getUnitType().getAttacks().get(1); + this.attack2Icon.setVisible(attackTwo.isShowUI()); + this.attack2IconBackdrop.setTexture(this.damageBackdrops.getTexture(attackTwo.getAttackType())); + this.attack2InfoPanelIconValue.setText(attackTwo.getMinDamage() + " - " + attackTwo.getMaxDamage()); + } + else { + this.attack2Icon.setVisible(false); + } + + 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.attack1Icon.setVisible(false); + this.attack2Icon.setVisible(false); + + 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(!constructing); + this.simpleBuildTimeIndicator.setVisible(constructing); + if (constructing) { + this.simpleBuildingActionLabel.setText(this.rootFrame.getTemplates().getDecoratedString("CONSTRUCTING")); + } + final Texture defenseTexture = this.defenseBackdrops + .getTexture(unit.getSimulationUnit().getUnitType().getDefenseType()); + if (defenseTexture == null) { + throw new RuntimeException( + unit.getSimulationUnit().getUnitType().getDefenseType() + " can't find texture!"); + } + localArmorIconBackdrop.setTexture(defenseTexture); + localArmorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense())); + clearAndRepopulateCommandCard(); + } + + private void clearCommandCard() { + for (int j = 0; j < COMMAND_CARD_HEIGHT; j++) { + for (int i = 0; i < COMMAND_CARD_WIDTH; i++) { + this.commandCard[j][i].setCommandButton(null); + } + } + } + + @Override + public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon, + final int abilityHandleId, final int orderId, final int autoCastId, final boolean active, + final boolean autoCastActive, final boolean menuButton) { + 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, abilityHandleId, orderId, autoCastId, active, autoCastActive, + menuButton); + } + + public void resize(final Rectangle viewport) { + this.cameraManager.resize(viewport); + positionPortrait(); + } + + public void positionPortrait() { + this.projectionTemp1.x = 422 * this.widthRatioCorrection; + this.projectionTemp1.y = 57 * this.heightRatioCorrection; + this.projectionTemp2.x = (422 + 167) * this.widthRatioCorrection; + this.projectionTemp2.y = (57 + 170) * this.heightRatioCorrection; + this.uiViewport.project(this.projectionTemp1); + this.uiViewport.project(this.projectionTemp2); + + this.tempRect.x = this.projectionTemp1.x + this.uiViewport.getScreenX(); + this.tempRect.y = this.projectionTemp1.y + this.uiViewport.getScreenY(); + this.tempRect.width = this.projectionTemp2.x - this.projectionTemp1.x; + this.tempRect.height = this.projectionTemp2.y - this.projectionTemp1.y; + this.portrait.portraitScene.camera.viewport(this.tempRect); + } + + private static final class InfoPanelIconBackdrops { + private final Texture[] damageBackdropTextures; + + public InfoPanelIconBackdrops(final CodeKeyType[] attackTypes, final GameUI gameUI, final String prefix, + final String suffix) { + this.damageBackdropTextures = new Texture[attackTypes.length]; + for (int index = 0; index < attackTypes.length; index++) { + final CodeKeyType attackType = attackTypes[index]; + String skinLookupKey = "InfoPanelIcon" + prefix + attackType.getCodeKey() + suffix; + final Texture suffixTexture = gameUI.loadTexture(gameUI.getSkinField(skinLookupKey)); + if (suffixTexture != null) { + this.damageBackdropTextures[index] = suffixTexture; + } + else { + skinLookupKey = "InfoPanelIcon" + prefix + attackType.getCodeKey(); + this.damageBackdropTextures[index] = gameUI.loadTexture(gameUI.getSkinField(skinLookupKey)); + } + } + } + + public Texture getTexture(final CodeKeyType attackType) { + if (attackType != null) { + final int ordinal = attackType.ordinal(); + if ((ordinal >= 0) && (ordinal < this.damageBackdropTextures.length)) { + return this.damageBackdropTextures[ordinal]; + } + } + return this.damageBackdropTextures[0]; + } + + private static String getSuffix(final CAttackType attackType) { + switch (attackType) { + case CHAOS: + return "Chaos"; + case HERO: + return "Hero"; + case MAGIC: + return "Magic"; + case NORMAL: + return "Normal"; + case PIERCE: + return "Pierce"; + case SIEGE: + return "Siege"; + case SPELLS: + return "Magic"; + case UNKNOWN: + return "Unknown"; + default: + throw new IllegalArgumentException("Unknown attack type: " + attackType); + } + + } + } + + @Override + public void lifeChanged() { + if (this.selectedUnit.getSimulationUnit().isDead()) { + selectUnit(null); + } + else { + this.unitLifeText + .setText(FastNumberFormat.formatWholeNumber(this.selectedUnit.getSimulationUnit().getLife()) + " / " + + this.selectedUnit.getSimulationUnit().getMaximumLife()); + } + } + + @Override + public void ordersChanged(final int abilityHandleId, final int orderId) { + reloadSelectedUnitUI(this.selectedUnit); + } + + private void clearAndRepopulateCommandCard() { + clearCommandCard(); + final AbilityDataUI abilityDataUI = this.war3MapViewer.getAbilityDataUI(); + final int menuOrderId = getSubMenuOrderId(); + if (this.activeCommand != null) { + final IconUI cancelUI = abilityDataUI.getCancelUI(); + this.commandButton(cancelUI.getButtonPositionX(), cancelUI.getButtonPositionY(), cancelUI.getIcon(), 0, + menuOrderId, 0, false, false, true); + } + else if (this.selectedUnit.getSimulationUnit().isConstructing()) { + + } + else { + if (menuOrderId != 0) { + final int exitOrderId = this.subMenuOrderIdStack.size() > 1 + ? this.subMenuOrderIdStack.get(this.subMenuOrderIdStack.size() - 2) + : 0; + final IconUI cancelUI = abilityDataUI.getCancelUI(); + this.commandButton(cancelUI.getButtonPositionX(), cancelUI.getButtonPositionY(), cancelUI.getIcon(), 0, + exitOrderId, 0, false, false, true); + } + this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this, abilityDataUI, menuOrderId); + } + } + + private int getSubMenuOrderId() { + return this.subMenuOrderIdStack.isEmpty() ? 0 + : this.subMenuOrderIdStack.get(this.subMenuOrderIdStack.size() - 1); + } + + 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 int screenX, final int screenY, final float worldScreenY, final int button) { + screenCoordsVector.set(screenX, screenY); + this.uiViewport.unproject(screenCoordsVector); + if (this.meleeUIMinimap.containsMouse(screenCoordsVector.x, screenCoordsVector.y)) { + final Vector2 worldPoint = this.meleeUIMinimap.getWorldPointFromScreen(screenCoordsVector.x, + screenCoordsVector.y); + this.cameraManager.target.x = worldPoint.x; + this.cameraManager.target.y = worldPoint.y; + return true; + } + final UIFrame clickedUIFrame = this.rootFrame.touchDown(screenCoordsVector.x, screenCoordsVector.y, button); + if (clickedUIFrame == null) { + // try to interact with world + if (this.activeCommand != null) { + if (button == Input.Buttons.RIGHT) { + this.activeCommandUnit = null; + this.activeCommand = null; + this.activeCommandOrderId = -1; + clearAndRepopulateCommandCard(); + } + else { + final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY, + this.activeCommandUnitTargetFilter); + final boolean shiftDown = isShiftDown(); + if (rayPickUnit != null) { + this.unitOrderListener.issueTargetOrder( + this.activeCommandUnit.getSimulationUnit().getHandleId(), + this.activeCommand.getHandleId(), this.activeCommandOrderId, + rayPickUnit.getSimulationUnit().getHandleId(), shiftDown); + if (getSelectedUnit().soundset.yesAttack + .playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { + portraitTalk(); + } + this.selectedSoundCount = 0; + if (!shiftDown) { + this.subMenuOrderIdStack.clear(); + this.activeCommandUnit = null; + this.activeCommand = null; + this.activeCommandOrderId = -1; + clearAndRepopulateCommandCard(); + } + } + else { + this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY); + clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y); + + this.activeCommand.checkCanTarget(this.war3MapViewer.simulation, + this.activeCommandUnit.getSimulationUnit(), this.activeCommandOrderId, + clickLocationTemp2, PointAbilityTargetCheckReceiver.INSTANCE); + final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget(); + if (target != null) { + if (this.activeCommand instanceof CAbilityAttack) { + this.war3MapViewer.showConfirmation(clickLocationTemp, 1, 0, 0); + } + else { + this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); + } + this.unitOrderListener.issuePointOrder( + this.activeCommandUnit.getSimulationUnit().getHandleId(), + this.activeCommand.getHandleId(), this.activeCommandOrderId, clickLocationTemp2.x, + clickLocationTemp2.y, shiftDown); + if (getSelectedUnit().soundset.yes + .playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { + portraitTalk(); + } + this.selectedSoundCount = 0; + if (this.activeCommand instanceof AbstractCAbilityBuild) { + this.war3MapViewer.getUiSounds().getSound("PlaceBuildingDefault") + .play(this.uiScene.audioContext, 0, 0); + } + if (!shiftDown) { + this.subMenuOrderIdStack.clear(); + this.activeCommandUnit = null; + this.activeCommand = null; + this.activeCommandOrderId = -1; + clearAndRepopulateCommandCard(); + } + + } + } + } + } + else { + if (button == Input.Buttons.RIGHT) { + if (getSelectedUnit() != null) { + final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY); + if ((rayPickUnit != null) && !rayPickUnit.getSimulationUnit().isDead()) { + boolean ordered = false; + for (final RenderUnit unit : this.war3MapViewer.selected) { + for (final CAbility ability : unit.getSimulationUnit().getAbilities()) { + ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(), + OrderIds.smart, rayPickUnit.getSimulationUnit(), + CWidgetAbilityTargetCheckReceiver.INSTANCE); + final CWidget targetWidget = CWidgetAbilityTargetCheckReceiver.INSTANCE.getTarget(); + if (targetWidget != null) { + this.unitOrderListener.issueTargetOrder(unit.getSimulationUnit().getHandleId(), + ability.getHandleId(), OrderIds.smart, targetWidget.getHandleId(), + isShiftDown()); + ordered = true; + } + } + + } + if (ordered) { + if (getSelectedUnit().soundset.yesAttack.playUnitResponse( + this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { + portraitTalk(); + } + this.selectedSoundCount = 0; + } + } + else { + this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY); + this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); + clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y); + + boolean ordered = false; + for (final RenderUnit unit : this.war3MapViewer.selected) { + for (final CAbility ability : unit.getSimulationUnit().getAbilities()) { + ability.checkCanUse(this.war3MapViewer.simulation, unit.getSimulationUnit(), + OrderIds.smart, BooleanAbilityActivationReceiver.INSTANCE); + if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) { + ability.checkCanTarget(this.war3MapViewer.simulation, unit.getSimulationUnit(), + OrderIds.smart, clickLocationTemp2, + PointAbilityTargetCheckReceiver.INSTANCE); + final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget(); + if (target != null) { + this.unitOrderListener.issuePointOrder( + unit.getSimulationUnit().getHandleId(), ability.getHandleId(), + OrderIds.smart, clickLocationTemp2.x, clickLocationTemp2.y, + isShiftDown()); + ordered = true; + } + } + } + + } + + if (ordered) { + if (getSelectedUnit().soundset.yes.playUnitResponse( + this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { + portraitTalk(); + } + this.selectedSoundCount = 0; + } + } + } + } + else { + final List selectedUnits = this.war3MapViewer.selectUnit(screenX, worldScreenY, false); + if (!selectedUnits.isEmpty()) { + final RenderUnit unit = selectedUnits.get(0); + final boolean selectionChanged = getSelectedUnit() != unit; + boolean playedNewSound = false; + if (selectionChanged) { + this.selectedSoundCount = 0; + } + if (unit.soundset != null) { + UnitSound ackSoundToPlay = unit.soundset.what; + int soundIndex; + final int pissedSoundCount = unit.soundset.pissed.getSoundCount(); + if (unit.getSimulationUnit().isConstructing()) { + ackSoundToPlay = this.war3MapViewer.getUiSounds() + .getSound(this.rootFrame.getSkinField("ConstructingBuilding")); + soundIndex = (int) (Math.random() * ackSoundToPlay.getSoundCount()); + } + else { + if ((this.selectedSoundCount >= 3) && (pissedSoundCount > 0)) { + soundIndex = this.selectedSoundCount - 3; + ackSoundToPlay = unit.soundset.pissed; + } + else { + soundIndex = (int) (Math.random() * ackSoundToPlay.getSoundCount()); + } + } + if ((ackSoundToPlay != null) && ackSoundToPlay + .playUnitResponse(this.war3MapViewer.worldScene.audioContext, unit, soundIndex)) { + this.selectedSoundCount++; + if ((this.selectedSoundCount - 3) >= pissedSoundCount) { + this.selectedSoundCount = 0; + } + playedNewSound = true; + } + } + if (selectionChanged) { + selectUnit(unit); + } + if (playedNewSound) { + portraitTalk(); + } + } + else { + selectUnit(null); + } + } + } + } + else { + if (clickedUIFrame instanceof CommandCardIcon) { + this.mouseDownUIFrame = (CommandCardIcon) clickedUIFrame; + this.mouseDownUIFrame.mouseDown(this.uiViewport); + } + } + return false; + } + + public boolean touchUp(final int screenX, final int screenY, final float worldScreenY, final int button) { + screenCoordsVector.set(screenX, screenY); + this.uiViewport.unproject(screenCoordsVector); + final UIFrame clickedUIFrame = this.rootFrame.touchUp(screenCoordsVector.x, screenCoordsVector.y, button); + if (this.mouseDownUIFrame != null) { + if (clickedUIFrame == this.mouseDownUIFrame) { + this.mouseDownUIFrame.onClick(button); + this.war3MapViewer.getUiSounds().getSound("InterfaceClick").play(this.uiScene.audioContext, 0, 0); + } + this.mouseDownUIFrame.mouseUp(this.uiViewport); + } + this.mouseDownUIFrame = null; + return false; + } + + private static boolean isShiftDown() { + return Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT); + } + + public boolean touchDragged(final int screenX, final int screenY, final float worldScreenY, final int pointer) { + screenCoordsVector.set(screenX, screenY); + this.uiViewport.unproject(screenCoordsVector); + + if (this.meleeUIMinimap.containsMouse(screenCoordsVector.x, screenCoordsVector.y)) { + final Vector2 worldPoint = this.meleeUIMinimap.getWorldPointFromScreen(screenCoordsVector.x, + screenCoordsVector.y); + this.cameraManager.target.x = worldPoint.x; + this.cameraManager.target.y = worldPoint.y; + } + return false; + } + + public float getHeightRatioCorrection() { + return this.heightRatioCorrection; + } }