mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Update some movement code and fun with projectiles
This commit is contained in:
parent
c4a24934dd
commit
f776a602f9
35
core/src/com/etheller/warsmash/CodeCounter.java
Normal file
35
core/src/com/etheller/warsmash/CodeCounter.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class CodeCounter {
|
||||
|
||||
public static void main(final String[] args) {
|
||||
final int sourceLines = countFile(new File("src/com"));
|
||||
System.out.println(sourceLines);
|
||||
}
|
||||
|
||||
public static int countFile(final File file) {
|
||||
if (file.isDirectory()) {
|
||||
int sum = 0;
|
||||
for (final File subFile : file.listFiles()) {
|
||||
sum += countFile(subFile);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (file.getName().toLowerCase().endsWith(".java")) {
|
||||
return Files.readAllLines(file.toPath()).size();
|
||||
}
|
||||
}
|
||||
catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
61
core/src/com/etheller/warsmash/MathSpeedBenchmark.java
Normal file
61
core/src/com/etheller/warsmash/MathSpeedBenchmark.java
Normal file
@ -0,0 +1,61 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
public class MathSpeedBenchmark {
|
||||
private static final int NUMBER_OF_ITERATIONS = 100000000;
|
||||
|
||||
public static void main(final String[] args) {
|
||||
// Let us solve for Ground Distance two ways.
|
||||
|
||||
long sumCosineTime = 0;
|
||||
long sumSquareRootTime = 0;
|
||||
final float[] thrallXs = new float[NUMBER_OF_ITERATIONS];
|
||||
final float[] thrallYs = new float[NUMBER_OF_ITERATIONS];
|
||||
final float[] murlocXs = new float[NUMBER_OF_ITERATIONS];
|
||||
final float[] murlocYs = new float[NUMBER_OF_ITERATIONS];
|
||||
for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {
|
||||
|
||||
thrallXs[i] = getRandomFloat(-25000.0f, 25000.0f);
|
||||
thrallYs[i] = getRandomFloat(-25000.0f, 25000.0f);
|
||||
murlocXs[i] = getRandomFloat(-25000.0f, 25000.0f);
|
||||
murlocYs[i] = getRandomFloat(-25000.0f, 25000.0f);
|
||||
}
|
||||
final long clockTime1 = System.currentTimeMillis();
|
||||
for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {
|
||||
final float distance2 = groundDistanceSqrt(thrallXs[i], thrallYs[i], murlocXs[i], murlocYs[i]);
|
||||
}
|
||||
final long clockTime2 = System.currentTimeMillis();
|
||||
for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {
|
||||
final float distance1 = groundDistanceCos(thrallXs[i], thrallYs[i], murlocXs[i], murlocYs[i]);
|
||||
}
|
||||
final long clockTime3 = System.currentTimeMillis();
|
||||
// if (Math.abs(distance2 - distance1) > 0.1) {
|
||||
// System.out.println(thrallX + "," + thrallY);
|
||||
// System.out.println(murlocX + "," + murlocY);
|
||||
// System.err.println(distance1 + " != " + distance2);
|
||||
// throw new RuntimeException("You have failed to do mathematics.");
|
||||
// }
|
||||
sumCosineTime = clockTime2 - clockTime1;
|
||||
sumSquareRootTime = clockTime3 - clockTime2;
|
||||
System.out.println("Square Root: " + sumCosineTime);
|
||||
System.out.println("Cosine: " + sumSquareRootTime);
|
||||
}
|
||||
|
||||
static float getRandomFloat(final float min, final float max) {
|
||||
final float range = max - min;
|
||||
return (float) ((Math.random() * range) + min);
|
||||
}
|
||||
|
||||
static float groundDistanceSqrt(final float thrallX, final float thrallY, final float murlocX,
|
||||
final float murlocY) {
|
||||
final float dx = murlocX - thrallX;
|
||||
final float dy = murlocY - thrallY;
|
||||
return (float) Math.sqrt((dx * dx) + (dy * dy));
|
||||
}
|
||||
|
||||
static float groundDistanceCos(final float thrallX, final float thrallY, final float murlocX, final float murlocY) {
|
||||
final float dx = murlocX - thrallX;
|
||||
final float dy = murlocY - thrallY;
|
||||
final double angle = Math.atan2(dy, dx);
|
||||
return (float) (dx / Math.cos(angle));
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ import com.etheller.warsmash.viewer5.ModelViewer;
|
||||
import com.etheller.warsmash.viewer5.PathSolver;
|
||||
import com.etheller.warsmash.viewer5.Scene;
|
||||
import com.etheller.warsmash.viewer5.SolvedPath;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.EventObjectEmitterObject;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
@ -74,7 +75,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
this.cameraManager.setupCamera(scene);
|
||||
|
||||
// this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx",
|
||||
this.mainModel = (MdxModel) this.viewer.load("Doodads\\Cinematic\\RisingWaterDoodad\\RisingWaterDoodad.mdx",
|
||||
this.mainModel = (MdxModel) this.viewer.load("Units\\Human\\HeroPaladinBoss\\HeroPaladinBoss.mdx",
|
||||
new PathSolver() {
|
||||
@Override
|
||||
public SolvedPath solve(final String src, final Object solverParams) {
|
||||
@ -82,6 +83,13 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
|
||||
}
|
||||
}, null);
|
||||
|
||||
final EventObjectEmitterObject evt = this.mainModel.getEventObjects().get(1);
|
||||
for (final Sequence seq : this.mainModel.getSequences()) {
|
||||
System.out.println(seq.getName() + ": " + Arrays.toString(seq.getInterval()));
|
||||
}
|
||||
System.out.println(Arrays.toString(evt.keyFrames));
|
||||
System.out.println(evt.name);
|
||||
|
||||
// this.modelCamera = this.mainModel.cameras.get(0);
|
||||
|
||||
this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0);
|
||||
|
@ -13,7 +13,6 @@ import com.badlogic.gdx.ApplicationAdapter;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.InputProcessor;
|
||||
import com.badlogic.gdx.audio.Music;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.GL20;
|
||||
import com.badlogic.gdx.graphics.GL30;
|
||||
@ -84,6 +83,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
private Rectangle minimapFilledArea;
|
||||
|
||||
private final Texture[] teamColors = new Texture[WarsmashConstants.MAX_PLAYERS];
|
||||
private Texture solidGreenTexture;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
@ -115,15 +115,15 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
.createDataSource();
|
||||
this.viewer = new War3MapViewer(this.codebase, this);
|
||||
|
||||
this.viewer.worldScene.enableAudio();
|
||||
this.viewer.enableAudio();
|
||||
try {
|
||||
// "Maps\\Campaign\\NightElf03.w3m"
|
||||
this.viewer.loadMap("Maps\\Campaign\\NightElf03.w3m");
|
||||
this.viewer.loadMap("ProjectileTest.w3x");
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.viewer.worldScene.enableAudio();
|
||||
this.viewer.enableAudio();
|
||||
|
||||
this.cameraManager = new CameraManager();
|
||||
this.cameraManager.setupCamera(this.viewer.worldScene);
|
||||
@ -200,13 +200,16 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
"ReplaceableTextures\\" + ReplaceableIds.getPathString(1) + ReplaceableIds.getIdString(i) + ".blp");
|
||||
}
|
||||
|
||||
Gdx.input.setInputProcessor(this);
|
||||
this.solidGreenTexture = ImageUtils.getBLPTexture(this.viewer.dataSource,
|
||||
"ReplaceableTextures\\TeamColor\\TeamColor06.blp");
|
||||
|
||||
final Music music = Gdx.audio
|
||||
.newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\NightElf3.mp3"));
|
||||
music.setVolume(0.2f);
|
||||
music.setLooping(true);
|
||||
music.play();
|
||||
Gdx.input.setInputProcessor(this);
|
||||
//
|
||||
// final Music music = Gdx.audio
|
||||
// .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\OrcTheme.mp3"));
|
||||
// music.setVolume(0.2f);
|
||||
// music.setLooping(true);
|
||||
// music.play();
|
||||
|
||||
this.minimap = new Rectangle(35, 7, 305, 272);
|
||||
final float worldWidth = (this.viewer.terrain.columns - 1);
|
||||
@ -303,6 +306,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
}
|
||||
}
|
||||
|
||||
this.batch.draw(this.solidGreenTexture, 413, 34, 122 * (this.selectedUnit.getSimulationUnit().getLife()
|
||||
/ this.selectedUnit.getSimulationUnit().getMaximumLife()), 7);
|
||||
|
||||
}
|
||||
for (final RenderUnit unit : this.viewer.units) {
|
||||
if (unit.playerIndex >= WarsmashConstants.MAX_PLAYERS) {
|
||||
|
@ -3,5 +3,5 @@ package com.etheller.warsmash.util;
|
||||
public class WarsmashConstants {
|
||||
public static final int MAX_PLAYERS = 16;
|
||||
public static final int REPLACEABLE_TEXTURE_LIMIT = 64;
|
||||
public static final float SIMULATION_STEP_TIME = 1 / 60f;
|
||||
public static final float SIMULATION_STEP_TIME = 1 / 20f;
|
||||
}
|
||||
|
@ -48,6 +48,12 @@ public abstract class Node extends GenericNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Node setLocation(final float x, final float y, final float z) {
|
||||
this.localLocation.set(x, y, z);
|
||||
this.dirty = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Node setLocation(final float[] location) {
|
||||
this.localLocation.set(location);
|
||||
this.dirty = true;
|
||||
|
@ -131,6 +131,7 @@ public class Scene {
|
||||
this.grid.remove(instance);
|
||||
|
||||
instance.scene = null;
|
||||
this.instances.remove(instance);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
private int geometryEmitterType = -1;
|
||||
public final String type;
|
||||
private final String id;
|
||||
private final long[] keyFrames;
|
||||
public final long[] keyFrames;
|
||||
private long globalSequence = -1;
|
||||
private final long[] defval = { 1 };
|
||||
public MdxModel internalModel;
|
||||
@ -185,7 +185,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
|
||||
private void load(final List<GenericResource> tables) {
|
||||
final MappedData firstTable = (MappedData) tables.get(0).data;
|
||||
final MappedDataRow row = firstTable.getRow(this.id);
|
||||
final MappedDataRow row = firstTable.getRow(this.id.trim());
|
||||
|
||||
if (row != null) {
|
||||
final MdxModel model = this.model;
|
||||
@ -285,9 +285,12 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
|
||||
}
|
||||
}
|
||||
else {
|
||||
System.err.println("Unknown event object ID: " + this.type + this.id);
|
||||
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) {
|
||||
|
@ -340,4 +340,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
return this.cameras;
|
||||
}
|
||||
|
||||
public List<EventObjectEmitterObject> getEventObjects() {
|
||||
return this.eventObjects;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -68,6 +68,19 @@ public class StandSequence {
|
||||
}
|
||||
}
|
||||
|
||||
public static void randomDeathSequence(final MdxComplexInstance target) {
|
||||
final MdxModel model = (MdxModel) target.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
final IndexedSequence sequence = selectSequence("death", sequences);
|
||||
|
||||
if (sequence != null) {
|
||||
target.setSequence(sequence.index);
|
||||
}
|
||||
else {
|
||||
target.setSequence(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void randomWalkSequence(final MdxComplexInstance target) {
|
||||
final MdxModel model = (MdxModel) target.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
@ -81,6 +94,19 @@ public class StandSequence {
|
||||
}
|
||||
}
|
||||
|
||||
public static void randomBirthSequence(final MdxComplexInstance target) {
|
||||
final MdxModel model = (MdxModel) target.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
final IndexedSequence sequence = selectSequence("birth", sequences);
|
||||
|
||||
if (sequence != null) {
|
||||
target.setSequence(sequence.index);
|
||||
}
|
||||
else {
|
||||
randomStandSequence(target);
|
||||
}
|
||||
}
|
||||
|
||||
public static void randomPortraitSequence(final MdxComplexInstance target) {
|
||||
final MdxModel model = (MdxModel) target.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
|
@ -10,8 +10,10 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
@ -57,6 +59,7 @@ import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.Terrain.Splat;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderAttackProjectile;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderItem;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
@ -65,9 +68,11 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.projectile.CAttackProjectile;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.CWidgetAbilityTargetCheckReceiver;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.PointAbilityTargetCheckReceiver;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ProjectileCreator;
|
||||
|
||||
import mpq.MPQArchive;
|
||||
import mpq.MPQException;
|
||||
@ -115,6 +120,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
public MappedData unitMetaData = new MappedData();
|
||||
public List<RenderUnit> units = new ArrayList<>();
|
||||
public List<RenderItem> items = new ArrayList<>();
|
||||
public List<RenderAttackProjectile> projectiles = new ArrayList<>();
|
||||
public boolean unitsReady;
|
||||
public War3Map mapMpq;
|
||||
public PathSolver mapPathSolver = PathSolver.DEFAULT;
|
||||
@ -136,6 +142,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
private final DynamicShadowManager dynamicShadowManager = new DynamicShadowManager();
|
||||
|
||||
private final Random seededRandom = new Random(1337L);
|
||||
|
||||
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
|
||||
super(dataSource, canvas);
|
||||
this.gameDataSource = dataSource;
|
||||
@ -297,7 +305,44 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
final Warcraft3MapObjectData modifications = this.mapMpq.readModifications();
|
||||
this.simulation = new CSimulation(modifications.getUnits(), modifications.getAbilities());
|
||||
this.simulation = new CSimulation(modifications.getUnits(), modifications.getAbilities(),
|
||||
new ProjectileCreator() {
|
||||
@Override
|
||||
public CAttackProjectile create(final CSimulation simulation, final CUnit source,
|
||||
final int attackIndex, final CWidget target) {
|
||||
final int a1ProjectileSpeed = simulation.getUnitData().getA1ProjectileSpeed(source.getTypeId());
|
||||
final float a1ProjectileArc = simulation.getUnitData().getA1ProjectileArc(source.getTypeId());
|
||||
String a1MissileArt = simulation.getUnitData().getA1MissileArt(source.getTypeId());
|
||||
final int a1MinDamage = simulation.getUnitData().getA1MinDamage(source.getTypeId());
|
||||
final int a1MaxDamage = simulation.getUnitData().getA1MaxDamage(source.getTypeId());
|
||||
final int damage = War3MapViewer.this.seededRandom.nextInt(a1MaxDamage - a1MinDamage)
|
||||
+ a1MinDamage;
|
||||
if (a1MissileArt.toLowerCase().endsWith(".mdl")) {
|
||||
a1MissileArt = a1MissileArt.substring(0, a1MissileArt.length() - 4);
|
||||
}
|
||||
if (!a1MissileArt.toLowerCase().endsWith(".mdx")) {
|
||||
a1MissileArt += ".mdx";
|
||||
}
|
||||
final float x = source.getX();
|
||||
final float y = source.getY();
|
||||
final float height = War3MapViewer.this.terrain.getGroundHeight(x, y) + source.getFlyHeight();
|
||||
final CAttackProjectile simulationAttackProjectile = new CAttackProjectile(x, y, height,
|
||||
a1ProjectileSpeed, a1ProjectileArc, target, source, damage);
|
||||
|
||||
final MdxModel model = (MdxModel) load(a1MissileArt, War3MapViewer.this.mapPathSolver,
|
||||
War3MapViewer.this.solverParams);
|
||||
final MdxComplexInstance modelInstance = (MdxComplexInstance) model.addInstance();
|
||||
modelInstance.setScene(War3MapViewer.this.worldScene);
|
||||
StandSequence.randomBirthSequence(modelInstance);
|
||||
modelInstance.setLocation(x, y, height);
|
||||
final RenderAttackProjectile renderAttackProjectile = new RenderAttackProjectile(
|
||||
simulationAttackProjectile, modelInstance);
|
||||
|
||||
War3MapViewer.this.projectiles.add(renderAttackProjectile);
|
||||
|
||||
return simulationAttackProjectile;
|
||||
}
|
||||
});
|
||||
|
||||
if (this.doodadsAndDestructiblesLoaded) {
|
||||
this.loadDoodadsAndDestructibles(modifications);
|
||||
@ -483,7 +528,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final float x = unit.getLocation()[0] - shadowX;
|
||||
final float y = unit.getLocation()[1] - shadowY;
|
||||
this.terrain.splats.get(texture).locations
|
||||
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 30 });
|
||||
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
|
||||
unitShadowSplat = this.terrain.splats.get(texture);
|
||||
}
|
||||
|
||||
@ -562,6 +607,13 @@ public class War3MapViewer extends ModelViewer {
|
||||
for (final RenderUnit unit : this.units) {
|
||||
unit.updateAnimations(this);
|
||||
}
|
||||
final Iterator<RenderAttackProjectile> projectileIterator = this.projectiles.iterator();
|
||||
while (projectileIterator.hasNext()) {
|
||||
final RenderAttackProjectile projectile = projectileIterator.next();
|
||||
if (projectile.updateAnimations(this)) {
|
||||
projectileIterator.remove();
|
||||
}
|
||||
}
|
||||
for (final RenderItem item : this.items) {
|
||||
final MdxComplexInstance instance = item.instance;
|
||||
final MdxComplexInstance mdxComplexInstance = instance;
|
||||
@ -655,7 +707,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
final float y = unit.location[1];
|
||||
final float z = unit.row.getFieldAsFloat(UNIT_SELECT_HEIGHT, 0);
|
||||
splats.get(path).locations
|
||||
.add(new float[] { x - radius, y - radius, x + radius, y + radius, z + 35 });
|
||||
.add(new float[] { x - radius, y - radius, x + radius, y + radius, z + 5 });
|
||||
splats.get(path).unitMapping.add(new Consumer<SplatModel.SplatMover>() {
|
||||
@Override
|
||||
public void accept(final SplatMover t) {
|
||||
|
@ -0,0 +1,68 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.StandSequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.projectile.CAttackProjectile;
|
||||
|
||||
public class RenderAttackProjectile {
|
||||
private final CAttackProjectile simulationProjectile;
|
||||
private final MdxComplexInstance modelInstance;
|
||||
private float x;
|
||||
private float y;
|
||||
private float z;
|
||||
|
||||
public RenderAttackProjectile(final CAttackProjectile simulationProjectile,
|
||||
final MdxComplexInstance modelInstance) {
|
||||
this.simulationProjectile = simulationProjectile;
|
||||
this.modelInstance = modelInstance;
|
||||
this.x = simulationProjectile.getX();
|
||||
this.y = simulationProjectile.getY();
|
||||
this.z = simulationProjectile.getZ();
|
||||
}
|
||||
|
||||
public boolean updateAnimations(final War3MapViewer war3MapViewer) {
|
||||
if (this.simulationProjectile.isDone()) {
|
||||
final MdxModel model = (MdxModel) this.modelInstance.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
final IndexedSequence sequence = StandSequence.selectSequence("death", sequences);
|
||||
if (this.modelInstance.sequence != sequence.index) {
|
||||
this.modelInstance.setSequence(sequence.index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.modelInstance.sequenceEnded || (this.modelInstance.sequence == -1)) {
|
||||
StandSequence.randomStandSequence(this.modelInstance);
|
||||
}
|
||||
}
|
||||
final float simX = this.simulationProjectile.getX();
|
||||
final float simY = this.simulationProjectile.getY();
|
||||
final float simZ = this.simulationProjectile.getZ();
|
||||
final float simDx = simX - this.x;
|
||||
final float simDy = simY - this.y;
|
||||
final float simDz = simZ - this.z;
|
||||
final float simD = (float) Math.sqrt((simDx * simDx) + (simDy * simDy));
|
||||
final float deltaTime = Gdx.graphics.getDeltaTime();
|
||||
final float speed = Math.min(simD, this.simulationProjectile.getSpeed() * deltaTime);
|
||||
if (simD > 0) {
|
||||
this.x = this.x + ((speed * simDx) / simD);
|
||||
this.y = this.y + ((speed * simDy) / simD);
|
||||
this.z = this.z + ((speed * simDz) / simD);
|
||||
}
|
||||
|
||||
this.modelInstance.setLocation(this.x, this.y, this.z);
|
||||
war3MapViewer.worldScene.grid.moved(this.modelInstance);
|
||||
|
||||
final boolean everythingDone = this.simulationProjectile.isDone() && this.modelInstance.sequenceEnded;
|
||||
if (everythingDone) {
|
||||
war3MapViewer.worldScene.removeInstance(this.modelInstance);
|
||||
}
|
||||
return everythingDone;
|
||||
}
|
||||
}
|
@ -3,13 +3,16 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.math.Quaternion;
|
||||
import com.etheller.warsmash.parsers.mdlx.Sequence;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.util.ImageUtils;
|
||||
import com.etheller.warsmash.util.RenderMathUtils;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.StandSequence;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.UnitSoundset;
|
||||
@ -24,6 +27,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityP
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityStop;
|
||||
|
||||
public class RenderUnit {
|
||||
private static final double GLOBAL_TURN_RATE = Math.toDegrees(7f);
|
||||
private static final Quaternion tempQuat = new Quaternion();
|
||||
private static final War3ID RED = War3ID.fromString("uclr");
|
||||
private static final War3ID GREEN = War3ID.fromString("uclg");
|
||||
@ -41,11 +45,14 @@ public class RenderUnit {
|
||||
private final CUnit simulationUnit;
|
||||
private COrder lastOrder;
|
||||
private String lastOrderAnimation;
|
||||
private float flyingHeight = 0;
|
||||
public SplatMover shadow;
|
||||
public SplatMover selectionCircle;
|
||||
private final List<CommandCardIcon> commandCardIcons = new ArrayList<>();
|
||||
|
||||
private float x;
|
||||
private float y;
|
||||
private float facing;
|
||||
|
||||
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) {
|
||||
@ -56,8 +63,11 @@ public class RenderUnit {
|
||||
final float[] location = unit.getLocation();
|
||||
System.arraycopy(location, 0, this.location, 0, 3);
|
||||
instance.move(location);
|
||||
final float angle = (float) Math.toRadians(simulationUnit.getFacing());
|
||||
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();
|
||||
@ -65,7 +75,7 @@ public class RenderUnit {
|
||||
instance.setScene(map.worldScene);
|
||||
|
||||
if (row != null) {
|
||||
heapZ[2] = this.flyingHeight = row.getFieldAsFloat(MOVE_HEIGHT, 0);
|
||||
heapZ[2] = simulationUnit.getFlyHeight();
|
||||
this.location[2] += heapZ[2];
|
||||
|
||||
instance.move(heapZ);
|
||||
@ -120,20 +130,65 @@ public class RenderUnit {
|
||||
}
|
||||
|
||||
public void updateAnimations(final War3MapViewer map) {
|
||||
final float x = this.simulationUnit.getX();
|
||||
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) {
|
||||
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.simulationUnit.getY();
|
||||
final float y = this.y;
|
||||
final float dy = y - this.location[1];
|
||||
this.location[1] = y;
|
||||
this.location[2] = this.flyingHeight + map.terrain.getGroundHeight(x, y);
|
||||
this.location[2] = this.simulationUnit.getFlyHeight() + map.terrain.getGroundHeight(x, y);
|
||||
this.instance.moveTo(this.location);
|
||||
this.instance
|
||||
.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.simulationUnit.getFacing()));
|
||||
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);
|
||||
float angleToAdd = (float) (Math.signum(facingDelta) * GLOBAL_TURN_RATE * deltaTime);
|
||||
if (absoluteFacingDelta < Math.abs(angleToAdd)) {
|
||||
angleToAdd = facingDelta;
|
||||
}
|
||||
this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360;
|
||||
this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing));
|
||||
map.worldScene.grid.moved(this.instance);
|
||||
final MdxComplexInstance mdxComplexInstance = this.instance;
|
||||
final COrder currentOrder = this.simulationUnit.getCurrentOrder();
|
||||
if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1) || (currentOrder != this.lastOrder)
|
||||
if (this.simulationUnit.getLife() <= 0) {
|
||||
final MdxModel model = (MdxModel) mdxComplexInstance.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
final IndexedSequence sequence = StandSequence.selectSequence("death", sequences);
|
||||
if ((sequence != null) && (mdxComplexInstance.sequence != sequence.index)) {
|
||||
mdxComplexInstance.setSequence(sequence.index);
|
||||
}
|
||||
}
|
||||
else if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1)
|
||||
|| (currentOrder != this.lastOrder)
|
||||
|| ((currentOrder != null) && (currentOrder.getAnimationName() != null)
|
||||
&& !currentOrder.getAnimationName().equals(this.lastOrderAnimation))) {
|
||||
if (this.simulationUnit.getCurrentOrder() != null) {
|
||||
|
@ -6,4 +6,9 @@ public class CDestructable extends CWidget {
|
||||
super(handleId, x, y, life);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFlyHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,4 +11,9 @@ public class CItem extends CWidget {
|
||||
this.itemType = itemType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getFlyHeight() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,23 +1,32 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CAbilityData;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.data.CUnitData;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.projectile.CAttackProjectile;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ProjectileCreator;
|
||||
|
||||
public class CSimulation {
|
||||
private final CUnitData unitData;
|
||||
private final CAbilityData abilityData;
|
||||
private final List<CUnit> units;
|
||||
private final List<CAttackProjectile> projectiles;
|
||||
private final HandleIdAllocator handleIdAllocator;
|
||||
private final ProjectileCreator projectileCreator;
|
||||
private int gameTurnTick = 0;
|
||||
|
||||
public CSimulation(final MutableObjectData parsedUnitData, final MutableObjectData parsedAbilityData) {
|
||||
public CSimulation(final MutableObjectData parsedUnitData, final MutableObjectData parsedAbilityData,
|
||||
final ProjectileCreator projectileCreator) {
|
||||
this.projectileCreator = projectileCreator;
|
||||
this.unitData = new CUnitData(parsedUnitData);
|
||||
this.abilityData = new CAbilityData(parsedAbilityData);
|
||||
this.units = new ArrayList<>();
|
||||
this.projectiles = new ArrayList<>();
|
||||
this.handleIdAllocator = new HandleIdAllocator();
|
||||
}
|
||||
|
||||
@ -39,9 +48,27 @@ public class CSimulation {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public CAttackProjectile createProjectile(final CUnit source, final int attackIndex, final CWidget target) {
|
||||
final CAttackProjectile projectile = this.projectileCreator.create(this, source, attackIndex, target);
|
||||
this.projectiles.add(projectile);
|
||||
return projectile;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
for (final CUnit unit : this.units) {
|
||||
unit.update(this);
|
||||
}
|
||||
final Iterator<CAttackProjectile> projectileIterator = this.projectiles.iterator();
|
||||
while (projectileIterator.hasNext()) {
|
||||
final CAttackProjectile projectile = projectileIterator.next();
|
||||
if (projectile.update(this)) {
|
||||
projectileIterator.remove();
|
||||
}
|
||||
}
|
||||
this.gameTurnTick++;
|
||||
}
|
||||
|
||||
public int getGameTurnTick() {
|
||||
return this.gameTurnTick;
|
||||
}
|
||||
}
|
||||
|
@ -9,13 +9,14 @@ import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
|
||||
|
||||
public class CUnit extends CWidget {
|
||||
|
||||
private War3ID typeId;
|
||||
private float facing; // degrees
|
||||
private float mana;
|
||||
private int maximumLife;
|
||||
private int maximumMana;
|
||||
private int speed;
|
||||
private int cooldownEndTime = 0;
|
||||
private float flyHeight;
|
||||
|
||||
private final List<CAbility> abilities = new ArrayList<>();
|
||||
|
||||
@ -23,7 +24,8 @@ public class CUnit extends CWidget {
|
||||
private final Queue<COrder> orderQueue = new LinkedList<>();
|
||||
|
||||
public CUnit(final int handleId, final float x, final float y, final float life, final War3ID typeId,
|
||||
final float facing, final float mana, final int maximumLife, final int maximumMana, final int speed) {
|
||||
final float facing, final float mana, final int maximumLife, final int maximumMana, final int speed,
|
||||
final float defaultFlyingHeight) {
|
||||
super(handleId, x, y, life);
|
||||
this.typeId = typeId;
|
||||
this.facing = facing;
|
||||
@ -31,6 +33,7 @@ public class CUnit extends CWidget {
|
||||
this.maximumLife = maximumLife;
|
||||
this.maximumMana = maximumMana;
|
||||
this.speed = speed;
|
||||
this.flyHeight = defaultFlyingHeight;
|
||||
}
|
||||
|
||||
public void add(final CSimulation simulation, final CAbility ability) {
|
||||
@ -121,4 +124,20 @@ public class CUnit extends CWidget {
|
||||
return this.abilities;
|
||||
}
|
||||
|
||||
public void setCooldownEndTime(final int cooldownEndTime) {
|
||||
this.cooldownEndTime = cooldownEndTime;
|
||||
}
|
||||
|
||||
public int getCooldownEndTime() {
|
||||
return this.cooldownEndTime;
|
||||
}
|
||||
|
||||
public float getFlyHeight() {
|
||||
return this.flyHeight;
|
||||
}
|
||||
|
||||
public void setFlyHeight(final float flyHeight) {
|
||||
this.flyHeight = flyHeight;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
public class CWidget {
|
||||
public abstract class CWidget {
|
||||
private final int handleId;
|
||||
private float x;
|
||||
private float y;
|
||||
@ -41,4 +41,10 @@ public class CWidget {
|
||||
this.life = life;
|
||||
}
|
||||
|
||||
public void damage(final CUnit source, final int damage) {
|
||||
this.life -= damage;
|
||||
}
|
||||
|
||||
public abstract float getFlyHeight();
|
||||
|
||||
}
|
||||
|
@ -23,10 +23,19 @@ public class CUnitData {
|
||||
private static final War3ID ATTACK1_DMG_BASE = War3ID.fromString("ua1b");
|
||||
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_PROJECTILE_SPEED = War3ID.fromString("ua1z");
|
||||
private static final War3ID ATTACK1_MISSILE_ART = War3ID.fromString("ua1m");
|
||||
private static final War3ID ATTACK1_PROJECTILE_ARC = War3ID.fromString("uma1");
|
||||
private static final War3ID ATTACK1_COOLDOWN = War3ID.fromString("ua1c");
|
||||
private static final War3ID ATTACK2_DMG_BASE = War3ID.fromString("ua2b");
|
||||
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_PROJECTILE_SPEED = War3ID.fromString("ua2z");
|
||||
private static final War3ID ATTACK2_MISSILE_ART = War3ID.fromString("ua2m");
|
||||
private static final War3ID ATTACK2_PROJECTILE_ARC = War3ID.fromString("uma2");
|
||||
private static final War3ID ATTACK2_COOLDOWN = War3ID.fromString("ua2c");
|
||||
private static final War3ID DEFENSE = War3ID.fromString("udef");
|
||||
private static final War3ID MOVE_HEIGHT = War3ID.fromString("umvh");
|
||||
private final MutableObjectData unitData;
|
||||
|
||||
public CUnitData(final MutableObjectData unitData) {
|
||||
@ -40,7 +49,9 @@ public class CUnitData {
|
||||
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 CUnit unit = new CUnit(handleId, x, y, life, typeId, facing, manaInitial, life, manaMaximum, speed);
|
||||
final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0);
|
||||
final CUnit unit = new CUnit(handleId, x, y, life, typeId, facing, manaInitial, life, manaMaximum, speed,
|
||||
moveHeight);
|
||||
if (speed > 0) {
|
||||
unit.add(simulation, CAbilityMove.INSTANCE);
|
||||
unit.add(simulation, CAbilityPatrol.INSTANCE);
|
||||
@ -96,4 +107,36 @@ public class CUnitData {
|
||||
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 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 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 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 getA2Cooldown(final War3ID unitTypeId) {
|
||||
return this.unitData.get(unitTypeId).getFieldAsFloat(ATTACK2_COOLDOWN, 0);
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ public class CAttackOrder implements COrder {
|
||||
final float absDelta = Math.abs(delta);
|
||||
final float propulsionWindow = simulation.getUnitData().getPropulsionWindow(this.unit.getTypeId());
|
||||
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
|
||||
final int speed = this.unit.getSpeed();
|
||||
|
||||
if (delta < -180) {
|
||||
delta = 360 + delta;
|
||||
@ -53,18 +52,6 @@ public class CAttackOrder implements COrder {
|
||||
this.unit.setFacing(facing + angleToAdd);
|
||||
}
|
||||
if (absDelta < propulsionWindow) {
|
||||
final float speedTick = speed * WarsmashConstants.SIMULATION_STEP_TIME;
|
||||
final float speedTickSq = speedTick * speedTick;
|
||||
|
||||
if (((deltaX * deltaX) + (deltaY * deltaY)) <= speedTickSq) {
|
||||
this.unit.setX(this.target.getX());
|
||||
this.unit.setY(this.target.getY());
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
this.unit.setX(prevX + (float) (Math.cos(goalAngleRad) * speedTick));
|
||||
this.unit.setY(prevY + (float) (Math.sin(goalAngleRad) * speedTick));
|
||||
}
|
||||
this.wasWithinPropWindow = true;
|
||||
}
|
||||
else {
|
||||
@ -73,6 +60,15 @@ public class CAttackOrder implements COrder {
|
||||
this.wasWithinPropWindow = false;
|
||||
}
|
||||
|
||||
final int cooldownEndTime = this.unit.getCooldownEndTime();
|
||||
final int currentTurnTick = simulation.getGameTurnTick();
|
||||
if (currentTurnTick >= cooldownEndTime) {
|
||||
final float a1Cooldown = simulation.getUnitData().getA1Cooldown(this.unit.getTypeId());
|
||||
final int a1CooldownSteps = (int) (a1Cooldown / WarsmashConstants.SIMULATION_STEP_TIME);
|
||||
this.unit.setCooldownEndTime(currentTurnTick + a1CooldownSteps);
|
||||
simulation.createProjectile(this.unit, 0, this.target);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -83,10 +79,7 @@ public class CAttackOrder implements COrder {
|
||||
|
||||
@Override
|
||||
public String getAnimationName() {
|
||||
if (!this.wasWithinPropWindow) {
|
||||
return "stand";
|
||||
}
|
||||
return "walk";
|
||||
return "attack";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,9 +29,8 @@ public class CMoveOrder implements COrder {
|
||||
if (goalAngle < 0) {
|
||||
goalAngle += 360;
|
||||
}
|
||||
final float facing = this.unit.getFacing();
|
||||
float facing = this.unit.getFacing();
|
||||
float delta = goalAngle - facing;
|
||||
final float absDelta = Math.abs(delta);
|
||||
final float propulsionWindow = simulation.getUnitData().getPropulsionWindow(this.unit.getTypeId());
|
||||
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
|
||||
final int speed = this.unit.getSpeed();
|
||||
@ -42,16 +41,18 @@ public class CMoveOrder implements COrder {
|
||||
if (delta > 180) {
|
||||
delta = -360 + delta;
|
||||
}
|
||||
final float absDelta = Math.abs(delta);
|
||||
|
||||
if ((absDelta <= 1.0) && (absDelta != 0)) {
|
||||
this.unit.setFacing(goalAngle);
|
||||
}
|
||||
else {
|
||||
float angleToAdd = ((Math.signum(delta) * turnRate) * WarsmashConstants.SIMULATION_STEP_TIME) * 360;
|
||||
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
|
||||
if (absDelta < Math.abs(angleToAdd)) {
|
||||
angleToAdd = delta;
|
||||
}
|
||||
this.unit.setFacing(facing + angleToAdd);
|
||||
facing += angleToAdd;
|
||||
this.unit.setFacing(facing);
|
||||
}
|
||||
if (absDelta < propulsionWindow) {
|
||||
final float speedTick = speed * WarsmashConstants.SIMULATION_STEP_TIME;
|
||||
@ -63,8 +64,9 @@ public class CMoveOrder implements COrder {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
this.unit.setX(prevX + (float) (Math.cos(goalAngleRad) * speedTick));
|
||||
this.unit.setY(prevY + (float) (Math.sin(goalAngleRad) * speedTick));
|
||||
final double radianFacing = Math.toRadians(facing);
|
||||
this.unit.setX(prevX + (float) (Math.cos(radianFacing) * speedTick));
|
||||
this.unit.setY(prevY + (float) (Math.sin(radianFacing) * speedTick));
|
||||
}
|
||||
this.wasWithinPropWindow = true;
|
||||
}
|
||||
|
@ -0,0 +1,102 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.projectile;
|
||||
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
|
||||
public class CAttackProjectile {
|
||||
private float x;
|
||||
private float y;
|
||||
private float z;
|
||||
private final float startingHeight;
|
||||
private final float speed;
|
||||
private final float arc;
|
||||
private final CWidget target;
|
||||
private final float halfStartingDistance;
|
||||
private final float arcPeakHeight;
|
||||
private float totalTravelDistance;
|
||||
private boolean done;
|
||||
private final CUnit source;
|
||||
private final int damage;
|
||||
|
||||
public CAttackProjectile(final float x, final float y, final float z, final float speed, final float arc,
|
||||
final CWidget target, final CUnit source, final int damage) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.startingHeight = z;
|
||||
this.speed = speed;
|
||||
this.arc = arc;
|
||||
this.target = target;
|
||||
final float dx = target.getX() - x;
|
||||
final float dy = target.getY() - y;
|
||||
final float startingDistance = (float) Math.sqrt((dx * dx) + (dy * dy));
|
||||
this.halfStartingDistance = startingDistance / 2f;
|
||||
this.arcPeakHeight = arc * startingDistance;
|
||||
this.source = source;
|
||||
this.damage = damage;
|
||||
}
|
||||
|
||||
public boolean update(final CSimulation cSimulation) {
|
||||
final float tx = this.target.getX();
|
||||
final float ty = this.target.getY();
|
||||
final float sx = this.x;
|
||||
final float sy = this.y;
|
||||
final float dtsx = tx - sx;
|
||||
final float dtsy = ty - sy;
|
||||
final float dtsz = this.target.getFlyHeight() - this.startingHeight;
|
||||
final float c = (float) Math.sqrt((dtsx * dtsx) + (dtsy * dtsy));
|
||||
|
||||
final float d1x = dtsx / c;
|
||||
final float d1y = dtsy / c;
|
||||
final float d1z = dtsz / (this.halfStartingDistance * 2);
|
||||
|
||||
float travelDistance = Math.min(c, this.speed * WarsmashConstants.SIMULATION_STEP_TIME);
|
||||
if (c <= travelDistance) {
|
||||
if (!this.done) {
|
||||
this.target.damage(this.source, this.damage);
|
||||
}
|
||||
this.done = true;
|
||||
travelDistance = c;
|
||||
}
|
||||
|
||||
final float dx = d1x * travelDistance;
|
||||
final float dy = d1y * travelDistance;
|
||||
this.totalTravelDistance += travelDistance;
|
||||
final float dz = d1z * this.totalTravelDistance;
|
||||
|
||||
this.x = this.x + dx;
|
||||
this.y = this.y + dy;
|
||||
|
||||
float firstTerm = ((1 / this.halfStartingDistance) * (this.totalTravelDistance - this.halfStartingDistance));
|
||||
firstTerm = firstTerm * firstTerm;
|
||||
this.z = this.startingHeight + ((-firstTerm + 1) * this.arcPeakHeight) + dz;
|
||||
|
||||
return this.done;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return this.x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public float getZ() {
|
||||
return this.z;
|
||||
}
|
||||
|
||||
public float getSpeed() {
|
||||
return this.speed;
|
||||
}
|
||||
|
||||
public CWidget getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return this.done;
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.util;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.projectile.CAttackProjectile;
|
||||
|
||||
public interface ProjectileCreator {
|
||||
CAttackProjectile create(CSimulation simulation, CUnit source, int attackIndex, CWidget target);
|
||||
}
|
Loading…
Reference in New Issue
Block a user