diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java index e8167dc..ba042ba 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/SpriteFrame.java @@ -127,4 +127,11 @@ public class SpriteFrame extends AbstractRenderableFrame { return this.instance.sequenceEnded; } + public void setReplaceableId(final int replaceableId, final String blpPath) { + if (this.instance != null) { + this.instance.setReplaceableTexture(replaceableId, blpPath); + } + + } + } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java index eef0852..24cef0f 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/StringFrame.java @@ -48,18 +48,30 @@ public class StringFrame extends AbstractRenderableFrame { public void setColor(final Color color) { this.color = color; + for (final SingleStringFrame internalFrame : this.internalFrames) { + internalFrame.setColor(color); + } } public void setFontShadowColor(final Color fontShadowColor) { this.fontShadowColor = fontShadowColor; + for (final SingleStringFrame internalFrame : this.internalFrames) { + internalFrame.setFontShadowColor(fontShadowColor); + } } public void setFontShadowOffsetX(final float fontShadowOffsetX) { this.fontShadowOffsetX = fontShadowOffsetX; + for (final SingleStringFrame internalFrame : this.internalFrames) { + internalFrame.setFontShadowOffsetX(fontShadowOffsetX); + } } public void setFontShadowOffsetY(final float fontShadowOffsetY) { this.fontShadowOffsetY = fontShadowOffsetY; + for (final SingleStringFrame internalFrame : this.internalFrames) { + internalFrame.setFontShadowOffsetY(fontShadowOffsetY); + } } @Override @@ -414,6 +426,9 @@ public class StringFrame extends AbstractRenderableFrame { public void setAlpha(final float alpha) { this.alpha = alpha; + for (final SingleStringFrame internalFrame : this.internalFrames) { + internalFrame.setAlpha(alpha); + } } diff --git a/core/src/com/etheller/warsmash/util/WarsmashConstants.java b/core/src/com/etheller/warsmash/util/WarsmashConstants.java index 694d5b2..6b2e4ab 100644 --- a/core/src/com/etheller/warsmash/util/WarsmashConstants.java +++ b/core/src/com/etheller/warsmash/util/WarsmashConstants.java @@ -6,7 +6,7 @@ public class WarsmashConstants { * With version, we use 0 for RoC, 1 for TFT emulation, and probably 2+ or * whatever for custom mods and other stuff */ - public static int GAME_VERSION = 0; + public static int GAME_VERSION = 1; public static final int REPLACEABLE_TEXTURE_LIMIT = 64; public static final float SIMULATION_STEP_TIME = 1 / 20f; public static final int PORT_NUMBER = 6115; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java index b4ed813..7fed7a8 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/EmitterGroup.java @@ -39,7 +39,7 @@ public class EmitterGroup extends GenericGroup { viewer.webGL.useShaderProgram(shader); shader.setUniformMatrix("u_mvp", mvp); - shader.setUniformf("u_texture", 0); + shader.setUniformi("u_texture", 0); final int a_position = shader.getAttributeLocation("a_position"); instancedArrays.glVertexAttribDivisorANGLE(a_position, 0); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java index 1cf61cd..378e5c7 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/GeometryEmitterFuncs.java @@ -433,7 +433,7 @@ public class GeometryEmitterFuncs { buffer.bindAndUpdate(size); - shader.setUniformi("u_emitter", emitterType); + shader.setUniformf("u_emitter", emitterType); shader.setVertexAttribute("a_p0", 3, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_P0); shader.setVertexAttribute("a_p1", 3, GL20.GL_FLOAT, false, BYTES_PER_OBJECT, BYTE_OFFSET_P1); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java index 61b203e..67ba901 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java @@ -6,6 +6,8 @@ import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Quaternion; import com.badlogic.gdx.math.Vector3; @@ -510,6 +512,11 @@ public class MdxComplexInstance extends ModelInstance { for (final GenericGroup group : model.opaqueGroups) { group.render(this, mvp); } + + final int glGetError = Gdx.gl.glGetError(); + if (glGetError != GL20.GL_NO_ERROR) { + throw new IllegalStateException("GL ERROR: " + glGetError + " ON " + model.name + " (Opaque)"); + } } @Override @@ -521,6 +528,11 @@ public class MdxComplexInstance extends ModelInstance { for (final GenericGroup group : model.translucentGroups) { group.render(this, this.scene.camera.viewProjectionMatrix); + + final int glGetError = Gdx.gl.glGetError(); + if (glGetError != GL20.GL_NO_ERROR) { + throw new IllegalStateException("GL ERROR: " + glGetError + " ON " + model.name + " (Translucent)"); + } } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/TextTag.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/TextTag.java index af3bb82..921995b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/TextTag.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/TextTag.java @@ -20,7 +20,7 @@ public class TextTag { public boolean update(final float deltaTime) { this.screenCoordsZHeight += 60.0f * deltaTime; this.lifetime += deltaTime; - return this.lifetime > 3.5f; + return this.lifetime > 2.5f; } public Vector3 getPosition() { @@ -28,7 +28,7 @@ public class TextTag { } public float getRemainingLife() { - return 3.5f - this.lifetime; + return 2.5f - this.lifetime; } public Color getColor() { 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 38324f2..e5f2409 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -24,6 +24,7 @@ import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; @@ -91,6 +92,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityUI; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; @@ -130,6 +132,8 @@ public class War3MapViewer extends AbstractMdxModelViewer { private static final War3ID DESTRUCTABLE_PATHING_DEATH = War3ID.fromString("bptd"); private static final War3ID ELEVATION_SAMPLE_RADIUS = War3ID.fromString("uerd"); private static final War3ID MAX_PITCH = War3ID.fromString("umxp"); + private static final War3ID ALLOW_CUSTOM_TEAM_COLOR = War3ID.fromString("utcc"); + private static final War3ID TEAM_COLOR = War3ID.fromString("utco"); private static final War3ID MAX_ROLL = War3ID.fromString("umxr"); private static final War3ID sloc = War3ID.fromString("sloc"); private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation(); @@ -160,7 +164,6 @@ public class War3MapViewer extends AbstractMdxModelViewer { public MappedData unitMetaData = new MappedData(); public List widgets = new ArrayList<>(); public List units = new ArrayList<>(); - public List items = new ArrayList<>(); public List projectiles = new ArrayList<>(); public boolean unitsReady; public War3Map mapMpq; @@ -199,6 +202,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { private final Map unitToRenderPeer = new HashMap<>(); private final Map destructableToRenderPeer = new HashMap<>(); + private final Map itemToRenderPeer = new HashMap<>(); private final Map unitIdToTypeData = new HashMap<>(); private GameUI gameUI; private Vector3 lightDirection; @@ -451,7 +455,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { this.confirmationInstance.setScene(this.worldScene); this.allObjectData = this.mapMpq.readModifications(); - this.simulation = new CSimulation(this.miscData, this.allObjectData.getUnits(), + this.simulation = new CSimulation(this.miscData, this.allObjectData.getUnits(), this.allObjectData.getItems(), this.allObjectData.getDestructibles(), this.allObjectData.getAbilities(), new SimulationRenderController() { private final Map keyToCombatSound = new HashMap<>(); @@ -541,10 +545,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { @Override public void spawnDamageSound(final CWidget damagedDestructable, final String weaponSound, final String armorType) { - RenderWidget damagedWidget = War3MapViewer.this.unitToRenderPeer.get(damagedDestructable); - if (damagedWidget == null) { - damagedWidget = War3MapViewer.this.destructableToRenderPeer.get(damagedDestructable); - } + final RenderWidget damagedWidget = getRenderPeer(damagedDestructable); if (damagedWidget == null) { return; } @@ -621,7 +622,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { 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)); + playerIndex, (float) Math.toRadians(facing)); } @Override @@ -724,6 +725,26 @@ public class War3MapViewer extends AbstractMdxModelViewer { break; } } + + @Override + public void spawnUIUnitGetItemSound(final CUnit cUnit, final CItem item) { + final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(cUnit); + if (localPlayerIndex == renderPeer.getSimulationUnit().getPlayerIndex()) { + War3MapViewer.this.uiSounds.getSound("ItemGet").play( + War3MapViewer.this.worldScene.audioContext, renderPeer.getX(), renderPeer.getY(), + renderPeer.getZ()); + } + } + + @Override + public void spawnUIUnitDropItemSound(final CUnit cUnit, final CItem item) { + final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(cUnit); + if (localPlayerIndex == renderPeer.getSimulationUnit().getPlayerIndex()) { + War3MapViewer.this.uiSounds.getSound("ItemDrop").play( + War3MapViewer.this.worldScene.audioContext, renderPeer.getX(), renderPeer.getY(), + renderPeer.getZ()); + } + } }, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers(), this.commandErrorListener); @@ -983,10 +1004,11 @@ public class War3MapViewer extends AbstractMdxModelViewer { final float unitY = unit.getLocation()[1]; final float unitZ = unit.getLocation()[2]; final int playerIndex = unit.getPlayer(); + final int customTeamColor = unit.getCustomTeamColor(); final float unitAngle = unit.getAngle(); final CUnit unitCreated = createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, - unitAngle); + customTeamColor, unitAngle); if (unit.getGoldAmount() != 0) { unitCreated.setGold(unit.getGoldAmount()); } @@ -1001,7 +1023,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { } private CUnit createNewUnit(final Warcraft3MapObjectData modifications, final War3ID unitId, float unitX, - float unitY, final float unitZ, final int playerIndex, final float unitAngle) { + float unitY, final float unitZ, final int playerIndex, int customTeamColor, final float unitAngle) { UnitSoundset soundset = null; MutableGameObject row = null; String path = null; @@ -1174,7 +1196,15 @@ public class War3MapViewer extends AbstractMdxModelViewer { final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), playerIndex, unitX, unitY, angle, buildingPathingPixelMap, pathingInstance); final RenderUnitTypeData typeData = getUnitTypeData(unitId, row); - final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, playerIndex, + if (!typeData.isAllowCustomTeamColor() || (customTeamColor == -1)) { + if (typeData.getTeamColor() != -1) { + customTeamColor = typeData.getTeamColor(); + } + else { + customTeamColor = playerIndex; + } + } + final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, customTeamColor, soundset, portraitModel, simulationUnit, typeData, specialArtModel, buildingShadowInstance, this.selectionCircleScaleFactor); this.unitToRenderPeer.put(simulationUnit, renderUnit); @@ -1205,19 +1235,22 @@ public class War3MapViewer extends AbstractMdxModelViewer { return simulationUnit; } else { - this.items - .add(new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset, portraitModel)); // TODO - // store - // somewhere + final CItem simulationItem = this.simulation.createItem(row.getAlias(), unitX, unitY); + final RenderItem renderItem = new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset, + portraitModel, simulationItem); + this.widgets.add(renderItem); + this.itemToRenderPeer.put(simulationItem, renderItem); + if (unitShadowSplat != null) { unitShadowSplat.unitMapping.add(new Consumer() { @Override public void accept(final SplatMover t) { - + renderItem.shadow = t; } }); } if (unitShadowSplatDynamicIngame != null) { + renderItem.shadow = unitShadowSplatDynamicIngame; } } } @@ -1278,7 +1311,8 @@ public class War3MapViewer extends AbstractMdxModelViewer { 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)); + row.getFieldAsFloat(ELEVATION_SAMPLE_RADIUS, 0), row.getFieldAsBoolean(ALLOW_CUSTOM_TEAM_COLOR, 0), + row.getFieldAsInteger(TEAM_COLOR, 0)); this.unitIdToTypeData.put(key, unitTypeData); } return unitTypeData; @@ -1287,13 +1321,14 @@ public class War3MapViewer extends AbstractMdxModelViewer { @Override public void update() { if (this.anyReady) { - this.terrain.update(); + final float deltaTime = Gdx.graphics.getDeltaTime(); + this.terrain.update(deltaTime); super.update(); final Iterator textTagIterator = this.textTags.iterator(); while (textTagIterator.hasNext()) { - if (textTagIterator.next().update(Gdx.graphics.getDeltaTime())) { + if (textTagIterator.next().update(deltaTime)) { textTagIterator.remove(); } } @@ -1307,13 +1342,6 @@ public class War3MapViewer extends AbstractMdxModelViewer { 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) { @@ -1373,6 +1401,11 @@ public class War3MapViewer extends AbstractMdxModelViewer { scene.renderTranslucent(); } } + + final int glGetError = Gdx.gl.glGetError(); + if (glGetError != GL20.GL_NO_ERROR) { + throw new IllegalStateException("GL ERROR: " + glGetError); + } } } @@ -1492,11 +1525,11 @@ public class War3MapViewer extends AbstractMdxModelViewer { else { sel = Arrays.asList(entity); } + this.doSelectUnit(sel); } else { sel = Collections.emptyList(); } - this.doSelectUnit(sel); return sel; } @@ -1659,6 +1692,17 @@ public class War3MapViewer extends AbstractMdxModelViewer { return mdxPath; } + public String blp(String iconPath) { + final int lastDotIndex = iconPath.lastIndexOf('.'); + if (lastDotIndex != -1) { + iconPath = iconPath.substring(0, lastDotIndex); + } + if (!iconPath.toLowerCase().endsWith(".blp")) { + iconPath += ".blp"; + } + return iconPath; + } + public MdxModel loadModel(final String path) { return (MdxModel) load(mdx(path), PathSolver.DEFAULT, null); } @@ -1681,7 +1725,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { public void setGameUI(final GameUI gameUI) { this.gameUI = gameUI; this.abilityDataUI = new AbilityDataUI(this.allObjectData.getAbilities(), this.allObjectData.getUnits(), - this.allObjectData.getUpgrades(), gameUI, this); + this.allObjectData.getItems(), this.allObjectData.getUpgrades(), gameUI, this); } public GameUI getGameUI() { @@ -1722,6 +1766,21 @@ public class War3MapViewer extends AbstractMdxModelViewer { return this.destructableToRenderPeer.get(dest); } + public RenderItem getRenderPeer(final CItem item) { + return this.itemToRenderPeer.get(item); + } + + private RenderWidget getRenderPeer(final CWidget damagedDestructable) { + RenderWidget damagedWidget = War3MapViewer.this.unitToRenderPeer.get(damagedDestructable); + if (damagedWidget == null) { + damagedWidget = War3MapViewer.this.destructableToRenderPeer.get(damagedDestructable); + } + if (damagedWidget == null) { + damagedWidget = War3MapViewer.this.itemToRenderPeer.get(damagedDestructable); + } + return damagedWidget; + } + private static final class QuadtreeIntersectorFindsWalkableRenderHeight implements QuadtreeIntersector { private float z; @@ -1793,4 +1852,5 @@ public class War3MapViewer extends AbstractMdxModelViewer { public void add(final TextTag textTag) { this.textTags.add(textTag); } + } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/PathingGrid.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/PathingGrid.java index 10eac37..7f1b4ba 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/PathingGrid.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/PathingGrid.java @@ -83,7 +83,7 @@ public class PathingGrid { data |= PathingFlags.UNFLYABLE; } if (((rgb & 0xFF0000) >>> 16) > 127) { - data |= PathingFlags.UNWALKABLE; + data |= PathingFlags.UNWALKABLE | PathingFlags.UNSWIMABLE; } this.dynamicPathingOverlay[(yy * this.pathingGridSizes[0]) + xx] |= data; } @@ -188,8 +188,7 @@ public class PathingGrid { public RemovablePathingMapInstance createRemovablePathingOverlayTexture(final float positionX, final float positionY, final int rotationInput, final BufferedImage pathingTextureTga) { - return new RemovablePathingMapInstance(positionX, - positionY, rotationInput, pathingTextureTga); + return new RemovablePathingMapInstance(positionX, positionY, rotationInput, pathingTextureTga); } public int getWidth() { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java index fe868ce..3cdb5a7 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java @@ -188,7 +188,7 @@ public class Terrain { if (waterInfo != null) { this.waterHeightOffset = waterInfo.getFieldFloatValue("height"); this.waterTextureCount = waterInfo.getFieldValue("numTex"); - this.waterIncreasePerFrame = waterInfo.getFieldValue("texRate") / 60f; + this.waterIncreasePerFrame = waterInfo.getFieldValue("texRate"); } else { this.waterHeightOffset = 0; @@ -896,8 +896,8 @@ public class Terrain { } } - public void update() { - this.waterIndex += this.waterIncreasePerFrame; + public void update(final float deltaTime) { + this.waterIndex += this.waterIncreasePerFrame * deltaTime; if (this.waterIndex >= this.waterTextureCount) { this.waterIndex = 0; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java index ac7169c..2dc82bd 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderItem.java @@ -6,25 +6,35 @@ 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.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.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; -public class RenderItem { +public class RenderItem implements RenderWidget { private static final War3ID ITEM_MODEL_SCALE = War3ID.fromString("isca"); private static final War3ID ITEM_RED = War3ID.fromString("iclr"); private static final War3ID ITEM_GREEN = War3ID.fromString("iclg"); private static final War3ID ITEM_BLUE = War3ID.fromString("iclb"); + private final CItem simulationItem; public final MdxComplexInstance instance; public final MutableGameObject row; public final float[] location = new float[3]; public float radius; public UnitSoundset soundset; public final MdxModel portraitModel; + public SplatMover shadow; + public SplatMover selectionCircle; + private boolean hidden; + private boolean dead; public RenderItem(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final float x, - final float y, final float z, final float angle, final UnitSoundset soundset, - final MdxModel portraitModel) { + final float y, final float z, final float angle, final UnitSoundset soundset, final MdxModel portraitModel, + final CItem simulationItem) { this.portraitModel = portraitModel; + this.simulationItem = simulationItem; final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance(); this.location[0] = x; @@ -55,4 +65,115 @@ public class RenderItem { this.row = row; this.soundset = soundset; } + + @Override + public MdxComplexInstance getInstance() { + return this.instance; + } + + @Override + public CWidget getSimulationWidget() { + return this.simulationItem; + } + + @Override + public void updateAnimations(final War3MapViewer map) { + final boolean hidden = this.simulationItem.isHidden(); + if (hidden != this.hidden) { + this.hidden = hidden; + if (hidden) { + this.instance.hide(); + if (this.shadow != null) { + this.shadow.hide(); + } + } + else { + this.instance.show(); + if (this.shadow != null) { + this.shadow.show(map.terrain.centerOffset); + } + } + } + final boolean dead = this.simulationItem.isDead(); + final MdxComplexInstance mdxComplexInstance = this.instance; + if (dead) { + if (!this.dead) { + this.dead = dead; + SequenceUtils.randomDeathSequence(mdxComplexInstance); + } + } + else if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1)) { + SequenceUtils.randomStandSequence(mdxComplexInstance); + } + + final float prevX = this.location[0]; + final float prevY = this.location[1]; + final float simulationX = this.simulationItem.getX(); + final float simulationY = this.simulationItem.getY(); + final float simDx = simulationX - this.location[0]; + final float simDy = simulationY - this.location[1]; + this.location[0] = simulationX; + this.location[1] = simulationY; + final float dx = this.location[0] - prevX; + final float dy = this.location[1] - prevY; + final float groundHeight; + // land units will have their feet pass under the surface of the water, so items + // here are in the same place + final float groundHeightTerrainAndWater = map.terrain.getGroundHeight(this.location[0], this.location[1]); + MdxComplexInstance currentWalkableUnder; + currentWalkableUnder = map.getHighestWalkableUnder(this.location[0], this.location[1]); + War3MapViewer.gdxRayHeap.set(this.location[0], this.location[1], 4096, 0, 0, -8192); + if ((currentWalkableUnder != null) + && currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap, + War3MapViewer.intersectionHeap, true, true) + && (War3MapViewer.intersectionHeap.z > groundHeightTerrainAndWater)) { + groundHeight = War3MapViewer.intersectionHeap.z; + } + else { + groundHeight = groundHeightTerrainAndWater; + currentWalkableUnder = null; + } + this.location[2] = this.simulationItem.getFlyHeight() + groundHeight; + + this.instance.moveTo(this.location); + if (this.shadow != null) { + this.shadow.move(dx, dy, map.terrain.centerOffset); + this.shadow.setHeightAbsolute(currentWalkableUnder != null, groundHeight + map.imageWalkableZOffset); + } + } + + @Override + public boolean isIntersectedOnMeshAlways() { + return false; + } + + @Override + public float getSelectionScale() { + return 1.0f; + } + + @Override + public float getX() { + return this.location[0]; + } + + @Override + public float getY() { + return this.location[1]; + } + + @Override + public float getZ() { + return this.location[2]; + } + + @Override + public void unassignSelectionCircle() { + this.selectionCircle = null; + } + + @Override + public void assignSelectionCircle(final SplatMover t) { + this.selectionCircle = t; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java index 649ae3b..e6c1c6b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnitTypeData.java @@ -4,11 +4,16 @@ public class RenderUnitTypeData { private final float maxPitch; private final float maxRoll; private final float sampleRadius; + private final boolean allowCustomTeamColor; + private final int teamColor; - public RenderUnitTypeData(final float maxPitch, final float maxRoll, final float sampleRadius) { + public RenderUnitTypeData(final float maxPitch, final float maxRoll, final float sampleRadius, + final boolean allowCustomTeamColor, final int teamColor) { this.maxPitch = maxPitch; this.maxRoll = maxRoll; this.sampleRadius = sampleRadius; + this.allowCustomTeamColor = allowCustomTeamColor; + this.teamColor = teamColor; } public float getMaxPitch() { @@ -22,4 +27,12 @@ public class RenderUnitTypeData { public float getElevationSampleRadius() { return this.sampleRadius; } + + public boolean isAllowCustomTeamColor() { + return this.allowCustomTeamColor; + } + + public int getTeamColor() { + return this.teamColor; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java index 37a893a..42585ef 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/AbilityDataUI.java @@ -45,6 +45,13 @@ public class AbilityDataUI { private static final War3ID UNIT_TIP = War3ID.fromString("utip"); private static final War3ID UNIT_UBER_TIP = War3ID.fromString("utub"); + private static final War3ID ITEM_ICON_NORMAL_X = War3ID.fromString("ubpx"); + private static final War3ID ITEM_ICON_NORMAL_Y = War3ID.fromString("ubpy"); + private static final War3ID ITEM_ICON_NORMAL = War3ID.fromString("iico"); + private static final War3ID ITEM_TIP = War3ID.fromString("utip"); + private static final War3ID ITEM_UBER_TIP = War3ID.fromString("utub"); + private static final War3ID ITEM_DESCRIPTION = War3ID.fromString("ides"); + private static final War3ID UPGRADE_ICON_NORMAL_X = War3ID.fromString("gbpx"); private static final War3ID UPGRADE_ICON_NORMAL_Y = War3ID.fromString("gbpy"); private static final War3ID UPGRADE_ICON_NORMAL = War3ID.fromString("gar1"); @@ -54,6 +61,7 @@ public class AbilityDataUI { private final Map rawcodeToUI = new HashMap<>(); private final Map rawcodeToUnitUI = new HashMap<>(); + private final Map rawcodeToItemUI = new HashMap<>(); private final Map> rawcodeToUpgradeUI = new HashMap<>(); private final IconUI moveUI; private final IconUI stopUI; @@ -74,7 +82,8 @@ public class AbilityDataUI { private final IconUI selectSkillUI; public AbilityDataUI(final MutableObjectData abilityData, final MutableObjectData unitData, - final MutableObjectData upgradeData, final GameUI gameUI, final War3MapViewer viewer) { + final MutableObjectData itemData, final MutableObjectData upgradeData, final GameUI gameUI, + final War3MapViewer viewer) { final String disabledPrefix = gameUI.getSkinField("CommandButtonDisabledArtPath"); for (final War3ID alias : abilityData.keySet()) { final MutableGameObject abilityTypeData = abilityData.get(alias); @@ -129,6 +138,21 @@ public class AbilityDataUI { this.rawcodeToUnitUI.put(alias, new IconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY, iconTip, iconUberTip)); } + for (final War3ID alias : itemData.keySet()) { + final MutableGameObject abilityTypeData = itemData.get(alias); + final String iconNormalPath = gameUI.trySkinField(abilityTypeData.getFieldAsString(ITEM_ICON_NORMAL, 0)); + final int iconNormalX = abilityTypeData.getFieldAsInteger(ITEM_ICON_NORMAL_X, 0); + final int iconNormalY = abilityTypeData.getFieldAsInteger(ITEM_ICON_NORMAL_Y, 0); + final String iconTip = abilityTypeData.getFieldAsString(ITEM_TIP, 0); + final String iconUberTip = abilityTypeData.getFieldAsString(ITEM_UBER_TIP, 0); + final String iconDescription = abilityTypeData.getFieldAsString(ITEM_DESCRIPTION, 0); + final Texture iconNormal = gameUI.loadTexture(iconNormalPath); + final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix)); + this.rawcodeToItemUI.put(alias, + new ItemUI( + new IconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY, iconTip, iconUberTip), + abilityTypeData.getName(), iconDescription, iconNormalPath)); + } for (final War3ID alias : upgradeData.keySet()) { final MutableGameObject upgradeTypeData = upgradeData.get(alias); final int upgradeLevels = upgradeTypeData.getFieldAsInteger(UPGRADE_LEVELS, 0); @@ -186,6 +210,10 @@ public class AbilityDataUI { return this.rawcodeToUnitUI.get(rawcode); } + public ItemUI getItemUI(final War3ID rawcode) { + return this.rawcodeToItemUI.get(rawcode); + } + public IconUI getUpgradeUI(final War3ID rawcode, final int level) { final List upgradeUI = this.rawcodeToUpgradeUI.get(rawcode); if (upgradeUI != null) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/ItemUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/ItemUI.java new file mode 100644 index 0000000..b1a6a4d --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/ItemUI.java @@ -0,0 +1,32 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability; + +public class ItemUI { + private final IconUI iconUI; + private final String name; + private final String description; + private final String itemIconPathForDragging; + + public ItemUI(final IconUI iconUI, final String name, final String description, + final String itemIconPathForDragging) { + this.iconUI = iconUI; + this.name = name; + this.description = description; + this.itemIconPathForDragging = itemIconPathForDragging; + } + + public IconUI getIconUI() { + return this.iconUI; + } + + public String getName() { + return this.name; + } + + public String getDescription() { + return this.description; + } + + public String getItemIconPathForDragging() { + return this.itemIconPathForDragging; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CGameplayConstants.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CGameplayConstants.java index f885d54..2cba007 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CGameplayConstants.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CGameplayConstants.java @@ -65,6 +65,17 @@ public class CGameplayConstants { private final boolean maxLevelHeroesDrainExp; private final boolean buildingKillsGiveExp; + private final float dropItemRange; + private final float giveItemRange; + private final float pickupItemRange; + private final float pawnItemRange; + private final float pawnItemRate; + + private final float followRange; + private final float structureFollowRange; + private final float followItemRange; + private final float spellCastRangeBuffer; + public CGameplayConstants(final DataTable parsedDataTable) { final Element miscData = parsedDataTable.get("Misc"); // TODO use radians for half angle @@ -155,6 +166,18 @@ public class CGameplayConstants { this.agiAttackSpeedBonus = miscData.getFieldFloatValue("AgiAttackSpeedBonus"); this.heroAbilityLevelSkip = miscData.getFieldValue("HeroAbilityLevelSkip"); + + this.dropItemRange = miscData.getFieldFloatValue("DropItemRange"); + this.giveItemRange = miscData.getFieldFloatValue("GiveItemRange"); + this.pickupItemRange = miscData.getFieldFloatValue("PickupItemRange"); + this.pawnItemRange = miscData.getFieldFloatValue("PawnItemRange"); + this.pawnItemRate = miscData.getFieldFloatValue("PawnItemRate"); + + this.followRange = miscData.getFieldFloatValue("FollowRange"); + this.structureFollowRange = miscData.getFieldFloatValue("StructureFollowRange"); + this.followItemRange = miscData.getFieldFloatValue("FollowItemRange"); + + this.spellCastRangeBuffer = miscData.getFieldFloatValue("SpellCastRangeBuffer"); } public float getAttackHalfAngle() { @@ -305,6 +328,26 @@ public class CGameplayConstants { return getTableValue(this.grantNormalXp, level); } + public float getDropItemRange() { + return this.dropItemRange; + } + + public float getPickupItemRange() { + return this.pickupItemRange; + } + + public float getGiveItemRange() { + return this.giveItemRange; + } + + public float getPawnItemRange() { + return this.pawnItemRange; + } + + public float getPawnItemRate() { + return this.pawnItemRate; + } + private static int getTableValue(final int[] table, int level) { if (level <= 0) { return 0; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java index db6e5c1..2548e4d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItem.java @@ -3,17 +3,22 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation; import java.util.EnumSet; import com.etheller.warsmash.util.War3ID; +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.simulation.abilities.targeting.AbilityTargetVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; public class CItem extends CWidget { + private final War3ID typeId; + private final CItemType itemType; + private boolean hidden; - private final War3ID itemType; - - public CItem(final int handleId, final float x, final float y, final float life, final War3ID itemType) { + public CItem(final int handleId, final float x, final float y, final float life, final War3ID typeId, + final CItemType itemTypeInstance) { super(handleId, x, y, life); - this.itemType = itemType; + this.typeId = typeId; + this.itemType = itemTypeInstance; } @Override @@ -30,6 +35,7 @@ public class CItem extends CWidget { public void damage(final CSimulation simulation, final CUnit source, final CAttackType attackType, final String weaponType, final float damage) { this.life -= damage; + simulation.itemDamageEvent(this, weaponType, this.itemType.getArmorType()); } @Override @@ -38,8 +44,60 @@ public class CItem extends CWidget { return targetsAllowed.contains(CTargetType.ITEM); } + public void setX(final float x, final CWorldCollision collision) { + super.setX(x); + } + + public void setY(final float y, final CWorldCollision collision) { + super.setY(y); + } + @Override public T visit(final AbilityTargetVisitor visitor) { return visitor.accept(this); } + + public War3ID getTypeId() { + return this.typeId; + } + + public CItemType getItemType() { + return this.itemType; + } + + public void setHidden(final boolean hidden) { + this.hidden = hidden; + } + + public boolean isHidden() { + return this.hidden; + } + + public void setPointAndCheckUnstuck(final float newX, final float newY, final CSimulation game) { + final CWorldCollision collision = game.getWorldCollision(); + final PathingGrid pathingGrid = game.getPathingGrid(); + ; + float outputX = newX, outputY = newY; + int checkX = 0; + int checkY = 0; + float collisionSize; + tempRect.setSize(16, 16); + collisionSize = 16; + for (int i = 0; i < 300; i++) { + final float centerX = newX + (checkX * 64); + final float centerY = newY + (checkY * 64); + tempRect.setCenter(centerX, centerY); + if (pathingGrid.isPathable(centerX, centerY, MovementType.FOOT, collisionSize)) { + outputX = centerX; + outputY = centerY; + break; + } + final double angle = ((((int) Math.floor(Math.sqrt((4 * i) + 1))) % 4) * Math.PI) / 2; + checkX -= (int) Math.cos(angle); + checkY -= (int) Math.sin(angle); + } + setX(outputX); + setY(outputY); + } + } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItemType.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItemType.java index 8f40ce1..47de954 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItemType.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CItemType.java @@ -1,5 +1,156 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation; +import java.util.List; + +import com.etheller.warsmash.util.War3ID; + public class CItemType { + private final List abilityList; + private final War3ID cooldownGroup; + private final boolean ignoreCooldown; + private final int numberOfCharges; + private final boolean activelyUsed; + private final boolean perishable; + private final boolean useAutomaticallyWhenAcquired; + private final int goldCost; + private final int lumberCost; + private final int stockMax; + private final int stockReplenishInterval; + private final int stockStartDelay; + private final int hitPoints; + private final String armorType; + private final int level; + private final int levelUnclassified; + private final int priority; + private final boolean sellable; + private final boolean pawnable; + private final boolean droppedWhenCarrierDies; + private final boolean canBeDropped; + private final boolean validTargetForTransformation; + private final boolean includeAsRandomChoice; + + public CItemType(final List abilityList, final War3ID cooldownGroup, final boolean ignoreCooldown, + final int numberOfCharges, final boolean activelyUsed, final boolean perishable, + final boolean useAutomaticallyWhenAcquired, final int goldCost, final int lumberCost, final int stockMax, + final int stockReplenishInterval, final int stockStartDelay, final int hitPoints, final String armorType, + final int level, final int levelUnclassified, final int priority, final boolean sellable, + final boolean pawnable, final boolean droppedWhenCarrierDies, final boolean canBeDropped, + final boolean validTargetForTransformation, final boolean includeAsRandomChoice) { + this.abilityList = abilityList; + this.cooldownGroup = cooldownGroup; + this.ignoreCooldown = ignoreCooldown; + this.numberOfCharges = numberOfCharges; + this.activelyUsed = activelyUsed; + this.perishable = perishable; + this.useAutomaticallyWhenAcquired = useAutomaticallyWhenAcquired; + this.goldCost = goldCost; + this.lumberCost = lumberCost; + this.stockMax = stockMax; + this.stockReplenishInterval = stockReplenishInterval; + this.stockStartDelay = stockStartDelay; + this.hitPoints = hitPoints; + this.armorType = armorType; + this.level = level; + this.levelUnclassified = levelUnclassified; + this.priority = priority; + this.sellable = sellable; + this.pawnable = pawnable; + this.droppedWhenCarrierDies = droppedWhenCarrierDies; + this.canBeDropped = canBeDropped; + this.validTargetForTransformation = validTargetForTransformation; + this.includeAsRandomChoice = includeAsRandomChoice; + } + + public List getAbilityList() { + return this.abilityList; + } + + public War3ID getCooldownGroup() { + return this.cooldownGroup; + } + + public boolean isIgnoreCooldown() { + return this.ignoreCooldown; + } + + public int getNumberOfCharges() { + return this.numberOfCharges; + } + + public boolean isActivelyUsed() { + return this.activelyUsed; + } + + public boolean isPerishable() { + return this.perishable; + } + + public boolean isUseAutomaticallyWhenAcquired() { + return this.useAutomaticallyWhenAcquired; + } + + public int getGoldCost() { + return this.goldCost; + } + + public int getLumberCost() { + return this.lumberCost; + } + + public int getStockMax() { + return this.stockMax; + } + + public int getStockReplenishInterval() { + return this.stockReplenishInterval; + } + + public int getStockStartDelay() { + return this.stockStartDelay; + } + + public int getHitPoints() { + return this.hitPoints; + } + + public String getArmorType() { + return this.armorType; + } + + public int getLevel() { + return this.level; + } + + public int getLevelUnclassified() { + return this.levelUnclassified; + } + + public int getPriority() { + return this.priority; + } + + public boolean isSellable() { + return this.sellable; + } + + public boolean isPawnable() { + return this.pawnable; + } + + public boolean isDroppedWhenCarrierDies() { + return this.droppedWhenCarrierDies; + } + + public boolean isCanBeDropped() { + return this.canBeDropped; + } + + public boolean isValidTargetForTransformation() { + return this.validTargetForTransformation; + } + + public boolean isIncludeAsRandomChoice() { + return this.includeAsRandomChoice; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java index 47874be..dc82645 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CSimulation.java @@ -26,6 +26,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUni import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.CAttackProjectile; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CAbilityData; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CDestructableData; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CItemData; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CUnitData; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindingProcessor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType; @@ -40,9 +41,11 @@ public class CSimulation { private final CAbilityData abilityData; private final CUnitData unitData; private final CDestructableData destructableData; + private final CItemData itemData; private final List units; private final List newUnits; private final List destructables; + private final List items; private final List players; private final List projectiles; private final List newProjectiles; @@ -58,14 +61,15 @@ public class CSimulation { private float currentGameDayTimeElapsed; private final Map handleIdToUnit = new HashMap<>(); private final Map handleIdToDestructable = new HashMap<>(); + private final Map handleIdToItem = new HashMap<>(); private final Map handleIdToAbility = new HashMap<>(); private transient CommandErrorListener commandErrorListener; public CSimulation(final DataTable miscData, final MutableObjectData parsedUnitData, - final MutableObjectData parsedDestructableData, final MutableObjectData parsedAbilityData, - final SimulationRenderController simulationRenderController, final PathingGrid pathingGrid, - final Rectangle entireMapBounds, final Random seededRandom, final List playerInfos, - final CommandErrorListener commandErrorListener) { + final MutableObjectData parsedItemData, final MutableObjectData parsedDestructableData, + final MutableObjectData parsedAbilityData, final SimulationRenderController simulationRenderController, + final PathingGrid pathingGrid, final Rectangle entireMapBounds, final Random seededRandom, + final List playerInfos, final CommandErrorListener commandErrorListener) { this.gameplayConstants = new CGameplayConstants(miscData); this.simulationRenderController = simulationRenderController; this.pathingGrid = pathingGrid; @@ -73,9 +77,11 @@ public class CSimulation { this.unitData = new CUnitData(this.gameplayConstants, parsedUnitData, this.abilityData, this.simulationRenderController); this.destructableData = new CDestructableData(parsedDestructableData, simulationRenderController); + this.itemData = new CItemData(parsedItemData); this.units = new ArrayList<>(); this.newUnits = new ArrayList<>(); this.destructables = new ArrayList<>(); + this.items = new ArrayList<>(); this.projectiles = new ArrayList<>(); this.newProjectiles = new ArrayList<>(); this.handleIdAllocator = new HandleIdAllocator(); @@ -155,6 +161,13 @@ public class CSimulation { return dest; } + public CItem createItem(final War3ID alias, final float unitX, final float unitY) { + final CItem item = this.itemData.create(this, alias, unitX, unitY, this.handleIdAllocator.createId()); + this.handleIdToItem.put(item.getHandleId(), item); + this.items.add(item); + return item; + } + public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y, final float facing) { return this.simulationRenderController.createUnit(this, typeId, playerIndex, x, y, facing); @@ -271,6 +284,10 @@ public class CSimulation { this.simulationRenderController.spawnDamageSound(damagedDestructable, weaponSound, armorType); } + public void itemDamageEvent(final CItem damageItem, final String weaponSound, final String armorType) { + this.simulationRenderController.spawnDamageSound(damageItem, weaponSound, armorType); + } + public void unitConstructedEvent(final CUnit constructingUnit, final CUnit constructedStructure) { this.simulationRenderController.spawnUnitConstructionSound(constructingUnit, constructedStructure); } @@ -279,7 +296,7 @@ public class CSimulation { return this.players.get(index); } - public CommandErrorListener getCommandErrorListener() { + public CommandErrorListener getCommandErrorListener(final int playerIndex) { return this.commandErrorListener; } @@ -317,6 +334,14 @@ public class CSimulation { this.playerHeroes[hero.getPlayerIndex()].add(hero); } + public void unitPickUpItemEvent(final CUnit cUnit, final CItem item) { + this.simulationRenderController.spawnUIUnitGetItemSound(cUnit, item); + } + + public void unitDropItemEvent(final CUnit cUnit, final CItem item) { + this.simulationRenderController.spawnUIUnitDropItemSound(cUnit, item); + } + public List getPlayerHeroes(final int playerIndex) { return this.playerHeroes[playerIndex]; } @@ -342,6 +367,10 @@ public class CSimulation { if (destructable != null) { return destructable; } + final CItem item = this.handleIdToItem.get(handleId); + if (item != null) { + return item; + } return null; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java index 557db7d..c64acc5 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnit.java @@ -20,6 +20,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.CAbilityBuildInProgress; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbilityHero; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.mine.CAbilityGoldMine; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; @@ -45,7 +46,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbility import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityTargetCheckReceiver; public class CUnit extends CWidget { - private static final Rectangle tempRect = new Rectangle(); private War3ID typeId; private float facing; // degrees private float mana; @@ -436,7 +436,7 @@ public class CUnit extends CWidget { if (ability != null) { // Allow the ability to response to the order without actually placing itself in // the queue, nor modifying (interrupting) the queue. - if (!ability.checkBeforeQueue(game, this, order.getOrderId())) { + if (!ability.checkBeforeQueue(game, this, order.getOrderId(), order.getTarget(game))) { this.stateNotifier.ordersChanged(); return; } @@ -629,9 +629,15 @@ public class CUnit extends CWidget { return this.unitType.getImpactZ(); } + public double angleTo(final AbilityTarget target) { + final double dx = target.getX() - getX(); + final double dy = target.getY() - getY(); + return StrictMath.atan2(dy, dx); + } + public double distance(final AbilityTarget target) { - double dx = Math.abs(target.getX() - getX()); - double dy = Math.abs(target.getY() - getY()); + double dx = StrictMath.abs(target.getX() - getX()); + double dy = StrictMath.abs(target.getY() - getY()); final float thisCollisionSize = this.unitType.getCollisionSize(); float targetCollisionSize; if (target instanceof CUnit) { @@ -701,17 +707,28 @@ public class CUnit extends CWidget { } else { if ((this.currentBehavior == null) || (this.currentBehavior == this.defaultBehavior)) { - if (!simulation.getPlayer(getPlayerIndex()).hasAlliance(source.getPlayerIndex(), - CAllianceType.PASSIVE)) { + boolean foundMatchingReturnFireAttack = false; + if (!simulation.getPlayer(getPlayerIndex()).hasAlliance(source.getPlayerIndex(), CAllianceType.PASSIVE) + && !this.unitType.getClassifications().contains(CUnitClassification.PEON)) { for (final CUnitAttack attack : this.getAttacks()) { if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) { this.currentBehavior = getAttackBehavior().reset(OrderIds.attack, attack, source, false, CBehaviorAttackListener.DO_NOTHING); this.currentBehavior.begin(simulation); + foundMatchingReturnFireAttack = true; break; } } } + if (!foundMatchingReturnFireAttack && this.unitType.isCanFlee() && !isMovementDisabled() + && (this.moveBehavior != null)) { + final double angleTo = source.angleTo(this); + final int distanceToFlee = getSpeed(); + this.currentBehavior = this.moveBehavior.reset(OrderIds.move, + new AbilityPointTarget((float) (getX() + (distanceToFlee * StrictMath.cos(angleTo))), + (float) (getY() + (distanceToFlee * StrictMath.sin(angleTo))))); + this.currentBehavior.begin(simulation); + } } } } @@ -1194,7 +1211,7 @@ public class CUnit extends CWidget { } else { this.queuedUnitFoodPaid = false; - game.getCommandErrorListener().showNoFoodError(); + game.getCommandErrorListener(this.playerIndex).showNoFoodError(); } } } @@ -1374,6 +1391,15 @@ public class CUnit extends CWidget { return null; } + public CAbilityInventory getInventoryData() { + for (final CAbility ability : this.abilities) { + if (ability instanceof CAbilityInventory) { + return (CAbilityInventory) ability; + } + } + return null; + } + public void setUnitSpecificAttacks(final List unitSpecificAttacks) { this.unitSpecificAttacks = unitSpecificAttacks; } @@ -1388,4 +1414,18 @@ public class CUnit extends CWidget { } return this.unitType.getAttacks(); } + + public void onPickUpItem(final CSimulation game, final CItem item, final boolean playUserUISounds) { + this.stateNotifier.inventoryChanged(); + if (playUserUISounds) { + game.unitPickUpItemEvent(this, item); + } + } + + public void onDropItem(final CSimulation game, final CItem droppedItem, final boolean playUserUISounds) { + this.stateNotifier.inventoryChanged(); + if (playUserUISounds) { + game.unitDropItemEvent(this, droppedItem); + } + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitStateListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitStateListener.java index 5613e73..2f1356a 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitStateListener.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitStateListener.java @@ -15,6 +15,8 @@ public interface CUnitStateListener { void heroStatsChanged(); + void inventoryChanged(); + public static final class CUnitStateNotifier extends SubscriberSetNotifier implements CUnitStateListener { @Override @@ -58,5 +60,12 @@ public interface CUnitStateListener { listener.heroStatsChanged(); } } + + @Override + public void inventoryChanged() { + for (final CUnitStateListener listener : set) { + listener.inventoryChanged(); + } + } } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java index aaf26f6..9146721 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CUnitType.java @@ -71,6 +71,7 @@ public class CUnitType { private final List heroAbilityList; private final List heroProperNames; private final int properNamesCount; + private final boolean canFlee; public CUnitType(final String name, final int life, final int manaInitial, final int manaMaximum, final int speed, final int defense, final String abilityList, final boolean isBldg, final MovementType movementType, @@ -86,7 +87,8 @@ public class CUnitType { final List requirements, final int level, final boolean hero, final int strength, final float strengthPerLevel, final int agility, final float agilityPerLevel, final int intelligence, final float intelligencePerLevel, final CPrimaryAttribute primaryAttribute, - final List heroAbilityList, final List heroProperNames, final int properNamesCount) { + final List heroAbilityList, final List heroProperNames, final int properNamesCount, + final boolean canFlee) { this.name = name; this.life = life; this.manaInitial = manaInitial; @@ -136,6 +138,7 @@ public class CUnitType { this.heroAbilityList = heroAbilityList; this.heroProperNames = heroProperNames; this.properNamesCount = properNamesCount; + this.canFlee = canFlee; } public String getName() { @@ -333,4 +336,8 @@ public class CUnitType { public int getProperNamesCount() { return this.properNamesCount; } + + public boolean isCanFlee() { + return this.canFlee; + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java index 02989ac..1ae17c3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CWidget.java @@ -2,11 +2,13 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation; import java.util.EnumSet; +import com.badlogic.gdx.math.Rectangle; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; public abstract class CWidget implements AbilityTarget { + protected static final Rectangle tempRect = new Rectangle(); private final int handleId; private float x; private float y; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java index 612c221..7664f44 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbility.java @@ -4,6 +4,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; public interface CAbility extends CAbilityView { @@ -18,7 +19,7 @@ public interface CAbility extends CAbilityView { void onCancelFromQueue(CSimulation game, CUnit unit, int orderId); /* return false to not do anything, such as for toggling autocast */ - boolean checkBeforeQueue(CSimulation game, CUnit caster, int orderId); + boolean checkBeforeQueue(CSimulation game, CUnit caster, int orderId, AbilityTarget target); CBehavior begin(CSimulation game, CUnit caster, int orderId, CWidget target); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java index a6f4dd6..89599aa 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityAttack.java @@ -4,6 +4,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttackListener; @@ -64,7 +65,7 @@ public class CAbilityAttack extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityGeneric.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityGeneric.java index 2debd14..df4e170 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityGeneric.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityGeneric.java @@ -5,6 +5,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; @@ -66,7 +67,7 @@ public class CAbilityGeneric extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return false; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java index 1f2d1b7..232287d 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityMove.java @@ -4,6 +4,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorFollow; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorHoldPosition; @@ -91,7 +92,7 @@ public class CAbilityMove extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/AbstractCAbilityBuild.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/AbstractCAbilityBuild.java index df29658..811ca64 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/AbstractCAbilityBuild.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/AbstractCAbilityBuild.java @@ -14,6 +14,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.menu.CAbilityMenu; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; @@ -107,7 +108,7 @@ public abstract class AbstractCAbilityBuild extends AbstractCAbility implements } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityBuildInProgress.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityBuildInProgress.java index 4b38b76..7c9c085 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityBuildInProgress.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityBuildInProgress.java @@ -6,6 +6,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; @@ -33,7 +34,7 @@ public class CAbilityBuildInProgress extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { final CPlayer player = game.getPlayer(caster.getPlayerIndex()); player.refundFor(caster.getUnitType()); caster.setLife(game, 0); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityColdArrows.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityColdArrows.java index a965a4a..14dd9a0 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityColdArrows.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/combat/CAbilityColdArrows.java @@ -7,6 +7,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttackListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; @@ -91,7 +92,7 @@ public class CAbilityColdArrows extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { switch (orderId) { case OrderIds.coldarrows: case OrderIds.uncoldarrows: diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericNoIconAbility.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericNoIconAbility.java index f215c1c..8abdcae 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericNoIconAbility.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericNoIconAbility.java @@ -5,6 +5,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; public abstract class AbstractGenericNoIconAbility extends AbstractCAbility implements GenericNoIconAbility { private final War3ID alias; @@ -15,7 +16,8 @@ public abstract class AbstractGenericNoIconAbility extends AbstractCAbility impl } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, + final AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericSingleIconActiveAbility.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericSingleIconActiveAbility.java index 155f253..169f4ce 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericSingleIconActiveAbility.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericSingleIconActiveAbility.java @@ -7,6 +7,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; @@ -20,7 +21,7 @@ public abstract class AbstractGenericSingleIconActiveAbility extends AbstractCAb } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java index c9aefd6..28e2e40 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/hero/CAbilityHero.java @@ -12,6 +12,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; @@ -77,7 +78,7 @@ public class CAbilityHero extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/inventory/CAbilityInventory.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/inventory/CAbilityInventory.java new file mode 100644 index 0000000..08da64d --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/inventory/CAbilityInventory.java @@ -0,0 +1,240 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory; + +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItemType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.AbstractGenericNoIconAbility; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorDropItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorGetItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; + +public class CAbilityInventory extends AbstractGenericNoIconAbility { + private final boolean canDropItems; + private final boolean canGetItems; + private final boolean canUseItems; + private final boolean dropItemsOnDeath; + private final CItem[] itemsHeld; + private CBehaviorGetItem behaviorGetItem; + private CBehaviorDropItem behaviorDropItem; + + public CAbilityInventory(final int handleId, final War3ID alias, final boolean canDropItems, + final boolean canGetItems, final boolean canUseItems, final boolean dropItemsOnDeath, + final int itemCapacity) { + super(handleId, alias); + this.canDropItems = canDropItems; + this.canGetItems = canGetItems; + this.canUseItems = canUseItems; + this.dropItemsOnDeath = dropItemsOnDeath; + this.itemsHeld = new CItem[itemCapacity]; + } + + @Override + public void onAdd(final CSimulation game, final CUnit unit) { + this.behaviorGetItem = new CBehaviorGetItem(unit, this); + this.behaviorDropItem = new CBehaviorDropItem(unit, this); + } + + @Override + public void onRemove(final CSimulation game, final CUnit unit) { + + } + + @Override + public void onTick(final CSimulation game, final CUnit unit) { + + } + + @Override + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, + final AbilityTarget target) { + if ((orderId >= OrderIds.itemdrag00) && (orderId <= OrderIds.itemdrag05)) { + for (int i = 0; i < this.itemsHeld.length; i++) { + if (this.itemsHeld[i] == target) { + final CItem temp = this.itemsHeld[i]; + final int dragDropDestinationIndex = orderId - OrderIds.itemdrag00; + this.itemsHeld[i] = this.itemsHeld[dragDropDestinationIndex]; + this.itemsHeld[dragDropDestinationIndex] = temp; + return false; + } + } + } + return super.checkBeforeQueue(game, caster, orderId, target); + } + + @Override + public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) { + + } + + public int getItemCapacity() { + return this.itemsHeld.length; + } + + public CItem getItemInSlot(final int slotIndex) { + if ((slotIndex < 0) || (slotIndex >= this.itemsHeld.length)) { + return null; + } + return this.itemsHeld[slotIndex]; + } + + public boolean isDropItemsOnDeath() { + return this.dropItemsOnDeath; + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) { + return this.behaviorGetItem.reset((CItem) target); + } + + public CBehavior beginDropItem(final CSimulation game, final CUnit caster, final int orderId, + final CItem itemToDrop, final AbilityPointTarget target) { + return this.behaviorDropItem.reset(itemToDrop, target); + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, + final AbilityPointTarget point) { + // TODO Auto-generated method stub + return null; + } + + @Override + public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target, + final AbilityTargetCheckReceiver receiver) { + if (((orderId == OrderIds.getitem) || (orderId == OrderIds.smart)) && !target.isDead()) { + if (target instanceof CItem) { + final CItem targetItem = (CItem) target; + if (!targetItem.isHidden()) { + receiver.targetOk(target); + } + else { + receiver.orderIdNotAccepted(); + } + } + else { + receiver.orderIdNotAccepted(); + } + } + else { + if ((orderId >= OrderIds.itemdrag00) && (orderId <= OrderIds.itemdrag05)) { + if (target instanceof CItem) { + final int slot = getSlot((CItem) target); + if (slot != -1) { + receiver.targetOk(target); + } + else { + receiver.orderIdNotAccepted(); + } + } + else { + receiver.orderIdNotAccepted(); + } + } + receiver.orderIdNotAccepted(); + } + } + + public int getSlot(final CItem target) { + int slot = -1; + for (int i = 0; i < this.itemsHeld.length; i++) { + if (this.itemsHeld[i] == target) { + slot = i; + } + } + return slot; + } + + @Override + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityPointTarget target, final AbilityTargetCheckReceiver receiver) { + if (orderId == OrderIds.dropitem) { + receiver.orderIdNotAccepted(); + } + } + + @Override + public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityTargetCheckReceiver receiver) { + receiver.orderIdNotAccepted(); + } + + @Override + protected void innerCheckCanUse(final CSimulation game, final CUnit unit, final int orderId, + final AbilityActivationReceiver receiver) { + receiver.useOk(); + } + + /** + * Attempts to give the hero the specified item, returning the item slot to + * which the item is added or -1 if no available slot is found + * + * @param item + * @return + */ + public int giveItem(final CSimulation simulation, final CUnit hero, final CItem item, + final boolean playUserUISounds) { + if ((item != null) && !item.isDead() && !item.isHidden()) { + final CItemType itemType = item.getItemType(); + if (itemType.isUseAutomaticallyWhenAcquired()) { + if (itemType.isActivelyUsed()) { + item.setLife(simulation, 0); + // TODO when we give unit ability here, then use ability + } + } + else { + for (int i = 0; i < this.itemsHeld.length; i++) { + if (this.itemsHeld[i] == null) { + this.itemsHeld[i] = item; + item.setHidden(true); + hero.onPickUpItem(simulation, item, true); + return i; + } + } + if (playUserUISounds) { + simulation.getCommandErrorListener(hero.getPlayerIndex()).showInventoryFullError(); + } + } + } + return -1; + } + + public void dropItem(final CSimulation simulation, final CUnit hero, final int slotIndex, final float x, + final float y, final boolean playUserUISounds) { + final CItem droppedItem = this.itemsHeld[slotIndex]; + hero.onDropItem(simulation, droppedItem, true); + this.itemsHeld[slotIndex] = null; + droppedItem.setHidden(false); + droppedItem.setPointAndCheckUnstuck(x, y, simulation); + } + + public void dropItem(final CSimulation simulation, final CUnit hero, final CItem itemToDrop, final float x, + final float y, final boolean playUserUISounds) { + boolean foundItem = false; + for (int i = 0; i < this.itemsHeld.length; i++) { + if (this.itemsHeld[i] == itemToDrop) { + this.itemsHeld[i] = null; + foundItem = true; + } + } + if (foundItem) { + hero.onDropItem(simulation, itemToDrop, true); + itemToDrop.setHidden(false); + itemToDrop.setPointAndCheckUnstuck(x, y, simulation); + } + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityQueue.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityQueue.java index fbd44d8..7250841 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityQueue.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityQueue.java @@ -13,6 +13,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; @@ -115,7 +116,7 @@ public final class CAbilityQueue extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityRally.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityRally.java index f1b7283..4f2fecc 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityRally.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityRally.java @@ -6,6 +6,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; @@ -72,7 +73,7 @@ public class CAbilityRally extends AbstractCAbility { } @Override - public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { return true; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetItemVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetItemVisitor.java new file mode 100644 index 0000000..b1276ab --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetItemVisitor.java @@ -0,0 +1,30 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; + +public class AbilityTargetItemVisitor implements AbilityTargetVisitor { + public static final AbilityTargetItemVisitor INSTANCE = new AbilityTargetItemVisitor(); + + @Override + public CItem accept(final AbilityPointTarget target) { + return null; + } + + @Override + public CItem accept(final CUnit target) { + return null; + } + + @Override + public CItem accept(final CDestructable target) { + return null; + } + + @Override + public CItem accept(final CItem target) { + return target; + } + +} \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetStillAliveVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetStillAliveVisitor.java index bd70964..d6a7917 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetStillAliveVisitor.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/targeting/AbilityTargetStillAliveVisitor.java @@ -24,7 +24,7 @@ public class AbilityTargetStillAliveVisitor implements AbilityTargetVisitor + implements CAbilityTypeDefinition { + protected static final War3ID ITEM_CAPACITY = War3ID.fromString("inv1"); + protected static final War3ID DROP_ITEMS_ON_DEATH = War3ID.fromString("inv2"); + protected static final War3ID CAN_USE_ITEMS = War3ID.fromString("inv3"); + protected static final War3ID CAN_GET_ITEMS = War3ID.fromString("inv4"); + protected static final War3ID CAN_DROP_ITEMS = War3ID.fromString("inv5"); + + @Override + protected CAbilityTypeInventoryLevelData createLevelData(final MutableGameObject abilityEditorData, + final int level) { + final String targetsAllowedAtLevelString = abilityEditorData.getFieldAsString(TARGETS_ALLOWED, level); + final EnumSet targetsAllowedAtLevel = CTargetType.parseTargetTypeSet(targetsAllowedAtLevelString); + final int itemCapacity = abilityEditorData.getFieldAsInteger(ITEM_CAPACITY, level); + final boolean dropItemsOnDeath = abilityEditorData.getFieldAsBoolean(DROP_ITEMS_ON_DEATH, level); + final boolean canUseItems = abilityEditorData.getFieldAsBoolean(CAN_USE_ITEMS, level); + final boolean canGetItems = abilityEditorData.getFieldAsBoolean(CAN_GET_ITEMS, level); + final boolean canDropItems = abilityEditorData.getFieldAsBoolean(CAN_DROP_ITEMS, level); + return new CAbilityTypeInventoryLevelData(targetsAllowedAtLevel, canDropItems, canGetItems, canUseItems, + dropItemsOnDeath, itemCapacity); + } + + @Override + protected CAbilityType innerCreateAbilityType(final War3ID alias, final MutableGameObject abilityEditorData, + final List levelData) { + return new CAbilityTypeInventory(alias, abilityEditorData.getCode(), levelData); + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInventory.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInventory.java new file mode 100644 index 0000000..66b06f2 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInventory.java @@ -0,0 +1,24 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.impl; + +import java.util.List; + +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAbilityType; + +public class CAbilityTypeInventory extends CAbilityType { + + public CAbilityTypeInventory(final War3ID alias, final War3ID code, + final List levelData) { + super(alias, code, levelData); + } + + @Override + public CAbility createAbility(final int handleId) { + final CAbilityTypeInventoryLevelData levelData = getLevelData(0); + return new CAbilityInventory(handleId, getAlias(), levelData.isCanDropItems(), levelData.isCanGetItems(), + levelData.isCanUseItems(), levelData.isDropItemsOnDeath(), levelData.getItemCapacity()); + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInventoryLevelData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInventoryLevelData.java new file mode 100644 index 0000000..518b95f --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/types/impl/CAbilityTypeInventoryLevelData.java @@ -0,0 +1,47 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.impl; + +import java.util.EnumSet; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAbilityTypeLevelData; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; + +public class CAbilityTypeInventoryLevelData extends CAbilityTypeLevelData { + + private final boolean canDropItems; + private final boolean canGetItems; + private final boolean canUseItems; + private final boolean dropItemsOnDeath; + private final int itemCapacity; + + public CAbilityTypeInventoryLevelData(final EnumSet targetsAllowed, final boolean canDropItems, + final boolean canGetItems, final boolean canUseItems, final boolean dropItemsOnDeath, + final int itemCapacity) { + super(targetsAllowed); + this.canDropItems = canDropItems; + this.canGetItems = canGetItems; + this.canUseItems = canUseItems; + this.dropItemsOnDeath = dropItemsOnDeath; + this.itemCapacity = itemCapacity; + } + + public boolean isCanDropItems() { + return this.canDropItems; + } + + public boolean isCanGetItems() { + return this.canGetItems; + } + + public boolean isCanUseItems() { + return this.canUseItems; + } + + public boolean isDropItemsOnDeath() { + return this.dropItemsOnDeath; + } + + public int getItemCapacity() { + return this.itemCapacity; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorOrcBuild.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorOrcBuild.java index 78a72e7..1b91123 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorOrcBuild.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorOrcBuild.java @@ -61,9 +61,10 @@ public class CBehaviorOrcBuild extends CAbstractRangedBehavior { buildLocationObstructed = true; } } + final int playerIndex = this.unit.getPlayerIndex(); if (!buildLocationObstructed) { - final CUnit constructedStructure = simulation.createUnit(this.orderId, this.unit.getPlayerIndex(), - this.target.getX(), this.target.getY(), simulation.getGameplayConstants().getBuildingAngle()); + final CUnit constructedStructure = simulation.createUnit(this.orderId, playerIndex, this.target.getX(), + this.target.getY(), simulation.getGameplayConstants().getBuildingAngle()); constructedStructure.setConstructing(true); constructedStructure.setWorkerInside(this.unit); constructedStructure.setLife(simulation, @@ -80,9 +81,9 @@ public class CBehaviorOrcBuild extends CAbstractRangedBehavior { simulation.unitConstructedEvent(this.unit, constructedStructure); } else { - final CPlayer player = simulation.getPlayer(this.unit.getPlayerIndex()); + final CPlayer player = simulation.getPlayer(playerIndex); refund(player, unitTypeToCreate); - simulation.getCommandErrorListener().showCantPlaceError(); + simulation.getCommandErrorListener(playerIndex).showCantPlaceError(); } } return this.unit.pollNextOrderBehavior(simulation); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java index f6491fc..6fd5b1c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/CBehaviorUndeadBuild.java @@ -77,9 +77,10 @@ public class CBehaviorUndeadBuild extends CAbstractRangedBehavior { buildLocationObstructed = true; } } + final int playerIndex = this.unit.getPlayerIndex(); if (!buildLocationObstructed) { - final CUnit constructedStructure = simulation.createUnit(this.orderId, this.unit.getPlayerIndex(), - this.target.getX(), this.target.getY(), simulation.getGameplayConstants().getBuildingAngle()); + final CUnit constructedStructure = simulation.createUnit(this.orderId, playerIndex, this.target.getX(), + this.target.getY(), simulation.getGameplayConstants().getBuildingAngle()); constructedStructure.setConstructing(true); constructedStructure.setLife(simulation, constructedStructure.getMaximumLife() * WarsmashConstants.BUILDING_CONSTRUCT_START_LIFE); @@ -100,9 +101,9 @@ public class CBehaviorUndeadBuild extends CAbstractRangedBehavior { this.doneTick = simulation.getGameTurnTick() + delayAnimationTicks; } else { - final CPlayer player = simulation.getPlayer(this.unit.getPlayerIndex()); + final CPlayer player = simulation.getPlayer(playerIndex); refund(player, unitTypeToCreate); - simulation.getCommandErrorListener().showCantPlaceError(); + simulation.getCommandErrorListener(playerIndex).showCantPlaceError(); return this.unit.pollNextOrderBehavior(simulation); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/inventory/CBehaviorDropItem.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/inventory/CBehaviorDropItem.java new file mode 100644 index 0000000..a74bd3d --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/inventory/CBehaviorDropItem.java @@ -0,0 +1,71 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetStillAliveVisitor; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CAbstractRangedBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; + +public class CBehaviorDropItem extends CAbstractRangedBehavior { + private final CAbilityInventory inventory; + private CItem targetItem; + + public CBehaviorDropItem(final CUnit unit, final CAbilityInventory inventory) { + super(unit); + this.inventory = inventory; + } + + public CBehaviorDropItem reset(final CItem targetItem, final AbilityPointTarget targetPoint) { + innerReset(targetPoint); + this.targetItem = targetItem; + return this; + } + + @Override + public boolean isWithinRange(final CSimulation simulation) { + return this.unit.canReach(this.target, simulation.getGameplayConstants().getDropItemRange()); + } + + @Override + public void endMove(final CSimulation game, final boolean interrupted) { + } + + @Override + public void begin(final CSimulation game) { + } + + @Override + public void end(final CSimulation game, final boolean interrupted) { + } + + @Override + public int getHighlightOrderId() { + return OrderIds.dropitem; + } + + @Override + protected CBehavior update(final CSimulation simulation, final boolean withinRange) { + this.inventory.dropItem(simulation, this.unit, this.targetItem, this.target.getX(), this.target.getY(), true); + return this.unit.pollNextOrderBehavior(simulation); + } + + @Override + protected CBehavior updateOnInvalidTarget(final CSimulation simulation) { + return this.unit.pollNextOrderBehavior(simulation); + } + + @Override + protected boolean checkTargetStillValid(final CSimulation simulation) { + return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE); + } + + @Override + protected void resetBeforeMoving(final CSimulation simulation) { + + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/inventory/CBehaviorGetItem.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/inventory/CBehaviorGetItem.java new file mode 100644 index 0000000..6568c68 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/inventory/CBehaviorGetItem.java @@ -0,0 +1,70 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetItemVisitor; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetStillAliveVisitor; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CAbstractRangedBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; + +public class CBehaviorGetItem extends CAbstractRangedBehavior { + private final CAbilityInventory inventory; + + public CBehaviorGetItem(final CUnit unit, final CAbilityInventory inventory) { + super(unit); + this.inventory = inventory; + } + + public CBehaviorGetItem reset(final CItem targetItem) { + innerReset(targetItem); + return this; + } + + @Override + public boolean isWithinRange(final CSimulation simulation) { + return this.unit.canReach(this.target, simulation.getGameplayConstants().getPickupItemRange()); + } + + @Override + public void endMove(final CSimulation game, final boolean interrupted) { + } + + @Override + public void begin(final CSimulation game) { + } + + @Override + public void end(final CSimulation game, final boolean interrupted) { + } + + @Override + public int getHighlightOrderId() { + return OrderIds.getitem; + } + + @Override + protected CBehavior update(final CSimulation simulation, final boolean withinRange) { + final CItem targetItem = this.target.visit(AbilityTargetItemVisitor.INSTANCE); + this.inventory.giveItem(simulation, this.unit, targetItem, true); + return this.unit.pollNextOrderBehavior(simulation); + } + + @Override + protected CBehavior updateOnInvalidTarget(final CSimulation simulation) { + return this.unit.pollNextOrderBehavior(simulation); + } + + @Override + protected boolean checkTargetStillValid(final CSimulation simulation) { + return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE); + } + + @Override + protected void resetBeforeMoving(final CSimulation simulation) { + + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java index 6a92634..d0cdcb5 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CAbilityData.java @@ -14,6 +14,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.def import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionColdArrows; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionGoldMine; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionHarvest; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionInventory; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.definitions.impl.CAbilityTypeDefinitionReturnResources; public class CAbilityData { @@ -34,6 +35,7 @@ public class CAbilityData { this.codeToAbilityTypeDefinition.put(War3ID.fromString("Artn"), new CAbilityTypeDefinitionReturnResources()); this.codeToAbilityTypeDefinition.put(War3ID.fromString("Ahar"), new CAbilityTypeDefinitionHarvest()); this.codeToAbilityTypeDefinition.put(War3ID.fromString("ANcl"), new CAbilityTypeDefinitionChannelTest()); + this.codeToAbilityTypeDefinition.put(War3ID.fromString("AInv"), new CAbilityTypeDefinitionInventory()); } public CAbilityType getAbilityType(final War3ID alias) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CItemData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CItemData.java index 9c740c5..bfc8305 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CItemData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CItemData.java @@ -1,8 +1,117 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation.data; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.etheller.warsmash.units.manager.MutableObjectData; +import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItemType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; public class CItemData { + private static final War3ID ABILITY_LIST = War3ID.fromString("iabi"); + private static final War3ID COOLDOWN_GROUP = War3ID.fromString("icid"); + private static final War3ID IGNORE_COOLDOWN = War3ID.fromString("iicd"); + private static final War3ID NUMBER_OF_CHARGES = War3ID.fromString("iuse"); + private static final War3ID ACTIVELY_USED = War3ID.fromString("iusa"); + private static final War3ID PERISHABLE = War3ID.fromString("iper"); + private static final War3ID USE_AUTOMATICALLY_WHEN_ACQUIRED = War3ID.fromString("ipow"); + + private static final War3ID GOLD_COST = War3ID.fromString("igol"); + private static final War3ID LUMBER_COST = War3ID.fromString("ilum"); + private static final War3ID STOCK_MAX = War3ID.fromString("isto"); + private static final War3ID STOCK_REPLENISH_INTERVAL = War3ID.fromString("istr"); + private static final War3ID STOCK_START_DELAY = War3ID.fromString("isst"); + + private static final War3ID HIT_POINTS = War3ID.fromString("ihtp"); + private static final War3ID ARMOR_TYPE = War3ID.fromString("iarm"); + + private static final War3ID LEVEL = War3ID.fromString("ilev"); + private static final War3ID LEVEL_UNCLASSIFIED = War3ID.fromString("ilvo"); + private static final War3ID PRIORITY = War3ID.fromString("ipri"); + + private static final War3ID SELLABLE = War3ID.fromString("isel"); + private static final War3ID PAWNABLE = War3ID.fromString("ipaw"); + + private static final War3ID DROPPED_WHEN_CARRIER_DIES = War3ID.fromString("idrp"); + private static final War3ID CAN_BE_DROPPED = War3ID.fromString("idro"); + + private static final War3ID VALID_TARGET_FOR_TRANSFORMATION = War3ID.fromString("imor"); + private static final War3ID INCLUDE_AS_RANDOM_CHOICE = War3ID.fromString("iprn"); + + private final Map itemIdToItemType = new HashMap<>(); + private final MutableObjectData itemData; + public CItemData(final MutableObjectData itemData) { + this.itemData = itemData; + } + + public CItem create(final CSimulation simulation, final War3ID typeId, final float x, final float y, + final int handleId) { + final MutableGameObject itemType = this.itemData.get(typeId); + final CItemType itemTypeInstance = getItemTypeInstance(typeId, itemType); + + return new CItem(handleId, x, y, itemTypeInstance.getHitPoints(), typeId, itemTypeInstance); + } + + private CItemType getItemTypeInstance(final War3ID typeId, final MutableGameObject itemType) { + CItemType itemTypeInstance = this.itemIdToItemType.get(typeId); + if (itemTypeInstance == null) { + final String abilityListString = itemType.getFieldAsString(ABILITY_LIST, 0); + final String[] abilityListStringItems = abilityListString.split(","); + final List abilityList = new ArrayList<>(); + for (final String abilityListStringItem : abilityListStringItems) { + if (abilityListStringItem.length() == 4) { + abilityList.add(War3ID.fromString(abilityListStringItem)); + } + } + + final War3ID cooldownGroup; + final String cooldownGroupString = itemType.getFieldAsString(COOLDOWN_GROUP, 0); + if ((cooldownGroupString != null) && (cooldownGroupString.length() == 4)) { + cooldownGroup = War3ID.fromString(cooldownGroupString); + } + else { + cooldownGroup = null; + } + final boolean ignoreCooldown = itemType.getFieldAsBoolean(IGNORE_COOLDOWN, 0); + final int numberOfCharges = itemType.getFieldAsInteger(NUMBER_OF_CHARGES, 0); + final boolean activelyUsed = itemType.getFieldAsBoolean(ACTIVELY_USED, 0); + final boolean perishable = itemType.getFieldAsBoolean(PERISHABLE, 0); + final boolean useAutomaticallyWhenAcquired = itemType.getFieldAsBoolean(USE_AUTOMATICALLY_WHEN_ACQUIRED, 0); + + final int goldCost = itemType.getFieldAsInteger(GOLD_COST, 0); + final int lumberCost = itemType.getFieldAsInteger(LUMBER_COST, 0); + final int stockMax = itemType.getFieldAsInteger(STOCK_MAX, 0); + final int stockReplenishInterval = itemType.getFieldAsInteger(STOCK_REPLENISH_INTERVAL, 0); + final int stockStartDelay = itemType.getFieldAsInteger(STOCK_START_DELAY, 0); + + final int hitPoints = itemType.getFieldAsInteger(HIT_POINTS, 0); + final String armorType = itemType.getFieldAsString(ARMOR_TYPE, 0); + + final int level = itemType.getFieldAsInteger(LEVEL, 0); + final int levelUnclassified = itemType.getFieldAsInteger(LEVEL_UNCLASSIFIED, 0); + final int priority = itemType.getFieldAsInteger(PRIORITY, 0); + + final boolean sellable = itemType.getFieldAsBoolean(SELLABLE, 0); + final boolean pawnable = itemType.getFieldAsBoolean(PAWNABLE, 0); + + final boolean droppedWhenCarrierDies = itemType.getFieldAsBoolean(DROPPED_WHEN_CARRIER_DIES, 0); + final boolean canBeDropped = itemType.getFieldAsBoolean(CAN_BE_DROPPED, 0); + + final boolean validTargetForTransformation = itemType.getFieldAsBoolean(VALID_TARGET_FOR_TRANSFORMATION, 0); + final boolean includeAsRandomChoice = itemType.getFieldAsBoolean(INCLUDE_AS_RANDOM_CHOICE, 0); + + itemTypeInstance = new CItemType(abilityList, cooldownGroup, ignoreCooldown, numberOfCharges, activelyUsed, + perishable, useAutomaticallyWhenAcquired, goldCost, lumberCost, stockMax, stockReplenishInterval, + stockStartDelay, hitPoints, armorType, level, levelUnclassified, priority, sellable, pawnable, + droppedWhenCarrierDies, canBeDropped, validTargetForTransformation, includeAsRandomChoice); + this.itemIdToItemType.put(typeId, itemTypeInstance); + } + return itemTypeInstance; } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java index b566693..283b160 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/data/CUnitData.java @@ -168,6 +168,8 @@ public class CUnitData { private static final War3ID INT_PLUS = War3ID.fromString("uinp"); private static final War3ID PRIMARY_ATTRIBUTE = War3ID.fromString("upra"); + private static final War3ID CAN_FLEE = War3ID.fromString("ufle"); + private final CGameplayConstants gameplayConstants; private final MutableObjectData unitData; private final Map unitIdToUnitType = new HashMap<>(); @@ -279,6 +281,8 @@ public class CUnitData { final float propWindow = unitType.getFieldAsFloat(PROPULSION_WINDOW, 0); final float turnRate = unitType.getFieldAsFloat(TURN_RATE, 0); + final boolean canFlee = unitType.getFieldAsBoolean(CAN_FLEE, 0); + final float strPlus = unitType.getFieldAsFloat(STR_PLUS, 0); final float agiPlus = unitType.getFieldAsFloat(AGI_PLUS, 0); final float intPlus = unitType.getFieldAsFloat(INT_PLUS, 0); @@ -533,7 +537,7 @@ public class CUnitData { minimumAttackRange, structuresBuilt, unitsTrained, researchesAvailable, unitRace, goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes, requiredPathingTypes, propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus, agility, agiPlus, intelligence, intPlus, - primaryAttribute, heroAbilityList, heroProperNames, properNamesCount); + primaryAttribute, heroAbilityList, heroProperNames, properNamesCount, canFlee); this.unitIdToUnitType.put(typeId, unitTypeInstance); } return unitTypeInstance; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderDropItemAtPoint.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderDropItemAtPoint.java new file mode 100644 index 0000000..0084a25 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderDropItemAtPoint.java @@ -0,0 +1,111 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgTargetCheckReceiver; + +public class COrderDropItemAtPoint implements COrder { + private final int abilityHandleId; + private final int orderId; + private final int itemHandleId; + private final AbilityPointTarget target; + private final boolean queued; + + public COrderDropItemAtPoint(final int abilityHandleId, final int orderId, final int itemHandleId, + final AbilityPointTarget target, final boolean queued) { + this.abilityHandleId = abilityHandleId; + this.orderId = orderId; + this.itemHandleId = itemHandleId; + this.target = target; + this.queued = queued; + } + + @Override + public int getAbilityHandleId() { + return this.abilityHandleId; + } + + @Override + public int getOrderId() { + return this.orderId; + } + + @Override + public AbilityPointTarget getTarget(final CSimulation game) { + return this.target; + } + + @Override + public boolean isQueued() { + return this.queued; + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster) { + final CAbilityInventory ability = (CAbilityInventory) game.getAbility(this.abilityHandleId); + ability.checkCanUse(game, caster, this.orderId, this.abilityActivationReceiver.reset()); + if (this.abilityActivationReceiver.isUseOk()) { + final StringMsgTargetCheckReceiver targetReceiver = (StringMsgTargetCheckReceiver) targetCheckReceiver; + final CItem itemToDrop = (CItem) game.getWidget(this.itemHandleId); + return ability.beginDropItem(game, caster, this.orderId, itemToDrop, this.target); + } + else { + game.getCommandErrorListener(caster.getPlayerIndex()) + .showCommandError(this.abilityActivationReceiver.getMessage()); + return caster.pollNextOrderBehavior(game); + } + + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + this.abilityHandleId; + result = (prime * result) + this.itemHandleId; + result = (prime * result) + this.orderId; + result = (prime * result) + (this.queued ? 1231 : 1237); + result = (prime * result) + ((this.target == null) ? 0 : this.target.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final COrderDropItemAtPoint other = (COrderDropItemAtPoint) obj; + if (this.abilityHandleId != other.abilityHandleId) { + return false; + } + if (this.itemHandleId != other.itemHandleId) { + return false; + } + if (this.orderId != other.orderId) { + return false; + } + if (this.queued != other.queued) { + return false; + } + if (this.target == null) { + if (other.target != null) { + return false; + } + } + else if (!this.target.equals(other.target)) { + return false; + } + return true; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderNoTarget.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderNoTarget.java index a1537b3..6b08cd9 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderNoTarget.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderNoTarget.java @@ -44,12 +44,13 @@ public class COrderNoTarget implements COrder { return ability.beginNoTarget(game, caster, this.orderId); } else { - game.getCommandErrorListener().showCommandError(targetReceiver.getMessage()); + game.getCommandErrorListener(caster.getPlayerIndex()).showCommandError(targetReceiver.getMessage()); return caster.pollNextOrderBehavior(game); } } else { - game.getCommandErrorListener().showCommandError(this.abilityActivationReceiver.getMessage()); + game.getCommandErrorListener(caster.getPlayerIndex()) + .showCommandError(this.abilityActivationReceiver.getMessage()); return caster.pollNextOrderBehavior(game); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetPoint.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetPoint.java index d3948db..89e25c8 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetPoint.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetPoint.java @@ -52,12 +52,13 @@ public class COrderTargetPoint implements COrder { return ability.begin(game, caster, this.orderId, this.target); } else { - game.getCommandErrorListener().showCommandError(targetReceiver.getMessage()); + game.getCommandErrorListener(caster.getPlayerIndex()).showCommandError(targetReceiver.getMessage()); return caster.pollNextOrderBehavior(game); } } else { - game.getCommandErrorListener().showCommandError(this.abilityActivationReceiver.getMessage()); + game.getCommandErrorListener(caster.getPlayerIndex()) + .showCommandError(this.abilityActivationReceiver.getMessage()); return caster.pollNextOrderBehavior(game); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetWidget.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetWidget.java index d9c4c01..0262d05 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetWidget.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/orders/COrderTargetWidget.java @@ -55,12 +55,13 @@ public class COrderTargetWidget implements COrder { return ability.begin(game, caster, this.orderId, targetReceiver.getTarget()); } else { - game.getCommandErrorListener().showCommandError(targetReceiver.getMessage()); + game.getCommandErrorListener(caster.getPlayerIndex()).showCommandError(targetReceiver.getMessage()); return caster.pollNextOrderBehavior(game); } } else { - game.getCommandErrorListener().showCommandError(this.abilityActivationReceiver.getMessage()); + game.getCommandErrorListener(caster.getPlayerIndex()) + .showCommandError(this.abilityActivationReceiver.getMessage()); return caster.pollNextOrderBehavior(game); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderExecutor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderExecutor.java index 104ee22..96c2458 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderExecutor.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderExecutor.java @@ -4,6 +4,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorHoldPosition; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderDropItemAtPoint; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetPoint; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetWidget; @@ -26,6 +27,14 @@ public class CPlayerUnitOrderExecutor implements CPlayerUnitOrderListener { unit.order(this.game, new COrderTargetWidget(abilityHandleId, orderId, targetHandleId, queue), queue); } + @Override + public void issueDropItemAtPointOrder(final int unitHandleId, final int abilityHandleId, final int orderId, + final int targetHandleId, final float x, final float y, final boolean queue) { + final CUnit unit = this.game.getUnit(unitHandleId); + unit.order(this.game, new COrderDropItemAtPoint(abilityHandleId, orderId, targetHandleId, + new AbilityPointTarget(x, y), queue), queue); + } + @Override public void issuePointOrder(final int unitHandleId, final int abilityHandleId, final int orderId, final float x, final float y, final boolean queue) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderListener.java index 187dc11..af64694 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderListener.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayerUnitOrderListener.java @@ -5,8 +5,9 @@ public interface CPlayerUnitOrderListener { void issuePointOrder(int unitHandleId, int abilityHandleId, int orderId, float x, float y, boolean queue); - // Below: used for "DROP ITEM AT POINT" ???? -// boolean issueTargetAndPointOrder(int unitHandleId, int orderId, int targetHandleId, float x, float y); + // Below: used for "DROP ITEM AT POINT" + void issueDropItemAtPointOrder(int unitHandleId, int abilityHandleId, int orderId, int targetHandleId, float x, + float y, final boolean queue); void issueImmediateOrder(int unitHandleId, int abilityHandleId, int orderId, boolean queue); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/SimulationRenderController.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/SimulationRenderController.java index 051df71..05b9350 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/SimulationRenderController.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/SimulationRenderController.java @@ -4,6 +4,7 @@ import java.awt.image.BufferedImage; import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; @@ -50,4 +51,8 @@ public interface SimulationRenderController { void spawnGainResourceTextTag(CUnit gainingUnit, ResourceType resourceType, int amount); void spawnEffectOnUnit(CUnit unit, String effectPath); + + void spawnUIUnitGetItemSound(CUnit cUnit, CItem item); + + void spawnUIUnitDropItemSound(CUnit cUnit, CItem item); } 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 5f3948d..15bf620 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 @@ -12,7 +12,6 @@ import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame; import com.etheller.warsmash.parsers.fdf.frames.TextureFrame; import com.etheller.warsmash.parsers.fdf.frames.UIFrame; import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag; -import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButton; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.ClickableActionFrame; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandCardCommandListener; @@ -22,7 +21,8 @@ public class CommandCardIcon extends AbstractRenderableFrame implements Clickabl private TextureFrame activeHighlightFrame; private SpriteFrame cooldownFrame; private SpriteFrame autocastFrame; - private CommandButton commandButton; + private float defaultWidth; + private float defaultHeight; private int abilityHandleId; private int orderId; private int autoCastOrderId; @@ -49,33 +49,16 @@ public class CommandCardIcon extends AbstractRenderableFrame implements Clickabl this.autocastFrame = autocastFrame; } - public void setCommandButton(final CommandButton commandButton) { - this.commandButton = commandButton; - if (commandButton == null) { - this.iconFrame.setVisible(false); + public void clear() { + this.iconFrame.setVisible(false); + if (this.activeHighlightFrame != null) { this.activeHighlightFrame.setVisible(false); - this.cooldownFrame.setVisible(false); + } + this.cooldownFrame.setVisible(false); + if (this.autocastFrame != null) { this.autocastFrame.setVisible(false); - setVisible(false); - } - else { - if (commandButton.isEnabled()) { - this.iconFrame.setTexture(commandButton.getIcon()); - } - else { - this.iconFrame.setTexture(commandButton.getDisabledIcon()); - } - if (commandButton.getCooldownRemaining() <= 0) { - this.cooldownFrame.setVisible(false); - } - else { - this.cooldownFrame.setVisible(true); - this.cooldownFrame.setSequence(PrimaryTag.STAND); - this.cooldownFrame.setAnimationSpeed(commandButton.getCooldown()); - this.cooldownFrame - .setFrameByRatio(1 - (commandButton.getCooldownRemaining() / commandButton.getCooldown())); - } } + setVisible(false); } public void setCommandButtonData(final Texture texture, final int abilityHandleId, final int orderId, @@ -84,19 +67,23 @@ public class CommandCardIcon extends AbstractRenderableFrame implements Clickabl this.menuButton = menuButton; setVisible(true); this.iconFrame.setVisible(true); - this.activeHighlightFrame.setVisible(active); + if (this.activeHighlightFrame != null) { + this.activeHighlightFrame.setVisible(active); + } this.cooldownFrame.setVisible(false); - this.autocastFrame.setVisible(autoCastOrderId != 0); - if (autoCastOrderId != 0) { - if (this.autoCastActive != autoCastActive) { - if (autoCastActive) { - this.autocastFrame.setSequence(PrimaryTag.STAND); - } - else { - this.autocastFrame.setSequence(-1); + if (this.autocastFrame != null) { + this.autocastFrame.setVisible(autoCastOrderId != 0); + if (autoCastOrderId != 0) { + if (this.autoCastActive != autoCastActive) { + if (autoCastActive) { + this.autocastFrame.setSequence(PrimaryTag.STAND); + } + else { + this.autocastFrame.setSequence(-1); + } } + this.autoCastActive = autoCastActive; } - this.autoCastActive = autoCastActive; } this.iconFrame.setTexture(texture); this.abilityHandleId = abilityHandleId; @@ -112,23 +99,32 @@ public class CommandCardIcon extends AbstractRenderableFrame implements Clickabl @Override protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) { this.iconFrame.positionBounds(gameUI, viewport); - this.activeHighlightFrame.positionBounds(gameUI, viewport); + if (this.activeHighlightFrame != null) { + this.activeHighlightFrame.positionBounds(gameUI, viewport); + } this.cooldownFrame.positionBounds(gameUI, viewport); - this.autocastFrame.positionBounds(gameUI, viewport); + if (this.autocastFrame != null) { + this.autocastFrame.positionBounds(gameUI, viewport); + } } @Override protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) { this.iconFrame.render(batch, baseFont, glyphLayout); - this.activeHighlightFrame.render(batch, baseFont, glyphLayout); + if (this.activeHighlightFrame != null) { + this.activeHighlightFrame.render(batch, baseFont, glyphLayout); + } this.cooldownFrame.render(batch, baseFont, glyphLayout); - this.autocastFrame.render(batch, baseFont, glyphLayout); + if (this.autocastFrame != null) { + this.autocastFrame.render(batch, baseFont, glyphLayout); + } } @Override public UIFrame touchDown(final float screenX, final float screenY, final int button) { if (isVisible() && this.renderBounds.contains(screenX, screenY)) { - if ((this.orderId != 0) || this.menuButton) { + if (((button == Input.Buttons.LEFT) && (this.orderId != 0)) + || ((button == Input.Buttons.RIGHT) && (this.autoCastOrderId != 0)) || this.menuButton) { return this; } } @@ -154,28 +150,40 @@ public class CommandCardIcon extends AbstractRenderableFrame implements Clickabl this.commandCardCommandListener.openMenu(this.orderId); } else { - this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.orderId, false); + this.commandCardCommandListener.onClick(this.abilityHandleId, this.orderId, false); } } else if (button == Input.Buttons.RIGHT) { - this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.autoCastOrderId, true); + this.commandCardCommandListener.onClick(this.abilityHandleId, this.autoCastOrderId, true); } } @Override public void mouseDown(final GameUI gameUI, final Viewport uiViewport) { - this.iconFrame.setWidth(GameUI.convertX(uiViewport, MeleeUI.DEFAULT_COMMAND_CARD_ICON_PRESSED_WIDTH)); - this.iconFrame.setHeight(GameUI.convertY(uiViewport, MeleeUI.DEFAULT_COMMAND_CARD_ICON_PRESSED_WIDTH)); + this.iconFrame.setWidth(this.defaultWidth * 0.95f); + this.iconFrame.setHeight(this.defaultHeight * 0.95f); positionBounds(gameUI, uiViewport); } @Override public void mouseUp(final GameUI gameUI, final Viewport uiViewport) { - this.iconFrame.setWidth(GameUI.convertX(uiViewport, MeleeUI.DEFAULT_COMMAND_CARD_ICON_WIDTH)); - this.iconFrame.setHeight(GameUI.convertY(uiViewport, MeleeUI.DEFAULT_COMMAND_CARD_ICON_WIDTH)); + this.iconFrame.setWidth(this.defaultWidth); + this.iconFrame.setHeight(this.defaultHeight); positionBounds(gameUI, uiViewport); } + @Override + public void setWidth(final float width) { + this.defaultWidth = width; + super.setWidth(width); + } + + @Override + public void setHeight(final float height) { + this.defaultHeight = height; + super.setHeight(height); + } + @Override public void mouseEnter(final GameUI gameUI, final Viewport uiViewport) { } 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 04d5f1b..290c4c9 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 @@ -79,10 +79,12 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.ItemUI; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButtonListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CGameplayConstants; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItemType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit.QueueItemType; @@ -110,6 +112,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.G import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.GenericSingleIconActiveAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbilityHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CPrimaryAttribute; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; @@ -147,9 +150,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma - WORLD_FRAME_MESSAGE_FADEOUT_MILLIS; private static final String BUILDING_PATHING_PREVIEW_KEY = "buildingPathingPreview"; public static final float DEFAULT_COMMAND_CARD_ICON_WIDTH = 0.039f; - public static final float DEFAULT_COMMAND_CARD_ICON_PRESSED_WIDTH = 0.037f; + public static final float DEFAULT_INVENTORY_ICON_WIDTH = 0.03125f; private static final int COMMAND_CARD_WIDTH = 4; private static final int COMMAND_CARD_HEIGHT = 3; + private static final int INVENTORY_WIDTH = 2; + private static final int INVENTORY_HEIGHT = 3; private static final Vector2 screenCoordsVector = new Vector2(); private static final Vector3 clickLocationTemp = new Vector3(); @@ -217,6 +222,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private UIFrame heroInfoPanel; + private SimpleFrame inventoryBarFrame; + private StringFrame inventoryTitleFrame; + private final CommandCardIcon[][] inventoryIcons = new CommandCardIcon[INVENTORY_HEIGHT][INVENTORY_WIDTH]; + private Texture consoleInventoryNoCapacityTexture; + private final CommandCardIcon[][] commandCard = new CommandCardIcon[COMMAND_CARD_HEIGHT][COMMAND_CARD_WIDTH]; private RenderUnit selectedUnit; @@ -274,6 +284,10 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private StringFrame intelligenceValue; private SimpleFrame smashHeroInfoPanelWrapper; + private final StringBuilder recycleStringBuilder = new StringBuilder(); + private CItem draggingItem; + private final ItemCommandCardCommandListener itemCommandCardCommandListener; + public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport, final Scene uiScene, final Scene portraitScene, final CameraPreset[] cameraPresets, final CameraRates cameraRates, final War3MapViewer war3MapViewer, final RootFrameListener rootFrameListener, @@ -303,6 +317,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.cursorTargetSetupVisitor = new CursorTargetSetupVisitor(); this.localPlayer.addStateListener(this); + + this.itemCommandCardCommandListener = new ItemCommandCardCommandListener(); } private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) { @@ -576,6 +592,57 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.agilityValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconHeroAgilityValue", 0); this.intelligenceValue = (StringFrame) this.rootFrame.getFrameByName("InfoPanelIconHeroIntellectValue", 0); + this.inventoryBarFrame = (SimpleFrame) this.rootFrame.createSimpleFrame("SmashSimpleInventoryBar", + this.rootFrame, 0); + this.inventoryBarFrame.setWidth(GameUI.convertX(this.uiViewport, 0.064f)); + this.inventoryBarFrame.setHeight(GameUI.convertY(this.uiViewport, 0.115f)); + this.inventoryBarFrame.addSetPoint(new SetPoint(FramePoint.BOTTOMRIGHT, this.consoleUI, FramePoint.BOTTOMLEFT, + GameUI.convertX(this.uiViewport, 0.496f), GameUI.convertY(this.uiViewport, 0.0f))); + + int commandButtonIndex = 0; + for (int j = 0; j < INVENTORY_HEIGHT; j++) { + for (int i = 0; i < INVENTORY_WIDTH; i++) { + final CommandCardIcon commandCardIcon = new CommandCardIcon( + "SmashInventoryButton_" + commandButtonIndex, this.inventoryBarFrame, + this.itemCommandCardCommandListener); + this.inventoryBarFrame.add(commandCardIcon); + final TextureFrame iconFrame = new TextureFrame( + "SmashInventoryButton_" + (commandButtonIndex) + "_Icon", this.rootFrame, false, null); + final SpriteFrame cooldownFrame = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE", + "SmashInventoryButton_" + (commandButtonIndex) + "_Cooldown", this.rootFrame, "", 0); + commandCardIcon.addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.inventoryBarFrame, + FramePoint.TOPRIGHT, GameUI.convertX(this.uiViewport, 0.0187f + (0.04f * i)), + GameUI.convertY(this.uiViewport, -0.0021f - (0.03815f * j)))); + commandCardIcon.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_INVENTORY_ICON_WIDTH)); + commandCardIcon.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_INVENTORY_ICON_WIDTH)); + iconFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0)); + iconFrame.setWidth(GameUI.convertX(this.uiViewport, DEFAULT_INVENTORY_ICON_WIDTH)); + iconFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_INVENTORY_ICON_WIDTH)); + iconFrame.setTexture(ImageUtils.DEFAULT_ICON_PATH, 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_INVENTORY_ICON_WIDTH)); + cooldownFrame.setHeight(GameUI.convertY(this.uiViewport, DEFAULT_INVENTORY_ICON_WIDTH)); + commandCardIcon.set(iconFrame, null, cooldownFrame, null); + this.inventoryIcons[j][i] = commandCardIcon; + commandCardIcon.clear(); + commandButtonIndex++; + } + } + this.inventoryTitleFrame = this.rootFrame.createStringFrame("SmashInventoryText", this.inventoryBarFrame, + new Color(0xFCDE12FF), TextJustify.LEFT, TextJustify.MIDDLE, 0.0109f); + this.rootFrame.setText(this.inventoryTitleFrame, this.rootFrame.getTemplates().getDecoratedString("INVENTORY")); + this.inventoryTitleFrame + .addSetPoint(new SetPoint(FramePoint.TOPLEFT, this.inventoryBarFrame, FramePoint.TOPRIGHT, + GameUI.convertX(this.uiViewport, 0.029f), GameUI.convertY(this.uiViewport, 0.0165625f))); + this.inventoryTitleFrame.setWidth(GameUI.convertX(this.uiViewport, 0.076875f)); + this.inventoryTitleFrame.setHeight(GameUI.convertX(this.uiViewport, 0.01125f)); + this.inventoryTitleFrame.setFontShadowColor(new Color(0f, 0f, 0f, 0.9f)); + this.inventoryTitleFrame.setFontShadowOffsetX(GameUI.convertX(this.uiViewport, 0.001f)); + this.inventoryTitleFrame.setFontShadowOffsetY(GameUI.convertY(this.uiViewport, -0.001f)); + this.consoleInventoryNoCapacityTexture = ImageUtils.getAnyExtensionTexture(this.dataSource, + this.rootFrame.getSkinField("ConsoleInventoryNoCapacity")); + this.inventoryCover = this.rootFrame.createSimpleFrame("SmashConsoleInventoryCover", this.rootFrame, 0); final Element fontHeights = this.war3MapViewer.miscData.get("FontHeights"); @@ -592,7 +659,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.errorMessageFrame.setFontShadowOffsetY(GameUI.convertY(this.uiViewport, -0.001f)); this.errorMessageFrame.setVisible(false); - int commandButtonIndex = 0; + 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, @@ -631,7 +698,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma 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); + commandCardIcon.clear(); commandButtonIndex++; } } @@ -713,7 +780,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } @Override - public void startUsingAbility(final int abilityHandleId, final int orderId, final boolean rightClick) { + public void onClick(final int abilityHandleId, final int orderId, final boolean rightClick) { // TODO not O(N) if (this.selectedUnit == null) { return; @@ -776,8 +843,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma public void showCommandError(final String message) { this.rootFrame.setText(this.errorMessageFrame, message); this.errorMessageFrame.setVisible(true); - this.lastErrorMessageExpireTime = TimeUtils.millis() + WORLD_FRAME_MESSAGE_EXPIRE_MILLIS; - this.lastErrorMessageFadeTime = TimeUtils.millis() + WORLD_FRAME_MESSAGE_FADEOUT_MILLIS; + final long millis = TimeUtils.millis(); + this.lastErrorMessageExpireTime = millis + WORLD_FRAME_MESSAGE_EXPIRE_MILLIS; + this.lastErrorMessageFadeTime = millis + WORLD_FRAME_MESSAGE_FADEOUT_MILLIS; this.errorMessageFrame.setAlpha(1.0f); } @@ -795,6 +863,13 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma .play(this.uiScene.audioContext, 0, 0, 0); } + @Override + public void showInventoryFullError() { + showCommandError(this.rootFrame.getErrorString("InventoryFull")); + this.war3MapViewer.getUiSounds().getSound(this.rootFrame.getSkinField("InventoryFullSound")) + .play(this.uiScene.audioContext, 0, 0, 0); + } + public void update(final float deltaTime) { this.portrait.update(); @@ -824,7 +899,12 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.cursorFrame.setFramePointY(FramePoint.BOTTOM, screenCoordsVector.y); if (this.activeCommand != null) { - this.activeCommand.visit(this.cursorTargetSetupVisitor.reset(baseMouseX, baseMouseY)); + if (this.draggingItem != null) { + this.cursorFrame.setSequence("HoldItem"); + } + else { + this.activeCommand.visit(this.cursorTargetSetupVisitor.reset(baseMouseX, baseMouseY)); + } } else { if (this.cursorModelInstance != null) { @@ -1349,6 +1429,52 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } } + public void setDraggingItem(final CItem itemInSlot) { + this.draggingItem = itemInSlot; + if (itemInSlot != null) { + final String iconPath = this.war3MapViewer.getAbilityDataUI().getItemUI(itemInSlot.getTypeId()) + .getItemIconPathForDragging(); + this.cursorFrame.setReplaceableId(21, this.war3MapViewer.blp(iconPath)); + + int index = 0; + final CAbilityInventory inventory = this.selectedUnit.getSimulationUnit().getInventoryData(); + for (int i = 0; i < INVENTORY_HEIGHT; i++) { + for (int j = 0; j < INVENTORY_WIDTH; j++) { + final CommandCardIcon inventoryIcon = this.inventoryIcons[i][j]; + final CItem item = inventory.getItemInSlot(index); + if (item == null) { + if (index < inventory.getItemCapacity()) { + inventoryIcon.setCommandButtonData(null, 0, 0, index + 1, true, false, false, null, null, 0, + 0, 0); + } + } + index++; + } + } + } + else { + if (this.selectedUnit != null) { + final CAbilityInventory inventory = this.selectedUnit.getSimulationUnit().getInventoryData(); + if (inventory != null) { + int index = 0; + for (int i = 0; i < INVENTORY_HEIGHT; i++) { + for (int j = 0; j < INVENTORY_WIDTH; j++) { + final CommandCardIcon inventoryIcon = this.inventoryIcons[i][j]; + final CItem item = inventory.getItemInSlot(index); + if (item == null) { + if (index < inventory.getItemCapacity()) { + inventoryIcon.clear(); + } + } + index++; + } + } + } + } + + } + } + public void selectUnit(RenderUnit unit) { this.subMenuOrderIdStack.clear(); if ((unit != null) && unit.getSimulationUnit().isDead()) { @@ -1359,6 +1485,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } this.portrait.setSelectedUnit(unit); this.selectedUnit = unit; + setDraggingItem(null); if (unit == null) { clearCommandCard(); this.rootFrame.setText(this.simpleNameValue, ""); @@ -1386,6 +1513,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.heroInfoPanel.setVisible(false); this.rallyPointInstance.hide(); this.rallyPointInstance.detach(); + this.inventoryCover.setVisible(true); + this.inventoryBarFrame.setVisible(false); repositionWaypointFlags(null); } else { @@ -1518,12 +1647,16 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma final IconUI upgradeUI = this.war3MapViewer.getAbilityDataUI() .getUpgradeUI(simulationUnit.getBuildQueue()[i], 0); this.queueIconFrames[i].setTexture(upgradeUI.getIcon()); + this.queueIconFrames[i].setToolTip(upgradeUI.getToolTip()); + this.queueIconFrames[i].setUberTip(upgradeUI.getUberTip()); break; case UNIT: default: final IconUI unitUI = this.war3MapViewer.getAbilityDataUI() .getUnitUI(simulationUnit.getBuildQueue()[i]); this.queueIconFrames[i].setTexture(unitUI.getIcon()); + this.queueIconFrames[i].setToolTip(unitUI.getToolTip()); + this.queueIconFrames[i].setUberTip(unitUI.getUberTip()); break; } } @@ -1662,6 +1795,59 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } this.simpleHeroLevelBar.setVisible(false); } + final CAbilityInventory inventory = simulationUnit.getInventoryData(); + this.inventoryCover.setVisible(inventory == null); + if (inventory != null) { + this.inventoryBarFrame.setVisible(true); + int index = 0; + for (int i = 0; i < INVENTORY_HEIGHT; i++) { + for (int j = 0; j < INVENTORY_WIDTH; j++) { + final CommandCardIcon inventoryIcon = this.inventoryIcons[i][j]; + final CItem item = inventory.getItemInSlot(index); + if (item != null) { + final ItemUI itemUI = this.war3MapViewer.getAbilityDataUI().getItemUI(item.getTypeId()); + final IconUI iconUI = itemUI.getIconUI(); + final CItemType itemType = item.getItemType(); + // TODO: below we set menu=false, this is bad, item should be based on item abil + final boolean activelyUsed = itemType.isActivelyUsed(); + final boolean pawnable = itemType.isPawnable(); + final String uberTip = iconUI.getUberTip(); + this.recycleStringBuilder.setLength(0); + if (pawnable) { + this.recycleStringBuilder + .append(this.rootFrame.getTemplates().getDecoratedString("ITEM_PAWN_TOOLTIP")); + this.recycleStringBuilder.append("|n"); + } + if (activelyUsed) { + this.recycleStringBuilder + .append(this.rootFrame.getTemplates().getDecoratedString("ITEM_USE_TOOLTIP")); + this.recycleStringBuilder.append("|n"); + } + this.recycleStringBuilder.append(uberTip); + inventoryIcon.setCommandButtonData(iconUI.getIcon(), 0, + activelyUsed ? itemType.getCooldownGroup().getValue() : 0, index + 1, activelyUsed, + false, false, itemUI.getName(), this.recycleStringBuilder.toString(), + itemType.getGoldCost(), itemType.getLumberCost(), 0); + } + else { + if (index >= inventory.getItemCapacity()) { + inventoryIcon.setCommandButtonData(this.consoleInventoryNoCapacityTexture, 0, 0, 0, + false, false, false, null, null, 0, 0, 0); + } + else { + if (this.draggingItem != null) { + inventoryIcon.setCommandButtonData(null, 0, 0, index + 1, true, false, false, null, + null, 0, 0, 0); + } + else { + inventoryIcon.clear(); + } + } + } + index++; + } + } + } localArmorIcon.setVisible(!constructing); this.simpleBuildTimeIndicator.setVisible(constructing); @@ -1711,7 +1897,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma 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); + this.commandCard[j][i].clear(); } } } @@ -1865,6 +2051,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma reloadSelectedUnitUI(this.selectedUnit); } + @Override + public void inventoryChanged() { + reloadSelectedUnitUI(this.selectedUnit); + } + @Override public void queueChanged() { reloadSelectedUnitUI(this.selectedUnit); @@ -1874,7 +2065,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma clearCommandCard(); final AbilityDataUI abilityDataUI = this.war3MapViewer.getAbilityDataUI(); final int menuOrderId = getSubMenuOrderId(); - if (this.activeCommand != null) { + if ((this.activeCommand != null) && (this.draggingItem == null)) { final IconUI cancelUI = abilityDataUI.getCancelUI(); this.commandButton(cancelUI.getButtonPositionX(), cancelUI.getButtonPositionY(), cancelUI.getIcon(), 0, menuOrderId, 0, false, false, true, cancelUI.getToolTip(), cancelUI.getUberTip(), 0, 0, 0); @@ -1932,6 +2123,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.activeCommandUnit = null; this.activeCommand = null; this.activeCommandOrderId = -1; + if (this.draggingItem != null) { + setDraggingItem(null); + } clearAndRepopulateCommandCard(); } else { @@ -1966,44 +2160,65 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma 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.activeCommandOrderId == OrderIds.attack)) { - this.war3MapViewer.showConfirmation(clickLocationTemp, 1, 0, 0); - } - else { - this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); - } - this.unitOrderListener.issuePointOrder( + if (this.draggingItem != null) { + this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); + + this.unitOrderListener.issueDropItemAtPointOrder( this.activeCommandUnit.getSimulationUnit().getHandleId(), - this.activeCommand.getHandleId(), this.activeCommandOrderId, clickLocationTemp2.x, - clickLocationTemp2.y, shiftDown); + this.activeCommand.getHandleId(), this.activeCommandOrderId, + this.draggingItem.getHandleId(), 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, 0); - } - else if (this.activeCommand instanceof CAbilityRally) { - this.war3MapViewer.getUiSounds().getSound("RallyPointPlace") - .play(this.uiScene.audioContext, 0, 0, 0); - } - if (!shiftDown) { - this.subMenuOrderIdStack.clear(); - this.activeCommandUnit = null; - this.activeCommand = null; - this.activeCommandOrderId = -1; - clearAndRepopulateCommandCard(); - } - + this.activeCommandUnit = null; + this.activeCommand = null; + this.activeCommandOrderId = -1; + setDraggingItem(null); + clearAndRepopulateCommandCard(); } + else { + 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.activeCommandOrderId == OrderIds.attack)) { + 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, 0); + } + else if (this.activeCommand instanceof CAbilityRally) { + this.war3MapViewer.getUiSounds().getSound("RallyPointPlace") + .play(this.uiScene.audioContext, 0, 0, 0); + } + if (!shiftDown) { + this.subMenuOrderIdStack.clear(); + this.activeCommandUnit = null; + this.activeCommand = null; + this.activeCommandOrderId = -1; + clearAndRepopulateCommandCard(); + } + + } + } + } } } @@ -2224,45 +2439,51 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma final int lumberCost = this.mouseOverUIFrame.getToolTipLumberCost(); final int foodCost = this.mouseOverUIFrame.getToolTipFoodCost(); final String toolTip = this.mouseOverUIFrame.getToolTip(); - this.rootFrame.setText(this.tooltipUberTipText, this.mouseOverUIFrame.getUberTip()); - int resourceIndex = 0; - if (goldCost != 0) { - this.tooltipResourceFrames[resourceIndex].setVisible(true); - this.tooltipResourceIconFrames[resourceIndex].setTexture("ToolTipGoldIcon", this.rootFrame); - this.rootFrame.setText(this.tooltipResourceTextFrames[resourceIndex], Integer.toString(goldCost)); - resourceIndex++; - } - if (lumberCost != 0) { - this.tooltipResourceFrames[resourceIndex].setVisible(true); - this.tooltipResourceIconFrames[resourceIndex].setTexture("ToolTipLumberIcon", this.rootFrame); - this.rootFrame.setText(this.tooltipResourceTextFrames[resourceIndex], Integer.toString(lumberCost)); - resourceIndex++; - } - if (foodCost != 0) { - this.tooltipResourceFrames[resourceIndex].setVisible(true); - this.tooltipResourceIconFrames[resourceIndex].setTexture("ToolTipSupplyIcon", this.rootFrame); - this.rootFrame.setText(this.tooltipResourceTextFrames[resourceIndex], Integer.toString(foodCost)); - resourceIndex++; - } - for (int i = resourceIndex; i < this.tooltipResourceFrames.length; i++) { - this.tooltipResourceFrames[i].setVisible(false); - } - float resourcesHeight; - if (resourceIndex != 0) { - this.tooltipUberTipText.addSetPoint(this.uberTipWithResourcesSetPoint); - resourcesHeight = 0.014f; + final String uberTip = this.mouseOverUIFrame.getUberTip(); + if ((toolTip == null) || (uberTip == null)) { + this.tooltipFrame.setVisible(false); } else { - this.tooltipUberTipText.addSetPoint(this.uberTipNoResourcesSetPoint); - resourcesHeight = 0.004f; + this.rootFrame.setText(this.tooltipUberTipText, uberTip); + int resourceIndex = 0; + if (goldCost != 0) { + this.tooltipResourceFrames[resourceIndex].setVisible(true); + this.tooltipResourceIconFrames[resourceIndex].setTexture("ToolTipGoldIcon", this.rootFrame); + this.rootFrame.setText(this.tooltipResourceTextFrames[resourceIndex], Integer.toString(goldCost)); + resourceIndex++; + } + if (lumberCost != 0) { + this.tooltipResourceFrames[resourceIndex].setVisible(true); + this.tooltipResourceIconFrames[resourceIndex].setTexture("ToolTipLumberIcon", this.rootFrame); + this.rootFrame.setText(this.tooltipResourceTextFrames[resourceIndex], Integer.toString(lumberCost)); + resourceIndex++; + } + if (foodCost != 0) { + this.tooltipResourceFrames[resourceIndex].setVisible(true); + this.tooltipResourceIconFrames[resourceIndex].setTexture("ToolTipSupplyIcon", this.rootFrame); + this.rootFrame.setText(this.tooltipResourceTextFrames[resourceIndex], Integer.toString(foodCost)); + resourceIndex++; + } + for (int i = resourceIndex; i < this.tooltipResourceFrames.length; i++) { + this.tooltipResourceFrames[i].setVisible(false); + } + float resourcesHeight; + if (resourceIndex != 0) { + this.tooltipUberTipText.addSetPoint(this.uberTipWithResourcesSetPoint); + resourcesHeight = 0.014f; + } + else { + this.tooltipUberTipText.addSetPoint(this.uberTipNoResourcesSetPoint); + resourcesHeight = 0.004f; + } + this.rootFrame.setText(this.tooltipText, toolTip); + final float predictedViewportHeight = this.tooltipText.getPredictedViewportHeight() + + GameUI.convertY(this.uiViewport, resourcesHeight) + + this.tooltipUberTipText.getPredictedViewportHeight() + GameUI.convertY(this.uiViewport, 0.003f); + this.tooltipFrame.setHeight(predictedViewportHeight); + this.tooltipFrame.positionBounds(this.rootFrame, this.uiViewport); + this.tooltipFrame.setVisible(true); } - this.rootFrame.setText(this.tooltipText, toolTip); - final float predictedViewportHeight = this.tooltipText.getPredictedViewportHeight() - + GameUI.convertY(this.uiViewport, resourcesHeight) - + this.tooltipUberTipText.getPredictedViewportHeight() + GameUI.convertY(this.uiViewport, 0.003f); - this.tooltipFrame.setHeight(predictedViewportHeight); - this.tooltipFrame.positionBounds(this.rootFrame, this.uiViewport); - this.tooltipFrame.setVisible(true); } public float getHeightRatioCorrection() { @@ -2309,4 +2530,39 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.rootFrame.dispose(); } } + + private class ItemCommandCardCommandListener implements CommandCardCommandListener { + @Override + public void onClick(final int abilityHandleId, final int orderId, final boolean rightClick) { + if (rightClick) { + final RenderUnit selectedUnit2 = MeleeUI.this.selectedUnit; + final CUnit simulationUnit = selectedUnit2.getSimulationUnit(); + final CAbilityInventory inventoryData = simulationUnit.getInventoryData(); + final int slot = orderId - 1; + final CItem itemInSlot = inventoryData.getItemInSlot(slot); + if (MeleeUI.this.draggingItem != null) { + final CUnit activeCmdSimUnit = MeleeUI.this.activeCommandUnit.getSimulationUnit(); + MeleeUI.this.unitOrderListener.issueTargetOrder(activeCmdSimUnit.getHandleId(), + activeCmdSimUnit.getInventoryData().getHandleId(), OrderIds.itemdrag00 + slot, + MeleeUI.this.draggingItem.getHandleId(), false); + setDraggingItem(null); + MeleeUI.this.activeCommand = null; + MeleeUI.this.activeCommandUnit = null; + } + else { + if (itemInSlot != null) { + setDraggingItem(itemInSlot); + MeleeUI.this.activeCommand = inventoryData; + MeleeUI.this.activeCommandUnit = selectedUnit2; + } + } + } + } + + @Override + public void openMenu(final int orderId) { + MeleeUI.this.openMenu(orderId); + } + + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java index fcaf00f..c3af169 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MeleeUIMinimap.java @@ -34,7 +34,7 @@ public class MeleeUIMinimap { batch.draw(this.minimapTexture, this.minimap.x, this.minimap.y, this.minimap.width, this.minimap.height); for (final RenderUnit unit : units) { - final Texture minimapIcon = this.teamColors[unit.playerIndex]; + final Texture minimapIcon = this.teamColors[unit.getSimulationUnit().getPlayerIndex()]; batch.draw(minimapIcon, this.minimapFilledArea.x + (((unit.location[0] - this.playableMapArea.getX()) / (this.playableMapArea.getWidth())) diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/QueueIcon.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/QueueIcon.java index 64d01df..fe090d2 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/QueueIcon.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/QueueIcon.java @@ -20,6 +20,9 @@ public class QueueIcon extends AbstractRenderableFrame implements ClickableActio private float defaultHeight; private final int queueIconIndexId; + private String toolTip; + private String uberTip; + public QueueIcon(final String name, final UIFrame parent, final QueueIconListener clickListener, final int queueIconIndexId) { super(name, parent); @@ -113,14 +116,22 @@ public class QueueIcon extends AbstractRenderableFrame implements ClickableActio return null; } + public void setToolTip(final String toolTip) { + this.toolTip = toolTip; + } + + public void setUberTip(final String uberTip) { + this.uberTip = uberTip; + } + @Override public String getToolTip() { - return "QueueIcon " + this.queueIconIndexId; + return this.toolTip; } @Override public String getUberTip() { - return "The |cffffcc00QueueIcon|r is a hardcoded Warsmash engine component that is not yet loading its |cffffaa88description|r."; + return this.uberTip; } @Override diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandCardCommandListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandCardCommandListener.java index 36dc718..fe003ab 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandCardCommandListener.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandCardCommandListener.java @@ -1,7 +1,7 @@ package com.etheller.warsmash.viewer5.handlers.w3x.ui.command; public interface CommandCardCommandListener { - void startUsingAbility(int abilityHandleId, int orderId, boolean rightClick); + void onClick(int abilityHandleId, int orderId, boolean rightClick); void openMenu(int orderId); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandErrorListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandErrorListener.java index ed33944..70fa4f3 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandErrorListener.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/CommandErrorListener.java @@ -6,4 +6,6 @@ public interface CommandErrorListener { void showCantPlaceError(); void showNoFoodError(); + + void showInventoryFullError(); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/SettableCommandErrorListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/SettableCommandErrorListener.java index 2cd025c..7bed78b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/SettableCommandErrorListener.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/command/SettableCommandErrorListener.java @@ -18,6 +18,11 @@ public class SettableCommandErrorListener implements CommandErrorListener { this.delegate.showNoFoodError(); } + @Override + public void showInventoryFullError() { + this.delegate.showInventoryFullError(); + } + public void setDelegate(final CommandErrorListener delegate) { this.delegate = delegate; } diff --git a/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxModel.java b/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxModel.java index 6929b1b..01511eb 100644 --- a/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxModel.java +++ b/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxModel.java @@ -393,7 +393,7 @@ public class MdlxModel { break; case MdlUtils.TOKEN_TEXTURE_ANIMS: loadNumberedObjectBlock(this.textureAnimations, MdlxBlockDescriptor.TEXTURE_ANIMATION, - MdlUtils.TOKEN_TEXTURE_ANIM, stream); + MdlUtils.TOKEN_TVERTEX_ANIM, stream); break; case MdlUtils.TOKEN_GEOSET: loadObject(this.geosets, MdlxBlockDescriptor.GEOSET, stream); diff --git a/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxTextureAnimation.java b/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxTextureAnimation.java index a61c04f..acbc889 100644 --- a/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxTextureAnimation.java +++ b/core/src/com/hiveworkshop/rms/parsers/mdlx/MdlxTextureAnimation.java @@ -42,7 +42,7 @@ public class MdlxTextureAnimation extends MdlxAnimatedObject { @Override public void writeMdl(final MdlTokenOutputStream stream, final int version) { - stream.startBlock(MdlUtils.TOKEN_TVERTEX_ANIM_SPACE); + stream.startBlock(MdlUtils.TOKEN_TVERTEX_ANIM); writeTimeline(stream, AnimationMap.KTAT); writeTimeline(stream, AnimationMap.KTAR); writeTimeline(stream, AnimationMap.KTAS); diff --git a/core/src/com/hiveworkshop/rms/parsers/mdlx/mdl/MdlUtils.java b/core/src/com/hiveworkshop/rms/parsers/mdlx/mdl/MdlUtils.java index b53f567..5f3f4fa 100644 --- a/core/src/com/hiveworkshop/rms/parsers/mdlx/mdl/MdlUtils.java +++ b/core/src/com/hiveworkshop/rms/parsers/mdlx/mdl/MdlUtils.java @@ -29,7 +29,7 @@ public class MdlUtils { public static final String TOKEN_WRAP_HEIGHT = "WrapHeight"; public static final String TOKEN_BITMAP = "Bitmap"; - public static final String TOKEN_TVERTEX_ANIM_SPACE = "TVertexAnim"; + public static final String TOKEN_TVERTEX_ANIM = "TVertexAnim"; public static final String TOKEN_DONT_INTERP = "DontInterp"; public static final String TOKEN_LINEAR = "Linear"; @@ -177,7 +177,6 @@ public class MdlUtils { public static final String TOKEN_TEXTURES = "Textures"; public static final String TOKEN_MATERIALS = "Materials"; public static final String TOKEN_TEXTURE_ANIMS = "TextureAnims"; - public static final String TOKEN_TEXTURE_ANIM = "TextureAnim"; public static final String TOKEN_PIVOT_POINTS = "PivotPoints"; public static final String TOKEN_ATTACHMENT = "Attachment"; @@ -198,7 +197,7 @@ public class MdlUtils { // > 800 public static final String TOKEN_EMISSIVE_GAIN = "EmissiveGain"; - public static final String TOKEN_FRESNEL_COLOR = "FresnelColor"; + public static final String TOKEN_FRESNEL_COLOR = "FresnelColor"; public static final String TOKEN_FRESNEL_OPACITY = "FresnelOpacity"; public static final String TOKEN_FRESNEL_TEAM_COLOR = "FresnelTeamColor"; } diff --git a/desktop/src/com/badlogic/gdx/backends/lwjgl/LwjglCanvas.java b/desktop/src/com/badlogic/gdx/backends/lwjgl/LwjglCanvas.java new file mode 100644 index 0000000..a1c915e --- /dev/null +++ b/desktop/src/com/badlogic/gdx/backends/lwjgl/LwjglCanvas.java @@ -0,0 +1,501 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.badlogic.gdx.backends.lwjgl; + +import java.awt.Canvas; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.util.HashMap; +import java.util.Map; + +import org.lwjgl.opengl.AWTGLCanvas; +import org.lwjgl.opengl.Display; + +import com.badlogic.gdx.Application; +import com.badlogic.gdx.ApplicationListener; +import com.badlogic.gdx.ApplicationLogger; +import com.badlogic.gdx.Audio; +import com.badlogic.gdx.Files; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Graphics; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.LifecycleListener; +import com.badlogic.gdx.Net; +import com.badlogic.gdx.Preferences; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Clipboard; +import com.badlogic.gdx.utils.SharedLibraryLoader; +import com.etheller.warsmash.audio.OpenALAudio; + +/** + * An OpenGL surface on an AWT Canvas, allowing OpenGL to be embedded in a Swing + * application. This uses {@link Display#setParent(Canvas)}, which is preferred + * over {@link AWTGLCanvas} but is limited to a single LwjglCanvas in an + * application. All OpenGL calls are done on the EDT. Note that you may need to + * call {@link #stop()} or a Swing application may deadlock on System.exit due + * to how LWJGL and/or Swing deal with shutdown hooks. + * + * @author Nathan Sweet + */ +public class LwjglCanvas implements Application { + static boolean isWindows = System.getProperty("os.name").contains("Windows"); + + LwjglGraphics graphics; + OpenALAudio audio; + LwjglFiles files; + LwjglInput input; + LwjglNet net; + ApplicationListener listener; + Canvas canvas; + final Array runnables = new Array(); + final Array executedRunnables = new Array(); + final Array lifecycleListeners = new Array(); + boolean running = true; + int logLevel = LOG_INFO; + ApplicationLogger applicationLogger; + Cursor cursor; + + public LwjglCanvas(final ApplicationListener listener) { + final LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); + initialize(listener, config); + } + + public LwjglCanvas(final ApplicationListener listener, final LwjglApplicationConfiguration config) { + initialize(listener, config); + } + + private void initialize(final ApplicationListener listener, final LwjglApplicationConfiguration config) { + LwjglNativesLoader.load(); + setApplicationLogger(new LwjglApplicationLogger()); + this.canvas = new Canvas() { + private final Dimension minSize = new Dimension(1, 1); + + @Override + public final void addNotify() { + super.addNotify(); + if (SharedLibraryLoader.isMac) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + create(); + } + }); + } + else { + create(); + } + } + + @Override + public final void removeNotify() { + stop(); + super.removeNotify(); + } + + @Override + public Dimension getMinimumSize() { + return this.minSize; + } + }; + this.canvas.setSize(1, 1); + this.canvas.setIgnoreRepaint(true); + + this.graphics = new LwjglGraphics(this.canvas, config) { + @Override + public void setTitle(final String title) { + super.setTitle(title); + LwjglCanvas.this.setTitle(title); + } + + public boolean setWindowedMode(final int width, final int height, final boolean fullscreen) { + if (!super.setWindowedMode(width, height)) { + return false; + } + if (!fullscreen) { + LwjglCanvas.this.setDisplayMode(width, height); + } + return true; + } + + @Override + public boolean setFullscreenMode(final DisplayMode displayMode) { + if (!super.setFullscreenMode(displayMode)) { + return false; + } + LwjglCanvas.this.setDisplayMode(displayMode.width, displayMode.height); + return true; + } + }; + this.graphics.setVSync(config.vSyncEnabled); + if (!LwjglApplicationConfiguration.disableAudio) { + this.audio = new OpenALAudio(); + } + this.files = new LwjglFiles(); + this.input = new LwjglInput(); + this.net = new LwjglNet(); + this.listener = listener; + + Gdx.app = this; + Gdx.graphics = this.graphics; + Gdx.audio = this.audio; + Gdx.files = this.files; + Gdx.input = this.input; + Gdx.net = this.net; + } + + protected void setDisplayMode(final int width, final int height) { + } + + protected void setTitle(final String title) { + } + + @Override + public ApplicationListener getApplicationListener() { + return this.listener; + } + + public Canvas getCanvas() { + return this.canvas; + } + + @Override + public Audio getAudio() { + return this.audio; + } + + @Override + public Files getFiles() { + return this.files; + } + + @Override + public Graphics getGraphics() { + return this.graphics; + } + + @Override + public Input getInput() { + return this.input; + } + + @Override + public Net getNet() { + return this.net; + } + + @Override + public ApplicationType getType() { + return ApplicationType.Desktop; + } + + @Override + public int getVersion() { + return 0; + } + + void create() { + try { + this.graphics.setupDisplay(); + + this.listener.create(); + this.listener.resize(Math.max(1, this.graphics.getWidth()), Math.max(1, this.graphics.getHeight())); + + start(); + } + catch (final Exception ex) { + stopped(); + exception(ex); + return; + } + + EventQueue.invokeLater(new Runnable() { + int lastWidth = Math.max(1, LwjglCanvas.this.graphics.getWidth()); + int lastHeight = Math.max(1, LwjglCanvas.this.graphics.getHeight()); + + @Override + public void run() { + if (!LwjglCanvas.this.running || Display.isCloseRequested()) { + LwjglCanvas.this.running = false; + stopped(); + return; + } + try { + Display.processMessages(); + if ((LwjglCanvas.this.cursor != null) || !isWindows) { + LwjglCanvas.this.canvas.setCursor(LwjglCanvas.this.cursor); + } + + boolean shouldRender = false; + + final int width = Math.max(1, LwjglCanvas.this.graphics.getWidth()); + final int height = Math.max(1, LwjglCanvas.this.graphics.getHeight()); + if ((this.lastWidth != width) || (this.lastHeight != height)) { + this.lastWidth = width; + this.lastHeight = height; + Gdx.gl.glViewport(0, 0, this.lastWidth, this.lastHeight); + resize(width, height); + LwjglCanvas.this.listener.resize(width, height); + shouldRender = true; + } + + if (executeRunnables()) { + shouldRender = true; + } + + // If one of the runnables set running to false, for example after an exit(). + if (!LwjglCanvas.this.running) { + return; + } + + LwjglCanvas.this.input.update(); + shouldRender |= LwjglCanvas.this.graphics.shouldRender(); + LwjglCanvas.this.input.processEvents(); + if (LwjglCanvas.this.audio != null) { + LwjglCanvas.this.audio.update(); + } + + if (shouldRender) { + LwjglCanvas.this.graphics.updateTime(); + LwjglCanvas.this.graphics.frameId++; + LwjglCanvas.this.listener.render(); + Display.update(false); + } + + Display.sync(getFrameRate()); + } + catch (final Throwable ex) { + exception(ex); + } + EventQueue.invokeLater(this); + } + }); + } + + public boolean executeRunnables() { + synchronized (this.runnables) { + for (int i = this.runnables.size - 1; i >= 0; i--) { + this.executedRunnables.addAll(this.runnables.get(i)); + } + this.runnables.clear(); + } + if (this.executedRunnables.size == 0) { + return false; + } + do { + this.executedRunnables.pop().run(); + } + while (this.executedRunnables.size > 0); + return true; + } + + protected int getFrameRate() { + int frameRate = Display.isActive() ? this.graphics.config.foregroundFPS : this.graphics.config.backgroundFPS; + if (frameRate == -1) { + frameRate = 10; + } + if (frameRate == 0) { + frameRate = this.graphics.config.backgroundFPS; + } + if (frameRate == 0) { + frameRate = 30; + } + return frameRate; + } + + protected void exception(final Throwable ex) { + ex.printStackTrace(); + stop(); + } + + /** + * Called after {@link ApplicationListener} create and resize, but before the + * game loop iteration. + */ + protected void start() { + } + + /** Called when the canvas size changes. */ + protected void resize(final int width, final int height) { + } + + /** Called when the game loop has stopped. */ + protected void stopped() { + } + + public void stop() { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + if (!LwjglCanvas.this.running) { + return; + } + LwjglCanvas.this.running = false; + final Array listeners = LwjglCanvas.this.lifecycleListeners; + synchronized (listeners) { + for (final LifecycleListener listener : listeners) { + listener.pause(); + listener.dispose(); + } + } + LwjglCanvas.this.listener.pause(); + LwjglCanvas.this.listener.dispose(); + try { + Display.destroy(); + if (LwjglCanvas.this.audio != null) { + LwjglCanvas.this.audio.dispose(); + } + } + catch (final Throwable ignored) { + } + } + }); + } + + @Override + public long getJavaHeap() { + return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); + } + + @Override + public long getNativeHeap() { + return getJavaHeap(); + } + + Map preferences = new HashMap(); + + @Override + public Preferences getPreferences(final String name) { + if (this.preferences.containsKey(name)) { + return this.preferences.get(name); + } + else { + final Preferences prefs = new LwjglPreferences(name, ".prefs/"); + this.preferences.put(name, prefs); + return prefs; + } + } + + @Override + public Clipboard getClipboard() { + return new LwjglClipboard(); + } + + @Override + public void postRunnable(final Runnable runnable) { + synchronized (this.runnables) { + this.runnables.add(runnable); + Gdx.graphics.requestRendering(); + } + } + + @Override + public void debug(final String tag, final String message) { + if (this.logLevel >= LOG_DEBUG) { + getApplicationLogger().debug(tag, message); + } + } + + @Override + public void debug(final String tag, final String message, final Throwable exception) { + if (this.logLevel >= LOG_DEBUG) { + getApplicationLogger().debug(tag, message, exception); + } + } + + @Override + public void log(final String tag, final String message) { + if (this.logLevel >= LOG_INFO) { + getApplicationLogger().log(tag, message); + } + } + + @Override + public void log(final String tag, final String message, final Throwable exception) { + if (this.logLevel >= LOG_INFO) { + getApplicationLogger().log(tag, message, exception); + } + } + + @Override + public void error(final String tag, final String message) { + if (this.logLevel >= LOG_ERROR) { + getApplicationLogger().error(tag, message); + } + } + + @Override + public void error(final String tag, final String message, final Throwable exception) { + if (this.logLevel >= LOG_ERROR) { + getApplicationLogger().error(tag, message, exception); + } + } + + @Override + public void setLogLevel(final int logLevel) { + this.logLevel = logLevel; + } + + @Override + public int getLogLevel() { + return this.logLevel; + } + + @Override + public void setApplicationLogger(final ApplicationLogger applicationLogger) { + this.applicationLogger = applicationLogger; + } + + @Override + public ApplicationLogger getApplicationLogger() { + return this.applicationLogger; + } + + @Override + public void exit() { + postRunnable(new Runnable() { + @Override + public void run() { + LwjglCanvas.this.listener.pause(); + LwjglCanvas.this.listener.dispose(); + if (LwjglCanvas.this.audio != null) { + LwjglCanvas.this.audio.dispose(); + } + System.exit(-1); + } + }); + } + + /** @param cursor May be null. */ + public void setCursor(final Cursor cursor) { + this.cursor = cursor; + } + + @Override + public void addLifecycleListener(final LifecycleListener listener) { + synchronized (this.lifecycleListeners) { + this.lifecycleListeners.add(listener); + } + } + + @Override + public void removeLifecycleListener(final LifecycleListener listener) { + synchronized (this.lifecycleListeners) { + this.lifecycleListeners.removeValue(listener, true); + } + } +}