mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
Update for pathing
This commit is contained in:
parent
1c084e9695
commit
93f91b0f4d
@ -1,29 +1,7 @@
|
||||
package com.etheller.warsmash;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.datasources.MpqDataSourceDescriptor;
|
||||
|
||||
public class TestMain {
|
||||
public static void main(final String[] args) {
|
||||
final MpqDataSourceDescriptor desc = new MpqDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127\\Z.mpq");
|
||||
final DataSource createDataSource = desc.createDataSource();
|
||||
try {
|
||||
final InputStream cliffZ = createDataSource.getResourceAsStream("ReplaceableTextures\\Cliff\\Cliff0.blp");
|
||||
final BufferedImage img = ImageIO.read(cliffZ);
|
||||
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
|
||||
}
|
||||
catch (final IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println(Integer.parseInt("4294967295"));
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,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;
|
||||
@ -135,7 +134,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.viewer.worldScene.enableAudio();
|
||||
this.viewer.enableAudio();
|
||||
try {
|
||||
this.viewer.loadMap("American Colo EX 1.0 unpro.w3x");
|
||||
this.viewer.loadMap("Pathing.w3x");
|
||||
}
|
||||
catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -231,11 +230,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
|
||||
Gdx.input.setInputProcessor(this);
|
||||
|
||||
final Music music = Gdx.audio
|
||||
.newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3"));
|
||||
music.setVolume(0.2f);
|
||||
music.setLooping(true);
|
||||
music.play();
|
||||
// final Music music = Gdx.audio.newMusic(
|
||||
// new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\War2IntroMusic.mp3"));
|
||||
// music.setVolume(0.2f);
|
||||
// music.setLooping(true);
|
||||
// music.play();
|
||||
|
||||
this.minimap = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f);
|
||||
final float worldWidth = (this.viewer.terrain.columns - 1);
|
||||
@ -269,8 +268,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
final float deltaTime = Gdx.graphics.getDeltaTime();
|
||||
Gdx.gl30.glBindVertexArray(WarsmashGdxGame.VAO);
|
||||
this.cameraManager.target.add(this.cameraVelocity.x * deltaTime, this.cameraVelocity.y * deltaTime, 0);
|
||||
this.cameraManager.target.z = this.viewer.terrain.getGroundHeight(this.cameraManager.target.x,
|
||||
this.cameraManager.target.y);
|
||||
this.cameraManager.target.z = Math.max(
|
||||
this.viewer.terrain.getGroundHeight(this.cameraManager.target.x, this.cameraManager.target.y),
|
||||
this.viewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y));
|
||||
this.cameraManager.updateCamera();
|
||||
this.portraitCameraManager.updateCamera();
|
||||
this.viewer.updateAndRender();
|
||||
@ -454,10 +454,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.rotationSpeed = (float) (Math.PI / 180);
|
||||
this.zoomFactor = 0.1f;
|
||||
this.horizontalAngle = 0;// (float) (Math.PI / 2);
|
||||
this.verticalAngle = (float) (Math.PI / 5);
|
||||
this.distance = 1600;
|
||||
this.verticalAngle = (float) Math.toRadians(34);
|
||||
this.distance = 1650;
|
||||
this.position = new Vector3();
|
||||
this.target = new Vector3(0, 0, 50);
|
||||
this.target = new Vector3(0, 0, 0);
|
||||
this.worldUp = new Vector3(0, 0, 1);
|
||||
this.vecHeap = new Vector3();
|
||||
this.quatHeap = new Quaternion();
|
||||
@ -469,11 +469,6 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
}
|
||||
|
||||
private void updateCamera() {
|
||||
// Limit the vertical angle so it doesn't flip.
|
||||
// Since the camera uses a quaternion, flips don't matter to it, but this feels
|
||||
// better.
|
||||
this.verticalAngle = (float) Math.min(Math.max(0.01, this.verticalAngle), Math.PI - 0.01);
|
||||
|
||||
this.quatHeap.idt();
|
||||
this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle);
|
||||
this.quatHeap2.idt();
|
||||
@ -504,6 +499,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
this.camera.perspective(this.modelCamera.fieldOfView * 0.75f, this.camera.getAspect(),
|
||||
this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane);
|
||||
}
|
||||
else {
|
||||
this.camera.perspective(70, this.camera.getAspect(), 100, 5000);
|
||||
}
|
||||
|
||||
this.camera.moveToAndFace(this.position, this.target, this.worldUp);
|
||||
}
|
||||
@ -666,13 +664,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
|
||||
|
||||
@Override
|
||||
public boolean scrolled(final int amount) {
|
||||
this.cameraManager.verticalAngle -= amount / 10.f;
|
||||
if (this.cameraManager.verticalAngle > (Math.PI / 2)) {
|
||||
this.cameraManager.verticalAngle = (float) Math.PI / 2;
|
||||
}
|
||||
if (this.cameraManager.verticalAngle < (Math.PI / 5)) {
|
||||
this.cameraManager.verticalAngle = (float) (Math.PI / 5);
|
||||
}
|
||||
// this.cameraManager.verticalAngle -= amount / 10.f;
|
||||
// if (this.cameraManager.verticalAngle > (Math.PI / 2)) {
|
||||
// this.cameraManager.verticalAngle = (float) Math.PI / 2;
|
||||
// }
|
||||
// if (this.cameraManager.verticalAngle < (Math.PI / 5)) {
|
||||
// this.cameraManager.verticalAngle = (float) (Math.PI / 5);
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
package com.etheller.warsmash.gameui;
|
||||
|
||||
import com.etheller.warsmash.parsers.fdf.datamodel.FrameTemplateEnvironment;
|
||||
|
||||
public class FDFGameUI {
|
||||
private final FrameTemplateEnvironment frameTemplateEnvironment;
|
||||
|
||||
public FDFGameUI(final FrameTemplateEnvironment frameTemplateEnvironment) {
|
||||
this.frameTemplateEnvironment = frameTemplateEnvironment;
|
||||
}
|
||||
}
|
@ -114,18 +114,18 @@ public class Sequence implements MdlxBlock {
|
||||
private void populateTags() {
|
||||
this.primaryTags.clear();
|
||||
this.secondaryTags.clear();
|
||||
for (final String token : this.name.split("\\s+")) {
|
||||
TokenLoop: for (final String token : this.name.split("\\s+")) {
|
||||
final String upperCaseToken = token.toUpperCase();
|
||||
for (final PrimaryTag primaryTag : PrimaryTag.values()) {
|
||||
if (upperCaseToken.equals(primaryTag.name())) {
|
||||
this.primaryTags.add(primaryTag);
|
||||
continue;
|
||||
continue TokenLoop;
|
||||
}
|
||||
}
|
||||
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
|
||||
if (upperCaseToken.equals(secondaryTag.name())) {
|
||||
this.secondaryTags.add(secondaryTag);
|
||||
continue;
|
||||
continue TokenLoop;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -159,4 +159,12 @@ public class Sequence implements MdlxBlock {
|
||||
public Extent getExtent() {
|
||||
return this.extent;
|
||||
}
|
||||
|
||||
public EnumSet<AnimationTokens.PrimaryTag> getPrimaryTags() {
|
||||
return this.primaryTags;
|
||||
}
|
||||
|
||||
public EnumSet<AnimationTokens.SecondaryTag> getSecondaryTags() {
|
||||
return this.secondaryTags;
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import com.etheller.warsmash.parsers.w3x.objectdata.Warcraft3MapObjectData;
|
||||
import com.etheller.warsmash.parsers.w3x.unitsdoo.War3MapUnitsDoo;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
|
||||
import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
||||
import com.google.common.io.LittleEndianDataInputStream;
|
||||
|
||||
import mpq.MPQArchive;
|
||||
@ -71,6 +72,15 @@ public class War3Map implements DataSource {
|
||||
return environment;
|
||||
}
|
||||
|
||||
public War3MapWpm readPathing() throws IOException {
|
||||
War3MapWpm pathingMap;
|
||||
try (LittleEndianDataInputStream stream = new LittleEndianDataInputStream(
|
||||
this.dataSource.getResourceAsStream("war3map.wpm"))) {
|
||||
pathingMap = new War3MapWpm(stream);
|
||||
}
|
||||
return pathingMap;
|
||||
}
|
||||
|
||||
public War3MapDoo readDoodads() throws IOException {
|
||||
War3MapDoo doodadsFile;
|
||||
try (LittleEndianDataInputStream stream = new LittleEndianDataInputStream(
|
||||
|
@ -58,7 +58,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
|
||||
|
||||
@Override
|
||||
public ModelInstance createInstance(final int type) {
|
||||
if ((type == 1) && false) {
|
||||
if (type == 1) {
|
||||
return new MdxSimpleInstance(this);
|
||||
}
|
||||
else {
|
||||
|
@ -19,7 +19,7 @@ public class Doodad {
|
||||
final boolean isSimple = row.readSLKTagBoolean("lightweight");
|
||||
ModelInstance instance;
|
||||
|
||||
if (isSimple) {
|
||||
if (isSimple && false) {
|
||||
instance = model.addInstance(1);
|
||||
}
|
||||
else {
|
||||
|
@ -1,11 +1,14 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
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.AnimationTokens.PrimaryTag;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
|
||||
|
||||
public class StandSequence {
|
||||
|
||||
@ -26,6 +29,34 @@ public class StandSequence {
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private static List<IndexedSequence> filterSequences(final PrimaryTag type, final EnumSet<SecondaryTag> tags,
|
||||
final List<Sequence> sequences) {
|
||||
final List<IndexedSequence> filtered = new ArrayList<>();
|
||||
|
||||
for (int i = 0, l = sequences.size(); i < l; i++) {
|
||||
final Sequence sequence = sequences.get(i);
|
||||
if (sequence.getPrimaryTags().contains(type) && sequence.getSecondaryTags().containsAll(tags)
|
||||
&& tags.containsAll(sequence.getSecondaryTags())) {
|
||||
for (final AnimationTokens.SecondaryTag secondaryTag : sequence.getSecondaryTags()) {
|
||||
if (tags.contains(secondaryTag)) {
|
||||
filtered.add(new IndexedSequence(sequence, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filtered.isEmpty()) {
|
||||
for (int i = 0, l = sequences.size(); i < l; i++) {
|
||||
final Sequence sequence = sequences.get(i);
|
||||
if (sequence.getPrimaryTags().contains(type) && sequence.getSecondaryTags().containsAll(tags)
|
||||
&& tags.containsAll(sequence.getSecondaryTags())) {
|
||||
filtered.add(new IndexedSequence(sequence, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
public static IndexedSequence selectSequence(final String type, final List<Sequence> sequences) {
|
||||
final List<IndexedSequence> filtered = filterSequences(type, sequences);
|
||||
|
||||
@ -55,6 +86,36 @@ public class StandSequence {
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public static IndexedSequence selectSequence(final AnimationTokens.PrimaryTag type,
|
||||
final EnumSet<AnimationTokens.SecondaryTag> tags, final List<Sequence> sequences) {
|
||||
final List<IndexedSequence> filtered = filterSequences(type, tags, sequences);
|
||||
|
||||
filtered.sort(STAND_SEQUENCE_COMPARATOR);
|
||||
|
||||
int i = 0;
|
||||
for (final int l = filtered.size(); i < l; i++) {
|
||||
final Sequence sequence = filtered.get(i).sequence;
|
||||
final float rarity = sequence.getRarity();
|
||||
|
||||
if (rarity == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Math.random() * 10) > rarity) {
|
||||
return filtered.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
final int sequencesLeft = filtered.size() - i;
|
||||
final int random = (int) (i + Math.floor(Math.random() * sequencesLeft));
|
||||
if (sequencesLeft <= 0) {
|
||||
return null; // new IndexedSequence(null, 0);
|
||||
}
|
||||
final IndexedSequence sequence = filtered.get(random);
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public static void randomStandSequence(final MdxComplexInstance target) {
|
||||
final MdxModel model = (MdxModel) target.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
@ -145,4 +206,18 @@ public class StandSequence {
|
||||
randomStandSequence(target);
|
||||
}
|
||||
}
|
||||
|
||||
public static void randomSequence(final MdxComplexInstance target, final PrimaryTag animationName,
|
||||
final EnumSet<SecondaryTag> secondaryAnimationTags) {
|
||||
final MdxModel model = (MdxModel) target.model;
|
||||
final List<Sequence> sequences = model.getSequences();
|
||||
final IndexedSequence sequence = selectSequence(animationName, secondaryAnimationTags, sequences);
|
||||
|
||||
if (sequence != null) {
|
||||
target.setSequence(sequence.index);
|
||||
}
|
||||
else {
|
||||
randomStandSequence(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ public class W3xShaders {
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
" varying float a_positionHeight;\r\n" + //
|
||||
" const float normalDist = 0.25;\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" vec2 halfPixel = u_pixel * 0.5;\r\n" + //
|
||||
@ -34,6 +35,7 @@ public class W3xShaders {
|
||||
" v_uv = a_uv;\r\n" + //
|
||||
" v_suv = base / u_size;\r\n" + //
|
||||
" gl_Position = u_mvp * vec4(a_position.xy, height * 128.0 + a_position.z, 1.0);\r\n" + //
|
||||
" a_positionHeight = a_position.z;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" ";
|
||||
|
||||
@ -44,6 +46,7 @@ public class W3xShaders {
|
||||
" varying vec2 v_uv;\r\n" + //
|
||||
" varying vec2 v_suv;\r\n" + //
|
||||
" varying vec3 v_normal;\r\n" + //
|
||||
" varying float a_positionHeight;\r\n" + //
|
||||
" const vec3 lightDirection = normalize(vec3(-0.3, -0.3, 0.25));\r\n" + //
|
||||
" void main() {\r\n" + //
|
||||
" if (any(bvec4(lessThan(v_uv, vec2(0.0)), greaterThan(v_uv, vec2(1.0))))) {\r\n" + //
|
||||
@ -52,7 +55,9 @@ public class W3xShaders {
|
||||
" vec4 color = texture2D(u_texture, clamp(v_uv, 0.0, 1.0)).rgba * u_color;\r\n" + //
|
||||
" float shadow = texture2D(u_shadowMap, v_suv).r;\r\n" + //
|
||||
" color.xyz *= clamp(dot(v_normal, lightDirection) + 0.45, 0.0, 1.0);\r\n" + //
|
||||
" if (a_positionHeight <= 4.0) {;\r\n" + //
|
||||
" color.xyz *= 1.0 - shadow;\r\n" + //
|
||||
" };\r\n" + //
|
||||
" gl_FragColor = color;\r\n" + //
|
||||
" }\r\n" + //
|
||||
" ";
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -35,6 +36,7 @@ import com.etheller.warsmash.parsers.w3x.objectdata.Warcraft3MapObjectData;
|
||||
import com.etheller.warsmash.parsers.w3x.unitsdoo.War3MapUnitsDoo;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
|
||||
import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
@ -57,6 +59,7 @@ import com.etheller.warsmash.viewer5.gl.WebGL;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxHandler;
|
||||
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
|
||||
import com.etheller.warsmash.viewer5.handlers.tga.TgaFile;
|
||||
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;
|
||||
@ -91,6 +94,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
private static final War3ID UNIT_SELECT_HEIGHT = War3ID.fromString("uslz");
|
||||
private static final War3ID UNIT_SOUNDSET = War3ID.fromString("usnd");
|
||||
private static final War3ID ITEM_FILE = War3ID.fromString("ifil");
|
||||
private static final War3ID UNIT_PATHING = War3ID.fromString("upat");
|
||||
private static final War3ID DESTRUCTABLE_PATHING = War3ID.fromString("bptx");
|
||||
private static final War3ID sloc = War3ID.fromString("sloc");
|
||||
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
|
||||
private static final float[] rayHeap = new float[6];
|
||||
@ -146,6 +151,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
private final Random seededRandom = new Random(1337L);
|
||||
|
||||
private final Map<String, BufferedImage> filePathToPathingMap = new HashMap<>();
|
||||
|
||||
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
|
||||
super(dataSource, canvas);
|
||||
this.gameDataSource = dataSource;
|
||||
@ -279,7 +286,10 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
final War3MapW3e terrainData = this.mapMpq.readEnvironment();
|
||||
|
||||
this.terrain = new Terrain(terrainData, w3iFile, this.webGL, this.dataSource, worldEditStrings, this);
|
||||
final War3MapWpm terrainPathing = this.mapMpq.readPathing();
|
||||
|
||||
this.terrain = new Terrain(terrainData, terrainPathing, w3iFile, this.webGL, this.dataSource, worldEditStrings,
|
||||
this);
|
||||
|
||||
final float[] centerOffset = terrainData.getCenterOffset();
|
||||
final int[] mapSize = terrainData.getMapSize();
|
||||
@ -357,7 +367,7 @@ public class War3MapViewer extends ModelViewer {
|
||||
|
||||
return simulationAttackProjectile;
|
||||
}
|
||||
});
|
||||
}, this.terrain.pathingGrid);
|
||||
|
||||
if (this.doodadsAndDestructiblesLoaded) {
|
||||
this.loadDoodadsAndDestructibles(modifications);
|
||||
@ -382,8 +392,6 @@ public class War3MapViewer extends ModelViewer {
|
||||
}
|
||||
|
||||
private void loadDoodadsAndDestructibles(final Warcraft3MapObjectData modifications) throws IOException {
|
||||
final War3MapDoo dooFile = this.mapMpq.readDoodads();
|
||||
|
||||
this.applyModificationFile(this.doodadsData, this.doodadMetaData, modifications.getDoodads(),
|
||||
WorldEditorDataType.DOODADS);
|
||||
this.applyModificationFile(this.doodadsData, this.destructableMetaData, modifications.getDestructibles(),
|
||||
@ -422,6 +430,18 @@ public class War3MapViewer extends ModelViewer {
|
||||
this.terrain.addShadow(shadowString, doodad.getLocation()[0], doodad.getLocation()[1]);
|
||||
}
|
||||
|
||||
final String pathingTexture = row.readSLKTag("pathTex");
|
||||
if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) {
|
||||
|
||||
BufferedImage bufferedImage = this.filePathToPathingMap.get(pathingTexture.toLowerCase());
|
||||
if (bufferedImage == null) {
|
||||
bufferedImage = TgaFile.readTGA(pathingTexture,
|
||||
this.mapMpq.getResourceAsStream(pathingTexture));
|
||||
this.filePathToPathingMap.put(pathingTexture.toLowerCase(), bufferedImage);
|
||||
}
|
||||
this.terrain.pathingGrid.blitPathingOverlayTexture(doodad.getLocation()[0],
|
||||
doodad.getLocation()[1], (int) Math.toDegrees(doodad.getAngle()), bufferedImage);
|
||||
}
|
||||
}
|
||||
// First see if the model is local.
|
||||
// Doodads referring to local models may have invalid variations, so if the
|
||||
@ -580,6 +600,17 @@ public class War3MapViewer extends ModelViewer {
|
||||
if ((buildingShadow != null) && !"_".equals(buildingShadow)) {
|
||||
this.terrain.addShadow(buildingShadow, unit.getLocation()[0], unit.getLocation()[1]);
|
||||
}
|
||||
final String pathingTexture = row.getFieldAsString(UNIT_PATHING, 0);
|
||||
if ((pathingTexture != null) && (pathingTexture.length() > 0) && !"_".equals(pathingTexture)) {
|
||||
BufferedImage bufferedImage = this.filePathToPathingMap.get(pathingTexture.toLowerCase());
|
||||
if (bufferedImage == null) {
|
||||
bufferedImage = TgaFile.readTGA(pathingTexture,
|
||||
this.mapMpq.getResourceAsStream(pathingTexture));
|
||||
this.filePathToPathingMap.put(pathingTexture.toLowerCase(), bufferedImage);
|
||||
}
|
||||
this.terrain.pathingGrid.blitPathingOverlayTexture(unit.getLocation()[0], unit.getLocation()[1],
|
||||
(int) Math.toDegrees(unit.getAngle()), bufferedImage);
|
||||
}
|
||||
|
||||
final String soundName = row.getFieldAsString(UNIT_SOUNDSET, 0);
|
||||
UnitSoundset unitSoundset = soundsetNameToSoundset.get(soundName);
|
||||
@ -816,7 +847,8 @@ public class War3MapViewer extends ModelViewer {
|
||||
RenderUnit entity = null;
|
||||
for (final RenderUnit unit : this.units) {
|
||||
final MdxComplexInstance instance = unit.instance;
|
||||
if (instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap)) {
|
||||
if (instance.isVisible(this.worldScene.camera)
|
||||
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap)) {
|
||||
entity = unit;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,221 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
||||
|
||||
public class PathingGrid {
|
||||
private static final Map<String, MovementType> movetpToMovementType = new HashMap<>();
|
||||
static {
|
||||
for (final MovementType movementType : MovementType.values()) {
|
||||
if (movementType != MovementType.DISABLED) {
|
||||
movetpToMovementType.put(movementType.typeKey, movementType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final short[] pathingGrid;
|
||||
private final short[] dynamicPathingOverlay; // for buildings and trees
|
||||
private final int[] pathingGridSizes;
|
||||
private final float[] centerOffset;
|
||||
|
||||
public PathingGrid(final War3MapWpm terrainPathing, final float[] centerOffset) {
|
||||
this.centerOffset = centerOffset;
|
||||
this.pathingGrid = terrainPathing.getPathing();
|
||||
this.pathingGridSizes = terrainPathing.getSize();
|
||||
this.dynamicPathingOverlay = new short[this.pathingGrid.length];
|
||||
}
|
||||
|
||||
// this blit function is basically copied from HiveWE, maybe remember to mention
|
||||
// that in credits as well:
|
||||
// https://github.com/stijnherfst/HiveWE/blob/master/Base/PathingMap.cpp
|
||||
public void blitPathingOverlayTexture(final float positionX, final float positionY, final int rotation,
|
||||
final BufferedImage pathingTextureTga) {
|
||||
final int divW = ((rotation % 180) != 0) ? pathingTextureTga.getHeight() : pathingTextureTga.getWidth();
|
||||
final int divH = ((rotation % 180) != 0) ? pathingTextureTga.getWidth() : pathingTextureTga.getHeight();
|
||||
for (int j = 0; j < pathingTextureTga.getHeight(); j++) {
|
||||
for (int i = 0; i < pathingTextureTga.getWidth(); i++) {
|
||||
int x = i;
|
||||
int y = j;
|
||||
|
||||
switch (rotation) {
|
||||
case 90:
|
||||
x = pathingTextureTga.getHeight() - 1 - j;
|
||||
y = i;
|
||||
break;
|
||||
case 180:
|
||||
x = pathingTextureTga.getWidth() - 1 - i;
|
||||
y = pathingTextureTga.getHeight() - 1 - j;
|
||||
break;
|
||||
case 270:
|
||||
x = j;
|
||||
y = pathingTextureTga.getWidth() - 1 - i;
|
||||
break;
|
||||
}
|
||||
// Width and height for centering change if rotation is not divisible by 180
|
||||
final int xx = (getCellX(positionX) + x) - (divW / 2);
|
||||
final int yy = (getCellY(positionY) + y) - (divH / 2);
|
||||
|
||||
if ((xx < 0) || (xx > (this.pathingGridSizes[0] - 1)) || (yy < 0)
|
||||
|| (yy > (this.pathingGridSizes[1] - 1))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final int rgb = pathingTextureTga.getRGB(i, pathingTextureTga.getHeight() - 1 - j);
|
||||
byte data = 0;
|
||||
if ((rgb & 0xFF) > 250) {
|
||||
data |= PathingFlags.UNBUILDABLE;
|
||||
}
|
||||
if (((rgb & 0xFF00) >> 8) > 250) {
|
||||
data |= PathingFlags.UNFLYABLE;
|
||||
}
|
||||
if (((rgb & 0xFF0000) >> 16) > 250) {
|
||||
data |= PathingFlags.UNWALKABLE;
|
||||
}
|
||||
this.dynamicPathingOverlay[(yy * this.pathingGridSizes[0]) + xx] |= data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return this.pathingGridSizes[0];
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return this.pathingGridSizes[1];
|
||||
}
|
||||
|
||||
public short getPathing(final float x, final float y) {
|
||||
return getCellPathing(getCellX(x), getCellY(y));
|
||||
}
|
||||
|
||||
public int getCellX(final float x) {
|
||||
final float userCellSpaceX = (x - this.centerOffset[0]) / 32.0f;
|
||||
final int cellX = (int) userCellSpaceX;
|
||||
return cellX;
|
||||
}
|
||||
|
||||
public int getCellY(final float y) {
|
||||
final float userCellSpaceY = (y - this.centerOffset[1]) / 32.0f;
|
||||
final int cellY = (int) userCellSpaceY;
|
||||
return cellY;
|
||||
}
|
||||
|
||||
public float getWorldX(final int cellX) {
|
||||
return (cellX * 32f) + this.centerOffset[0] + 16f;
|
||||
}
|
||||
|
||||
public float getWorldY(final int cellY) {
|
||||
return (cellY * 32f) + this.centerOffset[1] + 16f;
|
||||
}
|
||||
|
||||
public short getCellPathing(final int cellX, final int cellY) {
|
||||
return (short) (this.pathingGrid[(cellY * this.pathingGridSizes[0]) + cellX]
|
||||
| this.dynamicPathingOverlay[(cellY * this.pathingGridSizes[0]) + cellX]);
|
||||
}
|
||||
|
||||
public boolean isPathable(final float x, final float y, final PathingType pathingType) {
|
||||
return !PathingFlags.isPathingFlag(getPathing(x, y), pathingType.preventionFlag);
|
||||
}
|
||||
|
||||
public boolean isPathable(final float x, final float y, final MovementType pathingType) {
|
||||
return pathingType.isPathable(getPathing(x, y));
|
||||
}
|
||||
|
||||
public boolean isCellPathable(final int x, final int y, final MovementType pathingType) {
|
||||
return pathingType.isPathable(getCellPathing(x, y));
|
||||
}
|
||||
|
||||
public static boolean isPathingFlag(final short pathingValue, final PathingType pathingType) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, pathingType.preventionFlag);
|
||||
}
|
||||
|
||||
// movetp referring to the unit data field of the same name
|
||||
public static MovementType getMovementType(final String movetp) {
|
||||
return movetpToMovementType.get(movetp);
|
||||
}
|
||||
|
||||
public static final class PathingFlags {
|
||||
public static int UNWALKABLE = 0x2;
|
||||
public static int UNFLYABLE = 0x4;
|
||||
public static int UNBUILDABLE = 0x8;
|
||||
public static int UNSWIMABLE = 0x40; // PROBABLY, didn't confirm this flag value is accurate
|
||||
|
||||
public static boolean isPathingFlag(final short pathingValue, final int flag) {
|
||||
return (pathingValue & flag) != 0;
|
||||
}
|
||||
|
||||
private PathingFlags() {
|
||||
}
|
||||
}
|
||||
|
||||
public static enum MovementType {
|
||||
FOOT("foot") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNWALKABLE);
|
||||
}
|
||||
},
|
||||
HORSE("horse") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNWALKABLE);
|
||||
}
|
||||
},
|
||||
FLY("fly") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNFLYABLE);
|
||||
}
|
||||
},
|
||||
HOVER("hover") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNWALKABLE);
|
||||
}
|
||||
},
|
||||
FLOAT("float") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNSWIMABLE);
|
||||
}
|
||||
},
|
||||
AMPHIBIOUS("amph") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNWALKABLE)
|
||||
|| !PathingFlags.isPathingFlag(pathingValue, PathingFlags.UNSWIMABLE);
|
||||
}
|
||||
},
|
||||
DISABLED("") {
|
||||
@Override
|
||||
public boolean isPathable(final short pathingValue) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private final String typeKey;
|
||||
|
||||
// TODO windwalk pathing type can walk through units but not through items
|
||||
|
||||
private MovementType(final String typeKey) {
|
||||
this.typeKey = typeKey;
|
||||
}
|
||||
|
||||
public abstract boolean isPathable(short pathingValue);
|
||||
}
|
||||
|
||||
public static enum PathingType {
|
||||
WALKABLE(PathingFlags.UNWALKABLE),
|
||||
FLYABLE(PathingFlags.UNFLYABLE),
|
||||
BUILDABLE(PathingFlags.UNBUILDABLE),
|
||||
SWIMMABLE(PathingFlags.UNSWIMABLE);
|
||||
|
||||
private final int preventionFlag;
|
||||
|
||||
private PathingType(final int preventionFlag) {
|
||||
this.preventionFlag = preventionFlag;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import com.etheller.warsmash.datasources.DataSource;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.Corner;
|
||||
import com.etheller.warsmash.parsers.w3x.w3e.War3MapW3e;
|
||||
import com.etheller.warsmash.parsers.w3x.w3i.War3MapW3i;
|
||||
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
|
||||
import com.etheller.warsmash.units.DataTable;
|
||||
import com.etheller.warsmash.units.Element;
|
||||
import com.etheller.warsmash.units.StandardObjectData;
|
||||
@ -124,8 +125,9 @@ public class Terrain {
|
||||
private final int testArrayBuffer;
|
||||
private final int testElementBuffer;
|
||||
|
||||
public Terrain(final War3MapW3e w3eFile, final War3MapW3i w3iFile, final WebGL webGL, final DataSource dataSource,
|
||||
final WorldEditStrings worldEditStrings, final War3MapViewer viewer) throws IOException {
|
||||
public Terrain(final War3MapW3e w3eFile, final War3MapWpm terrainPathing, final War3MapW3i w3iFile,
|
||||
final WebGL webGL, final DataSource dataSource, final WorldEditStrings worldEditStrings,
|
||||
final War3MapViewer viewer) throws IOException {
|
||||
this.webGL = webGL;
|
||||
this.viewer = viewer;
|
||||
this.camera = viewer.worldScene.camera;
|
||||
@ -394,6 +396,7 @@ public class Terrain {
|
||||
|
||||
this.waveBuilder = new WaveBuilder(this.mapSize, this.waterTable, viewer, this.corners, this.centerOffset,
|
||||
this.waterHeightOffset, w3eFile, w3iFile);
|
||||
this.pathingGrid = new PathingGrid(terrainPathing, this.centerOffset);
|
||||
}
|
||||
|
||||
public void createWaves() {
|
||||
@ -1147,6 +1150,7 @@ public class Terrain {
|
||||
static Vector3 tmp2 = new Vector3();
|
||||
static Vector3 tmp3 = new Vector3();
|
||||
private final WaveBuilder waveBuilder;
|
||||
public PathingGrid pathingGrid;
|
||||
|
||||
/**
|
||||
* Intersects the given ray with list of triangles. Returns the nearest
|
||||
@ -1246,6 +1250,34 @@ public class Terrain {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public float getWaterHeight(final float x, final float y) {
|
||||
final float userCellSpaceX = (x - this.centerOffset[0]) / 128.0f;
|
||||
final float userCellSpaceY = (y - this.centerOffset[1]) / 128.0f;
|
||||
final int cellX = (int) userCellSpaceX;
|
||||
final int cellY = (int) userCellSpaceY;
|
||||
|
||||
if ((cellX >= 0) && (cellX < (this.mapSize[0] - 1)) && (cellY >= 0) && (cellY < (this.mapSize[1] - 1))) {
|
||||
final float bottomLeft = this.waterHeights[(cellY * this.columns) + cellX];
|
||||
final float bottomRight = this.waterHeights[(cellY * this.columns) + cellX + 1];
|
||||
final float topLeft = this.waterHeights[((cellY + 1) * this.columns) + cellX];
|
||||
final float topRight = this.waterHeights[((cellY + 1) * this.columns) + cellX + 1];
|
||||
final float sqX = userCellSpaceX - cellX;
|
||||
final float sqY = userCellSpaceY - cellY;
|
||||
float height;
|
||||
|
||||
if ((sqX + sqY) < 1) {
|
||||
height = bottomLeft + ((bottomRight - bottomLeft) * sqX) + ((topLeft - bottomLeft) * sqY);
|
||||
}
|
||||
else {
|
||||
height = topRight + ((bottomRight - topRight) * (1 - sqY)) + ((topLeft - topRight) * (1 - sqX));
|
||||
}
|
||||
|
||||
return ((height + this.waterHeightOffset) * 128.0f);
|
||||
}
|
||||
|
||||
return this.waterHeightOffset * 128.0f;
|
||||
}
|
||||
|
||||
public static final class Splat {
|
||||
public List<float[]> locations = new ArrayList<>();
|
||||
public List<Consumer<SplatMover>> unitMapping = new ArrayList<>();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
@ -12,11 +13,15 @@ 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.AnimationTokens;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
|
||||
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;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.MovementType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
|
||||
@ -44,7 +49,7 @@ public class RenderUnit {
|
||||
public int playerIndex;
|
||||
private final CUnit simulationUnit;
|
||||
private COrder lastOrder;
|
||||
private String lastOrderAnimation;
|
||||
private AnimationTokens.PrimaryTag lastOrderAnimation;
|
||||
public SplatMover shadow;
|
||||
public SplatMover selectionCircle;
|
||||
private final List<CommandCardIcon> commandCardIcons = new ArrayList<>();
|
||||
@ -53,6 +58,11 @@ public class RenderUnit {
|
||||
private float y;
|
||||
private float facing;
|
||||
|
||||
private boolean swimming;
|
||||
|
||||
private final EnumSet<AnimationTokens.SecondaryTag> secondaryAnimationTags = EnumSet
|
||||
.noneOf(AnimationTokens.SecondaryTag.class);
|
||||
|
||||
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) {
|
||||
@ -152,7 +162,30 @@ public class RenderUnit {
|
||||
final float y = this.y;
|
||||
final float dy = y - this.location[1];
|
||||
this.location[1] = y;
|
||||
this.location[2] = this.simulationUnit.getFlyHeight() + map.terrain.getGroundHeight(x, 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);
|
||||
}
|
||||
boolean changedAnimationTags = false;
|
||||
if (swimming && !this.swimming) {
|
||||
this.secondaryAnimationTags.add(AnimationTokens.SecondaryTag.SWIM);
|
||||
changedAnimationTags = true;
|
||||
}
|
||||
else if (!swimming && this.swimming) {
|
||||
this.secondaryAnimationTags.remove(AnimationTokens.SecondaryTag.SWIM);
|
||||
changedAnimationTags = true;
|
||||
}
|
||||
this.swimming = swimming;
|
||||
this.location[2] = this.simulationUnit.getFlyHeight() + groundHeight;
|
||||
this.instance.moveTo(this.location);
|
||||
float simulationFacing = this.simulationUnit.getFacing();
|
||||
if (simulationFacing < 0) {
|
||||
@ -190,14 +223,16 @@ public class RenderUnit {
|
||||
else if (mdxComplexInstance.sequenceEnded || (mdxComplexInstance.sequence == -1)
|
||||
|| (currentOrder != this.lastOrder)
|
||||
|| ((currentOrder != null) && (currentOrder.getAnimationName() != null)
|
||||
&& !currentOrder.getAnimationName().equals(this.lastOrderAnimation))) {
|
||||
&& !currentOrder.getAnimationName().equals(this.lastOrderAnimation))
|
||||
|| changedAnimationTags) {
|
||||
if (this.simulationUnit.getCurrentOrder() != null) {
|
||||
final String animationName = this.simulationUnit.getCurrentOrder().getAnimationName();
|
||||
StandSequence.randomSequence(mdxComplexInstance, animationName);
|
||||
final AnimationTokens.PrimaryTag animationName = this.simulationUnit.getCurrentOrder()
|
||||
.getAnimationName();
|
||||
StandSequence.randomSequence(mdxComplexInstance, animationName, this.secondaryAnimationTags);
|
||||
this.lastOrderAnimation = animationName;
|
||||
}
|
||||
else {
|
||||
StandSequence.randomStandSequence(mdxComplexInstance);
|
||||
StandSequence.randomSequence(mdxComplexInstance, PrimaryTag.STAND, this.secondaryAnimationTags);
|
||||
}
|
||||
}
|
||||
this.lastOrder = currentOrder;
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
||||
|
||||
public interface COrder {
|
||||
/**
|
||||
* Executes one step of game simulation of the current order, returning true if
|
||||
@ -25,5 +27,5 @@ public interface COrder {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getAnimationName();
|
||||
AnimationTokens.PrimaryTag getAnimationName();
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
import java.awt.Point;
|
||||
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.environment.PathingGrid;
|
||||
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.pathing.CPathfindingProcessor;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.projectile.CAttackProjectile;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ProjectileCreator;
|
||||
|
||||
@ -19,10 +22,14 @@ public class CSimulation {
|
||||
private final HandleIdAllocator handleIdAllocator;
|
||||
private final ProjectileCreator projectileCreator;
|
||||
private int gameTurnTick = 0;
|
||||
private final PathingGrid pathingGrid;
|
||||
private final CPathfindingProcessor pathfindingProcessor;
|
||||
|
||||
public CSimulation(final MutableObjectData parsedUnitData, final MutableObjectData parsedAbilityData,
|
||||
final ProjectileCreator projectileCreator) {
|
||||
final ProjectileCreator projectileCreator, final PathingGrid pathingGrid) {
|
||||
this.projectileCreator = projectileCreator;
|
||||
this.pathingGrid = pathingGrid;
|
||||
this.pathfindingProcessor = new CPathfindingProcessor(pathingGrid);
|
||||
this.unitData = new CUnitData(parsedUnitData);
|
||||
this.abilityData = new CAbilityData(parsedAbilityData);
|
||||
this.units = new ArrayList<>();
|
||||
@ -56,6 +63,15 @@ public class CSimulation {
|
||||
return projectile;
|
||||
}
|
||||
|
||||
public PathingGrid getPathingGrid() {
|
||||
return this.pathingGrid;
|
||||
}
|
||||
|
||||
public List<Point> findNaiveSlowPath(final int startX, final int startY, final int goalX, final int goalY,
|
||||
final PathingGrid.MovementType movementType) {
|
||||
return this.pathfindingProcessor.findNaiveSlowPath(startX, startY, goalX, goalY, movementType);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
for (final CUnit unit : this.units) {
|
||||
unit.update(this);
|
||||
|
@ -0,0 +1,10 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
/**
|
||||
* Provides limited access to game map data when necessary for the game
|
||||
* simulation logic. Do not add methods to this to query anything that isn't
|
||||
* going to be network sync'ed.
|
||||
*/
|
||||
public interface CSimulationMapData {
|
||||
short getTerrainPathing(float x, float y);
|
||||
}
|
@ -23,10 +23,11 @@ public class CUnit extends CWidget {
|
||||
|
||||
private COrder currentOrder;
|
||||
private final Queue<COrder> orderQueue = new LinkedList<>();
|
||||
private final CUnitType unitType;
|
||||
|
||||
public CUnit(final int handleId, final int playerIndex, 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 defaultFlyingHeight) {
|
||||
final int speed, final CUnitType unitType) {
|
||||
super(handleId, x, y, life);
|
||||
this.typeId = typeId;
|
||||
this.facing = facing;
|
||||
@ -34,7 +35,8 @@ public class CUnit extends CWidget {
|
||||
this.maximumLife = maximumLife;
|
||||
this.maximumMana = maximumMana;
|
||||
this.speed = speed;
|
||||
this.flyHeight = defaultFlyingHeight;
|
||||
this.flyHeight = unitType.getDefaultFlyingHeight();
|
||||
this.unitType = unitType;
|
||||
}
|
||||
|
||||
public void add(final CSimulation simulation, final CAbility ability) {
|
||||
@ -150,4 +152,8 @@ public class CUnit extends CWidget {
|
||||
this.playerIndex = playerIndex;
|
||||
}
|
||||
|
||||
public CUnitType getUnitType() {
|
||||
return this.unitType;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.MovementType;
|
||||
|
||||
/**
|
||||
* The quick (symbol table instead of map) lookup for unit type values that we
|
||||
* probably cannot change per unit instance.
|
||||
*/
|
||||
public class CUnitType {
|
||||
private final PathingGrid.MovementType movementType;
|
||||
private final float defaultFlyingHeight;
|
||||
|
||||
public CUnitType(final MovementType movementType, final float defaultFlyingHeight) {
|
||||
this.movementType = movementType;
|
||||
this.defaultFlyingHeight = defaultFlyingHeight;
|
||||
}
|
||||
|
||||
public float getDefaultFlyingHeight() {
|
||||
return this.defaultFlyingHeight;
|
||||
}
|
||||
|
||||
public PathingGrid.MovementType getMovementType() {
|
||||
return this.movementType;
|
||||
}
|
||||
}
|
@ -1,10 +1,15 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.data;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData;
|
||||
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
|
||||
import com.etheller.warsmash.util.War3ID;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
|
||||
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.CUnitType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityHoldPosition;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
|
||||
@ -39,7 +44,9 @@ public class CUnitData {
|
||||
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 static final War3ID MOVE_TYPE = War3ID.fromString("umvt");
|
||||
private final MutableObjectData unitData;
|
||||
private final Map<War3ID, CUnitType> unitIdToUnitType = new HashMap<>();
|
||||
|
||||
public CUnitData(final MutableObjectData unitData) {
|
||||
this.unitData = unitData;
|
||||
@ -53,8 +60,10 @@ public class CUnitData {
|
||||
final int manaMaximum = unitType.getFieldAsInteger(MANA_MAXIMUM, 0);
|
||||
final int speed = unitType.getFieldAsInteger(MOVEMENT_SPEED_BASE, 0);
|
||||
final float moveHeight = unitType.getFieldAsFloat(MOVE_HEIGHT, 0);
|
||||
final String movetp = unitType.getFieldAsString(MOVE_TYPE, 0);
|
||||
final PathingGrid.MovementType movementType = PathingGrid.getMovementType(movetp);
|
||||
final CUnit unit = new CUnit(handleId, playerIndex, x, y, life, typeId, facing, manaInitial, life, manaMaximum,
|
||||
speed, moveHeight);
|
||||
speed, new CUnitType(movementType, moveHeight));
|
||||
if (speed > 0) {
|
||||
unit.add(simulation, CAbilityMove.INSTANCE);
|
||||
unit.add(simulation, CAbilityPatrol.INSTANCE);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
|
||||
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
@ -80,8 +81,8 @@ public class CAttackOrder implements COrder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAnimationName() {
|
||||
return "attack";
|
||||
public AnimationTokens.PrimaryTag getAnimationName() {
|
||||
return AnimationTokens.PrimaryTag.ATTACK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
|
||||
@ -21,8 +22,8 @@ public class CDoNothingOrder implements COrder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAnimationName() {
|
||||
return "stand";
|
||||
public AnimationTokens.PrimaryTag getAnimationName() {
|
||||
return AnimationTokens.PrimaryTag.STAND;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.List;
|
||||
|
||||
import com.etheller.warsmash.util.WarsmashConstants;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.MovementType;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
|
||||
@ -11,6 +17,8 @@ public class CMoveOrder implements COrder {
|
||||
private final float targetX;
|
||||
private final float targetY;
|
||||
private boolean wasWithinPropWindow = false;
|
||||
private List<Point> path = null;
|
||||
private boolean recalculated = false;
|
||||
|
||||
public CMoveOrder(final CUnit unit, final float targetX, final float targetY) {
|
||||
this.unit = unit;
|
||||
@ -22,8 +30,82 @@ public class CMoveOrder implements COrder {
|
||||
public boolean update(final CSimulation simulation) {
|
||||
final float prevX = this.unit.getX();
|
||||
final float prevY = this.unit.getY();
|
||||
final float deltaY = this.targetY - prevY;
|
||||
final float deltaX = this.targetX - prevX;
|
||||
|
||||
final MovementType movementType = this.unit.getUnitType().getMovementType();
|
||||
final PathingGrid pathingGrid = simulation.getPathingGrid();
|
||||
final int startCellX = pathingGrid.getCellX(prevX);
|
||||
final int startCellY = pathingGrid.getCellY(prevY);
|
||||
final int goalCellX = pathingGrid.getCellX(this.targetX);
|
||||
final int goalCellY = pathingGrid.getCellY(this.targetY);
|
||||
if (this.path == null) {
|
||||
this.path = simulation.findNaiveSlowPath(startCellX, startCellY, goalCellX, goalCellY, movementType);
|
||||
// check for smoothing
|
||||
if (!this.path.isEmpty()) {
|
||||
int lastX = startCellX;
|
||||
int lastY = startCellY;
|
||||
int smoothingGroupStartX = startCellX;
|
||||
int smoothingGroupStartY = startCellY;
|
||||
final Point firstPathElement = this.path.get(0);
|
||||
double totalPathDistance = firstPathElement.distance(lastX, lastY);
|
||||
lastX = firstPathElement.x;
|
||||
lastY = firstPathElement.y;
|
||||
int smoothingStartIndex = -1;
|
||||
for (int i = 0; i < (this.path.size() - 1); i++) {
|
||||
final Point nextPossiblePathElement = this.path.get(i + 1);
|
||||
totalPathDistance += nextPossiblePathElement.distance(lastX, lastY);
|
||||
if ((totalPathDistance < (1.25
|
||||
* nextPossiblePathElement.distance(smoothingGroupStartX, smoothingGroupStartY)))
|
||||
&& pathingGrid.isCellPathable((smoothingGroupStartX + nextPossiblePathElement.x) / 2,
|
||||
(smoothingGroupStartY + nextPossiblePathElement.y) / 2, movementType)) {
|
||||
if (smoothingStartIndex == -1) {
|
||||
smoothingStartIndex = i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (smoothingStartIndex != -1) {
|
||||
for (int j = smoothingStartIndex; j < i; j++) {
|
||||
final Point removed = this.path.remove(j);
|
||||
}
|
||||
i = smoothingStartIndex;
|
||||
}
|
||||
smoothingStartIndex = -1;
|
||||
final Point smoothGroupNext = this.path.get(i);
|
||||
smoothingGroupStartX = smoothGroupNext.x;
|
||||
smoothingGroupStartY = smoothGroupNext.y;
|
||||
totalPathDistance = nextPossiblePathElement.distance(smoothGroupNext);
|
||||
}
|
||||
lastX = nextPossiblePathElement.x;
|
||||
lastY = nextPossiblePathElement.y;
|
||||
}
|
||||
if (smoothingStartIndex != -1) {
|
||||
for (int j = smoothingStartIndex; j < (this.path.size() - 1); j++) {
|
||||
final Point removed = this.path.remove(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
float currentTargetX;
|
||||
float currentTargetY;
|
||||
if (this.path.size() < 2) {
|
||||
currentTargetX = this.targetX;
|
||||
currentTargetY = this.targetY;
|
||||
}
|
||||
else {
|
||||
Point nextPathElement = this.path.get(0);
|
||||
if ((this.path.size() > 2) && !this.recalculated) {
|
||||
final Point secondPathElement = this.path.get(1);
|
||||
if ((secondPathElement.distance(startCellX, startCellY) >= 5 /* 5 nodes */)
|
||||
&& (nextPathElement.distance(startCellX, startCellY) <= 2)) {
|
||||
nextPathElement = secondPathElement;
|
||||
this.path.remove(0);
|
||||
}
|
||||
}
|
||||
currentTargetX = pathingGrid.getWorldX(nextPathElement.x);
|
||||
currentTargetY = pathingGrid.getWorldY(nextPathElement.y);
|
||||
}
|
||||
|
||||
float deltaX = currentTargetX - prevX;
|
||||
float deltaY = currentTargetY - prevY;
|
||||
final double goalAngleRad = Math.atan2(deltaY, deltaX);
|
||||
float goalAngle = (float) Math.toDegrees(goalAngleRad);
|
||||
if (goalAngle < 0) {
|
||||
@ -56,20 +138,61 @@ public class CMoveOrder implements COrder {
|
||||
}
|
||||
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.targetX);
|
||||
this.unit.setY(this.targetY);
|
||||
return true;
|
||||
double continueDistance = speedTick;
|
||||
do {
|
||||
boolean done;
|
||||
float nextX, nextY;
|
||||
final double travelDistance = Math.sqrt((deltaX * deltaX) + (deltaY * deltaY));
|
||||
if (travelDistance <= continueDistance) {
|
||||
nextX = currentTargetX;
|
||||
nextY = currentTargetY;
|
||||
continueDistance = continueDistance - travelDistance;
|
||||
done = true;
|
||||
}
|
||||
else {
|
||||
final double radianFacing = Math.toRadians(facing);
|
||||
this.unit.setX(prevX + (float) (Math.cos(radianFacing) * speedTick));
|
||||
this.unit.setY(prevY + (float) (Math.sin(radianFacing) * speedTick));
|
||||
nextX = (prevX + (float) (Math.cos(radianFacing) * continueDistance));
|
||||
nextY = (prevY + (float) (Math.sin(radianFacing) * continueDistance));
|
||||
continueDistance = 0;
|
||||
done = (pathingGrid.getCellX(nextX) == pathingGrid.getCellX(currentTargetX))
|
||||
&& (pathingGrid.getCellY(nextY) == pathingGrid.getCellY(currentTargetY));
|
||||
}
|
||||
final short terrainPathing = pathingGrid.getPathing(nextX, nextY);
|
||||
if (movementType.isPathable(terrainPathing)) {
|
||||
this.unit.setX(nextX);
|
||||
this.unit.setY(nextY);
|
||||
if (done) {
|
||||
if (this.path.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
this.path.remove(0);
|
||||
if (this.path.size() < 2) {
|
||||
currentTargetX = this.targetX;
|
||||
currentTargetY = this.targetY;
|
||||
}
|
||||
else {
|
||||
final Point firstPathElement = this.path.get(0);
|
||||
currentTargetX = pathingGrid.getWorldX(firstPathElement.x);
|
||||
currentTargetY = pathingGrid.getWorldY(firstPathElement.y);
|
||||
}
|
||||
deltaY = currentTargetY - prevY;
|
||||
deltaX = currentTargetX - prevX;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.path = simulation.findNaiveSlowPath(startCellX, startCellY, goalCellX, goalCellY,
|
||||
movementType);
|
||||
this.recalculated = true;
|
||||
if (this.path.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
this.wasWithinPropWindow = true;
|
||||
}
|
||||
while (continueDistance > 0);
|
||||
}
|
||||
else {
|
||||
// If this happens, the unit is facing the wrong way, and has to turn before
|
||||
// moving.
|
||||
@ -85,11 +208,11 @@ public class CMoveOrder implements COrder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAnimationName() {
|
||||
public AnimationTokens.PrimaryTag getAnimationName() {
|
||||
if (!this.wasWithinPropWindow) {
|
||||
return "stand";
|
||||
return AnimationTokens.PrimaryTag.STAND;
|
||||
}
|
||||
return "walk";
|
||||
return AnimationTokens.PrimaryTag.WALK;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,156 @@
|
||||
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.PriorityQueue;
|
||||
|
||||
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
|
||||
|
||||
public class CPathfindingProcessor {
|
||||
private final PathingGrid pathingGrid;
|
||||
private final Node[][] nodes;
|
||||
private Node goal;
|
||||
|
||||
public CPathfindingProcessor(final PathingGrid pathingGrid) {
|
||||
this.pathingGrid = pathingGrid;
|
||||
this.nodes = new Node[pathingGrid.getHeight()][pathingGrid.getWidth()];
|
||||
for (int i = 0; i < this.nodes.length; i++) {
|
||||
for (int j = 0; j < this.nodes[i].length; j++) {
|
||||
this.nodes[i][j] = new Node(new Point(j, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to a point using a naive, slow, and unoptimized algorithm.
|
||||
* Does not have optimizations yet, do this for a bunch of units and it will
|
||||
* probably lag like a walrus. The implementation here was created by reading
|
||||
* the wikipedia article on A* to jog my memory from data structures class back
|
||||
* in college, and is meant only as a first draft to get things working.
|
||||
*
|
||||
*
|
||||
* @param start
|
||||
* @param goal
|
||||
* @return
|
||||
*/
|
||||
public List<Point> findNaiveSlowPath(final int startX, final int startY, final int goalX, final int goalY,
|
||||
final PathingGrid.MovementType movementType) {
|
||||
if ((startX == goalX) && (startY == goalY)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
this.goal = this.nodes[goalY][goalX];
|
||||
final Node start = this.nodes[startY][startX];
|
||||
for (int i = 0; i < this.nodes.length; i++) {
|
||||
for (int j = 0; j < this.nodes[i].length; j++) {
|
||||
this.nodes[i][j].g = Float.POSITIVE_INFINITY;
|
||||
this.nodes[i][j].f = Float.POSITIVE_INFINITY;
|
||||
this.nodes[i][j].cameFrom = null;
|
||||
}
|
||||
}
|
||||
start.g = 0;
|
||||
start.f = h(start);
|
||||
final PriorityQueue<Node> openSet = new PriorityQueue<>(new Comparator<Node>() {
|
||||
@Override
|
||||
public int compare(final Node a, final Node b) {
|
||||
return Double.compare(f(a), f(b));
|
||||
}
|
||||
});
|
||||
openSet.add(start);
|
||||
|
||||
while (!openSet.isEmpty()) {
|
||||
Node current = openSet.poll();
|
||||
if (current == this.goal) {
|
||||
final LinkedList<Point> totalPath = new LinkedList<>();
|
||||
Direction lastCameFromDirection = null;
|
||||
while (current.cameFrom != null) {
|
||||
if ((lastCameFromDirection == null) || (current.cameFromDirection != lastCameFromDirection)) {
|
||||
totalPath.addFirst(current.point);
|
||||
lastCameFromDirection = current.cameFromDirection;
|
||||
}
|
||||
current = current.cameFrom;
|
||||
}
|
||||
return totalPath;
|
||||
}
|
||||
|
||||
for (final Direction direction : Direction.VALUES) {
|
||||
final int x = current.point.x + direction.xOffset;
|
||||
final int y = current.point.y + direction.yOffset;
|
||||
if ((x >= 0) && (x < this.pathingGrid.getWidth()) && (y >= 0) && (y < this.pathingGrid.getHeight())
|
||||
&& movementType.isPathable(this.pathingGrid.getCellPathing(x, y))
|
||||
&& movementType.isPathable(this.pathingGrid.getCellPathing(current.point.x, y))
|
||||
&& movementType.isPathable(this.pathingGrid.getCellPathing(x, current.point.y))) {
|
||||
double turnCost;
|
||||
if ((current.cameFromDirection != null) && (direction != current.cameFromDirection)) {
|
||||
turnCost = 0.25;
|
||||
}
|
||||
else {
|
||||
turnCost = 0;
|
||||
}
|
||||
final double tentativeScore = current.g + direction.length + turnCost;
|
||||
final Node neighbor = this.nodes[y][x];
|
||||
if (tentativeScore < neighbor.g) {
|
||||
neighbor.cameFrom = current;
|
||||
neighbor.cameFromDirection = direction;
|
||||
neighbor.g = tentativeScore;
|
||||
neighbor.f = tentativeScore + h(neighbor);
|
||||
if (!openSet.contains(neighbor)) {
|
||||
openSet.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public double f(final Node n) {
|
||||
return n.g + h(n);
|
||||
}
|
||||
|
||||
public double g(final Node n) {
|
||||
return n.g;
|
||||
}
|
||||
|
||||
public float h(final Node n) {
|
||||
return (float) n.point.distance(this.goal.point);
|
||||
}
|
||||
|
||||
public static final class Node {
|
||||
public Direction cameFromDirection;
|
||||
private final Point point;
|
||||
private double f;
|
||||
private double g;
|
||||
private Node cameFrom;
|
||||
|
||||
private Node(final Point point) {
|
||||
this.point = point;
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Direction {
|
||||
NORTH_WEST(-1, 1),
|
||||
NORTH(0, 1),
|
||||
NORTH_EAST(1, 1),
|
||||
EAST(1, 0),
|
||||
SOUTH_EAST(1, -1),
|
||||
SOUTH(0, -1),
|
||||
SOUTH_WEST(-1, -1),
|
||||
WEST(-1, 0);
|
||||
|
||||
public static final Direction[] VALUES = values();
|
||||
|
||||
private final int xOffset;
|
||||
private final int yOffset;
|
||||
private final double length;
|
||||
|
||||
private Direction(final int xOffset, final int yOffset) {
|
||||
this.xOffset = xOffset;
|
||||
this.yOffset = yOffset;
|
||||
final double sqrt = Math.sqrt((xOffset * xOffset) + (yOffset * yOffset));
|
||||
this.length = sqrt;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import org.lwjgl.opengl.GL33;
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import com.etheller.warsmash.WarsmashGdxGame;
|
||||
import com.etheller.warsmash.WarsmashGdxMapGame;
|
||||
import com.etheller.warsmash.viewer5.gl.ANGLEInstancedArrays;
|
||||
import com.etheller.warsmash.viewer5.gl.DynamicShadowExtension;
|
||||
import com.etheller.warsmash.viewer5.gl.Extensions;
|
||||
@ -58,10 +58,10 @@ public class DesktopLauncher {
|
||||
config.gles30ContextMajorVersion = 3;
|
||||
config.gles30ContextMinorVersion = 3;
|
||||
config.samples = 16;
|
||||
// config.fullscreen = false;
|
||||
// config.fullscreen = true;
|
||||
// final DisplayMode desktopDisplayMode = LwjglApplicationConfiguration.getDesktopDisplayMode();
|
||||
// config.width = desktopDisplayMode.width;
|
||||
// config.height = desktopDisplayMode.height;
|
||||
new LwjglApplication(new WarsmashGdxGame(), config);
|
||||
new LwjglApplication(new WarsmashGdxMapGame(), config);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user