mirror of
https://github.com/Retera/WarsmashModEngine.git
synced 2022-07-31 17:38:59 +02:00
More work on collisions to fix issues
This commit is contained in:
parent
a4a072be4f
commit
b2900f50fc
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
205
core/src/com/etheller/warsmash/util/Quadtree.java
Normal file
205
core/src/com/etheller/warsmash/util/Quadtree.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user