Update ui

This commit is contained in:
Retera 2020-10-26 08:34:33 -04:00
parent e21bb492c1
commit 0c0141cd71
39 changed files with 2194 additions and 1760 deletions

View File

@ -309,7 +309,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.font.setColor(Color.YELLOW); this.font.setColor(Color.YELLOW);
final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond(); final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond();
this.glyphLayout.setText(this.font, fpsString); this.glyphLayout.setText(this.font, fpsString);
this.font.draw(this.batch, fpsString, (this.uiViewport.getMinWorldWidth() - this.glyphLayout.width) / 2, 1100); this.font.draw(this.batch, fpsString, (this.uiViewport.getMinWorldWidth() - this.glyphLayout.width) / 2,
1100 * this.meleeUI.getHeightRatioCorrection());
this.batch.end(); this.batch.end();
Gdx.gl30.glEnable(GL30.GL_SCISSOR_TEST); Gdx.gl30.glEnable(GL30.GL_SCISSOR_TEST);

View File

@ -21,6 +21,8 @@ public abstract class SkeletalNode extends GenericNode {
public boolean billboardedY; public boolean billboardedY;
public boolean billboardedZ; public boolean billboardedZ;
public Matrix4 localBlendMatrix;
public SkeletalNode() { public SkeletalNode() {
this.pivot = new Vector3(); this.pivot = new Vector3();
this.localLocation = new Vector3(); this.localLocation = new Vector3();
@ -33,6 +35,7 @@ public abstract class SkeletalNode extends GenericNode {
this.inverseWorldRotation = new Quaternion(); this.inverseWorldRotation = new Quaternion();
this.inverseWorldScale = new Vector3(); this.inverseWorldScale = new Vector3();
this.localMatrix = new Matrix4(); this.localMatrix = new Matrix4();
this.localBlendMatrix = new Matrix4();
this.worldMatrix = new Matrix4(); this.worldMatrix = new Matrix4();
this.dontInheritTranslation = false; this.dontInheritTranslation = false;
this.dontInheritRotation = false; this.dontInheritRotation = false;
@ -66,7 +69,7 @@ public abstract class SkeletalNode extends GenericNode {
this.billboardedZ = false; this.billboardedZ = false;
} }
public void recalculateTransformation(final Scene scene) { public void recalculateTransformation(final Scene scene, final float blendTimeRatio) {
final Quaternion computedRotation; final Quaternion computedRotation;
Vector3 computedScaling; Vector3 computedScaling;
@ -135,6 +138,12 @@ public abstract class SkeletalNode extends GenericNode {
RenderMathUtils.fromRotationTranslationScaleOrigin(computedRotation, this.localLocation, computedScaling, RenderMathUtils.fromRotationTranslationScaleOrigin(computedRotation, this.localLocation, computedScaling,
this.localMatrix, this.pivot); this.localMatrix, this.pivot);
if (!Float.isNaN(blendTimeRatio) && (blendTimeRatio > 0)) {
for (int i = 0; i < this.localMatrix.val.length; i++) {
this.localMatrix.val[i] = (this.localBlendMatrix.val[i] * blendTimeRatio)
+ (this.localMatrix.val[i] * (1 - blendTimeRatio));
}
}
RenderMathUtils.mul(this.worldMatrix, this.parent.worldMatrix, this.localMatrix); RenderMathUtils.mul(this.worldMatrix, this.parent.worldMatrix, this.localMatrix);
@ -168,6 +177,10 @@ public abstract class SkeletalNode extends GenericNode {
this.inverseWorldLocation.z = -this.worldLocation.z; this.inverseWorldLocation.z = -this.worldLocation.z;
} }
public void beginBlending() {
this.localBlendMatrix.set(this.localMatrix);
}
public void updateChildren(final float dt, final Scene scene) { public void updateChildren(final float dt, final Scene scene) {
for (int i = 0, l = this.children.size(); i < l; i++) { for (int i = 0, l = this.children.size(); i < l; i++) {
this.children.get(i).update(dt, scene); this.children.get(i).update(dt, scene);

View File

@ -86,16 +86,9 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.CAttackProjectile; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.CAttackProjectile;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.CWidgetAbilityTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.PointAbilityTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
import mpq.MPQArchive; import mpq.MPQArchive;
@ -1275,74 +1268,6 @@ public class War3MapViewer extends ModelViewer {
return (numElements < 0) ? 1 : (numElements >= MAXIMUM_ACCEPTED) ? MAXIMUM_ACCEPTED : numElements + 1; return (numElements < 0) ? 1 : (numElements >= MAXIMUM_ACCEPTED) ? MAXIMUM_ACCEPTED : numElements + 1;
} }
public boolean orderSmart(final float x, final float y) {
mousePosHeap.x = x;
mousePosHeap.y = y;
boolean ordered = false;
for (final RenderUnit unit : this.selected) {
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
if (ability instanceof CAbilityMove) {
ability.checkCanUse(this.simulation, unit.getSimulationUnit(), OrderIds.smart,
BooleanAbilityActivationReceiver.INSTANCE);
if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) {
ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap,
PointAbilityTargetCheckReceiver.INSTANCE);
final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget();
if (target != null) {
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap,
false);
ordered = true;
}
else {
System.err.println("Target not valid.");
}
}
else {
System.err.println("Ability not ok to use.");
}
}
else {
System.err.println("Ability not move.");
}
}
}
return ordered;
}
public boolean orderSmart(final RenderUnit target) {
boolean ordered = false;
for (final RenderUnit unit : this.selected) {
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
if (ability instanceof CAbilityAttack) {
ability.checkCanUse(this.simulation, unit.getSimulationUnit(), OrderIds.smart,
BooleanAbilityActivationReceiver.INSTANCE);
if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) {
ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), OrderIds.smart,
target.getSimulationUnit(), CWidgetAbilityTargetCheckReceiver.INSTANCE);
final CWidget targetWidget = CWidgetAbilityTargetCheckReceiver.INSTANCE.getTarget();
if (targetWidget != null) {
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, targetWidget,
false);
ordered = true;
}
else {
System.err.println("Target not valid.");
}
}
else {
System.err.println("Ability not ok to use.");
}
}
else {
System.err.println("Ability not move.");
}
}
}
return ordered;
}
public void standOnRepeat(final MdxComplexInstance instance) { public void standOnRepeat(final MdxComplexInstance instance) {
instance.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP); instance.setSequenceLoopMode(SequenceLoopMode.ALWAYS_LOOP);
SequenceUtils.randomStandSequence(instance); SequenceUtils.randomStandSequence(instance);

View File

@ -38,6 +38,7 @@ public class RenderUnit {
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh"); private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
private static final War3ID ORIENTATION_INTERPOLATION = War3ID.fromString("uori"); private static final War3ID ORIENTATION_INTERPOLATION = War3ID.fromString("uori");
private static final War3ID ANIM_PROPS = War3ID.fromString("uani"); private static final War3ID ANIM_PROPS = War3ID.fromString("uani");
private static final War3ID BLEND_TIME = War3ID.fromString("uble");
private static final float[] heapZ = new float[3]; private static final float[] heapZ = new float[3];
public final MdxComplexInstance instance; public final MdxComplexInstance instance;
public final MutableGameObject row; public final MutableGameObject row;
@ -124,6 +125,9 @@ public class RenderUnit {
orientationInterpolationOrdinal = 0; orientationInterpolationOrdinal = 0;
} }
this.orientationInterpolation = OrientationInterpolation.VALUES[orientationInterpolationOrdinal]; this.orientationInterpolation = OrientationInterpolation.VALUES[orientationInterpolationOrdinal];
final float blendTime = row.getFieldAsFloat(BLEND_TIME, 0);
instance.setBlendTime(blendTime * 1000.0f);
} }
this.instance = instance; this.instance = instance;

View File

@ -35,5 +35,5 @@ public interface CommandButtonListener {
// //
// int getOrderId(); // int getOrderId();
void commandButton(int buttonPositionX, int buttonPositionY, Texture icon, int abilityHandleId, int orderId, void commandButton(int buttonPositionX, int buttonPositionY, Texture icon, int abilityHandleId, int orderId,
boolean active); int autoCastOrderId, boolean active, boolean autoCastActive);
} }

View File

@ -6,10 +6,11 @@ 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.abilities.CAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityColdArrows;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityGeneric;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor;
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.util.BooleanAbilityActivationReceiver;
public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void> { public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void> {
public static final CommandCardPopulatingAbilityVisitor INSTANCE = new CommandCardPopulatingAbilityVisitor(); public static final CommandCardPopulatingAbilityVisitor INSTANCE = new CommandCardPopulatingAbilityVisitor();
@ -33,41 +34,54 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
@Override @Override
public Void accept(final CAbilityAttack ability) { public Void accept(final CAbilityAttack ability) {
addCommandButton(ability, this.abilityDataUI.getAttackUI(), ability.getHandleId(), OrderIds.attack); addCommandButton(ability, this.abilityDataUI.getAttackUI(), ability.getHandleId(), OrderIds.attack, 0, false);
if (!this.hasStop) { if (!this.hasStop) {
this.hasStop = true; this.hasStop = true;
addCommandButton(null, this.abilityDataUI.getStopUI(), -1, OrderIds.stop); addCommandButton(null, this.abilityDataUI.getStopUI(), 0, OrderIds.stop, 0, false);
} }
return null; return null;
} }
@Override @Override
public Void accept(final CAbilityMove ability) { public Void accept(final CAbilityMove ability) {
addCommandButton(ability, this.abilityDataUI.getMoveUI(), ability.getHandleId(), OrderIds.move); addCommandButton(ability, this.abilityDataUI.getMoveUI(), ability.getHandleId(), OrderIds.move, 0, false);
addCommandButton(ability, this.abilityDataUI.getHoldPosUI(), ability.getHandleId(), OrderIds.holdposition); addCommandButton(ability, this.abilityDataUI.getHoldPosUI(), ability.getHandleId(), OrderIds.holdposition, 0,
addCommandButton(ability, this.abilityDataUI.getPatrolUI(), ability.getHandleId(), OrderIds.patrol); false);
addCommandButton(ability, this.abilityDataUI.getPatrolUI(), ability.getHandleId(), OrderIds.patrol, 0, false);
if (!this.hasStop) { if (!this.hasStop) {
this.hasStop = true; this.hasStop = true;
addCommandButton(null, this.abilityDataUI.getStopUI(), -1, OrderIds.stop); addCommandButton(null, this.abilityDataUI.getStopUI(), 0, OrderIds.stop, 0, false);
} }
return null; return null;
} }
private void addCommandButton(final CAbility ability, final IconUI iconUI, final int handleId, final int orderId) { @Override
final boolean active; public Void accept(final CAbilityGeneric ability) {
if (this.unit.getCurrentOrder() == null) { addCommandButton(ability, this.abilityDataUI.getUI(ability.getRawcode()).getOnIconUI(), ability.getHandleId(),
active = (orderId == OrderIds.stop); OrderIds.channel, 0, false);
return null;
}
@Override
public Void accept(final CAbilityColdArrows ability) {
final boolean autoCastActive = ability.isAutoCastActive();
int autoCastId;
if (autoCastActive) {
autoCastId = OrderIds.coldarrows;
} }
else { else {
if (ability == null) { autoCastId = OrderIds.uncoldarrows;
active = false;
}
else {
ability.checkCanUse(this.game, this.unit, orderId, BooleanAbilityActivationReceiver.INSTANCE);
active = BooleanAbilityActivationReceiver.INSTANCE.isOk();
}
} }
addCommandButton(ability, this.abilityDataUI.getUI(ability.getRawcode()).getOnIconUI(), ability.getHandleId(),
OrderIds.coldarrowstarg, autoCastId, autoCastActive);
return null;
}
private void addCommandButton(final CAbility ability, final IconUI iconUI, final int handleId, final int orderId,
final int autoCastOrderId, final boolean autoCastActive) {
final boolean active = ((handleId == this.unit.getCurrentAbilityHandleId())
&& (orderId == this.unit.getCurrentOrderId()));
this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(), this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(),
iconUI.getIcon(), handleId, orderId, active); iconUI.getIcon(), handleId, orderId, autoCastOrderId, active, autoCastActive);
} }
} }

View File

@ -28,10 +28,11 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CMapControl
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
public class CSimulation { public class CSimulation {
private final CUnitData unitData;
private final CAbilityData abilityData; private final CAbilityData abilityData;
private final CUnitData unitData;
private final List<CUnit> units; private final List<CUnit> units;
private final List<CPlayer> players; private final List<CPlayer> players;
private final List<CAttackProjectile> projectiles; private final List<CAttackProjectile> projectiles;
@ -47,6 +48,7 @@ public class CSimulation {
private float currentGameDayTimeElapsed; private float currentGameDayTimeElapsed;
private final Map<Integer, CUnit> handleIdToUnit = new HashMap<>(); private final Map<Integer, CUnit> handleIdToUnit = new HashMap<>();
private final Map<Integer, CAbility> handleIdToAbility = new HashMap<>(); private final Map<Integer, CAbility> handleIdToAbility = new HashMap<>();
private transient CommandErrorListener commandErrorListener;
public CSimulation(final DataTable miscData, final MutableObjectData parsedUnitData, public CSimulation(final DataTable miscData, final MutableObjectData parsedUnitData,
final MutableObjectData parsedAbilityData, final SimulationRenderController simulationRenderController, final MutableObjectData parsedAbilityData, final SimulationRenderController simulationRenderController,
@ -55,8 +57,8 @@ public class CSimulation {
this.gameplayConstants = new CGameplayConstants(miscData); this.gameplayConstants = new CGameplayConstants(miscData);
this.simulationRenderController = simulationRenderController; this.simulationRenderController = simulationRenderController;
this.pathingGrid = pathingGrid; this.pathingGrid = pathingGrid;
this.unitData = new CUnitData(parsedUnitData);
this.abilityData = new CAbilityData(parsedAbilityData); this.abilityData = new CAbilityData(parsedAbilityData);
this.unitData = new CUnitData(parsedUnitData, this.abilityData);
this.units = new ArrayList<>(); this.units = new ArrayList<>();
this.projectiles = new ArrayList<>(); this.projectiles = new ArrayList<>();
this.newProjectiles = new ArrayList<>(); this.newProjectiles = new ArrayList<>();
@ -92,6 +94,13 @@ public class CSimulation {
neutralPassive.setAlliance(cPlayer, CAllianceType.PASSIVE, true); neutralPassive.setAlliance(cPlayer, CAllianceType.PASSIVE, true);
} }
this.commandErrorListener = new CommandErrorListener() {
@Override
public void showCommandError(final String message) {
throw new RuntimeException(message);
}
};
} }
public CUnitData getUnitData() { public CUnitData getUnitData() {
@ -208,4 +217,8 @@ public class CSimulation {
public CPlayer getPlayer(final int index) { public CPlayer getPlayer(final int index) {
return this.players.get(index); return this.players.get(index);
} }
public CommandErrorListener getCommandErrorListener() {
return this.commandErrorListener;
}
} }

View File

@ -14,11 +14,16 @@ import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils; import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener.CUnitStateNotifier; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener.CUnitStateNotifier;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorFollow;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorPatrol;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorStop;
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.CTargetType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
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.CBehaviorAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehavior;
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.CAllianceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
@ -38,8 +43,9 @@ public class CUnit extends CWidget {
private final List<CAbility> abilities = new ArrayList<>(); private final List<CAbility> abilities = new ArrayList<>();
private CBehavior currentOrder; private CBehavior currentBehavior;
private final Queue<CBehavior> orderQueue = new LinkedList<>(); private COrder currentOrder;
private final Queue<COrder> orderQueue = new LinkedList<>();
private final CUnitType unitType; private final CUnitType unitType;
private Rectangle collisionRectangle; private Rectangle collisionRectangle;
@ -59,6 +65,12 @@ public class CUnit extends CWidget {
private final float acquisitionRange; private final float acquisitionRange;
private transient static AutoAttackTargetFinderEnum autoAttackTargetFinderEnum = new AutoAttackTargetFinderEnum(); private transient static AutoAttackTargetFinderEnum autoAttackTargetFinderEnum = new AutoAttackTargetFinderEnum();
private transient CBehaviorMove moveBehavior;
private transient CBehaviorAttack attackBehavior;
private transient CBehaviorFollow followBehavior;
private transient CBehaviorPatrol patrolBehavior;
private transient CBehaviorStop stopBehavior;
public CUnit(final int handleId, final int playerIndex, final float x, final float y, final float life, public CUnit(final int handleId, final int playerIndex, final float x, final float y, final float life,
final War3ID typeId, final float facing, final float mana, final int maximumLife, final int maximumMana, final War3ID typeId, final float facing, final float mana, final int maximumLife, final int maximumMana,
final int speed, final int defense, final CUnitType unitType) { final int speed, final int defense, final CUnitType unitType) {
@ -75,6 +87,8 @@ public class CUnit extends CWidget {
this.unitType = unitType; this.unitType = unitType;
this.classifications.addAll(unitType.getClassifications()); this.classifications.addAll(unitType.getClassifications());
this.acquisitionRange = unitType.getDefaultAcquisitionRange(); this.acquisitionRange = unitType.getDefaultAcquisitionRange();
this.stopBehavior = new CBehaviorStop(this);
this.currentBehavior = this.stopBehavior;
} }
public void setUnitAnimationListener(final CUnitAnimationListener unitAnimationListener) { public void setUnitAnimationListener(final CUnitAnimationListener unitAnimationListener) {
@ -185,38 +199,33 @@ public class CUnit extends CWidget {
return true; return true;
} }
} }
else if (this.currentOrder != null) { else if (this.currentBehavior != null) {
if (this.currentOrder.update(game)) { this.currentBehavior = this.currentBehavior.update(game);
// remove current order, because it's completed, polling next
// item from order queue
this.currentOrder = this.orderQueue.poll();
this.stateNotifier.ordersChanged();
}
if (this.currentOrder == null) {
// maybe order "stop" here
this.unitAnimationListener.playAnimation(true, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, true);
}
} }
else { else {
// check to auto acquire targets // check to auto acquire targets
if (!this.unitType.getAttacks().isEmpty()) { autoAcquireAttackTargets(game);
if (this.collisionRectangle != null) {
tempRect.set(this.collisionRectangle);
}
else {
tempRect.set(this.getX(), this.getY(), 0, 0);
}
final float halfSize = this.acquisitionRange;
tempRect.x -= halfSize;
tempRect.y -= halfSize;
tempRect.width += halfSize * 2;
tempRect.height += halfSize * 2;
game.getWorldCollision().enumUnitsInRect(tempRect, autoAttackTargetFinderEnum.reset(game, this));
}
} }
return false; return false;
} }
public void autoAcquireAttackTargets(final CSimulation game) {
if (!this.unitType.getAttacks().isEmpty()) {
if (this.collisionRectangle != null) {
tempRect.set(this.collisionRectangle);
}
else {
tempRect.set(this.getX(), this.getY(), 0, 0);
}
final float halfSize = this.acquisitionRange;
tempRect.x -= halfSize;
tempRect.y -= halfSize;
tempRect.width += halfSize * 2;
tempRect.height += halfSize * 2;
game.getWorldCollision().enumUnitsInRect(tempRect, autoAttackTargetFinderEnum.reset(game, this));
}
}
public float getEndingDecayTime(final CSimulation game) { public float getEndingDecayTime(final CSimulation game) {
if (this.unitType.isBuilding()) { if (this.unitType.isBuilding()) {
return game.getGameplayConstants().getStructureDecayTime(); return game.getGameplayConstants().getStructureDecayTime();
@ -224,7 +233,7 @@ public class CUnit extends CWidget {
return game.getGameplayConstants().getBoneDecayTime(); return game.getGameplayConstants().getBoneDecayTime();
} }
public void order(final CBehavior order, final boolean queue) { public void order(final CSimulation game, final COrder order, final boolean queue) {
if (isDead()) { if (isDead()) {
return; return;
} }
@ -232,14 +241,37 @@ public class CUnit extends CWidget {
this.orderQueue.add(order); this.orderQueue.add(order);
} }
else { else {
this.currentOrder = order; this.currentBehavior = beginOrder(game, order);
this.orderQueue.clear(); this.orderQueue.clear();
} }
this.stateNotifier.ordersChanged();
} }
public CBehavior getCurrentOrder() { private CBehavior beginOrder(final CSimulation game, final COrder order) {
return this.currentOrder; final boolean omitNotify = (this.currentOrder == null) && (order == null);
this.currentOrder = order;
if (!omitNotify) {
this.stateNotifier.ordersChanged(getCurrentAbilityHandleId(), getCurrentOrderId());
}
CBehavior nextBehavior;
if (order != null) {
nextBehavior = order.begin(game, this);
}
else {
nextBehavior = this.stopBehavior;
}
return nextBehavior;
}
public CBehavior getCurrentBehavior() {
return this.currentBehavior;
}
public int getCurrentAbilityHandleId() {
return this.currentOrder == null ? 0 : this.currentOrder.getAbilityHandleId();
}
public int getCurrentOrderId() {
return this.currentOrder == null ? OrderIds.stop : this.currentOrder.getOrderId();
} }
public List<CAbility> getAbilities() { public List<CAbility> getAbilities() {
@ -389,12 +421,12 @@ public class CUnit extends CWidget {
} }
} }
else { else {
if (this.currentOrder == null) { if (this.currentBehavior == null) {
if (!simulation.getPlayer(getPlayerIndex()).hasAlliance(source.getPlayerIndex(), if (!simulation.getPlayer(getPlayerIndex()).hasAlliance(source.getPlayerIndex(),
CAllianceType.PASSIVE)) { CAllianceType.PASSIVE)) {
for (final CUnitAttack attack : this.unitType.getAttacks()) { for (final CUnitAttack attack : this.unitType.getAttacks()) {
if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) { if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) {
this.order(new CBehaviorAttack(this, attack, OrderIds.attack, source), false); this.currentBehavior = getAttackBehavior().reset(attack, source);
break; break;
} }
} }
@ -404,7 +436,7 @@ public class CUnit extends CWidget {
} }
private void kill(final CSimulation simulation) { private void kill(final CSimulation simulation) {
this.currentOrder = null; this.currentBehavior = null;
this.orderQueue.clear(); this.orderQueue.clear();
this.deathTurnTick = simulation.getGameTurnTick(); this.deathTurnTick = simulation.getGameTurnTick();
} }
@ -558,7 +590,7 @@ public class CUnit extends CWidget {
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())) {
this.source.order(new CBehaviorAttack(this.source, attack, OrderIds.attack, unit), false); this.source.currentBehavior = this.source.getAttackBehavior().reset(attack, unit);
return true; return true;
} }
} }
@ -566,4 +598,45 @@ public class CUnit extends CWidget {
return false; return false;
} }
} }
public CBehaviorMove getMoveBehavior() {
return this.moveBehavior;
}
public void setMoveBehavior(final CBehaviorMove moveBehavior) {
this.moveBehavior = moveBehavior;
}
public CBehaviorAttack getAttackBehavior() {
return this.attackBehavior;
}
public void setAttackBehavior(final CBehaviorAttack attackBehavior) {
this.attackBehavior = attackBehavior;
}
public CBehaviorStop getStopBehavior() {
return this.stopBehavior;
}
public void setFollowBehavior(final CBehaviorFollow followBehavior) {
this.followBehavior = followBehavior;
}
public void setPatrolBehavior(final CBehaviorPatrol patrolBehavior) {
this.patrolBehavior = patrolBehavior;
}
public CBehaviorFollow getFollowBehavior() {
return this.followBehavior;
}
public CBehaviorPatrol getPatrolBehavior() {
return this.patrolBehavior;
}
public CBehavior pollNextOrderBehavior(final CSimulation game) {
final COrder order = this.orderQueue.poll();
return beginOrder(game, order);
}
} }

View File

@ -5,7 +5,7 @@ import com.etheller.warsmash.util.SubscriberSetNotifier;
public interface CUnitStateListener { public interface CUnitStateListener {
void lifeChanged(); // hp (current) changes void lifeChanged(); // hp (current) changes
void ordersChanged(); void ordersChanged(int abilityHandleId, int orderId);
public static final class CUnitStateNotifier extends SubscriberSetNotifier<CUnitStateListener> public static final class CUnitStateNotifier extends SubscriberSetNotifier<CUnitStateListener>
implements CUnitStateListener { implements CUnitStateListener {
@ -17,9 +17,9 @@ public interface CUnitStateListener {
} }
@Override @Override
public void ordersChanged() { public void ordersChanged(final int abilityHandleId, final int orderId) {
for (final CUnitStateListener listener : set) { for (final CUnitStateListener listener : set) {
listener.ordersChanged(); listener.ordersChanged(abilityHandleId, orderId);
} }
} }
} }

View File

@ -4,6 +4,7 @@ import com.badlogic.gdx.math.Vector2;
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.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
public interface CAbility extends CAbilityView { public interface CAbility extends CAbilityView {
/* should fire when ability added to unit */ /* should fire when ability added to unit */
@ -12,10 +13,10 @@ public interface CAbility extends CAbilityView {
/* should fire when ability removed from unit */ /* should fire when ability removed from unit */
void onRemove(CSimulation game, CUnit unit); void onRemove(CSimulation game, CUnit unit);
void onOrder(CSimulation game, CUnit caster, int orderId, CWidget target, boolean queue); CBehavior begin(CSimulation game, CUnit caster, int orderId, CWidget target);
void onOrder(CSimulation game, CUnit caster, int orderId, Vector2 point, boolean queue); CBehavior begin(CSimulation game, CUnit caster, int orderId, Vector2 point);
void onOrderNoTarget(CSimulation game, CUnit caster, int orderId, boolean queue); CBehavior beginNoTarget(CSimulation game, CUnit caster, int orderId);
} }

View File

@ -4,12 +4,10 @@ import com.badlogic.gdx.math.Vector2;
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.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.StringsToExternalizeLater; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CWeaponType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CWeaponType;
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.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorMove;
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.CAllianceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver;
@ -99,6 +97,7 @@ public class CAbilityAttack implements CAbility {
@Override @Override
public void onAdd(final CSimulation game, final CUnit unit) { public void onAdd(final CSimulation game, final CUnit unit) {
unit.setAttackBehavior(new CBehaviorAttack(unit));
} }
@Override @Override
@ -106,30 +105,28 @@ public class CAbilityAttack implements CAbility {
} }
@Override @Override
public void onOrder(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) {
final boolean queue) { CBehavior behavior = null;
CBehavior order = null;
for (final CUnitAttack attack : caster.getUnitType().getAttacks()) { for (final CUnitAttack attack : caster.getUnitType().getAttacks()) {
if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) { if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) {
order = new CBehaviorAttack(caster, attack, orderId, target); behavior = caster.getAttackBehavior().reset(attack, target);
break; break;
} }
} }
if (order == null) { if (behavior == null) {
order = new CBehaviorMove(caster, orderId, target.getX(), target.getY()); behavior = caster.getMoveBehavior().reset(target.getX(), target.getY());
} }
caster.order(order, queue); return behavior;
} }
@Override @Override
public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final Vector2 target, public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
final boolean queue) { return caster.pollNextOrderBehavior(game);
throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_WIDGET);
} }
@Override @Override
public void onOrderNoTarget(final CSimulation game, final CUnit caster, final int orderId, final boolean queue) { public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_WIDGET); return caster.pollNextOrderBehavior(game);
} }
@Override @Override

View File

@ -0,0 +1,117 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities;
import com.badlogic.gdx.math.Vector2;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
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.util.AbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
/**
* Represents an ability from the object data
*/
public class CAbilityColdArrows implements CAbility {
private final War3ID rawcode;
private final int handleId;
private boolean autoCastActive;
public CAbilityColdArrows(final War3ID rawcode, final int handleId) {
this.rawcode = rawcode;
this.handleId = handleId;
}
@Override
public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId,
final AbilityActivationReceiver receiver) {
receiver.useOk();
}
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
final AbilityTargetCheckReceiver<CWidget> receiver) {
switch (orderId) {
case OrderIds.coldarrowstarg:
receiver.targetOk(target);
break;
default:
receiver.orderIdNotAccepted();
break;
}
}
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target,
final AbilityTargetCheckReceiver<Vector2> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityTargetCheckReceiver<Void> receiver) {
switch (orderId) {
case OrderIds.coldarrows:
case OrderIds.uncoldarrows:
receiver.targetOk(null);
break;
default:
receiver.orderIdNotAccepted();
break;
}
}
public War3ID getRawcode() {
return this.rawcode;
}
@Override
public int getHandleId() {
return this.handleId;
}
public boolean isAutoCastActive() {
return this.autoCastActive;
}
@Override
public <T> T visit(final CAbilityVisitor<T> visitor) {
return visitor.accept(this);
}
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
}
@Override
public void onRemove(final CSimulation game, final CUnit unit) {
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) {
CBehavior behavior = null;
for (final CUnitAttack attack : caster.getUnitType().getAttacks()) {
if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) {
behavior = caster.getAttackBehavior().reset(attack, target);
break;
}
}
if (behavior != null) {
return behavior;
}
return caster.pollNextOrderBehavior(game);
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
return caster.pollNextOrderBehavior(game);
}
@Override
public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
this.autoCastActive = !this.autoCastActive;
return caster.pollNextOrderBehavior(game);
}
}

View File

@ -0,0 +1,84 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities;
import com.badlogic.gdx.math.Vector2;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
/**
* Represents an ability from the object data
*/
public class CAbilityGeneric implements CAbility {
private final War3ID rawcode;
private final int handleId;
public CAbilityGeneric(final War3ID rawcode, final int handleId) {
this.rawcode = rawcode;
this.handleId = handleId;
}
public War3ID getRawcode() {
return this.rawcode;
}
@Override
public void checkCanUse(final CSimulation game, final CUnit unit, final int orderId,
final AbilityActivationReceiver receiver) {
receiver.notAnActiveAbility();
}
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
final AbilityTargetCheckReceiver<CWidget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target,
final AbilityTargetCheckReceiver<Vector2> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityTargetCheckReceiver<Void> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public int getHandleId() {
return this.handleId;
}
@Override
public <T> T visit(final CAbilityVisitor<T> visitor) {
return visitor.accept(this);
}
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
}
@Override
public void onRemove(final CSimulation game, final CUnit unit) {
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) {
return caster.pollNextOrderBehavior(game);
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
return caster.pollNextOrderBehavior(game);
}
@Override
public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
return caster.pollNextOrderBehavior(game);
}
}

View File

@ -4,9 +4,10 @@ import com.badlogic.gdx.math.Vector2;
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.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.StringsToExternalizeLater; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorMove; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorFollow;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorPatrol; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorPatrol;
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.util.AbilityActivationReceiver; 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.AbilityTargetCheckReceiver;
@ -31,7 +32,7 @@ public class CAbilityMove implements CAbility {
switch (orderId) { switch (orderId) {
case OrderIds.smart: case OrderIds.smart:
case OrderIds.patrol: case OrderIds.patrol:
if (target instanceof CUnit) { if ((target instanceof CUnit) && (target != unit)) {
receiver.targetOk(target); receiver.targetOk(target);
} }
else { else {
@ -74,7 +75,9 @@ public class CAbilityMove implements CAbility {
@Override @Override
public void onAdd(final CSimulation game, final CUnit unit) { public void onAdd(final CSimulation game, final CUnit unit) {
unit.setMoveBehavior(new CBehaviorMove(unit));
unit.setFollowBehavior(new CBehaviorFollow(unit));
unit.setPatrolBehavior(new CBehaviorPatrol(unit));
} }
@Override @Override
@ -83,20 +86,23 @@ public class CAbilityMove implements CAbility {
} }
@Override @Override
public void onOrder(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) {
final boolean queue) { return caster.getFollowBehavior().reset((CUnit) target);
caster.order(new CBehaviorPatrol(caster, orderId, (CUnit) target), queue);
} }
@Override @Override
public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final Vector2 target, public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
final boolean queue) { if (orderId == OrderIds.patrol) {
caster.order(new CBehaviorMove(caster, orderId, target.x, target.y), queue); return caster.getPatrolBehavior().reset(point);
}
else {
return caster.getMoveBehavior().reset(point.x, point.y);
}
} }
@Override @Override
public void onOrderNoTarget(final CSimulation game, final CUnit caster, final int orderId, final boolean queue) { public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
throw new IllegalArgumentException(StringsToExternalizeLater.MUST_TARGET_POINT); return caster.pollNextOrderBehavior(game);
} }
@Override @Override

View File

@ -12,4 +12,8 @@ public interface CAbilityVisitor<T> {
T accept(CAbilityAttack ability); T accept(CAbilityAttack ability);
T accept(CAbilityMove ability); T accept(CAbilityMove ability);
T accept(CAbilityGeneric ability);
T accept(CAbilityColdArrows ability);
} }

View File

@ -0,0 +1,108 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
public abstract class CAbstractRangedBehavior implements CRangedBehavior {
protected final CUnit unit;
public CAbstractRangedBehavior(final CUnit unit) {
this.unit = unit;
}
private boolean wasWithinPropWindow = false;
protected CWidget target;
private boolean wasInRange = false;
private CBehaviorMove moveBehavior;
protected final CAbstractRangedBehavior innerReset(final CWidget target) {
this.wasWithinPropWindow = false;
this.target = target;
this.wasInRange = false;
if (!this.unit.isMovementDisabled()) {
if ((target instanceof CUnit) && !((CUnit) target).getUnitType().isBuilding()) {
this.moveBehavior = this.unit.getMoveBehavior().reset((CUnit) target, this);
}
else {
this.moveBehavior = this.unit.getMoveBehavior().reset(target.getX(), target.getY(), this);
}
}
else {
this.moveBehavior = null;
}
return this;
}
protected abstract CBehavior update(CSimulation simulation, boolean withinRange);
protected abstract boolean checkTargetStillValid(CSimulation simulation);
protected abstract void resetBeforeMoving(CSimulation simulation);
@Override
public final CBehavior update(final CSimulation simulation) {
if (!checkTargetStillValid(simulation)) {
return this.unit.pollNextOrderBehavior(simulation);
}
if (!isWithinRange(simulation)) {
if (this.moveBehavior == null) {
return this.unit.pollNextOrderBehavior(simulation);
}
this.wasInRange = false;
resetBeforeMoving(simulation);
;
return this.unit.getMoveBehavior();
}
this.wasInRange = true;
if (!this.unit.isMovementDisabled()) {
final float prevX = this.unit.getX();
final float prevY = this.unit.getY();
final float deltaY = this.target.getY() - prevY;
final float deltaX = this.target.getX() - prevX;
final double goalAngleRad = Math.atan2(deltaY, deltaX);
float goalAngle = (float) Math.toDegrees(goalAngleRad);
if (goalAngle < 0) {
goalAngle += 360;
}
float facing = this.unit.getFacing();
float delta = goalAngle - facing;
final float propulsionWindow = simulation.getGameplayConstants().getAttackHalfAngle();
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
if (delta < -180) {
delta = 360 + delta;
}
if (delta > 180) {
delta = -360 + delta;
}
final float absDelta = Math.abs(delta);
if ((absDelta <= 1.0) && (absDelta != 0)) {
this.unit.setFacing(goalAngle);
}
else {
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
if (absDelta < Math.abs(angleToAdd)) {
angleToAdd = delta;
}
facing += angleToAdd;
this.unit.setFacing(facing);
}
if (absDelta < propulsionWindow) {
this.wasWithinPropWindow = true;
}
else {
// If this happens, the unit is facing the wrong way, and has to turn before
// moving.
this.wasWithinPropWindow = false;
}
}
else {
this.wasWithinPropWindow = true;
}
return update(simulation, this.wasWithinPropWindow);
}
}

View File

@ -0,0 +1,13 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
public interface CBehavior {
/**
* Executes one step of game simulation of the current order, and then returns
* the next behavior for the unit after the result of the update cycle.
*
* @return
*/
CBehavior update(CSimulation game);
}

View File

@ -0,0 +1,111 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
public class CBehaviorAttack extends CAbstractRangedBehavior {
public CBehaviorAttack(final CUnit unit) {
super(unit);
}
private CUnitAttack unitAttack;
private int damagePointLaunchTime;
private int backSwingTime;
private int thisOrderCooldownEndTime;
public CBehaviorAttack reset(final CUnitAttack unitAttack, final CWidget target) {
super.innerReset(target);
this.unitAttack = unitAttack;
this.damagePointLaunchTime = 0;
this.backSwingTime = 0;
this.thisOrderCooldownEndTime = 0;
return this;
}
@Override
public boolean isWithinRange(final CSimulation simulation) {
float range = this.unitAttack.getRange();
if ((this.target instanceof CUnit) && (((CUnit) this.target).getCurrentBehavior() instanceof CBehaviorMove)
&& (this.damagePointLaunchTime != 0 /*
* only apply range motion buffer if they were already in range and
* attacked
*/)) {
range += this.unitAttack.getRangeMotionBuffer();
}
return this.unit.canReach(this.target, range)
&& (this.unit.distance(this.target) >= this.unit.getUnitType().getMinimumAttackRange());
}
@Override
protected boolean checkTargetStillValid(final CSimulation simulation) {
return !this.target.isDead()
&& this.target.canBeTargetedBy(simulation, this.unit, this.unitAttack.getTargetsAllowed());
}
@Override
protected void resetBeforeMoving(final CSimulation simulation) {
this.damagePointLaunchTime = 0;
this.thisOrderCooldownEndTime = 0;
}
@Override
public CBehavior update(final CSimulation simulation, final boolean withinRange) {
final int cooldownEndTime = this.unit.getCooldownEndTime();
final int currentTurnTick = simulation.getGameTurnTick();
if (withinRange) {
if (this.damagePointLaunchTime != 0) {
if (currentTurnTick >= this.damagePointLaunchTime) {
int minDamage = this.unitAttack.getMinDamage();
final int maxDamage = Math.max(0, this.unitAttack.getMaxDamage());
if (minDamage > maxDamage) {
minDamage = maxDamage;
}
final int damage;
if (maxDamage == 0) {
damage = 0;
}
else if (minDamage == maxDamage) {
damage = minDamage;
}
else {
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
}
this.unitAttack.launch(simulation, this.unit, this.target, damage);
this.damagePointLaunchTime = 0;
}
}
else if (currentTurnTick >= cooldownEndTime) {
final float cooldownTime = this.unitAttack.getCooldownTime();
final float animationBackswingPoint = this.unitAttack.getAnimationBackswingPoint();
final int a1CooldownSteps = (int) (cooldownTime / WarsmashConstants.SIMULATION_STEP_TIME);
final int a1BackswingSteps = (int) (animationBackswingPoint / WarsmashConstants.SIMULATION_STEP_TIME);
final int a1DamagePointSteps = (int) (this.unitAttack.getAnimationDamagePoint()
/ WarsmashConstants.SIMULATION_STEP_TIME);
this.unit.setCooldownEndTime(currentTurnTick + a1CooldownSteps);
this.thisOrderCooldownEndTime = currentTurnTick + a1CooldownSteps;
this.damagePointLaunchTime = currentTurnTick + a1DamagePointSteps;
this.backSwingTime = currentTurnTick + a1DamagePointSteps + a1BackswingSteps;
this.unit.getUnitAnimationListener().playAnimation(true, PrimaryTag.ATTACK, SequenceUtils.EMPTY, 1.0f,
true);
this.unit.getUnitAnimationListener().queueAnimation(PrimaryTag.STAND, SequenceUtils.READY, false);
}
else if ((currentTurnTick >= this.thisOrderCooldownEndTime)) {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.READY, 1.0f,
false);
}
}
else {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.READY, 1.0f,
false);
}
return this;
}
}

View File

@ -0,0 +1,38 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
public class CBehaviorFollow extends CAbstractRangedBehavior {
public CBehaviorFollow(final CUnit unit) {
super(unit);
}
public CBehavior reset(final CUnit target) {
return innerReset(target);
}
@Override
public boolean isWithinRange(final CSimulation simulation) {
return this.unit.canReach(this.target, this.unit.getAcquisitionRange());
}
@Override
protected CBehavior update(final CSimulation simulation, final boolean withinRange) {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, false);
return this;
}
@Override
protected boolean checkTargetStillValid(final CSimulation simulation) {
return !this.target.isDead();
}
@Override
protected void resetBeforeMoving(final CSimulation simulation) {
}
}

View File

@ -1,4 +1,4 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Float; import java.awt.geom.Point2D.Float;
@ -18,35 +18,58 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindin
public class CBehaviorMove implements CBehavior { public class CBehaviorMove implements CBehavior {
private static final Rectangle tempRect = new Rectangle(); private static final Rectangle tempRect = new Rectangle();
private final CUnit unit; private final CUnit unit;
private final int orderId;
private boolean wasWithinPropWindow = false;
private List<Point2D.Float> path = null;
private final CPathfindingProcessor.GridMapping gridMapping;
private final Point2D.Float target;
private int searchCycles = 0;
private CUnit followUnit;
public CBehaviorMove(final CUnit unit, final int orderId, final float targetX, final float targetY) { public CBehaviorMove(final CUnit unit) {
this.unit = unit; this.unit = unit;
this.orderId = orderId;
this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners(
unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS
: CPathfindingProcessor.GridMapping.CELLS;
this.target = new Point2D.Float(targetX, targetY);
} }
public CBehaviorMove(final CUnit unit, final int orderId, final CUnit followUnit) { private boolean wasWithinPropWindow = false;
this.unit = unit; private List<Point2D.Float> path = null;
this.orderId = orderId; private CPathfindingProcessor.GridMapping gridMapping;
private Point2D.Float target;
private int searchCycles = 0;
private CUnit followUnit;
private CRangedBehavior rangedBehavior;
public CBehaviorMove reset(final float targetX, final float targetY) {
return reset(targetX, targetY, null);
}
public CBehaviorMove reset(final float targetX, final float targetY, final CRangedBehavior rangedBehavior) {
this.wasWithinPropWindow = false;
this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners( this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners(
unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS this.unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS
: CPathfindingProcessor.GridMapping.CELLS;
this.target = new Point2D.Float(targetX, targetY);
this.path = null;
this.searchCycles = 0;
this.followUnit = null;
this.rangedBehavior = rangedBehavior;
return this;
}
public CBehaviorMove reset(final CUnit followUnit) {
return reset(followUnit, null);
}
public CBehaviorMove reset(final CUnit followUnit, final CRangedBehavior rangedBehavior) {
this.wasWithinPropWindow = false;
this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners(
this.unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS
: CPathfindingProcessor.GridMapping.CELLS; : CPathfindingProcessor.GridMapping.CELLS;
this.target = new Point2D.Float(followUnit.getX(), followUnit.getY()); this.target = new Point2D.Float(followUnit.getX(), followUnit.getY());
this.path = null;
this.searchCycles = 0;
this.followUnit = followUnit; this.followUnit = followUnit;
this.rangedBehavior = rangedBehavior;
return this;
} }
@Override @Override
public boolean update(final CSimulation simulation) { public CBehavior update(final CSimulation simulation) {
if ((this.rangedBehavior != null) && this.rangedBehavior.isWithinRange(simulation)) {
return this.rangedBehavior;
}
final float prevX = this.unit.getX(); final float prevX = this.unit.getX();
final float prevY = this.unit.getY(); final float prevY = this.unit.getY();
@ -119,7 +142,7 @@ public class CBehaviorMove implements CBehavior {
this.searchCycles < 4); this.searchCycles < 4);
System.out.println("new path (for target) " + this.path); System.out.println("new path (for target) " + this.path);
if (this.path.isEmpty()) { if (this.path.isEmpty()) {
return true; return this.unit.pollNextOrderBehavior(simulation);
} }
} }
float currentTargetX; float currentTargetX;
@ -217,7 +240,7 @@ public class CBehaviorMove implements CBehavior {
this.searchCycles = 0; this.searchCycles = 0;
} }
if (this.path.isEmpty()) { if (this.path.isEmpty()) {
return true; return this.unit.pollNextOrderBehavior(simulation);
} }
else { else {
System.out.println(this.path); System.out.println(this.path);
@ -249,7 +272,7 @@ public class CBehaviorMove implements CBehavior {
deltaY = currentTargetY - nextY; deltaY = currentTargetY - nextY;
deltaX = currentTargetX - nextX; deltaX = currentTargetX - nextX;
if ((deltaX == 0.000f) && (deltaY == 0.000f) && this.path.isEmpty()) { if ((deltaX == 0.000f) && (deltaY == 0.000f) && this.path.isEmpty()) {
return true; return this.unit.pollNextOrderBehavior(simulation);
} }
System.out.println("new target: " + currentTargetX + "," + currentTargetY); System.out.println("new target: " + currentTargetX + "," + currentTargetY);
System.out.println("new delta: " + deltaX + "," + deltaY); System.out.println("new delta: " + deltaX + "," + deltaY);
@ -274,7 +297,7 @@ public class CBehaviorMove implements CBehavior {
SequenceUtils.EMPTY, 1.0f, true); SequenceUtils.EMPTY, 1.0f, true);
} }
this.wasWithinPropWindow = false; this.wasWithinPropWindow = false;
return false; return this;
} }
} }
} }
@ -290,7 +313,7 @@ public class CBehaviorMove implements CBehavior {
this.searchCycles++; this.searchCycles++;
System.out.println("new path " + this.path); System.out.println("new path " + this.path);
if (this.path.isEmpty() || (this.searchCycles > 5)) { if (this.path.isEmpty() || (this.searchCycles > 5)) {
return true; return this.unit.pollNextOrderBehavior(simulation);
} }
} }
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.WALK, SequenceUtils.EMPTY, 1.0f, this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.WALK, SequenceUtils.EMPTY, 1.0f,
@ -309,12 +332,7 @@ public class CBehaviorMove implements CBehavior {
this.wasWithinPropWindow = false; this.wasWithinPropWindow = false;
} }
return false; return this;
}
@Override
public int getOrderId() {
return this.orderId;
} }
} }

View File

@ -0,0 +1,37 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.badlogic.gdx.math.Vector2;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
public class CBehaviorPatrol implements CRangedBehavior {
private final CUnit unit;
private Vector2 target;
private Vector2 startPoint;
public CBehaviorPatrol(final CUnit unit) {
this.unit = unit;
}
public CBehavior reset(final Vector2 target) {
this.target = target;
this.startPoint = new Vector2(this.unit.getX(), this.unit.getY());
return this;
}
@Override
public boolean isWithinRange(final CSimulation simulation) {
return this.unit.distance(this.target.x, this.target.y) <= simulation.getGameplayConstants()
.getCloseEnoughRange(); // TODO this is not how it was meant to be used
}
@Override
public CBehavior update(final CSimulation simulation) {
final Vector2 temp = this.target;
this.target = this.startPoint;
this.startPoint = temp;
return this.unit.getMoveBehavior().reset(this.target.x, this.target.y, this);
}
}

View File

@ -0,0 +1,22 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
public class CBehaviorStop implements CBehavior {
private final CUnit unit;
public CBehaviorStop(final CUnit unit) {
this.unit = unit;
}
@Override
public CBehavior update(final CSimulation game) {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, true);
return this.unit.pollNextOrderBehavior(game);
}
}

View File

@ -0,0 +1,7 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
public interface CRangedBehavior extends CBehavior {
boolean isWithinRange(final CSimulation simulation);
}

View File

@ -1,27 +1,24 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.data; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.data;
import com.etheller.warsmash.units.manager.MutableObjectData; import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityColdArrows;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityGeneric;
public class CAbilityData { public class CAbilityData {
private static final War3ID ABILITY_ICON = War3ID.fromString("aart"); private static final War3ID COLD_ARROWS = War3ID.fromString("ACcw");
private final MutableObjectData abilityData; private final MutableObjectData abilityData;
public CAbilityData(final MutableObjectData abilityData) { public CAbilityData(final MutableObjectData abilityData) {
this.abilityData = abilityData; this.abilityData = abilityData;
} }
public String getIconPath(final War3ID id, final int level) { public CAbility createAbility(final String ability, final int handleId) {
final MutableGameObject mutableGameObject = this.abilityData.get(id); final War3ID war3Id = War3ID.fromString(ability);
if (mutableGameObject == null) { if (war3Id.equals(COLD_ARROWS)) {
return ImageUtils.DEFAULT_ICON_PATH; return new CAbilityColdArrows(war3Id, handleId);
} }
final String iconPath = mutableGameObject.getFieldAsString(ABILITY_ICON, level); return new CAbilityGeneric(war3Id, handleId);
if ((iconPath == null) || "".equals(iconPath)) {
return ImageUtils.DEFAULT_ICON_PATH;
}
return iconPath;
} }
} }

View File

@ -32,396 +32,410 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUni
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
public class CUnitData { public class CUnitData {
private static final War3ID MANA_INITIAL_AMOUNT = War3ID.fromString("umpi"); private static final War3ID MANA_INITIAL_AMOUNT = War3ID.fromString("umpi");
private static final War3ID MANA_MAXIMUM = War3ID.fromString("umpm"); private static final War3ID MANA_MAXIMUM = War3ID.fromString("umpm");
private static final War3ID HIT_POINT_MAXIMUM = War3ID.fromString("uhpm"); private static final War3ID HIT_POINT_MAXIMUM = War3ID.fromString("uhpm");
private static final War3ID MOVEMENT_SPEED_BASE = War3ID.fromString("umvs"); private static final War3ID MOVEMENT_SPEED_BASE = War3ID.fromString("umvs");
private static final War3ID PROPULSION_WINDOW = War3ID.fromString("uprw"); private static final War3ID PROPULSION_WINDOW = War3ID.fromString("uprw");
private static final War3ID TURN_RATE = War3ID.fromString("umvr"); private static final War3ID TURN_RATE = War3ID.fromString("umvr");
private static final War3ID IS_BLDG = War3ID.fromString("ubdg"); private static final War3ID IS_BLDG = War3ID.fromString("ubdg");
private static final War3ID NAME = War3ID.fromString("unam"); private static final War3ID NAME = War3ID.fromString("unam");
private static final War3ID PROJECTILE_LAUNCH_X = War3ID.fromString("ulpx"); private static final War3ID PROJECTILE_LAUNCH_X = War3ID.fromString("ulpx");
private static final War3ID PROJECTILE_LAUNCH_Y = War3ID.fromString("ulpy"); private static final War3ID PROJECTILE_LAUNCH_Y = War3ID.fromString("ulpy");
private static final War3ID PROJECTILE_LAUNCH_Z = War3ID.fromString("ulpz"); private static final War3ID PROJECTILE_LAUNCH_Z = War3ID.fromString("ulpz");
private static final War3ID ATTACKS_ENABLED = War3ID.fromString("uaen"); private static final War3ID ATTACKS_ENABLED = War3ID.fromString("uaen");
private static final War3ID ATTACK1_BACKSWING_POINT = War3ID.fromString("ubs1"); private static final War3ID ATTACK1_BACKSWING_POINT = War3ID.fromString("ubs1");
private static final War3ID ATTACK1_DAMAGE_POINT = War3ID.fromString("udp1"); private static final War3ID ATTACK1_DAMAGE_POINT = War3ID.fromString("udp1");
private static final War3ID ATTACK1_AREA_OF_EFFECT_FULL_DMG = War3ID.fromString("ua1f"); private static final War3ID ATTACK1_AREA_OF_EFFECT_FULL_DMG = War3ID.fromString("ua1f");
private static final War3ID ATTACK1_AREA_OF_EFFECT_HALF_DMG = War3ID.fromString("ua1h"); private static final War3ID ATTACK1_AREA_OF_EFFECT_HALF_DMG = War3ID.fromString("ua1h");
private static final War3ID ATTACK1_AREA_OF_EFFECT_QUARTER_DMG = War3ID.fromString("ua1q"); private static final War3ID ATTACK1_AREA_OF_EFFECT_QUARTER_DMG = War3ID.fromString("ua1q");
private static final War3ID ATTACK1_AREA_OF_EFFECT_TARGETS = War3ID.fromString("ua1p"); private static final War3ID ATTACK1_AREA_OF_EFFECT_TARGETS = War3ID.fromString("ua1p");
private static final War3ID ATTACK1_ATTACK_TYPE = War3ID.fromString("ua1t"); private static final War3ID ATTACK1_ATTACK_TYPE = War3ID.fromString("ua1t");
private static final War3ID ATTACK1_COOLDOWN = War3ID.fromString("ua1c"); private static final War3ID ATTACK1_COOLDOWN = War3ID.fromString("ua1c");
private static final War3ID ATTACK1_DMG_BASE = War3ID.fromString("ua1b"); private static final War3ID ATTACK1_DMG_BASE = War3ID.fromString("ua1b");
private static final War3ID ATTACK1_DAMAGE_FACTOR_HALF = War3ID.fromString("uhd1"); private static final War3ID ATTACK1_DAMAGE_FACTOR_HALF = War3ID.fromString("uhd1");
private static final War3ID ATTACK1_DAMAGE_FACTOR_QUARTER = War3ID.fromString("uqd1"); private static final War3ID ATTACK1_DAMAGE_FACTOR_QUARTER = War3ID.fromString("uqd1");
private static final War3ID ATTACK1_DAMAGE_LOSS_FACTOR = War3ID.fromString("udl1"); private static final War3ID ATTACK1_DAMAGE_LOSS_FACTOR = War3ID.fromString("udl1");
private static final War3ID ATTACK1_DMG_DICE = War3ID.fromString("ua1d"); private static final War3ID ATTACK1_DMG_DICE = War3ID.fromString("ua1d");
private static final War3ID ATTACK1_DMG_SIDES_PER_DIE = War3ID.fromString("ua1s"); private static final War3ID ATTACK1_DMG_SIDES_PER_DIE = War3ID.fromString("ua1s");
private static final War3ID ATTACK1_DMG_SPILL_DIST = War3ID.fromString("usd1"); private static final War3ID ATTACK1_DMG_SPILL_DIST = War3ID.fromString("usd1");
private static final War3ID ATTACK1_DMG_SPILL_RADIUS = War3ID.fromString("usr1"); private static final War3ID ATTACK1_DMG_SPILL_RADIUS = War3ID.fromString("usr1");
private static final War3ID ATTACK1_DMG_UPGRADE_AMT = War3ID.fromString("udu1"); private static final War3ID ATTACK1_DMG_UPGRADE_AMT = War3ID.fromString("udu1");
private static final War3ID ATTACK1_TARGET_COUNT = War3ID.fromString("utc1"); private static final War3ID ATTACK1_TARGET_COUNT = War3ID.fromString("utc1");
private static final War3ID ATTACK1_PROJECTILE_ARC = War3ID.fromString("uma1"); private static final War3ID ATTACK1_PROJECTILE_ARC = War3ID.fromString("uma1");
private static final War3ID ATTACK1_MISSILE_ART = War3ID.fromString("ua1m"); private static final War3ID ATTACK1_MISSILE_ART = War3ID.fromString("ua1m");
private static final War3ID ATTACK1_PROJECTILE_HOMING_ENABLED = War3ID.fromString("umh1"); private static final War3ID ATTACK1_PROJECTILE_HOMING_ENABLED = War3ID.fromString("umh1");
private static final War3ID ATTACK1_PROJECTILE_SPEED = War3ID.fromString("ua1z"); private static final War3ID ATTACK1_PROJECTILE_SPEED = War3ID.fromString("ua1z");
private static final War3ID ATTACK1_RANGE = War3ID.fromString("ua1r"); private static final War3ID ATTACK1_RANGE = War3ID.fromString("ua1r");
private static final War3ID ATTACK1_RANGE_MOTION_BUFFER = War3ID.fromString("urb1"); private static final War3ID ATTACK1_RANGE_MOTION_BUFFER = War3ID.fromString("urb1");
private static final War3ID ATTACK1_SHOW_UI = War3ID.fromString("uwu1"); private static final War3ID ATTACK1_SHOW_UI = War3ID.fromString("uwu1");
private static final War3ID ATTACK1_TARGETS_ALLOWED = War3ID.fromString("ua1g"); private static final War3ID ATTACK1_TARGETS_ALLOWED = War3ID.fromString("ua1g");
private static final War3ID ATTACK1_WEAPON_SOUND = War3ID.fromString("ucs1"); private static final War3ID ATTACK1_WEAPON_SOUND = War3ID.fromString("ucs1");
private static final War3ID ATTACK1_WEAPON_TYPE = War3ID.fromString("ua1w"); private static final War3ID ATTACK1_WEAPON_TYPE = War3ID.fromString("ua1w");
private static final War3ID ATTACK2_BACKSWING_POINT = War3ID.fromString("ubs2"); private static final War3ID ATTACK2_BACKSWING_POINT = War3ID.fromString("ubs2");
private static final War3ID ATTACK2_DAMAGE_POINT = War3ID.fromString("udp2"); private static final War3ID ATTACK2_DAMAGE_POINT = War3ID.fromString("udp2");
private static final War3ID ATTACK2_AREA_OF_EFFECT_FULL_DMG = War3ID.fromString("ua2f"); private static final War3ID ATTACK2_AREA_OF_EFFECT_FULL_DMG = War3ID.fromString("ua2f");
private static final War3ID ATTACK2_AREA_OF_EFFECT_HALF_DMG = War3ID.fromString("ua2h"); private static final War3ID ATTACK2_AREA_OF_EFFECT_HALF_DMG = War3ID.fromString("ua2h");
private static final War3ID ATTACK2_AREA_OF_EFFECT_QUARTER_DMG = War3ID.fromString("ua2q"); private static final War3ID ATTACK2_AREA_OF_EFFECT_QUARTER_DMG = War3ID.fromString("ua2q");
private static final War3ID ATTACK2_AREA_OF_EFFECT_TARGETS = War3ID.fromString("ua2p"); private static final War3ID ATTACK2_AREA_OF_EFFECT_TARGETS = War3ID.fromString("ua2p");
private static final War3ID ATTACK2_ATTACK_TYPE = War3ID.fromString("ua2t"); private static final War3ID ATTACK2_ATTACK_TYPE = War3ID.fromString("ua2t");
private static final War3ID ATTACK2_COOLDOWN = War3ID.fromString("ua2c"); private static final War3ID ATTACK2_COOLDOWN = War3ID.fromString("ua2c");
private static final War3ID ATTACK2_DMG_BASE = War3ID.fromString("ua2b"); private static final War3ID ATTACK2_DMG_BASE = War3ID.fromString("ua2b");
private static final War3ID ATTACK2_DAMAGE_FACTOR_HALF = War3ID.fromString("uhd2"); private static final War3ID ATTACK2_DAMAGE_FACTOR_HALF = War3ID.fromString("uhd2");
private static final War3ID ATTACK2_DAMAGE_FACTOR_QUARTER = War3ID.fromString("uqd2"); private static final War3ID ATTACK2_DAMAGE_FACTOR_QUARTER = War3ID.fromString("uqd2");
private static final War3ID ATTACK2_DAMAGE_LOSS_FACTOR = War3ID.fromString("udl2"); private static final War3ID ATTACK2_DAMAGE_LOSS_FACTOR = War3ID.fromString("udl2");
private static final War3ID ATTACK2_DMG_DICE = War3ID.fromString("ua2d"); private static final War3ID ATTACK2_DMG_DICE = War3ID.fromString("ua2d");
private static final War3ID ATTACK2_DMG_SIDES_PER_DIE = War3ID.fromString("ua2s"); private static final War3ID ATTACK2_DMG_SIDES_PER_DIE = War3ID.fromString("ua2s");
private static final War3ID ATTACK2_DMG_SPILL_DIST = War3ID.fromString("usd2"); private static final War3ID ATTACK2_DMG_SPILL_DIST = War3ID.fromString("usd2");
private static final War3ID ATTACK2_DMG_SPILL_RADIUS = War3ID.fromString("usr2"); private static final War3ID ATTACK2_DMG_SPILL_RADIUS = War3ID.fromString("usr2");
private static final War3ID ATTACK2_DMG_UPGRADE_AMT = War3ID.fromString("udu2"); private static final War3ID ATTACK2_DMG_UPGRADE_AMT = War3ID.fromString("udu2");
private static final War3ID ATTACK2_TARGET_COUNT = War3ID.fromString("utc2"); private static final War3ID ATTACK2_TARGET_COUNT = War3ID.fromString("utc2");
private static final War3ID ATTACK2_PROJECTILE_ARC = War3ID.fromString("uma2"); private static final War3ID ATTACK2_PROJECTILE_ARC = War3ID.fromString("uma2");
private static final War3ID ATTACK2_MISSILE_ART = War3ID.fromString("ua2m"); private static final War3ID ATTACK2_MISSILE_ART = War3ID.fromString("ua2m");
private static final War3ID ATTACK2_PROJECTILE_HOMING_ENABLED = War3ID.fromString("umh2"); private static final War3ID ATTACK2_PROJECTILE_HOMING_ENABLED = War3ID.fromString("umh2");
private static final War3ID ATTACK2_PROJECTILE_SPEED = War3ID.fromString("ua2z"); private static final War3ID ATTACK2_PROJECTILE_SPEED = War3ID.fromString("ua2z");
private static final War3ID ATTACK2_RANGE = War3ID.fromString("ua2r"); private static final War3ID ATTACK2_RANGE = War3ID.fromString("ua2r");
private static final War3ID ATTACK2_RANGE_MOTION_BUFFER = War3ID.fromString("urb2"); private static final War3ID ATTACK2_RANGE_MOTION_BUFFER = War3ID.fromString("urb2");
private static final War3ID ATTACK2_SHOW_UI = War3ID.fromString("uwu2"); private static final War3ID ATTACK2_SHOW_UI = War3ID.fromString("uwu2");
private static final War3ID ATTACK2_TARGETS_ALLOWED = War3ID.fromString("ua2g"); private static final War3ID ATTACK2_TARGETS_ALLOWED = War3ID.fromString("ua2g");
private static final War3ID ATTACK2_WEAPON_SOUND = War3ID.fromString("ucs2"); private static final War3ID ATTACK2_WEAPON_SOUND = War3ID.fromString("ucs2");
private static final War3ID ATTACK2_WEAPON_TYPE = War3ID.fromString("ua2w"); private static final War3ID ATTACK2_WEAPON_TYPE = War3ID.fromString("ua2w");
private static final War3ID ACQUISITION_RANGE = War3ID.fromString("uacq"); private static final War3ID ACQUISITION_RANGE = War3ID.fromString("uacq");
private static final War3ID MINIMUM_ATTACK_RANGE = War3ID.fromString("uamn"); private static final War3ID MINIMUM_ATTACK_RANGE = War3ID.fromString("uamn");
private static final War3ID PROJECTILE_IMPACT_Z = War3ID.fromString("uimz"); private static final War3ID PROJECTILE_IMPACT_Z = War3ID.fromString("uimz");
private static final War3ID DEATH_TYPE = War3ID.fromString("udea"); private static final War3ID DEATH_TYPE = War3ID.fromString("udea");
private static final War3ID ARMOR_TYPE = War3ID.fromString("uarm"); private static final War3ID ARMOR_TYPE = War3ID.fromString("uarm");
private static final War3ID DEFENSE = War3ID.fromString("udef"); private static final War3ID DEFENSE = War3ID.fromString("udef");
private static final War3ID DEFENSE_TYPE = War3ID.fromString("udty"); private static final War3ID DEFENSE_TYPE = War3ID.fromString("udty");
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh"); private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
private static final War3ID MOVE_TYPE = War3ID.fromString("umvt"); private static final War3ID MOVE_TYPE = War3ID.fromString("umvt");
private static final War3ID COLLISION_SIZE = War3ID.fromString("ucol"); private static final War3ID COLLISION_SIZE = War3ID.fromString("ucol");
private static final War3ID CLASSIFICATION = War3ID.fromString("utyp"); private static final War3ID CLASSIFICATION = War3ID.fromString("utyp");
private static final War3ID DEATH_TIME = War3ID.fromString("udtm"); private static final War3ID DEATH_TIME = War3ID.fromString("udtm");
private static final War3ID TARGETED_AS = War3ID.fromString("utar"); private static final War3ID TARGETED_AS = War3ID.fromString("utar");
private final MutableObjectData unitData;
private final Map<War3ID, CUnitType> unitIdToUnitType = new HashMap<>();
public CUnitData(final MutableObjectData unitData) { private static final War3ID ABILITIES_NORMAL = War3ID.fromString("uabi");
this.unitData = unitData; private final MutableObjectData unitData;
} private final Map<War3ID, CUnitType> unitIdToUnitType = new HashMap<>();
private final CAbilityData abilityData;
public CUnit create(final CSimulation simulation, final int playerIndex, final War3ID typeId, final float x, public CUnitData(final MutableObjectData unitData, final CAbilityData abilityData) {
final float y, final float facing, final BufferedImage buildingPathingPixelMap, this.unitData = unitData;
final SimulationRenderController simulationRenderController, final HandleIdAllocator handleIdAllocator) { this.abilityData = abilityData;
final MutableGameObject unitType = this.unitData.get(typeId); }
final int handleId = handleIdAllocator.createId();
final int life = unitType.getFieldAsInteger(HIT_POINT_MAXIMUM, 0);
final int manaInitial = unitType.getFieldAsInteger(MANA_INITIAL_AMOUNT, 0);
final int manaMaximum = unitType.getFieldAsInteger(MANA_MAXIMUM, 0);
final int speed = unitType.getFieldAsInteger(MOVEMENT_SPEED_BASE, 0);
final int defense = unitType.getFieldAsInteger(DEFENSE, 0);
final CUnitType unitTypeInstance = getUnitTypeInstance(typeId, buildingPathingPixelMap, unitType); public CUnit create(final CSimulation simulation, final int playerIndex, final War3ID typeId, final float x,
final float y, final float facing, final BufferedImage buildingPathingPixelMap,
final SimulationRenderController simulationRenderController, final HandleIdAllocator handleIdAllocator) {
final MutableGameObject unitType = this.unitData.get(typeId);
final int handleId = handleIdAllocator.createId();
final int life = unitType.getFieldAsInteger(HIT_POINT_MAXIMUM, 0);
final int manaInitial = unitType.getFieldAsInteger(MANA_INITIAL_AMOUNT, 0);
final int manaMaximum = unitType.getFieldAsInteger(MANA_MAXIMUM, 0);
final int speed = unitType.getFieldAsInteger(MOVEMENT_SPEED_BASE, 0);
final int defense = unitType.getFieldAsInteger(DEFENSE, 0);
final String abilityList = unitType.getFieldAsString(ABILITIES_NORMAL, 0);
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum, final CUnitType unitTypeInstance = getUnitTypeInstance(typeId, buildingPathingPixelMap, unitType);
speed, defense, unitTypeInstance);
if (speed > 0) {
unit.add(simulation, new CAbilityMove(handleIdAllocator.createId()));
}
if (!unitTypeInstance.getAttacks().isEmpty()) {
unit.add(simulation, new CAbilityAttack(handleIdAllocator.createId()));
}
return unit;
}
private CUnitType getUnitTypeInstance(final War3ID typeId, final BufferedImage buildingPathingPixelMap, final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
final MutableGameObject unitType) { speed, defense, unitTypeInstance);
CUnitType unitTypeInstance = this.unitIdToUnitType.get(typeId); if (speed > 0) {
if (unitTypeInstance == null) { unit.add(simulation, new CAbilityMove(handleIdAllocator.createId()));
final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0); }
final String movetp = unitType.getFieldAsString(MOVE_TYPE, 0); if (!unitTypeInstance.getAttacks().isEmpty()) {
final float collisionSize = unitType.getFieldAsFloat(COLLISION_SIZE, 0); unit.add(simulation, new CAbilityAttack(handleIdAllocator.createId()));
final boolean isBldg = unitType.getFieldAsBoolean(IS_BLDG, 0); }
final PathingGrid.MovementType movementType = PathingGrid.getMovementType(movetp); for (final String ability : abilityList.split(",")) {
final String unitName = unitType.getFieldAsString(NAME, 0); unit.add(simulation, this.abilityData.createAbility(ability, handleIdAllocator.createId()));
final float acquisitionRange = unitType.getFieldAsFloat(ACQUISITION_RANGE, 0); }
final float minimumAttackRange = unitType.getFieldAsFloat(MINIMUM_ATTACK_RANGE, 0); return unit;
final EnumSet<CTargetType> targetedAs = CTargetType }
.parseTargetTypeSet(unitType.getFieldAsString(TARGETED_AS, 0));
final String classificationString = unitType.getFieldAsString(CLASSIFICATION, 0);
final EnumSet<CUnitClassification> classifications = EnumSet.noneOf(CUnitClassification.class);
if (classificationString != null) {
final String[] classificationValues = classificationString.split(",");
for (final String unitEditorKey : classificationValues) {
final CUnitClassification unitClassification = CUnitClassification
.parseUnitClassification(unitEditorKey);
if (unitClassification != null) {
classifications.add(unitClassification);
}
}
}
final List<CUnitAttack> attacks = new ArrayList<>();
final int attacksEnabled = unitType.getFieldAsInteger(ATTACKS_ENABLED, 0);
if ((attacksEnabled & 0x1) != 0) {
try {
// attack one
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK1_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK1_DAMAGE_POINT, 0);
final int areaOfEffectFullDamage = unitType.getFieldAsInteger(ATTACK1_AREA_OF_EFFECT_FULL_DMG, 0);
final int areaOfEffectMediumDamage = unitType.getFieldAsInteger(ATTACK1_AREA_OF_EFFECT_HALF_DMG, 0);
final int areaOfEffectSmallDamage = unitType.getFieldAsInteger(ATTACK1_AREA_OF_EFFECT_QUARTER_DMG, 0);
final EnumSet<CTargetType> areaOfEffectTargets = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK1_AREA_OF_EFFECT_TARGETS, 0));
final CAttackType attackType = CAttackType
.parseAttackType(unitType.getFieldAsString(ATTACK1_ATTACK_TYPE, 0));
final float cooldownTime = unitType.getFieldAsFloat(ATTACK1_COOLDOWN, 0);
final int damageBase = unitType.getFieldAsInteger(ATTACK1_DMG_BASE, 0);
final float damageFactorMedium = unitType.getFieldAsFloat(ATTACK1_DAMAGE_FACTOR_HALF, 0);
final float damageFactorSmall = unitType.getFieldAsFloat(ATTACK1_DAMAGE_FACTOR_QUARTER, 0);
final float damageLossFactor = unitType.getFieldAsFloat(ATTACK1_DAMAGE_LOSS_FACTOR, 0);
final int damageDice = unitType.getFieldAsInteger(ATTACK1_DMG_DICE, 0);
final int damageSidesPerDie = unitType.getFieldAsInteger(ATTACK1_DMG_SIDES_PER_DIE, 0);
final float damageSpillDistance = unitType.getFieldAsFloat(ATTACK1_DMG_SPILL_DIST, 0);
final float damageSpillRadius = unitType.getFieldAsFloat(ATTACK1_DMG_SPILL_RADIUS, 0);
final int damageUpgradeAmount = unitType.getFieldAsInteger(ATTACK1_DMG_UPGRADE_AMT, 0);
final int maximumNumberOfTargets = unitType.getFieldAsInteger(ATTACK1_TARGET_COUNT, 0);
final float projectileArc = unitType.getFieldAsFloat(ATTACK1_PROJECTILE_ARC, 0);
final String projectileArt = unitType.getFieldAsString(ATTACK1_MISSILE_ART, 0);
final boolean projectileHomingEnabled = unitType.getFieldAsBoolean(ATTACK1_PROJECTILE_HOMING_ENABLED,
0);
final int projectileSpeed = unitType.getFieldAsInteger(ATTACK1_PROJECTILE_SPEED, 0);
final int range = unitType.getFieldAsInteger(ATTACK1_RANGE, 0);
final float rangeMotionBuffer = unitType.getFieldAsFloat(ATTACK1_RANGE_MOTION_BUFFER, 0);
final boolean showUI = unitType.getFieldAsBoolean(ATTACK1_SHOW_UI, 0);
final EnumSet<CTargetType> targetsAllowed = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK1_TARGETS_ALLOWED, 0));
final String weaponSound = unitType.getFieldAsString(ATTACK1_WEAPON_SOUND, 0);
final CWeaponType weaponType = CWeaponType
.parseWeaponType(unitType.getFieldAsString(ATTACK1_WEAPON_TYPE, 0));
attacks.add(createAttack(animationBackswingPoint, animationDamagePoint, areaOfEffectFullDamage,
areaOfEffectMediumDamage, areaOfEffectSmallDamage, areaOfEffectTargets, attackType,
cooldownTime, damageBase, damageFactorMedium, damageFactorSmall, damageLossFactor, damageDice,
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
} catch (Exception exc) {
System.err.println("Attack 1 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
if ((attacksEnabled & 0x2) != 0) {
try {
// attack two
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK2_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK2_DAMAGE_POINT, 0);
final int areaOfEffectFullDamage = unitType.getFieldAsInteger(ATTACK2_AREA_OF_EFFECT_FULL_DMG, 0);
final int areaOfEffectMediumDamage = unitType.getFieldAsInteger(ATTACK2_AREA_OF_EFFECT_HALF_DMG, 0);
final int areaOfEffectSmallDamage = unitType.getFieldAsInteger(ATTACK2_AREA_OF_EFFECT_QUARTER_DMG, 0);
final EnumSet<CTargetType> areaOfEffectTargets = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK2_AREA_OF_EFFECT_TARGETS, 0));
final CAttackType attackType = CAttackType
.parseAttackType(unitType.getFieldAsString(ATTACK2_ATTACK_TYPE, 0));
final float cooldownTime = unitType.getFieldAsFloat(ATTACK2_COOLDOWN, 0);
final int damageBase = unitType.getFieldAsInteger(ATTACK2_DMG_BASE, 0);
final float damageFactorMedium = unitType.getFieldAsFloat(ATTACK2_DAMAGE_FACTOR_HALF, 0);
final float damageFactorSmall = unitType.getFieldAsFloat(ATTACK2_DAMAGE_FACTOR_QUARTER, 0);
final float damageLossFactor = unitType.getFieldAsFloat(ATTACK2_DAMAGE_LOSS_FACTOR, 0);
final int damageDice = unitType.getFieldAsInteger(ATTACK2_DMG_DICE, 0);
final int damageSidesPerDie = unitType.getFieldAsInteger(ATTACK2_DMG_SIDES_PER_DIE, 0);
final float damageSpillDistance = unitType.getFieldAsFloat(ATTACK2_DMG_SPILL_DIST, 0);
final float damageSpillRadius = unitType.getFieldAsFloat(ATTACK2_DMG_SPILL_RADIUS, 0);
final int damageUpgradeAmount = unitType.getFieldAsInteger(ATTACK2_DMG_UPGRADE_AMT, 0);
final int maximumNumberOfTargets = unitType.getFieldAsInteger(ATTACK2_TARGET_COUNT, 0);
final float projectileArc = unitType.getFieldAsFloat(ATTACK2_PROJECTILE_ARC, 0);
final String projectileArt = unitType.getFieldAsString(ATTACK2_MISSILE_ART, 0);
final boolean projectileHomingEnabled = unitType.getFieldAsBoolean(ATTACK2_PROJECTILE_HOMING_ENABLED,
0);
final int projectileSpeed = unitType.getFieldAsInteger(ATTACK2_PROJECTILE_SPEED, 0);
final int range = unitType.getFieldAsInteger(ATTACK2_RANGE, 0);
final float rangeMotionBuffer = unitType.getFieldAsFloat(ATTACK2_RANGE_MOTION_BUFFER, 0);
final boolean showUI = unitType.getFieldAsBoolean(ATTACK2_SHOW_UI, 0);
final EnumSet<CTargetType> targetsAllowed = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK2_TARGETS_ALLOWED, 0));
final String weaponSound = unitType.getFieldAsString(ATTACK2_WEAPON_SOUND, 0);
final CWeaponType weaponType = CWeaponType
.parseWeaponType(unitType.getFieldAsString(ATTACK2_WEAPON_TYPE, 0));
attacks.add(createAttack(animationBackswingPoint, animationDamagePoint, areaOfEffectFullDamage,
areaOfEffectMediumDamage, areaOfEffectSmallDamage, areaOfEffectTargets, attackType,
cooldownTime, damageBase, damageFactorMedium, damageFactorSmall, damageLossFactor, damageDice,
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
} catch (Exception exc) {
System.err.println("Attack 2 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
final int deathType = unitType.getFieldAsInteger(DEATH_TYPE, 0);
final boolean raise = (deathType & 0x1) != 0;
final boolean decay = (deathType & 0x2) != 0;
final String armorType = unitType.getFieldAsString(ARMOR_TYPE, 0);
final float impactZ = unitType.getFieldAsFloat(PROJECTILE_IMPACT_Z, 0);
final CDefenseType defenseType = CDefenseType.parseDefenseType(unitType.getFieldAsString(DEFENSE_TYPE, 0));
final float deathTime = unitType.getFieldAsFloat(DEATH_TIME, 0);
unitTypeInstance = new CUnitType(unitName, isBldg, movementType, moveHeight, collisionSize, classifications,
attacks, armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime,
targetedAs, acquisitionRange, minimumAttackRange);
this.unitIdToUnitType.put(typeId, unitTypeInstance);
}
return unitTypeInstance;
}
private CUnitAttack createAttack(final float animationBackswingPoint, final float animationDamagePoint, private CUnitType getUnitTypeInstance(final War3ID typeId, final BufferedImage buildingPathingPixelMap,
final int areaOfEffectFullDamage, final int areaOfEffectMediumDamage, final int areaOfEffectSmallDamage, final MutableGameObject unitType) {
final EnumSet<CTargetType> areaOfEffectTargets, final CAttackType attackType, final float cooldownTime, CUnitType unitTypeInstance = this.unitIdToUnitType.get(typeId);
final int damageBase, final float damageFactorMedium, final float damageFactorSmall, if (unitTypeInstance == null) {
final float damageLossFactor, final int damageDice, final int damageSidesPerDie, final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0);
final float damageSpillDistance, final float damageSpillRadius, final int damageUpgradeAmount, final String movetp = unitType.getFieldAsString(MOVE_TYPE, 0);
final int maximumNumberOfTargets, final float projectileArc, final String projectileArt, final float collisionSize = unitType.getFieldAsFloat(COLLISION_SIZE, 0);
final boolean projectileHomingEnabled, final int projectileSpeed, final int range, final boolean isBldg = unitType.getFieldAsBoolean(IS_BLDG, 0);
final float rangeMotionBuffer, final boolean showUI, final EnumSet<CTargetType> targetsAllowed, final PathingGrid.MovementType movementType = PathingGrid.getMovementType(movetp);
final String weaponSound, final CWeaponType weaponType) { final String unitName = unitType.getFieldAsString(NAME, 0);
final CUnitAttack attack; final float acquisitionRange = unitType.getFieldAsFloat(ACQUISITION_RANGE, 0);
switch (weaponType) { final float minimumAttackRange = unitType.getFieldAsFloat(MINIMUM_ATTACK_RANGE, 0);
case MISSILE: final EnumSet<CTargetType> targetedAs = CTargetType
attack = new CUnitAttackMissile(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime, .parseTargetTypeSet(unitType.getFieldAsString(TARGETED_AS, 0));
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI, final String classificationString = unitType.getFieldAsString(CLASSIFICATION, 0);
targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt, projectileHomingEnabled, final EnumSet<CUnitClassification> classifications = EnumSet.noneOf(CUnitClassification.class);
projectileSpeed); if (classificationString != null) {
break; final String[] classificationValues = classificationString.split(",");
case MBOUNCE: for (final String unitEditorKey : classificationValues) {
attack = new CUnitAttackMissileBounce(animationBackswingPoint, animationDamagePoint, attackType, final CUnitClassification unitClassification = CUnitClassification
cooldownTime, damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, .parseUnitClassification(unitEditorKey);
rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt, if (unitClassification != null) {
projectileHomingEnabled, projectileSpeed, damageLossFactor, maximumNumberOfTargets, classifications.add(unitClassification);
areaOfEffectFullDamage, areaOfEffectTargets); }
break; }
case MSPLASH: }
case ARTILLERY: final List<CUnitAttack> attacks = new ArrayList<>();
attack = new CUnitAttackMissileSplash(animationBackswingPoint, animationDamagePoint, attackType, final int attacksEnabled = unitType.getFieldAsInteger(ATTACKS_ENABLED, 0);
cooldownTime, damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, if ((attacksEnabled & 0x1) != 0) {
rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt, try {
projectileHomingEnabled, projectileSpeed, areaOfEffectFullDamage, areaOfEffectMediumDamage, // attack one
areaOfEffectSmallDamage, areaOfEffectTargets, damageFactorMedium, damageFactorSmall); final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK1_BACKSWING_POINT, 0);
break; final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK1_DAMAGE_POINT, 0);
case MLINE: final int areaOfEffectFullDamage = unitType.getFieldAsInteger(ATTACK1_AREA_OF_EFFECT_FULL_DMG, 0);
case ALINE: final int areaOfEffectMediumDamage = unitType.getFieldAsInteger(ATTACK1_AREA_OF_EFFECT_HALF_DMG, 0);
attack = new CUnitAttackMissileLine(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime, final int areaOfEffectSmallDamage = unitType.getFieldAsInteger(ATTACK1_AREA_OF_EFFECT_QUARTER_DMG,
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI, 0);
targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt, projectileHomingEnabled, final EnumSet<CTargetType> areaOfEffectTargets = CTargetType
projectileSpeed, damageSpillDistance, damageSpillRadius); .parseTargetTypeSet(unitType.getFieldAsString(ATTACK1_AREA_OF_EFFECT_TARGETS, 0));
break; final CAttackType attackType = CAttackType
case INSTANT: .parseAttackType(unitType.getFieldAsString(ATTACK1_ATTACK_TYPE, 0));
attack = new CUnitAttackInstant(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime, final float cooldownTime = unitType.getFieldAsFloat(ATTACK1_COOLDOWN, 0);
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI, final int damageBase = unitType.getFieldAsInteger(ATTACK1_DMG_BASE, 0);
targetsAllowed, weaponSound, weaponType, projectileArt); final float damageFactorMedium = unitType.getFieldAsFloat(ATTACK1_DAMAGE_FACTOR_HALF, 0);
break; final float damageFactorSmall = unitType.getFieldAsFloat(ATTACK1_DAMAGE_FACTOR_QUARTER, 0);
default: final float damageLossFactor = unitType.getFieldAsFloat(ATTACK1_DAMAGE_LOSS_FACTOR, 0);
case NORMAL: final int damageDice = unitType.getFieldAsInteger(ATTACK1_DMG_DICE, 0);
attack = new CUnitAttackNormal(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime, final int damageSidesPerDie = unitType.getFieldAsInteger(ATTACK1_DMG_SIDES_PER_DIE, 0);
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI, final float damageSpillDistance = unitType.getFieldAsFloat(ATTACK1_DMG_SPILL_DIST, 0);
targetsAllowed, weaponSound, weaponType); final float damageSpillRadius = unitType.getFieldAsFloat(ATTACK1_DMG_SPILL_RADIUS, 0);
break; final int damageUpgradeAmount = unitType.getFieldAsInteger(ATTACK1_DMG_UPGRADE_AMT, 0);
} final int maximumNumberOfTargets = unitType.getFieldAsInteger(ATTACK1_TARGET_COUNT, 0);
return attack; final float projectileArc = unitType.getFieldAsFloat(ATTACK1_PROJECTILE_ARC, 0);
} final String projectileArt = unitType.getFieldAsString(ATTACK1_MISSILE_ART, 0);
final boolean projectileHomingEnabled = unitType
.getFieldAsBoolean(ATTACK1_PROJECTILE_HOMING_ENABLED, 0);
final int projectileSpeed = unitType.getFieldAsInteger(ATTACK1_PROJECTILE_SPEED, 0);
final int range = unitType.getFieldAsInteger(ATTACK1_RANGE, 0);
final float rangeMotionBuffer = unitType.getFieldAsFloat(ATTACK1_RANGE_MOTION_BUFFER, 0);
final boolean showUI = unitType.getFieldAsBoolean(ATTACK1_SHOW_UI, 0);
final EnumSet<CTargetType> targetsAllowed = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK1_TARGETS_ALLOWED, 0));
final String weaponSound = unitType.getFieldAsString(ATTACK1_WEAPON_SOUND, 0);
final CWeaponType weaponType = CWeaponType
.parseWeaponType(unitType.getFieldAsString(ATTACK1_WEAPON_TYPE, 0));
attacks.add(createAttack(animationBackswingPoint, animationDamagePoint, areaOfEffectFullDamage,
areaOfEffectMediumDamage, areaOfEffectSmallDamage, areaOfEffectTargets, attackType,
cooldownTime, damageBase, damageFactorMedium, damageFactorSmall, damageLossFactor,
damageDice, damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled,
projectileSpeed, range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound,
weaponType));
}
catch (final Exception exc) {
System.err.println("Attack 1 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
if ((attacksEnabled & 0x2) != 0) {
try {
// attack two
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK2_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK2_DAMAGE_POINT, 0);
final int areaOfEffectFullDamage = unitType.getFieldAsInteger(ATTACK2_AREA_OF_EFFECT_FULL_DMG, 0);
final int areaOfEffectMediumDamage = unitType.getFieldAsInteger(ATTACK2_AREA_OF_EFFECT_HALF_DMG, 0);
final int areaOfEffectSmallDamage = unitType.getFieldAsInteger(ATTACK2_AREA_OF_EFFECT_QUARTER_DMG,
0);
final EnumSet<CTargetType> areaOfEffectTargets = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK2_AREA_OF_EFFECT_TARGETS, 0));
final CAttackType attackType = CAttackType
.parseAttackType(unitType.getFieldAsString(ATTACK2_ATTACK_TYPE, 0));
final float cooldownTime = unitType.getFieldAsFloat(ATTACK2_COOLDOWN, 0);
final int damageBase = unitType.getFieldAsInteger(ATTACK2_DMG_BASE, 0);
final float damageFactorMedium = unitType.getFieldAsFloat(ATTACK2_DAMAGE_FACTOR_HALF, 0);
final float damageFactorSmall = unitType.getFieldAsFloat(ATTACK2_DAMAGE_FACTOR_QUARTER, 0);
final float damageLossFactor = unitType.getFieldAsFloat(ATTACK2_DAMAGE_LOSS_FACTOR, 0);
final int damageDice = unitType.getFieldAsInteger(ATTACK2_DMG_DICE, 0);
final int damageSidesPerDie = unitType.getFieldAsInteger(ATTACK2_DMG_SIDES_PER_DIE, 0);
final float damageSpillDistance = unitType.getFieldAsFloat(ATTACK2_DMG_SPILL_DIST, 0);
final float damageSpillRadius = unitType.getFieldAsFloat(ATTACK2_DMG_SPILL_RADIUS, 0);
final int damageUpgradeAmount = unitType.getFieldAsInteger(ATTACK2_DMG_UPGRADE_AMT, 0);
final int maximumNumberOfTargets = unitType.getFieldAsInteger(ATTACK2_TARGET_COUNT, 0);
final float projectileArc = unitType.getFieldAsFloat(ATTACK2_PROJECTILE_ARC, 0);
final String projectileArt = unitType.getFieldAsString(ATTACK2_MISSILE_ART, 0);
final boolean projectileHomingEnabled = unitType
.getFieldAsBoolean(ATTACK2_PROJECTILE_HOMING_ENABLED, 0);
final int projectileSpeed = unitType.getFieldAsInteger(ATTACK2_PROJECTILE_SPEED, 0);
final int range = unitType.getFieldAsInteger(ATTACK2_RANGE, 0);
final float rangeMotionBuffer = unitType.getFieldAsFloat(ATTACK2_RANGE_MOTION_BUFFER, 0);
final boolean showUI = unitType.getFieldAsBoolean(ATTACK2_SHOW_UI, 0);
final EnumSet<CTargetType> targetsAllowed = CTargetType
.parseTargetTypeSet(unitType.getFieldAsString(ATTACK2_TARGETS_ALLOWED, 0));
final String weaponSound = unitType.getFieldAsString(ATTACK2_WEAPON_SOUND, 0);
final CWeaponType weaponType = CWeaponType
.parseWeaponType(unitType.getFieldAsString(ATTACK2_WEAPON_TYPE, 0));
attacks.add(createAttack(animationBackswingPoint, animationDamagePoint, areaOfEffectFullDamage,
areaOfEffectMediumDamage, areaOfEffectSmallDamage, areaOfEffectTargets, attackType,
cooldownTime, damageBase, damageFactorMedium, damageFactorSmall, damageLossFactor,
damageDice, damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled,
projectileSpeed, range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound,
weaponType));
}
catch (final Exception exc) {
System.err.println("Attack 2 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
final int deathType = unitType.getFieldAsInteger(DEATH_TYPE, 0);
final boolean raise = (deathType & 0x1) != 0;
final boolean decay = (deathType & 0x2) != 0;
final String armorType = unitType.getFieldAsString(ARMOR_TYPE, 0);
final float impactZ = unitType.getFieldAsFloat(PROJECTILE_IMPACT_Z, 0);
final CDefenseType defenseType = CDefenseType.parseDefenseType(unitType.getFieldAsString(DEFENSE_TYPE, 0));
final float deathTime = unitType.getFieldAsFloat(DEATH_TIME, 0);
unitTypeInstance = new CUnitType(unitName, isBldg, movementType, moveHeight, collisionSize, classifications,
attacks, armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime,
targetedAs, acquisitionRange, minimumAttackRange);
this.unitIdToUnitType.put(typeId, unitTypeInstance);
}
return unitTypeInstance;
}
public float getPropulsionWindow(final War3ID unitTypeId) { private CUnitAttack createAttack(final float animationBackswingPoint, final float animationDamagePoint,
return this.unitData.get(unitTypeId).getFieldAsFloat(PROPULSION_WINDOW, 0); final int areaOfEffectFullDamage, final int areaOfEffectMediumDamage, final int areaOfEffectSmallDamage,
} final EnumSet<CTargetType> areaOfEffectTargets, final CAttackType attackType, final float cooldownTime,
final int damageBase, final float damageFactorMedium, final float damageFactorSmall,
final float damageLossFactor, final int damageDice, final int damageSidesPerDie,
final float damageSpillDistance, final float damageSpillRadius, final int damageUpgradeAmount,
final int maximumNumberOfTargets, final float projectileArc, final String projectileArt,
final boolean projectileHomingEnabled, final int projectileSpeed, final int range,
final float rangeMotionBuffer, final boolean showUI, final EnumSet<CTargetType> targetsAllowed,
final String weaponSound, final CWeaponType weaponType) {
final CUnitAttack attack;
switch (weaponType) {
case MISSILE:
attack = new CUnitAttackMissile(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime,
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI,
targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt, projectileHomingEnabled,
projectileSpeed);
break;
case MBOUNCE:
attack = new CUnitAttackMissileBounce(animationBackswingPoint, animationDamagePoint, attackType,
cooldownTime, damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range,
rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt,
projectileHomingEnabled, projectileSpeed, damageLossFactor, maximumNumberOfTargets,
areaOfEffectFullDamage, areaOfEffectTargets);
break;
case MSPLASH:
case ARTILLERY:
attack = new CUnitAttackMissileSplash(animationBackswingPoint, animationDamagePoint, attackType,
cooldownTime, damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range,
rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt,
projectileHomingEnabled, projectileSpeed, areaOfEffectFullDamage, areaOfEffectMediumDamage,
areaOfEffectSmallDamage, areaOfEffectTargets, damageFactorMedium, damageFactorSmall);
break;
case MLINE:
case ALINE:
attack = new CUnitAttackMissileLine(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime,
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI,
targetsAllowed, weaponSound, weaponType, projectileArc, projectileArt, projectileHomingEnabled,
projectileSpeed, damageSpillDistance, damageSpillRadius);
break;
case INSTANT:
attack = new CUnitAttackInstant(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime,
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI,
targetsAllowed, weaponSound, weaponType, projectileArt);
break;
default:
case NORMAL:
attack = new CUnitAttackNormal(animationBackswingPoint, animationDamagePoint, attackType, cooldownTime,
damageBase, damageDice, damageSidesPerDie, damageUpgradeAmount, range, rangeMotionBuffer, showUI,
targetsAllowed, weaponSound, weaponType);
break;
}
return attack;
}
public float getTurnRate(final War3ID unitTypeId) { public float getPropulsionWindow(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(TURN_RATE, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(PROPULSION_WINDOW, 0);
} }
public boolean isBuilding(final War3ID unitTypeId) { public float getTurnRate(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsBoolean(IS_BLDG, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(TURN_RATE, 0);
} }
public String getName(final War3ID unitTypeId) { public boolean isBuilding(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getName(); return this.unitData.get(unitTypeId).getFieldAsBoolean(IS_BLDG, 0);
} }
public int getA1MinDamage(final War3ID unitTypeId) { public String getName(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_BASE, 0) return this.unitData.get(unitTypeId).getName();
+ this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_DICE, 0); }
}
public int getA1MaxDamage(final War3ID unitTypeId) { public int getA1MinDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_BASE, 0) return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_BASE, 0)
+ (this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_DICE, 0) + this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_DICE, 0);
* this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_SIDES_PER_DIE, 0)); }
}
public int getA2MinDamage(final War3ID unitTypeId) { public int getA1MaxDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_BASE, 0) return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_BASE, 0)
+ this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_DICE, 0); + (this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_DICE, 0)
} * this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_SIDES_PER_DIE, 0));
}
public int getA2MaxDamage(final War3ID unitTypeId) { public int getA2MinDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_BASE, 0) return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_BASE, 0)
+ (this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_DICE, 0) + this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_DICE, 0);
* this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_SIDES_PER_DIE, 0)); }
}
public int getDefense(final War3ID unitTypeId) { public int getA2MaxDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(DEFENSE, 0); return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_BASE, 0)
} + (this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_DICE, 0)
* this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_SIDES_PER_DIE, 0));
}
public int getA1ProjectileSpeed(final War3ID unitTypeId) { public int getDefense(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_PROJECTILE_SPEED, 0); return this.unitData.get(unitTypeId).getFieldAsInteger(DEFENSE, 0);
} }
public float getA1ProjectileArc(final War3ID unitTypeId) { public int getA1ProjectileSpeed(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_PROJECTILE_ARC, 0); return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_PROJECTILE_SPEED, 0);
} }
public int getA2ProjectileSpeed(final War3ID unitTypeId) { public float getA1ProjectileArc(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_PROJECTILE_SPEED, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_PROJECTILE_ARC, 0);
} }
public float getA2ProjectileArc(final War3ID unitTypeId) { public int getA2ProjectileSpeed(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_PROJECTILE_ARC, 0); return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_PROJECTILE_SPEED, 0);
} }
public String getA1MissileArt(final War3ID unitTypeId) { public float getA2ProjectileArc(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsString(ATTACK1_MISSILE_ART, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_PROJECTILE_ARC, 0);
} }
public String getA2MissileArt(final War3ID unitTypeId) { public String getA1MissileArt(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsString(ATTACK2_MISSILE_ART, 0); return this.unitData.get(unitTypeId).getFieldAsString(ATTACK1_MISSILE_ART, 0);
} }
public float getA1Cooldown(final War3ID unitTypeId) { public String getA2MissileArt(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_COOLDOWN, 0); return this.unitData.get(unitTypeId).getFieldAsString(ATTACK2_MISSILE_ART, 0);
} }
public float getA2Cooldown(final War3ID unitTypeId) { public float getA1Cooldown(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_COOLDOWN, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_COOLDOWN, 0);
} }
public float getProjectileLaunchX(final War3ID unitTypeId) { public float getA2Cooldown(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_X, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_COOLDOWN, 0);
} }
public float getProjectileLaunchY(final War3ID unitTypeId) { public float getProjectileLaunchX(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_Y, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_X, 0);
} }
public float getProjectileLaunchZ(final War3ID unitTypeId) { public float getProjectileLaunchY(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_Z, 0); return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_Y, 0);
} }
public float getProjectileLaunchZ(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_Z, 0);
}
} }

View File

@ -1,23 +0,0 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
public interface CBehavior {
/**
* Executes one step of game simulation of the current order, returning true if
* the order has completed. Many orders may wrap the move order and spend a
* number of simulation steps moving to get within range of the target point
* before completing.
*
* @return
*/
boolean update(CSimulation game);
/**
* Gets the Order ID of the order, useful for determining which icon to
* highlight on the unit's command card.
*
* @return
*/
int getOrderId();
}

View File

@ -1,177 +0,0 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
public class CBehaviorAttack implements CBehavior {
private final CUnit unit;
private final int orderId;
private boolean wasWithinPropWindow = false;
private final CUnitAttack unitAttack;
private final CWidget target;
private int damagePointLaunchTime;
private int backSwingTime;
private CBehavior moveOrder;
private int thisOrderCooldownEndTime;
private boolean wasInRange = false;
public CBehaviorAttack(final CUnit unit, final CUnitAttack unitAttack, final int orderId, final CWidget target) {
this.unit = unit;
this.unitAttack = unitAttack;
this.orderId = orderId;
this.target = target;
createMoveOrder(unit, target);
}
private void createMoveOrder(final CUnit unit, final CWidget target) {
if (!unit.isMovementDisabled()) { // TODO: Check mobility instead
if ((target instanceof CUnit) && !(((CUnit) target).getUnitType().isBuilding())) {
this.moveOrder = new CBehaviorMove(unit, this.orderId, (CUnit) target);
}
else {
this.moveOrder = new CBehaviorMove(unit, this.orderId, target.getX(), target.getY());
}
}
else {
this.moveOrder = null;
}
}
@Override
public boolean update(final CSimulation simulation) {
if (this.target.isDead()
|| !this.target.canBeTargetedBy(simulation, this.unit, this.unitAttack.getTargetsAllowed())) {
return true;
}
float range = this.unitAttack.getRange();
if ((this.target instanceof CUnit) && (((CUnit) this.target).getCurrentOrder() instanceof CBehaviorMove)
&& (this.damagePointLaunchTime != 0 /*
* only apply range motion buffer if they were already in range and
* attacked
*/)) {
range += this.unitAttack.getRangeMotionBuffer();
}
if (!this.unit.canReach(this.target, range)
|| (this.unit.distance(this.target) < this.unit.getUnitType().getMinimumAttackRange())) {
if (this.moveOrder == null) {
return true;
}
if (this.moveOrder.update(simulation)) {
return true; // we just cant reach them
}
this.wasInRange = false;
this.damagePointLaunchTime = 0;
this.thisOrderCooldownEndTime = 0;
return false;
}
this.wasInRange = true;
if (!this.unit.isMovementDisabled()) {
final float prevX = this.unit.getX();
final float prevY = this.unit.getY();
final float deltaY = this.target.getY() - prevY;
final float deltaX = this.target.getX() - prevX;
final double goalAngleRad = Math.atan2(deltaY, deltaX);
float goalAngle = (float) Math.toDegrees(goalAngleRad);
if (goalAngle < 0) {
goalAngle += 360;
}
float facing = this.unit.getFacing();
float delta = goalAngle - facing;
final float propulsionWindow = simulation.getGameplayConstants().getAttackHalfAngle();
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
if (delta < -180) {
delta = 360 + delta;
}
if (delta > 180) {
delta = -360 + delta;
}
final float absDelta = Math.abs(delta);
if ((absDelta <= 1.0) && (absDelta != 0)) {
this.unit.setFacing(goalAngle);
}
else {
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
if (absDelta < Math.abs(angleToAdd)) {
angleToAdd = delta;
}
facing += angleToAdd;
this.unit.setFacing(facing);
}
if (absDelta < propulsionWindow) {
this.wasWithinPropWindow = true;
}
else {
// If this happens, the unit is facing the wrong way, and has to turn before
// moving.
this.wasWithinPropWindow = false;
}
}
else {
this.wasWithinPropWindow = true;
}
final int cooldownEndTime = this.unit.getCooldownEndTime();
final int currentTurnTick = simulation.getGameTurnTick();
if (this.wasWithinPropWindow) {
if (this.damagePointLaunchTime != 0) {
if (currentTurnTick >= this.damagePointLaunchTime) {
int minDamage = this.unitAttack.getMinDamage();
final int maxDamage = Math.max(0, this.unitAttack.getMaxDamage());
if (minDamage > maxDamage) {
minDamage = maxDamage;
}
final int damage;
if (maxDamage == 0) {
damage = 0;
}
else if (minDamage == maxDamage) {
damage = minDamage;
}
else {
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
}
this.unitAttack.launch(simulation, this.unit, this.target, damage);
this.damagePointLaunchTime = 0;
}
}
else if (currentTurnTick >= cooldownEndTime) {
final float cooldownTime = this.unitAttack.getCooldownTime();
final float animationBackswingPoint = this.unitAttack.getAnimationBackswingPoint();
final int a1CooldownSteps = (int) (cooldownTime / WarsmashConstants.SIMULATION_STEP_TIME);
final int a1BackswingSteps = (int) (animationBackswingPoint / WarsmashConstants.SIMULATION_STEP_TIME);
final int a1DamagePointSteps = (int) (this.unitAttack.getAnimationDamagePoint()
/ WarsmashConstants.SIMULATION_STEP_TIME);
this.unit.setCooldownEndTime(currentTurnTick + a1CooldownSteps);
this.thisOrderCooldownEndTime = currentTurnTick + a1CooldownSteps;
this.damagePointLaunchTime = currentTurnTick + a1DamagePointSteps;
this.backSwingTime = currentTurnTick + a1DamagePointSteps + a1BackswingSteps;
this.unit.getUnitAnimationListener().playAnimation(true, PrimaryTag.ATTACK, SequenceUtils.EMPTY, 1.0f,
true);
this.unit.getUnitAnimationListener().queueAnimation(PrimaryTag.STAND, SequenceUtils.READY, false);
}
else if ((currentTurnTick >= this.thisOrderCooldownEndTime)) {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.READY, 1.0f,
false);
}
}
else {
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.READY, 1.0f,
false);
}
return false;
}
@Override
public int getOrderId() {
return this.orderId;
}
}

View File

@ -1,96 +0,0 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
public class CBehaviorPatrol implements CBehavior {
private final CUnit unit;
private final int orderId;
private final CWidget target;
private CBehavior moveOrder;
public CBehaviorPatrol(final CUnit unit, final int orderId, final CUnit target) {
this.unit = unit;
this.orderId = orderId;
this.target = target;
createMoveOrder(unit, target);
}
private void createMoveOrder(final CUnit unit, final CUnit target) {
if (!unit.isMovementDisabled()) { // TODO: Check mobility instead
if ((target instanceof CUnit) && !(target.getUnitType().isBuilding())) {
this.moveOrder = new CBehaviorMove(unit, this.orderId, target);
}
else {
this.moveOrder = new CBehaviorMove(unit, this.orderId, target.getX(), target.getY());
}
}
else {
this.moveOrder = null;
}
}
@Override
public boolean update(final CSimulation simulation) {
if (this.target.isDead()) {
return true;
}
final float range = this.unit.getAcquisitionRange();
if (!this.unit.canReach(this.target, range)) {
if (this.moveOrder == null) {
return true;
}
if (this.moveOrder.update(simulation)) {
return true; // we just cant reach them
}
return false;
}
if (!this.unit.isMovementDisabled()) {
final float prevX = this.unit.getX();
final float prevY = this.unit.getY();
final float deltaY = this.target.getY() - prevY;
final float deltaX = this.target.getX() - prevX;
final double goalAngleRad = Math.atan2(deltaY, deltaX);
float goalAngle = (float) Math.toDegrees(goalAngleRad);
if (goalAngle < 0) {
goalAngle += 360;
}
float facing = this.unit.getFacing();
float delta = goalAngle - facing;
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
if (delta < -180) {
delta = 360 + delta;
}
if (delta > 180) {
delta = -360 + delta;
}
final float absDelta = Math.abs(delta);
if ((absDelta <= 1.0) && (absDelta != 0)) {
this.unit.setFacing(goalAngle);
}
else {
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
if (absDelta < Math.abs(angleToAdd)) {
angleToAdd = delta;
}
facing += angleToAdd;
this.unit.setFacing(facing);
}
}
else {
}
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, false);
return false;
}
@Override
public int getOrderId() {
return this.orderId;
}
}

View File

@ -1,22 +0,0 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
public class CBehaviorStop implements CBehavior {
private final int orderId;
public CBehaviorStop(final int orderId) {
this.orderId = orderId;
}
@Override
public boolean update(final CSimulation game) {
return true;
}
@Override
public int getOrderId() {
return this.orderId;
}
}

View File

@ -0,0 +1,18 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
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.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgAbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgTargetCheckReceiver;
public interface COrder {
int getAbilityHandleId();
int getOrderId();
CBehavior begin(final CSimulation game, CUnit caster);
final StringMsgTargetCheckReceiver<?> targetCheckReceiver = new StringMsgTargetCheckReceiver<>();
final StringMsgAbilityActivationReceiver abilityActivationReceiver = new StringMsgAbilityActivationReceiver();
}

View File

@ -0,0 +1,33 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
public class COrderNoTarget implements COrder {
private final int abilityHandleId;
private final int orderId;
public COrderNoTarget(final int abilityHandleId, final int orderId) {
this.abilityHandleId = abilityHandleId;
this.orderId = orderId;
}
@Override
public int getAbilityHandleId() {
return this.abilityHandleId;
}
@Override
public int getOrderId() {
return this.orderId;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster) {
final CAbility ability = game.getAbility(this.abilityHandleId);
return ability.beginNoTarget(game, caster, this.orderId);
}
}

View File

@ -0,0 +1,57 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.badlogic.gdx.math.Vector2;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgTargetCheckReceiver;
public class COrderTargetPoint implements COrder {
private final int abilityHandleId;
private final int orderId;
private final Vector2 target;
public COrderTargetPoint(final int abilityHandleId, final int orderId, final Vector2 target) {
this.abilityHandleId = abilityHandleId;
this.orderId = orderId;
this.target = target;
}
@Override
public int getAbilityHandleId() {
return this.abilityHandleId;
}
@Override
public int getOrderId() {
return this.orderId;
}
public Vector2 getTarget() {
return this.target;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster) {
final CAbility ability = game.getAbility(this.abilityHandleId);
ability.checkCanUse(game, caster, this.orderId, this.abilityActivationReceiver.reset());
if (this.abilityActivationReceiver.isUseOk()) {
final StringMsgTargetCheckReceiver<Vector2> targetReceiver = (StringMsgTargetCheckReceiver<Vector2>) targetCheckReceiver;
ability.checkCanTarget(game, caster, this.orderId, this.target, targetReceiver);
if (targetReceiver.getTarget() != null) {
return ability.begin(game, caster, this.orderId, this.target);
}
else {
game.getCommandErrorListener().showCommandError(targetReceiver.getMessage());
return caster.pollNextOrderBehavior(game);
}
}
else {
game.getCommandErrorListener().showCommandError(this.abilityActivationReceiver.getMessage());
return caster.pollNextOrderBehavior(game);
}
}
}

View File

@ -1,5 +1,52 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
public class COrderTargetWidget { import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgTargetCheckReceiver;
public class COrderTargetWidget implements COrder {
private final int abilityHandleId;
private final int orderId;
private final int targetHandleId;
public COrderTargetWidget(final int abilityHandleId, final int orderId, final int targetHandleId) {
this.abilityHandleId = abilityHandleId;
this.orderId = orderId;
this.targetHandleId = targetHandleId;
}
@Override
public int getAbilityHandleId() {
return this.abilityHandleId;
}
@Override
public int getOrderId() {
return this.orderId;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster) {
final CAbility ability = game.getAbility(this.abilityHandleId);
ability.checkCanUse(game, caster, this.orderId, abilityActivationReceiver.reset());
if (abilityActivationReceiver.isUseOk()) {
final CUnit target = game.getUnit(this.targetHandleId);
final StringMsgTargetCheckReceiver<CWidget> targetReceiver = (StringMsgTargetCheckReceiver<CWidget>) targetCheckReceiver;
ability.checkCanTarget(game, caster, this.orderId, target, targetReceiver);
if (targetReceiver.getTarget() != null) {
return ability.begin(game, caster, this.orderId, targetReceiver.getTarget());
}
else {
game.getCommandErrorListener().showCommandError(targetReceiver.getMessage());
return caster.pollNextOrderBehavior(game);
}
}
else {
game.getCommandErrorListener().showCommandError(this.abilityActivationReceiver.getMessage());
return caster.pollNextOrderBehavior(game);
}
}
} }

View File

@ -3,21 +3,14 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation.players;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
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.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetPoint;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.StringMsgTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
public class CPlayerUnitOrderExecutor implements CPlayerUnitOrderListener { public class CPlayerUnitOrderExecutor implements CPlayerUnitOrderListener {
private final CSimulation game; private final CSimulation game;
private final CommandErrorListener errorListener; private final CommandErrorListener errorListener;
private final StringMsgTargetCheckReceiver<?> targetCheckReceiver = new StringMsgTargetCheckReceiver<>();
private final StringMsgAbilityActivationReceiver abilityActivationReceiver = new StringMsgAbilityActivationReceiver();
private <T> StringMsgTargetCheckReceiver<T> targetCheckReceiver() {
return (StringMsgTargetCheckReceiver<T>) this.targetCheckReceiver.reset();
}
public CPlayerUnitOrderExecutor(final CSimulation game, final CommandErrorListener errorListener) { public CPlayerUnitOrderExecutor(final CSimulation game, final CommandErrorListener errorListener) {
this.game = game; this.game = game;
@ -25,77 +18,24 @@ public class CPlayerUnitOrderExecutor implements CPlayerUnitOrderListener {
} }
@Override @Override
public boolean issueTargetOrder(final int unitHandleId, final int abilityHandleId, final int orderId, public void issueTargetOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final int targetHandleId, final boolean queue) { final int targetHandleId, final boolean queue) {
final CUnit unit = this.game.getUnit(unitHandleId); final CUnit unit = this.game.getUnit(unitHandleId);
final CAbility ability = this.game.getAbility(abilityHandleId); unit.order(this.game, new COrderTargetWidget(abilityHandleId, orderId, targetHandleId), queue);
ability.checkCanUse(this.game, unit, orderId, this.abilityActivationReceiver.reset());
if (this.abilityActivationReceiver.isUseOk()) {
final CUnit target = this.game.getUnit(targetHandleId);
final StringMsgTargetCheckReceiver<CWidget> targetReceiver = this.<CWidget>targetCheckReceiver();
ability.checkCanTarget(this.game, unit, orderId, target, targetReceiver);
if (targetReceiver.getTarget() != null) {
ability.onOrder(this.game, unit, orderId, target, queue);
return true;
}
else {
this.errorListener.showCommandError(targetReceiver.getMessage());
return false;
}
}
else {
this.errorListener.showCommandError(this.abilityActivationReceiver.getMessage());
return false;
}
} }
@Override @Override
public boolean issuePointOrder(final int unitHandleId, final int abilityHandleId, final int orderId, final float x, public void issuePointOrder(final int unitHandleId, final int abilityHandleId, final int orderId, final float x,
final float y, final boolean queue) { final float y, final boolean queue) {
final CUnit unit = this.game.getUnit(unitHandleId); final CUnit unit = this.game.getUnit(unitHandleId);
final CAbility ability = this.game.getAbility(abilityHandleId); unit.order(this.game, new COrderTargetPoint(abilityHandleId, orderId, new Vector2(x, y)), queue);
ability.checkCanUse(this.game, unit, orderId, this.abilityActivationReceiver.reset());
if (this.abilityActivationReceiver.isUseOk()) {
final Vector2 target = new Vector2(x, y);
final StringMsgTargetCheckReceiver<Vector2> targetReceiver = this.<Vector2>targetCheckReceiver();
ability.checkCanTarget(this.game, unit, orderId, target, targetReceiver);
if (targetReceiver.getTarget() != null) {
ability.onOrder(this.game, unit, orderId, target, queue);
return true;
}
else {
this.errorListener.showCommandError(targetReceiver.getMessage());
return false;
}
}
else {
this.errorListener.showCommandError(this.abilityActivationReceiver.getMessage());
return false;
}
} }
@Override @Override
public boolean issueImmediateOrder(final int unitHandleId, final int abilityHandleId, final int orderId, public void issueImmediateOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final boolean queue) { final boolean queue) {
final CUnit unit = this.game.getUnit(unitHandleId); final CUnit unit = this.game.getUnit(unitHandleId);
final CAbility ability = this.game.getAbility(abilityHandleId); unit.order(this.game, new COrderNoTarget(abilityHandleId, orderId), queue);
ability.checkCanUse(this.game, unit, orderId, this.abilityActivationReceiver.reset());
if (this.abilityActivationReceiver.isUseOk()) {
final StringMsgTargetCheckReceiver<Void> targetReceiver = this.<Void>targetCheckReceiver();
ability.checkCanTargetNoTarget(this.game, unit, orderId, targetReceiver);
if (targetReceiver.getMessage() == null) {
ability.onOrderNoTarget(this.game, unit, orderId, queue);
return true;
}
else {
this.errorListener.showCommandError(targetReceiver.getMessage());
return false;
}
}
else {
this.errorListener.showCommandError(this.abilityActivationReceiver.getMessage());
return false;
}
} }
} }

View File

@ -1,12 +1,12 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.players; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.players;
public interface CPlayerUnitOrderListener { public interface CPlayerUnitOrderListener {
boolean issueTargetOrder(int unitHandleId, int abilityHandleId, int orderId, int targetHandleId, boolean queue); void issueTargetOrder(int unitHandleId, int abilityHandleId, int orderId, int targetHandleId, boolean queue);
boolean issuePointOrder(int unitHandleId, int abilityHandleId, int orderId, float x, float y, boolean queue); void issuePointOrder(int unitHandleId, int abilityHandleId, int orderId, float x, float y, boolean queue);
// Below: used for "DROP ITEM AT POINT" ???? // Below: used for "DROP ITEM AT POINT" ????
// boolean issueTargetAndPointOrder(int unitHandleId, int orderId, int targetHandleId, float x, float y); // boolean issueTargetAndPointOrder(int unitHandleId, int orderId, int targetHandleId, float x, float y);
boolean issueImmediateOrder(int unitHandleId, int abilityHandleId, int orderId, boolean queue); void issueImmediateOrder(int unitHandleId, int abilityHandleId, int orderId, boolean queue);
} }

View File

@ -23,6 +23,7 @@ public class CommandCardIcon extends AbstractRenderableFrame {
private CommandButton commandButton; private CommandButton commandButton;
private int abilityHandleId; private int abilityHandleId;
private int orderId; private int orderId;
private int autoCastOrderId;
private final CommandCardCommandListener commandCardCommandListener; private final CommandCardCommandListener commandCardCommandListener;
public CommandCardIcon(final String name, final UIFrame parent, public CommandCardIcon(final String name, final UIFrame parent,
@ -45,7 +46,7 @@ public class CommandCardIcon extends AbstractRenderableFrame {
this.iconFrame.setVisible(false); this.iconFrame.setVisible(false);
this.activeHighlightFrame.setVisible(false); this.activeHighlightFrame.setVisible(false);
this.cooldownFrame.setVisible(false); this.cooldownFrame.setVisible(false);
this.autocastFrame.setVisible(false); this.autocastFrame.setSequence(PrimaryTag.DEATH);
} }
else { else {
if (commandButton.isEnabled()) { if (commandButton.isEnabled()) {
@ -64,19 +65,24 @@ public class CommandCardIcon extends AbstractRenderableFrame {
this.cooldownFrame this.cooldownFrame
.setFrameByRatio(1 - (commandButton.getCooldownRemaining() / commandButton.getCooldown())); .setFrameByRatio(1 - (commandButton.getCooldownRemaining() / commandButton.getCooldown()));
} }
this.autocastFrame.setVisible(commandButton.isAutoCastActive());
} }
} }
public void setCommandButtonData(final Texture texture, final int abilityHandleId, final int orderId, public void setCommandButtonData(final Texture texture, final int abilityHandleId, final int orderId,
final boolean active) { final int autoCastOrderId, final boolean active, final boolean autoCastActive) {
this.iconFrame.setVisible(true); this.iconFrame.setVisible(true);
this.activeHighlightFrame.setVisible(active); this.activeHighlightFrame.setVisible(active);
this.cooldownFrame.setVisible(false); this.cooldownFrame.setVisible(false);
this.autocastFrame.setVisible(false); if (autoCastActive) {
this.autocastFrame.setSequence(PrimaryTag.STAND);
}
else {
this.autocastFrame.setSequence(-1);
}
this.iconFrame.setTexture(texture); this.iconFrame.setTexture(texture);
this.abilityHandleId = abilityHandleId; this.abilityHandleId = abilityHandleId;
this.orderId = orderId; this.orderId = orderId;
this.autoCastOrderId = autoCastOrderId;
} }
@Override @Override
@ -102,7 +108,7 @@ public class CommandCardIcon extends AbstractRenderableFrame {
this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.orderId); this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.orderId);
} }
else if (button == Input.Buttons.RIGHT) { else if (button == Input.Buttons.RIGHT) {
this.commandCardCommandListener.toggleAutoCastAbility(this.abilityHandleId); this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.autoCastOrderId);
} }
return this; return this;
} }

View File

@ -13,11 +13,10 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.viewport.Viewport; import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.datasources.DataSource;
import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition;
@ -77,7 +76,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
private static final Vector3 clickLocationTemp = new Vector3(); private static final Vector3 clickLocationTemp = new Vector3();
private static final Vector2 clickLocationTemp2 = new Vector2(); private static final Vector2 clickLocationTemp2 = new Vector2();
private final DataSource dataSource; private final DataSource dataSource;
private final Viewport uiViewport; private final ExtendViewport uiViewport;
private final FreeTypeFontGenerator fontGenerator; private final FreeTypeFontGenerator fontGenerator;
private final Scene uiScene; private final Scene uiScene;
private final Scene portraitScene; private final Scene portraitScene;
@ -137,10 +136,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
private int selectedSoundCount = 0; private int selectedSoundCount = 0;
private final ActiveCommandUnitTargetFilter activeCommandUnitTargetFilter; private final ActiveCommandUnitTargetFilter activeCommandUnitTargetFilter;
public MeleeUI(final DataSource dataSource, final Viewport uiViewport, final FreeTypeFontGenerator fontGenerator, // TODO these corrections are used for old hardcoded UI stuff, we should
final Scene uiScene, final Scene portraitScene, final CameraPreset[] cameraPresets, // probably remove them later
final CameraRates cameraRates, final War3MapViewer war3MapViewer, final RootFrameListener rootFrameListener, private final float widthRatioCorrection;
final CPlayerUnitOrderListener unitOrderListener) { private final float heightRatioCorrection;
public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport,
final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final Scene portraitScene,
final CameraPreset[] cameraPresets, final CameraRates cameraRates, final War3MapViewer war3MapViewer,
final RootFrameListener rootFrameListener, final CPlayerUnitOrderListener unitOrderListener) {
this.dataSource = dataSource; this.dataSource = dataSource;
this.uiViewport = uiViewport; this.uiViewport = uiViewport;
this.fontGenerator = fontGenerator; this.fontGenerator = fontGenerator;
@ -161,11 +165,15 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.activeButtonTexture = ImageUtils.getBLPTexture(war3MapViewer.mapMpq, this.activeButtonTexture = ImageUtils.getBLPTexture(war3MapViewer.mapMpq,
"UI\\Widgets\\Console\\Human\\CommandButton\\human-activebutton.blp"); "UI\\Widgets\\Console\\Human\\CommandButton\\human-activebutton.blp");
this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter(); this.activeCommandUnitTargetFilter = new ActiveCommandUnitTargetFilter();
this.widthRatioCorrection = this.uiViewport.getMinWorldWidth() / 1600f;
this.heightRatioCorrection = this.uiViewport.getMinWorldHeight() / 1200f;
} }
private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) { private MeleeUIMinimap createMinimap(final War3MapViewer war3MapViewer) {
final Rectangle minimapDisplayArea = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f); final Rectangle minimapDisplayArea = new Rectangle(18.75f * this.widthRatioCorrection,
13.75f * this.heightRatioCorrection, 278.75f * this.widthRatioCorrection,
276.25f * this.heightRatioCorrection);
Texture minimapTexture = null; Texture minimapTexture = null;
if (war3MapViewer.dataSource.has("war3mapMap.tga")) { if (war3MapViewer.dataSource.has("war3mapMap.tga")) {
try { try {
@ -396,11 +404,6 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.errorMessageFrame.setText(message); this.errorMessageFrame.setText(message);
} }
@Override
public void toggleAutoCastAbility(final int abilityHandleId) {
}
public void update(final float deltaTime) { public void update(final float deltaTime) {
this.portrait.update(); this.portrait.update();
@ -468,8 +471,6 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.cameraManager.updateCamera(); this.cameraManager.updateCamera();
} }
private final ShapeRenderer shapeRenderer = new ShapeRenderer();
public void render(final SpriteBatch batch, final BitmapFont font20, final GlyphLayout glyphLayout) { public void render(final SpriteBatch batch, final BitmapFont font20, final GlyphLayout glyphLayout) {
this.rootFrame.render(batch, font20, glyphLayout); this.rootFrame.render(batch, font20, glyphLayout);
if (this.selectedUnit != null) { if (this.selectedUnit != null) {
@ -658,10 +659,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
@Override @Override
public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon, public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon,
final int abilityHandleId, final int orderId, final boolean active) { final int abilityHandleId, final int orderId, final int autoCastId, final boolean active,
final boolean autoCastActive) {
final int x = Math.max(0, Math.min(COMMAND_CARD_WIDTH - 1, buttonPositionX)); final int x = Math.max(0, Math.min(COMMAND_CARD_WIDTH - 1, buttonPositionX));
final int y = Math.max(0, Math.min(COMMAND_CARD_HEIGHT - 1, buttonPositionY)); final int y = Math.max(0, Math.min(COMMAND_CARD_HEIGHT - 1, buttonPositionY));
this.commandCard[y][x].setCommandButtonData(icon, abilityHandleId, orderId, active); this.commandCard[y][x].setCommandButtonData(icon, abilityHandleId, orderId, autoCastId, active, autoCastActive);
} }
public void resize(final Rectangle viewport) { public void resize(final Rectangle viewport) {
@ -670,10 +672,10 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
} }
public void positionPortrait() { public void positionPortrait() {
this.projectionTemp1.x = 422; this.projectionTemp1.x = 422 * this.widthRatioCorrection;
this.projectionTemp1.y = 57; this.projectionTemp1.y = 57 * this.heightRatioCorrection;
this.projectionTemp2.x = 422 + 167; this.projectionTemp2.x = (422 + 167) * this.widthRatioCorrection;
this.projectionTemp2.y = 57 + 170; this.projectionTemp2.y = (57 + 170) * this.heightRatioCorrection;
this.uiViewport.project(this.projectionTemp1); this.uiViewport.project(this.projectionTemp1);
this.uiViewport.project(this.projectionTemp2); this.uiViewport.project(this.projectionTemp2);
@ -752,7 +754,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
} }
@Override @Override
public void ordersChanged() { public void ordersChanged(final int abilityHandleId, final int orderId) {
clearCommandCard(); clearCommandCard();
this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this, this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this,
this.war3MapViewer.getAbilityDataUI()); this.war3MapViewer.getAbilityDataUI());
@ -798,20 +800,19 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.activeCommandUnitTargetFilter); this.activeCommandUnitTargetFilter);
final boolean shiftDown = isShiftDown(); final boolean shiftDown = isShiftDown();
if (rayPickUnit != null) { if (rayPickUnit != null) {
if (this.unitOrderListener.issueTargetOrder( this.unitOrderListener.issueTargetOrder(
this.activeCommandUnit.getSimulationUnit().getHandleId(), this.activeCommandUnit.getSimulationUnit().getHandleId(),
this.activeCommand.getHandleId(), this.activeCommandOrderId, this.activeCommand.getHandleId(), this.activeCommandOrderId,
rayPickUnit.getSimulationUnit().getHandleId(), shiftDown)) { rayPickUnit.getSimulationUnit().getHandleId(), shiftDown);
if (getSelectedUnit().soundset.yesAttack if (getSelectedUnit().soundset.yesAttack
.playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { .playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
portraitTalk(); portraitTalk();
} }
this.selectedSoundCount = 0; this.selectedSoundCount = 0;
if (!shiftDown) { if (!shiftDown) {
this.activeCommandUnit = null; this.activeCommandUnit = null;
this.activeCommand = null; this.activeCommand = null;
this.activeCommandOrderId = -1; this.activeCommandOrderId = -1;
}
} }
} }
else { else {
@ -829,21 +830,21 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
else { else {
this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0); this.war3MapViewer.showConfirmation(clickLocationTemp, 0, 1, 0);
} }
if (this.unitOrderListener.issuePointOrder( this.unitOrderListener.issuePointOrder(
this.activeCommandUnit.getSimulationUnit().getHandleId(), this.activeCommandUnit.getSimulationUnit().getHandleId(),
this.activeCommand.getHandleId(), this.activeCommandOrderId, clickLocationTemp2.x, this.activeCommand.getHandleId(), this.activeCommandOrderId, clickLocationTemp2.x,
clickLocationTemp2.y, shiftDown)) { clickLocationTemp2.y, shiftDown);
if (getSelectedUnit().soundset.yes.playUnitResponse( if (getSelectedUnit().soundset.yes
this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) { .playUnitResponse(this.war3MapViewer.worldScene.audioContext, getSelectedUnit())) {
portraitTalk(); portraitTalk();
}
this.selectedSoundCount = 0;
if (!shiftDown) {
this.activeCommandUnit = null;
this.activeCommand = null;
this.activeCommandOrderId = -1;
}
} }
this.selectedSoundCount = 0;
if (!shiftDown) {
this.activeCommandUnit = null;
this.activeCommand = null;
this.activeCommandOrderId = -1;
}
} }
} }
} }
@ -975,4 +976,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
} }
return false; return false;
} }
public float getHeightRatioCorrection() {
return this.heightRatioCorrection;
}
} }

View File

@ -2,6 +2,4 @@ package com.etheller.warsmash.viewer5.handlers.w3x.ui.command;
public interface CommandCardCommandListener { public interface CommandCardCommandListener {
void startUsingAbility(int abilityHandleId, int orderId); void startUsingAbility(int abilityHandleId, int orderId);
void toggleAutoCastAbility(int abilityHandleId);
} }