diff --git a/core/src/com/etheller/warsmash/TestMain.java b/core/src/com/etheller/warsmash/TestMain.java index 18f1c2d..6ab5d22 100644 --- a/core/src/com/etheller/warsmash/TestMain.java +++ b/core/src/com/etheller/warsmash/TestMain.java @@ -2,6 +2,27 @@ package com.etheller.warsmash; public class TestMain { public static void main(final String[] args) { - System.out.println(Integer.parseInt("4294967295")); +// System.out.println(Integer.parseInt("4294967295")); + for (int i = 1; i <= 30; i++) { +// System.out.println(a(i)); + } + + int checkX = 0; + int checkY = 0; + for (int i = 0; i < 300; i++) { + System.out.println(checkX + "," + checkY); + final double angle = ((((int) Math.floor(Math.sqrt((4 * i) + 1))) % 4) * Math.PI) / 2; + checkX += (int) Math.sin(angle); + checkY += (int) Math.cos(angle); + } + } + + public static int a(final int n) { + if (n == 1) { + return 0; + } + else { + return a(n - 1) - (int) Math.sin(((Math.floor(Math.sqrt((4 * (n - 2)) + 1)) % 4) * Math.PI) / 2); + } } } diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java index c512c03..a1b8897 100644 --- a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -56,6 +56,7 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { private final Viewport viewport; private final Scene uiScene; private final AbstractMdxModelViewer modelViewer; + private final int racialCommandIndex; private final FrameTemplateEnvironment templates; private final Map pathToTexture = new HashMap<>(); private final boolean autoPosition = false; @@ -67,13 +68,15 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { private final Element errorStrings; public GameUI(final DataSource dataSource, final Element skin, final Viewport viewport, - final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final AbstractMdxModelViewer modelViewer) { + final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final AbstractMdxModelViewer modelViewer, + final int racialCommandIndex) { super("GameUI", null); this.dataSource = dataSource; this.skin = skin; this.viewport = viewport; this.uiScene = uiScene; this.modelViewer = modelViewer; + this.racialCommandIndex = racialCommandIndex; if (viewport instanceof ExtendViewport) { this.renderBounds.set(0, 0, ((ExtendViewport) viewport).getMinWorldWidth(), ((ExtendViewport) viewport).getMinWorldHeight()); @@ -162,7 +165,13 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { final String skinsField = main.getField("Skins"); final String[] skins = skinsField.split(","); final Element defaultSkin = skinsTable.get("Default"); - final Element userSkin = skinsTable.get(skins[skinIndex]); + final Element userSkin; + if ((skinIndex >= 0) && (skinIndex < skins.length)) { + userSkin = skinsTable.get(skins[skinIndex]); + } + else { + userSkin = new Element("UserSkin", skinsTable); + } final Element customSkin = skinsTable.get("CustomSkin"); for (final String key : defaultSkin.keySet()) { if (!userSkin.hasField(key)) { @@ -319,6 +328,11 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { simpleStatusBarFrame.add(inflate(childDefinition, simpleStatusBarFrame, frameDefinition, inDecorateFileNames || childDefinition.has("DecorateFileNames"))); } + final String barTexture = frameDefinition.getString("BarTexture"); + if (barTexture != null) { + simpleStatusBarFrame.getBarFrame().setTexture(barTexture, this); + simpleStatusBarFrame.getBorderFrame().setTexture(barTexture + "Border", this); + } inflatedFrame = simpleStatusBarFrame; } else if ("SPRITE".equals(frameDefinition.getFrameType())) { @@ -691,6 +705,6 @@ public final class GameUI extends AbstractUIFrame implements UIFrame { } public String getErrorString(final String key) { - return this.errorStrings.getField(key); + return this.errorStrings.getField(key, this.racialCommandIndex); } } diff --git a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java index 38f8540..15c6690 100644 --- a/core/src/com/etheller/warsmash/parsers/jass/Jass2.java +++ b/core/src/com/etheller/warsmash/parsers/jass/Jass2.java @@ -138,7 +138,7 @@ public class Jass2 { final String skinArg = arguments.get(0).visit(StringJassValueVisitor.getInstance()); final Element skin = GameUI.loadSkin(dataSource, skinArg); final GameUI gameUI = new GameUI(dataSource, skin, uiViewport, fontGenerator, uiScene, - war3MapViewer); + war3MapViewer, 0); JUIEnvironment.this.gameUI = gameUI; JUIEnvironment.this.skin = skin; rootFrameListener.onCreate(gameUI); diff --git a/core/src/com/etheller/warsmash/units/HashedGameObject.java b/core/src/com/etheller/warsmash/units/HashedGameObject.java index e80625b..3e9f109 100644 --- a/core/src/com/etheller/warsmash/units/HashedGameObject.java +++ b/core/src/com/etheller/warsmash/units/HashedGameObject.java @@ -124,6 +124,9 @@ public abstract class HashedGameObject implements GameObject { if (list.size() > index) { value = list.get(index); } + else if (list.size() > 0) { + value = list.get(list.size() - 1); + } } } return value; 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 46231d9..1b7dd28 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -295,6 +295,17 @@ public class War3MapViewer extends AbstractMdxModelViewer { this.miscData.readTXT(miscDataTxtStream, true); } } + final Element misc = this.miscData.get("Misc"); + // TODO Find the upkeep constants inside the assets files ????? + if (!misc.hasField("UpkeepUsage")) { + misc.setField("UpkeepUsage", "50,80,10000,10000,10000,10000,10000,10000,10000,10000"); + } + if (!misc.hasField("UpkeepGoldTax")) { + misc.setField("UpkeepGoldTax", "0.00,0.30,0.60,0.60,0.60,0.60,0.60,0.60,0.60,0.60"); + } + if (!misc.hasField("UpkeepLumberTax")) { + misc.setField("UpkeepLumberTax", "0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00"); + } final Element light = this.miscData.get("Light"); final float lightX = light.getFieldFloatValue("Direction", 0); final float lightY = light.getFieldFloatValue("Direction", 1); @@ -840,6 +851,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, unitAngle); } } + this.simulation.unitsLoaded(); this.terrain.loadSplats(); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CAbilityType.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CAbilityType.java new file mode 100644 index 0000000..de8b0cd --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CAbilityType.java @@ -0,0 +1,39 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation; + +import java.util.EnumSet; +import java.util.List; + +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; + +public class CAbilityType { + /* alias: defines which ability editor ability to use */ + private final War3ID alias; + /* code: defines which CAbility class to use */ + private final War3ID code; + + private final List levelData; + + public CAbilityType(final War3ID alias, final War3ID code, final List levelData) { + this.alias = alias; + this.code = code; + this.levelData = levelData; + } + + public War3ID getAlias() { + return this.alias; + } + + public War3ID getCode() { + return this.code; + } + + public EnumSet getTargetsAllowed(final int level) { + return getLevelData(level).getTargetsAllowed(); + } + + private CAbilityTypeLevelData getLevelData(final int level) { + return this.levelData.get(level); + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CAbilityTypeLevelData.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CAbilityTypeLevelData.java new file mode 100644 index 0000000..975abe7 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CAbilityTypeLevelData.java @@ -0,0 +1,17 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation; + +import java.util.EnumSet; + +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; + +public class CAbilityTypeLevelData { + private final EnumSet targetsAllowed; + + public CAbilityTypeLevelData(final EnumSet targetsAllowed) { + this.targetsAllowed = targetsAllowed; + } + + public EnumSet getTargetsAllowed() { + return this.targetsAllowed; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CPlayerStateListener.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CPlayerStateListener.java new file mode 100644 index 0000000..3fbea2e --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/CPlayerStateListener.java @@ -0,0 +1,44 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation; + +import com.etheller.warsmash.util.SubscriberSetNotifier; + +public interface CPlayerStateListener { + void goldChanged(); + + void lumberChanged(); + + void foodChanged(); + + void upkeepChanged(); + + public static final class CPlayerStateNotifier extends SubscriberSetNotifier + implements CPlayerStateListener { + @Override + public void goldChanged() { + for (final CPlayerStateListener listener : set) { + listener.goldChanged(); + } + } + + @Override + public void lumberChanged() { + for (final CPlayerStateListener listener : set) { + listener.lumberChanged(); + } + } + + @Override + public void foodChanged() { + for (final CPlayerStateListener listener : set) { + listener.foodChanged(); + } + } + + @Override + public void upkeepChanged() { + for (final CPlayerStateListener listener : set) { + listener.upkeepChanged(); + } + } + } +} 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 69f03c7..190951f 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 @@ -182,8 +182,7 @@ public class CSimulation { this.simulationRenderController.removeUnit(unit); } } - this.units.addAll(this.newUnits); - this.newUnits.clear(); + finishAddingNewUnits(); final Iterator projectileIterator = this.projectiles.iterator(); while (projectileIterator.hasNext()) { final CAttackProjectile projectile = projectileIterator.next(); @@ -198,6 +197,11 @@ public class CSimulation { % this.gameplayConstants.getGameDayLength(); } + private void finishAddingNewUnits() { + this.units.addAll(this.newUnits); + this.newUnits.clear(); + } + public float getGameTimeOfDay() { return (this.currentGameDayTimeElapsed / this.gameplayConstants.getGameDayLength()) * this.gameplayConstants.getGameDayHours(); @@ -254,4 +258,15 @@ public class CSimulation { public void unitRepositioned(final CUnit cUnit) { this.simulationRenderController.unitRepositioned(cUnit); } + + public void unitsLoaded() { + // called on startup after the system loads the map's units layer, but not any + // custom scripts yet + finishAddingNewUnits(); + for (final CUnit unit : this.units) { + final CPlayer player = this.players.get(unit.getPlayerIndex()); + player.setUnitFoodUsed(unit, unit.getUnitType().getFoodUsed()); + player.setUnitFoodMade(unit, unit.getUnitType().getFoodMade()); + } + } } 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 11c7e44..f785f84 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 @@ -94,6 +94,9 @@ public class CUnit extends CWidget { private final QueueItemType[] buildQueueTypes = new QueueItemType[WarsmashConstants.BUILD_QUEUE_SIZE]; private AbilityTarget rallyPoint; + private int foodMade; + private int foodUsed; + public CUnit(final int handleId, final int playerIndex, final float x, final float y, final float life, final War3ID typeId, final float facing, final float mana, final int maximumLife, final int maximumMana, final int speed, final int defense, final CUnitType unitType, @@ -248,6 +251,10 @@ public class CUnit extends CWidget { ability.setIconShowing(true); } } + if (this.unitType.getFoodMade() != 0) { + final CPlayer player = game.getPlayer(this.playerIndex); + player.setFoodCap(player.getFoodCap() + this.unitType.getFoodMade()); + } game.unitConstructFinishEvent(this); this.stateNotifier.ordersChanged(getCurrentAbilityHandleId(), getCurrentOrderId()); } @@ -263,6 +270,10 @@ public class CUnit extends CWidget { this.constructionProgress = 0; final CUnit trainedUnit = game.createUnit(queuedRawcode, this.playerIndex, getX(), getY(), game.getGameplayConstants().getBuildingAngle()); + // dont add food cost to player 2x + trainedUnit.setFoodUsed(trainedUnitType.getFoodUsed()); + game.getPlayer(this.playerIndex).setUnitFoodMade(trainedUnit, + trainedUnitType.getFoodMade()); // nudge the trained unit out around us trainedUnit.nudgeAround(game, this); game.unitTrainedEvent(this, trainedUnit); @@ -272,11 +283,9 @@ public class CUnit extends CWidget { UseAbilityOnTargetByIdVisitor.INSTANCE.reset(game, trainedUnit, rallyOrderId)); } for (int i = 0; i < (this.buildQueue.length - 1); i++) { - this.buildQueue[i] = this.buildQueue[i + 1]; - this.buildQueueTypes[i] = this.buildQueueTypes[i + 1]; + setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]); } - this.buildQueue[this.buildQueue.length - 1] = null; - this.buildQueueTypes[this.buildQueue.length - 1] = null; + setBuildQueueItem(game, this.buildQueue.length - 1, null, null); this.stateNotifier.queueChanged(); } } @@ -285,11 +294,9 @@ public class CUnit extends CWidget { if (this.constructionProgress >= trainedUnitType.getBuildTime()) { this.constructionProgress = 0; for (int i = 0; i < (this.buildQueue.length - 1); i++) { - this.buildQueue[i] = this.buildQueue[i + 1]; - this.buildQueueTypes[i] = this.buildQueueTypes[i + 1]; + setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]); } - this.buildQueue[this.buildQueue.length - 1] = null; - this.buildQueueTypes[this.buildQueue.length - 1] = null; + setBuildQueueItem(game, this.buildQueue.length - 1, null, null); this.stateNotifier.queueChanged(); } } @@ -617,6 +624,13 @@ public class CUnit extends CWidget { this.buildingShadowInstance = null; } popoutWorker(simulation); + final CPlayer player = simulation.getPlayer(this.playerIndex); + if (this.foodMade != 0) { + player.setUnitFoodMade(this, 0); + } + if (this.foodUsed != 0) { + player.setUnitFoodUsed(this, 0); + } } public boolean canReach(final AbilityTarget target, final float range) { @@ -898,14 +912,14 @@ public class CUnit extends CWidget { this.stateNotifier.lifeChanged(); } - private void queue(final War3ID rawcode, final QueueItemType queueItemType) { + private boolean queue(final CSimulation game, final War3ID rawcode, final QueueItemType queueItemType) { for (int i = 0; i < this.buildQueue.length; i++) { if (this.buildQueue[i] == null) { - this.buildQueue[i] = rawcode; - this.buildQueueTypes[i] = queueItemType; - break; + setBuildQueueItem(game, i, rawcode, queueItemType); + return true; } } + return false; } public War3ID[] getBuildQueue() { @@ -933,28 +947,62 @@ public class CUnit extends CWidget { public void cancelBuildQueueItem(final CSimulation game, final int cancelIndex) { if ((cancelIndex >= 0) && (cancelIndex < this.buildQueueTypes.length)) { - if (this.buildQueueTypes[cancelIndex] != null) { + final QueueItemType cancelledType = this.buildQueueTypes[cancelIndex]; + if (cancelledType != null) { // TODO refund here! if (cancelIndex == 0) { this.constructionProgress = 0.0f; + switch (cancelledType) { + case RESEARCH: + break; + case UNIT: + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]); + player.setFoodUsed(player.getFoodUsed() - unitType.getFoodUsed()); + break; + } + } + switch (cancelledType) { + case RESEARCH: + break; + case UNIT: + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]); + player.refundFor(unitType); + break; } for (int i = cancelIndex; i < (this.buildQueueTypes.length - 1); i++) { - this.buildQueueTypes[i] = this.buildQueueTypes[i + 1]; - this.buildQueue[i] = this.buildQueue[i + 1]; + setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]); } - this.buildQueueTypes[this.buildQueueTypes.length - 1] = null; - this.buildQueue[this.buildQueue.length - 1] = null; + setBuildQueueItem(game, this.buildQueue.length - 1, null, null); this.stateNotifier.queueChanged(); } } } - public void queueTrainingUnit(final War3ID rawcode) { - queue(rawcode, QueueItemType.UNIT); + public void setBuildQueueItem(final CSimulation game, final int index, final War3ID rawcode, + final QueueItemType queueItemType) { + this.buildQueue[index] = rawcode; + this.buildQueueTypes[index] = queueItemType; + if ((index == 0) && (rawcode != null) && (queueItemType == QueueItemType.UNIT)) { + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[index]); + if (unitType.getFoodUsed() != 0) { + player.setFoodUsed(player.getFoodUsed() + unitType.getFoodUsed()); + } + } } - public void queueResearch(final War3ID rawcode) { - queue(rawcode, QueueItemType.RESEARCH); + public void queueTrainingUnit(final CSimulation game, final War3ID rawcode) { + if (queue(game, rawcode, QueueItemType.UNIT)) { + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnitData().getUnitType(rawcode); + player.chargeFor(unitType); + } + } + + public void queueResearch(final CSimulation game, final War3ID rawcode) { + queue(game, rawcode, QueueItemType.RESEARCH); } public static enum QueueItemType { @@ -1049,4 +1097,24 @@ public class CUnit extends CWidget { return acceptWidget(this.game, this.trainedUnit, this.rallyOrderId, target); } } + + public int getFoodMade() { + return this.foodMade; + } + + public int getFoodUsed() { + return this.foodUsed; + } + + public int setFoodMade(final int foodMade) { + final int delta = foodMade - this.foodMade; + this.foodMade = foodMade; + return delta; + } + + public int setFoodUsed(final int foodUsed) { + final int delta = foodUsed - this.foodUsed; + this.foodUsed = foodUsed; + return delta; + } } 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 f8db48e..6cb05de 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 @@ -44,6 +44,8 @@ public class CUnitType { private final CUnitRace unitRace; private final int goldCost; private final int lumberCost; + private final int foodUsed; + private final int foodMade; private final int buildTime; private final EnumSet preventedPathingTypes; private final EnumSet requiredPathingTypes; @@ -55,7 +57,7 @@ public class CUnitType { final BufferedImage buildingPathingPixelMap, final float deathTime, final EnumSet targetedAs, final float defaultAcquisitionRange, final float minimumAttackRange, final List structuresBuilt, final List unitsTrained, final List researchesAvailable, final CUnitRace unitRace, - final int goldCost, final int lumberCost, final int buildTime, + final int goldCost, final int lumberCost, final int foodUsed, final int foodMade, final int buildTime, final EnumSet preventedPathingTypes, final EnumSet requiredPathingTypes) { this.name = name; @@ -81,6 +83,8 @@ public class CUnitType { this.unitRace = unitRace; this.goldCost = goldCost; this.lumberCost = lumberCost; + this.foodUsed = foodUsed; + this.foodMade = foodMade; this.buildTime = buildTime; this.preventedPathingTypes = preventedPathingTypes; this.requiredPathingTypes = requiredPathingTypes; @@ -178,6 +182,14 @@ public class CUnitType { return this.lumberCost; } + public int getFoodUsed() { + return this.foodUsed; + } + + public int getFoodMade() { + return this.foodMade; + } + public int getBuildTime() { return this.buildTime; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/AbstractCAbility.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/AbstractCAbility.java index 4b2a2d9..7aab18f 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/AbstractCAbility.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/AbstractCAbility.java @@ -19,22 +19,22 @@ public abstract class AbstractCAbility implements CAbility { } @Override - public boolean isDisabled() { + public final boolean isDisabled() { return this.disabled; } @Override - public void setDisabled(final boolean disabled) { + public final void setDisabled(final boolean disabled) { this.disabled = disabled; } @Override - public boolean isIconShowing() { + public final boolean isIconShowing() { return this.iconShowing; } @Override - public void setIconShowing(final boolean iconShowing) { + public final void setIconShowing(final boolean iconShowing) { this.iconShowing = iconShowing; } 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 b5e94b2..60cafd6 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 @@ -8,6 +8,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityV 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.orders.OrderIds; +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; @@ -29,6 +30,8 @@ public class CAbilityBuildInProgress extends AbstractCAbility { @Override public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + final CPlayer player = game.getPlayer(caster.getPlayerIndex()); + player.refundFor(caster.getUnitType()); caster.setLife(game, 0); return false; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityOrcBuild.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityOrcBuild.java index 27289ca..3967432 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityOrcBuild.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/build/CAbilityOrcBuild.java @@ -6,12 +6,14 @@ import java.util.List; import com.etheller.warsmash.util.War3ID; 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.CUnitType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; 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.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.build.CBehaviorOrcBuild; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; public class CAbilityOrcBuild extends AbstractCAbilityBuild { private CBehaviorOrcBuild buildBehavior; @@ -42,8 +44,9 @@ public class CAbilityOrcBuild extends AbstractCAbilityBuild { @Override public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final AbilityPointTarget point) { - final BufferedImage buildingPathingPixelMap = game.getUnitData().getUnitType(new War3ID(orderId)) - .getBuildingPathingPixelMap(); + final War3ID orderIdAsRawtype = new War3ID(orderId); + final CUnitType unitType = game.getUnitData().getUnitType(orderIdAsRawtype); + final BufferedImage buildingPathingPixelMap = unitType.getBuildingPathingPixelMap(); if (buildingPathingPixelMap != null) { point.x = (float) Math.floor(point.x / 64f) * 64f; point.y = (float) Math.floor(point.y / 64f) * 64f; @@ -54,6 +57,8 @@ public class CAbilityOrcBuild extends AbstractCAbilityBuild { point.y += 32f; } } + final CPlayer player = game.getPlayer(caster.getPlayerIndex()); + player.chargeFor(unitType); return this.buildBehavior.reset(point, orderId, getBaseOrderId()); } 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 new file mode 100644 index 0000000..7c36b20 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/AbstractGenericSingleIconActiveAbility.java @@ -0,0 +1,69 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic; + +import com.etheller.warsmash.util.War3ID; +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.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.util.AbilityTargetCheckReceiver; + +public abstract class AbstractGenericSingleIconActiveAbility extends AbstractCAbility + implements GenericSingleIconActiveAbility { + private final War3ID alias; + + public AbstractGenericSingleIconActiveAbility(final int handleId, final War3ID alias) { + super(handleId); + this.alias = alias; + } + + @Override + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) { + return true; + } + + @Override + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target, + final AbilityTargetCheckReceiver receiver) { + if (orderId == getBaseOrderId()) { + receiver.targetOk(target); + } + else { + receiver.orderIdNotAccepted(); + } + } + + @Override + public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityPointTarget target, final AbilityTargetCheckReceiver receiver) { + if (orderId == getBaseOrderId()) { + receiver.targetOk(target); + } + else { + receiver.orderIdNotAccepted(); + } + } + + @Override + public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityTargetCheckReceiver receiver) { + if (orderId == getBaseOrderId()) { + receiver.targetOk(null); + } + else { + receiver.orderIdNotAccepted(); + } + } + + @Override + public T visit(final CAbilityVisitor visitor) { + return visitor.accept(this); + } + + @Override + public War3ID getAlias() { + return this.alias; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/GenericSingleIconActiveAbility.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/GenericSingleIconActiveAbility.java index 096fb86..5a9f8ce 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/GenericSingleIconActiveAbility.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/generic/GenericSingleIconActiveAbility.java @@ -7,4 +7,6 @@ public interface GenericSingleIconActiveAbility extends CAbility { War3ID getAlias(); int getBaseOrderId(); + + boolean isToggleOn(); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/harvest/CAbilityHarvest.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/harvest/CAbilityHarvest.java new file mode 100644 index 0000000..f11a60e --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/harvest/CAbilityHarvest.java @@ -0,0 +1,62 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.harvest; + +import com.etheller.warsmash.util.War3ID; +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.AbstractGenericSingleIconActiveAbility; +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.orders.OrderIds; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType; + +public class CAbilityHarvest extends AbstractGenericSingleIconActiveAbility { + private int carriedResourceAmount; + private ResourceType carriedResourceType; + + public CAbilityHarvest(final int handleId, final War3ID alias) { + super(handleId, alias); + } + + @Override + public void onAdd(final CSimulation game, final CUnit unit) { + } + + @Override + public void onRemove(final CSimulation game, final CUnit unit) { + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) { + return caster.pollNextOrderBehavior(game); + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, + final AbilityPointTarget point) { + return caster.pollNextOrderBehavior(game); + } + + @Override + public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) { + return caster.pollNextOrderBehavior(game); + } + + @Override + public int getBaseOrderId() { + return OrderIds.harvest; + } + + @Override + public boolean isToggleOn() { + return this.carriedResourceAmount > 0; + } + + @Override + protected void innerCheckCanUse(final CSimulation game, final CUnit unit, final int orderId, + final AbilityActivationReceiver receiver) { + receiver.useOk(); + } + +} 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 2bd4d48..1b5acba 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 @@ -47,7 +47,12 @@ public final class CAbilityQueue extends AbstractCAbility { final CPlayer player = game.getPlayer(unit.getPlayerIndex()); if (player.getGold() >= unitType.getGoldCost()) { if (player.getLumber() >= unitType.getLumberCost()) { - receiver.useOk(); + if ((player.getFoodUsed() + unitType.getFoodUsed()) <= player.getFoodCap()) { + receiver.useOk(); + } + else { + receiver.notEnoughResources(ResourceType.FOOD); + } } else { receiver.notEnoughResources(ResourceType.LUMBER); @@ -127,10 +132,10 @@ public final class CAbilityQueue extends AbstractCAbility { else { final War3ID rawcode = new War3ID(orderId); if (this.unitsTrained.contains(rawcode)) { - caster.queueTrainingUnit(rawcode); + caster.queueTrainingUnit(game, rawcode); } else if (this.researchesAvailable.contains(rawcode)) { - caster.queueResearch(rawcode); + caster.queueResearch(game, rawcode); } } return null; 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 6d75cfe..42cb3ed 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 @@ -1,17 +1,49 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation.data; +import java.util.ArrayList; +import java.util.EnumSet; +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.CAbilityType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CAbilityTypeLevelData; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityGeneric; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.combat.CAbilityColdArrows; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; public class CAbilityData { + private static final War3ID TARGETS_ALLOWED = War3ID.fromString("atar"); + private static final War3ID LEVELS = War3ID.fromString("alev"); + private static final War3ID COLD_ARROWS = War3ID.fromString("ACcw"); private final MutableObjectData abilityData; + private Map aliasToAbilityType = new HashMap<>(); public CAbilityData(final MutableObjectData abilityData) { this.abilityData = abilityData; + this.aliasToAbilityType = new HashMap<>(); + } + + public CAbilityType getAbilityType(final War3ID alias) { + CAbilityType abilityType = this.aliasToAbilityType.get(alias); + if (abilityType == null) { + final MutableGameObject mutableGameObject = this.abilityData.get(alias); + final int levels = mutableGameObject.getFieldAsInteger(LEVELS, 0); + final List levelData = new ArrayList<>(); + for (int level = 0; level < levels; level++) { + final String targetsAllowedAtLevelString = mutableGameObject.getFieldAsString(TARGETS_ALLOWED, level); + final EnumSet targetsAllowedAtLevel = CTargetType + .parseTargetTypeSet(targetsAllowedAtLevelString); + levelData.add(new CAbilityTypeLevelData(targetsAllowedAtLevel)); + } + abilityType = new CAbilityType(alias, mutableGameObject.getCode(), levelData); + } + return abilityType; } public CAbility createAbility(final String ability, final int handleId) { 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 957e7d7..122d456 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 @@ -141,6 +141,8 @@ public class CUnitData { private static final War3ID GOLD_COST = War3ID.fromString("ugol"); private static final War3ID LUMBER_COST = War3ID.fromString("ulum"); private static final War3ID BUILD_TIME = War3ID.fromString("ubld"); + private static final War3ID FOOD_USED = War3ID.fromString("ufoo"); + private static final War3ID FOOD_MADE = War3ID.fromString("ufma"); private static final War3ID REQUIRE_PLACE = War3ID.fromString("upar"); private static final War3ID PREVENT_PLACE = War3ID.fromString("upap"); @@ -377,6 +379,8 @@ public class CUnitData { final int goldCost = unitType.getFieldAsInteger(GOLD_COST, 0); final int lumberCost = unitType.getFieldAsInteger(LUMBER_COST, 0); final int buildTime = unitType.getFieldAsInteger(BUILD_TIME, 0); + final int foodUsed = unitType.getFieldAsInteger(FOOD_USED, 0); + final int foodMade = unitType.getFieldAsInteger(FOOD_MADE, 0); final String unitsTrainedString = unitType.getFieldAsString(UNITS_TRAINED, 0); final String[] unitsTrainedStringItems = unitsTrainedString.trim().split(","); @@ -416,8 +420,8 @@ public class CUnitData { unitTypeInstance = new CUnitType(unitName, isBldg, movementType, moveHeight, collisionSize, classifications, attacks, armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime, targetedAs, acquisitionRange, minimumAttackRange, structuresBuilt, unitsTrained, - researchesAvailable, unitRace, goldCost, lumberCost, buildTime, preventedPathingTypes, - requiredPathingTypes); + researchesAvailable, unitRace, goldCost, lumberCost, foodUsed, foodMade, buildTime, + preventedPathingTypes, requiredPathingTypes); this.unitIdToUnitType.put(typeId, unitTypeInstance); } return unitTypeInstance; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayer.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayer.java index 98747ea..078c8f2 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/players/CPlayer.java @@ -6,6 +6,10 @@ import java.util.Map; import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.util.WarsmashConstants; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener.CPlayerStateNotifier; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType; public class CPlayer { private final int id; @@ -15,11 +19,18 @@ public class CPlayer { private final CRace race; private final float[] startLocation; private final EnumSet racePrefs; - private int gold = 5000; - private int lumber = 5000; + private int gold = 500; + private int lumber = 150; + private int foodCap; + private int foodUsed; private final EnumSet[] alliances = new EnumSet[WarsmashConstants.MAX_PLAYERS]; private final Map rawcodeToTechtreeUnlocked = new HashMap<>(); + // if you use triggers for this then the transient tag here becomes really + // questionable -- it already was -- but I meant for those to inform us + // which fields shouldn't be persisted if we do game state save later + private transient CPlayerStateNotifier stateNotifier = new CPlayerStateNotifier(); + public CPlayer(final int id, final CMapControl controlType, final String name, final CRace race, final float[] startLocation) { this.id = id; @@ -95,16 +106,36 @@ public class CPlayer { return this.lumber; } + public int getFoodCap() { + return this.foodCap; + } + + public int getFoodUsed() { + return this.foodUsed; + } + public float[] getStartLocation() { return this.startLocation; } public void setGold(final int gold) { this.gold = gold; + this.stateNotifier.goldChanged(); } public void setLumber(final int lumber) { this.lumber = lumber; + this.stateNotifier.lumberChanged(); + } + + public void setFoodCap(final int foodCap) { + this.foodCap = foodCap; + this.stateNotifier.foodChanged(); + } + + public void setFoodUsed(final int foodUsed) { + this.foodUsed = foodUsed; + this.stateNotifier.foodChanged(); } public void setColorIndex(final int colorIndex) { @@ -118,4 +149,36 @@ public class CPlayer { } return techtreeUnlocked; } + + public void addStateListener(final CPlayerStateListener listener) { + this.stateNotifier.subscribe(listener); + } + + public void removeStateListener(final CPlayerStateListener listener) { + this.stateNotifier.unsubscribe(listener); + } + + public void chargeFor(final CUnitType unitType) { + this.lumber -= unitType.getLumberCost(); + this.gold -= unitType.getGoldCost(); + this.stateNotifier.lumberChanged(); + this.stateNotifier.goldChanged(); + } + + public void refundFor(final CUnitType unitType) { + this.lumber += unitType.getLumberCost(); + this.gold += unitType.getGoldCost(); + this.stateNotifier.lumberChanged(); + this.stateNotifier.goldChanged(); + } + + public void setUnitFoodUsed(final CUnit unit, final int foodUsed) { + this.foodUsed += unit.setFoodUsed(foodUsed); + this.stateNotifier.foodChanged(); + } + + public void setUnitFoodMade(final CUnit unit, final int foodMade) { + this.foodCap += unit.setFoodMade(foodMade); + this.stateNotifier.foodChanged(); + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityActivationErrorHandler.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityActivationErrorHandler.java new file mode 100644 index 0000000..fafc5b2 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/AbilityActivationErrorHandler.java @@ -0,0 +1,22 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.util; + +import com.etheller.warsmash.viewer5.AudioContext; +import com.etheller.warsmash.viewer5.handlers.w3x.UnitSound; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener; + +public class AbilityActivationErrorHandler { + private final String errorString; + private final UnitSound errorSound; + + public AbilityActivationErrorHandler(final String errorString, final UnitSound errorSound) { + this.errorString = errorString; + this.errorSound = errorSound; + } + + public void onClick(final CommandErrorListener commandErrorListener, final AudioContext worldSceneAudioContext, + final RenderUnit commandedUnit) { + commandErrorListener.showCommandError(this.errorString); + this.errorSound.playUnitResponse(worldSceneAudioContext, commandedUnit); + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/MeleeUIAbilityActivationReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/MeleeUIAbilityActivationReceiver.java new file mode 100644 index 0000000..7012c8b --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/MeleeUIAbilityActivationReceiver.java @@ -0,0 +1,85 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.util; + +import com.etheller.warsmash.viewer5.AudioContext; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; +import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener; + +public class MeleeUIAbilityActivationReceiver implements AbilityActivationReceiver { + private final AbilityActivationErrorHandler noGoldError; + private final AbilityActivationErrorHandler noLumberError; + private final AbilityActivationErrorHandler noFoodError; + private final AbilityActivationErrorHandler genericError; + + private boolean ok = false; + private CommandErrorListener commandErrorListener; + private AudioContext worldSceneAudioContext; + private RenderUnit commandedUnit; + + public MeleeUIAbilityActivationReceiver(final AbilityActivationErrorHandler noGoldError, + final AbilityActivationErrorHandler noLumberError, final AbilityActivationErrorHandler noFoodError, + final AbilityActivationErrorHandler genericError) { + this.noGoldError = noGoldError; + this.noLumberError = noLumberError; + this.noFoodError = noFoodError; + this.genericError = genericError; + } + + public MeleeUIAbilityActivationReceiver reset(final CommandErrorListener commandErrorListener, + final AudioContext worldSceneAudioContext, final RenderUnit commandedUnit) { + this.commandErrorListener = commandErrorListener; + this.worldSceneAudioContext = worldSceneAudioContext; + this.commandedUnit = commandedUnit; + this.ok = false; + return this; + } + + @Override + public void useOk() { + this.ok = true; + } + + @Override + public void notEnoughResources(final ResourceType resource) { + switch (resource) { + case GOLD: + this.noGoldError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + break; + case LUMBER: + this.noLumberError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + break; + case FOOD: + this.noFoodError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + break; + } + } + + @Override + public void notAnActiveAbility() { + this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + } + + @Override + public void missingRequirement(final String name) { + this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + } + + @Override + public void casterMovementDisabled() { + this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + } + + @Override + public void cargoCapacityUnavailable() { + this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + } + + @Override + public void disabled() { + this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); + } + + public boolean isUseOk() { + return this.ok; + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/ResourceType.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/ResourceType.java index dd123b7..a375252 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/ResourceType.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/ResourceType.java @@ -2,6 +2,7 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation.util; public enum ResourceType { GOLD, - LUMBER; + LUMBER, + FOOD; } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/StringMsgAbilityActivationReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/StringMsgAbilityActivationReceiver.java index 9e8b8d9..5ec8516 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/StringMsgAbilityActivationReceiver.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/StringMsgAbilityActivationReceiver.java @@ -1,12 +1,6 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation.util; public class StringMsgAbilityActivationReceiver implements AbilityActivationReceiver { - private static final StringMsgAbilityActivationReceiver INSTANCE = new StringMsgAbilityActivationReceiver(); - - public static StringMsgAbilityActivationReceiver getInstance() { - return INSTANCE; - } - private String message; private boolean useOk = false; 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 3695ad6..93d4aef 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 @@ -78,6 +78,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI; 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.CItem; +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; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; @@ -113,19 +114,22 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUni import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissileSplash; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CBuildingPathingType; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerUnitOrderListener; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationErrorHandler; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityTargetCheckReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.CWidgetAbilityTargetCheckReceiver; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.MeleeUIAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.PointAbilityTargetCheckReceiver; -import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.ClickableActionFrame; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandCardCommandListener; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.QueueIconListener; public class MeleeUI implements CUnitStateListener, CommandButtonListener, CommandCardCommandListener, - QueueIconListener, CommandErrorListener { + QueueIconListener, CommandErrorListener, CPlayerStateListener { private static final long WORLD_FRAME_MESSAGE_FADEOUT_MILLIS = TimeUnit.SECONDS.toMillis(9); private static final long WORLD_FRAME_MESSAGE_EXPIRE_MILLIS = TimeUnit.SECONDS.toMillis(10); private static final long WORLD_FRAME_MESSAGE_FADE_DURATION = WORLD_FRAME_MESSAGE_EXPIRE_MILLIS @@ -234,6 +238,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma private SimpleFrame smashAttack2IconWrapper; private SimpleFrame smashArmorIconWrapper; private final RallyPositioningVisitor rallyPositioningVisitor; + private final CPlayer localPlayer; + private MeleeUIAbilityActivationReceiver meleeUIAbilityActivationReceiver; public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport, final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final Scene portraitScene, @@ -251,8 +257,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.cameraManager = new GameCameraManager(cameraPresets, cameraRates); this.cameraManager.setupCamera(war3MapViewer.worldScene); - final float[] startLocation = this.war3MapViewer.simulation.getPlayer(war3MapViewer.getLocalPlayerIndex()) - .getStartLocation(); + this.localPlayer = this.war3MapViewer.simulation.getPlayer(war3MapViewer.getLocalPlayerIndex()); + final float[] startLocation = this.localPlayer.getStartLocation(); this.cameraManager.target.x = startLocation[0]; this.cameraManager.target.y = startLocation[1]; @@ -263,6 +269,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.heightRatioCorrection = this.uiViewport.getMinWorldHeight() / 1200f; this.rallyPositioningVisitor = new RallyPositioningVisitor(); this.cursorTargetSetupVisitor = new CursorTargetSetupVisitor(); + + this.localPlayer.addStateListener(this); } private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) { @@ -307,8 +315,35 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma // ================================= // Load skins and templates // ================================= - this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 0), this.uiViewport, - this.fontGenerator, this.uiScene, this.war3MapViewer); + final CRace race = this.localPlayer.getRace(); + final int racialSkinIndex; + int racialCommandIndex; + switch (race) { + case HUMAN: + racialSkinIndex = 1; + racialCommandIndex = 0; + break; + case ORC: + racialSkinIndex = 0; + racialCommandIndex = 1; + break; + case NIGHTELF: + racialSkinIndex = 2; + racialCommandIndex = 3; + break; + case UNDEAD: + racialSkinIndex = 3; + racialCommandIndex = 2; + break; + case DEMON: + case OTHER: + default: + racialSkinIndex = -1; + racialCommandIndex = 0; + break; + } + this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, racialSkinIndex), this.uiViewport, + this.fontGenerator, this.uiScene, this.war3MapViewer, racialCommandIndex); this.rootFrameListener.onCreate(this.rootFrame); try { this.rootFrame.loadTOCFile("UI\\FrameDef\\FrameDef.toc"); @@ -338,14 +373,13 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.resourceBar = this.rootFrame.createSimpleFrame("ResourceBarFrame", this.consoleUI, 0); this.resourceBar.addSetPoint(new SetPoint(FramePoint.TOPRIGHT, this.consoleUI, FramePoint.TOPRIGHT, 0, 0)); this.resourceBarGoldText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarGoldText", 0); - this.resourceBarGoldText.setText("500"); + goldChanged(); this.resourceBarLumberText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarLumberText", 0); - this.resourceBarLumberText.setText("150"); + lumberChanged(); this.resourceBarSupplyText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarSupplyText", 0); - this.resourceBarSupplyText.setText("12/100"); + foodChanged(); this.resourceBarUpkeepText = (StringFrame) this.rootFrame.getFrameByName("ResourceBarUpkeepText", 0); - this.resourceBarUpkeepText.setText("No Upkeep"); - this.resourceBarUpkeepText.setColor(Color.GREEN); + upkeepChanged(); // Create the Time Indicator (clock) this.timeIndicator = (SpriteFrame) this.rootFrame.createFrame("TimeOfDayIndicator", this.rootFrame, 0, 0); @@ -559,6 +593,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.meleeUIMinimap = createMinimap(this.war3MapViewer); + this.meleeUIAbilityActivationReceiver = new MeleeUIAbilityActivationReceiver( + new AbilityActivationErrorHandler(this.rootFrame.getErrorString("NoGold"), + this.war3MapViewer.getUiSounds().getSound(this.rootFrame.getSkinField("NoGoldSound"))), + new AbilityActivationErrorHandler(this.rootFrame.getErrorString("NoLumber"), + this.war3MapViewer.getUiSounds().getSound(this.rootFrame.getSkinField("NoLumberSound"))), + new AbilityActivationErrorHandler(this.rootFrame.getErrorString("NoFood"), + this.war3MapViewer.getUiSounds().getSound(this.rootFrame.getSkinField("NoFoodSound"))), + new AbilityActivationErrorHandler("", this.war3MapViewer.getUiSounds().getSound("InterfaceError"))); + final MdxModel rallyModel = (MdxModel) this.war3MapViewer.load( War3MapViewer.mdx(this.rootFrame.getSkinField("RallyIndicatorDst")), this.war3MapViewer.mapPathSolver, this.war3MapViewer.solverParams); @@ -591,14 +634,10 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } } if (abilityToUse != null) { - final StringMsgAbilityActivationReceiver stringMsgActivationReceiver = StringMsgAbilityActivationReceiver - .getInstance().reset(); abilityToUse.checkCanUse(this.war3MapViewer.simulation, this.selectedUnit.getSimulationUnit(), orderId, - stringMsgActivationReceiver); - if (!stringMsgActivationReceiver.isUseOk()) { - showCommandError(stringMsgActivationReceiver.getMessage()); - } - else { + this.meleeUIAbilityActivationReceiver.reset(this, this.war3MapViewer.worldScene.audioContext, + this.selectedUnit)); + if (this.meleeUIAbilityActivationReceiver.isUseOk()) { final BooleanAbilityTargetCheckReceiver noTargetReceiver = BooleanAbilityTargetCheckReceiver .getInstance().reset(); abilityToUse.checkCanTargetNoTarget(this.war3MapViewer.simulation, @@ -1026,7 +1065,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma int index = -1; for (int i = 0; i < model.attachments.size(); i++) { final Attachment attachment = model.attachments.get(i); - if (attachment.getName().startsWith("sprite first ref")) { + if (attachment.getName().startsWith("sprite")) { index = i; break; } @@ -1475,6 +1514,29 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } } + @Override + public void goldChanged() { + this.resourceBarGoldText.setText(Integer.toString(this.localPlayer.getGold())); + } + + @Override + public void lumberChanged() { + this.resourceBarLumberText.setText(Integer.toString(this.localPlayer.getLumber())); + } + + @Override + public void foodChanged() { + this.resourceBarSupplyText.setText(this.localPlayer.getFoodUsed() + "/" + this.localPlayer.getFoodCap()); + this.resourceBarSupplyText + .setColor(this.localPlayer.getFoodUsed() > this.localPlayer.getFoodCap() ? Color.RED : Color.WHITE); + } + + @Override + public void upkeepChanged() { + this.resourceBarUpkeepText.setText("Upkeep NYI"); + this.resourceBarUpkeepText.setColor(Color.CYAN); + } + @Override public void ordersChanged(final int abilityHandleId, final int orderId) { reloadSelectedUnitUI(this.selectedUnit); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java index 4291b65..270693a 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/ui/MenuUI.java @@ -65,7 +65,7 @@ public class MenuUI { // Load skins and templates // ================================= this.rootFrame = new GameUI(this.dataSource, GameUI.loadSkin(this.dataSource, 1), this.uiViewport, - this.fontGenerator, this.uiScene, this.viewer); + this.fontGenerator, this.uiScene, this.viewer, 0); this.rootFrameListener.onCreate(this.rootFrame); try { this.rootFrame.loadTOCFile("UI\\FrameDef\\FrameDef.toc");