Support for walkable destructables

This commit is contained in:
Retera 2020-10-23 22:35:25 -04:00
parent e919f35904
commit 7cea3e3fcd
17 changed files with 3237 additions and 3081 deletions

View File

@ -38,6 +38,7 @@ import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.Element;
import com.etheller.warsmash.units.HashedGameObject;
import com.etheller.warsmash.util.DataSourceFileHandle;
import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.viewer5.CanvasProvider;
@ -141,7 +142,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),

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

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

@ -29,374 +29,431 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitAnimationListe
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
public class RenderUnit {
private static final Quaternion tempQuat = new Quaternion();
private static final War3ID RED = War3ID.fromString("uclr");
private static final War3ID GREEN = War3ID.fromString("uclg");
private static final War3ID BLUE = War3ID.fromString("uclb");
private static final War3ID MODEL_SCALE = War3ID.fromString("usca");
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
private static final War3ID ORIENTATION_INTERPOLATION = War3ID.fromString("uori");
private static final War3ID ANIM_PROPS = War3ID.fromString("uani");
private static final float[] heapZ = new float[3];
public final MdxComplexInstance instance;
public final MutableGameObject row;
public final float[] location = new float[3];
public float selectionScale;
public UnitSoundset soundset;
public final MdxModel portraitModel;
public int playerIndex;
private final CUnit simulationUnit;
public SplatMover shadow;
public SplatMover selectionCircle;
private static final Quaternion tempQuat = new Quaternion();
private static final War3ID RED = War3ID.fromString("uclr");
private static final War3ID GREEN = War3ID.fromString("uclg");
private static final War3ID BLUE = War3ID.fromString("uclb");
private static final War3ID MODEL_SCALE = War3ID.fromString("usca");
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
private static final War3ID ORIENTATION_INTERPOLATION = War3ID.fromString("uori");
private static final War3ID ANIM_PROPS = War3ID.fromString("uani");
private static final float[] heapZ = new float[3];
public final MdxComplexInstance instance;
public final MutableGameObject row;
public final float[] location = new float[3];
public float selectionScale;
public UnitSoundset soundset;
public final MdxModel portraitModel;
public int playerIndex;
private final CUnit simulationUnit;
public SplatMover shadow;
public SplatMover selectionCircle;
private float x;
private float y;
private float facing;
private float x;
private float y;
private float facing;
private boolean swimming;
private boolean swimming;
private boolean dead = false;
private boolean dead = false;
private final UnitAnimationListenerImpl unitAnimationListenerImpl;
private OrientationInterpolation orientationInterpolation;
private float currentTurnVelocity = 0;
public long lastUnitResponseEndTimeMillis;
private boolean corpse;
private boolean boneCorpse;
private final RenderUnitTypeData typeData;
private final UnitAnimationListenerImpl unitAnimationListenerImpl;
private OrientationInterpolation orientationInterpolation;
private float currentTurnVelocity = 0;
public long lastUnitResponseEndTimeMillis;
private boolean corpse;
private boolean boneCorpse;
private final RenderUnitTypeData typeData;
public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit, final UnitSoundset soundset,
final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData) {
this.portraitModel = portraitModel;
this.simulationUnit = simulationUnit;
this.typeData = typeData;
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit, final UnitSoundset soundset,
final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData) {
this.portraitModel = portraitModel;
this.simulationUnit = simulationUnit;
this.typeData = typeData;
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
final float[] location = unit.getLocation();
System.arraycopy(location, 0, this.location, 0, 3);
instance.move(location);
this.facing = simulationUnit.getFacing();
final float angle = (float) Math.toRadians(this.facing);
final float[] location = unit.getLocation();
System.arraycopy(location, 0, this.location, 0, 3);
instance.move(location);
this.facing = simulationUnit.getFacing();
final float angle = (float) Math.toRadians(this.facing);
// instance.localRotation.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle);
this.x = simulationUnit.getX();
this.y = simulationUnit.getY();
instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle));
instance.scale(unit.getScale());
this.playerIndex = unit.getPlayer() & 0xFFFF;
instance.setTeamColor(this.playerIndex);
instance.setScene(map.worldScene);
this.unitAnimationListenerImpl = new UnitAnimationListenerImpl(instance);
simulationUnit.setUnitAnimationListener(this.unitAnimationListenerImpl);
final String requiredAnimationNames = row.getFieldAsString(ANIM_PROPS, 0);
TokenLoop: for (final String animationName : requiredAnimationNames.split(",")) {
final String upperCaseToken = animationName.toUpperCase();
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
if (upperCaseToken.equals(secondaryTag.name())) {
this.unitAnimationListenerImpl.addSecondaryTag(secondaryTag);
continue TokenLoop;
}
}
}
this.x = simulationUnit.getX();
this.y = simulationUnit.getY();
instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle));
instance.scale(unit.getScale());
this.playerIndex = unit.getPlayer() & 0xFFFF;
instance.setTeamColor(this.playerIndex);
instance.setScene(map.worldScene);
this.unitAnimationListenerImpl = new UnitAnimationListenerImpl(instance);
simulationUnit.setUnitAnimationListener(this.unitAnimationListenerImpl);
final String requiredAnimationNames = row.getFieldAsString(ANIM_PROPS, 0);
TokenLoop:
for (final String animationName : requiredAnimationNames.split(",")) {
final String upperCaseToken = animationName.toUpperCase();
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
if (upperCaseToken.equals(secondaryTag.name())) {
this.unitAnimationListenerImpl.addSecondaryTag(secondaryTag);
continue TokenLoop;
}
}
}
if (row != null) {
heapZ[2] = simulationUnit.getFlyHeight();
this.location[2] += heapZ[2];
if (row != null) {
heapZ[2] = simulationUnit.getFlyHeight();
this.location[2] += heapZ[2];
instance.move(heapZ);
War3ID red;
War3ID green;
War3ID blue;
War3ID scale;
scale = MODEL_SCALE;
red = RED;
green = GREEN;
blue = BLUE;
instance.setVertexColor(new float[] { (row.getFieldAsInteger(red, 0)) / 255f,
(row.getFieldAsInteger(green, 0)) / 255f, (row.getFieldAsInteger(blue, 0)) / 255f });
instance.uniformScale(row.getFieldAsFloat(scale, 0));
instance.move(heapZ);
War3ID red;
War3ID green;
War3ID blue;
War3ID scale;
scale = MODEL_SCALE;
red = RED;
green = GREEN;
blue = BLUE;
instance.setVertexColor(new float[]{(row.getFieldAsInteger(red, 0)) / 255f,
(row.getFieldAsInteger(green, 0)) / 255f, (row.getFieldAsInteger(blue, 0)) / 255f});
instance.uniformScale(row.getFieldAsFloat(scale, 0));
this.selectionScale = row.getFieldAsFloat(War3MapViewer.UNIT_SELECT_SCALE, 0);
int orientationInterpolationOrdinal = row.getFieldAsInteger(ORIENTATION_INTERPOLATION, 0);
if ((orientationInterpolationOrdinal < 0)
|| (orientationInterpolationOrdinal >= OrientationInterpolation.VALUES.length)) {
orientationInterpolationOrdinal = 0;
}
this.orientationInterpolation = OrientationInterpolation.VALUES[orientationInterpolationOrdinal];
}
this.selectionScale = row.getFieldAsFloat(War3MapViewer.UNIT_SELECT_SCALE, 0);
int orientationInterpolationOrdinal = row.getFieldAsInteger(ORIENTATION_INTERPOLATION, 0);
if ((orientationInterpolationOrdinal < 0)
|| (orientationInterpolationOrdinal >= OrientationInterpolation.VALUES.length)) {
orientationInterpolationOrdinal = 0;
}
this.orientationInterpolation = OrientationInterpolation.VALUES[orientationInterpolationOrdinal];
}
this.instance = instance;
this.row = row;
this.soundset = soundset;
this.instance = instance;
this.row = row;
this.soundset = soundset;
}
}
public void populateCommandCard(final CommandButtonListener commandButtonListener,
final AbilityDataUI abilityDataUI) {
for (final CAbility ability : this.simulationUnit.getAbilities()) {
ability.visit(CommandCardPopulatingAbilityVisitor.INSTANCE.reset(commandButtonListener, abilityDataUI));
}
}
public void populateCommandCard(final CommandButtonListener commandButtonListener,
final AbilityDataUI abilityDataUI) {
for (final CAbility ability : this.simulationUnit.getAbilities()) {
ability.visit(CommandCardPopulatingAbilityVisitor.INSTANCE.reset(commandButtonListener, abilityDataUI));
}
}
public void updateAnimations(final War3MapViewer map) {
final float deltaTime = Gdx.graphics.getDeltaTime();
final float simulationX = this.simulationUnit.getX();
final float simulationY = this.simulationUnit.getY();
final float simDx = simulationX - this.x;
final float simDy = simulationY - this.y;
final float distanceToSimulation = (float) Math.sqrt((simDx * simDx) + (simDy * simDy));
final int speed = this.simulationUnit.getSpeed();
final float speedDelta = speed * deltaTime;
if ((distanceToSimulation > speedDelta) && (deltaTime < 1.0)) {
// The 1.0 here says that after 1 second of lag, units just teleport to show
// where they actually are
this.x += (speedDelta * simDx) / distanceToSimulation;
this.y += (speedDelta * simDy) / distanceToSimulation;
}
else {
this.x = simulationX;
this.y = simulationY;
}
final float x = this.x;
final float dx = x - this.location[0];
this.location[0] = x;
final float y = this.y;
final float dy = y - this.location[1];
this.location[1] = y;
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)
&& 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));
}
else {
groundHeight = map.terrain.getGroundHeight(x, y);
}
if (swimming && !this.swimming) {
this.unitAnimationListenerImpl.addSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
}
else if (!swimming && this.swimming) {
this.unitAnimationListenerImpl.removeSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
}
this.swimming = swimming;
final boolean dead = this.simulationUnit.isDead();
final boolean corpse = this.simulationUnit.isCorpse();
final boolean boneCorpse = this.simulationUnit.isBoneCorpse();
if (dead && !this.dead) {
this.unitAnimationListenerImpl.playAnimation(true, PrimaryTag.DEATH, SequenceUtils.EMPTY, 1.0f, true);
if (this.shadow != null) {
this.shadow.destroy(Gdx.gl30, map.terrain.centerOffset);
this.shadow = null;
}
if (this.selectionCircle != null) {
this.selectionCircle.destroy(Gdx.gl30, map.terrain.centerOffset);
this.selectionCircle = null;
}
}
if (boneCorpse && !this.boneCorpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE,
this.simulationUnit.getEndingDecayTime(map.simulation), true);
}
else if (corpse && !this.corpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH,
map.simulation.getGameplayConstants().getDecayTime(), true);
}
this.dead = dead;
this.corpse = corpse;
this.boneCorpse = boneCorpse;
this.location[2] = this.simulationUnit.getFlyHeight() + groundHeight;
this.instance.moveTo(this.location);
float simulationFacing = this.simulationUnit.getFacing();
if (simulationFacing < 0) {
simulationFacing += 360;
}
float renderFacing = this.facing;
if (renderFacing < 0) {
renderFacing += 360;
}
float facingDelta = simulationFacing - renderFacing;
if (facingDelta < -180) {
facingDelta = 360 + facingDelta;
}
if (facingDelta > 180) {
facingDelta = -360 + facingDelta;
}
final float absoluteFacingDelta = Math.abs(facingDelta);
final float turningSign = Math.signum(facingDelta);
public void updateAnimations(final War3MapViewer map) {
final float deltaTime = Gdx.graphics.getDeltaTime();
final float simulationX = this.simulationUnit.getX();
final float simulationY = this.simulationUnit.getY();
final float simDx = simulationX - this.x;
final float simDy = simulationY - this.y;
final float distanceToSimulation = (float) Math.sqrt((simDx * simDx) + (simDy * simDy));
final int speed = this.simulationUnit.getSpeed();
final float speedDelta = speed * deltaTime;
if ((distanceToSimulation > speedDelta) && (deltaTime < 1.0)) {
// The 1.0 here says that after 1 second of lag, units just teleport to show
// where they actually are
this.x += (speedDelta * simDx) / distanceToSimulation;
this.y += (speedDelta * simDy) / distanceToSimulation;
} else {
this.x = simulationX;
this.y = simulationY;
}
final float x = this.x;
final float dx = x - this.location[0];
this.location[0] = x;
final float y = this.y;
final float dy = y - this.location[1];
this.location[1] = y;
final float groundHeight;
final MovementType movementType = this.simulationUnit.getUnitType().getMovementType();
final short terrainPathing = map.terrain.pathingGrid.getPathing(x, y);
boolean swimming = (movementType == MovementType.AMPHIBIOUS)
&& PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.SWIMMABLE)
&& !PathingGrid.isPathingFlag(terrainPathing, PathingGrid.PathingType.WALKABLE);
float groundHeightTerrain = map.terrain.getGroundHeight(x, y);
float groundHeightTerrainAndWater;
MdxComplexInstance currentWalkableUnder;
boolean standingOnWater = (swimming) || (movementType == MovementType.FLOAT) || (movementType == MovementType.FLY)
|| (movementType == MovementType.HOVER);
if (standingOnWater) {
groundHeightTerrainAndWater = Math.max(groundHeightTerrain, map.terrain.getWaterHeight(x, y));
} else {
// 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);
} else if (!swimming && this.swimming) {
this.unitAnimationListenerImpl.removeSecondaryTag(AnimationTokens.SecondaryTag.SWIM);
}
this.swimming = swimming;
final boolean dead = this.simulationUnit.isDead();
final boolean corpse = this.simulationUnit.isCorpse();
final boolean boneCorpse = this.simulationUnit.isBoneCorpse();
if (dead && !this.dead) {
this.unitAnimationListenerImpl.playAnimation(true, PrimaryTag.DEATH, SequenceUtils.EMPTY, 1.0f, true);
if (this.shadow != null) {
this.shadow.destroy(Gdx.gl30, map.terrain.centerOffset);
this.shadow = null;
}
if (this.selectionCircle != null) {
this.selectionCircle.destroy(Gdx.gl30, map.terrain.centerOffset);
this.selectionCircle = null;
}
}
if (boneCorpse && !this.boneCorpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.BONE,
this.simulationUnit.getEndingDecayTime(map.simulation), true);
} else if (corpse && !this.corpse) {
this.unitAnimationListenerImpl.playAnimationWithDuration(true, PrimaryTag.DECAY, SequenceUtils.FLESH,
map.simulation.getGameplayConstants().getDecayTime(), true);
}
this.dead = dead;
this.corpse = corpse;
this.boneCorpse = boneCorpse;
this.location[2] = this.simulationUnit.getFlyHeight() + groundHeight;
this.instance.moveTo(this.location);
float simulationFacing = this.simulationUnit.getFacing();
if (simulationFacing < 0) {
simulationFacing += 360;
}
float renderFacing = this.facing;
if (renderFacing < 0) {
renderFacing += 360;
}
float facingDelta = simulationFacing - renderFacing;
if (facingDelta < -180) {
facingDelta = 360 + facingDelta;
}
if (facingDelta > 180) {
facingDelta = -360 + facingDelta;
}
final float absoluteFacingDelta = Math.abs(facingDelta);
final float turningSign = Math.signum(facingDelta);
final float absoluteFacingDeltaRadians = (float) Math.toRadians(absoluteFacingDelta);
float acceleration;
final boolean endPhase = (absoluteFacingDeltaRadians <= this.orientationInterpolation.getEndingAccelCutoff())
&& ((this.currentTurnVelocity * turningSign) > 0);
if (endPhase) {
this.currentTurnVelocity = (1
- ((this.orientationInterpolation.getEndingAccelCutoff() - absoluteFacingDeltaRadians)
/ this.orientationInterpolation.getEndingAccelCutoff()))
* (this.orientationInterpolation.getMaxVelocity()) * turningSign;
}
else {
acceleration = this.orientationInterpolation.getStartingAcceleration() * turningSign;
this.currentTurnVelocity = this.currentTurnVelocity + acceleration;
}
if ((this.currentTurnVelocity * turningSign) > this.orientationInterpolation.getMaxVelocity()) {
this.currentTurnVelocity = this.orientationInterpolation.getMaxVelocity() * turningSign;
}
float angleToAdd = (float) ((Math.toDegrees(this.currentTurnVelocity) * deltaTime) / 0.03f);
final float absoluteFacingDeltaRadians = (float) Math.toRadians(absoluteFacingDelta);
float acceleration;
final boolean endPhase = (absoluteFacingDeltaRadians <= this.orientationInterpolation.getEndingAccelCutoff())
&& ((this.currentTurnVelocity * turningSign) > 0);
if (endPhase) {
this.currentTurnVelocity = (1
- ((this.orientationInterpolation.getEndingAccelCutoff() - absoluteFacingDeltaRadians)
/ this.orientationInterpolation.getEndingAccelCutoff()))
* (this.orientationInterpolation.getMaxVelocity()) * turningSign;
} else {
acceleration = this.orientationInterpolation.getStartingAcceleration() * turningSign;
this.currentTurnVelocity = this.currentTurnVelocity + acceleration;
}
if ((this.currentTurnVelocity * turningSign) > this.orientationInterpolation.getMaxVelocity()) {
this.currentTurnVelocity = this.orientationInterpolation.getMaxVelocity() * turningSign;
}
float angleToAdd = (float) ((Math.toDegrees(this.currentTurnVelocity) * deltaTime) / 0.03f);
if (absoluteFacingDelta < Math.abs(angleToAdd)) {
angleToAdd = facingDelta;
this.currentTurnVelocity = 0.0f;
}
this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360;
this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing));
if (absoluteFacingDelta < Math.abs(angleToAdd)) {
angleToAdd = facingDelta;
this.currentTurnVelocity = 0.0f;
}
this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360;
this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing));
final float facingRadians = (float) Math.toRadians(this.facing);
final float maxPitch = this.typeData.getMaxPitch();
final float maxRoll = this.typeData.getMaxRoll();
final float sampleRadius = this.typeData.getElevationSampleRadius();
float pitch, roll;
final float pitchSampleForwardX = x + (sampleRadius * (float) Math.cos(facingRadians));
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);
roll = Math.max(-maxRoll, Math.min(maxRoll,
(float) Math.atan2(rollSampleGroundHeight2 - rollSampleGroundHeight1, sampleRadius * 2)));
this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
final float facingRadians = (float) Math.toRadians(this.facing);
final float maxPitch = this.typeData.getMaxPitch();
final float maxRoll = this.typeData.getMaxRoll();
final float sampleRadius = this.typeData.getElevationSampleRadius();
float pitch, roll;
final float pitchSampleForwardX = x + (sampleRadius * (float) Math.cos(facingRadians));
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 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 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 {
float pitchGroundHeight1 = map.terrain.getGroundHeight(pitchSampleBackwardX, pitchSampleBackwardY);
float pitchGroundHeight2 = map.terrain.getGroundHeight(pitchSampleForwardX, pitchSampleForwardY);
float rollGroundHeight1 = map.terrain.getGroundHeight(rollSampleBackwardX, rollSampleBackwardY);
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));
this.instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
map.worldScene.instanceMoved(this.instance, this.location[0], this.location[1]);
if (this.shadow != null) {
this.shadow.move(dx, dy, map.terrain.centerOffset);
}
if (this.selectionCircle != null) {
this.selectionCircle.move(dx, dy, map.terrain.centerOffset);
}
this.unitAnimationListenerImpl.update();
}
map.worldScene.instanceMoved(this.instance, this.location[0], this.location[1]);
if (this.shadow != null) {
this.shadow.move(dx, dy, map.terrain.centerOffset);
}
if (this.selectionCircle != null) {
this.selectionCircle.move(dx, dy, map.terrain.centerOffset);
}
this.unitAnimationListenerImpl.update();
}
public CUnit getSimulationUnit() {
return this.simulationUnit;
}
private float getGroundHeightSample(float groundHeight, MdxComplexInstance currentWalkableUnder, float sampleX, 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;
}
private static final class UnitAnimationListenerImpl implements CUnitAnimationListener {
private final MdxComplexInstance instance;
private final EnumSet<AnimationTokens.SecondaryTag> secondaryAnimationTags = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private final EnumSet<AnimationTokens.SecondaryTag> recycleSet = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private PrimaryTag currentAnimation;
private EnumSet<SecondaryTag> currentAnimationSecondaryTags;
private float currentSpeedRatio;
private boolean currentlyAllowingRarityVariations;
private final Queue<QueuedAnimation> animationQueue = new LinkedList<>();
public CUnit getSimulationUnit() {
return this.simulationUnit;
}
public UnitAnimationListenerImpl(final MdxComplexInstance instance) {
this.instance = instance;
}
private static final class UnitAnimationListenerImpl implements CUnitAnimationListener {
private final MdxComplexInstance instance;
private final EnumSet<AnimationTokens.SecondaryTag> secondaryAnimationTags = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private final EnumSet<AnimationTokens.SecondaryTag> recycleSet = EnumSet
.noneOf(AnimationTokens.SecondaryTag.class);
private PrimaryTag currentAnimation;
private EnumSet<SecondaryTag> currentAnimationSecondaryTags;
private float currentSpeedRatio;
private boolean currentlyAllowingRarityVariations;
private final Queue<QueuedAnimation> animationQueue = new LinkedList<>();
public void addSecondaryTag(final AnimationTokens.SecondaryTag tag) {
this.secondaryAnimationTags.add(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
public UnitAnimationListenerImpl(final MdxComplexInstance instance) {
this.instance = instance;
}
public void removeSecondaryTag(final AnimationTokens.SecondaryTag tag) {
this.secondaryAnimationTags.remove(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
public void addSecondaryTag(final AnimationTokens.SecondaryTag tag) {
this.secondaryAnimationTags.add(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
@Override
public void playAnimation(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float speedRatio,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentSpeedRatio = speedRatio;
this.currentlyAllowingRarityVariations = allowRarityVariations;
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
this.instance.setAnimationSpeed(speedRatio);
SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet, allowRarityVariations);
}
}
public void removeSecondaryTag(final AnimationTokens.SecondaryTag tag) {
this.secondaryAnimationTags.remove(tag);
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags, this.currentSpeedRatio,
this.currentlyAllowingRarityVariations);
}
public void playAnimationWithDuration(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float duration,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations;
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
final Sequence sequence = SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations);
if (sequence != null) {
this.currentSpeedRatio = ((sequence.getInterval()[1] - sequence.getInterval()[0]) / 1000.0f)
/ duration;
this.instance.setAnimationSpeed(this.currentSpeedRatio);
}
}
}
@Override
public void playAnimation(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float speedRatio,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentSpeedRatio = speedRatio;
this.currentlyAllowingRarityVariations = allowRarityVariations;
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
this.instance.setAnimationSpeed(speedRatio);
SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet, allowRarityVariations);
}
}
@Override
public void queueAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationQueue.add(new QueuedAnimation(animationName, secondaryAnimationTags, allowRarityVariations));
}
public void playAnimationWithDuration(final boolean force, final PrimaryTag animationName,
final EnumSet<SecondaryTag> secondaryAnimationTags, final float duration,
final boolean allowRarityVariations) {
this.animationQueue.clear();
if (force || (animationName != this.currentAnimation)) {
this.currentAnimation = animationName;
this.currentAnimationSecondaryTags = secondaryAnimationTags;
this.currentlyAllowingRarityVariations = allowRarityVariations;
this.recycleSet.clear();
this.recycleSet.addAll(this.secondaryAnimationTags);
this.recycleSet.addAll(secondaryAnimationTags);
final Sequence sequence = SequenceUtils.randomSequence(this.instance, animationName, this.recycleSet,
allowRarityVariations);
if (sequence != null) {
this.currentSpeedRatio = ((sequence.getInterval()[1] - sequence.getInterval()[0]) / 1000.0f)
/ duration;
this.instance.setAnimationSpeed(this.currentSpeedRatio);
}
}
}
public void update() {
if (this.instance.sequenceEnded || (this.instance.sequence == -1)) {
// animation done
if ((this.instance.sequence != -1) && (((MdxModel) this.instance.model).getSequences()
.get(this.instance.sequence).getFlags() == 0)) {
// animation is a looping animation
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
}
else {
final QueuedAnimation nextAnimation = this.animationQueue.poll();
if (nextAnimation != null) {
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,
nextAnimation.allowRarityVariations);
}
}
}
}
@Override
public void queueAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationQueue.add(new QueuedAnimation(animationName, secondaryAnimationTags, allowRarityVariations));
}
}
public void update() {
if (this.instance.sequenceEnded || (this.instance.sequence == -1)) {
// animation done
if ((this.instance.sequence != -1) && (((MdxModel) this.instance.model).getSequences()
.get(this.instance.sequence).getFlags() == 0)) {
// animation is a looping animation
playAnimation(true, this.currentAnimation, this.currentAnimationSecondaryTags,
this.currentSpeedRatio, this.currentlyAllowingRarityVariations);
} else {
final QueuedAnimation nextAnimation = this.animationQueue.poll();
if (nextAnimation != null) {
playAnimation(true, nextAnimation.animationName, nextAnimation.secondaryAnimationTags, 1.0f,
nextAnimation.allowRarityVariations);
}
}
}
}
private static final class QueuedAnimation {
private final PrimaryTag animationName;
private final EnumSet<SecondaryTag> secondaryAnimationTags;
private final boolean allowRarityVariations;
}
public QueuedAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationName = animationName;
this.secondaryAnimationTags = secondaryAnimationTags;
this.allowRarityVariations = allowRarityVariations;
}
}
private static final class QueuedAnimation {
private final PrimaryTag animationName;
private final EnumSet<SecondaryTag> secondaryAnimationTags;
private final boolean allowRarityVariations;
public QueuedAnimation(final PrimaryTag animationName, final EnumSet<SecondaryTag> secondaryAnimationTags,
final boolean allowRarityVariations) {
this.animationName = animationName;
this.secondaryAnimationTags = secondaryAnimationTags;
this.allowRarityVariations = allowRarityVariations;
}
}
}

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

@ -138,7 +138,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

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

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