Update to have lumber harvest, still bugged

This commit is contained in:
Retera 2021-02-01 08:38:04 -05:00
parent 600ec9536b
commit 581f51ef65
37 changed files with 661 additions and 199 deletions

View File

@ -20,7 +20,7 @@ Path07="."
[Map]
//FilePath="CombatUnitTests.w3x"
//FilePath="PitchRoll.w3x"
FilePath="PeonStartingBase.w3x"
FilePath="PeonStartingBase_Simple.w3x"
//FilePath="MyStromguarde.w3m"
//FilePath="ColdArrows.w3m"
//FilePath="DungeonGoldMine.w3m"

View File

@ -1,5 +1,6 @@
package com.etheller.warsmash.viewer5;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion;
import com.badlogic.gdx.math.Rectangle;
@ -310,10 +311,11 @@ public class Camera {
final Rectangle viewport = this.rect;
vectorHeap.set(v);
vectorHeap.prj(this.inverseViewMatrix);
vectorHeap.prj(this.viewProjectionMatrix);
out.x = Math.round(((vectorHeap.x + 1) / 2) * viewport.width);
out.y = Math.round(((vectorHeap.y + 1) / 2) * viewport.height);
out.y = ((Gdx.graphics.getHeight() - viewport.y - viewport.height) + (viewport.height))
- Math.round(((vectorHeap.y + 1) / 2) * viewport.height);
return out;
}

View File

@ -17,6 +17,7 @@ public class SequenceUtils {
public static final EnumSet<SecondaryTag> FLESH = EnumSet.of(SecondaryTag.FLESH);
public static final EnumSet<SecondaryTag> TALK = EnumSet.of(SecondaryTag.TALK);
public static final EnumSet<SecondaryTag> BONE = EnumSet.of(SecondaryTag.BONE);
public static final EnumSet<SecondaryTag> HIT = EnumSet.of(SecondaryTag.HIT);
private static final StandSequenceComparator STAND_SEQUENCE_COMPARATOR = new StandSequenceComparator();
private static final SecondaryTagSequenceComparator SECONDARY_TAG_SEQUENCE_COMPARATOR = new SecondaryTagSequenceComparator(

View File

@ -0,0 +1,36 @@
package com.etheller.warsmash.viewer5.handlers.w3x;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector3;
public class TextTag {
private final Vector3 position;
private final String text;
private final Color color;
private int lifetime = 0;
public TextTag(final Vector3 position, final String text, final Color color) {
this.position = position;
this.text = text;
this.color = color;
position.z += 64f;
}
public boolean update() {
this.position.z += 1.0f;
this.lifetime++;
return this.lifetime > 196;
}
public Vector3 getPosition() {
return this.position;
}
public Color getColor() {
return this.color;
}
public String getText() {
return this.text;
}
}

View File

@ -23,6 +23,7 @@ import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
@ -94,8 +95,10 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidgetFilterFunction;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackListener;
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.util.ResourceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.sound.KeyedSounds;
@ -104,6 +107,8 @@ import mpq.MPQArchive;
import mpq.MPQException;
public class War3MapViewer extends AbstractMdxModelViewer {
private static final Color PLACEHOLDER_LUMBER_COLOR = new Color(0.0f, 200f / 255f, 80f / 255f, 1.0f);
private static final Color PLACEHOLDER_GOLD_COLOR = new Color(1.0f, 220f / 255f, 0f, 1.0f);
private static final War3ID UNIT_FILE = War3ID.fromString("umdl");
private static final War3ID UNIT_SPECIAL = War3ID.fromString("uspa");
private static final War3ID UBER_SPLAT = War3ID.fromString("uubs");
@ -203,6 +208,8 @@ public class War3MapViewer extends AbstractMdxModelViewer {
private int localPlayerIndex;
private final CommandErrorListener commandErrorListener;
public final List<TextTag> textTags = new ArrayList<>();
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas,
final CommandErrorListener errorListener) {
super(dataSource, canvas);
@ -449,7 +456,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
public CAttackProjectile createAttackProjectile(final CSimulation simulation, final float launchX,
final float launchY, final float launchFacing, final CUnit source,
final CUnitAttackMissile unitAttack, final AbilityTarget target, final float damage,
final int bounceIndex) {
final int bounceIndex, final CUnitAttackListener attackListener) {
final War3ID typeId = source.getTypeId();
final int projectileSpeed = unitAttack.getProjectileSpeed();
final float projectileArc = unitAttack.getProjectileArc();
@ -468,7 +475,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
final float height = War3MapViewer.this.terrain.getGroundHeight(x, y) + source.getFlyHeight()
+ projectileLaunchZ;
final CAttackProjectile simulationAttackProjectile = new CAttackProjectile(x, y,
projectileSpeed, target, source, damage, unitAttack, bounceIndex);
projectileSpeed, target, source, damage, unitAttack, bounceIndex, attackListener);
final MdxModel model = (MdxModel) load(missileArt, War3MapViewer.this.mapPathSolver,
War3MapViewer.this.solverParams);
@ -563,7 +570,6 @@ public class War3MapViewer extends AbstractMdxModelViewer {
@Override
public void removeDestructable(final CDestructable dest) {
final RenderDestructable renderPeer = War3MapViewer.this.destructableToRenderPeer.remove(dest);
War3MapViewer.this.doodads.remove(renderPeer);
War3MapViewer.this.worldScene.removeInstance(renderPeer.instance);
if (renderPeer.walkableBounds != null) {
War3MapViewer.this.walkableObjectsTree.remove((MdxComplexInstance) renderPeer.instance,
@ -634,6 +640,24 @@ public class War3MapViewer extends AbstractMdxModelViewer {
final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(cUnit);
renderPeer.repositioned(War3MapViewer.this);
}
@Override
public void spawnGainResourceTextTag(final CUnit gainingUnit, final ResourceType resourceType,
final int amount) {
final RenderUnit renderPeer = War3MapViewer.this.unitToRenderPeer.get(gainingUnit);
switch (resourceType) {
case FOOD:
throw new IllegalArgumentException();
case GOLD:
War3MapViewer.this.textTags.add(new TextTag(new Vector3(renderPeer.location), "+" + amount,
PLACEHOLDER_GOLD_COLOR));
break;
case LUMBER:
War3MapViewer.this.textTags.add(new TextTag(new Vector3(renderPeer.location), "+" + amount,
PLACEHOLDER_LUMBER_COLOR));
break;
}
}
}, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers(),
this.commandErrorListener);
@ -784,6 +808,8 @@ public class War3MapViewer extends AbstractMdxModelViewer {
final float y = doodad.getLocation()[1];
final CDestructable simulationDestructable = this.simulation.createDestructable(row.getAlias(), x,
y, destructablePathing, destructablePathingDeath);
simulationDestructable.setLife(this.simulation,
simulationDestructable.getLife() * (doodad.getLife() / 100f));
final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type,
maxPitch, maxRoll, doodad.getLife(), destructableShadow, simulationDestructable);
if (row.readSLKTagBoolean("walkable")) {
@ -796,7 +822,6 @@ public class War3MapViewer extends AbstractMdxModelViewer {
renderDestructableBounds);
renderDestructable.walkableBounds = renderDestructableBounds;
}
this.doodads.add(renderDestructable);
this.widgets.add(renderDestructable);
}
else {
@ -1199,6 +1224,12 @@ public class War3MapViewer extends AbstractMdxModelViewer {
super.update();
final Iterator<TextTag> textTagIterator = this.textTags.iterator();
while (textTagIterator.hasNext()) {
if (textTagIterator.next().update()) {
textTagIterator.remove();
}
}
for (final RenderWidget unit : this.widgets) {
unit.updateAnimations(this);
}
@ -1683,4 +1714,8 @@ public class War3MapViewer extends AbstractMdxModelViewer {
return false;
}
}
public void add(final TextTag textTag) {
this.textTags.add(textTag);
}
}

View File

@ -1,5 +1,6 @@
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
@ -7,6 +8,7 @@ import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.BuildingShadow;
@ -18,17 +20,21 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget {
private static final War3ID TEX_ID = War3ID.fromString("btxi");
private static final War3ID SEL_CIRCLE_SIZE = War3ID.fromString("bgsc");
private final float life;
private float life;
public Rectangle walkableBounds;
private final CDestructable simulationDestructable;
private SplatMover selectionCircle;
private final UnitAnimationListenerImpl unitAnimationListenerImpl;
private boolean dead;
private BuildingShadow destructableShadow;
public RenderDestructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type,
final float maxPitch, final float maxRoll, final float life, final BuildingShadow destructableShadow,
final CDestructable simulationDestructable) {
super(map, model, row, doodad, type, maxPitch, maxRoll);
this.life = life;
this.life = simulationDestructable.getLife();
this.destructableShadow = destructableShadow;
this.simulationDestructable = simulationDestructable;
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
@ -41,6 +47,9 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget {
this.instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
}
this.selectionScale *= row.getFieldAsFloat(SEL_CIRCLE_SIZE, 0);
this.unitAnimationListenerImpl = new UnitAnimationListenerImpl((MdxComplexInstance) this.instance);
simulationDestructable.setUnitAnimationListener(this.unitAnimationListenerImpl);
this.unitAnimationListenerImpl.playAnimation(true, getAnimation(), SequenceUtils.EMPTY, 1.0f, true);
}
@Override
@ -65,6 +74,37 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget {
public void updateAnimations(final War3MapViewer war3MapViewer) {
// TODO maybe move getAnimation behaviors to here and make this thing not a
// doodad
final boolean dead = this.simulationDestructable.isDead();
if (dead && !this.dead) {
this.unitAnimationListenerImpl.playAnimation(true, PrimaryTag.DEATH, SequenceUtils.EMPTY, 1.0f, true);
if (this.destructableShadow != null) {
this.destructableShadow.remove();
this.destructableShadow = null;
}
if (this.selectionCircle != null) {
this.selectionCircle.destroy(Gdx.gl30, war3MapViewer.terrain.centerOffset);
this.selectionCircle = null;
}
}
else if (!dead) {
if (this.dead) {
this.unitAnimationListenerImpl.playAnimation(true, PrimaryTag.BIRTH, SequenceUtils.EMPTY, 1.0f, true);
// TODO add back shadow here
}
else {
if (Math.abs(this.life - this.simulationDestructable.getLife()) > 0.003f) {
if (this.life > this.simulationDestructable.getLife()) {
this.unitAnimationListenerImpl.playAnimation(true, PrimaryTag.STAND, SequenceUtils.HIT, 1.0f,
true);
}
this.life = this.simulationDestructable.getLife();
}
}
}
this.dead = dead;
this.unitAnimationListenerImpl.update();
}
@Override

View File

@ -1,8 +1,6 @@
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.Queue;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Quaternion;
@ -11,7 +9,6 @@ import com.etheller.warsmash.util.RenderMathUtils;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
@ -27,7 +24,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.Comma
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandCardPopulatingAbilityVisitor;
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.CUnitAnimationListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
@ -436,119 +432,6 @@ public class RenderUnit implements RenderWidget {
return this.unitAnimationListenerImpl.secondaryAnimationTags;
}
private static final class UnitAnimationListenerImpl implements CUnitAnimationListener {
private final MdxComplexInstance instance;
private final EnumSet<AnimationTokens.SecondaryTag> secondaryAnimationTags = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private final EnumSet<AnimationTokens.SecondaryTag> recycleSet = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private PrimaryTag currentAnimation;
private EnumSet<SecondaryTag> currentAnimationSecondaryTags;
private float currentSpeedRatio;
private boolean currentlyAllowingRarityVariations;
private final Queue<QueuedAnimation> animationQueue = new LinkedList<>();
public UnitAnimationListenerImpl(final MdxComplexInstance instance) {
this.instance = instance;
}
@Override
public void addSecondaryTag(final AnimationTokens.SecondaryTag tag) {
this.secondaryAnimationTags.add(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
@Override
public void removeSecondaryTag(final AnimationTokens.SecondaryTag tag) {
this.secondaryAnimationTags.remove(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
@Override
public void playAnimation(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float speedRatio,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)
|| !secondaryAnimationTags.equals(this.currentAnimationSecondaryTags)) {
this.currentSpeedRatio = speedRatio;
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
this.instance.setAnimationSpeed(speedRatio);
if (SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations) != null) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations;
}
}
}
public void playAnimationWithDuration(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float duration,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)
|| !secondaryAnimationTags.equals(this.currentAnimationSecondaryTags)) {
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
final Sequence sequence = SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations);
if (sequence != null) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations;
this.currentSpeedRatio = ((sequence.getInterval()[1] - sequence.getInterval()[0]) / 1000.0f)
/ duration;
this.instance.setAnimationSpeed(this.currentSpeedRatio);
}
}
}
@Override
public void queueAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationQueue.add(new QueuedAnimation(animationName, secondaryAnimationTags, allowRarityVariations));
}
public void update() {
if (this.instance.sequenceEnded || (this.instance.sequence == -1)) {
// animation done
if ((this.instance.sequence != -1) && (((MdxModel) this.instance.model).getSequences()
.get(this.instance.sequence).getFlags() == 0)) {
// animation is a looping animation
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
}
else {
final QueuedAnimation nextAnimation = this.animationQueue.poll();
if (nextAnimation != null) {
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,
nextAnimation.allowRarityVariations);
}
}
}
}
}
private static final class QueuedAnimation {
private final PrimaryTag animationName;
private final EnumSet<SecondaryTag> secondaryAnimationTags;
private final boolean allowRarityVariations;
public QueuedAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationName = animationName;
this.secondaryAnimationTags = secondaryAnimationTags;
this.allowRarityVariations = allowRarityVariations;
}
}
public void repositioned(final War3MapViewer map) {
final float prevX = this.location[0];
final float prevY = this.location[1];

View File

@ -1,8 +1,19 @@
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.Queue;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitAnimationListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
public interface RenderWidget {
@ -23,4 +34,127 @@ public interface RenderWidget {
void unassignSelectionCircle();
void assignSelectionCircle(SplatMover t);
public static final class UnitAnimationListenerImpl implements CUnitAnimationListener {
private final MdxComplexInstance instance;
protected final EnumSet<AnimationTokens.SecondaryTag> secondaryAnimationTags = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private final EnumSet<AnimationTokens.SecondaryTag> recycleSet = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private PrimaryTag currentAnimation;
private EnumSet<SecondaryTag> currentAnimationSecondaryTags;
private float currentSpeedRatio;
private boolean currentlyAllowingRarityVariations;
private final Queue<QueuedAnimation> animationQueue = new LinkedList<>();
public UnitAnimationListenerImpl(final MdxComplexInstance instance) {
this.instance = instance;
}
@Override
public void addSecondaryTag(final AnimationTokens.SecondaryTag tag) {
if (!secondaryAnimationTags.contains(tag)) {
this.secondaryAnimationTags.add(tag);
if (!animationQueue.isEmpty()) {
final QueuedAnimation nextAnimation = animationQueue.poll();
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,
nextAnimation.allowRarityVariations);
}
else {
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
}
}
}
@Override
public void removeSecondaryTag(final AnimationTokens.SecondaryTag tag) {
if (secondaryAnimationTags.contains(tag)) {
this.secondaryAnimationTags.remove(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
}
@Override
public void playAnimation(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float speedRatio,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)
|| !secondaryAnimationTags.equals(this.currentAnimationSecondaryTags)) {
this.currentSpeedRatio = speedRatio;
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
this.instance.setAnimationSpeed(speedRatio);
if (SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations) != null) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations;
}
}
}
public void playAnimationWithDuration(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float duration,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)
|| !secondaryAnimationTags.equals(this.currentAnimationSecondaryTags)) {
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
final Sequence sequence = SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations);
if (sequence != null) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations;
this.currentSpeedRatio = ((sequence.getInterval()[1] - sequence.getInterval()[0]) / 1000.0f)
/ duration;
this.instance.setAnimationSpeed(this.currentSpeedRatio);
}
}
}
@Override
public void queueAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationQueue.add(new QueuedAnimation(animationName, secondaryAnimationTags, allowRarityVariations));
}
public void update() {
if (this.instance.sequenceEnded || (this.instance.sequence == -1)) {
// animation done
if ((this.instance.sequence != -1) && (((MdxModel) this.instance.model).getSequences()
.get(this.instance.sequence).getFlags() == 0)) {
// animation is a looping animation
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
}
else {
final QueuedAnimation nextAnimation = this.animationQueue.poll();
if (nextAnimation != null) {
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,
nextAnimation.allowRarityVariations);
}
}
}
}
}
public static final class QueuedAnimation {
private final PrimaryTag animationName;
private final EnumSet<SecondaryTag> secondaryAnimationTags;
private final boolean allowRarityVariations;
public QueuedAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationName = animationName;
this.secondaryAnimationTags = secondaryAnimationTags;
this.allowRarityVariations = allowRarityVariations;
}
}
}

View File

@ -3,6 +3,7 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
import java.util.EnumSet;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.RemovablePathingMapInstance;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget.UnitAnimationListenerImpl;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
@ -12,6 +13,7 @@ public class CDestructable extends CWidget {
private final CDestructableType destType;
private final RemovablePathingMapInstance pathingInstance;
private final RemovablePathingMapInstance pathingInstanceDeath;
private UnitAnimationListenerImpl unitAnimationListenerImpl;
public CDestructable(final int handleId, final float x, final float y, final float life,
final CDestructableType destTypeInstance, final RemovablePathingMapInstance pathingInstance,
@ -65,4 +67,8 @@ public class CDestructable extends CWidget {
public CDestructableType getDestType() {
return this.destType;
}
public void setUnitAnimationListener(final UnitAnimationListenerImpl unitAnimationListenerImpl) {
this.unitAnimationListenerImpl = unitAnimationListenerImpl;
}
}

View File

@ -21,6 +21,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackListener;
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.data.CAbilityData;
@ -31,6 +32,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceTy
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.CRace;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
@ -125,6 +127,10 @@ public class CSimulation {
return this.units;
}
public List<CDestructable> getDestructables() {
return this.destructables;
}
public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y,
final float facing, final BufferedImage buildingPathingPixelMap,
final RemovablePathingMapInstance pathingInstance) {
@ -164,9 +170,9 @@ public class CSimulation {
public CAttackProjectile createProjectile(final CUnit source, final float launchX, final float launchY,
final float launchFacing, final CUnitAttackMissile attack, final AbilityTarget target, final float damage,
final int bounceIndex) {
final int bounceIndex, final CUnitAttackListener attackListener) {
final CAttackProjectile projectile = this.simulationRenderController.createAttackProjectile(this, launchX,
launchY, launchFacing, source, attack, target, damage, bounceIndex);
launchY, launchFacing, source, attack, target, damage, bounceIndex, attackListener);
this.newProjectiles.add(projectile);
return projectile;
}
@ -292,6 +298,10 @@ public class CSimulation {
this.simulationRenderController.unitRepositioned(cUnit);
}
public void unitGainResourceEvent(final CUnit unit, final ResourceType resourceType, final int amount) {
this.simulationRenderController.spawnGainResourceTextTag(unit, resourceType, amount);
}
public void unitsLoaded() {
// called on startup after the system loads the map's units layer, but not any
// custom scripts yet

View File

@ -25,6 +25,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttackListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorFollow;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorHoldPosition;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorMove;
@ -591,12 +592,6 @@ public class CUnit extends CWidget {
return groundDistance;
}
public double distanceSquaredNoCollision(final AbilityTarget target) {
final double dx = Math.abs(target.getX() - getX());
final double dy = Math.abs(target.getY() - getY());
return (dx * dx) + (dy * dy);
}
public double distance(final float x, final float y) {
double dx = Math.abs(x - getX());
double dy = Math.abs(y - getY());
@ -645,7 +640,8 @@ public class CUnit extends CWidget {
CAllianceType.PASSIVE)) {
for (final CUnitAttack attack : this.unitType.getAttacks()) {
if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) {
this.currentBehavior = getAttackBehavior().reset(OrderIds.attack, attack, source, false);
this.currentBehavior = getAttackBehavior().reset(OrderIds.attack, attack, source, false,
CBehaviorAttackListener.DO_NOTHING);
this.currentBehavior.begin(simulation);
break;
}
@ -695,6 +691,17 @@ public class CUnit extends CWidget {
}
}
}
else if (target instanceof CDestructable) {
final CDestructable targetDest = (CDestructable) target;
final CDestructableType targetDestType = targetDest.getDestType();
final BufferedImage pathingPixelMap = targetDest.isDead() ? targetDestType.getPathingDeathPixelMap()
: targetDestType.getPathingPixelMap();
final float targetX = target.getX();
final float targetY = target.getY();
if ((pathingPixelMap != null) && canReachToPathing(range, 270, pathingPixelMap, targetX, targetY)) {
return true;
}
}
return distance <= range;
}
@ -853,7 +860,7 @@ public class CUnit extends CWidget {
this.source.currentBehavior.end(this.game);
}
this.source.currentBehavior = this.source.getAttackBehavior().reset(OrderIds.attack, attack,
unit, this.disableMove);
unit, this.disableMove, CBehaviorAttackListener.DO_NOTHING);
this.source.currentBehavior.begin(this.game);
this.foundAnyTarget = true;
return true;

View File

@ -23,10 +23,12 @@ public abstract class CWidget implements AbilityTarget {
return this.handleId;
}
@Override
public float getX() {
return this.x;
}
@Override
public float getY() {
return this.y;
}
@ -60,4 +62,10 @@ public abstract class CWidget implements AbilityTarget {
public abstract boolean canBeTargetedBy(CSimulation simulation, CUnit source,
final EnumSet<CTargetType> targetsAllowed);
public double distanceSquaredNoCollision(final AbilityTarget target) {
final double dx = Math.abs(target.getX() - getX());
final double dy = Math.abs(target.getY() - getY());
return (dx * dx) + (dy * dy);
}
}

View File

@ -6,6 +6,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttackListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType;
@ -36,6 +37,10 @@ public class CAbilityAttack extends AbstractCAbility {
return;
}
}
else {
receiver.orderIdNotAccepted();
return;
}
}
if ((orderId == OrderIds.smart) || (orderId == OrderIds.attack)) {
boolean canTarget = false;
@ -115,7 +120,8 @@ public class CAbilityAttack extends AbstractCAbility {
CBehavior behavior = null;
for (final CUnitAttack attack : caster.getUnitType().getAttacks()) {
if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) {
behavior = caster.getAttackBehavior().reset(OrderIds.attack, attack, target, false);
behavior = caster.getAttackBehavior().reset(OrderIds.attack, attack, target, false,
CBehaviorAttackListener.DO_NOTHING);
break;
}
}
@ -138,7 +144,8 @@ public class CAbilityAttack extends AbstractCAbility {
CBehavior behavior = null;
for (final CUnitAttack attack : caster.getUnitType().getAttacks()) {
if (attack.getWeaponType().isAttackGroundSupported()) {
behavior = caster.getAttackBehavior().reset(OrderIds.attackground, attack, point, false);
behavior = caster.getAttackBehavior().reset(OrderIds.attackground, attack, point, false,
CBehaviorAttackListener.DO_NOTHING);
break;
}
}

View File

@ -8,6 +8,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractC
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttackListener;
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;
@ -106,7 +107,8 @@ public class CAbilityColdArrows extends AbstractCAbility {
CBehavior behavior = null;
for (final CUnitAttack attack : caster.getUnitType().getAttacks()) {
if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) {
behavior = caster.getAttackBehavior().reset(OrderIds.coldarrowstarg, attack, target, false);
behavior = caster.getAttackBehavior().reset(OrderIds.coldarrowstarg, attack, target, false,
CBehaviorAttackListener.DO_NOTHING);
break;
}
}

View File

@ -1,5 +1,8 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.harvest;
import java.util.EnumSet;
import java.util.List;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
@ -12,6 +15,11 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.harvest.CBehaviorHarvest;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.harvest.CBehaviorReturnResources;
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.CWeaponType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackNormal;
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;
@ -21,23 +29,46 @@ public class CAbilityHarvest extends AbstractGenericSingleIconActiveAbility {
private final int damageToTree;
private final int goldCapacity;
private final int lumberCapacity;
private final float castRange;
private final float duration;
private CBehaviorHarvest behaviorHarvest;
private CBehaviorReturnResources behaviorReturnResources;
private int carriedResourceAmount;
private ResourceType carriedResourceType;
private CUnitAttack treeAttack;
private CWidget lastHarvestTarget;
public CAbilityHarvest(final int handleId, final War3ID alias, final int damageToTree, final int goldCapacity,
final int lumberCapacity) {
final int lumberCapacity, final float castRange, final float duration) {
super(handleId, alias);
this.damageToTree = damageToTree;
this.goldCapacity = goldCapacity;
this.lumberCapacity = lumberCapacity;
this.castRange = castRange;
this.duration = duration;
}
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
this.behaviorHarvest = new CBehaviorHarvest(unit, this);
this.behaviorReturnResources = new CBehaviorReturnResources(unit, this);
final List<CUnitAttack> unitAttacks = unit.getUnitType().getAttacks();
CUnitAttack bestFitTreeAttack = null;
for (final CUnitAttack attack : unitAttacks) {
if (attack.getTargetsAllowed().contains(CTargetType.TREE)) {
bestFitTreeAttack = attack;
}
}
this.treeAttack = new CUnitAttackNormal(
bestFitTreeAttack == null ? 0.433f : bestFitTreeAttack.getAnimationBackswingPoint(),
bestFitTreeAttack == null ? 0.433f : bestFitTreeAttack.getAnimationDamagePoint(), CAttackType.NORMAL,
this.duration, 0, 1, this.damageToTree * 2, 0, (int) this.castRange,
bestFitTreeAttack == null ? 250 : bestFitTreeAttack.getRangeMotionBuffer(),
bestFitTreeAttack == null ? false : bestFitTreeAttack.isShowUI(),
bestFitTreeAttack == null ? EnumSet.of(CTargetType.TREE) : bestFitTreeAttack.getTargetsAllowed(),
bestFitTreeAttack == null ? "AxeMediumChop" : bestFitTreeAttack.getWeaponSound(),
bestFitTreeAttack == null ? CWeaponType.NORMAL : bestFitTreeAttack.getWeaponType());
}
@Override
@ -104,8 +135,13 @@ public class CAbilityHarvest extends AbstractGenericSingleIconActiveAbility {
receiver.mustTargetResources();
}
else if (target instanceof CDestructable) {
if (target.canBeTargetedBy(game, unit, this.treeAttack.getTargetsAllowed())) {
receiver.targetOk(target);
}
else {
receiver.mustTargetResources();
}
}
else {
receiver.mustTargetResources();
}
@ -173,4 +209,16 @@ public class CAbilityHarvest extends AbstractGenericSingleIconActiveAbility {
return this.behaviorReturnResources;
}
public CUnitAttack getTreeAttack() {
return this.treeAttack;
}
public void setLastHarvestTarget(final CWidget lastHarvestTarget) {
this.lastHarvestTarget = lastHarvestTarget;
}
public CWidget getLastHarvestTarget() {
return this.lastHarvestTarget;
}
}

View File

@ -20,7 +20,7 @@ public class CAbilityGoldMine extends AbstractGenericNoIconAbility {
private final float miningDuration;
private final int miningCapacity;
private final List<CBehaviorHarvest> activeMiners;
private final boolean wasEmpty;
private boolean wasEmpty;
public CAbilityGoldMine(final int handleId, final War3ID alias, final int maxGold, final float miningDuration,
final int miningCapacity) {
@ -52,6 +52,7 @@ public class CAbilityGoldMine extends AbstractGenericNoIconAbility {
else {
unit.getUnitAnimationListener().addSecondaryTag(SecondaryTag.WORK);
}
this.wasEmpty = empty;
}
for (int i = this.activeMiners.size() - 1; i >= 0; i--) {
final CBehaviorHarvest activeMiner = this.activeMiners.get(i);

View File

@ -13,6 +13,8 @@ public abstract class AbstractCAbilityTypeDefinition<TYPE_LEVEL_DATA extends CAb
implements CAbilityTypeDefinition {
protected static final War3ID TARGETS_ALLOWED = War3ID.fromString("atar");
private static final War3ID LEVELS = War3ID.fromString("alev");
protected static final War3ID CAST_RANGE = War3ID.fromString("aran");
protected static final War3ID DURATION = War3ID.fromString("adur");
@Override
public CAbilityType<?> createAbilityType(final War3ID alias, final MutableGameObject abilityEditorData) {

View File

@ -24,7 +24,10 @@ public class CAbilityTypeDefinitionHarvest extends AbstractCAbilityTypeDefinitio
final int damageToTree = abilityEditorData.getFieldAsInteger(DAMAGE_TO_TREE, level);
final int goldCapacity = abilityEditorData.getFieldAsInteger(GOLD_CAPACITY, level);
final int lumberCapacity = abilityEditorData.getFieldAsInteger(LUMBER_CAPACITY, level);
return new CAbilityTypeHarvestLevelData(targetsAllowedAtLevel, damageToTree, goldCapacity, lumberCapacity);
final float castRange = abilityEditorData.getFieldAsFloat(CAST_RANGE, level);
final float duration = abilityEditorData.getFieldAsFloat(DURATION, level);
return new CAbilityTypeHarvestLevelData(targetsAllowedAtLevel, damageToTree, goldCapacity, lumberCapacity,
castRange, duration);
}
@Override

View File

@ -18,7 +18,7 @@ public class CAbilityTypeHarvest extends CAbilityType<CAbilityTypeHarvestLevelDa
public CAbility createAbility(final int handleId) {
final CAbilityTypeHarvestLevelData levelData = getLevelData(0);
return new CAbilityHarvest(handleId, getAlias(), levelData.getDamageToTree(), levelData.getGoldCapacity(),
levelData.getLumberCapacity());
levelData.getLumberCapacity(), levelData.getCastRange(), levelData.getDuration());
}
}

View File

@ -9,13 +9,17 @@ public class CAbilityTypeHarvestLevelData extends CAbilityTypeLevelData {
private final int damageToTree;
private final int goldCapacity;
private final int lumberCapacity;
private final float castRange;
private final float duration;
public CAbilityTypeHarvestLevelData(final EnumSet<CTargetType> targetsAllowed, final int damageToTree,
final int goldCapacity, final int lumberCapacity) {
final int goldCapacity, final int lumberCapacity, final float castRange, final float duration) {
super(targetsAllowed);
this.damageToTree = damageToTree;
this.goldCapacity = goldCapacity;
this.lumberCapacity = lumberCapacity;
this.castRange = castRange;
this.duration = duration;
}
public int getDamageToTree() {
@ -30,4 +34,12 @@ public class CAbilityTypeHarvestLevelData extends CAbilityTypeLevelData {
return this.lumberCapacity;
}
public float getCastRange() {
return this.castRange;
}
public float getDuration() {
return this.duration;
}
}

View File

@ -6,11 +6,9 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting
public abstract class CAbstractRangedBehavior implements CRangedBehavior {
protected final CUnit unit;
private final boolean disableCollision;
public CAbstractRangedBehavior(final CUnit unit, final boolean disableCollision) {
public CAbstractRangedBehavior(final CUnit unit) {
this.unit = unit;
this.disableCollision = disableCollision;
}
protected AbilityTarget target;
@ -20,12 +18,16 @@ public abstract class CAbstractRangedBehavior implements CRangedBehavior {
private CBehaviorMove moveBehavior;
protected final CAbstractRangedBehavior innerReset(final AbilityTarget target) {
return innerReset(target, false);
}
protected final CAbstractRangedBehavior innerReset(final AbilityTarget target, final boolean disableCollision) {
this.target = target;
this.wasWithinPropWindow = false;
this.wasInRange = false;
CBehaviorMove moveBehavior;
if (!this.unit.isMovementDisabled()) {
moveBehavior = this.unit.getMoveBehavior().reset(this.target, this, this.disableCollision);
moveBehavior = this.unit.getMoveBehavior().reset(this.target, this, disableCollision);
}
else {
moveBehavior = null;
@ -36,6 +38,8 @@ public abstract class CAbstractRangedBehavior implements CRangedBehavior {
protected abstract CBehavior update(CSimulation simulation, boolean withinRange);
protected abstract CBehavior updateOnInvalidTarget(CSimulation simulation);
protected abstract boolean checkTargetStillValid(CSimulation simulation);
protected abstract void resetBeforeMoving(CSimulation simulation);
@ -43,7 +47,7 @@ public abstract class CAbstractRangedBehavior implements CRangedBehavior {
@Override
public final CBehavior update(final CSimulation simulation) {
if (!checkTargetStillValid(simulation)) {
return this.unit.pollNextOrderBehavior(simulation);
return updateOnInvalidTarget(simulation);
}
if (!isWithinRange(simulation)) {
if ((this.moveBehavior == null) || this.disableMove) {

View File

@ -15,7 +15,7 @@ public class CBehaviorAttack extends CAbstractRangedBehavior {
private final AbilityTargetStillAliveAndTargetableVisitor abilityTargetStillAliveVisitor;
public CBehaviorAttack(final CUnit unit) {
super(unit, false);
super(unit);
this.abilityTargetStillAliveVisitor = new AbilityTargetStillAliveAndTargetableVisitor();
}
@ -23,10 +23,12 @@ public class CBehaviorAttack extends CAbstractRangedBehavior {
private int damagePointLaunchTime;
private int backSwingTime;
private int thisOrderCooldownEndTime;
private CBehaviorAttackListener attackListener;
public CBehaviorAttack reset(final int highlightOrderId, final CUnitAttack unitAttack, final AbilityTarget target,
final boolean disableMove) {
final boolean disableMove, final CBehaviorAttackListener attackListener) {
this.highlightOrderId = highlightOrderId;
this.attackListener = attackListener;
super.innerReset(target);
this.unitAttack = unitAttack;
this.damagePointLaunchTime = 0;
@ -63,6 +65,11 @@ public class CBehaviorAttack extends CAbstractRangedBehavior {
this.thisOrderCooldownEndTime = 0;
}
@Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
return this.attackListener.onFinish(simulation, this.unit);
}
@Override
public CBehavior update(final CSimulation simulation, final boolean withinRange) {
final int cooldownEndTime = this.unit.getCooldownEndTime();
@ -85,7 +92,7 @@ public class CBehaviorAttack extends CAbstractRangedBehavior {
else {
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
}
this.unitAttack.launch(simulation, this.unit, this.target, damage);
this.unitAttack.launch(simulation, this.unit, this.target, damage, this.attackListener);
this.damagePointLaunchTime = 0;
}
}
@ -114,6 +121,11 @@ public class CBehaviorAttack extends CAbstractRangedBehavior {
false);
}
if ((this.backSwingTime != 0) && (currentTurnTick >= this.backSwingTime)) {
this.backSwingTime = 0;
System.out.println("INTERRUPT AFTER BACKSWING");
return this.attackListener.onFirstUpdateAfterBackswing(this);
}
return this;
}

View File

@ -0,0 +1,35 @@
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.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackListener;
public interface CBehaviorAttackListener extends CUnitAttackListener {
// For this function, return the current attack behavior to keep attacking, or
// else return something else to interrupt it
CBehavior onFirstUpdateAfterBackswing(CBehaviorAttack currentAttackBehavior);
CBehavior onFinish(CSimulation game, final CUnit finishingUnit);
CBehaviorAttackListener DO_NOTHING = new CBehaviorAttackListener() {
@Override
public void onHit(final AbilityTarget target, final float damage) {
}
@Override
public void onLaunch() {
}
@Override
public CBehavior onFirstUpdateAfterBackswing(final CBehaviorAttack currentAttackBehavior) {
return currentAttackBehavior;
}
@Override
public CBehavior onFinish(final CSimulation game, final CUnit finishingUnit) {
return finishingUnit.pollNextOrderBehavior(game);
}
};
}

View File

@ -11,7 +11,7 @@ public class CBehaviorFollow extends CAbstractRangedBehavior {
private int higlightOrderId;
public CBehaviorFollow(final CUnit unit) {
super(unit, false);
super(unit);
}
public CBehavior reset(final int higlightOrderId, final CUnit target) {
@ -35,6 +35,11 @@ public class CBehaviorFollow extends CAbstractRangedBehavior {
return this;
}
@Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
return this.unit.pollNextOrderBehavior(simulation);
}
@Override
protected boolean checkTargetStillValid(final CSimulation simulation) {
return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE);

View File

@ -21,7 +21,7 @@ public class CBehaviorOrcBuild extends CAbstractRangedBehavior {
private War3ID orderId;
public CBehaviorOrcBuild(final CUnit unit) {
super(unit, false);
super(unit);
}
public CBehavior reset(final AbilityPointTarget target, final int orderId, final int highlightOrderId) {
@ -88,6 +88,11 @@ public class CBehaviorOrcBuild extends CAbstractRangedBehavior {
return true;
}
@Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
return this.unit.pollNextOrderBehavior(simulation);
}
@Override
protected void resetBeforeMoving(final CSimulation simulation) {

View File

@ -13,27 +13,30 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.harvest.CAbilityHarvest;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.mine.CAbilityGoldMine;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetStillAliveVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CAbstractRangedBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorAttackListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType;
public class CBehaviorHarvest extends CAbstractRangedBehavior implements AbilityTargetVisitor<CBehavior> {
public class CBehaviorHarvest extends CAbstractRangedBehavior
implements AbilityTargetVisitor<CBehavior>, CBehaviorAttackListener {
private final CAbilityHarvest abilityHarvest;
private CSimulation simulation;
private int popoutFromMineTurnTick = 0;
private CAbilityGoldMine abilityGoldMine;
public CBehaviorHarvest(final CUnit unit, final CAbilityHarvest abilityHarvest) {
super(unit, true);
super(unit);
this.abilityHarvest = abilityHarvest;
}
public CBehaviorHarvest reset(final CWidget target) {
innerReset(target);
this.abilityGoldMine = null;
innerReset(target, target instanceof CUnit);
this.abilityHarvest.setLastHarvestTarget(target);
if (this.popoutFromMineTurnTick != 0) {
// TODO this check is probably only for debug and should be removed after
// extensive testing
@ -76,9 +79,9 @@ public class CBehaviorHarvest extends CAbstractRangedBehavior implements Ability
this.unit.setHidden(true);
this.unit.setInvulnerable(true);
this.unit.setPaused(true);
this.unit.setAcceptingOrders(false);
this.popoutFromMineTurnTick = this.simulation.getGameTurnTick()
+ (int) (abilityGoldMine.getMiningDuration() / WarsmashConstants.SIMULATION_STEP_TIME);
this.abilityGoldMine = abilityGoldMine;
}
else {
// we are stuck waiting to mine, let's make sure we play stand animation
@ -103,6 +106,8 @@ public class CBehaviorHarvest extends CAbstractRangedBehavior implements Ability
this.unit.setHidden(false);
this.unit.setInvulnerable(false);
this.unit.setPaused(false);
this.unit.setAcceptingOrders(true);
dropResources();
this.abilityHarvest.setCarriedResources(ResourceType.GOLD, goldMined);
this.unit.getUnitAnimationListener().addSecondaryTag(SecondaryTag.GOLD);
this.simulation.unitRepositioned(this.unit);
@ -110,15 +115,49 @@ public class CBehaviorHarvest extends CAbstractRangedBehavior implements Ability
@Override
public CBehavior accept(final CDestructable target) {
// TODO cut trees!
if (String.valueOf(target).length() > 5) {
return this.unit.pollNextOrderBehavior(this.simulation);
if ((this.abilityHarvest.getCarriedResourceType() != ResourceType.LUMBER)
|| (this.abilityHarvest.getCarriedResourceAmount() < this.abilityHarvest.getLumberCapacity())) {
return this.unit.getAttackBehavior().reset(getHighlightOrderId(), this.abilityHarvest.getTreeAttack(),
target, false, this);
}
else {
return null;
// we have some LUMBER and we can't carry any more, time to return resources
return this.abilityHarvest.getBehaviorReturnResources().reset(this.simulation);
}
}
@Override
public void onHit(final AbilityTarget target, final float damage) {
if (this.abilityHarvest.getCarriedResourceType() != ResourceType.LUMBER) {
dropResources();
}
this.abilityHarvest.setCarriedResources(ResourceType.LUMBER,
Math.min(this.abilityHarvest.getCarriedResourceAmount() + this.abilityHarvest.getDamageToTree(),
this.abilityHarvest.getLumberCapacity()));
this.unit.getUnitAnimationListener().addSecondaryTag(SecondaryTag.LUMBER);
}
@Override
public void onLaunch() {
}
@Override
public CBehavior onFirstUpdateAfterBackswing(final CBehaviorAttack currentAttackBehavior) {
if (this.abilityHarvest.getCarriedResourceAmount() >= this.abilityHarvest.getLumberCapacity()) {
return this.abilityHarvest.getBehaviorReturnResources().reset(this.simulation);
}
return currentAttackBehavior;
}
@Override
public CBehavior onFinish(final CSimulation game, final CUnit finishingUnit) {
if (this.abilityHarvest.getCarriedResourceAmount() >= this.abilityHarvest.getLumberCapacity()) {
return this.abilityHarvest.getBehaviorReturnResources().reset(this.simulation);
}
return updateOnInvalidTarget(game);
}
@Override
public CBehavior accept(final CItem target) {
return this.unit.pollNextOrderBehavior(this.simulation);
@ -129,6 +168,20 @@ public class CBehaviorHarvest extends CAbstractRangedBehavior implements Ability
return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE);
}
@Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
if (this.target instanceof CDestructable) {
// wood
final CDestructable nearestTree = CBehaviorReturnResources.findNearestTree(this.unit, this.abilityHarvest,
simulation, (CDestructable) this.target);
if (nearestTree != null) {
this.target = nearestTree;
return this;
}
}
return this.unit.pollNextOrderBehavior(simulation);
}
@Override
protected void resetBeforeMoving(final CSimulation simulation) {
@ -152,4 +205,20 @@ public class CBehaviorHarvest extends CAbstractRangedBehavior implements Ability
return this.abilityHarvest.getGoldCapacity();
}
private void dropResources() {
if (this.abilityHarvest.getCarriedResourceType() != null) {
switch (this.abilityHarvest.getCarriedResourceType()) {
case FOOD:
throw new IllegalStateException("Unit used Harvest skill to carry FOOD resource!");
case GOLD:
this.unit.getUnitAnimationListener().removeSecondaryTag(SecondaryTag.GOLD);
break;
case LUMBER:
this.unit.getUnitAnimationListener().removeSecondaryTag(SecondaryTag.LUMBER);
break;
}
}
this.abilityHarvest.setCarriedResources(null, 0);
}
}

View File

@ -5,6 +5,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.harvest.CAbilityHarvest;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.harvest.CAbilityReturnResources;
@ -22,7 +23,7 @@ public class CBehaviorReturnResources extends CAbstractRangedBehavior implements
private CSimulation simulation;
public CBehaviorReturnResources(final CUnit unit, final CAbilityHarvest abilityHarvest) {
super(unit, true);
super(unit);
this.abilityHarvest = abilityHarvest;
}
@ -32,7 +33,7 @@ public class CBehaviorReturnResources extends CAbstractRangedBehavior implements
// TODO it is unconventional not to return self here
return this.unit.pollNextOrderBehavior(simulation);
}
innerReset(nearestDropoffPoint);
innerReset(nearestDropoffPoint, true);
return this;
}
@ -65,22 +66,44 @@ public class CBehaviorReturnResources extends CAbstractRangedBehavior implements
final CAbilityReturnResources abilityReturnResources = (CAbilityReturnResources) ability;
if (abilityReturnResources.accepts(this.abilityHarvest.getCarriedResourceType())) {
final CPlayer player = this.simulation.getPlayer(this.unit.getPlayerIndex());
CWidget nextTarget = null;
switch (this.abilityHarvest.getCarriedResourceType()) {
case FOOD:
throw new IllegalStateException("Unit used Harvest skill to carry FOOD resource!");
case GOLD:
player.setGold(player.getGold() + this.abilityHarvest.getCarriedResourceAmount());
this.unit.getUnitAnimationListener().removeSecondaryTag(SecondaryTag.GOLD);
if ((this.abilityHarvest.getLastHarvestTarget() != null) && this.abilityHarvest
.getLastHarvestTarget().visit(AbilityTargetStillAliveVisitor.INSTANCE)) {
nextTarget = this.abilityHarvest.getLastHarvestTarget();
}
else {
nextTarget = findNearestMine(this.unit, this.simulation);
}
break;
case LUMBER:
player.setLumber(player.getLumber() + this.abilityHarvest.getCarriedResourceAmount());
this.unit.getUnitAnimationListener().removeSecondaryTag(SecondaryTag.LUMBER);
if (this.abilityHarvest.getLastHarvestTarget() != null) {
if (this.abilityHarvest.getLastHarvestTarget()
.visit(AbilityTargetStillAliveVisitor.INSTANCE)) {
nextTarget = this.abilityHarvest.getLastHarvestTarget();
}
else {
nextTarget = findNearestTree(this.unit, this.abilityHarvest, this.simulation,
this.abilityHarvest.getLastHarvestTarget());
}
}
else {
nextTarget = findNearestTree(this.unit, this.abilityHarvest, this.simulation, this.unit);
}
break;
}
this.simulation.unitGainResourceEvent(this.unit, this.abilityHarvest.getCarriedResourceType(),
this.abilityHarvest.getCarriedResourceAmount());
this.abilityHarvest.setCarriedResources(null, 0);
final CUnit nearestMine = findNearestMine(this.unit, this.simulation);
if (nearestMine != null) {
return this.abilityHarvest.getBehaviorHarvest().reset(nearestMine);
if (nextTarget != null) {
return this.abilityHarvest.getBehaviorHarvest().reset(nextTarget);
}
return this.unit.pollNextOrderBehavior(this.simulation);
}
@ -102,18 +125,17 @@ public class CBehaviorReturnResources extends CAbstractRangedBehavior implements
@Override
protected boolean checkTargetStillValid(final CSimulation simulation) {
final boolean aliveCheck = this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE);
if (!aliveCheck) {
return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE);
}
@Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
final CUnit nearestDropoff = findNearestDropoffPoint(simulation);
if (nearestDropoff == null) {
return false;
}
else {
if (nearestDropoff != null) {
this.target = nearestDropoff;
return true;
return this;
}
}
return true;
return this.unit.pollNextOrderBehavior(simulation);
}
@Override
@ -176,6 +198,24 @@ public class CBehaviorReturnResources extends CAbstractRangedBehavior implements
return nearestMine;
}
public static CDestructable findNearestTree(final CUnit worker, final CAbilityHarvest abilityHarvest,
final CSimulation simulation, final CWidget toObject) {
CDestructable nearestMine = null;
double nearestMineDistance = Float.MAX_VALUE;
for (final CDestructable unit : simulation.getDestructables()) {
if (unit.canBeTargetedBy(simulation, worker, abilityHarvest.getTreeAttack().getTargetsAllowed())) {
// TODO maybe use distance squared, problem is that we're using this
// inefficient more complex distance function on unit
final double distance = unit.distanceSquaredNoCollision(toObject);
if (distance < nearestMineDistance) {
nearestMineDistance = distance;
nearestMine = unit;
}
}
}
return nearestMine;
}
@Override
public void begin(final CSimulation game) {

View File

@ -198,5 +198,6 @@ public abstract class CUnitAttack {
return this.maxDamage;
}
public abstract void launch(CSimulation simulation, CUnit unit, AbilityTarget target, float damage);
public abstract void launch(CSimulation simulation, CUnit unit, AbilityTarget target, float damage,
CUnitAttackListener attackListener);
}

View File

@ -34,11 +34,14 @@ public class CUnitAttackInstant extends CUnitAttack {
}
@Override
public void launch(final CSimulation simulation, final CUnit unit, final AbilityTarget target, final float damage) {
public void launch(final CSimulation simulation, final CUnit unit, final AbilityTarget target, final float damage,
final CUnitAttackListener attackListener) {
attackListener.onLaunch();
final CWidget widget = target.visit(AbilityTargetWidgetVisitor.INSTANCE);
if (widget != null) {
simulation.createInstantAttackEffect(unit, this, widget);
widget.damage(simulation, unit, getAttackType(), getWeaponSound(), damage);
attackListener.onHit(target, damage);
}
}

View File

@ -0,0 +1,9 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
public interface CUnitAttackListener {
void onLaunch();
void onHit(AbilityTarget target, float damage);
}

View File

@ -65,16 +65,20 @@ public class CUnitAttackMissile extends CUnitAttack {
}
@Override
public void launch(final CSimulation simulation, final CUnit unit, final AbilityTarget target, final float damage) {
public void launch(final CSimulation simulation, final CUnit unit, final AbilityTarget target, final float damage,
final CUnitAttackListener attackListener) {
attackListener.onLaunch();
simulation.createProjectile(unit, unit.getX(), unit.getY(), (float) Math.toRadians(unit.getFacing()), this,
target, damage, 0);
target, damage, 0, attackListener);
}
public void doDamage(final CSimulation cSimulation, final CUnit source, final AbilityTarget target,
final float damage, final float x, final float y, final int bounceIndex) {
final float damage, final float x, final float y, final int bounceIndex,
final CUnitAttackListener attackListener) {
final CWidget widget = target.visit(AbilityTargetWidgetVisitor.INSTANCE);
if (widget != null) {
widget.damage(cSimulation, source, getAttackType(), getWeaponSound(), damage);
attackListener.onHit(target, damage);
}
}
}

View File

@ -54,14 +54,15 @@ public class CUnitAttackMissileBounce extends CUnitAttackMissile {
@Override
public void doDamage(final CSimulation cSimulation, final CUnit source, final AbilityTarget target,
final float damage, final float x, final float y, final int bounceIndex) {
super.doDamage(cSimulation, source, target, damage, x, y, bounceIndex);
final float damage, final float x, final float y, final int bounceIndex,
final CUnitAttackListener attackListener) {
super.doDamage(cSimulation, source, target, damage, x, y, bounceIndex, attackListener);
final CWidget widget = target.visit(AbilityTargetWidgetVisitor.INSTANCE);
if (widget != null) {
final int nextBounceIndex = bounceIndex + 1;
if (nextBounceIndex != this.maximumNumberOfTargets) {
BounceMissileConsumer.INSTANCE.nextBounce(cSimulation, source, widget, this, x, y, damage,
nextBounceIndex);
nextBounceIndex, attackListener);
}
}
}
@ -77,11 +78,12 @@ public class CUnitAttackMissileBounce extends CUnitAttackMissile {
private float y;
private float damage;
private int bounceIndex;
private CUnitAttackListener attackListener;
private boolean launched = false;
public void nextBounce(final CSimulation simulation, final CUnit source, final CWidget target,
final CUnitAttackMissileBounce attack, final float x, final float y, final float damage,
final int bounceIndex) {
final int bounceIndex, final CUnitAttackListener attackListener) {
this.simulation = simulation;
this.source = source;
this.target = target;
@ -90,6 +92,7 @@ public class CUnitAttackMissileBounce extends CUnitAttackMissile {
this.y = y;
this.damage = damage;
this.bounceIndex = bounceIndex;
this.attackListener = attackListener;
this.launched = false;
final float doubleMaxArea = attack.areaOfEffectFullDamage
+ (this.simulation.getGameplayConstants().getCloseEnoughRange() * 2);
@ -112,7 +115,7 @@ public class CUnitAttackMissileBounce extends CUnitAttackMissile {
final float dy = enumUnit.getY() - this.y;
final float angle = (float) Math.atan2(dy, dx);
this.simulation.createProjectile(this.source, this.x, this.y, angle, this.attack, enumUnit,
this.damage * (1.0f - this.attack.damageLossFactor), this.bounceIndex);
this.damage * (1.0f - this.attack.damageLossFactor), this.bounceIndex, this.attackListener);
this.launched = true;
return true;
}

View File

@ -94,10 +94,12 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile {
@Override
public void doDamage(final CSimulation cSimulation, final CUnit source, final AbilityTarget target,
final float damage, final float x, final float y, final int bounceIndex) {
SplashDamageConsumer.INSTANCE.doDamage(cSimulation, source, target, this, x, y, damage);
final float damage, final float x, final float y, final int bounceIndex,
final CUnitAttackListener attackListener) {
SplashDamageConsumer.INSTANCE.doDamage(cSimulation, source, target, this, x, y, damage, attackListener);
if ((getWeaponType() != CWeaponType.ARTILLERY) && !SplashDamageConsumer.INSTANCE.hitTarget) {
super.doDamage(cSimulation, source, target, damage * this.damageFactorSmall, x, y, bounceIndex);
super.doDamage(cSimulation, source, target, damage * this.damageFactorSmall, x, y, bounceIndex,
attackListener);
}
}
@ -111,10 +113,12 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile {
private float x;
private float y;
private float damage;
private CUnitAttackListener attackListener;
private boolean hitTarget;
public void doDamage(final CSimulation simulation, final CUnit source, final AbilityTarget target,
final CUnitAttackMissileSplash attack, final float x, final float y, final float damage) {
final CUnitAttackMissileSplash attack, final float x, final float y, final float damage,
final CUnitAttackListener attackListener) {
this.simulation = simulation;
this.source = source;
this.target = target;
@ -122,6 +126,7 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile {
this.x = x;
this.y = y;
this.damage = damage;
this.attackListener = attackListener;
this.hitTarget = false;
final float doubleMaxArea = (attack.areaOfEffectSmallDamage) * 2;
final float maxArea = doubleMaxArea / 2;
@ -136,14 +141,17 @@ public class CUnitAttackMissileSplash extends CUnitAttackMissile {
if (distance <= (this.attack.areaOfEffectFullDamage)) {
enumUnit.damage(this.simulation, this.source, this.attack.getAttackType(),
this.attack.getWeaponSound(), this.damage);
this.attackListener.onHit(enumUnit, this.damage);
}
else if (distance <= (this.attack.areaOfEffectMediumDamage)) {
enumUnit.damage(this.simulation, this.source, this.attack.getAttackType(),
this.attack.getWeaponSound(), this.damage * this.attack.damageFactorMedium);
this.attackListener.onHit(enumUnit, this.damage);
}
else if (distance <= (this.attack.areaOfEffectSmallDamage)) {
enumUnit.damage(this.simulation, this.source, this.attack.getAttackType(),
this.attack.getWeaponSound(), this.damage * this.attack.damageFactorSmall);
this.attackListener.onHit(enumUnit, this.damage);
}
if (enumUnit == this.target) {
this.hitTarget = true;

View File

@ -24,10 +24,13 @@ public class CUnitAttackNormal extends CUnitAttack {
}
@Override
public void launch(final CSimulation simulation, final CUnit unit, final AbilityTarget target, final float damage) {
public void launch(final CSimulation simulation, final CUnit unit, final AbilityTarget target, final float damage,
final CUnitAttackListener attackListener) {
attackListener.onLaunch();
final CWidget widget = target.visit(AbilityTargetWidgetVisitor.INSTANCE);
if (widget != null) {
widget.damage(simulation, unit, getAttackType(), getWeaponSound(), damage);
attackListener.onHit(target, damage);
}
}

View File

@ -5,6 +5,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CWeaponType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile;
public class CAttackProjectile {
@ -19,9 +20,11 @@ public class CAttackProjectile {
private final float damage;
private final CUnitAttackMissile unitAttack;
private final int bounceIndex;
private final CUnitAttackListener attackListener;
public CAttackProjectile(final float x, final float y, final float speed, final AbilityTarget target,
final CUnit source, final float damage, final CUnitAttackMissile unitAttack, final int bounceIndex) {
final CUnit source, final float damage, final CUnitAttackMissile unitAttack, final int bounceIndex,
final CUnitAttackListener attackListener) {
this.x = x;
this.y = y;
this.speed = speed;
@ -30,6 +33,7 @@ public class CAttackProjectile {
this.damage = damage;
this.unitAttack = unitAttack;
this.bounceIndex = bounceIndex;
this.attackListener = attackListener;
this.initialTargetX = target.getX();
this.initialTargetY = target.getY();
}
@ -60,7 +64,7 @@ public class CAttackProjectile {
if (done && !this.done) {
this.unitAttack.doDamage(cSimulation, this.source, this.target, this.damage, this.x, this.y,
this.bounceIndex);
this.bounceIndex, this.attackListener);
this.done = true;
}
return this.done;

View File

@ -9,12 +9,14 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.CAttackProjectile;
public interface SimulationRenderController {
CAttackProjectile createAttackProjectile(CSimulation simulation, float launchX, float launchY, float launchFacing,
CUnit source, CUnitAttackMissile attack, AbilityTarget target, float damage, int bounceIndex);
CUnit source, CUnitAttackMissile attack, AbilityTarget target, float damage, int bounceIndex,
CUnitAttackListener attackListener);
CUnit createUnit(CSimulation simulation, final War3ID typeId, final int playerIndex, final float x, final float y,
final float facing);
@ -42,4 +44,6 @@ public interface SimulationRenderController {
void spawnUnitReadySound(CUnit trainedUnit);
void unitRepositioned(CUnit cUnit);
void spawnGainResourceTextTag(CUnit gainingUnit, ResourceType resourceType, int amount);
}

View File

@ -22,6 +22,7 @@ import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.graphics.glutils.PixmapTextureData;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
@ -65,6 +66,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
import com.etheller.warsmash.viewer5.handlers.w3x.TextTag;
import com.etheller.warsmash.viewer5.handlers.w3x.UnitSound;
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
import com.etheller.warsmash.viewer5.handlers.w3x.camera.CameraPreset;
@ -252,6 +254,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
private MdxModel waypointModel;
private final List<MdxComplexInstance> waypointModelInstances = new ArrayList<>();
private List<RenderUnit> selectedUnits;
private BitmapFont textTagFont;
public MeleeUI(final DataSource dataSource, final ExtendViewport uiViewport,
final FreeTypeFontGenerator fontGenerator, final Scene uiScene, final Scene portraitScene,
@ -634,9 +637,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
War3MapViewer.mdx(this.rootFrame.getSkinField("WaypointIndicator")), this.war3MapViewer.mapPathSolver,
this.war3MapViewer.solverParams);
final FreeTypeFontParameter fontParam = new FreeTypeFontParameter();
fontParam.size = (int) GameUI.convertY(this.uiViewport, 0.012f);
this.textTagFont = this.fontGenerator.generateFont(fontParam);
this.rootFrame.positionBounds(this.rootFrame, this.uiViewport);
selectUnit(null);
}
@Override
@ -834,6 +842,14 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.meleeUIMinimap.render(batch, this.war3MapViewer.units);
this.timeIndicator.setFrameByRatio(this.war3MapViewer.simulation.getGameTimeOfDay()
/ this.war3MapViewer.simulation.getGameplayConstants().getGameDayHours());
for (final TextTag textTag : this.war3MapViewer.textTags) {
this.war3MapViewer.worldScene.camera.worldToScreen(screenCoordsVector, textTag.getPosition());
final Vector2 unprojected = this.uiViewport.unproject(screenCoordsVector);
this.textTagFont.setColor(textTag.getColor());
glyphLayout.setText(this.textTagFont, textTag.getText());
this.textTagFont.draw(batch, textTag.getText(), unprojected.x - (glyphLayout.width / 2),
(unprojected.y - (glyphLayout.height / 2)));
}
}
public void portraitTalk() {