mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Support for walkable destructables
This commit is contained in:
parent
e919f35904
commit
7cea3e3fcd
@ -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),
|
||||
|
@ -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)) {
|
||||
|
@ -31,4 +31,8 @@ public class Bounds {
|
||||
public boolean intersectRayFast(final Ray ray) {
|
||||
return Intersector.intersectRayBoundsFast(ray, this.boundingBox);
|
||||
}
|
||||
|
||||
public BoundingBox getBoundingBox() {
|
||||
return boundingBox;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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" + //
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user