Hero revival

This commit is contained in:
Retera 2021-07-03 02:19:19 -04:00
parent 28aeffd6d4
commit 3f1a19ae5d
29 changed files with 791 additions and 83 deletions

View File

@ -2,4 +2,5 @@ package com.etheller.warsmash.networking;
public class MultiplayerHack { public class MultiplayerHack {
public static String MULTIPLAYER_HACK_SERVER_ADDR = null; public static String MULTIPLAYER_HACK_SERVER_ADDR = null;
public static int LP_VAL = 0;
} }

View File

@ -66,7 +66,7 @@ public class WarsmashClientWriter {
public void unitCancelTrainingItem(final int unitHandleId, final int cancelIndex) { public void unitCancelTrainingItem(final int unitHandleId, final int cancelIndex) {
this.sendBuffer.clear(); 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(ClientToServerProtocol.UNIT_CANCEL_TRAINING);
this.sendBuffer.putInt(unitHandleId); this.sendBuffer.putInt(unitHandleId);
this.sendBuffer.putInt(cancelIndex); this.sendBuffer.putInt(cancelIndex);

View File

@ -19,4 +19,5 @@ public abstract class SubscriberSetNotifier<LISTENER_TYPE> {
public final void unsubscribe(final LISTENER_TYPE listener) { public final void unsubscribe(final LISTENER_TYPE listener) {
this.set.remove(listener); this.set.remove(listener);
} }
} }

View File

@ -293,6 +293,12 @@ public class MdxComplexInstance extends ModelInstance {
final MdxModel model = (MdxModel) this.model; final MdxModel model = (MdxModel) this.model;
final List<GenericObject> sortedGenericObjects = model.sortedGenericObjects; final List<GenericObject> sortedGenericObjects = model.sortedGenericObjects;
final Scene scene = this.scene; 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 // Update the nodes
for (int i = 0, l = sortedNodes.length; i < l; i++) { for (int i = 0, l = sortedNodes.length; i < l; i++) {

View File

@ -127,6 +127,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
public static int DEBUG_DEPTH = 9999; public static int DEBUG_DEPTH = 9999;
private static final War3ID ABILITY_HERO_RAWCODE = War3ID.fromString("AHer"); 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_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 Color PLACEHOLDER_GOLD_COLOR = new Color(1.0f, 220f / 255f, 0f, 1.0f);
private static final War3ID UNIT_FILE = War3ID.fromString("umdl"); 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 AbilityUI heroUI = War3MapViewer.this.abilityDataUI.getUI(ABILITY_HERO_RAWCODE);
final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source); final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source);
final String heroLevelUpArt = heroUI.getCasterArt(0); final String heroLevelUpArt = heroUI.getCasterArt(0);
final MdxModel heroLevelUpModel = loadModel(heroLevelUpArt); spawnFxOnOrigin(renderUnit, heroLevelUpArt);
if (heroLevelUpModel != null) { }
final MdxComplexInstance modelInstance = (MdxComplexInstance) heroLevelUpModel
.addInstance();
modelInstance.setTeamColor(source.getPlayerIndex());
final MdxModel model = (MdxModel) renderUnit.instance.model; @Override
int index = -1; public void heroRevived(CUnit source) {
for (int i = 0; i < model.attachments.size(); i++) { final AbilityUI reviveUI = War3MapViewer.this.abilityDataUI.getUI(ABILITY_REVIVE_RAWCODE);
final Attachment attachment = model.attachments.get(i); final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source);
if (attachment.getName().startsWith("origin ref")) { CPlayer player = simulation.getPlayer(source.getPlayerIndex());
index = i; final String heroReviveArt = reviveUI.getTargetArt(player.getRace().ordinal());
break; spawnFxOnOrigin(renderUnit, heroReviveArt);
} MutableGameObject row = allObjectData.getUnits().get(source.getTypeId());
}
if (index != -1) {
final MdxNode attachment = renderUnit.instance.getAttachment(index);
modelInstance.setParent(attachment);
}
else {
modelInstance.setLocation(renderUnit.location);
}
modelInstance.setScene(War3MapViewer.this.worldScene); // Recreate unit shadow.... is needed here
SequenceUtils.randomBirthSequence(modelInstance);
War3MapViewer.this.projectiles final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0);
.add(new RenderAttackInstant(modelInstance, War3MapViewer.this, float unitX = source.getX();
(float) Math.toRadians(renderUnit.getSimulationUnit().getFacing()))); 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); 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) { protected BufferedImage getDestructablePathingPixelMap(final MutableGameObject row) {
return loadPathingTexture(row.getFieldAsString(DESTRUCTABLE_PATHING, 0)); return loadPathingTexture(row.getFieldAsString(DESTRUCTABLE_PATHING, 0));
} }

View File

@ -147,9 +147,9 @@ public class RenderUnit implements RenderWidget {
public void populateCommandCard(final CSimulation game, final GameUI gameUI, public void populateCommandCard(final CSimulation game, final GameUI gameUI,
final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI, final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI,
final int subMenuOrderId) { final int subMenuOrderId, boolean multiSelect) {
final CommandCardPopulatingAbilityVisitor commandCardPopulatingVisitor = CommandCardPopulatingAbilityVisitor.INSTANCE 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()) { for (final CAbility ability : this.simulationUnit.getAbilities()) {
ability.visit(commandCardPopulatingVisitor); ability.visit(commandCardPopulatingVisitor);
} }
@ -271,8 +271,13 @@ public class RenderUnit implements RenderWidget {
removeSplats(map); removeSplats(map);
} }
if (boneCorpse && !this.boneCorpse) { if (boneCorpse && !this.boneCorpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE, if(simulationUnit.getUnitType().isHero()) {
this.simulationUnit.getEndingDecayTime(map.simulation), true); 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) { else if (corpse && !this.corpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH, this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH,

View File

@ -56,6 +56,7 @@ public interface RenderWidget {
private float currentSpeedRatio; private float currentSpeedRatio;
private boolean currentlyAllowingRarityVariations; private boolean currentlyAllowingRarityVariations;
private final Queue<QueuedAnimation> animationQueue = new LinkedList<>(); private final Queue<QueuedAnimation> animationQueue = new LinkedList<>();
private int lastWalkFrame = -1;
public UnitAnimationListenerImpl(final MdxComplexInstance instance) { public UnitAnimationListenerImpl(final MdxComplexInstance instance) {
this.instance = instance; this.instance = instance;
@ -98,8 +99,14 @@ public interface RenderWidget {
this.recycleSet.addAll(this.secondaryAnimationTags); this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags); this.recycleSet.addAll(secondaryAnimationTags);
this.instance.setAnimationSpeed(speedRatio); this.instance.setAnimationSpeed(speedRatio);
if(animationName != PrimaryTag.WALK && currentAnimation == PrimaryTag.WALK) {
lastWalkFrame = instance.frame;
}
if (SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet, if (SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations) != null) { allowRarityVariations) != null) {
if(lastWalkFrame != -1 && animationName == PrimaryTag.WALK && currentAnimation != PrimaryTag.WALK) {
instance.setFrame(lastWalkFrame);
}
this.currentAnimation = animationName; this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags; this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations; this.currentlyAllowingRarityVariations = allowRarityVariations;
@ -116,9 +123,15 @@ public interface RenderWidget {
this.recycleSet.clear(); this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags); this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(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, final Sequence sequence = SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations); allowRarityVariations);
if (sequence != null) { if (sequence != null) {
if(lastWalkFrame != -1 && animationName == PrimaryTag.WALK && currentAnimation != PrimaryTag.WALK) {
instance.setFrame(lastWalkFrame);
}
this.currentAnimation = animationName; this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags; this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations; this.currentlyAllowingRarityVariations = allowRarityVariations;

View File

@ -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_Y = War3ID.fromString("ubpy");
private static final War3ID UNIT_ICON_NORMAL = War3ID.fromString("uico"); private static final War3ID UNIT_ICON_NORMAL = War3ID.fromString("uico");
private static final War3ID UNIT_TIP = War3ID.fromString("utip"); 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 UNIT_UBER_TIP = War3ID.fromString("utub");
private static final War3ID ITEM_ICON_NORMAL_X = War3ID.fromString("ubpx"); 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 static final War3ID UPGRADE_UBER_TIP = War3ID.fromString("gub1");
private final Map<War3ID, AbilityUI> rawcodeToUI = new HashMap<>(); private final Map<War3ID, AbilityUI> rawcodeToUI = new HashMap<>();
private final Map<War3ID, IconUI> rawcodeToUnitUI = new HashMap<>(); private final Map<War3ID, UnitIconUI> rawcodeToUnitUI = new HashMap<>();
private final Map<War3ID, ItemUI> rawcodeToItemUI = new HashMap<>(); private final Map<War3ID, ItemUI> rawcodeToItemUI = new HashMap<>();
private final Map<War3ID, List<IconUI>> rawcodeToUpgradeUI = new HashMap<>(); private final Map<War3ID, List<IconUI>> rawcodeToUpgradeUI = new HashMap<>();
private final IconUI moveUI; private final IconUI moveUI;
@ -138,11 +140,13 @@ public class AbilityDataUI {
final int iconNormalX = abilityTypeData.getFieldAsInteger(UNIT_ICON_NORMAL_X, 0); final int iconNormalX = abilityTypeData.getFieldAsInteger(UNIT_ICON_NORMAL_X, 0);
final int iconNormalY = abilityTypeData.getFieldAsInteger(UNIT_ICON_NORMAL_Y, 0); final int iconNormalY = abilityTypeData.getFieldAsInteger(UNIT_ICON_NORMAL_Y, 0);
final String iconTip = abilityTypeData.getFieldAsString(UNIT_TIP, 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 String iconUberTip = abilityTypeData.getFieldAsString(UNIT_UBER_TIP, 0);
final Texture iconNormal = gameUI.loadTexture(iconNormalPath); final Texture iconNormal = gameUI.loadTexture(iconNormalPath);
final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix)); final Texture iconNormalDisabled = gameUI.loadTexture(disable(iconNormalPath, disabledPrefix));
this.rawcodeToUnitUI.put(alias, 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()) { for (final War3ID alias : itemData.keySet()) {
final MutableGameObject abilityTypeData = itemData.get(alias); final MutableGameObject abilityTypeData = itemData.get(alias);
@ -212,7 +216,7 @@ public class AbilityDataUI {
return this.rawcodeToUI.get(rawcode); return this.rawcodeToUI.get(rawcode);
} }
public IconUI getUnitUI(final War3ID rawcode) { public UnitIconUI getUnitUI(final War3ID rawcode) {
return this.rawcodeToUnitUI.get(rawcode); return this.rawcodeToUnitUI.get(rawcode);
} }

View File

@ -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;
}
}

View File

@ -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.AbilityDataUI;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityUI; 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.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.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; 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.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.hero.CAbilityHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; 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.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.combat.attacks.CUnitAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; 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.players.CPlayer;
@ -42,20 +44,24 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
private CommandButtonListener commandButtonListener; private CommandButtonListener commandButtonListener;
private AbilityDataUI abilityDataUI; private AbilityDataUI abilityDataUI;
private int menuBaseOrderId; private int menuBaseOrderId;
private boolean multiSelect;
private boolean hasStop; private boolean hasStop;
private final CommandCardActivationReceiverPreviewCallback previewCallback = new CommandCardActivationReceiverPreviewCallback(); private final CommandCardActivationReceiverPreviewCallback previewCallback = new CommandCardActivationReceiverPreviewCallback();
private GameUI gameUI; private GameUI gameUI;
private boolean hasCancel ;
public CommandCardPopulatingAbilityVisitor reset(final CSimulation game, final GameUI gameUI, final CUnit unit, public CommandCardPopulatingAbilityVisitor reset(final CSimulation game, final GameUI gameUI, final CUnit unit,
final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI, final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI,
final int menuBaseOrderId) { final int menuBaseOrderId, boolean multiSelect) {
this.game = game; this.game = game;
this.gameUI = gameUI; this.gameUI = gameUI;
this.unit = unit; this.unit = unit;
this.commandButtonListener = commandButtonListener; this.commandButtonListener = commandButtonListener;
this.abilityDataUI = abilityDataUI; this.abilityDataUI = abilityDataUI;
this.menuBaseOrderId = menuBaseOrderId; this.menuBaseOrderId = menuBaseOrderId;
this.multiSelect = multiSelect;
this.hasStop = false; this.hasStop = false;
hasCancel = false;
return this; return this;
} }
@ -207,6 +213,9 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
} }
} }
else { else {
if(multiSelect) {
return;
}
addCommandButton(ability, buildUI, ability.getHandleId(), ability.getBaseOrderId(), 0, false, true); addCommandButton(ability, buildUI, ability.getHandleId(), ability.getBaseOrderId(), 0, false, true);
} }
} }
@ -217,8 +226,14 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
} }
private void addCommandButton(final CAbility ability, final IconUI iconUI, final int handleId, final int orderId, private void addCommandButton(final CAbility ability, final IconUI iconUI, final int handleId, final int orderId,
final int autoCastOrderId, final boolean autoCastActive, final boolean menuButton, int goldCost, final int autoCastOrderId, final boolean autoCastActive, final boolean menuButton, int goldCost,
int lumberCost, int foodCost) { int lumberCost, int foodCost) {
addCommandButton(ability, iconUI, iconUI.getToolTip(), iconUI.getButtonPositionX(), iconUI.getButtonPositionY(), handleId, orderId, autoCastOrderId, autoCastActive, menuButton, goldCost, lumberCost, foodCost);
}
private void addCommandButton(final CAbility ability, final IconUI iconUI, String toolTip, int buttonPosX, int buttonPosY, final int handleId, final int orderId,
final int autoCastOrderId, final boolean autoCastActive, final boolean menuButton, int goldCost,
int lumberCost, int foodCost) {
ability.checkCanUse(this.game, this.unit, orderId, this.previewCallback.reset()); ability.checkCanUse(this.game, this.unit, orderId, this.previewCallback.reset());
final boolean active = ((this.unit.getCurrentBehavior() != null) final boolean active = ((this.unit.getCurrentBehavior() != null)
&& (orderId == this.unit.getCurrentBehavior().getHighlightOrderId())); && (orderId == this.unit.getCurrentBehavior().getHighlightOrderId()));
@ -235,7 +250,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
} }
this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(), this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(),
disabled ? iconUI.getIconDisabled() : iconUI.getIcon(), handleId, disabled ? 0 : orderId, disabled ? iconUI.getIconDisabled() : iconUI.getIcon(), handleId, disabled ? 0 : orderId,
autoCastOrderId, active, autoCastActive, menuButton, iconUI.getToolTip(), uberTip, goldCost, lumberCost, autoCastOrderId, active, autoCastActive, menuButton, toolTip, uberTip, goldCost, lumberCost,
foodCost); foodCost);
} }
@ -248,6 +263,36 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
return null; return null;
} }
@Override
public Void accept(CAbilityReviveHero ability) {
if ((this.menuBaseOrderId == 0) && ability.isIconShowing()) {
int heroIndex = 0;
for(CUnit playerHero: game.getPlayerHeroes(unit.getPlayerIndex())) {
CAbilityHero heroData = playerHero.getHeroData();
if(playerHero.isDead() && heroData != null && heroData.isAwaitingRevive()) {
UnitIconUI unitUI = this.abilityDataUI.getUnitUI(playerHero.getTypeId());
if (unitUI != null) {
final CUnitType simulationUnitType = playerHero.getUnitType();
int goldCost = game.getGameplayConstants().getHeroReviveGoldCost(simulationUnitType.getGoldCost(), heroData.getHeroLevel());
int lumberCost = game.getGameplayConstants().getHeroReviveLumberCost(simulationUnitType.getLumberCost(), heroData.getHeroLevel());
addCommandButton(ability, unitUI, unitUI.getReviveTip() + " - " + heroData.getProperName(), heroIndex++, 0, ability.getHandleId(), playerHero.getHandleId(), 0, false, false,
goldCost, lumberCost,
simulationUnitType.getFoodUsed());
}
}
}
if (this.unit.getBuildQueueTypes()[0] != null) {
if(!hasCancel) {
hasCancel = true;
addCommandButton(ability, this.abilityDataUI.getCancelTrainUI(), ability.getHandleId(), OrderIds.cancel,
0, false, false);
}
}
}
return null;
}
@Override @Override
public Void accept(final CAbilityQueue ability) { public Void accept(final CAbilityQueue ability) {
if ((this.menuBaseOrderId == 0) && ability.isIconShowing()) { if ((this.menuBaseOrderId == 0) && ability.isIconShowing()) {
@ -270,8 +315,11 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
} }
} }
if (this.unit.getBuildQueueTypes()[0] != null) { if (this.unit.getBuildQueueTypes()[0] != null) {
addCommandButton(ability, this.abilityDataUI.getCancelTrainUI(), ability.getHandleId(), OrderIds.cancel, if(!hasCancel) {
0, false, false); hasCancel = true;
addCommandButton(ability, this.abilityDataUI.getCancelTrainUI(), ability.getHandleId(), OrderIds.cancel,
0, false, false);
}
} }
} }
return null; return null;

View File

@ -29,8 +29,36 @@ public class CGameplayConstants {
private final float defenseArmor; private final float defenseArmor;
private final int heroMaxReviveCostGold;
private final int heroMaxReviveCostLumber;
private final int heroMaxReviveTime;
private final int heroMaxAwakenCostGold;
private final int heroMaxAwakenCostLumber;
private final float heroReviveManaStart;
private final float heroReviveManaFactor;
private final float heroReviveLifeFactor;
private final float heroAwakenManaStart;
private final float heroAwakenManaFactor;
private final float heroAwakenLifeFactor;
private final int heroExpRange; private final int heroExpRange;
private final float reviveBaseFactor;
private final float reviveLevelFactor;
private final float reviveBaseLumberFactor;
private final float reviveLumberLevelFactor;
private final float reviveMaxFactor;
private final float reviveTimeFactor;
private final float reviveMaxTimeFactor;
private final float awakenBaseFactor;
private final float awakenLevelFactor;
private final float awakenBaseLumberFactor;
private final float awakenLumberLevelFactor;
private final float awakenMaxFactor;
private final int maxHeroLevel; private final int maxHeroLevel;
private final int maxUnitLevel; private final int maxUnitLevel;
private final int[] needHeroXp; private final int[] needHeroXp;
@ -123,8 +151,37 @@ public class CGameplayConstants {
this.maxLevelHeroesDrainExp = miscData.getFieldValue("MaxLevelHeroesDrainExp") != 0; this.maxLevelHeroesDrainExp = miscData.getFieldValue("MaxLevelHeroesDrainExp") != 0;
this.buildingKillsGiveExp = miscData.getFieldValue("BuildingKillsGiveExp") != 0; this.buildingKillsGiveExp = miscData.getFieldValue("BuildingKillsGiveExp") != 0;
this.heroMaxReviveCostGold = miscData.getFieldValue("HeroMaxReviveCostGold");
this.heroMaxReviveCostLumber = miscData.getFieldValue("HeroMaxReviveCostLumber");
this.heroMaxReviveTime = miscData.getFieldValue("HeroMaxReviveTime");
this.heroMaxAwakenCostGold = miscData.getFieldValue("HeroMaxAwakenCostGold");
this.heroMaxAwakenCostLumber = miscData.getFieldValue("HeroMaxAwakenCostLumber");
this.heroReviveManaStart = miscData.getFieldFloatValue("HeroReviveManaStart");
this.heroReviveManaFactor = miscData.getFieldFloatValue("HeroReviveManaFactor");
this.heroReviveLifeFactor = miscData.getFieldFloatValue("HeroReviveLifeFactor");
this.heroAwakenManaStart = miscData.getFieldFloatValue("HeroAwakenManaStart");
this.heroAwakenManaFactor = miscData.getFieldFloatValue("HeroAwakenManaFactor");
this.heroAwakenLifeFactor = miscData.getFieldFloatValue("HeroAwakenLifeFactor");
this.heroExpRange = miscData.getFieldValue("HeroExpRange"); this.heroExpRange = miscData.getFieldValue("HeroExpRange");
this.reviveBaseFactor = miscData.getFieldFloatValue("ReviveBaseFactor");
this.reviveLevelFactor = miscData.getFieldFloatValue("ReviveLevelFactor");
this.reviveBaseLumberFactor = miscData.getFieldFloatValue("ReviveBaseLumberFactor");
this.reviveLumberLevelFactor = miscData.getFieldFloatValue("ReviveLumberLevelFactor");
this.reviveMaxFactor = miscData.getFieldFloatValue("ReviveMaxFactor");
this.reviveTimeFactor = miscData.getFieldFloatValue("ReviveTimeFactor");
this.reviveMaxTimeFactor = miscData.getFieldFloatValue("ReviveMaxTimeFactor");
this.awakenBaseFactor = miscData.getFieldFloatValue("AwakenBaseFactor");
this.awakenLevelFactor = miscData.getFieldFloatValue("AwakenLevelFactor");
this.awakenBaseLumberFactor = miscData.getFieldFloatValue("AwakenBaseLumberFactor");
this.awakenLumberLevelFactor = miscData.getFieldFloatValue("AwakenLumberLevelFactor");
this.awakenMaxFactor = miscData.getFieldFloatValue("AwakenMaxFactor");
this.maxHeroLevel = miscData.getFieldValue("MaxHeroLevel"); this.maxHeroLevel = miscData.getFieldValue("MaxHeroLevel");
this.maxUnitLevel = miscData.getFieldValue("MaxUnitLevel"); this.maxUnitLevel = miscData.getFieldValue("MaxUnitLevel");
@ -364,6 +421,33 @@ public class CGameplayConstants {
return this.spellCastRangeBuffer; return this.spellCastRangeBuffer;
} }
public int getHeroReviveGoldCost(int originalCost, int level) {
int goldRevivalCost = (int)(originalCost * (reviveBaseFactor + (reviveLevelFactor * (level-1))));
return Math.min(goldRevivalCost, (int)(originalCost * reviveMaxFactor));
}
public int getHeroReviveLumberCost(int originalCost, int level) {
int lumberRevivalCost = (int)(originalCost * (reviveBaseLumberFactor + (reviveLumberLevelFactor * (level-1))));
return Math.min(lumberRevivalCost, (int)(originalCost * reviveMaxFactor));
}
public int getHeroReviveTime(int originalTime, int level) {
int revivalTime = (int)(originalTime * level * reviveTimeFactor);
return Math.min(revivalTime, (int)(originalTime * reviveMaxTimeFactor));
}
public float getHeroReviveLifeFactor() {
return heroReviveLifeFactor;
}
public float getHeroReviveManaFactor() {
return heroReviveManaFactor;
}
public float getHeroReviveManaStart() {
return heroReviveManaStart;
}
private static int getTableValue(final int[] table, int level) { private static int getTableValue(final int[] table, int level) {
if (level <= 0) { if (level <= 0) {
return 0; return 0;

View File

@ -11,7 +11,9 @@ public interface CPlayerStateListener {
void upkeepChanged(); void upkeepChanged();
public static final class CPlayerStateNotifier extends SubscriberSetNotifier<CPlayerStateListener> void heroDeath();
public static final class CPlayerStateNotifier extends SubscriberSetNotifier<CPlayerStateListener>
implements CPlayerStateListener { implements CPlayerStateListener {
@Override @Override
public void goldChanged() { public void goldChanged() {
@ -40,5 +42,12 @@ public interface CPlayerStateListener {
listener.upkeepChanged(); listener.upkeepChanged();
} }
} }
@Override
public void heroDeath() {
for (final CPlayerStateListener listener : set) {
listener.heroDeath();
}
}
} }
} }

View File

@ -114,7 +114,22 @@ public class CSimulation implements CPlayerAPI {
for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) { for (int i = 0; i < WarsmashConstants.MAX_PLAYERS; i++) {
final CBasePlayer configPlayer = config.getPlayer(i); final CBasePlayer configPlayer = config.getPlayer(i);
final War3MapConfigStartLoc startLoc = config.getStartLoc(configPlayer.getStartLocationIndex()); 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); configPlayer);
if(i < 3) { if(i < 3) {
for(int j = 0; j < 3; j++) { for(int j = 0; j < 3; j++) {
@ -189,6 +204,9 @@ public class CSimulation implements CPlayerAPI {
this.newUnits.add(unit); this.newUnits.add(unit);
this.handleIdToUnit.put(unit.getHandleId(), unit); this.handleIdToUnit.put(unit.getHandleId(), unit);
this.worldCollision.addUnit(unit); this.worldCollision.addUnit(unit);
if(unit.getHeroData() != null) {
heroCreateEvent(unit);
}
return unit; return unit;
} }
@ -389,6 +407,10 @@ public class CSimulation implements CPlayerAPI {
public void unitTrainedEvent(final CUnit trainingUnit, final CUnit trainedUnit) { public void unitTrainedEvent(final CUnit trainingUnit, final CUnit trainedUnit) {
this.simulationRenderController.spawnUnitReadySound(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) { public void unitRepositioned(final CUnit cUnit) {
this.simulationRenderController.unitRepositioned(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 static final class TimeOfDayVariableEvent extends VariableEvent {
private final GlobalScope globalScope; private final GlobalScope globalScope;

View File

@ -92,8 +92,7 @@ public class CUnit extends CWidget {
// questionable -- it already was -- but I meant for those to inform us // 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 // which fields shouldn't be persisted if we do game state save later
private transient CUnitStateNotifier stateNotifier = new CUnitStateNotifier(); private transient CUnitStateNotifier stateNotifier = new CUnitStateNotifier();
private transient List<CUnitStateListener> stateListenersNeedingAdd = new ArrayList<>(); private transient List<StateListenerUpdate> stateListenersUpdates = new ArrayList<>();
private transient List<CUnitStateListener> stateListenersNeedingRemove = new ArrayList<>();
private final float acquisitionRange; private final float acquisitionRange;
private transient static AutoAttackTargetFinderEnum autoAttackTargetFinderEnum = new AutoAttackTargetFinderEnum(); private transient static AutoAttackTargetFinderEnum autoAttackTargetFinderEnum = new AutoAttackTargetFinderEnum();
@ -246,14 +245,16 @@ public class CUnit extends CWidget {
* this unit from the game. * this unit from the game.
*/ */
public boolean update(final CSimulation game) { public boolean update(final CSimulation game) {
for (final CUnitStateListener listener : this.stateListenersNeedingAdd) { for(StateListenerUpdate update: this.stateListenersUpdates) {
this.stateNotifier.subscribe(listener); 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 (isDead()) {
if (this.collisionRectangle != null) { if (this.collisionRectangle != null) {
// Moved this here because doing it on "kill" was able to happen in some cases // 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 (!this.unitType.isDecay()) {
// if we dont raise AND dont decay, then now that death anim is over // if we dont raise AND dont decay, then now that death anim is over
// we just delete the unit // we just delete the unit
if(unitType.isHero()) {
if(!getHeroData().isAwaitingRevive()) {
setHidden(true);
getHeroData().setAwaitingRevive(true);
game.heroDeathEvent(this);
}
return false;
}
return true; return true;
} }
this.deathTurnTick = gameTurnTick; this.deathTurnTick = gameTurnTick;
@ -287,6 +296,14 @@ public class CUnit extends CWidget {
} }
else if (game.getGameTurnTick() > (this.deathTurnTick else if (game.getGameTurnTick() > (this.deathTurnTick
+ (int) (getEndingDecayTime(game) / WarsmashConstants.SIMULATION_STEP_TIME))) { + (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; return true;
} }
} }
@ -340,6 +357,15 @@ public class CUnit extends CWidget {
this.queuedUnitFoodPaid = true; 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 { else {
this.queuedUnitFoodPaid = true; this.queuedUnitFoodPaid = true;
System.err.println( System.err.println(
@ -372,6 +398,42 @@ public class CUnit extends CWidget {
this.stateNotifier.queueChanged(); 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) { else if (this.buildQueueTypes[0] == QueueItemType.RESEARCH) {
final CUnitType trainedUnitType = game.getUnitData().getUnitType(queuedRawcode); final CUnitType trainedUnitType = game.getUnitData().getUnitType(queuedRawcode);
if (this.constructionProgress >= trainedUnitType.getBuildTime()) { if (this.constructionProgress >= trainedUnitType.getBuildTime()) {
@ -1022,11 +1084,11 @@ public class CUnit extends CWidget {
} }
public void addStateListener(final CUnitStateListener listener) { public void addStateListener(final CUnitStateListener listener) {
this.stateListenersNeedingAdd.add(listener); this.stateListenersUpdates.add(new StateListenerUpdate(listener, StateListenerUpdateType.ADD));
} }
public void removeStateListener(final CUnitStateListener listener) { public void removeStateListener(final CUnitStateListener listener) {
this.stateListenersNeedingRemove.add(listener); this.stateListenersUpdates.add(new StateListenerUpdate(listener, StateListenerUpdateType.REMOVE));
} }
public boolean isCorpse() { public boolean isCorpse() {
@ -1089,11 +1151,12 @@ public class CUnit extends CWidget {
@Override @Override
public boolean call(final CUnit unit) { public boolean call(final CUnit unit) {
if (!this.game.getPlayer(this.source.getPlayerIndex()).hasAlliance(unit.getPlayerIndex(), 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()) { for (final CUnitAttack attack : this.source.getAttacks()) {
if (this.source.canReach(unit, this.source.acquisitionRange) if (this.source.canReach(unit, this.source.acquisitionRange)
&& unit.canBeTargetedBy(this.game, this.source, attack.getTargetsAllowed()) && 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) { if (this.source.currentBehavior != null) {
this.source.currentBehavior.end(this.game, false); this.source.currentBehavior.end(this.game, false);
} }
@ -1260,9 +1323,15 @@ public class CUnit extends CWidget {
switch (this.buildQueueTypes[0]) { switch (this.buildQueueTypes[0]) {
case RESEARCH: case RESEARCH:
return 999; // TODO return 999; // TODO
case UNIT: case UNIT: {
final CUnitType trainedUnitType = simulation.getUnitData().getUnitType(this.buildQueue[0]); final CUnitType trainedUnitType = simulation.getUnitData().getUnitType(this.buildQueue[0]);
return trainedUnitType.getBuildTime(); 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: default:
return 0; return 0;
} }
@ -1278,22 +1347,41 @@ public class CUnit extends CWidget {
switch (cancelledType) { switch (cancelledType) {
case RESEARCH: case RESEARCH:
break; break;
case UNIT: case UNIT: {
final CPlayer player = game.getPlayer(this.playerIndex); final CPlayer player = game.getPlayer(this.playerIndex);
final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]); final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]);
player.setFoodUsed(player.getFoodUsed() - unitType.getFoodUsed()); player.setFoodUsed(player.getFoodUsed() - unitType.getFoodUsed());
break; 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) { switch (cancelledType) {
case RESEARCH: case RESEARCH:
break; break;
case UNIT: case UNIT: {
final CPlayer player = game.getPlayer(this.playerIndex); final CPlayer player = game.getPlayer(this.playerIndex);
final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]); final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[cancelIndex]);
player.refundFor(unitType); player.refundFor(unitType);
break; 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++) { for (int i = cancelIndex; i < (this.buildQueueTypes.length - 1); i++) {
setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]); setBuildQueueItem(game, i, this.buildQueue[i + 1], this.buildQueueTypes[i + 1]);
} }
@ -1309,17 +1397,32 @@ public class CUnit extends CWidget {
this.buildQueueTypes[index] = queueItemType; this.buildQueueTypes[index] = queueItemType;
if (index == 0) { if (index == 0) {
this.queuedUnitFoodPaid = true; this.queuedUnitFoodPaid = true;
if ((rawcode != null) && (queueItemType == QueueItemType.UNIT)) { if (rawcode != null) {
final CPlayer player = game.getPlayer(this.playerIndex); if(queueItemType == QueueItemType.UNIT) {
final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[index]); final CPlayer player = game.getPlayer(this.playerIndex);
if (unitType.getFoodUsed() != 0) { final CUnitType unitType = game.getUnitData().getUnitType(this.buildQueue[index]);
final int newFoodUsed = player.getFoodUsed() + unitType.getFoodUsed(); if (unitType.getFoodUsed() != 0) {
if (newFoodUsed <= player.getFoodCap()) { final int newFoodUsed = player.getFoodUsed() + unitType.getFoodUsed();
player.setFoodUsed(newFoodUsed); if (newFoodUsed <= player.getFoodCap()) {
player.setFoodUsed(newFoodUsed);
}
else {
this.queuedUnitFoodPaid = false;
game.getCommandErrorListener(this.playerIndex).showNoFoodError();
}
} }
else { } else if(queueItemType == QueueItemType.HERO_REVIVE) {
this.queuedUnitFoodPaid = false; final CPlayer player = game.getPlayer(this.playerIndex);
game.getCommandErrorListener(this.playerIndex).showNoFoodError(); 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) { public void queueResearch(final CSimulation game, final War3ID rawcode) {
queue(game, rawcode, QueueItemType.RESEARCH); queue(game, rawcode, QueueItemType.RESEARCH);
} }
public static enum QueueItemType { public static enum QueueItemType {
UNIT, UNIT,
RESEARCH; RESEARCH,
HERO_REVIVE;
} }
public void setRallyPoint(final AbilityTarget target) { public void setRallyPoint(final AbilityTarget target) {
@ -1576,4 +1690,26 @@ public class CUnit extends CWidget {
setLife(simulation, 0); setLife(simulation, 0);
simulation.getWorldCollision().removeUnit(this); 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;
}
}
} }

View File

@ -75,6 +75,7 @@ public class CUnitType {
private final int properNamesCount; private final int properNamesCount;
private final boolean canFlee; private final boolean canFlee;
private final int priority; private final int priority;
private boolean revivesHeroes;
public CUnitType(final String name, final String legacyName, final War3ID typeId, final int maxLife, 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, 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 strengthPerLevel, final int agility, final float agilityPerLevel, final int intelligence,
final float intelligencePerLevel, final CPrimaryAttribute primaryAttribute, final float intelligencePerLevel, final CPrimaryAttribute primaryAttribute,
final List<War3ID> heroAbilityList, final List<String> heroProperNames, final int properNamesCount, final List<War3ID> heroAbilityList, final List<String> heroProperNames, final int properNamesCount,
final boolean canFlee, final int priority) { final boolean canFlee, final int priority, boolean revivesHeroes) {
this.name = name; this.name = name;
this.legacyName = legacyName; this.legacyName = legacyName;
this.typeId = typeId; this.typeId = typeId;
@ -146,6 +147,7 @@ public class CUnitType {
this.properNamesCount = properNamesCount; this.properNamesCount = properNamesCount;
this.canFlee = canFlee; this.canFlee = canFlee;
this.priority = priority; this.priority = priority;
this.revivesHeroes = revivesHeroes;
} }
public String getName() { public String getName() {
@ -359,4 +361,8 @@ public class CUnitType {
public int getPriority() { public int getPriority() {
return this.priority; return this.priority;
} }
public boolean isRevivesHeroes() {
return revivesHeroes;
}
} }

View File

@ -33,6 +33,7 @@ public class CAbilityMove extends AbstractCAbility {
switch (orderId) { switch (orderId) {
case OrderIds.smart: case OrderIds.smart:
case OrderIds.patrol: case OrderIds.patrol:
case OrderIds.move:
if ((target instanceof CUnit) && (target != unit)) { if ((target instanceof CUnit) && (target != unit)) {
receiver.targetOk(target); receiver.targetOk(target);
} }
@ -98,7 +99,9 @@ public class CAbilityMove extends AbstractCAbility {
@Override @Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) { 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 @Override

View File

@ -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.hero.CAbilityHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; 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.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 * 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> {
T accept(CAbilityQueue ability); T accept(CAbilityQueue ability);
T accept(CAbilityReviveHero ability);
T accept(GenericSingleIconActiveAbility ability); T accept(GenericSingleIconActiveAbility ability);
T accept(CAbilityRally ability); T accept(CAbilityRally ability);

View File

@ -28,6 +28,7 @@ public class CAbilityHero extends AbstractCAbility {
private HeroStatValue agility; private HeroStatValue agility;
private HeroStatValue intelligence; private HeroStatValue intelligence;
private String properName; private String properName;
private boolean awaitingRevive;
public CAbilityHero(final int handleId, final List<War3ID> skillsAvailable) { public CAbilityHero(final int handleId, final List<War3ID> skillsAvailable) {
super(handleId); super(handleId);
@ -168,6 +169,14 @@ public class CAbilityHero extends AbstractCAbility {
return this.properName; 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) { public void addXp(final CSimulation simulation, final CUnit unit, final int xp) {
this.xp += xp; this.xp += xp;
final CGameplayConstants gameplayConstants = simulation.getGameplayConstants(); final CGameplayConstants gameplayConstants = simulation.getGameplayConstants();
@ -232,7 +241,7 @@ public class CAbilityHero extends AbstractCAbility {
unit.setAgilityDefenseBonus(agilityDefenseBonus); unit.setAgilityDefenseBonus(agilityDefenseBonus);
} }
public static final class HeroStatValue { public static final class HeroStatValue {
private final float perLevelFactor; private final float perLevelFactor;
private int base; private int base;
private int bonus; private int bonus;

View File

@ -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<CWidget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityPointTarget target, final AbilityTargetCheckReceiver<AbilityPointTarget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public final void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityTargetCheckReceiver<Void> 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> T visit(final CAbilityVisitor<T> visitor) {
return visitor.accept(this);
}
@Override
public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) {
}
}

View File

@ -9,6 +9,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting
public class CBehaviorFollow extends CAbstractRangedBehavior { public class CBehaviorFollow extends CAbstractRangedBehavior {
private int higlightOrderId; private int higlightOrderId;
private boolean justAutoAttacked = false;
public CBehaviorFollow(final CUnit unit) { public CBehaviorFollow(final CUnit unit) {
super(unit); super(unit);
@ -26,27 +27,37 @@ public class CBehaviorFollow extends CAbstractRangedBehavior {
@Override @Override
public boolean isWithinRange(final CSimulation simulation) { public boolean isWithinRange(final CSimulation simulation) {
if(justAutoAttacked = this.unit.autoAcquireAttackTargets(simulation, false)) {
return true;
}
return this.unit.canReach(this.target, this.unit.getAcquisitionRange()); return this.unit.canReach(this.target, this.unit.getAcquisitionRange());
} }
@Override @Override
protected CBehavior update(final CSimulation simulation, final boolean withinFacingWindow) { protected CBehavior update(final CSimulation simulation, final boolean withinFacingWindow) {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, false); this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, false);
return this; return this;
} }
@Override @Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) { protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
unit.setDefaultBehavior(unit.getStopBehavior());
return this.unit.pollNextOrderBehavior(simulation); return this.unit.pollNextOrderBehavior(simulation);
} }
@Override @Override
protected boolean checkTargetStillValid(final CSimulation simulation) { 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); return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE);
} }
@Override @Override
protected void resetBeforeMoving(final CSimulation simulation) { protected void resetBeforeMoving(final CSimulation simulation) {
} }
@Override @Override

View File

@ -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.hero.CAbilityHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; 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.CAbilityRally;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero;
public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisitor<Void> { public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisitor<Void> {
public static final AbilityDisableWhileUnderConstructionVisitor INSTANCE = new AbilityDisableWhileUnderConstructionVisitor(); public static final AbilityDisableWhileUnderConstructionVisitor INSTANCE = new AbilityDisableWhileUnderConstructionVisitor();
@ -105,6 +106,13 @@ public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisi
return null; return null;
} }
@Override
public Void accept(CAbilityReviveHero ability) {
ability.setDisabled(true);
ability.setIconShowing(false);
return null;
}
@Override @Override
public Void accept(final GenericSingleIconActiveAbility ability) { public Void accept(final GenericSingleIconActiveAbility ability) {
ability.setDisabled(true); ability.setDisabled(true);

View File

@ -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.hero.CPrimaryAttribute;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; 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.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.CAttackType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; 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 STRUCTURES_BUILT = War3ID.fromString("ubui");
private static final War3ID UNITS_TRAINED = War3ID.fromString("utra"); private static final War3ID UNITS_TRAINED = War3ID.fromString("utra");
private static final War3ID RESEARCHES_AVAILABLE = War3ID.fromString("ures"); 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 UNIT_RACE = War3ID.fromString("urac");
private static final War3ID REQUIRES = War3ID.fromString("ureq"); private static final War3ID REQUIRES = War3ID.fromString("ureq");
@ -243,7 +245,10 @@ public class CUnitData {
if (!unitsTrained.isEmpty() || !researchesAvailable.isEmpty()) { if (!unitsTrained.isEmpty() || !researchesAvailable.isEmpty()) {
unit.add(simulation, new CAbilityQueue(handleIdAllocator.createId(), unitsTrained, researchesAvailable)); 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())); unit.add(simulation, new CAbilityRally(handleIdAllocator.createId()));
} }
if (unitTypeInstance.isHero()) { if (unitTypeInstance.isHero()) {
@ -450,6 +455,8 @@ public class CUnitData {
final int foodUsed = unitType.getFieldAsInteger(FOOD_USED, 0); final int foodUsed = unitType.getFieldAsInteger(FOOD_USED, 0);
final int foodMade = unitType.getFieldAsInteger(FOOD_MADE, 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 unitsTrainedString = unitType.getFieldAsString(UNITS_TRAINED, 0);
final String[] unitsTrainedStringItems = unitsTrainedString.trim().split(","); final String[] unitsTrainedStringItems = unitsTrainedString.trim().split(",");
final List<War3ID> unitsTrained = new ArrayList<>(); final List<War3ID> unitsTrained = new ArrayList<>();
@ -538,7 +545,7 @@ public class CUnitData {
goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes, requiredPathingTypes, goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes, requiredPathingTypes,
propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus, agility, agiPlus, propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus, agility, agiPlus,
intelligence, intPlus, primaryAttribute, heroAbilityList, heroProperNames, properNamesCount, intelligence, intPlus, primaryAttribute, heroAbilityList, heroProperNames, properNamesCount,
canFlee, priority); canFlee, priority, revivesHeroes);
this.unitIdToUnitType.put(typeId, unitTypeInstance); this.unitIdToUnitType.put(typeId, unitTypeInstance);
this.jassLegacyNameToUnitId.put(legacyName, typeId); this.jassLegacyNameToUnitId.put(legacyName, typeId);
} }

View File

@ -139,6 +139,13 @@ public class CPlayer extends CBasePlayer {
this.stateNotifier.goldChanged(); 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) { public void setUnitFoodUsed(final CUnit unit, final int foodUsed) {
this.foodUsed += unit.setFoodUsed(foodUsed); this.foodUsed += unit.setFoodUsed(foodUsed);
this.stateNotifier.foodChanged(); this.stateNotifier.foodChanged();
@ -148,4 +155,8 @@ public class CPlayer extends CBasePlayer {
this.foodCap += unit.setFoodMade(foodMade); this.foodCap += unit.setFoodMade(foodMade);
this.stateNotifier.foodChanged(); this.stateNotifier.foodChanged();
} }
public void onHeroDeath() {
stateNotifier.heroDeath();
}
} }

View File

@ -7,6 +7,11 @@ public class CWidgetAbilityTargetCheckReceiver implements AbilityTargetCheckRece
private CWidget target; private CWidget target;
public CWidgetAbilityTargetCheckReceiver reset() {
this.target = null;
return this;
}
@Override @Override
public void targetOk(final CWidget target) { public void targetOk(final CWidget target) {
this.target = target; this.target = target;

View File

@ -7,6 +7,11 @@ public class PointAbilityTargetCheckReceiver implements AbilityTargetCheckReceiv
private AbilityPointTarget target; private AbilityPointTarget target;
public PointAbilityTargetCheckReceiver reset() {
this.target = null;
return this;
}
@Override @Override
public void targetOk(final AbilityPointTarget target) { public void targetOk(final AbilityPointTarget target) {
this.target = target; this.target = target;

View File

@ -59,4 +59,6 @@ public interface SimulationRenderController {
void spawnAbilitySoundEffect(CUnit caster, War3ID alias); void spawnAbilitySoundEffect(CUnit caster, War3ID alias);
void unitPreferredSelectionReplacement(CUnit unit, CUnit newUnit); void unitPreferredSelectionReplacement(CUnit unit, CUnit newUnit);
void heroRevived(CUnit trainedUnit);
} }

View File

@ -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.AbilityDataUI;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI; 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.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.rendersim.commandbuttons.CommandButtonListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CGameplayConstants; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.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.inventory.CAbilityInventory;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; 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.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.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetVisitor; 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, abilityToUse.checkCanTargetNoTarget(this.war3MapViewer.simulation,
this.selectedUnit.getSimulationUnit(), orderId, noTargetReceiver); this.selectedUnit.getSimulationUnit(), orderId, noTargetReceiver);
if (noTargetReceiver.isTargetable()) { if (noTargetReceiver.isTargetable()) {
boolean shiftDown = isShiftDown();
this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), 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<Void> receiver = BooleanAbilityTargetCheckReceiver.<Void>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 { else {
this.activeCommand = abilityToUse; this.activeCommand = abilityToUse;
@ -1023,6 +1046,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
else { else {
this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(), this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(),
abilityHandleId, orderId, isShiftDown()); 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) { if (rightClick) {
this.war3MapViewer.getUiSounds().getSound("AutoCastButtonClick").play(this.uiScene.audioContext, 0, 0, 0); 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; this.hpBarFrameIndex = 0;
if (this.currentlyDraggingPointer == -1) { 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(); final SimpleStatusBarFrame simpleStatusBarFrame = getHpBar();
positionHealthBar(simpleStatusBarFrame, this.mouseOverUnit, 1.0f); 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)), simpleStatusBarFrame.getBarFrame().setColor(Math.min(1.0f, 2.0f - (lifeRatioRemaining * 2)),
Math.min(1.0f, lifeRatioRemaining * 2), 0, alpha); Math.min(1.0f, lifeRatioRemaining * 2), 0, alpha);
final Vector2 unprojected = this.uiViewport.unproject(screenCoordsVector); 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.setHeight(16);
simpleStatusBarFrame.addSetPoint( simpleStatusBarFrame.addSetPoint(
new SetPoint(FramePoint.CENTER, this.rootFrame, FramePoint.BOTTOMLEFT, unprojected.x, unprojected.y)); 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; return null;
} }
@Override
public Void accept(CAbilityReviveHero ability) {
handleTargetCursor(ability);
return null;
}
@Override @Override
public Void accept(final GenericSingleIconActiveAbility ability) { public Void accept(final GenericSingleIconActiveAbility ability) {
handleTargetCursor(ability); handleTargetCursor(ability);
@ -1999,8 +2036,18 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.queueIconFrames[i].setToolTip(upgradeUI.getToolTip()); this.queueIconFrames[i].setToolTip(upgradeUI.getToolTip());
this.queueIconFrames[i].setUberTip(upgradeUI.getUberTip()); this.queueIconFrames[i].setUberTip(upgradeUI.getUberTip());
break; 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: case UNIT:
default: default: {
final IconUI unitUI = this.war3MapViewer.getAbilityDataUI() final IconUI unitUI = this.war3MapViewer.getAbilityDataUI()
.getUnitUI(simulationUnit.getBuildQueue()[i]); .getUnitUI(simulationUnit.getBuildQueue()[i]);
this.queueIconFrames[i].setTexture(unitUI.getIcon()); this.queueIconFrames[i].setTexture(unitUI.getIcon());
@ -2008,6 +2055,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.queueIconFrames[i].setUberTip(unitUI.getUberTip()); this.queueIconFrames[i].setUberTip(unitUI.getUberTip());
break; break;
} }
}
} }
} }
this.simpleInfoPanelBuildingDetail.setVisible(!multiSelect); this.simpleInfoPanelBuildingDetail.setVisible(!multiSelect);
@ -2021,6 +2069,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
if (simulationUnit.getBuildQueueTypes()[0] == QueueItemType.UNIT) { if (simulationUnit.getBuildQueueTypes()[0] == QueueItemType.UNIT) {
this.rootFrame.setText(this.simpleBuildingBuildingActionLabel, this.rootFrame.setText(this.simpleBuildingBuildingActionLabel,
this.rootFrame.getTemplates().getDecoratedString("TRAINING")); this.rootFrame.getTemplates().getDecoratedString("TRAINING"));
} else if (simulationUnit.getBuildQueueTypes()[0] == QueueItemType.HERO_REVIVE) {
this.rootFrame.setText(this.simpleBuildingBuildingActionLabel,
this.rootFrame.getTemplates().getDecoratedString("REVIVING"));
} }
else { else {
this.rootFrame.setText(this.simpleBuildingBuildingActionLabel, this.rootFrame.setText(this.simpleBuildingBuildingActionLabel,
@ -2430,6 +2481,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.resourceBarUpkeepText.setColor(Color.CYAN); this.resourceBarUpkeepText.setColor(Color.CYAN);
} }
@Override
public void heroDeath() {
if(this.selectedUnit!=null) {
if(this.selectedUnit.getSimulationUnit().getUnitType().isRevivesHeroes()) {
reloadSelectedUnitUI(this.selectedUnit);
}
}
}
@Override @Override
public void ordersChanged() { public void ordersChanged() {
reloadSelectedUnitUI(this.selectedUnit); reloadSelectedUnitUI(this.selectedUnit);
@ -2474,7 +2534,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
0); 0);
} }
this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this.rootFrame, this, 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.activeCommandUnit.getSimulationUnit().getHandleId(),
this.activeCommand.getHandleId(), this.activeCommandOrderId, this.activeCommand.getHandleId(), this.activeCommandOrderId,
rayPickUnit.getSimulationWidget().getHandleId(), shiftDown); 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) final UnitSound yesSound = (this.activeCommand instanceof CAbilityAttack)
? getSelectedUnit().soundset.yesAttack ? getSelectedUnit().soundset.yesAttack
: getSelectedUnit().soundset.yes; : getSelectedUnit().soundset.yes;
@ -2597,6 +2679,28 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.activeCommandUnit.getSimulationUnit().getHandleId(), this.activeCommandUnit.getSimulationUnit().getHandleId(),
this.activeCommand.getHandleId(), this.activeCommandOrderId, this.activeCommand.getHandleId(), this.activeCommandOrderId,
clickLocationTemp2.x, clickLocationTemp2.y, shiftDown); 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( if (getSelectedUnit().soundset.yes.playUnitResponse(
this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
portraitTalk(); portraitTalk();

View File

@ -764,7 +764,7 @@ public class MenuUI {
return; return;
} }
else if (this.loadingMap != null) { else if (this.loadingMap != null) {
final int localPlayerIndex = 0; final int localPlayerIndex = MultiplayerHack.LP_VAL;
try { try {
this.loadingMap.viewer.loadMap(this.loadingMap.map, this.loadingMap.mapInfo, localPlayerIndex); this.loadingMap.viewer.loadMap(this.loadingMap.map, this.loadingMap.mapInfo, localPlayerIndex);
} }

View File

@ -43,9 +43,9 @@ public class DesktopLauncher {
config.gles30ContextMajorVersion = 3; config.gles30ContextMajorVersion = 3;
config.gles30ContextMinorVersion = 3; config.gles30ContextMinorVersion = 3;
// config.samples = 16; // config.samples = 16;
config.vSyncEnabled = false; // config.vSyncEnabled = false;
config.foregroundFPS = 0; // config.foregroundFPS = 0;
config.backgroundFPS = 0; // config.backgroundFPS = 0;
final DisplayMode desktopDisplayMode = LwjglApplicationConfiguration.getDesktopDisplayMode(); final DisplayMode desktopDisplayMode = LwjglApplicationConfiguration.getDesktopDisplayMode();
config.width = desktopDisplayMode.width; config.width = desktopDisplayMode.width;
config.height = desktopDisplayMode.height; config.height = desktopDisplayMode.height;
@ -63,6 +63,10 @@ public class DesktopLauncher {
argIndex++; argIndex++;
MultiplayerHack.MULTIPLAYER_HACK_SERVER_ADDR = arg[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(); loadExtensions();
final DataTable warsmashIni = loadWarsmashIni(); final DataTable warsmashIni = loadWarsmashIni();