From 3f1a19ae5d9c5326b7dc6c779e5d3cd1cf4c9f2e Mon Sep 17 00:00:00 2001 From: Retera Date: Sat, 3 Jul 2021 02:19:19 -0400 Subject: [PATCH] Hero revival --- .../warsmash/networking/MultiplayerHack.java | 1 + .../networking/WarsmashClientWriter.java | 2 +- .../warsmash/util/SubscriberSetNotifier.java | 1 + .../handlers/mdx/MdxComplexInstance.java | 6 + .../viewer5/handlers/w3x/War3MapViewer.java | 85 +++++--- .../handlers/w3x/rendersim/RenderUnit.java | 13 +- .../handlers/w3x/rendersim/RenderWidget.java | 13 ++ .../w3x/rendersim/ability/AbilityDataUI.java | 10 +- .../w3x/rendersim/ability/UnitIconUI.java | 22 ++ .../CommandCardPopulatingAbilityVisitor.java | 60 +++++- .../w3x/simulation/CGameplayConstants.java | 84 ++++++++ .../w3x/simulation/CPlayerStateListener.java | 11 +- .../handlers/w3x/simulation/CSimulation.java | 28 ++- .../handlers/w3x/simulation/CUnit.java | 190 +++++++++++++++--- .../handlers/w3x/simulation/CUnitType.java | 8 +- .../simulation/abilities/CAbilityMove.java | 5 +- .../simulation/abilities/CAbilityVisitor.java | 3 + .../abilities/hero/CAbilityHero.java | 11 +- .../abilities/queue/CAbilityReviveHero.java | 142 +++++++++++++ .../simulation/behaviors/CBehaviorFollow.java | 11 + ...yDisableWhileUnderConstructionVisitor.java | 8 + .../w3x/simulation/data/CUnitData.java | 11 +- .../w3x/simulation/players/CPlayer.java | 11 + .../CWidgetAbilityTargetCheckReceiver.java | 5 + .../util/PointAbilityTargetCheckReceiver.java | 5 + .../util/SimulationRenderController.java | 2 + .../viewer5/handlers/w3x/ui/MeleeUI.java | 114 ++++++++++- .../viewer5/handlers/w3x/ui/MenuUI.java | 2 +- .../warsmash/desktop/DesktopLauncher.java | 10 +- 29 files changed, 791 insertions(+), 83 deletions(-) create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/UnitIconUI.java create mode 100644 core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityReviveHero.java diff --git a/core/src/com/etheller/warsmash/networking/MultiplayerHack.java b/core/src/com/etheller/warsmash/networking/MultiplayerHack.java index 4bfcdc5..a8a640b 100644 --- a/core/src/com/etheller/warsmash/networking/MultiplayerHack.java +++ b/core/src/com/etheller/warsmash/networking/MultiplayerHack.java @@ -2,4 +2,5 @@ package com.etheller.warsmash.networking; public class MultiplayerHack { public static String MULTIPLAYER_HACK_SERVER_ADDR = null; + public static int LP_VAL = 0; } diff --git a/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java b/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java index 454032c..e050440 100644 --- a/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java +++ b/core/src/com/etheller/warsmash/networking/WarsmashClientWriter.java @@ -66,7 +66,7 @@ public class WarsmashClientWriter { public void unitCancelTrainingItem(final int unitHandleId, final int cancelIndex) { this.sendBuffer.clear(); - this.sendBuffer.putInt(4 + 4 + 4 + 4 + 1); + this.sendBuffer.putInt(4 + 4 + 4); this.sendBuffer.putInt(ClientToServerProtocol.UNIT_CANCEL_TRAINING); this.sendBuffer.putInt(unitHandleId); this.sendBuffer.putInt(cancelIndex); diff --git a/core/src/com/etheller/warsmash/util/SubscriberSetNotifier.java b/core/src/com/etheller/warsmash/util/SubscriberSetNotifier.java index 8151abe..a8da473 100644 --- a/core/src/com/etheller/warsmash/util/SubscriberSetNotifier.java +++ b/core/src/com/etheller/warsmash/util/SubscriberSetNotifier.java @@ -19,4 +19,5 @@ public abstract class SubscriberSetNotifier { public final void unsubscribe(final LISTENER_TYPE listener) { this.set.remove(listener); } + } 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 f0c1e9e..650102b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxComplexInstance.java @@ -293,6 +293,12 @@ public class MdxComplexInstance extends ModelInstance { final MdxModel model = (MdxModel) this.model; final List sortedGenericObjects = model.sortedGenericObjects; final Scene scene = this.scene; + if(scene == null) { + // too bad for this instance, this is not safe to update on null scene, got NPE from this during testing, + // so we are going to skip this cycle entirely! + // (Retera) + return; + } // Update the nodes for (int i = 0, l = sortedNodes.length; i < l; i++) { 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 f20a67c..898c871 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -127,6 +127,7 @@ public class War3MapViewer extends AbstractMdxModelViewer { public static int DEBUG_DEPTH = 9999; private static final War3ID ABILITY_HERO_RAWCODE = War3ID.fromString("AHer"); + private static final War3ID ABILITY_REVIVE_RAWCODE = War3ID.fromString("Arev"); private static final Color PLACEHOLDER_LUMBER_COLOR = new Color(0.0f, 200f / 255f, 80f / 255f, 1.0f); private static final Color PLACEHOLDER_GOLD_COLOR = new Color(1.0f, 220f / 255f, 0f, 1.0f); private static final War3ID UNIT_FILE = War3ID.fromString("umdl"); @@ -718,34 +719,35 @@ public class War3MapViewer extends AbstractMdxModelViewer { final AbilityUI heroUI = War3MapViewer.this.abilityDataUI.getUI(ABILITY_HERO_RAWCODE); final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source); final String heroLevelUpArt = heroUI.getCasterArt(0); - final MdxModel heroLevelUpModel = loadModel(heroLevelUpArt); - if (heroLevelUpModel != null) { - final MdxComplexInstance modelInstance = (MdxComplexInstance) heroLevelUpModel - .addInstance(); - modelInstance.setTeamColor(source.getPlayerIndex()); + spawnFxOnOrigin(renderUnit, heroLevelUpArt); + } - final MdxModel model = (MdxModel) renderUnit.instance.model; - int index = -1; - for (int i = 0; i < model.attachments.size(); i++) { - final Attachment attachment = model.attachments.get(i); - if (attachment.getName().startsWith("origin ref")) { - index = i; - break; - } - } - if (index != -1) { - final MdxNode attachment = renderUnit.instance.getAttachment(index); - modelInstance.setParent(attachment); - } - else { - modelInstance.setLocation(renderUnit.location); - } + @Override + public void heroRevived(CUnit source) { + final AbilityUI reviveUI = War3MapViewer.this.abilityDataUI.getUI(ABILITY_REVIVE_RAWCODE); + final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source); + CPlayer player = simulation.getPlayer(source.getPlayerIndex()); + final String heroReviveArt = reviveUI.getTargetArt(player.getRace().ordinal()); + spawnFxOnOrigin(renderUnit, heroReviveArt); + MutableGameObject row = allObjectData.getUnits().get(source.getTypeId()); - modelInstance.setScene(War3MapViewer.this.worldScene); - SequenceUtils.randomBirthSequence(modelInstance); - War3MapViewer.this.projectiles - .add(new RenderAttackInstant(modelInstance, War3MapViewer.this, - (float) Math.toRadians(renderUnit.getSimulationUnit().getFacing()))); + // Recreate unit shadow.... is needed here + + final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0); + float unitX = source.getX(); + float unitY = source.getY(); + if ((unitShadow != null) && !"_".equals(unitShadow)) { + final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp"; + final float shadowX = row.getFieldAsFloat(UNIT_SHADOW_X, 0); + final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0); + final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0); + final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0); + if (mapMpq.has(texture)) { + final float x = unitX - shadowX; + final float y = unitY - shadowY; + renderUnit.shadow = terrain.addUnitShadowSplat(texture, x, y, + x + shadowWidth, y + shadowHeight, 3, 0.5f); + } } } @@ -853,6 +855,37 @@ public class War3MapViewer extends AbstractMdxModelViewer { loadLightsAndShading(tileset); } + public void spawnFxOnOrigin(RenderUnit renderUnit, String heroLevelUpArt) { + final MdxModel heroLevelUpModel = loadModel(heroLevelUpArt); + if (heroLevelUpModel != null) { + final MdxComplexInstance modelInstance = (MdxComplexInstance) heroLevelUpModel + .addInstance(); + modelInstance.setTeamColor(renderUnit.playerIndex); + + final MdxModel model = (MdxModel) renderUnit.instance.model; + int index = -1; + for (int i = 0; i < model.attachments.size(); i++) { + final Attachment attachment = model.attachments.get(i); + if (attachment.getName().startsWith("origin ref")) { + index = i; + break; + } + } + if (index != -1) { + final MdxNode attachment = renderUnit.instance.getAttachment(index); + modelInstance.setParent(attachment); + } else { + modelInstance.setLocation(renderUnit.location); + } + + modelInstance.setScene(War3MapViewer.this.worldScene); + SequenceUtils.randomBirthSequence(modelInstance); + War3MapViewer.this.projectiles + .add(new RenderAttackInstant(modelInstance, War3MapViewer.this, + (float) Math.toRadians(renderUnit.getSimulationUnit().getFacing()))); + } + } + protected BufferedImage getDestructablePathingPixelMap(final MutableGameObject row) { return loadPathingTexture(row.getFieldAsString(DESTRUCTABLE_PATHING, 0)); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java index fd3512f..cc1ce59 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java @@ -147,9 +147,9 @@ public class RenderUnit implements RenderWidget { public void populateCommandCard(final CSimulation game, final GameUI gameUI, final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI, - final int subMenuOrderId) { + final int subMenuOrderId, boolean multiSelect) { final CommandCardPopulatingAbilityVisitor commandCardPopulatingVisitor = CommandCardPopulatingAbilityVisitor.INSTANCE - .reset(game, gameUI, this.simulationUnit, commandButtonListener, abilityDataUI, subMenuOrderId); + .reset(game, gameUI, this.simulationUnit, commandButtonListener, abilityDataUI, subMenuOrderId, multiSelect); for (final CAbility ability : this.simulationUnit.getAbilities()) { ability.visit(commandCardPopulatingVisitor); } @@ -271,8 +271,13 @@ public class RenderUnit implements RenderWidget { removeSplats(map); } if (boneCorpse && !this.boneCorpse) { - this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE, - this.simulationUnit.getEndingDecayTime(map.simulation), true); + if(simulationUnit.getUnitType().isHero()) { + this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DISSIPATE, SequenceUtils.EMPTY, + this.simulationUnit.getEndingDecayTime(map.simulation), true); + } else { + this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE, + this.simulationUnit.getEndingDecayTime(map.simulation), true); + } } else if (corpse && !this.corpse) { this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH, diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java index bc1eb29..62a2e66 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderWidget.java @@ -56,6 +56,7 @@ public interface RenderWidget { private float currentSpeedRatio; private boolean currentlyAllowingRarityVariations; private final Queue animationQueue = new LinkedList<>(); + private int lastWalkFrame = -1; public UnitAnimationListenerImpl(final MdxComplexInstance instance) { this.instance = instance; @@ -98,8 +99,14 @@ public interface RenderWidget { this.recycleSet.addAll(this.secondaryAnimationTags); this.recycleSet.addAll(secondaryAnimationTags); this.instance.setAnimationSpeed(speedRatio); + if(animationName != PrimaryTag.WALK && currentAnimation == PrimaryTag.WALK) { + lastWalkFrame = instance.frame; + } if (SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet, allowRarityVariations) != null) { + if(lastWalkFrame != -1 && animationName == PrimaryTag.WALK && currentAnimation != PrimaryTag.WALK) { + instance.setFrame(lastWalkFrame); + } this.currentAnimation = animationName; this.currentAnimationSecondaryTags = secondaryAnimationTags; this.currentlyAllowingRarityVariations = allowRarityVariations; @@ -116,9 +123,15 @@ public interface RenderWidget { this.recycleSet.clear(); this.recycleSet.addAll(this.secondaryAnimationTags); this.recycleSet.addAll(secondaryAnimationTags); + if(animationName != PrimaryTag.WALK && currentAnimation == PrimaryTag.WALK) { + lastWalkFrame = instance.frame; + } final Sequence sequence = SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet, allowRarityVariations); if (sequence != null) { + if(lastWalkFrame != -1 && animationName == PrimaryTag.WALK && currentAnimation != PrimaryTag.WALK) { + instance.setFrame(lastWalkFrame); + } this.currentAnimation = animationName; this.currentAnimationSecondaryTags = secondaryAnimationTags; this.currentlyAllowingRarityVariations = allowRarityVariations; 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 005eebb..422d518 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,8 @@ public class AbilityDataUI { private static final War3ID UNIT_ICON_NORMAL_Y = War3ID.fromString("ubpy"); private static final War3ID UNIT_ICON_NORMAL = War3ID.fromString("uico"); private static final War3ID UNIT_TIP = War3ID.fromString("utip"); + private static final War3ID UNIT_REVIVE_TIP = War3ID.fromString("utpr"); + private static final War3ID UNIT_AWAKEN_TIP = War3ID.fromString("uawt"); private static final War3ID UNIT_UBER_TIP = War3ID.fromString("utub"); private static final War3ID ITEM_ICON_NORMAL_X = War3ID.fromString("ubpx"); @@ -62,7 +64,7 @@ public class AbilityDataUI { private static final War3ID UPGRADE_UBER_TIP = War3ID.fromString("gub1"); private final Map rawcodeToUI = new HashMap<>(); - private final Map rawcodeToUnitUI = new HashMap<>(); + private final Map rawcodeToUnitUI = new HashMap<>(); private final Map rawcodeToItemUI = new HashMap<>(); private final Map> rawcodeToUpgradeUI = new HashMap<>(); private final IconUI moveUI; @@ -138,11 +140,13 @@ public class AbilityDataUI { final int iconNormalX = abilityTypeData.getFieldAsInteger(UNIT_ICON_NORMAL_X, 0); final int iconNormalY = abilityTypeData.getFieldAsInteger(UNIT_ICON_NORMAL_Y, 0); final String iconTip = abilityTypeData.getFieldAsString(UNIT_TIP, 0); + final String reviveTip = abilityTypeData.getFieldAsString(UNIT_REVIVE_TIP, 0); + final String awakenTip = abilityTypeData.getFieldAsString(UNIT_AWAKEN_TIP, 0); final String iconUberTip = abilityTypeData.getFieldAsString(UNIT_UBER_TIP, 0); final Texture iconNormal = gameUI.loadTexture(iconNormalPath); final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix)); this.rawcodeToUnitUI.put(alias, - new IconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY, iconTip, iconUberTip)); + new UnitIconUI(iconNormal, iconNormalDisabled, iconNormalX, iconNormalY, iconTip, iconUberTip, reviveTip, awakenTip)); } for (final War3ID alias : itemData.keySet()) { final MutableGameObject abilityTypeData = itemData.get(alias); @@ -212,7 +216,7 @@ public class AbilityDataUI { return this.rawcodeToUI.get(rawcode); } - public IconUI getUnitUI(final War3ID rawcode) { + public UnitIconUI getUnitUI(final War3ID rawcode) { return this.rawcodeToUnitUI.get(rawcode); } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/UnitIconUI.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/UnitIconUI.java new file mode 100644 index 0000000..f66f05b --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/ability/UnitIconUI.java @@ -0,0 +1,22 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability; + +import com.badlogic.gdx.graphics.Texture; + +public class UnitIconUI extends IconUI { + private String reviveTip; + private String awakenTip; + + public UnitIconUI(Texture icon, Texture iconDisabled, int buttonPositionX, int buttonPositionY, String toolTip, String uberTip, String reviveTip, String awakenTip) { + super(icon, iconDisabled, buttonPositionX, buttonPositionY, toolTip, uberTip); + this.reviveTip = reviveTip; + this.awakenTip = awakenTip; + } + + public String getReviveTip() { + return reviveTip; + } + + public String getAwakenTip() { + return awakenTip; + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java index e1f2227..d208305 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/commandbuttons/CommandCardPopulatingAbilityVisitor.java @@ -5,6 +5,7 @@ import com.etheller.warsmash.util.War3ID; 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.rendersim.ability.IconUI; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.UnitIconUI; 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; @@ -27,6 +28,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.G import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbilityHero; 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.queue.CAbilityReviveHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; @@ -42,20 +44,24 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor + void heroDeath(); + + public static final class CPlayerStateNotifier extends SubscriberSetNotifier implements CPlayerStateListener { @Override public void goldChanged() { @@ -40,5 +42,12 @@ public interface CPlayerStateListener { listener.upkeepChanged(); } } + + @Override + public void heroDeath() { + for (final CPlayerStateListener listener : set) { + listener.heroDeath(); + } + } } } 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 d53ede3..0a14fbe 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 @@ -114,7 +114,22 @@ public class CSimulation implements CPlayerAPI { for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { final CBasePlayer configPlayer = config.getPlayer(i); final War3MapConfigStartLoc startLoc = config.getStartLoc(configPlayer.getStartLocationIndex()); - final CPlayer newPlayer = new CPlayer(CRace.UNDEAD, new float[] { startLoc.getX(), startLoc.getY() }, + CRace defaultRace; + switch(i) { + case 0: + defaultRace = CRace.NIGHTELF; + break; + case 1: + defaultRace = CRace.UNDEAD; + break; + case 2: + defaultRace = CRace.HUMAN; + break; + default: + defaultRace = CRace.OTHER; + break; + } + final CPlayer newPlayer = new CPlayer(defaultRace, new float[] { startLoc.getX(), startLoc.getY() }, configPlayer); if(i < 3) { for(int j = 0; j < 3; j++) { @@ -189,6 +204,9 @@ public class CSimulation implements CPlayerAPI { this.newUnits.add(unit); this.handleIdToUnit.put(unit.getHandleId(), unit); this.worldCollision.addUnit(unit); + if(unit.getHeroData() != null) { + heroCreateEvent(unit); + } return unit; } @@ -389,6 +407,10 @@ public class CSimulation implements CPlayerAPI { public void unitTrainedEvent(final CUnit trainingUnit, final CUnit trainedUnit) { this.simulationRenderController.spawnUnitReadySound(trainedUnit); } + public void heroReviveEvent(final CUnit trainingUnit, final CUnit trainedUnit) { + this.simulationRenderController.heroRevived(trainedUnit); + this.simulationRenderController.spawnUnitReadySound(trainedUnit); + } public void unitRepositioned(final CUnit cUnit) { this.simulationRenderController.unitRepositioned(cUnit); @@ -471,6 +493,10 @@ public class CSimulation implements CPlayerAPI { }; } + public void heroDeathEvent(CUnit cUnit) { + getPlayer(cUnit.getPlayerIndex()).onHeroDeath(); + } + private static final class TimeOfDayVariableEvent extends VariableEvent { private final GlobalScope globalScope; 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 fa383d2..6289ecf 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 @@ -92,8 +92,7 @@ public class CUnit extends CWidget { // 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 CUnitStateNotifier stateNotifier = new CUnitStateNotifier(); - private transient List stateListenersNeedingAdd = new ArrayList<>(); - private transient List stateListenersNeedingRemove = new ArrayList<>(); + private transient List stateListenersUpdates = new ArrayList<>(); private final float acquisitionRange; private transient static AutoAttackTargetFinderEnum autoAttackTargetFinderEnum = new AutoAttackTargetFinderEnum(); @@ -246,14 +245,16 @@ public class CUnit extends CWidget { * this unit from the game. */ public boolean update(final CSimulation game) { - for (final CUnitStateListener listener : this.stateListenersNeedingAdd) { - this.stateNotifier.subscribe(listener); + for(StateListenerUpdate update: this.stateListenersUpdates) { + switch(update.getUpdateType()) { + case ADD: + stateNotifier.subscribe(update.listener); + break; + case REMOVE: + stateNotifier.unsubscribe(update.listener); + break; + } } - this.stateListenersNeedingAdd.clear(); - for (final CUnitStateListener listener : this.stateListenersNeedingRemove) { - this.stateNotifier.unsubscribe(listener); - } - this.stateListenersNeedingRemove.clear(); if (isDead()) { if (this.collisionRectangle != null) { // Moved this here because doing it on "kill" was able to happen in some cases @@ -273,6 +274,14 @@ public class CUnit extends CWidget { if (!this.unitType.isDecay()) { // if we dont raise AND dont decay, then now that death anim is over // we just delete the unit + if(unitType.isHero()) { + if(!getHeroData().isAwaitingRevive()) { + setHidden(true); + getHeroData().setAwaitingRevive(true); + game.heroDeathEvent(this); + } + return false; + } return true; } this.deathTurnTick = gameTurnTick; @@ -287,6 +296,14 @@ public class CUnit extends CWidget { } else if (game.getGameTurnTick() > (this.deathTurnTick + (int) (getEndingDecayTime(game) / WarsmashConstants.SIMULATION_STEP_TIME))) { + if(unitType.isHero()) { + if(!getHeroData().isAwaitingRevive()) { + setHidden(true); + getHeroData().setAwaitingRevive(true); + game.heroDeathEvent(this); + } + return false; + } return true; } } @@ -340,6 +357,15 @@ public class CUnit extends CWidget { this.queuedUnitFoodPaid = true; } } + else if (this.buildQueueTypes[0] == QueueItemType.HERO_REVIVE) { + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType trainedUnitType = game.getUnit(queuedRawcode.getValue()).getUnitType(); + final int newFoodUsed = player.getFoodUsed() + trainedUnitType.getFoodUsed(); + if (newFoodUsed <= player.getFoodCap()) { + player.setFoodUsed(newFoodUsed); + this.queuedUnitFoodPaid = true; + } + } else { this.queuedUnitFoodPaid = true; System.err.println( @@ -372,6 +398,42 @@ public class CUnit extends CWidget { this.stateNotifier.queueChanged(); } } + else if (this.buildQueueTypes[0] == QueueItemType.HERO_REVIVE) { + CUnit revivingHero = game.getUnit(queuedRawcode.getValue()); + final CUnitType trainedUnitType = revivingHero.getUnitType(); + CGameplayConstants gameplayConstants = game.getGameplayConstants(); + if (this.constructionProgress >= gameplayConstants.getHeroReviveTime(trainedUnitType.getBuildTime(), revivingHero.getHeroData().getHeroLevel())) { + this.constructionProgress = 0; + revivingHero.corpse = false; + revivingHero.boneCorpse = false; + revivingHero.deathTurnTick = 0; + revivingHero.setX(getX()); + revivingHero.setY(getY()); + game.getWorldCollision().addUnit(revivingHero); + revivingHero.setPoint(getX(), getY(), game.getWorldCollision(), game.getRegionManager()); + revivingHero.setHidden(false); + revivingHero.setLife(game, revivingHero.getMaximumLife() * gameplayConstants.getHeroReviveLifeFactor()); + revivingHero.setMana( revivingHero.getMaximumMana() * gameplayConstants.getHeroReviveManaFactor() + gameplayConstants.getHeroReviveManaStart() * trainedUnitType.getManaInitial()); + // dont add food cost to player 2x + revivingHero.setFoodUsed(trainedUnitType.getFoodUsed()); + final CPlayer player = game.getPlayer(this.playerIndex); + player.setUnitFoodMade(revivingHero, trainedUnitType.getFoodMade()); + player.addTechtreeUnlocked(queuedRawcode); + // nudge the trained unit out around us + revivingHero.nudgeAround(game, this); + game.heroReviveEvent(this, revivingHero); + if (this.rallyPoint != null) { + final int rallyOrderId = OrderIds.smart; + this.rallyPoint.visit( + UseAbilityOnTargetByIdVisitor.INSTANCE.reset(game, revivingHero, rallyOrderId)); + } + for (int i = 0; i < (this.buildQueue.length - 1); i++) { + setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]); + } + setBuildQueueItem(game, this.buildQueue.length - 1, null, null); + this.stateNotifier.queueChanged(); + } + } else if (this.buildQueueTypes[0] == QueueItemType.RESEARCH) { final CUnitType trainedUnitType = game.getUnitData().getUnitType(queuedRawcode); if (this.constructionProgress >= trainedUnitType.getBuildTime()) { @@ -1022,11 +1084,11 @@ public class CUnit extends CWidget { } public void addStateListener(final CUnitStateListener listener) { - this.stateListenersNeedingAdd.add(listener); + this.stateListenersUpdates.add(new StateListenerUpdate(listener, StateListenerUpdateType.ADD)); } public void removeStateListener(final CUnitStateListener listener) { - this.stateListenersNeedingRemove.add(listener); + this.stateListenersUpdates.add(new StateListenerUpdate(listener, StateListenerUpdateType.REMOVE)); } public boolean isCorpse() { @@ -1089,11 +1151,12 @@ public class CUnit extends CWidget { @Override public boolean call(final CUnit unit) { if (!this.game.getPlayer(this.source.getPlayerIndex()).hasAlliance(unit.getPlayerIndex(), - CAllianceType.PASSIVE)) { + CAllianceType.PASSIVE) && !unit.isDead() && !unit.isInvulnerable()) { for (final CUnitAttack attack : this.source.getAttacks()) { if (this.source.canReach(unit, this.source.acquisitionRange) && unit.canBeTargetedBy(this.game, this.source, attack.getTargetsAllowed()) - && (this.source.distance(unit) >= this.source.getUnitType().getMinimumAttackRange())) { + && (this.source.distance(unit) >= this.source.getUnitType().getMinimumAttackRange()) + ) { if (this.source.currentBehavior != null) { this.source.currentBehavior.end(this.game, false); } @@ -1260,9 +1323,15 @@ public class CUnit extends CWidget { switch (this.buildQueueTypes[0]) { case RESEARCH: return 999; // TODO - case UNIT: + case UNIT: { final CUnitType trainedUnitType = simulation.getUnitData().getUnitType(this.buildQueue[0]); return trainedUnitType.getBuildTime(); + } + case HERO_REVIVE: { + CUnit hero = simulation.getUnit(this.buildQueue[0].getValue()); + final CUnitType trainedUnitType = hero.getUnitType(); + return simulation.getGameplayConstants().getHeroReviveTime(trainedUnitType.getBuildTime(), hero.getHeroData().getHeroLevel()); + } default: return 0; } @@ -1278,22 +1347,41 @@ public class CUnit extends CWidget { switch (cancelledType) { case RESEARCH: break; - case UNIT: + 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; } + case HERO_REVIVE: { + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnit(this.buildQueue[cancelIndex].getValue()).getUnitType(); + player.setFoodUsed(player.getFoodUsed() - unitType.getFoodUsed()); + break; + } + } } switch (cancelledType) { case RESEARCH: break; - case UNIT: + case UNIT: { final CPlayer player = game.getPlayer(this.playerIndex); final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]); player.refundFor(unitType); break; } + case HERO_REVIVE: { + final CPlayer player = game.getPlayer(this.playerIndex); + CUnit hero = game.getUnit(this.buildQueue[cancelIndex].getValue()); + final CUnitType unitType = hero.getUnitType(); + CAbilityHero heroData = hero.getHeroData(); + heroData.setAwaitingRevive(true); + CGameplayConstants gameplayConstants = game.getGameplayConstants(); + player.refund(gameplayConstants.getHeroReviveGoldCost(unitType.getGoldCost(), heroData.getHeroLevel()), + gameplayConstants.getHeroReviveLumberCost(unitType.getLumberCost(), heroData.getHeroLevel())); + break; + } + } for (int i = cancelIndex; i < (this.buildQueueTypes.length - 1); i++) { setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]); } @@ -1309,17 +1397,32 @@ public class CUnit extends CWidget { this.buildQueueTypes[index] = queueItemType; if (index == 0) { this.queuedUnitFoodPaid = true; - if ((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) { - final int newFoodUsed = player.getFoodUsed() + unitType.getFoodUsed(); - if (newFoodUsed <= player.getFoodCap()) { - player.setFoodUsed(newFoodUsed); + if (rawcode != null) { + if(queueItemType == QueueItemType.UNIT) { + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[index]); + if (unitType.getFoodUsed() != 0) { + final int newFoodUsed = player.getFoodUsed() + unitType.getFoodUsed(); + if (newFoodUsed <= player.getFoodCap()) { + player.setFoodUsed(newFoodUsed); + } + else { + this.queuedUnitFoodPaid = false; + game.getCommandErrorListener(this.playerIndex).showNoFoodError(); + } } - else { - this.queuedUnitFoodPaid = false; - game.getCommandErrorListener(this.playerIndex).showNoFoodError(); + } else if(queueItemType == QueueItemType.HERO_REVIVE) { + final CPlayer player = game.getPlayer(this.playerIndex); + final CUnitType unitType = game.getUnit(this.buildQueue[index].getValue()).getUnitType(); + if (unitType.getFoodUsed() != 0) { + final int newFoodUsed = player.getFoodUsed() + unitType.getFoodUsed(); + if (newFoodUsed <= player.getFoodCap()) { + player.setFoodUsed(newFoodUsed); + } + else { + this.queuedUnitFoodPaid = false; + game.getCommandErrorListener(this.playerIndex).showNoFoodError(); + } } } } @@ -1334,13 +1437,24 @@ public class CUnit extends CWidget { } } + public void queueRevivingHero(final CSimulation game, final CUnit hero) { + if (queue(game, new War3ID(hero.getHandleId()), QueueItemType.HERO_REVIVE)) { + hero.getHeroData().setAwaitingRevive(false); + final CPlayer player = game.getPlayer(this.playerIndex); + int heroReviveGoldCost = game.getGameplayConstants().getHeroReviveGoldCost(hero.getUnitType().getGoldCost(), hero.getHeroData().getHeroLevel()); + int heroReviveLumberCost = game.getGameplayConstants().getHeroReviveLumberCost(hero.getUnitType().getGoldCost(), hero.getHeroData().getHeroLevel()); + player.charge(heroReviveGoldCost, heroReviveLumberCost); + } + } + public void queueResearch(final CSimulation game, final War3ID rawcode) { queue(game, rawcode, QueueItemType.RESEARCH); } public static enum QueueItemType { UNIT, - RESEARCH; + RESEARCH, + HERO_REVIVE; } public void setRallyPoint(final AbilityTarget target) { @@ -1576,4 +1690,26 @@ public class CUnit extends CWidget { setLife(simulation, 0); simulation.getWorldCollision().removeUnit(this); } + + private static enum StateListenerUpdateType { + ADD, REMOVE; + } + + private static final class StateListenerUpdate { + private CUnitStateListener listener; + private StateListenerUpdateType updateType; + + public StateListenerUpdate(CUnitStateListener listener, StateListenerUpdateType updateType) { + this.listener = listener; + this.updateType = updateType; + } + + public CUnitStateListener getListener() { + return listener; + } + + public StateListenerUpdateType getUpdateType() { + return updateType; + } + } } 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 f92592e..242f7c4 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 @@ -75,6 +75,7 @@ public class CUnitType { private final int properNamesCount; private final boolean canFlee; private final int priority; + private boolean revivesHeroes; public CUnitType(final String name, final String legacyName, final War3ID typeId, final int maxLife, final int manaInitial, final int manaMaximum, final int speed, final int defense, final String abilityList, @@ -92,7 +93,7 @@ public class CUnitType { 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 boolean canFlee, final int priority) { + final boolean canFlee, final int priority, boolean revivesHeroes) { this.name = name; this.legacyName = legacyName; this.typeId = typeId; @@ -146,6 +147,7 @@ public class CUnitType { this.properNamesCount = properNamesCount; this.canFlee = canFlee; this.priority = priority; + this.revivesHeroes = revivesHeroes; } public String getName() { @@ -359,4 +361,8 @@ public class CUnitType { public int getPriority() { return this.priority; } + + public boolean isRevivesHeroes() { + return revivesHeroes; + } } 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 232287d..ac37866 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 @@ -33,6 +33,7 @@ public class CAbilityMove extends AbstractCAbility { switch (orderId) { case OrderIds.smart: case OrderIds.patrol: + case OrderIds.move: if ((target instanceof CUnit) && (target != unit)) { receiver.targetOk(target); } @@ -98,7 +99,9 @@ public class CAbilityMove extends AbstractCAbility { @Override public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) { - return caster.getFollowBehavior().reset(OrderIds.move, (CUnit) target); + CBehavior followBehavior = caster.getFollowBehavior().reset(orderId == OrderIds.smart ? OrderIds.move : orderId, (CUnit) target); + caster.setDefaultBehavior(followBehavior); + return followBehavior; } @Override diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java index b2ec6e3..5874170 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/CAbilityVisitor.java @@ -13,6 +13,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.G import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbilityHero; 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.queue.CAbilityReviveHero; /** * A visitor for the lowest level inherent types of an ability. It's a bit of a @@ -47,6 +48,8 @@ public interface CAbilityVisitor { T accept(CAbilityQueue ability); + T accept(CAbilityReviveHero ability); + T accept(GenericSingleIconActiveAbility ability); T accept(CAbilityRally ability); 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 d7200e3..b3bd895 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 @@ -28,6 +28,7 @@ public class CAbilityHero extends AbstractCAbility { private HeroStatValue agility; private HeroStatValue intelligence; private String properName; + private boolean awaitingRevive; public CAbilityHero(final int handleId, final List skillsAvailable) { super(handleId); @@ -168,6 +169,14 @@ public class CAbilityHero extends AbstractCAbility { return this.properName; } + public void setAwaitingRevive(boolean awaitingRevive) { + this.awaitingRevive = awaitingRevive; + } + + public boolean isAwaitingRevive() { + return awaitingRevive; + } + public void addXp(final CSimulation simulation, final CUnit unit, final int xp) { this.xp += xp; final CGameplayConstants gameplayConstants = simulation.getGameplayConstants(); @@ -232,7 +241,7 @@ public class CAbilityHero extends AbstractCAbility { unit.setAgilityDefenseBonus(agilityDefenseBonus); } - public static final class HeroStatValue { + public static final class HeroStatValue { private final float perLevelFactor; private int base; private int bonus; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityReviveHero.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityReviveHero.java new file mode 100644 index 0000000..73aa8b3 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/abilities/queue/CAbilityReviveHero.java @@ -0,0 +1,142 @@ +package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue; + +import com.etheller.warsmash.util.War3ID; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.*; +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.hero.CAbilityHero; +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; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; +import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public final class CAbilityReviveHero extends AbstractCAbility { + + public CAbilityReviveHero(final int handleId) { + super(handleId); + } + + + @Override + protected void innerCheckCanUse(final CSimulation game, final CUnit unit, final int orderId, + final AbilityActivationReceiver receiver) { + CUnit deadHero = game.getUnit(orderId); + if (deadHero != null + && deadHero.getPlayerIndex()==unit.getPlayerIndex()) { + CAbilityHero heroData = deadHero.getHeroData(); + if( heroData != null && heroData.isAwaitingRevive()) { + final CPlayer player = game.getPlayer(unit.getPlayerIndex()); + int heroReviveGoldCost = game.getGameplayConstants().getHeroReviveGoldCost(deadHero.getUnitType().getGoldCost(), heroData.getHeroLevel()); + int heroReviveLumberCost = game.getGameplayConstants().getHeroReviveLumberCost(deadHero.getUnitType().getGoldCost(), heroData.getHeroLevel()); + + if (player.getGold() >= heroReviveGoldCost) { + if (player.getLumber() >= heroReviveLumberCost) { + if ((deadHero.getUnitType().getFoodUsed() == 0) + || ((player.getFoodUsed() + deadHero.getUnitType().getFoodUsed()) <= player.getFoodCap())) { + receiver.useOk(); + } + else { + receiver.notEnoughResources(ResourceType.FOOD); + } + } + else { + receiver.notEnoughResources(ResourceType.LUMBER); + } + } + else { + receiver.notEnoughResources(ResourceType.GOLD); + } + } + } + else { + /// ??? + receiver.useOk(); + } + } + + @Override + public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target, + final AbilityTargetCheckReceiver receiver) { + receiver.orderIdNotAccepted(); + } + + @Override + public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityPointTarget target, final AbilityTargetCheckReceiver receiver) { + receiver.orderIdNotAccepted(); + } + + @Override + public final void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId, + final AbilityTargetCheckReceiver receiver) { + CUnit deadHero = game.getUnit(orderId); + if (deadHero != null + && deadHero.getPlayerIndex()==unit.getPlayerIndex()) { + receiver.targetOk(null); + } + else if (orderId == OrderIds.cancel) { + receiver.targetOk(null); + } + else { + receiver.orderIdNotAccepted(); + } + } + + @Override + public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { + return true; + } + + @Override + public void onAdd(final CSimulation game, final CUnit unit) { + + } + + @Override + public void onRemove(final CSimulation game, final CUnit unit) { + + } + + @Override + public void onTick(final CSimulation game, final CUnit unit) { + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) { + return null; + } + + @Override + public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, + final AbilityPointTarget point) { + return null; + } + + @Override + public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) { + if (orderId == OrderIds.cancel) { + caster.cancelBuildQueueItem(game, 0); + } + else { + caster.queueRevivingHero(game, game.getUnit(orderId)); + } + return null; + } + + @Override + public T visit(final CAbilityVisitor visitor) { + return visitor.accept(this); + } + + @Override + public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) { + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorFollow.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorFollow.java index 2579932..6820bb8 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorFollow.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/CBehaviorFollow.java @@ -9,6 +9,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting public class CBehaviorFollow extends CAbstractRangedBehavior { private int higlightOrderId; + private boolean justAutoAttacked = false; public CBehaviorFollow(final CUnit unit) { super(unit); @@ -26,27 +27,37 @@ public class CBehaviorFollow extends CAbstractRangedBehavior { @Override public boolean isWithinRange(final CSimulation simulation) { + if(justAutoAttacked = this.unit.autoAcquireAttackTargets(simulation, false)) { + return true; + } return this.unit.canReach(this.target, this.unit.getAcquisitionRange()); } @Override protected CBehavior update(final CSimulation simulation, final boolean withinFacingWindow) { + this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, false); return this; } @Override protected CBehavior updateOnInvalidTarget(final CSimulation simulation) { + unit.setDefaultBehavior(unit.getStopBehavior()); return this.unit.pollNextOrderBehavior(simulation); } @Override protected boolean checkTargetStillValid(final CSimulation simulation) { + if (this.justAutoAttacked) { + this.justAutoAttacked = false; + this.unit.getMoveBehavior().reset(target, this, false); + } return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE); } @Override protected void resetBeforeMoving(final CSimulation simulation) { + } @Override diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/AbilityDisableWhileUnderConstructionVisitor.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/AbilityDisableWhileUnderConstructionVisitor.java index 06bc41d..7580353 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/AbilityDisableWhileUnderConstructionVisitor.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/behaviors/build/AbilityDisableWhileUnderConstructionVisitor.java @@ -17,6 +17,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.generic.G import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbilityHero; 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.queue.CAbilityReviveHero; public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisitor { public static final AbilityDisableWhileUnderConstructionVisitor INSTANCE = new AbilityDisableWhileUnderConstructionVisitor(); @@ -105,6 +106,13 @@ public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisi return null; } + @Override + public Void accept(CAbilityReviveHero ability) { + ability.setDisabled(true); + ability.setIconShowing(false); + return null; + } + @Override public Void accept(final GenericSingleIconActiveAbility ability) { ability.setDisabled(true); 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 021b938..c9f03d3 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 @@ -33,6 +33,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbi import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CPrimaryAttribute; 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.queue.CAbilityReviveHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; @@ -143,6 +144,7 @@ public class CUnitData { private static final War3ID STRUCTURES_BUILT = War3ID.fromString("ubui"); private static final War3ID UNITS_TRAINED = War3ID.fromString("utra"); private static final War3ID RESEARCHES_AVAILABLE = War3ID.fromString("ures"); + private static final War3ID REVIVES_HEROES = War3ID.fromString("urev"); private static final War3ID UNIT_RACE = War3ID.fromString("urac"); private static final War3ID REQUIRES = War3ID.fromString("ureq"); @@ -243,7 +245,10 @@ public class CUnitData { if (!unitsTrained.isEmpty() || !researchesAvailable.isEmpty()) { unit.add(simulation, new CAbilityQueue(handleIdAllocator.createId(), unitsTrained, researchesAvailable)); } - if (!unitsTrained.isEmpty()) { + if(unitTypeInstance.isRevivesHeroes()) { + unit.add(simulation, new CAbilityReviveHero(handleIdAllocator.createId())); + } + if (!unitsTrained.isEmpty() || unitTypeInstance.isRevivesHeroes()) { unit.add(simulation, new CAbilityRally(handleIdAllocator.createId())); } if (unitTypeInstance.isHero()) { @@ -450,6 +455,8 @@ public class CUnitData { final int foodUsed = unitType.getFieldAsInteger(FOOD_USED, 0); final int foodMade = unitType.getFieldAsInteger(FOOD_MADE, 0); + boolean revivesHeroes = unitType.getFieldAsBoolean(REVIVES_HEROES, 0); + final String unitsTrainedString = unitType.getFieldAsString(UNITS_TRAINED, 0); final String[] unitsTrainedStringItems = unitsTrainedString.trim().split(","); final List unitsTrained = new ArrayList<>(); @@ -538,7 +545,7 @@ public class CUnitData { goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes, requiredPathingTypes, propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus, agility, agiPlus, intelligence, intPlus, primaryAttribute, heroAbilityList, heroProperNames, properNamesCount, - canFlee, priority); + canFlee, priority, revivesHeroes); this.unitIdToUnitType.put(typeId, unitTypeInstance); this.jassLegacyNameToUnitId.put(legacyName, typeId); } 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 106772b..b116505 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 @@ -139,6 +139,13 @@ public class CPlayer extends CBasePlayer { this.stateNotifier.goldChanged(); } + public void refund(int gold, int lumber) { + this.gold += gold; + this.lumber += lumber; + this.stateNotifier.lumberChanged(); + this.stateNotifier.goldChanged(); + } + public void setUnitFoodUsed(final CUnit unit, final int foodUsed) { this.foodUsed += unit.setFoodUsed(foodUsed); this.stateNotifier.foodChanged(); @@ -148,4 +155,8 @@ public class CPlayer extends CBasePlayer { this.foodCap += unit.setFoodMade(foodMade); this.stateNotifier.foodChanged(); } + + public void onHeroDeath() { + stateNotifier.heroDeath(); + } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java index 3dd15f7..fb59f0b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/CWidgetAbilityTargetCheckReceiver.java @@ -7,6 +7,11 @@ public class CWidgetAbilityTargetCheckReceiver implements AbilityTargetCheckRece private CWidget target; + public CWidgetAbilityTargetCheckReceiver reset() { + this.target = null; + return this; + } + @Override public void targetOk(final CWidget target) { this.target = target; diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java index 0a17401..0c830c9 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/simulation/util/PointAbilityTargetCheckReceiver.java @@ -7,6 +7,11 @@ public class PointAbilityTargetCheckReceiver implements AbilityTargetCheckReceiv private AbilityPointTarget target; + public PointAbilityTargetCheckReceiver reset() { + this.target = null; + return this; + } + @Override public void targetOk(final AbilityPointTarget target) { this.target = target; 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 aef4c93..e38a1f9 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 @@ -59,4 +59,6 @@ public interface SimulationRenderController { void spawnAbilitySoundEffect(CUnit caster, War3ID alias); void unitPreferredSelectionReplacement(CUnit unit, CUnit newUnit); + + void heroRevived(CUnit trainedUnit); } 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 3477dfa..9e09850 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 @@ -89,6 +89,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.IconUI; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.ItemUI; +import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.UnitIconUI; 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; @@ -125,6 +126,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CPri 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.queue.CAbilityReviveHero; 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.abilities.targeting.AbilityTargetVisitor; @@ -1009,8 +1011,29 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma abilityToUse.checkCanTargetNoTarget(this.war3MapViewer.simulation, this.selectedUnit.getSimulationUnit(), orderId, noTargetReceiver); if (noTargetReceiver.isTargetable()) { + boolean shiftDown = isShiftDown(); this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), - abilityHandleId, orderId, isShiftDown()); + abilityHandleId, orderId, shiftDown); + if(selectedUnits.size() > 1){ + for(RenderUnit otherSelectedUnit: selectedUnits) { + if(otherSelectedUnit != activeCommandUnit) { + abilityToUse = null; + for(CAbility ability: otherSelectedUnit.getSimulationUnit().getAbilities()) { + BooleanAbilityTargetCheckReceiver receiver = BooleanAbilityTargetCheckReceiver.getInstance().reset(); + ability.checkCanTargetNoTarget(war3MapViewer.simulation, otherSelectedUnit.getSimulationUnit(), activeCommandOrderId, receiver); + if(receiver.isTargetable()) { + abilityToUse = ability; + } + } + if(abilityToUse != null) { + this.unitOrderListener.issueImmediateOrder( + otherSelectedUnit.getSimulationUnit().getHandleId(), + abilityToUse.getHandleId(), this.activeCommandOrderId, + shiftDown); + } + } + } + } } else { this.activeCommand = abilityToUse; @@ -1023,6 +1046,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma else { this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), abilityHandleId, orderId, isShiftDown()); + if(selectedUnits.size() > 1) { + for (RenderUnit otherSelectedUnit : selectedUnits) { + if (otherSelectedUnit != activeCommandUnit) { + this.unitOrderListener.issueImmediateOrder(otherSelectedUnit.getSimulationUnit().getHandleId(), + abilityHandleId, orderId, isShiftDown()); + } + } + } } if (rightClick) { this.war3MapViewer.getUiSounds().getSound("AutoCastButtonClick").play(this.uiScene.audioContext, 0, 0, 0); @@ -1106,7 +1137,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma } this.hpBarFrameIndex = 0; if (this.currentlyDraggingPointer == -1) { - if ((this.mouseOverUnit != null) && !this.mouseOverUnit.getSimulationWidget().isInvulnerable()) { + if ((this.mouseOverUnit != null) && !this.mouseOverUnit.getSimulationWidget().isInvulnerable() && this.mouseOverUnit.isSelectable() && !this.mouseOverUnit.getSimulationWidget().isDead()) { final SimpleStatusBarFrame simpleStatusBarFrame = getHpBar(); positionHealthBar(simpleStatusBarFrame, this.mouseOverUnit, 1.0f); } @@ -1265,7 +1296,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma simpleStatusBarFrame.getBarFrame().setColor(Math.min(1.0f, 2.0f - (lifeRatioRemaining * 2)), Math.min(1.0f, lifeRatioRemaining * 2), 0, alpha); final Vector2 unprojected = this.uiViewport.unproject(screenCoordsVector); - simpleStatusBarFrame.setWidth(unit.getSelectionScale() * 1.5f); + simpleStatusBarFrame.setWidth(unit.getSelectionScale() * 1.5f * Gdx.graphics.getWidth() / 2560); simpleStatusBarFrame.setHeight(16); simpleStatusBarFrame.addSetPoint( new SetPoint(FramePoint.CENTER, this.rootFrame, FramePoint.BOTTOMLEFT, unprojected.x, unprojected.y)); @@ -1450,6 +1481,12 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma return null; } + @Override + public Void accept(CAbilityReviveHero ability) { + handleTargetCursor(ability); + return null; + } + @Override public Void accept(final GenericSingleIconActiveAbility ability) { handleTargetCursor(ability); @@ -1999,8 +2036,18 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.queueIconFrames[i].setToolTip(upgradeUI.getToolTip()); this.queueIconFrames[i].setUberTip(upgradeUI.getUberTip()); break; + case HERO_REVIVE: { + War3ID handleIdEncoded = simulationUnit.getBuildQueue()[i]; + CUnit hero = war3MapViewer.simulation.getUnit(handleIdEncoded.getValue()); + final UnitIconUI unitUI = this.war3MapViewer.getAbilityDataUI() + .getUnitUI(hero.getTypeId()); + this.queueIconFrames[i].setTexture(unitUI.getIcon()); + this.queueIconFrames[i].setToolTip(unitUI.getReviveTip() + " - " + hero.getHeroData().getProperName()); + this.queueIconFrames[i].setUberTip(unitUI.getUberTip()); + break; + } case UNIT: - default: + default: { final IconUI unitUI = this.war3MapViewer.getAbilityDataUI() .getUnitUI(simulationUnit.getBuildQueue()[i]); this.queueIconFrames[i].setTexture(unitUI.getIcon()); @@ -2008,6 +2055,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.queueIconFrames[i].setUberTip(unitUI.getUberTip()); break; } + } } } this.simpleInfoPanelBuildingDetail.setVisible(!multiSelect); @@ -2021,6 +2069,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma if (simulationUnit.getBuildQueueTypes()[0] == QueueItemType.UNIT) { this.rootFrame.setText(this.simpleBuildingBuildingActionLabel, this.rootFrame.getTemplates().getDecoratedString("TRAINING")); + } else if (simulationUnit.getBuildQueueTypes()[0] == QueueItemType.HERO_REVIVE) { + this.rootFrame.setText(this.simpleBuildingBuildingActionLabel, + this.rootFrame.getTemplates().getDecoratedString("REVIVING")); } else { this.rootFrame.setText(this.simpleBuildingBuildingActionLabel, @@ -2430,6 +2481,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.resourceBarUpkeepText.setColor(Color.CYAN); } + @Override + public void heroDeath() { + if(this.selectedUnit!=null) { + if(this.selectedUnit.getSimulationUnit().getUnitType().isRevivesHeroes()) { + reloadSelectedUnitUI(this.selectedUnit); + } + } + } + @Override public void ordersChanged() { reloadSelectedUnitUI(this.selectedUnit); @@ -2474,7 +2534,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma 0); } this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this.rootFrame, this, - abilityDataUI, menuOrderId); + abilityDataUI, menuOrderId, selectedUnits.size() > 1); } } } @@ -2539,6 +2599,28 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.activeCommandUnit.getSimulationUnit().getHandleId(), this.activeCommand.getHandleId(), this.activeCommandOrderId, rayPickUnit.getSimulationWidget().getHandleId(), shiftDown); + if(selectedUnits.size() > 1){ + for(RenderUnit otherSelectedUnit: selectedUnits) { + if(otherSelectedUnit != activeCommandUnit) { + CAbility abilityToUse = null; + CWidget targetToUse = null; + for(CAbility ability: otherSelectedUnit.getSimulationUnit().getAbilities()) { + CWidgetAbilityTargetCheckReceiver receiver = CWidgetAbilityTargetCheckReceiver.INSTANCE.reset(); + ability.checkCanTarget(war3MapViewer.simulation, otherSelectedUnit.getSimulationUnit(), activeCommandOrderId, rayPickUnit.getSimulationWidget(), receiver); + if(receiver.getTarget() != null) { + abilityToUse = ability; + targetToUse = receiver.getTarget(); + } + } + if(abilityToUse != null) { + this.unitOrderListener.issueTargetOrder( + otherSelectedUnit.getSimulationUnit().getHandleId(), + abilityToUse.getHandleId(), this.activeCommandOrderId, + targetToUse.getHandleId(), shiftDown); + } + } + } + } final UnitSound yesSound = (this.activeCommand instanceof CAbilityAttack) ? getSelectedUnit().soundset.yesAttack : getSelectedUnit().soundset.yes; @@ -2597,6 +2679,28 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma this.activeCommandUnit.getSimulationUnit().getHandleId(), this.activeCommand.getHandleId(), this.activeCommandOrderId, clickLocationTemp2.x, clickLocationTemp2.y, shiftDown); + if(selectedUnits.size() > 1){ + for(RenderUnit otherSelectedUnit: selectedUnits) { + if(otherSelectedUnit != activeCommandUnit) { + CAbility abilityToUse = null; + AbilityPointTarget targetToUse = null; + for(CAbility ability: otherSelectedUnit.getSimulationUnit().getAbilities()) { + PointAbilityTargetCheckReceiver receiver = PointAbilityTargetCheckReceiver.INSTANCE.reset(); + ability.checkCanTarget(war3MapViewer.simulation, otherSelectedUnit.getSimulationUnit(), activeCommandOrderId, clickLocationTemp2, receiver); + if(receiver.getTarget() != null) { + abilityToUse = ability; + targetToUse = receiver.getTarget(); + } + } + if(abilityToUse != null) { + this.unitOrderListener.issuePointOrder( + otherSelectedUnit.getSimulationUnit().getHandleId(), + abilityToUse.getHandleId(), this.activeCommandOrderId, + targetToUse.getX(), targetToUse.getY(), shiftDown); + } + } + } + } if (getSelectedUnit().soundset.yes.playUnitResponse( this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { portraitTalk(); 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 ca0f141..aac3477 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 @@ -764,7 +764,7 @@ public class MenuUI { return; } else if (this.loadingMap != null) { - final int localPlayerIndex = 0; + final int localPlayerIndex = MultiplayerHack.LP_VAL; try { this.loadingMap.viewer.loadMap(this.loadingMap.map, this.loadingMap.mapInfo, localPlayerIndex); } diff --git a/desktop/src/com/etheller/warsmash/desktop/DesktopLauncher.java b/desktop/src/com/etheller/warsmash/desktop/DesktopLauncher.java index 99a2bc5..7f28931 100644 --- a/desktop/src/com/etheller/warsmash/desktop/DesktopLauncher.java +++ b/desktop/src/com/etheller/warsmash/desktop/DesktopLauncher.java @@ -43,9 +43,9 @@ public class DesktopLauncher { config.gles30ContextMajorVersion = 3; config.gles30ContextMinorVersion = 3; // config.samples = 16; - config.vSyncEnabled = false; - config.foregroundFPS = 0; - config.backgroundFPS = 0; +// config.vSyncEnabled = false; +// config.foregroundFPS = 0; +// config.backgroundFPS = 0; final DisplayMode desktopDisplayMode = LwjglApplicationConfiguration.getDesktopDisplayMode(); config.width = desktopDisplayMode.width; config.height = desktopDisplayMode.height; @@ -63,6 +63,10 @@ public class DesktopLauncher { argIndex++; MultiplayerHack.MULTIPLAYER_HACK_SERVER_ADDR = arg[argIndex]; } + else if ((arg.length > (argIndex + 1)) && "-lp".equals(arg[argIndex])) { + argIndex++; + MultiplayerHack.LP_VAL = Integer.parseInt(arg[argIndex]); + } } loadExtensions(); final DataTable warsmashIni = loadWarsmashIni();