Merge and some fixes, includes click drag ui test

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,12 +35,13 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
@Override
public InputStream read() {
return data;
};
}
;
};
if (data != null) {
return Gdx.audio.newSound(temp);
}
else {
} else {
System.err.println("Warning: missing sound file: " + this.filename);
return null;
}
@ -58,11 +59,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
stringBuilder.append(line);
stringBuilder.append("\n");
}
}
catch (final UnsupportedEncodingException e) {
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
catch (final IOException e) {
} catch (final IOException e) {
throw new RuntimeException(e);
}
return new MappedData(stringBuilder.toString());
@ -96,10 +95,10 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
/**
* If this is an SPL/UBR emitter object, ok will be set to true if the tables
* are loaded.
*
* <p>
* This is because, like the other geometry emitters, it is fine to use them
* even if the textures don't load.
*
* <p>
* The particles will simply be black.
*/
private boolean ok = false;
@ -120,11 +119,9 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
if ("SPL".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPLAT;
}
else if ("UBR".equals(type)) {
} else if ("UBR".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_UBERSPLAT;
}
else if ("SPN".equals(type)) {
} else if ("SPN".equals(type)) {
this.geometryEmitterType = GeometryEmitterFuncs.EMITTER_SPN;
}
@ -144,24 +141,20 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
if ("SPN".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SpawnData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("SPL".equals(type)) {
} else if ("SPL".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\SplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("UBR".equals(type)) {
} else if ("UBR".equals(type)) {
tables.add(viewer.loadGeneric(pathSolver.solve("Splats\\UberSplatData.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else if ("SND".equals(type)) {
} else if ("SND".equals(type)) {
if (!model.reforged) {
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimLookups.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
tables.add(viewer.loadGeneric(pathSolver.solve("UI\\SoundInfo\\AnimSounds.slk", solverParams).finalSrc,
FetchDataTypeName.SLK, mappedDataCallback));
}
else {
} else {
// Units\Critters\BlackStagMale\BlackStagMale.mdx has an event object named
// "Point01".
return;
@ -176,8 +169,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
final Float x = (Float) row.get(name);
if (x == null) {
return Float.NaN;
}
else {
} else {
return x.floatValue();
}
}
@ -190,14 +182,16 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
final Number x = (Number) row.get(name);
if (x == null) {
return defaultValue;
}
else {
} else {
return x.intValue();
}
}
private void load(final List<GenericResource> tables) {
final MappedData firstTable = (MappedData) tables.get(0).data;
if (firstTable == null) {
return;
}
final MappedDataRow row = firstTable.getRow(this.id.trim());
if (row != null) {
@ -214,8 +208,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
// this.internalModel.whenLoaded((model) => this.ok = model.ok)
this.ok = this.internalModel.ok;
}
}
else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
} else if ("SPL".equals(this.type) || "UBR".equals(this.type)) {
final String texturesExt = model.reforged ? ".dds" : ".blp";
this.internalTexture = (Texture) viewer.load(
@ -241,8 +234,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
getFloat(row, "LifespanRepeat")},
{getFloat(row, "UVDecayStart"), getFloat(row, "UVDecayEnd"),
getFloat(row, "DecayRepeat")},};
}
else {
} else {
this.columns = 1;
this.rows = 1;
this.lifeSpan = getFloat(row, "BirthTime") + getFloat(row, "PauseTime") + getFloat(row, "Decay");
@ -258,8 +250,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
this.blendDst = blendModes[1];
this.ok = true;
}
else if ("SND".equals(this.type)) {
} else if ("SND".equals(this.type)) {
// Only load sounds if audio is enabled.
// This is mostly to save on bandwidth and loading time, especially when loading
// full maps.
@ -288,8 +279,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
System.err.println("Null sound: " + fileNames[i]);
}
resources[i] = genericResource;
}
catch (final Exception exc) {
} catch (final Exception exc) {
System.err.println("Failed to load sound: " + path);
exc.printStackTrace();
}
@ -304,12 +294,10 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
this.ok = true;
}
}
}
else {
} else {
System.err.println("Unknown event object type: " + this.type + this.id);
}
}
else {
} else {
System.err.println("Unknown event object ID: " + this.type + this.id);
}
}
@ -318,13 +306,11 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
if (this.globalSequence != -1) {
return this.getValueAtTime(out, instance.counter % this.globalSequence, 0, this.globalSequence);
}
else if (instance.sequence != -1) {
} else if (instance.sequence != -1) {
final long[] interval = this.model.getSequences().get(instance.sequence).getInterval();
return this.getValueAtTime(out, instance.frame, interval[0], interval[1]);
}
else {
} else {
out[0] = this.defval[0];
return -1;
@ -338,8 +324,7 @@ public class EventObjectEmitterObject extends GenericObject implements EmitterOb
out[0] = 0;
return i;
}
else if (this.keyFrames[i] <= frame) {
} else if (this.keyFrames[i] <= frame) {
out[0] = 1;
return i;

View File

@ -160,14 +160,11 @@ public class MdxComplexInstance extends ModelInstance {
if ("SPN".equals(type)) {
emitter = new EventObjectSpnEmitter(this, emitterObject);
}
else if ("SPL".equals(type)) {
} else if ("SPL".equals(type)) {
emitter = new EventObjectSplEmitter(this, emitterObject);
}
else if ("UBR".equals(type)) {
} else if ("UBR".equals(type)) {
emitter = new EventObjectUbrEmitter(this, emitterObject);
}
else {
} else {
emitter = new EventObjectSndEmitter(this, emitterObject);
}
@ -234,22 +231,18 @@ public class MdxComplexInstance extends ModelInstance {
if (genericObject.parentId == -1) {
node.parent = this;
}
else {
} else {
node.parent = nodes[genericObject.parentId];
}
/// TODO: single-axis billboarding
if (genericObject.billboarded != 0) {
node.billboarded = true;
}
else if (genericObject.billboardedX != 0) {
} else if (genericObject.billboardedX != 0) {
node.billboardedX = true;
}
else if (genericObject.billboardedY != 0) {
} else if (genericObject.billboardedY != 0) {
node.billboardedY = true;
}
else if (genericObject.billboardedZ != 0) {
} else if (genericObject.billboardedZ != 0) {
node.billboardedZ = true;
}
@ -412,8 +405,7 @@ public class MdxComplexInstance extends ModelInstance {
geosetColor[3] = alphaHeap[0];
}
}
else if (forced) {
} else if (forced) {
geosetColor[0] = 1;
geosetColor[1] = 1;
geosetColor[2] = 1;
@ -464,8 +456,7 @@ public class MdxComplexInstance extends ModelInstance {
uvAnim[4] = scaleHeap[0];
}
}
else if (forced) {
} else if (forced) {
uvAnim[0] = 0;
uvAnim[1] = 0;
uvAnim[2] = 0;
@ -545,8 +536,7 @@ public class MdxComplexInstance extends ModelInstance {
this.floatingFrame = this.frame = (int) interval[0]; // TODO not cast
this.resetEventEmitters();
}
else if (this.sequenceLoopMode == SequenceLoopMode.LOOP_TO_NEXT_ANIMATION) { // faux queued animation
} else if (this.sequenceLoopMode == SequenceLoopMode.LOOP_TO_NEXT_ANIMATION) { // faux queued animation
// mode
final float framesPast = this.floatingFrame - interval[1];
@ -557,8 +547,7 @@ public class MdxComplexInstance extends ModelInstance {
this.sequenceEnded = false;
this.resetEventEmitters();
this.forced = true;
}
else {
} else {
this.floatingFrame = this.frame = (int) interval[1]; // TODO not cast
this.counter -= integerFrameTime;
this.allowParticleSpawn = false;
@ -568,8 +557,7 @@ public class MdxComplexInstance extends ModelInstance {
}
this.sequenceEnded = true;
}
else {
} else {
this.sequenceEnded = false;
}
}
@ -586,8 +574,7 @@ public class MdxComplexInstance extends ModelInstance {
// Update the batches
this.updateBatches(forced);
}
}
else {
} else {
// let variants = model.variants;
// if (forced || variants.nodes[sequenceId]) {
@ -665,8 +652,7 @@ public class MdxComplexInstance extends ModelInstance {
this.frame = 0;
this.floatingFrame = 0;
this.allowParticleSpawn = false;
}
else {
} else {
this.frame = (int) sequences.get(id).getInterval()[0]; // TODO not cast
this.floatingFrame = this.frame;
this.sequenceEnded = false;
@ -734,15 +720,17 @@ public class MdxComplexInstance extends ModelInstance {
*
* @param ray
*/
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh) {
public boolean intersectRayWithCollision(final Ray ray, final Vector3 intersection, final boolean alwaysUseMesh, final boolean onlyUseMesh) {
final MdxModel mdxModel = (MdxModel) this.model;
final List<CollisionShape> collisionShapes = mdxModel.collisionShapes;
if (!onlyUseMesh) {
for (final CollisionShape collisionShape : collisionShapes) {
final MdxNode mdxNode = this.nodes[collisionShape.index];
if (collisionShape.checkIntersect(ray, mdxNode, intersection)) {
return true;
}
}
}
if (collisionShapes.isEmpty() || alwaysUseMesh) {
for (final Geoset geoset : mdxModel.geosets) {
if (!geoset.unselectable) {

View File

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

View File

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

View File

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

View File

@ -13,8 +13,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
public class RenderDoodad {
private static final int SAMPLE_RADIUS = 4;
private static final War3ID TEX_FILE = War3ID.fromString("btxf");
private static final War3ID TEX_ID = War3ID.fromString("btxi");
public final ModelInstance instance;
private final MutableGameObject row;
private final float maxPitch;
@ -30,8 +28,7 @@ public class RenderDoodad {
if (isSimple && false) {
instance = model.addInstance(1);
}
else {
} else {
instance = model.addInstance();
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
}
@ -74,17 +71,6 @@ public class RenderDoodad {
final float defScale = row.readSLKTagFloat("defScale");
instance.uniformScale(defScale);
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
// TODO destructables need to be their own type, game simulation, etc
String replaceableTextureFile = row.getFieldAsString(TEX_FILE, 0);
final int replaceableTextureId = row.getFieldAsInteger(TEX_ID, 0);
if ((replaceableTextureFile != null) && (replaceableTextureFile.length() > 1)) {
if (!replaceableTextureFile.toLowerCase().endsWith(".blp")) {
replaceableTextureFile += ".blp";
}
instance.setReplaceableTexture(replaceableTextureId, replaceableTextureFile);
}
}
instance.setScene(map.worldScene);
this.instance = instance;

View File

@ -26,6 +26,7 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.math.collision.Ray;
import com.etheller.warsmash.common.FetchDataTypeName;
import com.etheller.warsmash.common.LoadGenericCallback;
@ -48,6 +49,8 @@ import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
import com.etheller.warsmash.util.MappedData;
import com.etheller.warsmash.util.Quadtree;
import com.etheller.warsmash.util.QuadtreeIntersector;
import com.etheller.warsmash.util.RenderMathUtils;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
@ -85,12 +88,14 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackInstant;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttackMissile;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.projectile.CAttackProjectile;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.CWidgetAbilityTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.PointAbilityTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
import mpq.MPQArchive;
@ -117,10 +122,11 @@ public class War3MapViewer extends ModelViewer {
private static final War3ID sloc = War3ID.fromString("sloc");
private static final LoadGenericCallback stringDataCallback = new StringDataCallbackImplementation();
private static final float[] rayHeap = new float[6];
private static final Ray gdxRayHeap = new Ray();
public static final Ray gdxRayHeap = new Ray();
private static final Vector2 mousePosHeap = new Vector2();
private static final Vector3 normalHeap = new Vector3();
private static final Vector3 intersectionHeap = new Vector3();
public static final Vector3 intersectionHeap = new Vector3();
private static final Rectangle rectangleHeap = new Rectangle();
public static final StreamDataCallbackImplementation streamDataCallback = new StreamDataCallbackImplementation();
public PathSolver wc3PathSolver = PathSolver.DEFAULT;
@ -185,6 +191,11 @@ public class War3MapViewer extends ModelViewer {
private GameUI gameUI;
private Vector3 lightDirection;
private Quadtree<MdxComplexInstance> walkableObjectsTree;
private final QuadtreeIntersectorFindsWalkableRenderHeight walkablesIntersector = new QuadtreeIntersectorFindsWalkableRenderHeight();
private final QuadtreeIntersectorFindsHitPoint walkablesIntersectionFinder = new QuadtreeIntersectorFindsHitPoint();
private final QuadtreeIntersectorFindsHighestWalkable intersectorFindsHighestWalkable = new QuadtreeIntersectorFindsHighestWalkable();
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
super(dataSource, canvas);
this.gameDataSource = dataSource;
@ -501,10 +512,9 @@ public class War3MapViewer extends ModelViewer {
War3MapViewer.this.units.remove(renderUnit);
War3MapViewer.this.worldScene.removeInstance(renderUnit.instance);
}
}, this.terrain.pathingGrid,
new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128),
this.seededRandom, w3iFile.getPlayers());
}, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers());
this.walkableObjectsTree = new Quadtree<>(this.terrain.getEntireMap());
if (this.doodadsAndDestructiblesLoaded) {
this.loadDoodadsAndDestructibles(this.allObjectData);
}
@ -632,8 +642,20 @@ public class War3MapViewer extends ModelViewer {
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
this.doodads.add(new RenderDestructable(this, model, row, doodad, type, maxPitch, maxRoll,
doodad.getLife()));
final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type,
maxPitch, maxRoll, doodad.getLife());
if (row.readSLKTagBoolean("walkable")) {
final float x = doodad.getLocation()[0];
final float y = doodad.getLocation()[1];
final BoundingBox boundingBox = model.bounds.getBoundingBox();
final float minX = boundingBox.min.x + x;
final float minY = boundingBox.min.y + y;
final Rectangle renderDestructableBounds = new Rectangle(minX, minY, boundingBox.getWidth(),
boundingBox.getHeight());
this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance,
renderDestructableBounds);
}
this.doodads.add(renderDestructable);
}
else {
this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll));
@ -1080,6 +1102,15 @@ public class War3MapViewer extends ModelViewer {
gdxRayHeap.direction.nor();// needed for libgdx
RenderMathUtils.intersectRayTriangles(gdxRayHeap, this.terrain.softwareGroundMesh.vertices,
this.terrain.softwareGroundMesh.indices, 3, out);
rectangleHeap.set(Math.min(out.x, gdxRayHeap.origin.x), Math.min(out.y, gdxRayHeap.origin.y),
Math.abs(out.x - gdxRayHeap.origin.x), Math.abs(out.y - gdxRayHeap.origin.y));
this.walkableObjectsTree.intersect(rectangleHeap, this.walkablesIntersectionFinder.reset(gdxRayHeap));
if (this.walkablesIntersectionFinder.found) {
out.set(this.walkablesIntersectionFinder.intersection);
}
else {
out.z = Math.max(getWalkableRenderHeight(out.x, out.y), this.terrain.getGroundHeight(out.x, out.y));
}
}
public void showConfirmation(final Vector3 position, final float red, final float green, final float blue) {
@ -1105,11 +1136,13 @@ public class War3MapViewer extends ModelViewer {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera)
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap,
unit.getSimulationUnit().getUnitType().isBuilding())
unit.getSimulationUnit().getUnitType().isBuilding(), false)
&& !unit.getSimulationUnit().isDead()) {
if ((entity == null) || (entity.instance.depth > instance.depth)) {
entity = unit;
}
}
}
List<RenderUnit> sel;
if (entity != null) {
if (toggle) {
@ -1148,7 +1181,7 @@ public class War3MapViewer extends ModelViewer {
for (final RenderUnit unit : this.units) {
final MdxComplexInstance instance = unit.instance;
if (instance.isVisible(this.worldScene.camera) && instance.intersectRayWithCollision(gdxRayHeap,
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding())) {
intersectionHeap, unit.getSimulationUnit().getUnitType().isBuilding(), false)) {
if (filter.call(unit.getSimulationUnit())) {
if ((entity == null) || (entity.instance.depth > instance.depth)) {
entity = unit;
@ -1242,6 +1275,41 @@ public class War3MapViewer extends ModelViewer {
return (numElements < 0) ? 1 : (numElements >= MAXIMUM_ACCEPTED) ? MAXIMUM_ACCEPTED : numElements + 1;
}
public boolean orderSmart(final float x, final float y) {
mousePosHeap.x = x;
mousePosHeap.y = y;
boolean ordered = false;
for (final RenderUnit unit : this.selected) {
for (final CAbility ability : unit.getSimulationUnit().getAbilities()) {
if (ability instanceof CAbilityMove) {
ability.checkCanUse(this.simulation, unit.getSimulationUnit(), OrderIds.smart,
BooleanAbilityActivationReceiver.INSTANCE);
if (BooleanAbilityActivationReceiver.INSTANCE.isOk()) {
ability.checkCanTarget(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap,
PointAbilityTargetCheckReceiver.INSTANCE);
final Vector2 target = PointAbilityTargetCheckReceiver.INSTANCE.getTarget();
if (target != null) {
ability.onOrder(this.simulation, unit.getSimulationUnit(), OrderIds.smart, mousePosHeap,
false);
ordered = true;
}
else {
System.err.println("Target not valid.");
}
}
else {
System.err.println("Ability not ok to use.");
}
}
else {
System.err.println("Ability not move.");
}
}
}
return ordered;
}
public boolean orderSmart(final RenderUnit target) {
boolean ordered = false;
for (final RenderUnit unit : this.selected) {
@ -1348,4 +1416,82 @@ public class War3MapViewer extends ModelViewer {
public AbilityDataUI getAbilityDataUI() {
return this.abilityDataUI;
}
public float getWalkableRenderHeight(final float x, final float y) {
this.walkableObjectsTree.intersect(x, y, this.walkablesIntersector.reset(x, y));
return this.walkablesIntersector.z;
}
public MdxComplexInstance getHighestWalkableUnder(final float x, final float y) {
this.walkableObjectsTree.intersect(x, y, this.intersectorFindsHighestWalkable.reset(x, y));
return this.intersectorFindsHighestWalkable.highestInstance;
}
private static final class QuadtreeIntersectorFindsWalkableRenderHeight
implements QuadtreeIntersector<MdxComplexInstance> {
private float z;
private final Ray ray = new Ray();
private final Vector3 intersection = new Vector3();
private QuadtreeIntersectorFindsWalkableRenderHeight reset(final float x, final float y) {
this.z = -Float.MAX_VALUE;
this.ray.set(x, y, 4096, 0, 0, -8192);
return this;
}
@Override
public boolean onIntersect(final MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) {
this.z = Math.max(this.z, this.intersection.z);
}
return false;
}
}
private static final class QuadtreeIntersectorFindsHighestWalkable
implements QuadtreeIntersector<MdxComplexInstance> {
private float z;
private final Ray ray = new Ray();
private final Vector3 intersection = new Vector3();
private MdxComplexInstance highestInstance;
private QuadtreeIntersectorFindsHighestWalkable reset(final float x, final float y) {
this.z = -Float.MAX_VALUE;
this.ray.set(x, y, 4096, 0, 0, -8192);
this.highestInstance = null;
return this;
}
@Override
public boolean onIntersect(final MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) {
if (this.intersection.z > this.z) {
this.z = this.intersection.z;
this.highestInstance = intersectingObject;
}
}
return false;
}
}
private static final class QuadtreeIntersectorFindsHitPoint implements QuadtreeIntersector<MdxComplexInstance> {
private Ray ray;
private final Vector3 intersection = new Vector3();
private boolean found;
private QuadtreeIntersectorFindsHitPoint reset(final Ray ray) {
this.ray = ray;
this.found = false;
return this;
}
@Override
public boolean onIntersect(final MdxComplexInstance intersectingObject) {
if (intersectingObject.intersectRayWithCollision(this.ray, this.intersection, true, true)) {
this.found = true;
return true;
}
return false;
}
}
}

View File

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

View File

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

View File

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

View File

@ -34,5 +34,6 @@ public interface CommandButtonListener {
// int getButtonPositionY();
//
// int getOrderId();
void commandButton(int buttonPositionX, int buttonPositionY, Texture icon, int abilityHandleId, int orderId);
void commandButton(int buttonPositionX, int buttonPositionY, Texture icon, int abilityHandleId, int orderId,
boolean active);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -178,6 +178,7 @@ public class CUnitData {
final List<CUnitAttack> attacks = new ArrayList<>();
final int attacksEnabled = unitType.getFieldAsInteger(ATTACKS_ENABLED, 0);
if ((attacksEnabled & 0x1) != 0) {
try {
// attack one
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK1_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK1_DAMAGE_POINT, 0);
@ -218,8 +219,12 @@ public class CUnitData {
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
} catch (Exception exc) {
System.err.println("Attack 1 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
if ((attacksEnabled & 0x2) != 0) {
try {
// attack two
final float animationBackswingPoint = unitType.getFieldAsFloat(ATTACK2_BACKSWING_POINT, 0);
final float animationDamagePoint = unitType.getFieldAsFloat(ATTACK2_DAMAGE_POINT, 0);
@ -260,6 +265,9 @@ public class CUnitData {
damageSidesPerDie, damageSpillDistance, damageSpillRadius, damageUpgradeAmount,
maximumNumberOfTargets, projectileArc, projectileArt, projectileHomingEnabled, projectileSpeed,
range, rangeMotionBuffer, showUI, targetsAllowed, weaponSound, weaponType));
} catch (Exception exc) {
System.err.println("Attack 2 failed to parse with: " + exc.getClass() + ":" + exc.getMessage());
}
}
final int deathType = unitType.getFieldAsInteger(DEATH_TYPE, 0);
final boolean raise = (deathType & 0x1) != 0;

View File

@ -1,6 +1,8 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
public interface COrder {
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
public interface CBehavior {
/**
* Executes one step of game simulation of the current order, returning true if
* the order has completed. Many orders may wrap the move order and spend a

View File

@ -3,13 +3,12 @@ package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
public class CAttackOrder implements COrder {
public class CBehaviorAttack implements CBehavior {
private final CUnit unit;
private final int orderId;
private boolean wasWithinPropWindow = false;
@ -17,11 +16,11 @@ public class CAttackOrder implements COrder {
private final CWidget target;
private int damagePointLaunchTime;
private int backSwingTime;
private COrder moveOrder;
private CBehavior moveOrder;
private int thisOrderCooldownEndTime;
private boolean wasInRange = false;
public CAttackOrder(final CUnit unit, final CUnitAttack unitAttack, final int orderId, final CWidget target) {
public CBehaviorAttack(final CUnit unit, final CUnitAttack unitAttack, final int orderId, final CWidget target) {
this.unit = unit;
this.unitAttack = unitAttack;
this.orderId = orderId;
@ -32,10 +31,10 @@ public class CAttackOrder implements COrder {
private void createMoveOrder(final CUnit unit, final CWidget target) {
if (!unit.isMovementDisabled()) { // TODO: Check mobility instead
if ((target instanceof CUnit) && !(((CUnit) target).getUnitType().isBuilding())) {
this.moveOrder = new CMoveOrder(unit, this.orderId, (CUnit) target);
this.moveOrder = new CBehaviorMove(unit, this.orderId, (CUnit) target);
}
else {
this.moveOrder = new CMoveOrder(unit, this.orderId, target.getX(), target.getY());
this.moveOrder = new CBehaviorMove(unit, this.orderId, target.getX(), target.getY());
}
}
else {
@ -50,7 +49,7 @@ public class CAttackOrder implements COrder {
return true;
}
float range = this.unitAttack.getRange();
if ((this.target instanceof CUnit) && (((CUnit) this.target).getCurrentOrder() instanceof CMoveOrder)
if ((this.target instanceof CUnit) && (((CUnit) this.target).getCurrentOrder() instanceof CBehaviorMove)
&& (this.damagePointLaunchTime != 0 /*
* only apply range motion buffer if they were already in range and
* attacked
@ -138,7 +137,6 @@ public class CAttackOrder implements COrder {
else {
damage = simulation.getSeededRandom().nextInt(maxDamage - minDamage) + minDamage;
}
System.out.println(damage + " from " + minDamage + " to " + maxDamage);
this.unitAttack.launch(simulation, this.unit, this.target, damage);
this.damagePointLaunchTime = 0;
}

View File

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

View File

@ -0,0 +1,96 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
public class CBehaviorPatrol implements CBehavior {
private final CUnit unit;
private final int orderId;
private final CWidget target;
private CBehavior moveOrder;
public CBehaviorPatrol(final CUnit unit, final int orderId, final CUnit target) {
this.unit = unit;
this.orderId = orderId;
this.target = target;
createMoveOrder(unit, target);
}
private void createMoveOrder(final CUnit unit, final CUnit target) {
if (!unit.isMovementDisabled()) { // TODO: Check mobility instead
if ((target instanceof CUnit) && !(target.getUnitType().isBuilding())) {
this.moveOrder = new CBehaviorMove(unit, this.orderId, target);
}
else {
this.moveOrder = new CBehaviorMove(unit, this.orderId, target.getX(), target.getY());
}
}
else {
this.moveOrder = null;
}
}
@Override
public boolean update(final CSimulation simulation) {
if (this.target.isDead()) {
return true;
}
final float range = this.unit.getAcquisitionRange();
if (!this.unit.canReach(this.target, range)) {
if (this.moveOrder == null) {
return true;
}
if (this.moveOrder.update(simulation)) {
return true; // we just cant reach them
}
return false;
}
if (!this.unit.isMovementDisabled()) {
final float prevX = this.unit.getX();
final float prevY = this.unit.getY();
final float deltaY = this.target.getY() - prevY;
final float deltaX = this.target.getX() - prevX;
final double goalAngleRad = Math.atan2(deltaY, deltaX);
float goalAngle = (float) Math.toDegrees(goalAngleRad);
if (goalAngle < 0) {
goalAngle += 360;
}
float facing = this.unit.getFacing();
float delta = goalAngle - facing;
final float turnRate = simulation.getUnitData().getTurnRate(this.unit.getTypeId());
if (delta < -180) {
delta = 360 + delta;
}
if (delta > 180) {
delta = -360 + delta;
}
final float absDelta = Math.abs(delta);
if ((absDelta <= 1.0) && (absDelta != 0)) {
this.unit.setFacing(goalAngle);
}
else {
float angleToAdd = Math.signum(delta) * (float) Math.toDegrees(turnRate);
if (absDelta < Math.abs(angleToAdd)) {
angleToAdd = delta;
}
facing += angleToAdd;
this.unit.setFacing(facing);
}
}
else {
}
this.unit.getUnitAnimationListener().playAnimation(false, PrimaryTag.STAND, SequenceUtils.EMPTY, 1.0f, false);
return false;
}
@Override
public int getOrderId() {
return this.orderId;
}
}

View File

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

View File

@ -1,58 +0,0 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.COrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
public class CPatrolOrder implements COrder {
private final CUnit unit;
private final int orderId;
private final CWidget target;
private COrder moveOrder;
public CPatrolOrder(final CUnit unit, final int orderId, final CUnit target) {
this.unit = unit;
this.orderId = orderId;
this.target = target;
createMoveOrder(unit, target);
}
private void createMoveOrder(final CUnit unit, final CUnit target) {
if (!unit.isMovementDisabled()) { // TODO: Check mobility instead
if ((target instanceof CUnit) && !(target.getUnitType().isBuilding())) {
this.moveOrder = new CMoveOrder(unit, this.orderId, target);
}
else {
this.moveOrder = new CMoveOrder(unit, this.orderId, target.getX(), target.getY());
}
}
else {
this.moveOrder = null;
}
}
@Override
public boolean update(final CSimulation simulation) {
if (this.target.isDead()) {
return true;
}
final float range = this.unit.getAcquisitionRange();
if (!this.unit.canReach(this.target, range)) {
if (this.moveOrder == null) {
return true;
}
if (this.moveOrder.update(simulation)) {
return true; // we just cant reach them
}
return false;
}
return false;
}
@Override
public int getOrderId() {
return this.orderId;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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