Merge and some fixes, includes click drag ui test

This commit is contained in:
Retera 2020-10-25 11:52:04 -04:00
commit 5b7db285e2
37 changed files with 2140 additions and 1657 deletions

View File

@ -18,7 +18,7 @@ Path06="."
[Map]
//FilePath="CombatUnitTests.w3x"
//FilePath="PitchRoll.w3x"
//FilePath="PeonStartingBase.w3x"
FilePath="PeonStartingBase.w3x"
//FilePath="DungeonGoldMine.w3m"
//FilePath="PlayerPeasants.w3m"
//FilePath="FireLord.w3x"
@ -29,4 +29,4 @@ Path06="."
//FilePath="OrcAssault.w3x"
//FilePath="FrostyVsFarm.w3m"
//FilePath="ModelTest.w3x"
FilePath="SpinningSample.w3x"
//FilePath="SpinningSample.w3x"

View File

@ -134,7 +134,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
}
final Element cameraData = this.viewer.miscData.get("Camera");
final Element cameraListenerData = this.viewer.miscData.get("Listener");
Element cameraListenerData = this.viewer.miscData.get("Listener");
if (cameraListenerData == null) {
cameraListenerData = new Element("Listener", new DataTable(null));
}
final CameraPreset[] cameraPresets = new CameraPreset[6];
for (int i = 0; i < cameraPresets.length; i++) {
cameraPresets[i] = new CameraPreset(cameraData.getFieldFloatValue("AOA", i),
@ -353,6 +356,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
@Override
public boolean keyTyped(final char character) {
if (character == '1') {
Gdx.input.setCursorCatched(!Gdx.input.isCursorCatched());
}
return false;
}
@ -373,6 +379,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
@Override
public boolean touchDragged(final int screenX, final int screenY, final int pointer) {
final float worldScreenY = getHeight() - screenY;
if (this.meleeUI.touchDragged(screenX, screenY, worldScreenY, pointer)) {
return false;
}
return false;
}

View File

@ -27,12 +27,14 @@ import com.etheller.warsmash.parsers.fdf.datamodel.SetPointDefinition;
import com.etheller.warsmash.parsers.fdf.datamodel.TextJustify;
import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition;
import com.etheller.warsmash.parsers.fdf.frames.AbstractUIFrame;
import com.etheller.warsmash.parsers.fdf.frames.FilterModeTextureFrame;
import com.etheller.warsmash.parsers.fdf.frames.SetPoint;
import com.etheller.warsmash.parsers.fdf.frames.SimpleFrame;
import com.etheller.warsmash.parsers.fdf.frames.SpriteFrame;
import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.Element;
import com.etheller.warsmash.util.ImageUtils;
@ -211,6 +213,16 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
return textureFrame;
}
public TextureFrame createTextureFrame(final String name, final UIFrame parent, final boolean decorateFileNames,
final Vector4Definition texCoord, final FilterMode filterMode) {
final FilterModeTextureFrame textureFrame = new FilterModeTextureFrame(name, parent, decorateFileNames,
texCoord);
textureFrame.setFilterMode(filterMode);
this.nameToFrame.put(name, textureFrame);
add(textureFrame);
return textureFrame;
}
public StringFrame createStringFrame(final String name, final UIFrame parent, final Color color,
final TextJustify justifyH, final TextJustify justifyV, final float fdfFontSize) {
this.fontParam.size = (int) convertY(this.viewport, fdfFontSize);

View File

@ -25,6 +25,9 @@ public class StringFrame extends AbstractRenderableFrame {
}
public void setText(final String text) {
if (text == null) {
throw new IllegalArgumentException();
}
this.text = text;
}

View File

@ -63,4 +63,12 @@ public class TextureFrame extends AbstractRenderableFrame {
public void setTexture(final TextureRegion texture) {
this.texture = texture;
}
@Override
public UIFrame touchDown(final float screenX, final float screenY, final int button) {
if (this.renderBounds.contains(screenX, screenY)) {
return this;
}
return super.touchDown(screenX, screenY, button);
}
}

View File

@ -75,6 +75,43 @@ public class Quadtree<T> {
}
}
public boolean intersect(float x, float y, final QuadtreeIntersector<T> intersector) {
if (this.leaf) {
for (int i = 0; i < this.nodes.size; i++) {
final Node<T> node = this.nodes.get(i);
if (node.bounds.contains(x, y)) {
if (intersector.onIntersect(node.object)) {
return true;
}
}
}
return false;
}
else {
if (this.northeast.bounds.contains(x, y)) {
if (this.northeast.intersect(x, y, intersector)) {
return true;
}
}
if (this.northwest.bounds.contains(x, y)) {
if (this.northwest.intersect(x, y, intersector)) {
return true;
}
}
if (this.southwest.bounds.contains(x, y)) {
if (this.southwest.intersect(x, y, intersector)) {
return true;
}
}
if (this.southeast.bounds.contains(x, y)) {
if (this.southeast.intersect(x, y, intersector)) {
return true;
}
}
return false;
}
}
private void add(final Node<T> node, final int depth) {
if (this.leaf) {
if ((this.nodes.size >= SPLIT_THRESHOLD) && (depth < MAX_DEPTH)) {

View File

@ -31,4 +31,8 @@ public class Bounds {
public boolean intersectRayFast(final Ray ray) {
return Intersector.intersectRayBoundsFast(ray, this.boundingBox);
}
public BoundingBox getBoundingBox() {
return boundingBox;
}
}

View File

@ -22,343 +22,328 @@ import com.etheller.warsmash.viewer5.Texture;
import com.etheller.warsmash.viewer5.handlers.EmitterObject;
public class EventObjectEmitterObject extends GenericObject implements EmitterObject {
private static final class LoadGenericSoundCallback implements LoadGenericCallback {
private final String filename;
private static final class LoadGenericSoundCallback implements LoadGenericCallback {
private final String filename;
public LoadGenericSoundCallback(final String filename) {
this.filename = filename;
}
public LoadGenericSoundCallback(final String filename) {
this.filename = filename;
}
@Override
public Object call(final InputStream data) {
final FileHandle temp = new FileHandle(this.filename) {
@Override
public InputStream read() {
return data;
};
};
if (data != null) {
return Gdx.audio.newSound(temp);
}
else {
System.err.println("Warning: missing sound file: " + this.filename);
return null;
}
}
}
@Override
public Object call(final InputStream data) {
final FileHandle temp = new FileHandle(this.filename) {
@Override
public InputStream read() {
return data;
}
private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() {
;
};
if (data != null) {
return Gdx.audio.newSound(temp);
} else {
System.err.println("Warning: missing sound file: " + this.filename);
return null;
}
}
}
@Override
public Object call(final InputStream data) {
final StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) {
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
stringBuilder.append("\n");
}
}
catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
catch (final IOException e) {
throw new RuntimeException(e);
}
return new MappedData(stringBuilder.toString());
}
};
private static final LoadGenericCallback mappedDataCallback = new LoadGenericCallback() {
private int geometryEmitterType = -1;
public final String type;
private final String id;
public final long[] keyFrames;
private long globalSequence = -1;
private final long[] defval = { 1 };
public MdxModel internalModel;
public Texture internalTexture;
public float[][] colors;
public float[] intervalTimes;
public float scale;
public int columns;
public int rows;
public float lifeSpan;
public int blendSrc;
public int blendDst;
public float[][] intervals;
public float distanceCutoff;
private float maxDistance;
public float minDistance;
private float pitch;
private float pitchVariance;
private float volume;
public List<Sound> decodedBuffers = new ArrayList<>();
/**
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
* are loaded.
*
* This is because, like the other geometry emitters, it is fine to use them
* even if the textures don't load.
*
* The particles will simply be black.
*/
private boolean ok = false;
@Override
public Object call(final InputStream data) {
final StringBuilder stringBuilder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(data, "utf-8"))) {
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
stringBuilder.append("\n");
}
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
} catch (final IOException e) {
throw new RuntimeException(e);
}
return new MappedData(stringBuilder.toString());
}
};
public EventObjectEmitterObject(final MdxModel model,
final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) {
super(model, eventObject, index);
private int geometryEmitterType = -1;
public final String type;
private final String id;
public final long[] keyFrames;
private long globalSequence = -1;
private final long[] defval = {1};
public MdxModel internalModel;
public Texture internalTexture;
public float[][] colors;
public float[] intervalTimes;
public float scale;
public int columns;
public int rows;
public float lifeSpan;
public int blendSrc;
public int blendDst;
public float[][] intervals;
public float distanceCutoff;
private float maxDistance;
public float minDistance;
private float pitch;
private float pitchVariance;
private float volume;
public List<Sound> decodedBuffers = new ArrayList<>();
/**
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
* are loaded.
* <p>
* This is because, like the other geometry emitters, it is fine to use them
* even if the textures don't load.
* <p>
* The particles will simply be black.
*/
private boolean ok = false;
final ModelViewer viewer = model.viewer;
final String name = eventObject.getName();
String type = name.substring(0, 3);
final String id = name.substring(4);
public EventObjectEmitterObject(final MdxModel model,
final com.etheller.warsmash.parsers.mdlx.EventObject eventObject, final int index) {
super(model, eventObject, index);
// Same thing
if ("FPT".equals(type)) {
type = "SPL";
}
final ModelViewer viewer = model.viewer;
final String name = eventObject.getName();
String type = name.substring(0, 3);
final String id = name.substring(4);
if ("SPL".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT;
}
else if ("UBR".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT;
}
else if ("SPN".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN;
}
// Same thing
if ("FPT".equals(type)) {
type = "SPL";
}
this.type = type;
this.id = id;
this.keyFrames = eventObject.getKeyFrames();
if ("SPL".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT;
} else if ("UBR".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT;
} else if ("SPN".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN;
}
final int globalSequenceId = eventObject.getGlobalSequenceId();
if (globalSequenceId != -1) {
this.globalSequence = model.getGlobalSequences().get(globalSequenceId);
}
this.type = type;
this.id = id;
this.keyFrames = eventObject.getKeyFrames();
final List<GenericResource> tables = new ArrayList<>();
final PathSolver pathSolver = model.pathSolver;
final Object solverParams = model.solverParams;
final int globalSequenceId = eventObject.getGlobalSequenceId();
if (globalSequenceId != -1) {
this.globalSequence = model.getGlobalSequences().get(globalSequenceId);
}
if ("SPN".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("SPL".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("UBR".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("SND".equals(type)) {
if (!model.reforged) {
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else {
// Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named
// "Point01".
return;
}
final List<GenericResource> tables = new ArrayList<>();
final PathSolver pathSolver = model.pathSolver;
final Object solverParams = model.solverParams;
// TODO I am scrapping some async stuff with promises here from the JS and
// calling load
this.load(tables);
}
if ("SPN".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
} else if ("SPL".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
} else if ("UBR".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
} else if ("SND".equals(type)) {
if (!model.reforged) {
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
} else {
// Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named
// "Point01".
return;
}
private float getFloat(final MappedDataRow row, final String name) {
final Float x = (Float) row.get(name);
if (x == null) {
return Float.NaN;
}
else {
return x.floatValue();
}
}
// TODO I am scrapping some async stuff with promises here from the JS and
// calling load
this.load(tables);
}
private int getInt(final MappedDataRow row, final String name) {
return getInt(row, name, Integer.MIN_VALUE);
}
private float getFloat(final MappedDataRow row, final String name) {
final Float x = (Float) row.get(name);
if (x == null) {
return Float.NaN;
} else {
return x.floatValue();
}
}
private int getInt(final MappedDataRow row, final String name, final int defaultValue) {
final Number x = (Number) row.get(name);
if (x == null) {
return defaultValue;
}
else {
return x.intValue();
}
}
private int getInt(final MappedDataRow row, final String name) {
return getInt(row, name, Integer.MIN_VALUE);
}
private void load(final List<GenericResource> tables) {
final MappedData firstTable = (MappedData) tables.get(0).data;
final MappedDataRow row = firstTable.getRow(this.id.trim());
private int getInt(final MappedDataRow row, final String name, final int defaultValue) {
final Number x = (Number) row.get(name);
if (x == null) {
return defaultValue;
} else {
return x.intValue();
}
}
if (row != null) {
final MdxModel model = this.model;
final ModelViewer viewer = model.viewer;
final PathSolver pathSolver = model.pathSolver;
private void load(final List<GenericResource> tables) {
final MappedData firstTable = (MappedData) tables.get(0).data;
if (firstTable == null) {
return;
}
final MappedDataRow row = firstTable.getRow(this.id.trim());
if ("SPN".equals(this.type)) {
this.internalModel = (MdxModel) viewer.load(((String) row.get("Model")).replace(".mdl", ".mdx"),
pathSolver, model.solverParams);
if (row != null) {
final MdxModel model = this.model;
final ModelViewer viewer = model.viewer;
final PathSolver pathSolver = model.pathSolver;
if (this.internalModel != null) {
// TODO javascript async code removed here
if ("SPN".equals(this.type)) {
this.internalModel = (MdxModel) viewer.load(((String) row.get("Model")).replace(".mdl", ".mdx"),
pathSolver, model.solverParams);
if (this.internalModel != null) {
// TODO javascript async code removed here
// this.internalModel.whenLoaded((model) => this.ok = model.ok)
this.ok = this.internalModel.ok;
}
}
else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
final String texturesExt = model.reforged ? ".dds" : ".blp";
this.ok = this.internalModel.ok;
}
} else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
final String texturesExt = model.reforged ? ".dds" : ".blp";
this.internalTexture = (Texture) viewer.load(
"ReplaceableTextures\\Splats\\" + row.get("file") + texturesExt, pathSolver,
model.solverParams);
this.internalTexture = (Texture) viewer.load(
"ReplaceableTextures\\Splats\\" + row.get("file") + texturesExt, pathSolver,
model.solverParams);
this.scale = getFloat(row, "Scale");
this.colors = new float[][] {
{ getFloat(row, "StartR"), getFloat(row, "StartG"), getFloat(row, "StartB"),
getFloat(row, "StartA") },
{ getFloat(row, "MiddleR"), getFloat(row, "MiddleG"), getFloat(row, "MiddleB"),
getFloat(row, "MiddleA") },
{ getFloat(row, "EndR"), getFloat(row, "EndG"), getFloat(row, "EndB"),
getFloat(row, "EndA") } };
this.scale = getFloat(row, "Scale");
this.colors = new float[][]{
{getFloat(row, "StartR"), getFloat(row, "StartG"), getFloat(row, "StartB"),
getFloat(row, "StartA")},
{getFloat(row, "MiddleR"), getFloat(row, "MiddleG"), getFloat(row, "MiddleB"),
getFloat(row, "MiddleA")},
{getFloat(row, "EndR"), getFloat(row, "EndG"), getFloat(row, "EndB"),
getFloat(row, "EndA")}};
if ("SPL".equals(this.type)) {
this.columns = getInt(row, "Columns");
this.rows = getInt(row, "Rows");
this.lifeSpan = getFloat(row, "Lifespan") + getFloat(row, "Decay");
this.intervalTimes = new float[] { getFloat(row, "Lifespan"), getFloat(row, "Decay") };
this.intervals = new float[][] {
{ getFloat(row, "UVLifespanStart"), getFloat(row, "UVLifespanEnd"),
getFloat(row, "LifespanRepeat") },
{ getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"),
getFloat(row, "DecayRepeat") }, };
}
else {
this.columns = 1;
this.rows = 1;
this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay");
this.intervalTimes = new float[] { getFloat(row, "BirthTime"), getFloat(row, "PauseTime"),
getFloat(row, "Decay") };
}
if ("SPL".equals(this.type)) {
this.columns = getInt(row, "Columns");
this.rows = getInt(row, "Rows");
this.lifeSpan = getFloat(row, "Lifespan") + getFloat(row, "Decay");
this.intervalTimes = new float[]{getFloat(row, "Lifespan"), getFloat(row, "Decay")};
this.intervals = new float[][]{
{getFloat(row, "UVLifespanStart"), getFloat(row, "UVLifespanEnd"),
getFloat(row, "LifespanRepeat")},
{getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"),
getFloat(row, "DecayRepeat")},};
} else {
this.columns = 1;
this.rows = 1;
this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay");
this.intervalTimes = new float[]{getFloat(row, "BirthTime"), getFloat(row, "PauseTime"),
getFloat(row, "Decay")};
}
final int[] blendModes = FilterMode
.emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode
.fromId(getInt(row, "BlendMode")));
final int[] blendModes = FilterMode
.emitterFilterMode(com.etheller.warsmash.parsers.mdlx.ParticleEmitter2.FilterMode
.fromId(getInt(row, "BlendMode")));
this.blendSrc = blendModes[0];
this.blendDst = blendModes[1];
this.blendSrc = blendModes[0];
this.blendDst = blendModes[1];
this.ok = true;
}
else if ("SND".equals(this.type)) {
// Only load sounds if audio is enabled.
// This is mostly to save on bandwidth and loading time, especially when loading
// full maps.
if (viewer.audioEnabled) {
final MappedData animSounds = (MappedData) tables.get(1).data;
this.ok = true;
} else if ("SND".equals(this.type)) {
// Only load sounds if audio is enabled.
// This is mostly to save on bandwidth and loading time, especially when loading
// full maps.
if (viewer.audioEnabled) {
final MappedData animSounds = (MappedData) tables.get(1).data;
final MappedDataRow animSoundsRow = animSounds.getRow((String) row.get("SoundLabel"));
final MappedDataRow animSoundsRow = animSounds.getRow((String) row.get("SoundLabel"));
if (animSoundsRow != null) {
this.distanceCutoff = getFloat(animSoundsRow, "DistanceCutoff");
this.maxDistance = getFloat(animSoundsRow, "MaxDistance");
this.minDistance = getFloat(animSoundsRow, "MinDistance");
this.pitch = getFloat(animSoundsRow, "Pitch");
this.pitchVariance = getFloat(animSoundsRow, "PitchVariance");
this.volume = getFloat(animSoundsRow, "Volume");
if (animSoundsRow != null) {
this.distanceCutoff = getFloat(animSoundsRow, "DistanceCutoff");
this.maxDistance = getFloat(animSoundsRow, "MaxDistance");
this.minDistance = getFloat(animSoundsRow, "MinDistance");
this.pitch = getFloat(animSoundsRow, "Pitch");
this.pitchVariance = getFloat(animSoundsRow, "PitchVariance");
this.volume = getFloat(animSoundsRow, "Volume");
final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(",");
final GenericResource[] resources = new GenericResource[fileNames.length];
for (int i = 0; i < fileNames.length; i++) {
final String path = ((String) animSoundsRow.get("DirectoryBase")) + fileNames[i];
try {
final String pathString = pathSolver.solve(path, model.solverParams).finalSrc;
final GenericResource genericResource = viewer.loadGeneric(pathString,
FetchDataTypeName.ARRAY_BUFFER, new LoadGenericSoundCallback(pathString));
if (genericResource == null) {
System.err.println("Null sound: " + fileNames[i]);
}
resources[i] = genericResource;
}
catch (final Exception exc) {
System.err.println("Failed to load sound: " + path);
exc.printStackTrace();
}
}
final String[] fileNames = ((String) animSoundsRow.get("FileNames")).split(",");
final GenericResource[] resources = new GenericResource[fileNames.length];
for (int i = 0; i < fileNames.length; i++) {
final String path = ((String) animSoundsRow.get("DirectoryBase")) + fileNames[i];
try {
final String pathString = pathSolver.solve(path, model.solverParams).finalSrc;
final GenericResource genericResource = viewer.loadGeneric(pathString,
FetchDataTypeName.ARRAY_BUFFER, new LoadGenericSoundCallback(pathString));
if (genericResource == null) {
System.err.println("Null sound: " + fileNames[i]);
}
resources[i] = genericResource;
} catch (final Exception exc) {
System.err.println("Failed to load sound: " + path);
exc.printStackTrace();
}
}
// TODO JS async removed
for (final GenericResource resource : resources) {
if (resource != null) {
this.decodedBuffers.add((Sound) resource.data);
}
}
this.ok = true;
}
}
}
else {
System.err.println("Unknown event object type: " + this.type + this.id);
}
}
else {
System.err.println("Unknown event object ID: " + this.type + this.id);
}
}
// TODO JS async removed
for (final GenericResource resource : resources) {
if (resource != null) {
this.decodedBuffers.add((Sound) resource.data);
}
}
this.ok = true;
}
}
} else {
System.err.println("Unknown event object type: " + this.type + this.id);
}
} else {
System.err.println("Unknown event object ID: " + this.type + this.id);
}
}
public int getValue(final long[] out, final MdxComplexInstance instance) {
if (this.globalSequence != -1) {
public int getValue(final long[] out, final MdxComplexInstance instance) {
if (this.globalSequence != -1) {
return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence);
}
else if (instance.sequence != -1) {
final long[] interval = this.model.getSequences().get(instance.sequence).getInterval();
return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence);
} else if (instance.sequence != -1) {
final long[] interval = this.model.getSequences().get(instance.sequence).getInterval();
return this.getValueAtTime(out, instance.frame, interval[0], interval[1]);
}
else {
out[0] = this.defval[0];
return this.getValueAtTime(out, instance.frame, interval[0], interval[1]);
} else {
out[0] = this.defval[0];
return -1;
}
}
return -1;
}
}
public int getValueAtTime(final long[] out, final long frame, final long start, final long end) {
if ((frame >= start) && (frame <= end)) {
for (int i = this.keyFrames.length - 1; i > -1; i--) {
if (this.keyFrames[i] < start) {
out[0] = 0;
public int getValueAtTime(final long[] out, final long frame, final long start, final long end) {
if ((frame >= start) && (frame <= end)) {
for (int i = this.keyFrames.length - 1; i > -1; i--) {
if (this.keyFrames[i] < start) {
out[0] = 0;
return i;
}
else if (this.keyFrames[i] <= frame) {
out[0] = 1;
return i;
} else if (this.keyFrames[i] <= frame) {
out[0] = 1;
return i;
}
}
}
return i;
}
}
}
out[0] = 0;
out[0] = 0;
return -1;
}
return -1;
}
@Override
public boolean ok() {
return this.ok;
}
@Override
public boolean ok() {
return this.ok;
}
@Override
public int getGeometryEmitterType() {
return this.geometryEmitterType;
}
@Override
public int getGeometryEmitterType() {
return this.geometryEmitterType;
}
}

View File

@ -32,14 +32,14 @@ public class MdxHandler extends ModelHandler {
Shaders.extendedShadowMap = viewer.webGL.createShaderProgram(
"#define EXTENDED_BONES\r\n" + MdxShaders.vsComplex, MdxShaders.fsComplexShadowMap);
Shaders.particles = viewer.webGL.createShaderProgram(MdxShaders.vsParticles, MdxShaders.fsParticles);
Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
//Shaders.simple = viewer.webGL.createShaderProgram(MdxShaders.vsSimple, MdxShaders.fsSimple);
// Shaders.hd = viewer.webGL.createShaderProgram(MdxShaders.vsHd, MdxShaders.fsHd);
// TODO HD reforged
// If a shader failed to compile, don't allow the handler to be registered, and
// send an error instead.
return Shaders.complex.isCompiled() && Shaders.extended.isCompiled() && Shaders.particles.isCompiled()
&& Shaders.simple.isCompiled() /* && Shaders.hd.isCompiled() */;
/* && Shaders.simple.isCompiled() && Shaders.hd.isCompiled() */;
}
@Override

View File

@ -152,6 +152,9 @@ public final class SdSequence<TYPE> {
}
private TYPE[] fixTimelineArray(final Timeline<TYPE> timeline, final TYPE[] values) {
if(values == null) {
return null;
}
if (timeline.getName().equals(AnimationMap.KLAC.getWar3id())
|| timeline.getName().equals(AnimationMap.KLBC.getWar3id())) {
final float[][] flippedColorData = new float[values.length][3];

View File

@ -2,25 +2,38 @@ package com.etheller.warsmash.viewer5.handlers.w3x;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
public class RenderDestructable extends RenderDoodad {
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
private static final War3ID TEX_ID = War3ID.fromString("btxi");
private final float life;
private final float life;
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) {
super(map, model, row, doodad, type, maxPitch, maxRoll);
this.life = life;
}
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) {
super(map, model, row, doodad, type, maxPitch, maxRoll);
this.life = life;
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
int dotIndex = replaceableTextureFile.lastIndexOf('.');
if (dotIndex != -1) {
replaceableTextureFile = replaceableTextureFile.substring(0, dotIndex);
}
replaceableTextureFile += ".blp";
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
}
}
@Override
public PrimaryTag getAnimation() {
if (this.life <= 0) {
return PrimaryTag.DEATH;
}
return super.getAnimation();
}
@Override
public PrimaryTag getAnimation() {
if (this.life <= 0) {
return PrimaryTag.DEATH;
}
return super.getAnimation();
}
}

View File

@ -12,86 +12,72 @@ import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
public class RenderDoodad {
private static final int SAMPLE_RADIUS = 4;
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
private static final War3ID TEX_ID = War3ID.fromString("btxi");
public final ModelInstance instance;
private final MutableGameObject row;
private final float maxPitch;
private final float maxRoll;
private static final int SAMPLE_RADIUS = 4;
public final ModelInstance instance;
private final MutableGameObject row;
private final float maxPitch;
private final float maxRoll;
public RenderDoodad(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) {
this.maxPitch = maxPitch;
this.maxRoll = maxRoll;
final boolean isSimple = row.readSLKTagBoolean("lightweight");
ModelInstance instance;
public RenderDoodad(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) {
this.maxPitch = maxPitch;
this.maxRoll = maxRoll;
final boolean isSimple = row.readSLKTagBoolean("lightweight");
ModelInstance instance;
if (isSimple && false) {
instance = model.addInstance(1);
}
else {
instance = model.addInstance();
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
}
if (isSimple && false) {
instance = model.addInstance(1);
} else {
instance = model.addInstance();
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
}
instance.move(doodad.getLocation());
// TODO: the following pitch/roll system is a heuristic, and we probably want to
// revisit it later.
// Specifically, I was pretty convinced that whichever is applied first
// (pitch/roll) should be used to do a projection onto the already-tilted plane
// to find the angle used for the other of the two
// (instead of measuring down from an imaginary flat ground plane, as we do
// currently).
final float facingRadians = doodad.getAngle();
float pitch, roll;
final float x = doodad.getLocation()[0];
final float y = doodad.getLocation()[1];
final float pitchSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
final float pitchSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
final float pitchSampleBackwardX = x - (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
final float pitchSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
pitch = Math.max(-maxPitch, Math.min(maxPitch,
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, SAMPLE_RADIUS * 2)));
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
final float rollSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
final float rollSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
final float rollSampleBackwardX = x - (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
final float rollSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
roll = Math.max(-maxRoll, Math.min(maxRoll,
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, SAMPLE_RADIUS * 2)));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, facingRadians));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
instance.move(doodad.getLocation());
// TODO: the following pitch/roll system is a heuristic, and we probably want to
// revisit it later.
// Specifically, I was pretty convinced that whichever is applied first
// (pitch/roll) should be used to do a projection onto the already-tilted plane
// to find the angle used for the other of the two
// (instead of measuring down from an imaginary flat ground plane, as we do
// currently).
final float facingRadians = doodad.getAngle();
float pitch, roll;
final float x = doodad.getLocation()[0];
final float y = doodad.getLocation()[1];
final float pitchSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
final float pitchSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
final float pitchSampleBackwardX = x - (SAMPLE_RADIUS * (float) Math.cos(facingRadians));
final float pitchSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(facingRadians));
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
pitch = Math.max(-maxPitch, Math.min(maxPitch,
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, SAMPLE_RADIUS * 2)));
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
final float rollSampleForwardX = x + (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
final float rollSampleForwardY = y + (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
final float rollSampleBackwardX = x - (SAMPLE_RADIUS * (float) Math.cos(leftOfFacingAngle));
final float rollSampleBackwardY = y - (SAMPLE_RADIUS * (float) Math.sin(leftOfFacingAngle));
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
roll = Math.max(-maxRoll, Math.min(maxRoll,
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, SAMPLE_RADIUS * 2)));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, facingRadians));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
// instance.rotate(new Quaternion().setEulerAnglesRad(facingRadians, 0, 0));
instance.scale(doodad.getScale());
if (type == WorldEditorDataType.DOODADS) {
final float defScale = row.readSLKTagFloat("defScale");
instance.uniformScale(defScale);
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
// TODO destructables need to be their own type, game simulation, etc
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
if (!replaceableTextureFile.toLowerCase().endsWith(".blp")) {
replaceableTextureFile += ".blp";
}
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
}
}
instance.setScene(map.worldScene);
instance.scale(doodad.getScale());
if (type == WorldEditorDataType.DOODADS) {
final float defScale = row.readSLKTagFloat("defScale");
instance.uniformScale(defScale);
}
instance.setScene(map.worldScene);
this.instance = instance;
this.row = row;
}
this.instance = instance;
this.row = row;
}
public PrimaryTag getAnimation() {
return PrimaryTag.STAND;
}
public PrimaryTag getAnimation() {
return PrimaryTag.STAND;
}
}

View File

@ -26,6 +26,7 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.math.collision.Ray;
import com.etheller.warsmash.common.FetchDataTypeName;
import com.etheller.warsmash.common.LoadGenericCallback;
@ -48,6 +49,8 @@ import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
import com.etheller.warsmash.util.MappedData;
import com.etheller.warsmash.util.Quadtree;
import com.etheller.warsmash.util.QuadtreeIntersector;
import com.etheller.warsmash.util.RenderMathUtils;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
@ -85,12 +88,14 @@ 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.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.CUnitAttackMissile;
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 mpq.MPQArchive;
@ -117,10 +122,11 @@ public class War3MapViewer extends ModelViewer {
private static final War3ID sloc = War3ID.fromString("sloc");
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
private static final float[] rayHeap = new float[6];
private static final Ray gdxRayHeap = new Ray();
public static final Ray gdxRayHeap = new Ray();
private static final Vector2 mousePosHeap = new Vector2();
private static final Vector3 normalHeap = new Vector3();
private static final Vector3 intersectionHeap = new Vector3();
public static final Vector3 intersectionHeap = new Vector3();
private static final Rectangle rectangleHeap = new Rectangle();
public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation();
public PathSolver wc3PathSolver = PathSolver.DEFAULT;
@ -185,6 +191,11 @@ public class War3MapViewer extends ModelViewer {
private GameUI gameUI;
private Vector3 lightDirection;
private Quadtree<MdxComplexInstance> walkableObjectsTree;
private final QuadtreeIntersectorFindsWalkableRenderHeight walkablesIntersector = new QuadtreeIntersectorFindsWalkableRenderHeight();
private final QuadtreeIntersectorFindsHitPoint walkablesIntersectionFinder = new QuadtreeIntersectorFindsHitPoint();
private final QuadtreeIntersectorFindsHighestWalkable intersectorFindsHighestWalkable = new QuadtreeIntersectorFindsHighestWalkable();
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
super(dataSource, canvas);
this.gameDataSource = dataSource;
@ -501,10 +512,9 @@ public class War3MapViewer extends ModelViewer {
War3MapViewer.this.units.remove(renderUnit);
War3MapViewer.this.worldScene.removeInstance(renderUnit.instance);
}
}, this.terrain.pathingGrid,
new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128),
this.seededRandom, w3iFile.getPlayers());
}, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers());
this.walkableObjectsTree = new Quadtree<>(this.terrain.getEntireMap());
if (this.doodadsAndDestructiblesLoaded) {
this.loadDoodadsAndDestructibles(this.allObjectData);
}
@ -632,8 +642,20 @@ public class War3MapViewer extends ModelViewer {
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
this.doodads.add(new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll,
doodad.getLife()));
final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type,
maxPitch, maxRoll, doodad.getLife());
if (row.readSLKTagBoolean("walkable")) {
final float x = doodad.getLocation()[0];
final float y = doodad.getLocation()[1];
final BoundingBox boundingBox = model.bounds.getBoundingBox();
final float minX = boundingBox.min.x + x;
final float minY = boundingBox.min.y + y;
final Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(),
boundingBox.getHeight());
this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance,
renderDestructableBounds);
}
this.doodads.add(renderDestructable);
}
else {
this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll));
@ -884,7 +906,7 @@ public class War3MapViewer extends ModelViewer {
}
else {
this.items.add(new RenderItem(this, model, row, unit, soundset, portraitModel)); // TODO store
// somewhere
// somewhere
if (unitShadowSplat != null) {
unitShadowSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
@ -1080,6 +1102,15 @@ public class War3MapViewer extends ModelViewer {
gdxRayHeap.direction.nor();// needed for libgdx
RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
this.terrain.softwareGroundMesh.indices, 3, out);
rectangleHeap.set(Math.min(out.x, gdxRayHeap.origin.x), Math.min(out.y, gdxRayHeap.origin.y),
Math.abs(out.x - gdxRayHeap.origin.x), Math.abs(out.y - gdxRayHeap.origin.y));
this.walkableObjectsTree.intersect(rectangleHeap, this.walkablesIntersectionFinder.reset(gdxRayHeap));
if (this.walkablesIntersectionFinder.found) {
out.set(this.walkablesIntersectionFinder.intersection);
}
else {
out.z = Math.max(getWalkableRenderHeight(out.x, out.y), this.terrain.getGroundHeight(out.x, out.y));
}
}
public void showConfirmation(final Vector3 position, final float red, final float green, final float blue) {
@ -1105,9 +1136,11 @@ public class War3MapViewer extends ModelViewer {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera)
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap,
unit.getSimulationUnit().getUnitType().isBuilding())
unit.getSimulationUnit().getUnitType().isBuilding(), false)
&& !unit.getSimulationUnit().isDead()) {
entity = unit;
if ((entity == null) || (entity.instance.depth > instance.depth)) {
entity = unit;
}
}
}
List<RenderUnit> sel;
@ -1148,7 +1181,7 @@ public class War3MapViewer extends ModelViewer {
for (final RenderUnit unit : this.units) {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap,
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding())) {
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
if (filter.call(unit.getSimulationUnit())) {
if ((entity == null) || (entity.instance.depth > instance.depth)) {
entity = unit;
@ -1242,6 +1275,41 @@ public class War3MapViewer extends ModelViewer {
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) {
@ -1348,4 +1416,82 @@ public class War3MapViewer extends ModelViewer {
public AbilityDataUI getAbilityDataUI() {
return this.abilityDataUI;
}
public float getWalkableRenderHeight(final float x, final float y) {
this.walkableObjectsTree.intersect(x, y, this.walkablesIntersector.reset(x, y));
return this.walkablesIntersector.z;
}
public MdxComplexInstance getHighestWalkableUnder(final float x, final float y) {
this.walkableObjectsTree.intersect(x, y, this.intersectorFindsHighestWalkable.reset(x, y));
return this.intersectorFindsHighestWalkable.highestInstance;
}
private static final class QuadtreeIntersectorFindsWalkableRenderHeight
implements QuadtreeIntersector<MdxComplexInstance> {
private float z;
private final Ray ray = new Ray();
private final Vector3 intersection = new Vector3();
private QuadtreeIntersectorFindsWalkableRenderHeight reset(final float x, final float y) {
this.z = -Float.MAX_VALUE;
this.ray.set(x, y, 4096, 0, 0, -8192);
return this;
}
@Override
public boolean onIntersect(final MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) {
this.z = Math.max(this.z, this.intersection.z);
}
return false;
}
}
private static final class QuadtreeIntersectorFindsHighestWalkable
implements QuadtreeIntersector<MdxComplexInstance> {
private float z;
private final Ray ray = new Ray();
private final Vector3 intersection = new Vector3();
private MdxComplexInstance highestInstance;
private QuadtreeIntersectorFindsHighestWalkable reset(final float x, final float y) {
this.z = -Float.MAX_VALUE;
this.ray.set(x, y, 4096, 0, 0, -8192);
this.highestInstance = null;
return this;
}
@Override
public boolean onIntersect(final MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) {
if (this.intersection.z > this.z) {
this.z = this.intersection.z;
this.highestInstance = intersectingObject;
}
}
return false;
}
}
private static final class QuadtreeIntersectorFindsHitPoint implements QuadtreeIntersector<MdxComplexInstance> {
private Ray ray;
private final Vector3 intersection = new Vector3();
private boolean found;
private QuadtreeIntersectorFindsHitPoint reset(final Ray ray) {
this.ray = ray;
this.found = false;
return this;
}
@Override
public boolean onIntersect(final MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) {
this.found = true;
return true;
}
return false;
}
}
}

View File

@ -411,6 +411,7 @@ public class Terrain {
this.shaderMapBoundsRectangle = new Rectangle(this.shaderMapBounds[0], this.shaderMapBounds[1],
this.shaderMapBounds[2] - this.shaderMapBounds[0], this.shaderMapBounds[3] - this.shaderMapBounds[1]);
this.mapSize = w3eFile.getMapSize();
this.entireMapRectangle = new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128);
this.softwareGroundMesh = new SoftwareGroundMesh(this.groundHeights, this.groundCornerHeights,
this.centerOffset, width, height);
@ -1019,15 +1020,14 @@ public class Terrain {
gl.glUniform1i(6, (int) this.waterIndex);
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetX"), this.centerOffset[0]);
gl.glUniform1f(this.waterShader.getUniformLocation("centerOffsetY"), this.centerOffset[1]);
gl.glUniform4fv(9, 1, this.shaderMapBounds, 0);
gl.glUniform4fv(11, 1, this.shaderMapBounds, 0);
final W3xSceneLightManager lightManager = (W3xSceneLightManager) this.viewer.worldScene.getLightManager();
final DataTexture unitLightsTexture = lightManager.getTerrainLightsTexture();
final DataTexture terrainLightsTexture = lightManager.getTerrainLightsTexture();
unitLightsTexture.bind(21);
gl.glUniform1i(this.waterShader.getUniformLocation("lightTexture"), 21);
gl.glUniform1f(this.waterShader.getUniformLocation("lightCount"), lightManager.getTerrainLightCount());
gl.glUniform1f(this.waterShader.getUniformLocation("lightCountHeight"), unitLightsTexture.getHeight());
terrainLightsTexture.bind(3);
gl.glUniform1f(9, lightManager.getTerrainLightCount());
gl.glUniform1f(10, terrainLightsTexture.getHeight());
gl.glActiveTexture(GL30.GL_TEXTURE0);
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterHeight);
@ -1035,7 +1035,7 @@ public class Terrain {
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.groundCornerHeight);
gl.glActiveTexture(GL30.GL_TEXTURE2);
gl.glBindTexture(GL30.GL_TEXTURE_2D, this.waterExists);
gl.glActiveTexture(GL30.GL_TEXTURE3);
gl.glActiveTexture(GL30.GL_TEXTURE4);
gl.glBindTexture(GL30.GL_TEXTURE_2D_ARRAY, this.waterTextureArray);
gl.glBindBuffer(GL30.GL_ARRAY_BUFFER, Shapes.INSTANCE.vertexBuffer);
@ -1239,6 +1239,7 @@ public class Terrain {
private final WaveBuilder waveBuilder;
public PathingGrid pathingGrid;
private final Rectangle shaderMapBoundsRectangle;
private final Rectangle entireMapRectangle;
private static final class UnloadedTexture {
private final int width;
@ -1399,4 +1400,8 @@ public class Terrain {
public Rectangle getPlayableMapArea() {
return this.shaderMapBoundsRectangle;
}
public Rectangle getEntireMap() {
return entireMapRectangle;
}
}

View File

@ -428,9 +428,9 @@ public class TerrainShaders {
"layout (location = 3) uniform vec4 deep_color_min;\r\n" + //
"layout (location = 4) uniform vec4 deep_color_max;\r\n" + //
"layout (location = 5) uniform float water_offset;\r\n" + //
"layout (location = 10) uniform sampler2D lightTexture;\r\n" + //
"layout (location = 11) uniform float lightCount;\r\n" + //
"layout (location = 12) uniform float lightTextureHeight;\r\n" + //
"layout (binding = 3) uniform sampler2D lightTexture;\r\n" + //
"layout (location = 9) uniform float lightCount;\r\n" + //
"layout (location = 10) uniform float lightTextureHeight;\r\n" + //
"\r\n" + //
"out vec2 UV;\r\n" + //
"out vec4 Color;\r\n" + //
@ -477,12 +477,12 @@ public class TerrainShaders {
public static final String frag = "#version 450 core\r\n" + //
"\r\n" + //
"layout (binding = 3) uniform sampler2DArray water_textures;\r\n" + //
"layout (binding = 4) uniform sampler2DArray water_textures;\r\n" + //
"layout (binding = 2) uniform sampler2D water_exists_texture;\r\n" + //
"\r\n" + //
"\r\n" + //
"layout (location = 6) uniform int current_texture;\r\n" + //
"layout (location = 9) uniform vec4 mapBounds;\r\n" + //
"layout (location = 11) uniform vec4 mapBounds;\r\n" + //
"\r\n" + //
"in vec2 UV;\r\n" + //
"in vec4 Color;\r\n" + //

View File

@ -24,6 +24,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.Moveme
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons.CommandButtonListener;
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.abilities.CAbility;
@ -131,10 +132,11 @@ public class RenderUnit {
}
public void populateCommandCard(final CommandButtonListener commandButtonListener,
public void populateCommandCard(final CSimulation game, final CommandButtonListener commandButtonListener,
final AbilityDataUI abilityDataUI) {
for (final CAbility ability : this.simulationUnit.getAbilities()) {
ability.visit(CommandCardPopulatingAbilityVisitor.INSTANCE.reset(commandButtonListener, abilityDataUI));
ability.visit(CommandCardPopulatingAbilityVisitor.INSTANCE.reset(game, this.simulationUnit,
commandButtonListener, abilityDataUI));
}
}
@ -166,15 +168,43 @@ public class RenderUnit {
final float groundHeight;
final MovementType movementType = this.simulationUnit.getUnitType().getMovementType();
final short terrainPathing = map.terrain.pathingGrid.getPathing(x, y);
final boolean swimming = (movementType == MovementType.AMPHIBIOUS)
boolean swimming = (movementType == MovementType.AMPHIBIOUS)
&& PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.SWIMMABLE)
&& !PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.WALKABLE);
if ((swimming) || (movementType == MovementType.FLOAT) || (movementType == MovementType.FLY)
|| (movementType == MovementType.HOVER)) {
groundHeight = Math.max(map.terrain.getGroundHeight(x, y), map.terrain.getWaterHeight(x, y));
final float groundHeightTerrain = map.terrain.getGroundHeight(x, y);
float groundHeightTerrainAndWater;
MdxComplexInstance currentWalkableUnder;
final boolean standingOnWater = (swimming) || (movementType == MovementType.FLOAT)
|| (movementType == MovementType.FLY) || (movementType == MovementType.HOVER);
if (standingOnWater) {
groundHeightTerrainAndWater = Math.max(groundHeightTerrain, map.terrain.getWaterHeight(x, y));
}
else {
groundHeight = map.terrain.getGroundHeight(x, y);
// land units will have their feet pass under the surface of the water
groundHeightTerrainAndWater = groundHeightTerrain;
}
if (movementType == MovementType.FLOAT) {
// boats cant go on bridges
groundHeight = groundHeightTerrainAndWater;
currentWalkableUnder = null;
}
else {
currentWalkableUnder = map.getHighestWalkableUnder(x, y);
if (currentWalkableUnder != null) {
System.out.println("WALKABLE UNDER");
}
War3MapViewer.gdxRayHeap.set(x, y, 4096, 0, 0, -8192);
if ((currentWalkableUnder != null)
&& currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap,
War3MapViewer.intersectionHeap, true, true)
&& (War3MapViewer.intersectionHeap.z > groundHeightTerrainAndWater)) {
groundHeight = War3MapViewer.intersectionHeap.z;
swimming = false; // Naga Royal Guard should slither across a bridge, not swim in rock
}
else {
groundHeight = groundHeightTerrainAndWater;
currentWalkableUnder = null;
}
}
if (swimming && !this.swimming) {
this.unitAnimationListenerImpl.addSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
@ -263,17 +293,49 @@ public class RenderUnit {
final float pitchSampleForwardY = y + (sampleRadius * (float) Math.sin(facingRadians));
final float pitchSampleBackwardX = x - (sampleRadius * (float) Math.cos(facingRadians));
final float pitchSampleBackwardY = y - (sampleRadius * (float) Math.sin(facingRadians));
final float pitchSampleGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
final float pitchSampleGorundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
pitch = Math.max(-maxPitch, Math.min(maxPitch,
(float) Math.atan2(pitchSampleGorundHeight2 - pitchSampleGroundHeight1, sampleRadius * 2)));
final double leftOfFacingAngle = facingRadians + (Math.PI / 2);
final float rollSampleForwardX = x + (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleForwardY = y + (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleBackwardX = x - (sampleRadius * (float) Math.cos(leftOfFacingAngle));
final float rollSampleBackwardY = y - (sampleRadius * (float) Math.sin(leftOfFacingAngle));
final float rollSampleGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
final float rollSampleGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
final float pitchSampleGroundHeight1;
final float pitchSampleGroundHeight2;
final float rollSampleGroundHeight1;
final float rollSampleGroundHeight2;
if (currentWalkableUnder != null) {
pitchSampleGroundHeight1 = getGroundHeightSample(groundHeight, currentWalkableUnder, pitchSampleBackwardX,
pitchSampleBackwardY);
pitchSampleGroundHeight2 = getGroundHeightSample(groundHeight, currentWalkableUnder, pitchSampleForwardX,
pitchSampleForwardY);
rollSampleGroundHeight1 = getGroundHeightSample(groundHeight, currentWalkableUnder, rollSampleBackwardX,
rollSampleBackwardY);
rollSampleGroundHeight2 = getGroundHeightSample(groundHeight, currentWalkableUnder, rollSampleForwardX,
rollSampleForwardY);
}
else {
final float pitchGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
final float pitchGroundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
final float rollGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
final float rollGroundHeight2 = map.terrain.getGroundHeight(rollSampleForwardX, rollSampleForwardY);
if (standingOnWater) {
pitchSampleGroundHeight1 = Math.max(pitchGroundHeight1,
map.terrain.getWaterHeight(pitchSampleBackwardX, pitchSampleBackwardY));
pitchSampleGroundHeight2 = Math.max(pitchGroundHeight2,
map.terrain.getWaterHeight(pitchSampleForwardX, pitchSampleForwardY));
rollSampleGroundHeight1 = Math.max(rollGroundHeight1,
map.terrain.getWaterHeight(rollSampleBackwardX, rollSampleBackwardY));
rollSampleGroundHeight2 = Math.max(rollGroundHeight2,
map.terrain.getWaterHeight(rollSampleForwardX, rollSampleForwardY));
}
else {
pitchSampleGroundHeight1 = pitchGroundHeight1;
pitchSampleGroundHeight2 = pitchGroundHeight2;
rollSampleGroundHeight1 = rollGroundHeight1;
rollSampleGroundHeight2 = rollGroundHeight2;
}
}
pitch = Math.max(-maxPitch, Math.min(maxPitch,
(float) Math.atan2(pitchSampleGroundHeight2 - pitchSampleGroundHeight1, sampleRadius * 2)));
roll = Math.max(-maxRoll, Math.min(maxRoll,
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, sampleRadius * 2)));
this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
@ -289,6 +351,21 @@ public class RenderUnit {
this.unitAnimationListenerImpl.update();
}
private float getGroundHeightSample(final float groundHeight, final MdxComplexInstance currentWalkableUnder,
final float sampleX, final float sampleY) {
final float sampleGroundHeight;
War3MapViewer.gdxRayHeap.origin.x = sampleX;
War3MapViewer.gdxRayHeap.origin.y = sampleY;
if (currentWalkableUnder.intersectRayWithCollision(War3MapViewer.gdxRayHeap, War3MapViewer.intersectionHeap,
true, true)) {
sampleGroundHeight = War3MapViewer.intersectionHeap.z;
}
else {
sampleGroundHeight = groundHeight;
}
return sampleGroundHeight;
}
public CUnit getSimulationUnit() {
return this.simulationUnit;
}

View File

@ -34,5 +34,6 @@ public interface CommandButtonListener {
// int getButtonPositionY();
//
// 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);
}

View File

@ -2,20 +2,29 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim.commandbuttons;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.AbilityDataUI;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.ability.IconUI;
import com.etheller.warsmash.viewer5.handlers.w3x.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.abilities.CAbilityAttack;
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.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver;
public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void> {
public static final CommandCardPopulatingAbilityVisitor INSTANCE = new CommandCardPopulatingAbilityVisitor();
private CSimulation game;
private CUnit unit;
private CommandButtonListener commandButtonListener;
private AbilityDataUI abilityDataUI;
private boolean hasStop;
public CommandCardPopulatingAbilityVisitor reset(final CommandButtonListener commandButtonListener,
final AbilityDataUI abilityDataUI) {
public CommandCardPopulatingAbilityVisitor reset(final CSimulation game, final CUnit unit,
final CommandButtonListener commandButtonListener, final AbilityDataUI abilityDataUI) {
this.game = game;
this.unit = unit;
this.commandButtonListener = commandButtonListener;
this.abilityDataUI = abilityDataUI;
this.hasStop = false;
@ -24,28 +33,41 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
@Override
public Void accept(final CAbilityAttack ability) {
addCommandButton(this.abilityDataUI.getAttackUI(), ability.getHandleId(), OrderIds.attack);
addCommandButton(ability, this.abilityDataUI.getAttackUI(), ability.getHandleId(), OrderIds.attack);
if (!this.hasStop) {
this.hasStop = true;
addCommandButton(this.abilityDataUI.getStopUI(), -1, OrderIds.stop);
addCommandButton(null, this.abilityDataUI.getStopUI(), -1, OrderIds.stop);
}
return null;
}
@Override
public Void accept(final CAbilityMove ability) {
addCommandButton(this.abilityDataUI.getMoveUI(), ability.getHandleId(), OrderIds.move);
addCommandButton(this.abilityDataUI.getHoldPosUI(), ability.getHandleId(), OrderIds.holdposition);
addCommandButton(this.abilityDataUI.getPatrolUI(), ability.getHandleId(), OrderIds.patrol);
addCommandButton(ability, this.abilityDataUI.getMoveUI(), ability.getHandleId(), OrderIds.move);
addCommandButton(ability, this.abilityDataUI.getHoldPosUI(), ability.getHandleId(), OrderIds.holdposition);
addCommandButton(ability, this.abilityDataUI.getPatrolUI(), ability.getHandleId(), OrderIds.patrol);
if (!this.hasStop) {
this.hasStop = true;
addCommandButton(this.abilityDataUI.getStopUI(), -1, OrderIds.stop);
addCommandButton(null, this.abilityDataUI.getStopUI(), -1, OrderIds.stop);
}
return null;
}
private void addCommandButton(final IconUI iconUI, final int handleId, final int orderId) {
private void addCommandButton(final CAbility ability, final IconUI iconUI, final int handleId, final int orderId) {
final boolean active;
if (this.unit.getCurrentOrder() == null) {
active = (orderId == OrderIds.stop);
}
else {
if (ability == null) {
active = false;
}
else {
ability.checkCanUse(this.game, this.unit, orderId, BooleanAbilityActivationReceiver.INSTANCE);
active = BooleanAbilityActivationReceiver.INSTANCE.isOk();
}
}
this.commandButtonListener.commandButton(iconUI.getButtonPositionX(), iconUI.getButtonPositionY(),
iconUI.getIcon(), handleId, orderId);
iconUI.getIcon(), handleId, orderId, active);
}
}

View File

@ -17,7 +17,8 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
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.attacks.CUnitAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CAttackOrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorAttack;
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.players.CAllianceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
@ -37,8 +38,8 @@ public class CUnit extends CWidget {
private final List<CAbility> abilities = new ArrayList<>();
private COrder currentOrder;
private final Queue<COrder> orderQueue = new LinkedList<>();
private CBehavior currentOrder;
private final Queue<CBehavior> orderQueue = new LinkedList<>();
private final CUnitType unitType;
private Rectangle collisionRectangle;
@ -189,6 +190,7 @@ public class CUnit extends CWidget {
// 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
@ -222,7 +224,7 @@ public class CUnit extends CWidget {
return game.getGameplayConstants().getBoneDecayTime();
}
public void order(final COrder order, final boolean queue) {
public void order(final CBehavior order, final boolean queue) {
if (isDead()) {
return;
}
@ -231,10 +233,12 @@ public class CUnit extends CWidget {
}
else {
this.currentOrder = order;
this.orderQueue.clear();
}
this.stateNotifier.ordersChanged();
}
public COrder getCurrentOrder() {
public CBehavior getCurrentOrder() {
return this.currentOrder;
}
@ -390,7 +394,7 @@ public class CUnit extends CWidget {
CAllianceType.PASSIVE)) {
for (final CUnitAttack attack : this.unitType.getAttacks()) {
if (source.canBeTargetedBy(simulation, this, attack.getTargetsAllowed())) {
this.order(new CAttackOrder(this, attack, OrderIds.attack, source), false);
this.order(new CBehaviorAttack(this, attack, OrderIds.attack, source), false);
break;
}
}
@ -521,6 +525,10 @@ public class CUnit extends CWidget {
}
}
}
else {
System.err.println("No targeting because " + targetsAllowed + " does not contain all of "
+ this.unitType.getTargetedAs());
}
return false;
}
@ -550,7 +558,7 @@ public class CUnit extends CWidget {
if (this.source.canReach(unit, this.source.acquisitionRange)
&& unit.canBeTargetedBy(this.game, this.source, attack.getTargetsAllowed())
&& (this.source.distance(unit) >= this.source.getUnitType().getMinimumAttackRange())) {
this.source.order(new CAttackOrder(this.source, attack, OrderIds.attack, unit), false);
this.source.order(new CBehaviorAttack(this.source, attack, OrderIds.attack, unit), false);
return true;
}
}

View File

@ -5,6 +5,8 @@ import com.etheller.warsmash.util.SubscriberSetNotifier;
public interface CUnitStateListener {
void lifeChanged(); // hp (current) changes
void ordersChanged();
public static final class CUnitStateNotifier extends SubscriberSetNotifier<CUnitStateListener>
implements CUnitStateListener {
@Override
@ -13,5 +15,12 @@ public interface CUnitStateListener {
listener.lifeChanged();
}
}
@Override
public void ordersChanged() {
for (final CUnitStateListener listener : set) {
listener.ordersChanged();
}
}
}
}

View File

@ -17,4 +17,5 @@ public interface CAbility extends CAbilityView {
void onOrder(CSimulation game, CUnit caster, int orderId, Vector2 point, boolean queue);
void onOrderNoTarget(CSimulation game, CUnit caster, int orderId, boolean queue);
}

View File

@ -1,15 +1,17 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities;
import com.badlogic.gdx.math.Vector2;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
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.StringsToExternalizeLater;
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.orders.CAttackOrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CMoveOrder;
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.players.CAllianceType;
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.TargetType;
@ -30,6 +32,15 @@ public class CAbilityAttack implements CAbility {
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
final AbilityTargetCheckReceiver<CWidget> receiver) {
if (orderId == OrderIds.smart) {
if (target instanceof CUnit) {
if (game.getPlayer(unit.getPlayerIndex()).hasAlliance(((CUnit) target).getPlayerIndex(),
CAllianceType.PASSIVE)) {
receiver.orderIdNotAccepted();
return;
}
}
}
if ((orderId == OrderIds.smart) || (orderId == OrderIds.attack)) {
boolean canTarget = false;
for (final CUnitAttack attack : unit.getUnitType().getAttacks()) {
@ -54,7 +65,30 @@ public class CAbilityAttack implements CAbility {
@Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final Vector2 target,
final AbilityTargetCheckReceiver<Vector2> receiver) {
receiver.mustTargetType(TargetType.UNIT);
switch (orderId) {
case OrderIds.attack:
receiver.targetOk(target);
break;
case OrderIds.attackground:
boolean allowAttackGround = false;
for (final CUnitAttack attack : unit.getUnitType().getAttacks()) {
if ((attack.getWeaponType() == CWeaponType.ARTILLERY)
|| (attack.getWeaponType() == CWeaponType.ALINE)) {
allowAttackGround = true;
break;
}
}
if (allowAttackGround) {
receiver.targetOk(target);
}
else {
receiver.orderIdNotAccepted();
}
break;
default:
receiver.orderIdNotAccepted();
break;
}
}
@Override
@ -74,15 +108,15 @@ public class CAbilityAttack implements CAbility {
@Override
public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final CWidget target,
final boolean queue) {
COrder order = null;
CBehavior order = null;
for (final CUnitAttack attack : caster.getUnitType().getAttacks()) {
if (target.canBeTargetedBy(game, caster, attack.getTargetsAllowed())) {
order = new CAttackOrder(caster, attack, orderId, target);
order = new CBehaviorAttack(caster, attack, orderId, target);
break;
}
}
if (order == null) {
order = new CMoveOrder(caster, orderId, target.getX(), target.getY());
order = new CBehaviorMove(caster, orderId, target.getX(), target.getY());
}
caster.order(order, queue);
}

View File

@ -5,8 +5,8 @@ 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.StringsToExternalizeLater;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CMoveOrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CPatrolOrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.CBehaviorPatrol;
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;
@ -85,13 +85,13 @@ public class CAbilityMove implements CAbility {
@Override
public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final CWidget target,
final boolean queue) {
caster.order(new CPatrolOrder(caster, orderId, (CUnit) target), queue);
caster.order(new CBehaviorPatrol(caster, orderId, (CUnit) target), queue);
}
@Override
public void onOrder(final CSimulation game, final CUnit caster, final int orderId, final Vector2 target,
final boolean queue) {
caster.order(new CMoveOrder(caster, orderId, target.x, target.y), queue);
caster.order(new CBehaviorMove(caster, orderId, target.x, target.y), queue);
}
@Override

View File

@ -32,388 +32,396 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUni
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
public class CUnitData {
private static final War3ID MANA_INITIAL_AMOUNT = War3ID.fromString("umpi");
private static final War3ID MANA_MAXIMUM = War3ID.fromString("umpm");
private static final War3ID HIT_POINT_MAXIMUM = War3ID.fromString("uhpm");
private static final War3ID MOVEMENT_SPEED_BASE = War3ID.fromString("umvs");
private static final War3ID PROPULSION_WINDOW = War3ID.fromString("uprw");
private static final War3ID TURN_RATE = War3ID.fromString("umvr");
private static final War3ID IS_BLDG = War3ID.fromString("ubdg");
private static final War3ID NAME = War3ID.fromString("unam");
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_Z = War3ID.fromString("ulpz");
private static final War3ID ATTACKS_ENABLED = War3ID.fromString("uaen");
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_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_QUARTER_DMG = War3ID.fromString("ua1q");
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_COOLDOWN = War3ID.fromString("ua1c");
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_QUARTER = War3ID.fromString("uqd1");
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_SIDES_PER_DIE = War3ID.fromString("ua1s");
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_UPGRADE_AMT = War3ID.fromString("udu1");
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_MISSILE_ART = War3ID.fromString("ua1m");
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_RANGE = War3ID.fromString("ua1r");
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_TARGETS_ALLOWED = War3ID.fromString("ua1g");
private static final War3ID ATTACK1_WEAPON_SOUND = War3ID.fromString("ucs1");
private static final War3ID ATTACK1_WEAPON_TYPE = War3ID.fromString("ua1w");
private static final War3ID MANA_INITIAL_AMOUNT = War3ID.fromString("umpi");
private static final War3ID MANA_MAXIMUM = War3ID.fromString("umpm");
private static final War3ID HIT_POINT_MAXIMUM = War3ID.fromString("uhpm");
private static final War3ID MOVEMENT_SPEED_BASE = War3ID.fromString("umvs");
private static final War3ID PROPULSION_WINDOW = War3ID.fromString("uprw");
private static final War3ID TURN_RATE = War3ID.fromString("umvr");
private static final War3ID IS_BLDG = War3ID.fromString("ubdg");
private static final War3ID NAME = War3ID.fromString("unam");
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_Z = War3ID.fromString("ulpz");
private static final War3ID ATTACKS_ENABLED = War3ID.fromString("uaen");
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_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_QUARTER_DMG = War3ID.fromString("ua1q");
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_COOLDOWN = War3ID.fromString("ua1c");
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_QUARTER = War3ID.fromString("uqd1");
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_SIDES_PER_DIE = War3ID.fromString("ua1s");
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_UPGRADE_AMT = War3ID.fromString("udu1");
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_MISSILE_ART = War3ID.fromString("ua1m");
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_RANGE = War3ID.fromString("ua1r");
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_TARGETS_ALLOWED = War3ID.fromString("ua1g");
private static final War3ID ATTACK1_WEAPON_SOUND = War3ID.fromString("ucs1");
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_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_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_TARGETS = War3ID.fromString("ua2p");
private static final War3ID ATTACK2_ATTACK_TYPE = War3ID.fromString("ua2t");
private static final War3ID ATTACK2_COOLDOWN = War3ID.fromString("ua2c");
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_QUARTER = War3ID.fromString("uqd2");
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_SIDES_PER_DIE = War3ID.fromString("ua2s");
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_UPGRADE_AMT = War3ID.fromString("udu2");
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_MISSILE_ART = War3ID.fromString("ua2m");
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_RANGE = War3ID.fromString("ua2r");
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_TARGETS_ALLOWED = War3ID.fromString("ua2g");
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_BACKSWING_POINT = War3ID.fromString("ubs2");
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_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_TARGETS = War3ID.fromString("ua2p");
private static final War3ID ATTACK2_ATTACK_TYPE = War3ID.fromString("ua2t");
private static final War3ID ATTACK2_COOLDOWN = War3ID.fromString("ua2c");
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_QUARTER = War3ID.fromString("uqd2");
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_SIDES_PER_DIE = War3ID.fromString("ua2s");
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_UPGRADE_AMT = War3ID.fromString("udu2");
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_MISSILE_ART = War3ID.fromString("ua2m");
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_RANGE = War3ID.fromString("ua2r");
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_TARGETS_ALLOWED = War3ID.fromString("ua2g");
private static final War3ID ATTACK2_WEAPON_SOUND = War3ID.fromString("ucs2");
private static final War3ID ATTACK2_WEAPON_TYPE = War3ID.fromString("ua2w");
private static final War3ID ACQUISITION_RANGE = War3ID.fromString("uacq");
private static final War3ID MINIMUM_ATTACK_RANGE = War3ID.fromString("uamn");
private static final War3ID ACQUISITION_RANGE = War3ID.fromString("uacq");
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 ARMOR_TYPE = War3ID.fromString("uarm");
private static final War3ID DEATH_TYPE = War3ID.fromString("udea");
private static final War3ID ARMOR_TYPE = War3ID.fromString("uarm");
private static final War3ID DEFENSE = War3ID.fromString("udef");
private static final War3ID DEFENSE_TYPE = War3ID.fromString("udty");
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
private static final War3ID MOVE_TYPE = War3ID.fromString("umvt");
private static final War3ID COLLISION_SIZE = War3ID.fromString("ucol");
private static final War3ID CLASSIFICATION = War3ID.fromString("utyp");
private static final War3ID DEATH_TIME = War3ID.fromString("udtm");
private static final War3ID TARGETED_AS = War3ID.fromString("utar");
private final MutableObjectData unitData;
private final Map<War3ID, CUnitType> unitIdToUnitType = new HashMap<>();
private static final War3ID DEFENSE = War3ID.fromString("udef");
private static final War3ID DEFENSE_TYPE = War3ID.fromString("udty");
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
private static final War3ID MOVE_TYPE = War3ID.fromString("umvt");
private static final War3ID COLLISION_SIZE = War3ID.fromString("ucol");
private static final War3ID CLASSIFICATION = War3ID.fromString("utyp");
private static final War3ID DEATH_TIME = War3ID.fromString("udtm");
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) {
this.unitData = unitData;
}
public CUnitData(final MutableObjectData unitData) {
this.unitData = unitData;
}
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);
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 CUnitType unitTypeInstance = getUnitTypeInstance(typeId, buildingPathingPixelMap, unitType);
final CUnitType unitTypeInstance = getUnitTypeInstance(typeId, buildingPathingPixelMap, unitType);
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
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;
}
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
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 MutableGameObject unitType) {
CUnitType unitTypeInstance = this.unitIdToUnitType.get(typeId);
if (unitTypeInstance == null) {
final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0);
final String movetp = unitType.getFieldAsString(MOVE_TYPE, 0);
final float collisionSize = unitType.getFieldAsFloat(COLLISION_SIZE, 0);
final boolean isBldg = unitType.getFieldAsBoolean(IS_BLDG, 0);
final PathingGrid.MovementType movementType = PathingGrid.getMovementType(movetp);
final String unitName = unitType.getFieldAsString(NAME, 0);
final float acquisitionRange = unitType.getFieldAsFloat(ACQUISITION_RANGE, 0);
final float minimumAttackRange = unitType.getFieldAsFloat(MINIMUM_ATTACK_RANGE, 0);
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) {
// 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));
}
if ((attacksEnabled & 0x2) != 0) {
// 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));
}
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 CUnitType getUnitTypeInstance(final War3ID typeId, final BufferedImage buildingPathingPixelMap,
final MutableGameObject unitType) {
CUnitType unitTypeInstance = this.unitIdToUnitType.get(typeId);
if (unitTypeInstance == null) {
final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0);
final String movetp = unitType.getFieldAsString(MOVE_TYPE, 0);
final float collisionSize = unitType.getFieldAsFloat(COLLISION_SIZE, 0);
final boolean isBldg = unitType.getFieldAsBoolean(IS_BLDG, 0);
final PathingGrid.MovementType movementType = PathingGrid.getMovementType(movetp);
final String unitName = unitType.getFieldAsString(NAME, 0);
final float acquisitionRange = unitType.getFieldAsFloat(ACQUISITION_RANGE, 0);
final float minimumAttackRange = unitType.getFieldAsFloat(MINIMUM_ATTACK_RANGE, 0);
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,
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;
}
private CUnitAttack createAttack(final float animationBackswingPoint, final float animationDamagePoint,
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 getPropulsionWindow(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROPULSION_WINDOW, 0);
}
public float getPropulsionWindow(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROPULSION_WINDOW, 0);
}
public float getTurnRate(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(TURN_RATE, 0);
}
public float getTurnRate(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(TURN_RATE, 0);
}
public boolean isBuilding(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsBoolean(IS_BLDG, 0);
}
public boolean isBuilding(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsBoolean(IS_BLDG, 0);
}
public String getName(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getName();
}
public String getName(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getName();
}
public int getA1MinDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_BASE, 0)
+ this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_DICE, 0);
}
public int getA1MinDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_BASE, 0)
+ this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_DMG_DICE, 0);
}
public int getA1MaxDamage(final War3ID unitTypeId) {
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_SIDES_PER_DIE, 0));
}
public int getA1MaxDamage(final War3ID unitTypeId) {
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_SIDES_PER_DIE, 0));
}
public int getA2MinDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_BASE, 0)
+ this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_DICE, 0);
}
public int getA2MinDamage(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_BASE, 0)
+ this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_DMG_DICE, 0);
}
public int getA2MaxDamage(final War3ID unitTypeId) {
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 getA2MaxDamage(final War3ID unitTypeId) {
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 getDefense(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(DEFENSE, 0);
}
public int getDefense(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(DEFENSE, 0);
}
public int getA1ProjectileSpeed(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_PROJECTILE_SPEED, 0);
}
public int getA1ProjectileSpeed(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK1_PROJECTILE_SPEED, 0);
}
public float getA1ProjectileArc(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_PROJECTILE_ARC, 0);
}
public float getA1ProjectileArc(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_PROJECTILE_ARC, 0);
}
public int getA2ProjectileSpeed(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_PROJECTILE_SPEED, 0);
}
public int getA2ProjectileSpeed(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsInteger(ATTACK2_PROJECTILE_SPEED, 0);
}
public float getA2ProjectileArc(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_PROJECTILE_ARC, 0);
}
public float getA2ProjectileArc(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_PROJECTILE_ARC, 0);
}
public String getA1MissileArt(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsString(ATTACK1_MISSILE_ART, 0);
}
public String getA1MissileArt(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsString(ATTACK1_MISSILE_ART, 0);
}
public String getA2MissileArt(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsString(ATTACK2_MISSILE_ART, 0);
}
public String getA2MissileArt(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsString(ATTACK2_MISSILE_ART, 0);
}
public float getA1Cooldown(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_COOLDOWN, 0);
}
public float getA1Cooldown(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK1_COOLDOWN, 0);
}
public float getA2Cooldown(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_COOLDOWN, 0);
}
public float getA2Cooldown(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_COOLDOWN, 0);
}
public float getProjectileLaunchX(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_X, 0);
}
public float getProjectileLaunchX(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_X, 0);
}
public float getProjectileLaunchY(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_Y, 0);
}
public float getProjectileLaunchY(final War3ID unitTypeId) {
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);
}
public float getProjectileLaunchZ(final War3ID unitTypeId) {
return this.unitData.get(unitTypeId).getFieldAsFloat(PROJECTILE_LAUNCH_Z, 0);
}
}

View File

@ -1,6 +1,8 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
public interface COrder {
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

View File

@ -3,13 +3,12 @@ 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.COrder;
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 CAttackOrder implements COrder {
public class CBehaviorAttack implements CBehavior {
private final CUnit unit;
private final int orderId;
private boolean wasWithinPropWindow = false;
@ -17,11 +16,11 @@ public class CAttackOrder implements COrder {
private final CWidget target;
private int damagePointLaunchTime;
private int backSwingTime;
private COrder moveOrder;
private CBehavior moveOrder;
private int thisOrderCooldownEndTime;
private boolean wasInRange = false;
public CAttackOrder(final CUnit unit, final CUnitAttack unitAttack, final int orderId, final CWidget target) {
public CBehaviorAttack(final CUnit unit, final CUnitAttack unitAttack, final int orderId, final CWidget target) {
this.unit = unit;
this.unitAttack = unitAttack;
this.orderId = orderId;
@ -32,10 +31,10 @@ public class CAttackOrder implements COrder {
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 CMoveOrder(unit, this.orderId, (CUnit) target);
this.moveOrder = new CBehaviorMove(unit, this.orderId, (CUnit) target);
}
else {
this.moveOrder = new CMoveOrder(unit, this.orderId, target.getX(), target.getY());
this.moveOrder = new CBehaviorMove(unit, this.orderId, target.getX(), target.getY());
}
}
else {
@ -50,7 +49,7 @@ public class CAttackOrder implements COrder {
return true;
}
float range = this.unitAttack.getRange();
if ((this.target instanceof CUnit) && (((CUnit) this.target).getCurrentOrder() instanceof CMoveOrder)
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
@ -138,7 +137,6 @@ public class CAttackOrder implements COrder {
else {
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
}
System.out.println(damage + " from " + minDamage + " to " + maxDamage);
this.unitAttack.launch(simulation, this.unit, this.target, damage);
this.damagePointLaunchTime = 0;
}

View File

@ -10,13 +10,12 @@ import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.MovementType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
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.CWorldCollision;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindingProcessor;
public class CMoveOrder implements COrder {
public class CBehaviorMove implements CBehavior {
private static final Rectangle tempRect = new Rectangle();
private final CUnit unit;
private final int orderId;
@ -27,7 +26,7 @@ public class CMoveOrder implements COrder {
private int searchCycles = 0;
private CUnit followUnit;
public CMoveOrder(final CUnit unit, final int orderId, final float targetX, final float targetY) {
public CBehaviorMove(final CUnit unit, final int orderId, final float targetX, final float targetY) {
this.unit = unit;
this.orderId = orderId;
this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners(
@ -36,7 +35,7 @@ public class CMoveOrder implements COrder {
this.target = new Point2D.Float(targetX, targetY);
}
public CMoveOrder(final CUnit unit, final int orderId, final CUnit followUnit) {
public CBehaviorMove(final CUnit unit, final int orderId, final CUnit followUnit) {
this.unit = unit;
this.orderId = orderId;
this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners(

View File

@ -0,0 +1,96 @@
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,12 +1,11 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
public class CDoNothingOrder implements COrder {
public class CBehaviorStop implements CBehavior {
private final int orderId;
public CDoNothingOrder(final int orderId) {
public CBehaviorStop(final int orderId) {
this.orderId = orderId;
}

View File

@ -1,58 +0,0 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
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 CPatrolOrder implements COrder {
private final CUnit unit;
private final int orderId;
private final CWidget target;
private COrder moveOrder;
public CPatrolOrder(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 CMoveOrder(unit, this.orderId, target);
}
else {
this.moveOrder = new CMoveOrder(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;
}
return false;
}
@Override
public int getOrderId() {
return this.orderId;
}
}

View File

@ -177,7 +177,8 @@ public class CPathfindingProcessor {
}
}
while (!openSet.isEmpty()) {
int searchIterations = 0;
while (!openSet.isEmpty() && searchIterations < 150000) {
Node current = openSet.poll();
if (isGoal(current)) {
final LinkedList<Point2D.Float> totalPath = new LinkedList<>();
@ -259,6 +260,7 @@ public class CPathfindingProcessor {
}
}
}
searchIterations++;
}
return Collections.emptyList();
}

View File

@ -83,7 +83,7 @@ public class CPlayerUnitOrderExecutor implements CPlayerUnitOrderListener {
if (this.abilityActivationReceiver.isUseOk()) {
final StringMsgTargetCheckReceiver<Void> targetReceiver = this.<Void>targetCheckReceiver();
ability.checkCanTargetNoTarget(this.game, unit, orderId, targetReceiver);
if (targetReceiver.getTarget() != null) {
if (targetReceiver.getMessage() == null) {
ability.onOrderNoTarget(this.game, unit, orderId, queue);
return true;
}

View File

@ -17,6 +17,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandCardCommandL
public class CommandCardIcon extends AbstractRenderableFrame {
private TextureFrame iconFrame;
private TextureFrame activeHighlightFrame;
private SpriteFrame cooldownFrame;
private SpriteFrame autocastFrame;
private CommandButton commandButton;
@ -30,8 +31,10 @@ public class CommandCardIcon extends AbstractRenderableFrame {
this.commandCardCommandListener = commandCardCommandListener;
}
public void set(final TextureFrame iconFrame, final SpriteFrame cooldownFrame, final SpriteFrame autocastFrame) {
public void set(final TextureFrame iconFrame, final TextureFrame activeHighlightFrame,
final SpriteFrame cooldownFrame, final SpriteFrame autocastFrame) {
this.iconFrame = iconFrame;
this.activeHighlightFrame = activeHighlightFrame;
this.cooldownFrame = cooldownFrame;
this.autocastFrame = autocastFrame;
}
@ -40,6 +43,7 @@ public class CommandCardIcon extends AbstractRenderableFrame {
this.commandButton = commandButton;
if (commandButton == null) {
this.iconFrame.setVisible(false);
this.activeHighlightFrame.setVisible(false);
this.cooldownFrame.setVisible(false);
this.autocastFrame.setVisible(false);
}
@ -64,8 +68,10 @@ public class CommandCardIcon extends AbstractRenderableFrame {
}
}
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) {
this.iconFrame.setVisible(true);
this.activeHighlightFrame.setVisible(active);
this.cooldownFrame.setVisible(false);
this.autocastFrame.setVisible(false);
this.iconFrame.setTexture(texture);
@ -76,6 +82,7 @@ public class CommandCardIcon extends AbstractRenderableFrame {
@Override
protected void innerPositionBounds(final Viewport viewport) {
this.iconFrame.positionBounds(viewport);
this.activeHighlightFrame.positionBounds(viewport);
this.cooldownFrame.positionBounds(viewport);
this.autocastFrame.positionBounds(viewport);
}
@ -83,6 +90,7 @@ public class CommandCardIcon extends AbstractRenderableFrame {
@Override
protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) {
this.iconFrame.render(batch, baseFont, glyphLayout);
this.activeHighlightFrame.render(batch, baseFont, glyphLayout);
this.cooldownFrame.render(batch, baseFont, glyphLayout);
this.autocastFrame.render(batch, baseFont, glyphLayout);
}

View File

@ -13,6 +13,8 @@ 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.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
@ -28,6 +30,7 @@ import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
import com.etheller.warsmash.util.FastNumberFormat;
import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.util.WarsmashConstants;
@ -135,6 +138,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
private int selectedSoundCount = 0;
private final ActiveCommandUnitTargetFilter activeCommandUnitTargetFilter;
private UIFrame clickUI = null;
public MeleeUI(final DataSource dataSource, final Viewport uiViewport, final FreeTypeFontGenerator fontGenerator,
final Scene uiScene, final Scene portraitScene, final CameraPreset[] cameraPresets,
final CameraRates cameraRates, final War3MapViewer war3MapViewer, final RootFrameListener rootFrameListener,
@ -305,6 +310,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.rootFrame.add(commandCardIcon);
final TextureFrame iconFrame = this.rootFrame.createTextureFrame(
"SmashCommandButton_" + (commandButtonIndex) + "_Icon", this.rootFrame, false, null);
final TextureFrame activeHighlightFrame = this.rootFrame.createTextureFrame(
"SmashCommandButton_" + (commandButtonIndex) + "_ActiveHighlight", this.rootFrame, true, null,
FilterMode.ADDALPHA);
final SpriteFrame cooldownFrame = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE",
"SmashCommandButton_" + (commandButtonIndex) + "_Cooldown", this.rootFrame, "", 0);
final SpriteFrame autocastFrame = (SpriteFrame) this.rootFrame.createFrameByType("SPRITE",
@ -318,6 +326,11 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
iconFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f));
iconFrame.setHeight(GameUI.convertY(this.uiViewport, 0.039f));
iconFrame.setTexture(ImageUtils.DEFAULT_ICON_PATH, this.rootFrame);
activeHighlightFrame
.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0));
activeHighlightFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f));
activeHighlightFrame.setHeight(GameUI.convertY(this.uiViewport, 0.039f));
activeHighlightFrame.setTexture("CommandButtonActiveHighlight", this.rootFrame);
cooldownFrame.addSetPoint(new SetPoint(FramePoint.CENTER, commandCardIcon, FramePoint.CENTER, 0, 0));
this.rootFrame.setSpriteFrameModel(cooldownFrame, this.rootFrame.getSkinField("CommandButtonCooldown"));
cooldownFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f));
@ -326,7 +339,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.rootFrame.setSpriteFrameModel(autocastFrame, this.rootFrame.getSkinField("CommandButtonAutocast"));
autocastFrame.setWidth(GameUI.convertX(this.uiViewport, 0.039f));
autocastFrame.setHeight(GameUI.convertY(this.uiViewport, 0.039f));
commandCardIcon.set(iconFrame, cooldownFrame, autocastFrame);
commandCardIcon.set(iconFrame, activeHighlightFrame, cooldownFrame, autocastFrame);
this.commandCard[j][i] = commandCardIcon;
commandCardIcon.setCommandButton(null);
commandButtonIndex++;
@ -408,7 +421,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
mouseX = Math.max(minX, Math.min(maxX, mouseX));
mouseY = Math.max(minY, Math.min(maxY, mouseY));
Gdx.input.setCursorPosition(mouseX, mouseY);
// Gdx.input.setCursorPosition(mouseX, mouseY);
screenCoordsVector.set(mouseX, mouseY);
this.uiViewport.unproject(screenCoordsVector);
@ -456,6 +469,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.cameraManager.updateCamera();
}
private final ShapeRenderer shapeRenderer = new ShapeRenderer();
public void render(final SpriteBatch batch, final BitmapFont font20, final GlyphLayout glyphLayout) {
this.rootFrame.render(batch, font20, glyphLayout);
if (this.selectedUnit != null) {
@ -466,6 +481,18 @@ 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());
if (this.clickUI != null) {
batch.end();
this.shapeRenderer.setProjectionMatrix(this.uiViewport.getCamera().combined);
this.shapeRenderer.begin(ShapeType.Line);
this.shapeRenderer.rect(this.clickUI.getFramePointX(FramePoint.LEFT),
this.clickUI.getFramePointY(FramePoint.BOTTOM),
this.clickUI.getFramePointX(FramePoint.RIGHT) - this.clickUI.getFramePointX(FramePoint.LEFT),
this.clickUI.getFramePointY(FramePoint.TOP) - this.clickUI.getFramePointY(FramePoint.BOTTOM));
this.shapeRenderer.end();
batch.begin();
}
}
public void portraitTalk() {
@ -477,8 +504,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
public boolean call(final CUnit unit) {
final BooleanAbilityTargetCheckReceiver<CWidget> targetReceiver = BooleanAbilityTargetCheckReceiver
.<CWidget>getInstance();
MeleeUI.this.activeCommand.checkCanTarget(MeleeUI.this.war3MapViewer.simulation, unit,
MeleeUI.this.activeCommandOrderId, unit, targetReceiver);
MeleeUI.this.activeCommand.checkCanTarget(MeleeUI.this.war3MapViewer.simulation,
MeleeUI.this.activeCommandUnit.getSimulationUnit(), MeleeUI.this.activeCommandOrderId, unit,
targetReceiver);
return targetReceiver.isTargetable();
}
}
@ -541,11 +569,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
this.portrait.setSelectedUnit(unit);
this.selectedUnit = unit;
for (int j = 0; j < COMMAND_CARD_HEIGHT; j++) {
for (int i = 0; i < COMMAND_CARD_WIDTH; i++) {
this.commandCard[j][i].setCommandButton(null);
}
}
clearCommandCard();
if (unit == null) {
this.simpleNameValue.setText("");
this.unitLifeText.setText("");
@ -633,16 +657,24 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
localArmorIconBackdrop.setTexture(defenseTexture);
localArmorInfoPanelIconValue.setText(Integer.toString(unit.getSimulationUnit().getDefense()));
unit.populateCommandCard(this, this.war3MapViewer.getAbilityDataUI());
unit.populateCommandCard(this.war3MapViewer.simulation, this, this.war3MapViewer.getAbilityDataUI());
}
}
private void clearCommandCard() {
for (int j = 0; j < COMMAND_CARD_HEIGHT; j++) {
for (int i = 0; i < COMMAND_CARD_WIDTH; i++) {
this.commandCard[j][i].setCommandButton(null);
}
}
}
@Override
public void commandButton(final int buttonPositionX, final int buttonPositionY, final Texture icon,
final int abilityHandleId, final int orderId) {
final int abilityHandleId, final int orderId, final boolean active) {
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));
this.commandCard[y][x].setCommandButtonData(icon, abilityHandleId, orderId);
this.commandCard[y][x].setCommandButtonData(icon, abilityHandleId, orderId, active);
}
public void resize(final Rectangle viewport) {
@ -732,6 +764,13 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
}
@Override
public void ordersChanged() {
clearCommandCard();
this.selectedUnit.populateCommandCard(this.war3MapViewer.simulation, this,
this.war3MapViewer.getAbilityDataUI());
}
public RenderUnit getSelectedUnit() {
return this.selectedUnit;
}
@ -748,6 +787,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.cameraManager.scrolled(amount);
}
private float lastX, lastY;
public boolean touchDown(final int screenX, final int screenY, final float worldScreenY, final int button) {
screenCoordsVector.set(screenX, screenY);
this.uiViewport.unproject(screenCoordsVector);
@ -756,6 +797,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
screenCoordsVector.y);
this.cameraManager.target.x = worldPoint.x;
this.cameraManager.target.y = worldPoint.y;
this.lastX = screenCoordsVector.x;
this.lastY = screenCoordsVector.y;
return true;
}
final UIFrame clickedUIFrame = this.rootFrame.touchDown(screenCoordsVector.x, screenCoordsVector.y, button);
@ -790,6 +833,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
else {
this.war3MapViewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY);
clickLocationTemp2.set(clickLocationTemp.x, clickLocationTemp.y);
this.activeCommand.checkCanTarget(this.war3MapViewer.simulation,
this.activeCommandUnit.getSimulationUnit(), this.activeCommandOrderId,
clickLocationTemp2, PointAbilityTargetCheckReceiver.INSTANCE);
@ -822,10 +867,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
else {
if (button == Input.Buttons.RIGHT) {
final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY);
if (getSelectedUnit() != null) {
if ((rayPickUnit != null) && (rayPickUnit.playerIndex != getSelectedUnit().playerIndex)
&& !rayPickUnit.getSimulationUnit().isDead()) {
final RenderUnit rayPickUnit = this.war3MapViewer.rayPickUnit(screenX, worldScreenY);
if ((rayPickUnit != null) && !rayPickUnit.getSimulationUnit().isDead()) {
boolean ordered = false;
for (final RenderUnit unit : this.war3MapViewer.selected) {
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
@ -929,10 +973,34 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
}
}
else {
this.clickUI = clickedUIFrame;
this.lastX = screenCoordsVector.x;
this.lastY = screenCoordsVector.y;
}
return false;
}
private static boolean isShiftDown() {
return Gdx.input.isKeyPressed(Input.Keys.SHIFT_LEFT) || Gdx.input.isKeyPressed(Input.Keys.SHIFT_RIGHT);
}
public boolean touchDragged(final int screenX, final int screenY, final float worldScreenY, final int pointer) {
screenCoordsVector.set(screenX, screenY);
this.uiViewport.unproject(screenCoordsVector);
final float dx = screenCoordsVector.x - this.lastX;
final float dy = screenCoordsVector.y - this.lastY;
if (this.meleeUIMinimap.containsMouse(screenCoordsVector.x, screenCoordsVector.y)) {
this.meleeUIMinimap.touchDragged(screenX, screenY, worldScreenY, pointer, dx, dy);
}
else if (this.clickUI != null) {
this.clickUI.setFramePointX(FramePoint.LEFT, this.clickUI.getFramePointX(FramePoint.LEFT) + dx);
this.clickUI.setFramePointY(FramePoint.BOTTOM, this.clickUI.getFramePointY(FramePoint.BOTTOM) + dy);
}
this.lastX = screenCoordsVector.x;
this.lastY = screenCoordsVector.y;
return false;
}
}

View File

@ -59,4 +59,12 @@ public class MeleeUIMinimap {
public boolean containsMouse(final float x, final float y) {
return this.minimapFilledArea.contains(x, y);
}
public void touchDragged(final int screenX, final int screenY, final float worldScreenY, final int pointer,
final float dx, final float dy) {
this.minimapFilledArea.x += dx;
this.minimapFilledArea.y += dy;
this.minimap.x += dx;
this.minimap.y += dy;
}
}

View File

@ -24,7 +24,7 @@ import com.etheller.warsmash.viewer5.gl.SoundLengthExtension;
import com.etheller.warsmash.viewer5.gl.WireframeExtension;
public class DesktopLauncher {
public static void main(final String[] arg) {
public static void main(String[] arg) {
Extensions.angleInstancedArrays = new ANGLEInstancedArrays() {
@Override
public void glVertexAttribDivisorANGLE(final int index, final int divisor) {
@ -83,10 +83,11 @@ public class DesktopLauncher {
config.useGL30 = true;
config.gles30ContextMajorVersion = 3;
config.gles30ContextMinorVersion = 3;
config.samples = 16;
//config.samples = 16;
// config.vSyncEnabled = false;
// config.foregroundFPS = 0;
// config.backgroundFPS = 0;
arg = new String[]{"-windowed"};
if ((arg.length > 0) && "-windowed".equals(arg[0])) {
config.fullscreen = false;
}