More work on collisions to fix issues

This commit is contained in:
Retera 2020-07-05 10:39:43 -04:00
parent a4a072be4f
commit b2900f50fc
48 changed files with 1371 additions and 480 deletions

View File

@ -66,7 +66,6 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private final Rectangle tempRect = new Rectangle();
private CameraManager portraitCameraManager;
private MdxComplexInstance portraitInstance;
private final float[] cameraPositionTemp = new float[3];
private final float[] cameraTargetTemp = new float[3];
@ -136,13 +135,14 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.viewer.worldScene.enableAudio();
this.viewer.enableAudio();
try {
this.viewer.loadMap("Pathing.w3x");
this.viewer.loadMap("Maps\\Campaign\\NightElf03.w3m");
}
catch (final IOException e) {
throw new RuntimeException(e);
}
this.cameraManager = new CameraManager();
this.cameraManager.setupCamera(this.viewer.worldScene);
System.out.println("Loaded");
@ -272,7 +272,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.cameraManager.target.add(this.cameraVelocity.x * deltaTime, this.cameraVelocity.y * deltaTime, 0);
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.viewer.terrain.getWaterHeight(this.cameraManager.target.x, this.cameraManager.target.y)) - 256;
this.cameraManager.updateCamera();
this.portraitCameraManager.updateCamera();
this.viewer.updateAndRender();
@ -283,9 +283,10 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
// this.font.draw(this.batch, Integer.toString(Gdx.graphics.getFramesPerSecond()), 0, 0);
// this.batch.end();
if ((this.portraitInstance != null)
&& (this.portraitInstance.sequenceEnded || (this.portraitInstance.sequence == -1))) {
StandSequence.randomPortraitSequence(this.portraitInstance);
if ((this.portraitCameraManager.modelInstance != null)
&& (this.portraitCameraManager.modelInstance.sequenceEnded
|| (this.portraitCameraManager.modelInstance.sequence == -1))) {
StandSequence.randomPortraitSequence(this.portraitCameraManager.modelInstance);
}
Gdx.gl30.glDisable(GL30.GL_SCISSOR_TEST);
@ -429,6 +430,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
class CameraManager {
public com.etheller.warsmash.viewer5.handlers.mdx.Camera modelCamera;
private MdxComplexInstance modelInstance;
private CanvasProvider canvas;
private Camera camera;
private float moveSpeed;
@ -483,13 +485,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.position = this.position.add(this.target);
if (this.modelCamera != null) {
this.modelCamera.getPositionTranslation(WarsmashGdxMapGame.this.cameraPositionTemp,
WarsmashGdxMapGame.this.portraitInstance.sequence,
WarsmashGdxMapGame.this.portraitInstance.frame,
WarsmashGdxMapGame.this.portraitInstance.counter);
this.modelInstance.sequence, this.modelInstance.frame, this.modelInstance.counter);
this.modelCamera.getTargetTranslation(WarsmashGdxMapGame.this.cameraTargetTemp,
WarsmashGdxMapGame.this.portraitInstance.sequence,
WarsmashGdxMapGame.this.portraitInstance.frame,
WarsmashGdxMapGame.this.portraitInstance.counter);
this.modelInstance.sequence, this.modelInstance.frame, this.modelInstance.counter);
this.position.set(this.modelCamera.position);
this.target.set(this.modelCamera.targetPosition);
@ -596,7 +594,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
final RenderUnit rayPickUnit = this.viewer.rayPickUnit(screenX, worldScreenY);
if ((rayPickUnit != null) && (rayPickUnit.playerIndex != this.selectedUnit.playerIndex)) {
if (this.viewer.orderSmart(rayPickUnit)) {
StandSequence.randomPortraitTalkSequence(this.portraitInstance);
StandSequence.randomPortraitTalkSequence(this.portraitCameraManager.modelInstance);
this.selectedSoundCount = 0;
}
}
@ -609,7 +607,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
System.out.println(x + "," + y);
this.viewer.terrain.logRomp(x, y);
if (this.viewer.orderSmart(clickLocationTemp.x, clickLocationTemp.y)) {
StandSequence.randomPortraitTalkSequence(this.portraitInstance);
StandSequence.randomPortraitTalkSequence(this.portraitCameraManager.modelInstance);
this.selectedSoundCount = 0;
}
}
@ -647,29 +645,29 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
if (selectionChanged) {
final MdxModel portraitModel = unit.portraitModel;
if (portraitModel != null) {
if (this.portraitInstance != null) {
this.portraitScene.removeInstance(this.portraitInstance);
if (this.portraitCameraManager.modelInstance != null) {
this.portraitScene.removeInstance(this.portraitCameraManager.modelInstance);
}
this.portraitInstance = (MdxComplexInstance) portraitModel.addInstance();
this.portraitInstance.setSequenceLoopMode(1);
this.portraitInstance.setScene(this.portraitScene);
this.portraitInstance.setVertexColor(unit.instance.vertexColor);
this.portraitCameraManager.modelInstance = (MdxComplexInstance) portraitModel.addInstance();
this.portraitCameraManager.modelInstance.setSequenceLoopMode(1);
this.portraitCameraManager.modelInstance.setScene(this.portraitScene);
this.portraitCameraManager.modelInstance.setVertexColor(unit.instance.vertexColor);
if (portraitModel.getCameras().size() > 0) {
this.portraitCameraManager.modelCamera = portraitModel.getCameras().get(0);
}
this.portraitInstance.setTeamColor(unit.playerIndex);
this.portraitCameraManager.modelInstance.setTeamColor(unit.playerIndex);
}
}
if (playedNewSound) {
StandSequence.randomPortraitTalkSequence(this.portraitInstance);
StandSequence.randomPortraitTalkSequence(this.portraitCameraManager.modelInstance);
}
}
else {
this.selectedUnit = null;
if (this.portraitInstance != null) {
this.portraitScene.removeInstance(this.portraitInstance);
if (this.portraitCameraManager.modelInstance != null) {
this.portraitScene.removeInstance(this.portraitCameraManager.modelInstance);
}
this.portraitInstance = null;
this.portraitCameraManager.modelInstance = null;
this.portraitCameraManager.modelCamera = null;
}
}

View File

@ -14,11 +14,16 @@ import com.etheller.interpreter.JassLexer;
import com.etheller.interpreter.JassParser;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.Trigger;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
import com.etheller.interpreter.ast.value.BooleanJassValue;
import com.etheller.interpreter.ast.value.HandleJassType;
import com.etheller.interpreter.ast.value.HandleJassValue;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.StringJassValue;
import com.etheller.interpreter.ast.value.visitor.IntegerJassValueVisitor;
import com.etheller.interpreter.ast.value.visitor.JassFunctionJassValueVisitor;
import com.etheller.interpreter.ast.value.visitor.ObjectJassValueVisitor;
import com.etheller.interpreter.ast.value.visitor.RealJassValueVisitor;
import com.etheller.interpreter.ast.value.visitor.StringJassValueVisitor;
@ -28,6 +33,13 @@ import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition;
import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.parsers.jass.triggers.BoolExprAnd;
import com.etheller.warsmash.parsers.jass.triggers.BoolExprCondition;
import com.etheller.warsmash.parsers.jass.triggers.BoolExprFilter;
import com.etheller.warsmash.parsers.jass.triggers.BoolExprNot;
import com.etheller.warsmash.parsers.jass.triggers.BoolExprOr;
import com.etheller.warsmash.parsers.jass.triggers.TriggerAction;
import com.etheller.warsmash.parsers.jass.triggers.TriggerCondition;
import com.etheller.warsmash.units.Element;
public class Jass2 {
@ -84,9 +96,19 @@ public class Jass2 {
public JUIEnvironment(final JassProgramVisitor jassProgramVisitor, final DataSource dataSource,
final Viewport uiViewport, final RootFrameListener rootFrameListener) {
final GlobalScope globals = jassProgramVisitor.getGlobals();
final HandleJassType frameHandleType = globals.registerHandleType("framehandle");
final HandleJassType framePointType = globals.registerHandleType("framepointtype");
final HandleJassType triggerType = globals.registerHandleType("trigger");
final HandleJassType triggerActionType = globals.registerHandleType("triggeraction");
final HandleJassType triggerConditionType = globals.registerHandleType("triggercondition");
final HandleJassType boolExprType = globals.registerHandleType("boolexpr");
final HandleJassType conditionFuncType = globals.registerHandleType("conditionfunc");
final HandleJassType filterType = globals.registerHandleType("filterfunc");
jassProgramVisitor.getJassNativeManager().createNative("LogError", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final String stringValue = arguments.get(0).visit(StringJassValueVisitor.getInstance());
System.err.println(stringValue);
return null;
@ -94,27 +116,29 @@ public class Jass2 {
});
jassProgramVisitor.getJassNativeManager().createNative("ConvertFramePointType", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final int value = arguments.get(0).visit(IntegerJassValueVisitor.getInstance());
return new HandleJassValue(jassProgramVisitor.getGlobals().framePointType,
FramePoint.values()[value]);
return new HandleJassValue(framePointType, FramePoint.values()[value]);
}
});
jassProgramVisitor.getJassNativeManager().createNative("CreateRootFrame", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final String skinArg = arguments.get(0).visit(StringJassValueVisitor.getInstance());
final Element skin = GameUI.loadSkin(dataSource, skinArg);
final GameUI gameUI = new GameUI(dataSource, skin, uiViewport);
JUIEnvironment.this.gameUI = gameUI;
JUIEnvironment.this.skin = skin;
rootFrameListener.onCreate(gameUI);
return new HandleJassValue(jassProgramVisitor.getGlobals().frameHandleType, gameUI);
return new HandleJassValue(frameHandleType, gameUI);
}
});
jassProgramVisitor.getJassNativeManager().createNative("LoadTOCFile", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final String tocFileName = arguments.get(0).visit(StringJassValueVisitor.getInstance());
try {
JUIEnvironment.this.gameUI.loadTOCFile(tocFileName);
@ -127,7 +151,8 @@ public class Jass2 {
});
jassProgramVisitor.getJassNativeManager().createNative("CreateSimpleFrame", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final String templateName = arguments.get(0).visit(StringJassValueVisitor.getInstance());
final UIFrame ownerFrame = arguments.get(1).visit(ObjectJassValueVisitor.<UIFrame>getInstance());
final int createContext = arguments.get(2).visit(IntegerJassValueVisitor.getInstance());
@ -135,12 +160,13 @@ public class Jass2 {
final UIFrame simpleFrame = JUIEnvironment.this.gameUI.createSimpleFrame(templateName, ownerFrame,
createContext);
return new HandleJassValue(jassProgramVisitor.getGlobals().frameHandleType, simpleFrame);
return new HandleJassValue(frameHandleType, simpleFrame);
}
});
jassProgramVisitor.getJassNativeManager().createNative("FrameSetAbsPoint", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final UIFrame frame = arguments.get(0).visit(ObjectJassValueVisitor.<UIFrame>getInstance());
final FramePoint framePoint = arguments.get(1)
.visit(ObjectJassValueVisitor.<FramePoint>getInstance());
@ -154,7 +180,8 @@ public class Jass2 {
});
jassProgramVisitor.getJassNativeManager().createNative("FramePositionBounds", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final UIFrame frame = arguments.get(0).visit(ObjectJassValueVisitor.<UIFrame>getInstance());
frame.positionBounds(uiViewport);
return null;
@ -162,11 +189,171 @@ public class Jass2 {
});
jassProgramVisitor.getJassNativeManager().createNative("SkinGetField", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final String fieldName = arguments.get(0).visit(StringJassValueVisitor.getInstance());
return new StringJassValue(JUIEnvironment.this.skin.getField(fieldName));
}
});
jassProgramVisitor.getJassNativeManager().createNative("CreateTrigger", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
return new HandleJassValue(triggerType, new Trigger());
}
});
jassProgramVisitor.getJassNativeManager().createNative("DestroyTrigger", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger trigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
trigger.destroy();
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("EnableTrigger", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger trigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
trigger.setEnabled(true);
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("DisableTrigger", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger trigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
trigger.setEnabled(false);
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("IsTriggerEnabled", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger trigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
return BooleanJassValue.of(trigger.isEnabled());
}
});
jassProgramVisitor.getJassNativeManager().createNative("Condition", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final JassFunction func = arguments.get(0).visit(JassFunctionJassValueVisitor.getInstance());
return new HandleJassValue(conditionFuncType, new BoolExprCondition(func));
}
});
jassProgramVisitor.getJassNativeManager().createNative("Filter", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final JassFunction func = arguments.get(0).visit(JassFunctionJassValueVisitor.getInstance());
return new HandleJassValue(filterType, new BoolExprFilter(func));
}
});
jassProgramVisitor.getJassNativeManager().createNative("DestroyCondition", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final BoolExprCondition trigger = arguments.get(0)
.visit(ObjectJassValueVisitor.<BoolExprCondition>getInstance());
System.err.println(
"DestroyCondition called but in Java we don't have a destructor, so we need to unregister later when that is implemented");
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("DestroyFilter", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final BoolExprFilter trigger = arguments.get(0)
.visit(ObjectJassValueVisitor.<BoolExprFilter>getInstance());
System.err.println(
"DestroyFilter called but in Java we don't have a destructor, so we need to unregister later when that is implemented");
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("DestroyBoolExpr", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final TriggerBooleanExpression trigger = arguments.get(0)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
System.err.println(
"DestroyBoolExpr called but in Java we don't have a destructor, so we need to unregister later when that is implemented");
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("And", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final TriggerBooleanExpression operandA = arguments.get(0)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
final TriggerBooleanExpression operandB = arguments.get(1)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
return new HandleJassValue(boolExprType, new BoolExprAnd(operandA, operandB));
}
});
jassProgramVisitor.getJassNativeManager().createNative("Or", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final TriggerBooleanExpression operandA = arguments.get(0)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
final TriggerBooleanExpression operandB = arguments.get(1)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
return new HandleJassValue(boolExprType, new BoolExprOr(operandA, operandB));
}
});
jassProgramVisitor.getJassNativeManager().createNative("Not", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final TriggerBooleanExpression operand = arguments.get(0)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
return new HandleJassValue(boolExprType, new BoolExprNot(operand));
}
});
jassProgramVisitor.getJassNativeManager().createNative("TriggerAddCondition", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger whichTrigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
final TriggerBooleanExpression condition = arguments.get(1)
.visit(ObjectJassValueVisitor.<TriggerBooleanExpression>getInstance());
final int index = whichTrigger.addCondition(condition);
return new HandleJassValue(triggerConditionType,
new TriggerCondition(condition, whichTrigger, index));
}
});
jassProgramVisitor.getJassNativeManager().createNative("TriggerRemoveCondition", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger whichTrigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
final TriggerCondition condition = arguments.get(1)
.visit(ObjectJassValueVisitor.<TriggerCondition>getInstance());
if (condition.getTrigger() != whichTrigger) {
throw new IllegalArgumentException("Unable to remove condition, wrong trigger");
}
whichTrigger.removeConditionAtIndex(condition.getConditionIndex());
return null;
}
});
jassProgramVisitor.getJassNativeManager().createNative("TriggerAddAction", new JassFunction() {
@Override
public JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
final Trigger whichTrigger = arguments.get(0).visit(ObjectJassValueVisitor.<Trigger>getInstance());
final JassFunction actionFunc = arguments.get(1).visit(JassFunctionJassValueVisitor.getInstance());
final int actionIndex = whichTrigger.addAction(actionFunc);
return new HandleJassValue(triggerActionType,
new TriggerAction(whichTrigger, actionFunc, actionIndex));
}
});
}
}
}

View File

@ -0,0 +1,21 @@
package com.etheller.warsmash.parsers.jass.triggers;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
public class BoolExprAnd implements TriggerBooleanExpression {
private final TriggerBooleanExpression operandA;
private final TriggerBooleanExpression operandB;
public BoolExprAnd(final TriggerBooleanExpression operandA, final TriggerBooleanExpression operandB) {
this.operandA = operandA;
this.operandB = operandB;
}
@Override
public boolean evaluate(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
return this.operandA.evaluate(globalScope, triggerScope) && this.operandB.evaluate(globalScope, triggerScope);
}
}

View File

@ -0,0 +1,27 @@
package com.etheller.warsmash.parsers.jass.triggers;
import java.util.Collections;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
public class BoolExprCondition implements TriggerBooleanExpression {
private final JassFunction takesNothingReturnsBooleanFunction;
public BoolExprCondition(final JassFunction returnsBooleanFunction) {
this.takesNothingReturnsBooleanFunction = returnsBooleanFunction;
}
@Override
public boolean evaluate(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
final JassValue booleanJassReturnValue = this.takesNothingReturnsBooleanFunction.call(Collections.EMPTY_LIST,
globalScope, triggerScope);
final Boolean booleanReturnValue = booleanJassReturnValue.visit(BooleanJassValueVisitor.getInstance());
return booleanReturnValue.booleanValue();
}
}

View File

@ -0,0 +1,27 @@
package com.etheller.warsmash.parsers.jass.triggers;
import java.util.Collections;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
public class BoolExprFilter implements TriggerBooleanExpression {
private final JassFunction takesNothingReturnsBooleanFunction;
public BoolExprFilter(final JassFunction returnsBooleanFunction) {
this.takesNothingReturnsBooleanFunction = returnsBooleanFunction;
}
@Override
public boolean evaluate(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
final JassValue booleanJassReturnValue = this.takesNothingReturnsBooleanFunction.call(Collections.EMPTY_LIST,
globalScope, triggerScope);
final Boolean booleanReturnValue = booleanJassReturnValue.visit(BooleanJassValueVisitor.getInstance());
return booleanReturnValue.booleanValue();
}
}

View File

@ -0,0 +1,19 @@
package com.etheller.warsmash.parsers.jass.triggers;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
public class BoolExprNot implements TriggerBooleanExpression {
private final TriggerBooleanExpression operand;
public BoolExprNot(final TriggerBooleanExpression operand) {
this.operand = operand;
}
@Override
public boolean evaluate(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
return this.operand.evaluate(globalScope, triggerScope);
}
}

View File

@ -0,0 +1,21 @@
package com.etheller.warsmash.parsers.jass.triggers;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
public class BoolExprOr implements TriggerBooleanExpression {
private final TriggerBooleanExpression operandA;
private final TriggerBooleanExpression operandB;
public BoolExprOr(final TriggerBooleanExpression operandA, final TriggerBooleanExpression operandB) {
this.operandA = operandA;
this.operandB = operandB;
}
@Override
public boolean evaluate(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
return this.operandA.evaluate(globalScope, triggerScope) || this.operandB.evaluate(globalScope, triggerScope);
}
}

View File

@ -0,0 +1,28 @@
package com.etheller.warsmash.parsers.jass.triggers;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.trigger.Trigger;
public class TriggerAction {
private final Trigger trigger;
private final JassFunction actionFunc;
private final int actionIndex;
public TriggerAction(final Trigger trigger, final JassFunction actionFunc, final int actionIndex) {
this.trigger = trigger;
this.actionFunc = actionFunc;
this.actionIndex = actionIndex;
}
public Trigger getTrigger() {
return this.trigger;
}
public JassFunction getActionFunc() {
return this.actionFunc;
}
public int getActionIndex() {
return this.actionIndex;
}
}

View File

@ -0,0 +1,28 @@
package com.etheller.warsmash.parsers.jass.triggers;
import com.etheller.interpreter.ast.scope.trigger.Trigger;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
public class TriggerCondition {
private final TriggerBooleanExpression boolexpr;
private final Trigger trigger;
private final int conditionIndex;
public TriggerCondition(final TriggerBooleanExpression boolexpr, final Trigger trigger, final int index) {
this.boolexpr = boolexpr;
this.trigger = trigger;
this.conditionIndex = index;
}
public TriggerBooleanExpression getBoolexpr() {
return this.boolexpr;
}
public Trigger getTrigger() {
return this.trigger;
}
public int getConditionIndex() {
return this.conditionIndex;
}
}

View File

@ -0,0 +1,205 @@
package com.etheller.warsmash.util;
import java.util.function.Consumer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;
public class Quadtree<T> {
private static final int MAX_DEPTH = 64;
private static final int SPLIT_THRESHOLD = 6;
private final Rectangle bounds;
private Quadtree<T> northeast;
private Quadtree<T> northwest;
private Quadtree<T> southwest;
private Quadtree<T> southeast;
private final Array<Node<T>> nodes = new Array<>();
private boolean leaf = true;
private final NodeAdder nodeAdder = new NodeAdder();
private final UniqueNodeAdder uniqueNodeAdder = new UniqueNodeAdder();
public Quadtree(final Rectangle bounds) {
this.bounds = bounds;
}
public void add(final T object, final Rectangle bounds) {
add(new Node<T>(object, bounds), 0);
}
public void remove(final T object, final Rectangle bounds) {
remove(object, bounds, null);
}
public void translate(final T object, final Rectangle prevBoundsToUpdate, final float xShift, final float yShift) {
final Node<T> node = remove(object, prevBoundsToUpdate, null);
prevBoundsToUpdate.x += xShift;
prevBoundsToUpdate.y += yShift;
add(node, 0);
}
public boolean intersectsAnythingOtherThan(final T sourceObjectToIgnore, final Rectangle bounds) {
if (this.leaf) {
for (int i = 0; i < this.nodes.size; i++) {
final Node<T> node = this.nodes.get(i);
if ((node.object != sourceObjectToIgnore) && node.bounds.overlaps(bounds)) {
return true;
}
}
return false;
}
else {
if (this.northeast.bounds.overlaps(bounds)) {
if (this.northeast.intersectsAnythingOtherThan(sourceObjectToIgnore, bounds)) {
return true;
}
}
if (this.northwest.bounds.overlaps(bounds)) {
if (this.northwest.intersectsAnythingOtherThan(sourceObjectToIgnore, bounds)) {
return true;
}
}
if (this.southwest.bounds.overlaps(bounds)) {
if (this.southwest.intersectsAnythingOtherThan(sourceObjectToIgnore, bounds)) {
return true;
}
}
if (this.southeast.bounds.overlaps(bounds)) {
if (this.southeast.intersectsAnythingOtherThan(sourceObjectToIgnore, bounds)) {
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)) {
split(depth);
// then dont return and add as a nonleaf
}
else {
this.nodes.add(node);
return;
}
}
if (this.northeast.bounds.overlaps(node.bounds)) {
this.northeast.add(node, depth + 1);
}
if (this.northwest.bounds.overlaps(node.bounds)) {
this.northwest.add(node, depth + 1);
}
if (this.southwest.bounds.overlaps(node.bounds)) {
this.southwest.add(node, depth + 1);
}
if (this.southeast.bounds.overlaps(node.bounds)) {
this.southeast.add(node, depth + 1);
}
}
private void split(final int depth) {
final int splitDepth = depth + 1;
final float halfWidth = this.bounds.width / 2;
final float x = this.bounds.x;
final float xMidpoint = x + halfWidth;
final float halfHeight = this.bounds.height / 2;
final float y = this.bounds.y;
final float yMidpoint = y + halfHeight;
this.northeast = new Quadtree<>(new Rectangle(xMidpoint, yMidpoint, halfWidth, halfHeight));
this.northwest = new Quadtree<>(new Rectangle(x, yMidpoint, halfWidth, halfHeight));
this.southwest = new Quadtree<>(new Rectangle(x, y, halfWidth, halfHeight));
this.southeast = new Quadtree<>(new Rectangle(xMidpoint, y, halfWidth, halfHeight));
this.leaf = false;
this.nodes.forEach(this.nodeAdder.reset(splitDepth));
this.nodes.clear();
}
private Node<T> remove(final T object, final Rectangle bounds, final Quadtree<T> parent) {
Node<T> returnValue = null;
if (this.leaf) {
for (int i = 0; i < this.nodes.size; i++) {
if (this.nodes.get(i).object == object) {
returnValue = this.nodes.removeIndex(i);
break;
}
}
}
else {
if (this.northeast.bounds.overlaps(bounds)) {
returnValue = this.northeast.remove(object, bounds, this);
}
if (this.northwest.bounds.overlaps(bounds)) {
returnValue = this.northwest.remove(object, bounds, this);
}
if (this.southwest.bounds.overlaps(bounds)) {
returnValue = this.southwest.remove(object, bounds, this);
}
if (this.southeast.bounds.overlaps(bounds)) {
returnValue = this.southeast.remove(object, bounds, this);
}
mergeIfNecessary();
}
return returnValue;
}
private void mergeIfNecessary() {
if (this.northeast.leaf && this.northwest.leaf && this.southwest.leaf && this.southeast.leaf) {
final int children = this.northeast.nodes.size + this.northwest.nodes.size + this.southwest.nodes.size
+ this.southeast.nodes.size; // might include duplicates
if (children <= SPLIT_THRESHOLD) {
this.leaf = true;
addAllUnique(this.northeast.nodes);
addAllUnique(this.northwest.nodes);
addAllUnique(this.southwest.nodes);
addAllUnique(this.southeast.nodes);
this.northeast = this.northwest = this.southwest = this.southeast = null;
}
}
}
private void addAllUnique(final Array<Node<T>> nodes) {
nodes.forEach(this.uniqueNodeAdder);
}
private static final class Node<T> {
private final T object;
private final Rectangle bounds;
public Node(final T object, final Rectangle bounds) {
this.object = object;
this.bounds = bounds;
}
}
private final class NodeAdder implements Consumer<Node<T>> {
private int splitDepth;
private NodeAdder reset(final int splitDepth) {
this.splitDepth = splitDepth;
return this;
}
@Override
public void accept(final Node<T> node) {
add(node, this.splitDepth);
}
}
private final class UniqueNodeAdder implements Consumer<Node<T>> {
private UniqueNodeAdder reset() {
return this;
}
@Override
public void accept(final Node<T> node) {
for (int i = 0; i < Quadtree.this.nodes.size; i++) {
if (Quadtree.this.nodes.get(i) == node) {
return;
}
}
Quadtree.this.nodes.add(node);
}
}
}

View File

@ -21,6 +21,7 @@ import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
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.Ray;
@ -141,6 +142,7 @@ public class War3MapViewer extends ModelViewer {
public List<SplatModel> selModels = new ArrayList<>();
public List<RenderUnit> selected = new ArrayList<>();
private DataTable unitAckSoundsTable;
private DataTable miscData;
private MdxComplexInstance confirmationInstance;
public CSimulation simulation;
private float updateTime;
@ -153,6 +155,8 @@ public class War3MapViewer extends ModelViewer {
private final Map<String, BufferedImage> filePathToPathingMap = new HashMap<>();
private final List<SelectionCircleSize> selectionCircleSizes = new ArrayList<>();
public War3MapViewer(final DataSource dataSource, final CanvasProvider canvas) {
super(dataSource, canvas);
this.gameDataSource = dataSource;
@ -223,7 +227,21 @@ public class War3MapViewer extends ModelViewer {
try (InputStream terrainSlkStream = this.dataSource.getResourceAsStream("UI\\SoundInfo\\UnitAckSounds.slk")) {
this.unitAckSoundsTable.readSLK(terrainSlkStream);
}
this.miscData = new DataTable(worldEditStrings);
try (InputStream miscDataTxtStream = this.dataSource.getResourceAsStream("UI\\MiscData.txt")) {
this.miscData.readTXT(miscDataTxtStream, true);
}
this.selectionCircleSizes.clear();
final Element selectionCircleData = this.miscData.get("SelectionCircle");
final int selectionCircleNumSizes = selectionCircleData.getFieldValue("NumSizes");
for (int i = 0; i < selectionCircleNumSizes; i++) {
final String indexString = i < 10 ? "0" + i : Integer.toString(i);
final float size = selectionCircleData.getFieldFloatValue("Size" + indexString);
final String texture = selectionCircleData.getField("Texture" + indexString);
final String textureDotted = selectionCircleData.getField("TextureDotted" + indexString);
this.selectionCircleSizes.add(new SelectionCircleSize(size, texture, textureDotted));
}
this.selectionCircleScaleFactor = selectionCircleData.getFieldFloatValue("ScaleFactor");
}
public GenericResource loadMapGeneric(final String path, final FetchDataTypeName dataType,
@ -367,7 +385,8 @@ public class War3MapViewer extends ModelViewer {
return simulationAttackProjectile;
}
}, this.terrain.pathingGrid);
}, this.terrain.pathingGrid,
new Rectangle(centerOffset[0], centerOffset[1], (mapSize[0] * 128f) - 128, (mapSize[1] * 128f) - 128));
if (this.doodadsAndDestructiblesLoaded) {
this.loadDoodadsAndDestructibles(modifications);
@ -770,27 +789,29 @@ public class War3MapViewer extends ModelViewer {
final Map<String, Terrain.Splat> splats = new HashMap<String, Terrain.Splat>();
for (final RenderUnit unit : units) {
if (unit.row != null) {
if (unit.radius > 0) {
final float radius = unit.radius;
String path;
// TODO these radius values must be read from UI\MiscData.txt instead
if (radius < 100) {
path = "ReplaceableTextures\\Selection\\SelectionCircleSmall.blp";
if (unit.selectionScale > 0) {
final float selectionSize = unit.selectionScale * this.selectionCircleScaleFactor;
String path = null;
for (int i = 0; i < this.selectionCircleSizes.size(); i++) {
final SelectionCircleSize selectionCircleSize = this.selectionCircleSizes.get(i);
if ((selectionSize < selectionCircleSize.size)
|| (i == (this.selectionCircleSizes.size() - 1))) {
path = selectionCircleSize.texture;
break;
}
else if (radius < 300) {
path = "ReplaceableTextures\\Selection\\SelectionCircleMed.blp";
}
else {
path = "ReplaceableTextures\\Selection\\SelectionCircleLarge.blp";
if (!path.toLowerCase().endsWith(".blp")) {
path += ".blp";
}
if (!splats.containsKey(path)) {
splats.put(path, new Splat());
}
final float x = unit.location[0];
final float y = unit.location[1];
System.out.println("Selecting a unit at " + x + "," + y);
final float z = unit.row.getFieldAsFloat(UNIT_SELECT_HEIGHT, 0);
splats.get(path).locations
.add(new float[] { x - radius, y - radius, x + radius, y + radius, z + 5 });
splats.get(path).locations.add(new float[] { x - (selectionSize / 2), y - (selectionSize / 2),
x + (selectionSize / 2), y + (selectionSize / 2), z + 5 });
splats.get(path).unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
public void accept(final SplatMover t) {
@ -875,109 +896,19 @@ public class War3MapViewer extends ModelViewer {
return sel;
}
public List<RenderUnit> selectUnitOld(final float x, final float y, final boolean toggle) {
final float[] ray = rayHeap;
mousePosHeap.set(x, y);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
final Vector3 dir = normalHeap;
dir.x = ray[3] - ray[0];
dir.y = ray[4] - ray[1];
dir.z = ray[5] - ray[2];
dir.nor();
// TODO good performance, do not create vectors on every check
final Vector3 eMid = new Vector3();
final Vector3 eSize = new Vector3();
final Vector3 rDir = new Vector3();
RenderUnit entity = null;
float entDist = 1e6f;
for (final RenderUnit unit : this.units) {
final float radius = unit.radius;
final float[] location = unit.location;
final MdxComplexInstance instance = unit.instance;
eMid.set(0, 0, radius / 2);
eSize.set(radius, radius, radius);
eMid.add(location[0], location[1], location[2]);
eMid.sub(ray[0], ray[1], ray[2]);
eMid.scl(1 / eSize.x, 1 / eSize.y, 1 / eSize.z);
rDir.x = dir.x / eSize.x;
rDir.y = dir.y / eSize.y;
rDir.z = dir.z / eSize.z;
final float dlen = rDir.len2();
final float dp = Math.max(0, rDir.dot(eMid)) / dlen;
if (dp > entDist) {
continue;
}
rDir.scl(dp);
if (rDir.dst2(eMid) < 1.0) {
entity = unit;
entDist = dp;
}
}
List<RenderUnit> sel;
if (entity != null) {
if (toggle) {
sel = new ArrayList<>(this.selected);
final int idx = sel.indexOf(entity);
if (idx >= 0) {
sel.remove(idx);
}
else {
sel.add(entity);
}
}
else {
sel = Arrays.asList(entity);
}
}
else {
sel = Collections.emptyList();
}
this.doSelectUnit(sel);
return sel;
}
public RenderUnit rayPickUnit(final float x, final float y) {
final float[] ray = rayHeap;
mousePosHeap.set(x, y);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);
final Vector3 dir = normalHeap;
dir.x = ray[3] - ray[0];
dir.y = ray[4] - ray[1];
dir.z = ray[5] - ray[2];
dir.nor();
// TODO good performance, do not create vectors on every check
final Vector3 eMid = new Vector3();
final Vector3 eSize = new Vector3();
final Vector3 rDir = new Vector3();
gdxRayHeap.set(ray[0], ray[1], ray[2], ray[3] - ray[0], ray[4] - ray[1], ray[5] - ray[2]);
gdxRayHeap.direction.nor();// needed for libgdx
RenderUnit entity = null;
float entDist = 1e6f;
for (final RenderUnit unit : this.units) {
final float radius = unit.radius;
final float[] location = unit.location;
final MdxComplexInstance instance = unit.instance;
eMid.set(0, 0, radius / 2);
eSize.set(radius, radius, radius);
eMid.add(location[0], location[1], location[2]);
eMid.sub(ray[0], ray[1], ray[2]);
eMid.scl(1 / eSize.x, 1 / eSize.y, 1 / eSize.z);
rDir.x = dir.x / eSize.x;
rDir.y = dir.y / eSize.y;
rDir.z = dir.z / eSize.z;
final float dlen = rDir.len2();
final float dp = Math.max(0, rDir.dot(eMid)) / dlen;
if (dp > entDist) {
continue;
}
rDir.scl(dp);
if (rDir.dst2(eMid) < 1.0) {
if (instance.isVisible(this.worldScene.camera)
&& instance.intersectRayWithCollision(gdxRayHeap, intersectionHeap)) {
entity = unit;
entDist = dp;
}
}
return entity;
@ -1047,6 +978,7 @@ public class War3MapViewer extends ModelViewer {
}
private static final int MAXIMUM_ACCEPTED = 1 << 30;
private float selectionCircleScaleFactor;
/**
* Returns a power of two size for the given target capacity.
@ -1135,4 +1067,15 @@ public class War3MapViewer extends ModelViewer {
StandSequence.randomStandSequence(instance);
}
private static final class SelectionCircleSize {
private final float size;
private final String texture;
private final String textureDotted;
public SelectionCircleSize(final float size, final String texture, final String textureDotted) {
this.size = size;
this.texture = texture;
this.textureDotted = textureDotted;
}
}
}

View File

@ -1,13 +1,10 @@
package com.etheller.warsmash.viewer5.handlers.w3x.environment;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.etheller.warsmash.parsers.w3x.wpm.War3MapWpm;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType;
public class PathingGrid {
private static final Map<String, MovementType> movetpToMovementType = new HashMap<>();
@ -21,10 +18,6 @@ public class PathingGrid {
private final short[] pathingGrid;
private final short[] dynamicPathingOverlay; // for buildings and trees
private final short[] unitPathingOverlay; // for constantly moving units
private final short[] ignorePathingOverlay; // to prevent unit collide with self
private final int[] unitsAtLocationCount; // number of units at location, probably really inefficient, should
// probably change later
private final int[] pathingGridSizes;
private final float[] centerOffset;
@ -33,53 +26,6 @@ public class PathingGrid {
this.pathingGrid = terrainPathing.getPathing();
this.pathingGridSizes = terrainPathing.getSize();
this.dynamicPathingOverlay = new short[this.pathingGrid.length];
this.unitPathingOverlay = new short[this.pathingGrid.length];
this.ignorePathingOverlay = new short[this.pathingGrid.length];
Arrays.fill(this.ignorePathingOverlay, (short) 0xFFFF);
this.unitsAtLocationCount = new int[this.pathingGrid.length];
}
public void resetUnitCollisionPathing(final Iterable<CUnit> units) {
Arrays.fill(this.unitPathingOverlay, (short) 0);
Arrays.fill(this.unitsAtLocationCount, 0);
for (final CUnit unit : units) {
final CUnitType unitType = unit.getUnitType();
final MovementType movementType = unitType.getMovementType();
if (!unitType.isBuilding() && (movementType != null)) {
final float collisionSize = unitType.getCollisionSize();
final float maxX = unit.getX() + collisionSize;
final float maxY = unit.getY() + collisionSize;
final short obstructionByte;
switch (movementType) {
case FLOAT:
obstructionByte = (short) (PathingGrid.PathingFlags.UNSWIMABLE);
break;
case AMPHIBIOUS:
obstructionByte = (short) (PathingGrid.PathingFlags.UNWALKABLE
| PathingGrid.PathingFlags.UNSWIMABLE);
break;
case FLY:
obstructionByte = (short) (PathingGrid.PathingFlags.UNFLYABLE);
break;
default:
case FOOT:
case DISABLED:
case HORSE:
case HOVER:
obstructionByte = (short) (PathingGrid.PathingFlags.UNWALKABLE);
break;
}
for (float minX = unit.getX() - collisionSize; minX < maxX; minX += 32f) {
for (float minY = unit.getY() - collisionSize; minY < maxY; minY += 32f) {
final int yy = getCellY(minY);
final int xx = getCellX(minX);
final int index = (yy * this.pathingGridSizes[0]) + xx;
this.unitPathingOverlay[index] |= obstructionByte;
this.unitsAtLocationCount[index]++;
}
}
}
}
}
// this blit function is basically copied from HiveWE, maybe remember to mention
@ -193,8 +139,7 @@ public class PathingGrid {
public short getCellPathing(final int cellX, final int cellY) {
final int index = (cellY * this.pathingGridSizes[0]) + cellX;
return (short) ((this.pathingGrid[index] | this.dynamicPathingOverlay[index] | this.unitPathingOverlay[index])
& this.ignorePathingOverlay[index]);
return (short) (this.pathingGrid[index] | this.dynamicPathingOverlay[index]);
}
public boolean isPathable(final float x, final float y, final PathingType pathingType) {
@ -247,22 +192,6 @@ public class PathingGrid {
return false;
}
public void setUnitIgnore(final float unitX, final float unitY, final float collisionSize, final boolean ignore) {
final float maxX = unitX + collisionSize;
final float maxY = unitY + collisionSize;
final short ignoreFlag = (short) (ignore ? 0 : 0xFFFF);
for (float minX = unitX - collisionSize; minX < maxX; minX += 32f) {
for (float minY = unitY - collisionSize; minY < maxY; minY += 32f) {
final int cellY = getCellY(minY);
final int cellX = getCellX(minX);
final int index = (cellY * this.pathingGridSizes[0]) + cellX;
if (this.unitsAtLocationCount[index] == 1) {
this.ignorePathingOverlay[index] = ignoreFlag;
}
}
}
}
public boolean isCellPathable(final int x, final int y, final MovementType pathingType, final float collisionSize) {
return isPathable(getWorldX(x), getWorldY(y), pathingType, collisionSize);
}

View File

@ -43,7 +43,7 @@ public class RenderUnit {
public final MdxComplexInstance instance;
public final MutableGameObject row;
public final float[] location = new float[3];
public float radius;
public float selectionScale;
public UnitSoundset soundset;
public final MdxModel portraitModel;
public int playerIndex;
@ -101,7 +101,7 @@ public class RenderUnit {
(row.getFieldAsInteger(green, 0)) / 255f, (row.getFieldAsInteger(blue, 0)) / 255f });
instance.uniformScale(row.getFieldAsFloat(scale, 0));
this.radius = row.getFieldAsFloat(War3MapViewer.UNIT_SELECT_SCALE, 0) * 36;
this.selectionScale = row.getFieldAsFloat(War3MapViewer.UNIT_SELECT_SCALE, 0);
}
this.instance = instance;
@ -148,7 +148,9 @@ public class RenderUnit {
final float distanceToSimulation = (float) Math.sqrt((simDx * simDx) + (simDy * simDy));
final int speed = this.simulationUnit.getSpeed();
final float speedDelta = speed * deltaTime;
if (distanceToSimulation > speedDelta) {
if ((distanceToSimulation > speedDelta) && (deltaTime < 1.0)) {
// The 1.0 here says that after 1 second of lag, units just teleport to show
// where they actually are
this.x += (speedDelta * simDx) / distanceToSimulation;
this.y += (speedDelta * simDy) / distanceToSimulation;
}

View File

@ -5,6 +5,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
@ -23,18 +24,20 @@ public class CSimulation {
private final ProjectileCreator projectileCreator;
private int gameTurnTick = 0;
private final PathingGrid pathingGrid;
private final CWorldCollision worldCollision;
private final CPathfindingProcessor pathfindingProcessor;
public CSimulation(final MutableObjectData parsedUnitData, final MutableObjectData parsedAbilityData,
final ProjectileCreator projectileCreator, final PathingGrid pathingGrid) {
final ProjectileCreator projectileCreator, final PathingGrid pathingGrid, final Rectangle entireMapBounds) {
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<>();
this.projectiles = new ArrayList<>();
this.handleIdAllocator = new HandleIdAllocator();
this.worldCollision = new CWorldCollision(entireMapBounds);
this.pathfindingProcessor = new CPathfindingProcessor(pathingGrid, this.worldCollision);
}
public CUnitData getUnitData() {
@ -54,6 +57,9 @@ public class CSimulation {
final CUnit unit = this.unitData.create(this, this.handleIdAllocator.createId(), playerIndex, typeId, x, y,
facing);
this.units.add(unit);
if (!unit.getUnitType().isBuilding()) {
this.worldCollision.addUnit(unit);
}
return unit;
}
@ -67,13 +73,14 @@ public class CSimulation {
return this.pathingGrid;
}
public List<Point2D.Float> findNaiveSlowPath(final float startX, final float startY, final float goalX,
final float goalY, final PathingGrid.MovementType movementType, final float collisionSize) {
return this.pathfindingProcessor.findNaiveSlowPath(startX, startY, goalX, goalY, movementType, collisionSize);
public List<Point2D.Float> findNaiveSlowPath(final CUnit ignoreIntersectionsWithThisUnit, final float startX,
final float startY, final Point2D.Float goal, final PathingGrid.MovementType movementType,
final float collisionSize) {
return this.pathfindingProcessor.findNaiveSlowPath(ignoreIntersectionsWithThisUnit, startX, startY, goal,
movementType, collisionSize);
}
public void update() {
this.pathingGrid.resetUnitCollisionPathing(this.units);
for (final CUnit unit : this.units) {
unit.update(this);
}
@ -90,4 +97,8 @@ public class CSimulation {
public int getGameTurnTick() {
return this.gameTurnTick;
}
public CWorldCollision getWorldCollision() {
return this.worldCollision;
}
}

View File

@ -5,6 +5,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
@ -25,6 +26,8 @@ public class CUnit extends CWidget {
private final Queue<COrder> orderQueue = new LinkedList<>();
private final CUnitType unitType;
private Rectangle collisionRectangle;
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 CUnitType unitType) {
@ -156,4 +159,38 @@ public class CUnit extends CWidget {
return this.unitType;
}
public void setCollisionRectangle(final Rectangle collisionRectangle) {
this.collisionRectangle = collisionRectangle;
}
public Rectangle getCollisionRectangle() {
return this.collisionRectangle;
}
public void setX(final float newX, final CWorldCollision collision) {
final float prevX = getX();
if (!this.unitType.isBuilding()) {
setX(newX);
collision.translate(this, newX - prevX, 0);
}
}
public void setY(final float newY, final CWorldCollision collision) {
final float prevY = getY();
if (!this.unitType.isBuilding()) {
setY(newY);
collision.translate(this, 0, newY - prevY);
}
}
public void setPoint(final float newX, final float newY, final CWorldCollision collision) {
final float prevX = getX();
final float prevY = getY();
setX(newX);
setY(newY);
if (!this.unitType.isBuilding()) {
collision.translate(this, newX - prevX, newY - prevY);
}
}
}

View File

@ -29,11 +29,11 @@ public abstract class CWidget {
return this.life;
}
public void setX(final float x) {
protected void setX(final float x) {
this.x = x;
}
public void setY(final float y) {
protected void setY(final float y) {
this.y = y;
}

View File

@ -0,0 +1,112 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.util.Quadtree;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.MovementType;
public class CWorldCollision {
private final Quadtree<CUnit> groundUnitCollision;
private final Quadtree<CUnit> airUnitCollision;
private final Quadtree<CUnit> seaUnitCollision;
public CWorldCollision(final Rectangle entireMapBounds) {
this.groundUnitCollision = new Quadtree<>(entireMapBounds);
this.airUnitCollision = new Quadtree<>(entireMapBounds);
this.seaUnitCollision = new Quadtree<>(entireMapBounds);
}
public void addUnit(final CUnit unit) {
if (unit.getUnitType().isBuilding()) {
throw new IllegalArgumentException("Cannot add building to the CWorldCollision");
}
Rectangle bounds = unit.getCollisionRectangle();
if (bounds == null) {
final float collisionSize = unit.getUnitType().getCollisionSize();
bounds = new Rectangle(unit.getX() - collisionSize, unit.getY() - collisionSize, collisionSize * 2,
collisionSize * 2);
unit.setCollisionRectangle(bounds);
}
final MovementType movementType = unit.getUnitType().getMovementType();
if (movementType != null) {
switch (movementType) {
case AMPHIBIOUS:
this.seaUnitCollision.add(unit, bounds);
this.groundUnitCollision.add(unit, bounds);
break;
case FLOAT:
this.seaUnitCollision.add(unit, bounds);
break;
case FLY:
this.airUnitCollision.add(unit, bounds);
break;
default:
case DISABLED:
case FOOT:
case HORSE:
case HOVER:
this.groundUnitCollision.add(unit, bounds);
break;
}
}
}
public boolean intersectsAnythingOtherThan(final Rectangle newPossibleRectangle, final CUnit sourceUnitToIgnore,
final MovementType movementType) {
if (movementType != null) {
switch (movementType) {
case AMPHIBIOUS:
if (this.seaUnitCollision.intersectsAnythingOtherThan(sourceUnitToIgnore, newPossibleRectangle)) {
return true;
}
if (this.groundUnitCollision.intersectsAnythingOtherThan(sourceUnitToIgnore, newPossibleRectangle)) {
return true;
}
return false;
case FLOAT:
return this.seaUnitCollision.intersectsAnythingOtherThan(sourceUnitToIgnore, newPossibleRectangle);
case FLY:
return this.airUnitCollision.intersectsAnythingOtherThan(sourceUnitToIgnore, newPossibleRectangle);
default:
case DISABLED:
case FOOT:
case HORSE:
case HOVER:
return this.groundUnitCollision.intersectsAnythingOtherThan(sourceUnitToIgnore, newPossibleRectangle);
}
}
return false;
}
public void translate(final CUnit unit, final float xShift, final float yShift) {
if (unit.getUnitType().isBuilding()) {
throw new IllegalArgumentException("Cannot add building to the CWorldCollision");
}
final MovementType movementType = unit.getUnitType().getMovementType();
final Rectangle bounds = unit.getCollisionRectangle();
if (movementType != null) {
switch (movementType) {
case AMPHIBIOUS:
final float oldX = bounds.x;
final float oldY = bounds.y;
this.seaUnitCollision.translate(unit, bounds, xShift, yShift);
bounds.x = oldX;
bounds.y = oldY;
this.groundUnitCollision.translate(unit, bounds, xShift, yShift);
break;
case FLOAT:
this.seaUnitCollision.translate(unit, bounds, xShift, yShift);
break;
case FLY:
this.airUnitCollision.translate(unit, bounds, xShift, yShift);
break;
default:
case DISABLED:
case FOOT:
case HORSE:
case HOVER:
this.groundUnitCollision.translate(unit, bounds, xShift, yShift);
break;
}
}
}
}

View File

@ -1,8 +1,10 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Float;
import java.util.List;
import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
@ -10,24 +12,24 @@ import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid.Moveme
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.abilities.CAbilityMove;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.pathing.CPathfindingProcessor;
public class CMoveOrder implements COrder {
private static final Rectangle tempRect = new Rectangle();
private final CUnit unit;
private final float targetX;
private final float targetY;
private boolean wasWithinPropWindow = false;
private List<Point2D.Float> path = null;
private final CPathfindingProcessor.GridMapping gridMapping;
private final Point2D.Float target;
public CMoveOrder(final CUnit unit, final float targetX, final float targetY) {
this.unit = unit;
this.targetX = targetX;
this.targetY = targetY;
this.gridMapping = CPathfindingProcessor.isCollisionSizeBetterSuitedForCorners(
unit.getUnitType().getCollisionSize()) ? CPathfindingProcessor.GridMapping.CORNERS
: CPathfindingProcessor.GridMapping.CELLS;
this.target = new Point2D.Float(targetX, targetY);
}
@Override
@ -37,19 +39,17 @@ public class CMoveOrder implements COrder {
final MovementType movementType = this.unit.getUnitType().getMovementType();
final PathingGrid pathingGrid = simulation.getPathingGrid();
final CWorldCollision worldCollision = simulation.getWorldCollision();
final float collisionSize = this.unit.getUnitType().getCollisionSize();
pathingGrid.setUnitIgnore(prevX, prevY, collisionSize, true);
try {
final float startFloatingX = prevX;
final float startFloatingY = prevY;
final float goalFloatingX = this.targetX;
final float goalFloatingY = this.targetY;
if (this.path == null) {
this.path = simulation.findNaiveSlowPath(startFloatingX, startFloatingY, goalFloatingX, goalFloatingY,
this.path = simulation.findNaiveSlowPath(this.unit, startFloatingX, startFloatingY, this.target,
movementType, collisionSize);
System.out.println("init path " + this.path);
// check for smoothing
if (!this.path.isEmpty()) {
this.path.add(this.target);
float lastX = startFloatingX;
float lastY = startFloatingY;
float smoothingGroupStartX = startFloatingX;
@ -72,7 +72,7 @@ public class CMoveOrder implements COrder {
}
else {
if (smoothingStartIndex != -1) {
for (int j = smoothingStartIndex; j < i; j++) {
for (int j = i - 1; j >= smoothingStartIndex; j--) {
this.path.remove(j);
}
i = smoothingStartIndex;
@ -95,9 +95,9 @@ public class CMoveOrder implements COrder {
}
float currentTargetX;
float currentTargetY;
if (this.path.size() < 2) {
currentTargetX = this.targetX;
currentTargetY = this.targetY;
if (this.path.isEmpty()) {
currentTargetX = this.target.x;
currentTargetY = this.target.y;
}
else {
final Point2D.Float nextPathElement = this.path.get(0);
@ -107,7 +107,7 @@ public class CMoveOrder implements COrder {
float deltaX = currentTargetX - prevX;
float deltaY = currentTargetY - prevY;
final double goalAngleRad = Math.atan2(deltaY, deltaX);
double goalAngleRad = Math.atan2(deltaY, deltaX);
float goalAngle = (float) Math.toDegrees(goalAngleRad);
if (goalAngle < 0) {
goalAngle += 360;
@ -124,7 +124,7 @@ public class CMoveOrder implements COrder {
if (delta > 180) {
delta = -360 + delta;
}
final float absDelta = Math.abs(delta);
float absDelta = Math.abs(delta);
if ((absDelta <= 1.0) && (absDelta != 0)) {
this.unit.setFacing(goalAngle);
@ -155,23 +155,28 @@ public class CMoveOrder implements COrder {
nextX = (prevX + (float) (Math.cos(radianFacing) * continueDistance));
nextY = (prevY + (float) (Math.sin(radianFacing) * continueDistance));
continueDistance = 0;
done = (this.gridMapping.getX(pathingGrid, nextX) == this.gridMapping.getX(pathingGrid,
currentTargetX))
&& (this.gridMapping.getY(pathingGrid, nextY) == this.gridMapping.getY(pathingGrid,
currentTargetY));
// done = (this.gridMapping.getX(pathingGrid, nextX) == this.gridMapping.getX(pathingGrid,
// currentTargetX))
// && (this.gridMapping.getY(pathingGrid, nextY) == this.gridMapping.getY(pathingGrid,
// currentTargetY));
done = false;
}
if (pathingGrid.isPathable(nextX, nextY, movementType, ((int) collisionSize / 16) * 16)) {
this.unit.setX(nextX);
this.unit.setY(nextY);
tempRect.set(this.unit.getCollisionRectangle());
tempRect.setCenter(nextX, nextY);
if (pathingGrid.isPathable(nextX, nextY, movementType, collisionSize)// ((int) collisionSize / 16) * 16
&& !worldCollision.intersectsAnythingOtherThan(tempRect, this.unit, movementType)) {
this.unit.setPoint(nextX, nextY, worldCollision);
if (done) {
if (this.path.isEmpty()) {
return true;
}
else {
this.path.remove(0);
if (this.path.size() < 2) {
currentTargetX = this.targetX;
currentTargetY = this.targetY;
final Float removed = this.path.remove(0);
System.out.println(
"We think we reached " + removed + " because are at " + nextX + "," + nextY);
if (this.path.isEmpty()) {
currentTargetX = this.target.x;
currentTargetY = this.target.y;
}
else {
final Point2D.Float firstPathElement = this.path.get(0);
@ -180,16 +185,40 @@ public class CMoveOrder implements COrder {
}
deltaY = currentTargetY - prevY;
deltaX = currentTargetX - prevX;
System.out.println("new target: " + currentTargetX + "," + currentTargetY);
System.out.println("new delta: " + deltaX + "," + deltaY);
goalAngleRad = Math.atan2(deltaY, deltaX);
goalAngle = (float) Math.toDegrees(goalAngleRad);
if (goalAngle < 0) {
goalAngle += 360;
}
facing = this.unit.getFacing();
delta = goalAngle - facing;
if (delta < -180) {
delta = 360 + delta;
}
if (delta > 180) {
delta = -360 + delta;
}
absDelta = Math.abs(delta);
if (absDelta >= propulsionWindow) {
this.wasWithinPropWindow = false;
return false;
}
}
}
}
else {
this.path = simulation.findNaiveSlowPath(startFloatingX, startFloatingY, goalFloatingX,
goalFloatingY, movementType, collisionSize);
this.path = simulation.findNaiveSlowPath(this.unit, startFloatingX, startFloatingY, this.target,
movementType, collisionSize);
System.out.println("new path " + this.path);
if (this.path.isEmpty()) {
return true;
}
else {
this.path.add(this.target);
}
}
this.wasWithinPropWindow = true;
}
@ -203,10 +232,6 @@ public class CMoveOrder implements COrder {
return false;
}
finally {
pathingGrid.setUnitIgnore(prevX, prevY, collisionSize, false);
}
}
@Override
public int getOrderId() {

View File

@ -7,16 +7,22 @@ import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.viewer5.handlers.w3x.environment.PathingGrid;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWorldCollision;
public class CPathfindingProcessor {
private static final Rectangle tempRect = new Rectangle();
private final PathingGrid pathingGrid;
private final CWorldCollision worldCollision;
private final Node[][] nodes;
private final Node[][] cornerNodes;
private Node goal;
public CPathfindingProcessor(final PathingGrid pathingGrid) {
public CPathfindingProcessor(final PathingGrid pathingGrid, final CWorldCollision worldCollision) {
this.pathingGrid = pathingGrid;
this.worldCollision = worldCollision;
this.nodes = new Node[pathingGrid.getHeight()][pathingGrid.getWidth()];
this.cornerNodes = new Node[pathingGrid.getHeight() + 1][pathingGrid.getWidth() + 1];
for (int i = 0; i < this.nodes.length; i++) {
@ -46,12 +52,19 @@ public class CPathfindingProcessor {
* @param goal
* @return
*/
public List<Point2D.Float> findNaiveSlowPath(final float startX, final float startY, final float goalX,
final float goalY, final PathingGrid.MovementType movementType, final float collisionSize) {
public List<Point2D.Float> findNaiveSlowPath(final CUnit ignoreIntersectionsWithThisUnit, final float startX,
final float startY, final Point2D.Float goal, final PathingGrid.MovementType movementType,
final float collisionSize) {
final float goalX = goal.x;
final float goalY = goal.y;
if (!this.pathingGrid.isPathable(goalX, goalY, movementType, collisionSize)) {
return Collections.emptyList();
}
System.out.println("beginning findNaiveSlowPath for " + startX + "," + startY + "," + goalX + "," + goalY);
if ((startX == goalX) && (startY == goalY)) {
return Collections.emptyList();
}
tempRect.set(0, 0, collisionSize * 2, collisionSize * 2);
Node[][] searchGraph;
GridMapping gridMapping;
if (isCollisionSizeBetterSuitedForCorners(collisionSize)) {
@ -65,37 +78,107 @@ public class CPathfindingProcessor {
System.out.println("using cells");
}
this.goal = searchGraph[gridMapping.getY(this.pathingGrid, goalY)][gridMapping.getX(this.pathingGrid, goalX)];
final Node start = searchGraph[gridMapping.getY(this.pathingGrid, startY)][gridMapping.getX(this.pathingGrid,
startX)];
final int startGridY = gridMapping.getY(this.pathingGrid, startY);
final int startGridX = gridMapping.getX(this.pathingGrid, startX);
for (int i = 0; i < searchGraph.length; i++) {
for (int j = 0; j < searchGraph[i].length; j++) {
final Node node = searchGraph[i][j];
node.g = Float.POSITIVE_INFINITY;
node.f = Float.POSITIVE_INFINITY;
node.cameFrom = null;
node.cameFromDirection = 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);
final Node start = searchGraph[startGridY][startGridX];
int startGridMinX;
int startGridMinY;
int startGridMaxX;
int startGridMaxY;
if (startX > start.point.x) {
startGridMinX = startGridX;
startGridMaxX = startGridX + 1;
}
else if (startX < start.point.x) {
startGridMinX = startGridX - 1;
startGridMaxX = startGridX;
}
else {
startGridMinX = startGridX;
startGridMaxX = startGridX;
}
if (startY > start.point.y) {
startGridMinY = startGridY;
startGridMaxY = startGridY + 1;
}
else if (startY < start.point.y) {
startGridMinY = startGridY - 1;
startGridMaxY = startGridY;
}
else {
startGridMinY = startGridY;
startGridMaxY = startGridY;
}
for (int cellX = startGridMinX; cellX <= startGridMaxX; cellX++) {
for (int cellY = startGridMinY; cellY <= startGridMaxY; cellY++) {
if ((cellX >= 0) && (cellX < this.pathingGrid.getWidth()) && (cellY >= 0)
&& (cellY < this.pathingGrid.getHeight())) {
final Node possibleNode = searchGraph[cellY][cellX];
final float x = possibleNode.point.x;
final float y = possibleNode.point.y;
if (pathableBetween(ignoreIntersectionsWithThisUnit, startX, startY, movementType, collisionSize, x,
y)) {
final double tentativeScore = possibleNode.point.distance(startX, startY);
possibleNode.g = tentativeScore;
possibleNode.f = tentativeScore + h(possibleNode);
openSet.add(possibleNode);
}
}
}
}
while (!openSet.isEmpty()) {
Node current = openSet.poll();
if (current == this.goal) {
final LinkedList<Point2D.Float> totalPath = new LinkedList<>();
Direction lastCameFromDirection = null;
if ((current.cameFrom != null)
&& pathableBetween(ignoreIntersectionsWithThisUnit, current.cameFrom.point.x,
current.cameFrom.point.y, movementType, collisionSize, goalX, goalY)) {
// do some basic smoothing to walk straight to the goal if it is not obstructed,
// skipping the last grid location
totalPath.addFirst(goal);
current = current.cameFrom;
}
else {
totalPath.addFirst(goal);
totalPath.addFirst(current.point);
}
lastCameFromDirection = current.cameFromDirection;
Node lastNode = null;
while (current.cameFrom != null) {
if ((lastCameFromDirection == null) || (current.cameFromDirection != lastCameFromDirection)) {
lastNode = current;
current = current.cameFrom;
if ((lastCameFromDirection == null) || (current.cameFromDirection != lastCameFromDirection)
|| (current.cameFromDirection == null)) {
if ((current.cameFromDirection != null) || (lastNode == null)
|| !pathableBetween(ignoreIntersectionsWithThisUnit, startX, startY, movementType,
collisionSize, lastNode.point.x, lastNode.point.y)) {
// Add the point if it's not the first one, or if we can only complete
// the journey by specifically walking to the first one
totalPath.addFirst(current.point);
lastCameFromDirection = current.cameFromDirection;
}
current = current.cameFrom;
}
}
return totalPath;
}
@ -103,9 +186,8 @@ public class CPathfindingProcessor {
for (final Direction direction : Direction.VALUES) {
final float x = current.point.x + (direction.xOffset * 32);
final float y = current.point.y + (direction.yOffset * 32);
if (this.pathingGrid.contains(x, y) && this.pathingGrid.isPathable(x, y, movementType, collisionSize)
&& this.pathingGrid.isPathable(current.point.x, y, movementType, collisionSize)
&& this.pathingGrid.isPathable(x, current.point.y, movementType, collisionSize)) {
if (this.pathingGrid.contains(x, y) && pathableBetween(ignoreIntersectionsWithThisUnit, current.point.x,
current.point.y, movementType, collisionSize, x, y)) {
double turnCost;
if ((current.cameFromDirection != null) && (direction != current.cameFromDirection)) {
turnCost = 0.25;
@ -131,6 +213,22 @@ public class CPathfindingProcessor {
return Collections.emptyList();
}
private boolean pathableBetween(final CUnit ignoreIntersectionsWithThisUnit, final float startX, final float startY,
final PathingGrid.MovementType movementType, final float collisionSize, final float x, final float y) {
return this.pathingGrid.isPathable(x, y, movementType, collisionSize)
&& this.pathingGrid.isPathable(startX, y, movementType, collisionSize)
&& this.pathingGrid.isPathable(x, startY, movementType, collisionSize)
&& isPathableDynamically(x, y, ignoreIntersectionsWithThisUnit, movementType)
&& isPathableDynamically(x, startY, ignoreIntersectionsWithThisUnit, movementType)
&& isPathableDynamically(startX, y, ignoreIntersectionsWithThisUnit, movementType);
}
private boolean isPathableDynamically(final float x, final float y, final CUnit ignoreIntersectionsWithThisUnit,
final PathingGrid.MovementType movementType) {
return !this.worldCollision.intersectsAnythingOtherThan(tempRect.setCenter(x, y),
ignoreIntersectionsWithThisUnit, movementType);
}
public static boolean isCollisionSizeBetterSuitedForCorners(final float collisionSize) {
return (((2 * (int) collisionSize) / 32) % 2) == 1;
}

View File

@ -5,6 +5,7 @@ import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL33;
import com.badlogic.gdx.Graphics.DisplayMode;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
@ -67,10 +68,10 @@ public class DesktopLauncher {
config.gles30ContextMajorVersion = 3;
config.gles30ContextMinorVersion = 3;
config.samples = 16;
// config.fullscreen = true;
// final DisplayMode desktopDisplayMode = LwjglApplicationConfiguration.getDesktopDisplayMode();
// config.width = desktopDisplayMode.width;
// config.height = desktopDisplayMode.height;
config.fullscreen = true;
final DisplayMode desktopDisplayMode = LwjglApplicationConfiguration.getDesktopDisplayMode();
config.width = desktopDisplayMode.width;
config.height = desktopDisplayMode.height;
new LwjglApplication(new WarsmashGdxMapGame(), config);
}
}

View File

@ -3,6 +3,7 @@ package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.Assignable;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.ArrayJassValue;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.ArrayJassValueVisitor;
@ -18,11 +19,12 @@ public class ArrayRefJassExpression implements JassExpression {
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) {
Assignable variable = localScope.getAssignableLocal(identifier);
final JassValue index = indexExpression.evaluate(globalScope, localScope);
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
Assignable variable = localScope.getAssignableLocal(this.identifier);
final JassValue index = this.indexExpression.evaluate(globalScope, localScope, triggerScope);
if (variable == null) {
variable = globalScope.getAssignableGlobal(identifier);
variable = globalScope.getAssignableGlobal(this.identifier);
}
if (variable.getValue() == null) {
throw new RuntimeException("Unable to use subscript on uninitialized variable");
@ -30,7 +32,8 @@ public class ArrayRefJassExpression implements JassExpression {
final ArrayJassValue arrayValue = variable.getValue().visit(ArrayJassValueVisitor.getInstance());
if (arrayValue != null) {
return arrayValue.get(index.visit(IntegerJassValueVisitor.getInstance()));
} else {
}
else {
throw new RuntimeException("Not an array");
}
}

View File

@ -6,6 +6,7 @@ import java.util.List;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class FunctionCallJassExpression implements JassExpression {
@ -18,17 +19,18 @@ public class FunctionCallJassExpression implements JassExpression {
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) {
final JassFunction functionByName = globalScope.getFunctionByName(functionName);
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
final JassFunction functionByName = globalScope.getFunctionByName(this.functionName);
if (functionByName == null) {
throw new RuntimeException("Undefined function: " + functionName);
throw new RuntimeException("Undefined function: " + this.functionName);
}
final List<JassValue> evaluatedExpressions = new ArrayList<>();
for (final JassExpression expr : arguments) {
final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope);
for (final JassExpression expr : this.arguments) {
final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope, triggerScope);
evaluatedExpressions.add(evaluatedExpression);
}
return functionByName.call(evaluatedExpressions, globalScope);
return functionByName.call(evaluatedExpressions, globalScope, triggerScope);
}
}

View File

@ -3,6 +3,7 @@ package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.CodeJassValue;
import com.etheller.interpreter.ast.value.JassValue;
@ -14,10 +15,11 @@ public class FunctionReferenceJassExpression implements JassExpression {
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) {
final JassFunction functionByName = globalScope.getFunctionByName(identifier);
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
final JassFunction functionByName = globalScope.getFunctionByName(this.identifier);
if (functionByName == null) {
throw new RuntimeException("Unable to find function: " + identifier);
throw new RuntimeException("Unable to find function: " + this.identifier);
}
return new CodeJassValue(functionByName);
}

View File

@ -2,8 +2,9 @@ package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public interface JassExpression {
JassValue evaluate(GlobalScope globalScope, LocalScope localScope);
JassValue evaluate(GlobalScope globalScope, LocalScope localScope, TriggerExecutionScope triggerScope);
}

View File

@ -2,6 +2,7 @@ package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class LiteralJassExpression implements JassExpression {
@ -12,8 +13,9 @@ public class LiteralJassExpression implements JassExpression {
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) {
return value;
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
return this.value;
}
}

View File

@ -2,8 +2,8 @@ package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
import com.etheller.interpreter.ast.value.visitor.NotJassValueVisitor;
public class NotJassExpression implements JassExpression {
@ -14,7 +14,8 @@ public class NotJassExpression implements JassExpression {
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) {
return this.expression.evaluate(globalScope, localScope).visit(NotJassValueVisitor.getInstance());
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
return this.expression.evaluate(globalScope, localScope, triggerScope).visit(NotJassValueVisitor.getInstance());
}
}

View File

@ -3,6 +3,7 @@ package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.Assignable;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class ReferenceJassExpression implements JassExpression {
@ -13,10 +14,11 @@ public class ReferenceJassExpression implements JassExpression {
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) {
final Assignable local = localScope.getAssignableLocal(identifier);
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
final Assignable local = localScope.getAssignableLocal(this.identifier);
if (local == null) {
return globalScope.getGlobal(identifier);
return globalScope.getGlobal(this.identifier);
}
return local.getValue();
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassType;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.JassTypeGettingValueVisitor;
@ -24,7 +25,8 @@ public abstract class AbstractJassFunction implements JassFunction {
}
@Override
public final JassValue call(final List<JassValue> arguments, final GlobalScope globalScope) {
public final JassValue call(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope) {
if (arguments.size() != this.parameters.size()) {
throw new RuntimeException("Invalid number of arguments passed to function");
}
@ -41,9 +43,9 @@ public abstract class AbstractJassFunction implements JassFunction {
}
localScope.createLocal(parameter.getIdentifier(), parameter.getType(), argument);
}
return innerCall(arguments, globalScope, localScope);
return innerCall(arguments, globalScope, triggerScope, localScope);
}
protected abstract JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope,
final LocalScope localScope);
TriggerExecutionScope triggerScope, final LocalScope localScope);
}

View File

@ -3,8 +3,9 @@ package com.etheller.interpreter.ast.function;
import java.util.List;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public interface JassFunction {
JassValue call(List<JassValue> arguments, GlobalScope globalScope);
JassValue call(List<JassValue> arguments, GlobalScope globalScope, TriggerExecutionScope triggerScope);
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassType;
import com.etheller.interpreter.ast.value.JassValue;
@ -15,12 +16,12 @@ public class NativeJassFunction extends AbstractJassFunction {
final JassFunction impl) {
super(parameters, returnType);
this.name = name;
implementation = impl;
this.implementation = impl;
}
@Override
protected JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope,
final LocalScope localScope) {
return implementation.call(arguments, globalScope);
final TriggerExecutionScope triggerScope, final LocalScope localScope) {
return this.implementation.call(arguments, globalScope, triggerScope);
}
}

View File

@ -4,6 +4,7 @@ import java.util.List;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.statement.JassStatement;
import com.etheller.interpreter.ast.value.JassType;
import com.etheller.interpreter.ast.value.JassValue;
@ -26,17 +27,17 @@ public final class UserJassFunction extends AbstractJassFunction {
@Override
public JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope,
final LocalScope localScope) {
for (final JassStatement statement : statements) {
final JassValue returnValue = statement.execute(globalScope, localScope);
final TriggerExecutionScope triggerScope, final LocalScope localScope) {
for (final JassStatement statement : this.statements) {
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) {
if (returnValue.visit(JassTypeGettingValueVisitor.getInstance()) != returnType) {
if (returnValue.visit(JassTypeGettingValueVisitor.getInstance()) != this.returnType) {
throw new RuntimeException("Invalid return type");
}
return returnValue;
}
}
if (JassType.NOTHING != returnType) {
if (JassType.NOTHING != this.returnType) {
throw new RuntimeException("Invalid return type");
}
return null;

View File

@ -22,15 +22,11 @@ public final class GlobalScope {
private final HandleTypeSuperTypeLoadingVisitor handleTypeSuperTypeLoadingVisitor = new HandleTypeSuperTypeLoadingVisitor();
public final HandleJassType handleType;
public final HandleJassType frameHandleType;
public final HandleJassType framePointType;
private static int lineNumber;
public GlobalScope() {
this.handleType = registerHandleType("handle");// the handle type
this.frameHandleType = registerHandleType("framehandle");
this.framePointType = registerHandleType("framepointtype");
registerPrimitiveType(JassType.BOOLEAN);
registerPrimitiveType(JassType.INTEGER);
registerPrimitiveType(JassType.CODE);
@ -47,7 +43,7 @@ public final class GlobalScope {
return lineNumber;
}
private HandleJassType registerHandleType(final String name) {
public HandleJassType registerHandleType(final String name) {
final HandleJassType handleJassType = new HandleJassType(null, name);
this.types.put(name, handleJassType);
return handleJassType;
@ -130,8 +126,14 @@ public final class GlobalScope {
final HandleJassType handleSuperType = superType.visit(HandleJassTypeVisitor.getInstance());
if (handleSuperType != null) {
final JassType jassType = this.types.get(type);
if (jassType != null) {
jassType.visit(this.handleTypeSuperTypeLoadingVisitor.reset(handleSuperType));
}
else {
throw new RuntimeException(
"unable to declare type " + type + " because it does not exist natively");
}
}
else {
throw new RuntimeException("type " + type + " cannot extend primitive type " + supertype);
}

View File

@ -0,0 +1,16 @@
package com.etheller.interpreter.ast.scope;
import com.etheller.interpreter.ast.scope.trigger.Trigger;
public class TriggerExecutionScope {
private final Trigger triggeringTrigger;
public TriggerExecutionScope(final Trigger triggeringTrigger) {
this.triggeringTrigger = triggeringTrigger;
}
public Trigger getTriggeringTrigger() {
return this.triggeringTrigger;
}
}

View File

@ -0,0 +1,74 @@
package com.etheller.interpreter.ast.scope.trigger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
public class Trigger {
private final List<TriggerBooleanExpression> conditions = new ArrayList<>();
private final List<JassFunction> actions = new ArrayList<>();
private int evalCount;
private int execCount;
private boolean enabled = true;
// used for eval
private transient final TriggerExecutionScope triggerExecutionScope = new TriggerExecutionScope(this);
public int addAction(final JassFunction function) {
final int index = this.actions.size();
this.actions.add(function);
return index;
}
public int addCondition(final TriggerBooleanExpression boolexpr) {
final int index = this.conditions.size();
this.conditions.add(boolexpr);
return index;
}
public void removeCondition(final TriggerBooleanExpression boolexpr) {
this.conditions.remove(boolexpr);
}
public void removeConditionAtIndex(final int conditionIndex) {
this.conditions.remove(conditionIndex);
}
public int getEvalCount() {
return this.evalCount;
}
public int getExecCount() {
return this.execCount;
}
public boolean evaluate(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
for (final TriggerBooleanExpression condition : this.conditions) {
if (!condition.evaluate(globalScope, triggerScope)) {
return false;
}
}
return true;
}
public void execute(final GlobalScope globalScope) {
for (final JassFunction action : this.actions) {
action.call(Collections.emptyList(), globalScope, this.triggerExecutionScope);
}
}
public boolean isEnabled() {
return this.enabled;
}
public void setEnabled(final boolean enabled) {
this.enabled = enabled;
}
public void destroy() {
}
}

View File

@ -0,0 +1,8 @@
package com.etheller.interpreter.ast.scope.trigger;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
public interface TriggerBooleanExpression {
boolean evaluate(GlobalScope globalScope, TriggerExecutionScope triggerScope);
}

View File

@ -4,6 +4,7 @@ import com.etheller.interpreter.ast.Assignable;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.ArrayJassValue;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.ArrayJassValueVisitor;
@ -24,10 +25,11 @@ public class JassArrayedAssignmentStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
Assignable variable = localScope.getAssignableLocal(this.identifier);
final JassValue index = this.indexExpression.evaluate(globalScope, localScope);
final JassValue index = this.indexExpression.evaluate(globalScope, localScope, triggerScope);
if (variable == null) {
variable = globalScope.getAssignableGlobal(this.identifier);
}
@ -37,7 +39,7 @@ public class JassArrayedAssignmentStatement implements JassStatement {
final ArrayJassValue arrayValue = variable.getValue().visit(ArrayJassValueVisitor.getInstance());
if (arrayValue != null) {
arrayValue.set(index.visit(IntegerJassValueVisitor.getInstance()),
this.expression.evaluate(globalScope, localScope));
this.expression.evaluate(globalScope, localScope, triggerScope));
}
else {
throw new RuntimeException("Not an array");

View File

@ -7,6 +7,7 @@ import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class JassCallStatement implements JassStatement {
@ -21,7 +22,8 @@ public class JassCallStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
final JassFunction functionByName = globalScope.getFunctionByName(this.functionName);
if (functionByName == null) {
@ -29,10 +31,10 @@ public class JassCallStatement implements JassStatement {
}
final List<JassValue> evaluatedExpressions = new ArrayList<>();
for (final JassExpression expr : this.arguments) {
final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope);
final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope, triggerScope);
evaluatedExpressions.add(evaluatedExpression);
}
functionByName.call(evaluatedExpressions, globalScope);
functionByName.call(evaluatedExpressions, globalScope, triggerScope);
// throw away return value
return null;
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
@ -23,18 +24,20 @@ public class JassIfElseIfStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
if (this.condition.evaluate(globalScope, localScope).visit(BooleanJassValueVisitor.getInstance())) {
if (this.condition.evaluate(globalScope, localScope, triggerScope)
.visit(BooleanJassValueVisitor.getInstance())) {
for (final JassStatement statement : this.thenStatements) {
final JassValue returnValue = statement.execute(globalScope, localScope);
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) {
return returnValue;
}
}
}
else {
return this.elseifTail.execute(globalScope, localScope);
return this.elseifTail.execute(globalScope, localScope, triggerScope);
}
return null;
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
@ -23,11 +24,13 @@ public class JassIfElseStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
if (this.condition.evaluate(globalScope, localScope).visit(BooleanJassValueVisitor.getInstance())) {
if (this.condition.evaluate(globalScope, localScope, triggerScope)
.visit(BooleanJassValueVisitor.getInstance())) {
for (final JassStatement statement : this.thenStatements) {
final JassValue returnValue = statement.execute(globalScope, localScope);
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) {
return returnValue;
}
@ -35,7 +38,7 @@ public class JassIfElseStatement implements JassStatement {
}
else {
for (final JassStatement statement : this.elseStatements) {
final JassValue returnValue = statement.execute(globalScope, localScope);
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) {
return returnValue;
}

View File

@ -5,6 +5,7 @@ import java.util.List;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
@ -28,11 +29,13 @@ public class JassIfStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
if (this.condition.evaluate(globalScope, localScope).visit(BooleanJassValueVisitor.getInstance())) {
if (this.condition.evaluate(globalScope, localScope, triggerScope)
.visit(BooleanJassValueVisitor.getInstance())) {
for (final JassStatement statement : this.thenStatements) {
final JassValue returnValue = statement.execute(globalScope, localScope);
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) {
return returnValue;
}

View File

@ -3,6 +3,7 @@ package com.etheller.interpreter.ast.statement;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class JassReturnStatement implements JassStatement {
@ -15,9 +16,10 @@ public class JassReturnStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
return this.expression.evaluate(globalScope, localScope);
return this.expression.evaluate(globalScope, localScope, triggerScope);
}
}

View File

@ -4,6 +4,7 @@ import com.etheller.interpreter.ast.Assignable;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class JassSetStatement implements JassStatement {
@ -18,14 +19,15 @@ public class JassSetStatement implements JassStatement {
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) {
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
final Assignable local = localScope.getAssignableLocal(this.identifier);
if (local != null) {
local.setValue(this.expression.evaluate(globalScope, localScope));
local.setValue(this.expression.evaluate(globalScope, localScope, triggerScope));
}
else {
globalScope.setGlobal(this.identifier, this.expression.evaluate(globalScope, localScope));
globalScope.setGlobal(this.identifier, this.expression.evaluate(globalScope, localScope, triggerScope));
}
return null;
}

View File

@ -2,10 +2,11 @@ package com.etheller.interpreter.ast.statement;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public interface JassStatement {
// When a value is returned, this indicates a RETURN statement,
// and will end outer execution
JassValue execute(GlobalScope globalScope, LocalScope localScope);
JassValue execute(GlobalScope globalScope, LocalScope localScope, TriggerExecutionScope triggerScope);
}

View File

@ -27,4 +27,13 @@ public class BooleanJassValue implements JassValue {
return TRUE;
}
}
public static BooleanJassValue of(final boolean flag) {
if (flag) {
return TRUE;
}
else {
return FALSE;
}
}
}

View File

@ -42,8 +42,9 @@ public class JassGlobalsVisitor extends JassBaseVisitor<Void> {
this.globals.createGlobalArray(ctx.ID().getText(), type);
}
else {
this.globals.createGlobal(ctx.ID().getText(), type, this.jassExpressionVisitor
.visit(ctx.assignTail().expression()).evaluate(this.globals, EMPTY_LOCAL_SCOPE));
this.globals.createGlobal(ctx.ID().getText(), type,
this.jassExpressionVisitor.visit(ctx.assignTail().expression()).evaluate(this.globals,
EMPTY_LOCAL_SCOPE, JassProgramVisitor.EMPTY_TRIGGER_SCOPE));
}
return null;
}

View File

@ -15,9 +15,11 @@ import com.etheller.interpreter.ast.function.JassFunction;
import com.etheller.interpreter.ast.function.JassNativeManager;
import com.etheller.interpreter.ast.function.UserJassFunction;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.statement.JassStatement;
public class JassProgramVisitor extends JassBaseVisitor<Void> {
public static final TriggerExecutionScope EMPTY_TRIGGER_SCOPE = new TriggerExecutionScope(null);
private final GlobalScope globals = new GlobalScope();
private final JassNativeManager jassNativeManager = new JassNativeManager();
private final JassTypeVisitor jassTypeVisitor = new JassTypeVisitor(this.globals);
@ -85,7 +87,7 @@ public class JassProgramVisitor extends JassBaseVisitor<Void> {
final JassFunction mainFunction = this.globals.getFunctionByName("main");
if (mainFunction != null) {
try {
mainFunction.call(Collections.EMPTY_LIST, this.globals);
mainFunction.call(Collections.EMPTY_LIST, this.globals, EMPTY_TRIGGER_SCOPE);
}
catch (final Exception exc) {
throw new RuntimeException("Exception on Line " + GlobalScope.getLineNumber(), exc);

View File

@ -19,6 +19,11 @@
type framehandle extends handle
type framepointtype extends handle
type trigger extends handle
type triggeraction extends handle
type triggercondition extends handle
type boolexpr extends handle
type conditionfunc extends boolexpr
native LogError takes string message returns nothing
constant native ConvertFramePointType takes integer i returns framepointtype
@ -92,3 +97,22 @@ native FramePositionBounds takes framehandle frame returns
// string "UI\\Console\\Human\\HumanUI-TimeIndicator.mdl"
// when the "Human" skin was loaded with CreateRootFrame
native SkinGetField takes string field returns string
//============================================================================
// Native trigger interface
//
native CreateTrigger takes nothing returns trigger
native DestroyTrigger takes trigger whichTrigger returns nothing
native EnableTrigger takes trigger whichTrigger returns nothing
native DisableTrigger takes trigger whichTrigger returns nothing
native IsTriggerEnabled takes trigger whichTrigger returns boolean
native TriggerAddCondition takes trigger whichTrigger, boolexpr condition returns triggercondition
native TriggerRemoveCondition takes trigger whichTrigger, triggercondition whichCondition returns nothing
native TriggerClearConditions takes trigger whichTrigger returns nothing
native TriggerAddAction takes trigger whichTrigger, code actionFunc returns triggeraction
native TriggerRemoveAction takes trigger whichTrigger, triggeraction whichAction returns nothing
native TriggerClearActions takes trigger whichTrigger returns nothing
native TriggerEvaluate takes trigger whichTrigger returns boolean
native TriggerExecute takes trigger whichTrigger returns nothing

View File

@ -1,7 +1,7 @@
globals
// Defaults for testing:
constant string SKIN = "Human"
constant string SKIN = "NightElf"
// Major UI components
framehandle ROOT_FRAME
framehandle CONSOLE_UI