From 813e7c9d9b3533f9f8bdbfd62056bb270cf53b07 Mon Sep 17 00:00:00 2001 From: Retera Date: Wed, 27 May 2020 23:55:48 -0400 Subject: [PATCH] Snapshot after breaking scene, work on FDF, work on JASS --- build.gradle | 10 + .../etheller/warsmash/WarsmashGdxGame.java | 17 +- .../etheller/warsmash/WarsmashGdxMapGame.java | 137 +++++---- .../etheller/warsmash/gameui/FDFGameUI.java | 11 + .../etheller/warsmash/parsers/fdf/GameUI.java | 200 ++++++++++++ .../warsmash/parsers/fdf/ModelExport.java | 37 +++ .../fdf/frames/AbstractRenderableFrame.java | 291 ++++++++++++++++++ .../parsers/fdf/frames/AbstractUIFrame.java | 36 +++ .../parsers/fdf/frames/SimpleFrame.java | 9 + .../parsers/fdf/frames/TextureFrame.java | 25 ++ .../warsmash/parsers/fdf/frames/UIFrame.java | 22 ++ .../warsmash/parsers/jass/JassTest.java | 64 ++++ .../etheller/warsmash/units/DataTable.java | 6 +- .../warsmash/util/RenderMathUtils.java | 27 +- .../etheller/warsmash/util/StringBundle.java | 19 ++ .../warsmash/util/WorldEditStrings.java | 4 +- .../com/etheller/warsmash/viewer5/Grid.java | 6 +- .../etheller/warsmash/viewer5/GridCell.java | 1 + .../warsmash/viewer5/ModelInstance.java | 26 +- .../com/etheller/warsmash/viewer5/Scene.java | 83 +---- .../warsmash/viewer5/SimpleScene.java | 73 +++++ .../etheller/warsmash/viewer5/WorldScene.java | 105 +++++++ .../warsmash/viewer5/handlers/mdx/Geoset.java | 22 +- .../viewer5/handlers/mdx/MdxModel.java | 8 +- .../viewer5/handlers/mdx/SetupGeosets.java | 52 +++- .../viewer5/handlers/w3x/War3MapViewer.java | 3 +- .../handlers/w3x/environment/Terrain.java | 10 +- .../w3x/rendersim/RenderAttackProjectile.java | 2 +- .../handlers/w3x/rendersim/RenderUnit.java | 2 +- fdfparser/.gitignore | 1 + .../fdf/datamodel/AnchorDefinition.java | 5 + .../fdf/datamodel/FrameDefinition.java | 58 ++++ .../fields/visitor/GetFloatFieldVisitor.java | 56 ++++ .../fields/visitor/GetStringFieldVisitor.java | 56 ++++ .../visitor/GetVector4FieldVisitor.java | 57 ++++ jassparser/antlr-src/Jass.g4 | 169 ++++++++++ jassparser/build.gradle | 49 +++ .../etheller/interpreter/ast/Assignable.java | 29 ++ .../etheller/interpreter/ast/JassRunner.java | 50 +++ .../expression/ArrayRefJassExpression.java | 38 +++ .../FunctionCallJassExpression.java | 34 ++ .../FunctionReferenceJassExpression.java | 25 ++ .../ast/expression/JassExpression.java | 9 + .../ast/expression/LiteralJassExpression.java | 19 ++ .../expression/ReferenceJassExpression.java | 24 ++ .../ast/function/AbstractJassFunction.java | 44 +++ .../ast/function/JassFunction.java | 10 + .../ast/function/JassNativeManager.java | 33 ++ .../ast/function/JassParameter.java | 27 ++ .../ast/function/NativeJassFunction.java | 26 ++ .../ast/function/UserJassFunction.java | 44 +++ .../interpreter/ast/scope/GlobalScope.java | 94 ++++++ .../interpreter/ast/scope/LocalScope.java | 42 +++ .../interpreter/ast/scope/TypeDefinition.java | 11 + .../JassArrayedAssignmentStatement.java | 44 +++ .../ast/statement/JassCallStatement.java | 37 +++ .../ast/statement/JassReturnStatement.java | 20 ++ .../ast/statement/JassSetStatement.java | 29 ++ .../ast/statement/JassStatement.java | 11 + .../interpreter/ast/value/ArrayJassType.java | 18 ++ .../interpreter/ast/value/ArrayJassValue.java | 34 ++ .../ast/value/BooleanJassValue.java | 18 ++ .../interpreter/ast/value/CodeJassValue.java | 21 ++ .../ast/value/IntegerJassValue.java | 18 ++ .../interpreter/ast/value/JassType.java | 12 + .../ast/value/JassTypeVisitor.java | 7 + .../interpreter/ast/value/JassValue.java | 5 + .../ast/value/JassValueVisitor.java | 15 + .../ast/value/PrimitiveJassType.java | 19 ++ .../interpreter/ast/value/RealJassValue.java | 18 ++ .../ast/value/StringJassValue.java | 18 ++ .../value/visitor/ArrayJassValueVisitor.java | 48 +++ .../visitor/ArrayPrimitiveTypeVisitor.java | 24 ++ .../visitor/IntegerJassValueVisitor.java | 48 +++ .../visitor/JassFunctionJassValueVisitor.java | 49 +++ .../visitor/JassTypeGettingValueVisitor.java | 49 +++ .../value/visitor/StringJassValueVisitor.java | 48 +++ .../visitors/ArgumentExpressionHandler.java | 15 + .../ast/visitors/JassArgumentsVisitor.java | 38 +++ .../ast/visitors/JassExpressionVisitor.java | 77 +++++ .../ast/visitors/JassGlobalsVisitor.java | 49 +++ .../ast/visitors/JassParametersVisitor.java | 38 +++ .../ast/visitors/JassProgramVisitor.java | 87 ++++++ .../ast/visitors/JassStatementVisitor.java | 44 +++ .../ast/visitors/JassTypeVisitor.java | 31 ++ settings.gradle | 2 +- 86 files changed, 3134 insertions(+), 175 deletions(-) create mode 100644 core/src/com/etheller/warsmash/gameui/FDFGameUI.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/GameUI.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/ModelExport.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java create mode 100644 core/src/com/etheller/warsmash/parsers/jass/JassTest.java create mode 100644 core/src/com/etheller/warsmash/util/StringBundle.java create mode 100644 core/src/com/etheller/warsmash/viewer5/SimpleScene.java create mode 100644 core/src/com/etheller/warsmash/viewer5/WorldScene.java create mode 100644 fdfparser/.gitignore create mode 100644 fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetFloatFieldVisitor.java create mode 100644 fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetStringFieldVisitor.java create mode 100644 fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetVector4FieldVisitor.java create mode 100644 jassparser/antlr-src/Jass.g4 create mode 100644 jassparser/build.gradle create mode 100644 jassparser/src/com/etheller/interpreter/ast/Assignable.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/JassRunner.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/expression/ArrayRefJassExpression.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/expression/FunctionCallJassExpression.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/expression/FunctionReferenceJassExpression.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/expression/JassExpression.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/expression/LiteralJassExpression.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/expression/ReferenceJassExpression.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/function/AbstractJassFunction.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/function/JassFunction.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/function/JassNativeManager.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/function/JassParameter.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/function/NativeJassFunction.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/function/UserJassFunction.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/scope/GlobalScope.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/scope/LocalScope.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/scope/TypeDefinition.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/statement/JassCallStatement.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/statement/JassReturnStatement.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/statement/JassStatement.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/ArrayJassType.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/BooleanJassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/CodeJassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/IntegerJassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/JassType.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/JassTypeVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/JassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/JassValueVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/PrimitiveJassType.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/RealJassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/StringJassValue.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayJassValueVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayPrimitiveTypeVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/visitor/IntegerJassValueVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/visitor/JassFunctionJassValueVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/visitor/JassTypeGettingValueVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/value/visitor/StringJassValueVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/ArgumentExpressionHandler.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassArgumentsVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassExpressionVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassGlobalsVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassParametersVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassProgramVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassStatementVisitor.java create mode 100644 jassparser/src/com/etheller/interpreter/ast/visitors/JassTypeVisitor.java diff --git a/build.gradle b/build.gradle index c0bc5fb..30c58f8 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,7 @@ project(":core") { dependencies { compile project(":fdfparser") + compile project(":jassparser") compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" @@ -85,6 +86,15 @@ project(":fdfparser") { } } +project(":jassparser") { + apply plugin: "antlr" + + + dependencies { + antlr "org.antlr:antlr4:$antlrVersion" // use antlr version 4 + } +} + tasks.eclipse.doLast { delete ".project" } \ No newline at end of file diff --git a/core/src/com/etheller/warsmash/WarsmashGdxGame.java b/core/src/com/etheller/warsmash/WarsmashGdxGame.java index 0d6bd42..9840bb8 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxGame.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxGame.java @@ -7,6 +7,7 @@ import java.util.Arrays; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; @@ -18,6 +19,7 @@ import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.datasources.DataSourceDescriptor; import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; import com.etheller.warsmash.parsers.mdlx.Sequence; +import com.etheller.warsmash.util.DataSourceFileHandle; import com.etheller.warsmash.viewer5.Camera; import com.etheller.warsmash.viewer5.CanvasProvider; import com.etheller.warsmash.viewer5.ModelViewer; @@ -72,7 +74,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide this.cameraManager = new CameraManager(); this.cameraManager.setupCamera(scene); - this.mainModel = (MdxModel) this.viewer.load("Buildings\\Other\\TempArtB\\TempArtB.mdx", + this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3d\\MainMenu3d.mdx", // this.mainModel = (MdxModel) this.viewer.load("Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdx", new PathSolver() { @Override @@ -88,7 +90,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide // System.out.println(Arrays.toString(evt.keyFrames)); // System.out.println(evt.name); -// this.modelCamera = this.mainModel.cameras.get(0); + this.modelCamera = this.mainModel.cameras.get(0); this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0); @@ -96,14 +98,21 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide int animIndex = 0; for (final Sequence s : this.mainModel.getSequences()) { - if (s.getName().toLowerCase().startsWith("walk")) { + if (s.getName().toLowerCase().startsWith("stand")) { animIndex = this.mainModel.getSequences().indexOf(s); + break; } } this.mainInstance.setSequence(animIndex); this.mainInstance.setSequenceLoopMode(0); + final Music music = Gdx.audio + .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\MainScreen.mp3")); + music.setVolume(0.2f); + music.setLooping(true); + music.play(); + // acolytesHarvestingSceneJoke2(scene); System.out.println("Loaded"); @@ -489,7 +498,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide WarsmashGdxGame.this.cameraPositionTemp[1], WarsmashGdxGame.this.cameraPositionTemp[2]); this.target.add(WarsmashGdxGame.this.cameraTargetTemp[0], WarsmashGdxGame.this.cameraTargetTemp[1], WarsmashGdxGame.this.cameraTargetTemp[2]); - this.camera.perspective(WarsmashGdxGame.this.modelCamera.fieldOfView, + this.camera.perspective(WarsmashGdxGame.this.modelCamera.fieldOfView * 0.75f, Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(), WarsmashGdxGame.this.modelCamera.nearClippingPlane, WarsmashGdxGame.this.modelCamera.farClippingPlane); diff --git a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java index fb8b6f9..177c486 100644 --- a/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java +++ b/core/src/com/etheller/warsmash/WarsmashGdxMapGame.java @@ -14,7 +14,6 @@ import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input; import com.badlogic.gdx.InputProcessor; -import com.badlogic.gdx.audio.Music; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL30; @@ -37,6 +36,11 @@ import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor; import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.datasources.DataSourceDescriptor; import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; +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.units.Element; import com.etheller.warsmash.util.DataSourceFileHandle; import com.etheller.warsmash.util.ImageUtils; import com.etheller.warsmash.util.WarsmashConstants; @@ -93,7 +97,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv private boolean showTalentTree; private final List messages = new LinkedList<>(); + private MdxModel timeIndicator; + /* + * (non-Javadoc) + * + * @see com.badlogic.gdx.ApplicationAdapter#create() + */ @Override public void create() { @@ -126,7 +136,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.viewer.worldScene.enableAudio(); this.viewer.enableAudio(); try { - this.viewer.loadMap("ReforgedGeorgeVacation.w3x"); + this.viewer.loadMap("PeasantTest.w3x"); } catch (final IOException e) { throw new RuntimeException(e); @@ -143,6 +153,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.portraitCameraManager = new CameraManager(); this.portraitCameraManager.setupCamera(this.portraitScene); + this.uiScene = this.viewer.addScene(); + this.uiScene.alpha = true; + // this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx", this.portraitScene.camera.viewport(new Rectangle(100, 0, 6400, 48)); @@ -151,6 +164,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv final float w = Gdx.graphics.getWidth(); final float h = Gdx.graphics.getHeight(); + this.tempRect.x = 0; + this.tempRect.y = 0; + this.tempRect.width = w; + this.tempRect.height = h; + this.uiScene.camera.viewport(this.tempRect); + this.uiScene.camera.ortho(0, 0.8f, 0, 0.6f, 0, 1); + final FreeTypeFontGenerator fontGenerator = new FreeTypeFontGenerator( new DataSourceFileHandle(this.viewer.dataSource, "fonts\\FRIZQT__.TTF")); final FreeTypeFontParameter fontParam = new FreeTypeFontParameter(); @@ -212,13 +232,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv Gdx.input.setInputProcessor(this); - final Music music = Gdx.audio - .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3")); - music.setVolume(0.2f); - music.setLooping(true); - music.play(); +// final Music music = Gdx.audio +// .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3")); +// music.setVolume(0.2f); +// music.setLooping(true); +// music.play(); - this.minimap = new Rectangle(35, 7, 305, 272); + this.minimap = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f); final float worldWidth = (this.viewer.terrain.columns - 1); final float worldHeight = this.viewer.terrain.rows - 1; final float worldSize = Math.max(worldWidth, worldHeight); @@ -234,6 +254,33 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.shapeRenderer = new ShapeRenderer(); this.talentTreeWindow = new Rectangle(100, 300, 1400, 800); + + final Element skin = GameUI.loadSkin(this.viewer.dataSource, "Human"); + this.gameUI = new GameUI(this.viewer.dataSource, skin, this.uiViewport); + String timeIndicatorPath = skin.getField("TimeOfDayIndicator"); + if (!this.viewer.dataSource.has(timeIndicatorPath)) { + final int lastDotIndex = timeIndicatorPath.lastIndexOf('.'); + if (lastDotIndex >= 0) { + timeIndicatorPath = timeIndicatorPath.substring(0, lastDotIndex); + } + timeIndicatorPath += ".mdx"; + } + this.timeIndicator = (MdxModel) this.viewer.load(timeIndicatorPath, this.viewer.mapPathSolver, + this.viewer.solverParams); + final MdxComplexInstance timeIndicatorInstance = (MdxComplexInstance) this.timeIndicator.addInstance(); + timeIndicatorInstance.setScene(this.uiScene); + timeIndicatorInstance.setSequence(0); + timeIndicatorInstance.setSequenceLoopMode(2); + try { + this.gameUI.loadTOCFile("UI\\FrameDef\\FrameDef.toc"); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + this.gameUI.createSimpleFrame("ConsoleUI", this.gameUI, 0); + final UIFrame resourceBarFrame = this.gameUI.createSimpleFrame("ResourceBarFrame", this.gameUI, 0); + resourceBarFrame.addAnchor(new AnchorDefinition(FramePoint.TOPRIGHT, 0, 0)); + this.gameUI.positionBounds(this.uiViewport); } @Override @@ -270,60 +317,35 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.uiViewport.apply(); this.batch.setProjectionMatrix(this.uiCamera.combined); this.batch.begin(); + this.gameUI.render(this.batch); this.font.setColor(Color.YELLOW); final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond(); this.glyphLayout.setText(this.font, fpsString); this.font.draw(this.batch, fpsString, (this.uiViewport.getWorldWidth() - this.glyphLayout.width) / 2, 1100); -// this.batch.draw(this.consoleUITexture, 0, 0, this.uiViewport.getWorldWidth(), 320); - this.batch.draw(this.minimapTexture, 35, 7, 305, 272); + this.batch.draw(this.minimapTexture, this.minimap.x, this.minimap.y, this.minimap.width, this.minimap.height); if (this.selectedUnit != null) { - this.font24.setColor(Color.WHITE); - final String name = this.viewer.simulation.getUnitData() - .getName(this.selectedUnit.getSimulationUnit().getTypeId()); - this.glyphLayout.setText(this.font24, name); - this.font24.draw(this.batch, name, ((this.uiViewport.getWorldWidth() - this.glyphLayout.width) / 2) + 100, - 200); - - this.font20.setColor(Color.YELLOW); - this.font20.draw(this.batch, "Attack:", 600, 120); - this.font20.draw(this.batch, "Defense:", 600, 98); - this.font20.draw(this.batch, "Speed:", 600, 76); - this.font20.setColor(Color.WHITE); int messageIndex = 0; for (final Message message : this.messages) { this.font20.draw(this.batch, message.text, 100, 400 + (25 * (messageIndex++))); } this.font20.setColor(Color.WHITE); - final int dmgMin = this.viewer.simulation.getUnitData() - .getA1MinDamage(this.selectedUnit.getSimulationUnit().getTypeId()); - final int dmgMax = this.viewer.simulation.getUnitData() - .getA1MaxDamage(this.selectedUnit.getSimulationUnit().getTypeId()); - final int def = this.viewer.simulation.getUnitData() - .getDefense(this.selectedUnit.getSimulationUnit().getTypeId()); - this.font20.draw(this.batch, Integer.toString(dmgMin) + " - " + Integer.toString(dmgMax), 700, 120); - this.font20.draw(this.batch, Integer.toString(def), 700, 98); - this.font20.draw(this.batch, Integer.toString(this.selectedUnit.getSimulationUnit().getSpeed()), 700, 76); final COrder currentOrder = this.selectedUnit.getSimulationUnit().getCurrentOrder(); for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) { - this.batch.draw(commandCardIcon.getTexture(), 1225 + (70 * commandCardIcon.getX()), - 160 - (70 * commandCardIcon.getY()), 64, 64); + this.batch.draw(commandCardIcon.getTexture(), 1235 + (86.8f * commandCardIcon.getX()), + 190 - (88 * commandCardIcon.getY()), 78f, 78f); if (((currentOrder != null) && (currentOrder.getOrderId() == commandCardIcon.getOrderId())) || ((currentOrder == null) && (commandCardIcon.getOrderId() == CAbilityStop.ORDER_ID))) { final int blendDstFunc = this.batch.getBlendDstFunc(); final int blendSrcFunc = this.batch.getBlendSrcFunc(); this.batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE); - this.batch.draw(this.activeButtonTexture, 1225 + (70 * commandCardIcon.getX()), - 160 - (70 * commandCardIcon.getY()), 64, 64); + this.batch.draw(this.activeButtonTexture, 1235 + (86.8f * commandCardIcon.getX()), + 190 - (88 * commandCardIcon.getY()), 78f, 78f); this.batch.setBlendFunction(blendSrcFunc, blendDstFunc); } } - - this.batch.draw(this.solidGreenTexture, 413, 34, 122 * (this.selectedUnit.getSimulationUnit().getLife() - / this.selectedUnit.getSimulationUnit().getMaximumLife()), 7); - } for (final RenderUnit unit : this.viewer.units) { if (unit.playerIndex >= WarsmashConstants.MAX_PLAYERS) { @@ -387,9 +409,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv public void resize(final int width, final int height) { super.resize(width, height); this.tempRect.x = 0; - this.tempRect.y = 0; this.tempRect.width = width; - this.tempRect.height = height; + final float topHeight = 0.02666f * height; + final float bottomHeight = 0.21333f * height; + this.tempRect.y = (int) bottomHeight; + this.tempRect.height = height - (int) (topHeight + bottomHeight); this.cameraManager.camera.viewport(this.tempRect); final float portraitTestWidth = (100 / 640f) * width; final float portraitTestHeight = (100 / 480f) * height; @@ -397,15 +421,21 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv this.uiViewport.update(width, height); this.uiCamera.position.set(this.uiCamera.viewportWidth / 2, this.uiCamera.viewportHeight / 2, 0); - positionPortrait(); + this.tempRect.x = this.uiViewport.getScreenX(); + this.tempRect.y = this.uiViewport.getScreenY(); + this.tempRect.width = this.uiViewport.getScreenWidth(); + this.tempRect.height = this.uiViewport.getScreenHeight(); + this.uiScene.camera.viewport(this.tempRect); + this.uiScene.camera.ortho(0f, 0.8f, 0f, 0.6f, -1f, 1); + positionPortrait(); } private void positionPortrait() { - this.projectionTemp1.x = 385; - this.projectionTemp1.y = 0; - this.projectionTemp2.x = 385 + 180; - this.projectionTemp2.y = 177; + this.projectionTemp1.x = 422; + this.projectionTemp1.y = 57; + this.projectionTemp2.x = 422 + 167; + this.projectionTemp2.y = 57 + 170; this.uiViewport.project(this.projectionTemp1); this.uiViewport.project(this.projectionTemp2); @@ -492,7 +522,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv WarsmashGdxMapGame.this.cameraPositionTemp[1], WarsmashGdxMapGame.this.cameraPositionTemp[2]); this.target.add(WarsmashGdxMapGame.this.cameraTargetTemp[0], WarsmashGdxMapGame.this.cameraTargetTemp[1], WarsmashGdxMapGame.this.cameraTargetTemp[2]); - this.camera.perspective(this.modelCamera.fieldOfView, this.camera.getAspect(), + this.camera.perspective(this.modelCamera.fieldOfView * 0.75f, this.camera.getAspect(), this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane); } @@ -509,6 +539,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv private Scene portraitScene; private Texture minimapTexture; private Rectangle talentTreeWindow; + private GameUI gameUI; + private Scene uiScene; @Override public boolean keyDown(final int keycode) { @@ -551,6 +583,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv @Override public boolean touchDown(final int screenX, final int screenY, final int pointer, final int button) { + final float worldScreenY = getHeight() - screenY; System.out.println(screenX + "," + screenY); clickLocationTemp2.x = screenX; @@ -559,8 +592,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv if (this.selectedUnit != null) { for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) { - if (new Rectangle(1225 + (70 * commandCardIcon.getX()), 160 - (70 * commandCardIcon.getY()), 64, 64) - .contains(clickLocationTemp2)) { + if (new Rectangle(1235 + (86.8f * commandCardIcon.getX()), 190 - (88 * commandCardIcon.getY()), 78f, + 78f).contains(clickLocationTemp2)) { if (button == Input.Buttons.RIGHT) { this.messages.add(new Message(Gdx.input.getCurrentEventTime(), "Right mouse click")); } @@ -581,14 +614,14 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv return false; } if (button == Input.Buttons.RIGHT) { - final RenderUnit rayPickUnit = this.viewer.rayPickUnit(screenX, screenY); + 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); } } else { - this.viewer.getClickLocation(clickLocationTemp, screenX, screenY); + this.viewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY); System.out.println(clickLocationTemp); this.viewer.showConfirmation(clickLocationTemp, 0, 1, 0); final int x = (int) ((clickLocationTemp.x - this.viewer.terrain.centerOffset[0]) / 128); @@ -601,7 +634,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv } } else { - final List selectedUnits = this.viewer.selectUnit(screenX, screenY, false); + final List selectedUnits = this.viewer.selectUnit(screenX, worldScreenY, false); if (!selectedUnits.isEmpty()) { final RenderUnit unit = selectedUnits.get(0); this.selectedUnit = unit; diff --git a/core/src/com/etheller/warsmash/gameui/FDFGameUI.java b/core/src/com/etheller/warsmash/gameui/FDFGameUI.java new file mode 100644 index 0000000..cad3521 --- /dev/null +++ b/core/src/com/etheller/warsmash/gameui/FDFGameUI.java @@ -0,0 +1,11 @@ +package com.etheller.warsmash.gameui; + +import com.etheller.warsmash.parsers.fdf.datamodel.FrameTemplateEnvironment; + +public class FDFGameUI { + private final FrameTemplateEnvironment frameTemplateEnvironment; + + public FDFGameUI(final FrameTemplateEnvironment frameTemplateEnvironment) { + this.frameTemplateEnvironment = frameTemplateEnvironment; + } +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java new file mode 100644 index 0000000..76baf89 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/GameUI.java @@ -0,0 +1,200 @@ +package com.etheller.warsmash.parsers.fdf; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.datasources.DataSource; +import com.etheller.warsmash.fdfparser.FDFParser; +import com.etheller.warsmash.fdfparser.FrameDefinitionVisitor; +import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; +import com.etheller.warsmash.parsers.fdf.datamodel.FrameClass; +import com.etheller.warsmash.parsers.fdf.datamodel.FrameDefinition; +import com.etheller.warsmash.parsers.fdf.datamodel.FrameTemplateEnvironment; +import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition; +import com.etheller.warsmash.parsers.fdf.frames.AbstractUIFrame; +import com.etheller.warsmash.parsers.fdf.frames.SimpleFrame; +import com.etheller.warsmash.parsers.fdf.frames.TextureFrame; +import com.etheller.warsmash.parsers.fdf.frames.UIFrame; +import com.etheller.warsmash.units.DataTable; +import com.etheller.warsmash.units.Element; +import com.etheller.warsmash.util.ImageUtils; +import com.etheller.warsmash.util.StringBundle; + +public final class GameUI extends AbstractUIFrame implements UIFrame { + + private final DataSource dataSource; + private final Element skin; + private final Viewport viewport; + private final FrameTemplateEnvironment templates; + private final Map pathToTexture = new HashMap<>(); + private final boolean autoPosition = false; + + public GameUI(final DataSource dataSource, final Element skin, final Viewport viewport) { + super("GameUI", null); + this.dataSource = dataSource; + this.skin = skin; + this.viewport = viewport; + this.renderBounds.set(0, 0, viewport.getWorldWidth(), viewport.getWorldHeight()); + this.templates = new FrameTemplateEnvironment(); + + } + + public static Element loadSkin(final DataSource dataSource, final String skin) { + final DataTable skinsTable = new DataTable(StringBundle.EMPTY); + try (InputStream stream = dataSource.getResourceAsStream("UI\\war3skins.txt")) { + skinsTable.readTXT(stream, true); + } + catch (final IOException e) { + throw new RuntimeException(e); + } +// final Element main = skinsTable.get("Main"); +// final String skinsField = main.getField("Skins"); +// final String[] skins = skinsField.split(","); + final Element defaultSkin = skinsTable.get("Default"); + final Element userSkin = skinsTable.get(skin); + for (final String key : defaultSkin.keySet()) { + if (!userSkin.hasField(key)) { + userSkin.setField(key, defaultSkin.getField(key)); + } + } + return userSkin; + } + + public void loadTOCFile(final String tocFilePath) throws IOException { + final DataSourceFDFParserBuilder dataSourceFDFParserBuilder = new DataSourceFDFParserBuilder(this.dataSource); + final FrameDefinitionVisitor fdfVisitor = new FrameDefinitionVisitor(this.templates, + dataSourceFDFParserBuilder); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(this.dataSource.getResourceAsStream(tocFilePath)))) { + String line; + int tocLines = 0; + while ((line = reader.readLine()) != null) { + final FDFParser firstFileParser = dataSourceFDFParserBuilder.build(line); + fdfVisitor.visit(firstFileParser.program()); + tocLines++; + } + System.out.println("TOC file loaded " + tocLines + " lines"); + } + } + + public UIFrame createFrame(final String name, final UIFrame owner, final int priority, final int createContext) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + public UIFrame createSimpleFrame(final String name, final UIFrame owner, final int createContext) { + final FrameDefinition frameDefinition = this.templates.getFrame(name); + if (frameDefinition.getFrameClass() == FrameClass.Frame) { + if ("SIMPLEFRAME".equals(frameDefinition.getFrameType())) { + final UIFrame inflated = inflate(frameDefinition, owner, null); + if (this.autoPosition) { + inflated.positionBounds(this.viewport); + } + add(inflated); + return inflated; + } + } + return null; + } + + public UIFrame inflate(final FrameDefinition frameDefinition, final UIFrame parent, + final FrameDefinition parentDefinitionIfAvailable) { + UIFrame inflatedFrame = null; + switch (frameDefinition.getFrameClass()) { + case Frame: + if ("SIMPLEFRAME".equals(frameDefinition.getFrameType())) { + final SimpleFrame simpleFrame = new SimpleFrame(frameDefinition.getName(), parent); + for (final FrameDefinition childDefinition : frameDefinition.getInnerFrames()) { + simpleFrame.add(inflate(childDefinition, simpleFrame, frameDefinition)); + } + inflatedFrame = simpleFrame; + } + break; + case Layer: + // NOT HANDLED YET + break; + case String: + break; + case Texture: + String file = frameDefinition.getString("File"); + if (frameDefinition.has("DecorateFileNames") || ((parentDefinitionIfAvailable != null) + && parentDefinitionIfAvailable.has("DecorateFileNames"))) { + if (this.skin.hasField(file)) { + file = this.skin.getField(file); + } + else { + throw new IllegalStateException("Decorated file name lookup not available: " + file); + } + } + final Texture texture = loadTexture(file); + final Vector4Definition texCoord = frameDefinition.getVector4("TexCoord"); + final TextureRegion texRegion; + if (texCoord != null) { + texRegion = new TextureRegion(texture, texCoord.getX(), texCoord.getZ(), texCoord.getY(), + texCoord.getW()); + } + else { + texRegion = new TextureRegion(texture); + } + final TextureFrame textureFrame = new TextureFrame(frameDefinition.getName(), parent, texRegion); + inflatedFrame = textureFrame; + break; + default: + break; + } + if (inflatedFrame != null) { + final Float width = frameDefinition.getFloat("Width"); + if (width != null) { + inflatedFrame.setWidth(convertX(this.viewport, width)); + } + final Float height = frameDefinition.getFloat("Height"); + if (height != null) { + inflatedFrame.setHeight(convertY(this.viewport, height)); + } + for (final AnchorDefinition anchor : frameDefinition.getAnchors()) { + inflatedFrame.addAnchor(new AnchorDefinition(anchor.getMyPoint(), + convertX(this.viewport, anchor.getX()), convertY(this.viewport, anchor.getY()))); + } + } + else { + // TODO in production throw some kind of exception here + } + return inflatedFrame; + } + + public UIFrame createFrameByType(final String typeName, final String name, final UIFrame owner, + final String inherits, final int createContext) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + public static float convertX(final Viewport viewport, final float fdfX) { + return (fdfX / 0.8f) * viewport.getWorldWidth(); + } + + public static float convertY(final Viewport viewport, final float fdfY) { + return (fdfY / 0.6f) * viewport.getWorldHeight(); + } + + private Texture loadTexture(String path) { + if (!path.contains(".")) { + path = path + ".blp"; + } + Texture texture = this.pathToTexture.get(path); + if (texture == null) { + texture = ImageUtils.getBLPTexture(this.dataSource, path); + this.pathToTexture.put(path, texture); + } + return texture; + } + + @Override + public final void positionBounds(final Viewport viewport) { + innerPositionBounds(viewport); + } +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/ModelExport.java b/core/src/com/etheller/warsmash/parsers/fdf/ModelExport.java new file mode 100644 index 0000000..6967bff --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/ModelExport.java @@ -0,0 +1,37 @@ +package com.etheller.warsmash.parsers.fdf; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor; +import com.etheller.warsmash.datasources.DataSource; +import com.etheller.warsmash.datasources.DataSourceDescriptor; +import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; +import com.etheller.warsmash.parsers.mdlx.MdlxModel; + +public class ModelExport { + + public static void main(final String[] args) { + + final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor("E:\\Backups\\Warcraft\\Data\\127"); + final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor("E:\\Backups\\Warsmash\\Data"); + final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor("."); + final DataSource dataSource = new CompoundDataSourceDescriptor( + Arrays.asList(war3mpq, testingFolder, currentFolder)).createDataSource(); + + try (InputStream modelStream = dataSource + .getResourceAsStream("UI\\Glues\\MainMenu\\MainMenu3D\\MainMenu3D.mdx")) { + final MdlxModel model = new MdlxModel(modelStream); + try (FileOutputStream fos = new FileOutputStream(new File("C:\\Temp\\MainMenu3D.mdl"))) { + model.saveMdl(fos); + } + } + catch (final IOException e) { + e.printStackTrace(); + } + } + +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java new file mode 100644 index 0000000..65e1aae --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractRenderableFrame.java @@ -0,0 +1,291 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; +import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; + +public abstract class AbstractRenderableFrame implements UIFrame { + protected String name; + protected UIFrame parent; + protected boolean visible; + protected int level; + protected final Rectangle renderBounds = new Rectangle(0, 0, 0, 0); // in libgdx rendering space + protected List anchors = new ArrayList<>(); + + public AbstractRenderableFrame(final String name, final UIFrame parent) { + this.name = name; + this.parent = parent; + } + + public void setWidth(final float width) { + this.renderBounds.width = width; + } + + public void setHeight(final float height) { + this.renderBounds.height = height; + } + + private boolean hasLeftAnchor() { + for (final AnchorDefinition anchor : this.anchors) { + switch (anchor.getMyPoint()) { + case CENTER: + case BOTTOM: + case TOP: + case BOTTOMRIGHT: + case RIGHT: + case TOPRIGHT: + break; + case BOTTOMLEFT: + case LEFT: + case TOPLEFT: + return true; + default: + break; + } + } + return false; + } + + private boolean hasRightAnchor() { + for (final AnchorDefinition anchor : this.anchors) { + switch (anchor.getMyPoint()) { + case CENTER: + case BOTTOM: + case TOP: + case BOTTOMLEFT: + case LEFT: + case TOPLEFT: + break; + case BOTTOMRIGHT: + case RIGHT: + case TOPRIGHT: + return true; + default: + break; + } + } + return false; + } + + private boolean hasTopAnchor() { + for (final AnchorDefinition anchor : this.anchors) { + switch (anchor.getMyPoint()) { + case CENTER: + case BOTTOM: + case BOTTOMLEFT: + case LEFT: + case BOTTOMRIGHT: + case RIGHT: + break; + case TOP: + case TOPLEFT: + case TOPRIGHT: + return true; + default: + break; + } + } + return false; + } + + private boolean hasBottomAnchor() { + for (final AnchorDefinition anchor : this.anchors) { + switch (anchor.getMyPoint()) { + case CENTER: + case LEFT: + case RIGHT: + case TOP: + case TOPLEFT: + case TOPRIGHT: + break; + case BOTTOM: + case BOTTOMLEFT: + case BOTTOMRIGHT: + return true; + default: + break; + } + } + return false; + } + + @Override + public float getFramePointX(final FramePoint framePoint) { + switch (framePoint) { + case CENTER: + case BOTTOM: + case TOP: + return this.renderBounds.x + (this.renderBounds.width / 2); + case BOTTOMLEFT: + case LEFT: + case TOPLEFT: + return this.renderBounds.x; + case BOTTOMRIGHT: + case RIGHT: + case TOPRIGHT: + return this.renderBounds.x + this.renderBounds.width; + default: + return 0; + } + } + + public void setFramePointX(final FramePoint framePoint, final float x) { + if (this.renderBounds.width == 0) { + this.renderBounds.x = x; + return; + } + switch (framePoint) { + case CENTER: + case BOTTOM: + case TOP: + this.renderBounds.x = x - (this.renderBounds.width / 2); + return; + case BOTTOMLEFT: + case LEFT: + case TOPLEFT: + if (hasRightAnchor()) { + final float oldRightX = this.renderBounds.x + this.renderBounds.width; + this.renderBounds.x = x; + this.renderBounds.width = oldRightX - x; + } + else { + // no right anchor, keep width + this.renderBounds.x = x; + } + return; + case BOTTOMRIGHT: + case RIGHT: + case TOPRIGHT: + if (hasLeftAnchor()) { + this.renderBounds.width = x - this.renderBounds.x; + } + else { + this.renderBounds.x = x - this.renderBounds.width; + } + return; + default: + return; + } + } + + @Override + public float getFramePointY(final FramePoint framePoint) { + switch (framePoint) { + case LEFT: + case CENTER: + case RIGHT: + return this.renderBounds.y + (this.renderBounds.height / 2); + case BOTTOMLEFT: + case BOTTOM: + case BOTTOMRIGHT: + return this.renderBounds.y; + case TOPLEFT: + case TOP: + case TOPRIGHT: + return this.renderBounds.y + this.renderBounds.height; + default: + return 0; + } + } + + public void setFramePointY(final FramePoint framePoint, final float y) { + if (this.renderBounds.height == 0) { + this.renderBounds.y = y; + return; + } + switch (framePoint) { + case LEFT: + case CENTER: + case RIGHT: + this.renderBounds.y = y - (this.renderBounds.height / 2); + return; + case TOPLEFT: + case TOP: + case TOPRIGHT: + if (hasBottomAnchor()) { + this.renderBounds.height = y - this.renderBounds.y; + } + else { + this.renderBounds.y = y - this.renderBounds.height; + } + return; + case BOTTOMLEFT: + case BOTTOM: + case BOTTOMRIGHT: + if (hasTopAnchor()) { + final float oldBottomY = this.renderBounds.y + this.renderBounds.height; + this.renderBounds.y = y; + this.renderBounds.height = oldBottomY - y; + } + else { + this.renderBounds.y = y; + } + return; + default: + return; + } + } + + @Override + public void addAnchor(final AnchorDefinition anchorDefinition) { + this.anchors.add(anchorDefinition); + } + + @Override + public void positionBounds(final Viewport viewport) { + if (this.parent == null) { + // TODO this is a bit of a hack, remove later + return; + } + if ("ResourceBarFrame".equals(this.name)) { + System.out.println("doing resource bar"); + } + if (this.anchors.isEmpty()) { + this.renderBounds.x = this.parent.getFramePointX(FramePoint.LEFT); + this.renderBounds.y = this.parent.getFramePointY(FramePoint.BOTTOM); + } + else { + for (final AnchorDefinition anchor : this.anchors) { + final float parentPointX = this.parent.getFramePointX(anchor.getMyPoint()); + final float parentPointY = this.parent.getFramePointY(anchor.getMyPoint()); + setFramePointX(anchor.getMyPoint(), parentPointX + anchor.getX()); + setFramePointY(anchor.getMyPoint(), parentPointY + anchor.getY()); + System.out.println(getClass().getSimpleName() + ":" + this.name + " anchoring to: " + anchor); + } + } + if (this.renderBounds.width == 0) { + this.renderBounds.width = this.parent.getFramePointX(FramePoint.RIGHT) + - this.parent.getFramePointX(FramePoint.LEFT); + } + if (this.renderBounds.height == 0) { + this.renderBounds.height = this.parent.getFramePointY(FramePoint.TOP) + - this.parent.getFramePointY(FramePoint.BOTTOM); + } + System.out.println( + getClass().getSimpleName() + ":" + this.name + " finishing position bounds: " + this.renderBounds); + innerPositionBounds(viewport); + } + + protected abstract void innerPositionBounds(final Viewport viewport); + + public boolean isVisible() { + return this.visible; + } + + public int getLevel() { + return this.level; + } + + public void setVisible(final boolean visible) { + this.visible = visible; + } + + public void setLevel(final int level) { + this.level = level; + } + +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java new file mode 100644 index 0000000..a203fdb --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/AbstractUIFrame.java @@ -0,0 +1,36 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import java.util.ArrayList; +import java.util.List; + +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.viewport.Viewport; + +public abstract class AbstractUIFrame extends AbstractRenderableFrame implements UIFrame { + private final List childFrames = new ArrayList<>(); + + public void add(final UIFrame childFrame) { + if (childFrame == null) { + return; + } + this.childFrames.add(childFrame); + } + + public AbstractUIFrame(final String name, final UIFrame parent) { + super(name, parent); + } + + @Override + public void render(final SpriteBatch batch) { + for (final UIFrame childFrame : this.childFrames) { + childFrame.render(batch); + } + } + + @Override + protected void innerPositionBounds(final Viewport viewport) { + for (final UIFrame childFrame : this.childFrames) { + childFrame.positionBounds(viewport); + } + } +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleFrame.java new file mode 100644 index 0000000..b90dd09 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/SimpleFrame.java @@ -0,0 +1,9 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +public class SimpleFrame extends AbstractUIFrame { + + public SimpleFrame(final String name, final UIFrame parent) { + super(name, parent); + } + +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java new file mode 100644 index 0000000..65b291b --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/TextureFrame.java @@ -0,0 +1,25 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.utils.viewport.Viewport; + +public class TextureFrame extends AbstractRenderableFrame { + private final TextureRegion texture; + + public TextureFrame(final String name, final UIFrame parent, final TextureRegion texture) { + super(name, parent); + this.texture = texture; + } + + @Override + public void render(final SpriteBatch batch) { + batch.draw(this.texture, this.renderBounds.x, this.renderBounds.y, this.renderBounds.width, + this.renderBounds.height); + } + + @Override + protected void innerPositionBounds(final Viewport viewport) { + } + +} diff --git a/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java b/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java new file mode 100644 index 0000000..8f20224 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/fdf/frames/UIFrame.java @@ -0,0 +1,22 @@ +package com.etheller.warsmash.parsers.fdf.frames; + +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.viewport.Viewport; +import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition; +import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; + +public interface UIFrame { + public void render(SpriteBatch batch); + + public float getFramePointX(FramePoint framePoint); + + public float getFramePointY(FramePoint framePoint); + + void positionBounds(final Viewport viewport); + + void addAnchor(final AnchorDefinition anchorDefinition); + + void setWidth(final float width); + + void setHeight(final float height); +} diff --git a/core/src/com/etheller/warsmash/parsers/jass/JassTest.java b/core/src/com/etheller/warsmash/parsers/jass/JassTest.java new file mode 100644 index 0000000..629dd51 --- /dev/null +++ b/core/src/com/etheller/warsmash/parsers/jass/JassTest.java @@ -0,0 +1,64 @@ +package com.etheller.warsmash.parsers.jass; + +import java.io.IOException; +import java.util.Arrays; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import com.etheller.interpreter.JassLexer; +import com.etheller.interpreter.JassParser; +import com.etheller.interpreter.ast.visitors.JassProgramVisitor; +import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor; +import com.etheller.warsmash.datasources.DataSource; +import com.etheller.warsmash.datasources.DataSourceDescriptor; +import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; + +public class JassTest { + public static final boolean REPORT_SYNTAX_ERRORS = true; + + public static void main(final String[] args) { + final JassProgramVisitor jassProgramVisitor = new JassProgramVisitor(); + try { + final FolderDataSourceDescriptor war3mpq = new FolderDataSourceDescriptor( + "E:\\Backups\\Warcraft\\Data\\127"); + final FolderDataSourceDescriptor testingFolder = new FolderDataSourceDescriptor( + "E:\\Backups\\Warsmash\\Data"); + final FolderDataSourceDescriptor currentFolder = new FolderDataSourceDescriptor("."); + final DataSource dataSource = new CompoundDataSourceDescriptor( + Arrays.asList(war3mpq, testingFolder, currentFolder)).createDataSource(); + JassLexer lexer; + try { + lexer = new JassLexer(CharStreams.fromStream(dataSource.getResourceAsStream("Scripts\\common.j"))); + } + catch (final IOException e) { + throw new RuntimeException(e); + } + final JassParser parser = new JassParser(new CommonTokenStream(lexer)); + parser.addErrorListener(new BaseErrorListener() { + @Override + public void syntaxError(final Recognizer recognizer, final Object offendingSymbol, final int line, + final int charPositionInLine, final String msg, final RecognitionException e) { + if (!REPORT_SYNTAX_ERRORS) { + return; + } + + String sourceName = recognizer.getInputStream().getSourceName(); + if (!sourceName.isEmpty()) { + sourceName = String.format("%s:%d:%d: ", sourceName, line, charPositionInLine); + } + + System.err.println(sourceName + "line " + line + ":" + charPositionInLine + " " + msg); + } + }); + jassProgramVisitor.visit(parser.program()); + } + catch (final Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/core/src/com/etheller/warsmash/units/DataTable.java b/core/src/com/etheller/warsmash/units/DataTable.java index ae85a0c..16e8919 100644 --- a/core/src/com/etheller/warsmash/units/DataTable.java +++ b/core/src/com/etheller/warsmash/units/DataTable.java @@ -11,16 +11,16 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import com.etheller.warsmash.util.WorldEditStrings; +import com.etheller.warsmash.util.StringBundle; public class DataTable implements ObjectData { private static final boolean DEBUG = false; Map dataTable = new LinkedHashMap<>(); - private final WorldEditStrings worldEditStrings; + private final StringBundle worldEditStrings; - public DataTable(final WorldEditStrings worldEditStrings) { + public DataTable(final StringBundle worldEditStrings) { this.worldEditStrings = worldEditStrings; } diff --git a/core/src/com/etheller/warsmash/util/RenderMathUtils.java b/core/src/com/etheller/warsmash/util/RenderMathUtils.java index 0232076..6c554fd 100644 --- a/core/src/com/etheller/warsmash/util/RenderMathUtils.java +++ b/core/src/com/etheller/warsmash/util/RenderMathUtils.java @@ -219,10 +219,10 @@ public enum RenderMathUtils { } public static void unpackPlanes(final Vector4[] planes, final Matrix4 m) { - final float a00 = m.val[Matrix4.M00], a01 = m.val[Matrix4.M10], a02 = m.val[Matrix4.M20], - a03 = m.val[Matrix4.M30], a10 = m.val[Matrix4.M01], a11 = m.val[Matrix4.M11], a12 = m.val[Matrix4.M21], - a13 = m.val[Matrix4.M31], a20 = m.val[Matrix4.M02], a21 = m.val[Matrix4.M12], a22 = m.val[Matrix4.M22], - a23 = m.val[Matrix4.M32], a30 = m.val[Matrix4.M03], a31 = m.val[Matrix4.M13], a32 = m.val[Matrix4.M23], + final float a00 = m.val[Matrix4.M00], a01 = m.val[Matrix4.M01], a02 = m.val[Matrix4.M02], + a03 = m.val[Matrix4.M03], a10 = m.val[Matrix4.M10], a11 = m.val[Matrix4.M11], a12 = m.val[Matrix4.M12], + a13 = m.val[Matrix4.M13], a20 = m.val[Matrix4.M20], a21 = m.val[Matrix4.M21], a22 = m.val[Matrix4.M22], + a23 = m.val[Matrix4.M23], a30 = m.val[Matrix4.M30], a31 = m.val[Matrix4.M31], a32 = m.val[Matrix4.M32], a33 = m.val[Matrix4.M33]; // Left clipping plane @@ -293,7 +293,7 @@ public enum RenderMathUtils { public static Vector3 unproject(final Vector3 out, final Vector3 v, final Matrix4 inverseMatrix, final Rectangle viewport) { final float x = ((2 * (v.x - viewport.x)) / viewport.width) - 1; - final float y = 1 - ((2 * (v.y - viewport.y)) / viewport.height); + final float y = ((2 * (v.y - viewport.y)) / viewport.height) - 1; final float z = (2 * v.z) - 1; heap.set(x, y, z, 1); @@ -481,6 +481,23 @@ public enum RenderMathUtils { return wrapper; } + public static IntBuffer wrap(final int[] positions) { + final IntBuffer wrapper = ByteBuffer.allocateDirect(positions.length * 4).order(ByteOrder.nativeOrder()) + .asIntBuffer(); + wrapper.put(positions); + wrapper.clear(); + return wrapper; + } + + public static ByteBuffer wrapAsBytes(final int[] positions) { + final ByteBuffer wrapper = ByteBuffer.allocateDirect(positions.length).order(ByteOrder.nativeOrder()); + for (final int face : positions) { + wrapper.put((byte) face); + } + wrapper.clear(); + return wrapper; + } + public static Buffer wrap(final short[] cornerTextures) { final ByteBuffer wrapper = ByteBuffer.allocateDirect(cornerTextures.length).order(ByteOrder.nativeOrder()); for (final short face : cornerTextures) { diff --git a/core/src/com/etheller/warsmash/util/StringBundle.java b/core/src/com/etheller/warsmash/util/StringBundle.java new file mode 100644 index 0000000..d9d1150 --- /dev/null +++ b/core/src/com/etheller/warsmash/util/StringBundle.java @@ -0,0 +1,19 @@ +package com.etheller.warsmash.util; + +public interface StringBundle { + String getString(String string); + + String getStringCaseSensitive(final String key); + + StringBundle EMPTY = new StringBundle() { + @Override + public String getStringCaseSensitive(final String key) { + return key; + } + + @Override + public String getString(final String string) { + return string; + } + }; +} diff --git a/core/src/com/etheller/warsmash/util/WorldEditStrings.java b/core/src/com/etheller/warsmash/util/WorldEditStrings.java index bc8eb41..8347d2c 100644 --- a/core/src/com/etheller/warsmash/util/WorldEditStrings.java +++ b/core/src/com/etheller/warsmash/util/WorldEditStrings.java @@ -9,7 +9,7 @@ import java.util.ResourceBundle; import com.etheller.warsmash.datasources.DataSource; -public class WorldEditStrings { +public class WorldEditStrings implements StringBundle { private ResourceBundle bundle; private ResourceBundle bundlegs; @@ -30,6 +30,7 @@ public class WorldEditStrings { } } + @Override public String getString(String string) { try { while (string.toUpperCase().startsWith("WESTRING")) { @@ -60,6 +61,7 @@ public class WorldEditStrings { } } + @Override public String getStringCaseSensitive(final String key) { try { return this.bundle.getString(key); diff --git a/core/src/com/etheller/warsmash/viewer5/Grid.java b/core/src/com/etheller/warsmash/viewer5/Grid.java index 3ac16c1..63364d1 100644 --- a/core/src/com/etheller/warsmash/viewer5/Grid.java +++ b/core/src/com/etheller/warsmash/viewer5/Grid.java @@ -72,10 +72,10 @@ public class Grid { } } - public void moved(final ModelInstance instance) { + public void moved(final ModelInstance instance, final float upcomingX, final float upcomingY) { final Bounds bounds = instance.model.bounds; - final float x = (instance.worldLocation.x + bounds.x) - this.x; - final float y = (instance.worldLocation.y + bounds.y) - this.y; + final float x = (upcomingX + bounds.x) - this.x; + final float y = (upcomingY + bounds.y) - this.y; final float r = bounds.r; final Vector3 s = instance.worldScale; int left = (int) (Math.floor((x - (r * s.x)) / this.cellWidth)); diff --git a/core/src/com/etheller/warsmash/viewer5/GridCell.java b/core/src/com/etheller/warsmash/viewer5/GridCell.java index 1b82ff7..10b4062 100644 --- a/core/src/com/etheller/warsmash/viewer5/GridCell.java +++ b/core/src/com/etheller/warsmash/viewer5/GridCell.java @@ -40,6 +40,7 @@ public class GridCell { if (true) { return true; } + this.plane = RenderMathUtils.testCell(camera.planes, this.left, this.right, this.bottom, this.top, this.plane); return this.plane == -1; diff --git a/core/src/com/etheller/warsmash/viewer5/ModelInstance.java b/core/src/com/etheller/warsmash/viewer5/ModelInstance.java index 8d07713..839b820 100644 --- a/core/src/com/etheller/warsmash/viewer5/ModelInstance.java +++ b/core/src/com/etheller/warsmash/viewer5/ModelInstance.java @@ -89,17 +89,31 @@ public abstract class ModelInstance extends Node { super.recalculateTransformation(); if (this.scene != null) { - this.scene.grid.moved(this); + this.scene.grid.moved(this, this.worldLocation.x, this.worldLocation.y); } } public boolean isVisible(final Camera camera) { - if (true) { - return true; + // can't just use world location if it moves + float x, y, z; + if (this.dirty) { + // TODO this is an incorrect, predicted location for dirty case + if ((this.parent != null) && !this.dontInheritTranslation) { + x = this.parent.localLocation.x + this.localLocation.x; + y = this.parent.localLocation.y + this.localLocation.y; + z = this.parent.localLocation.z + this.localLocation.z; + } + else { + x = this.localLocation.x; + y = this.localLocation.y; + z = this.localLocation.z; + } + } + else { + x = this.worldLocation.x; + y = this.worldLocation.y; + z = this.worldLocation.z; } - final float x = this.worldLocation.x; - final float y = this.worldLocation.y; - final float z = this.worldLocation.z; final Bounds bounds = this.model.bounds; final Vector4[] planes = camera.planes; diff --git a/core/src/com/etheller/warsmash/viewer5/Scene.java b/core/src/com/etheller/warsmash/viewer5/Scene.java index 76f1365..08f86fb 100644 --- a/core/src/com/etheller/warsmash/viewer5/Scene.java +++ b/core/src/com/etheller/warsmash/viewer5/Scene.java @@ -31,21 +31,18 @@ import com.etheller.warsmash.viewer5.handlers.w3x.DynamicShadowManager; * audio is always on in LibGDX generally. So we will probably simplify or skip * over those behaviors other than a boolean on/off toggle for audio. */ -public class Scene { +public abstract class Scene { public final ModelViewer viewer; public final Camera camera; - public Grid grid; - public int visibleCells; - public int visibleInstances; public int updatedParticles; public boolean audioEnabled; public AudioContext audioContext; public final List instances; - public final int currentInstance; + public int currentInstance; public final List batchedInstances; - public final int currentBatchedInstance; + public int currentBatchedInstance; public final EmittedObjectUpdater emitterObjectUpdater; public final Map batches; public final Comparator instanceDepthComparator; @@ -64,10 +61,7 @@ public class Scene { final CanvasProvider canvas = viewer.canvas; this.viewer = viewer; this.camera = new Camera(); - this.grid = new Grid(-100000, -100000, 200000, 200000, 200000, 200000); - this.visibleCells = 0; - this.visibleInstances = 0; this.updatedParticles = 0; this.audioEnabled = false; @@ -117,8 +111,7 @@ public class Scene { // Only allow instances that are actually ok to be added the scene. if (instance.model.ok) { - this.grid.moved(instance); - + instanceMoved(instance, instance.worldLocation.x, instance.worldLocation.y); return true; } } @@ -126,9 +119,11 @@ public class Scene { return false; } + public abstract void instanceMoved(ModelInstance instance, float x, float y); + public boolean removeInstance(final ModelInstance instance) { if (instance.scene == this) { - this.grid.remove(instance); + innerRemove(instance); instance.scene = null; this.instances.remove(instance); @@ -138,17 +133,9 @@ public class Scene { return false; } - public void clear() { - // First remove references to this scene stored in the instances. - for (final GridCell cell : this.grid.cells) { - for (final ModelInstance instance : cell.instances) { - instance.scene = null; - } - } + protected abstract void innerRemove(ModelInstance instance); - // Then remove references to the instances. - this.grid.clear(); - } + public abstract void clear(); public boolean detach() { if (this.viewer != null) { @@ -191,54 +178,10 @@ public class Scene { final int frame = this.viewer.frame; - int currentInstance = 0; - int currentBatchedInstance = 0; + final int currentInstance = 0; + final int currentBatchedInstance = 0; - this.visibleCells = 0; - this.visibleInstances = 0; - - // Update and collect all of the visible instances. - for (final GridCell cell : this.grid.cells) { - if (cell.isVisible(this.camera)) { - this.visibleCells += 1; - - for (final ModelInstance instance : new ArrayList<>(cell.instances)) { -// final ModelInstance instance = cell.instances.get(i); - if (instance.rendered && (instance.cullFrame < frame) && instance.isVisible(this.camera)) { - instance.cullFrame = frame; - - if (instance.updateFrame < frame) { - instance.update(dt, this); - if (!instance.rendered) { - // it became hidden while it updated - continue; - } - } - - if (instance.isBatched()) { - if (currentBatchedInstance < this.batchedInstances.size()) { - this.batchedInstances.set(currentBatchedInstance++, instance); - } - else { - this.batchedInstances.add(instance); - currentBatchedInstance++; - } - } - else { - if (currentInstance < this.instances.size()) { - this.instances.set(currentInstance++, instance); - } - else { - this.instances.add(instance); - currentInstance++; - } - } - - this.visibleInstances += 1; - } - } - } - } + innerUpdate(dt, frame); for (int i = this.batchedInstances.size() - 1; i >= currentBatchedInstance; i--) { this.batchedInstances.remove(i); @@ -253,6 +196,8 @@ public class Scene { this.updatedParticles = this.emitterObjectUpdater.objects.size(); } + protected abstract void innerUpdate(float dt, int frame); + public void startFrame() { final GL20 gl = this.viewer.gl; final Rectangle viewport = this.camera.rect; diff --git a/core/src/com/etheller/warsmash/viewer5/SimpleScene.java b/core/src/com/etheller/warsmash/viewer5/SimpleScene.java new file mode 100644 index 0000000..735ca65 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/SimpleScene.java @@ -0,0 +1,73 @@ +package com.etheller.warsmash.viewer5; + +import java.util.ArrayList; +import java.util.List; + +public class SimpleScene extends Scene { + private final List allInstances = new ArrayList<>(); + + @Override + public void instanceMoved(final ModelInstance instance, final float x, final float y) { + if (instance.left == -1) { + instance.left = 0; + this.allInstances.add(instance); + } + } + + @Override + protected void innerRemove(final ModelInstance instance) { + this.allInstances.remove(instance); + instance.left = -1; + } + + @Override + public void clear() { + for (final ModelInstance instance : this.allInstances) { + instance.scene = null; + } + this.allInstances.clear(); + } + + @Override + protected void innerUpdate(final float dt, final int frame) { + + // Update and collect all of the visible instances. + for (final ModelInstance instance : new ArrayList<>(this.allInstances)) { + // Below: current SimpleScene is not checking instance visibility. + // It's meant to be simple. Low number of models. Render everything. + // Otherwise unit portraits bust + if (instance.rendered && (instance.cullFrame < frame) && instance.isVisible(this.camera)) { + instance.cullFrame = frame; + + if (instance.updateFrame < frame) { + instance.update(dt, this); + if (!instance.rendered) { + // it became hidden while it updated + continue; + } + } + + if (instance.isBatched()) { + if (this.currentBatchedInstance < this.batchedInstances.size()) { + this.batchedInstances.set(this.currentBatchedInstance++, instance); + } + else { + this.batchedInstances.add(instance); + this.currentBatchedInstance++; + } + } + else { + if (this.currentInstance < this.instances.size()) { + this.instances.set(this.currentInstance++, instance); + } + else { + this.instances.add(instance); + this.currentInstance++; + } + } + + } + } + } + +} diff --git a/core/src/com/etheller/warsmash/viewer5/WorldScene.java b/core/src/com/etheller/warsmash/viewer5/WorldScene.java new file mode 100644 index 0000000..b2d6043 --- /dev/null +++ b/core/src/com/etheller/warsmash/viewer5/WorldScene.java @@ -0,0 +1,105 @@ +package com.etheller.warsmash.viewer5; + +import java.util.ArrayList; + +/** + * A scene. + * + * Every scene has its own list of model instances, and its own camera and + * viewport. + * + * In addition, in Ghostwolf's original code every scene may have its own + * AudioContext if enableAudio() is called. If audo is enabled, the + * AudioContext's listener's location will be updated automatically. Note that + * due to browser policies, this may be done only after user interaction with + * the web page. + * + * In "Warsmash", we are starting from an attempt to replicate Ghostwolf, but + * audio is always on in LibGDX generally. So we will probably simplify or skip + * over those behaviors other than a boolean on/off toggle for audio. + */ +public class WorldScene extends Scene { + + public Grid grid; + public int visibleCells; + public int visibleInstances; + + public WorldScene(final ModelViewer viewer) { + super(viewer); + this.grid = new Grid(-100000, -100000, 200000, 200000, 200000, 200000); + this.visibleCells = 0; + this.visibleInstances = 0; + } + + @Override + public void instanceMoved(final ModelInstance instance, final float x, final float y) { + this.grid.moved(instance, x, y); + } + + @Override + protected void innerRemove(final ModelInstance instance) { + this.grid.remove(instance); + } + + @Override + public void clear() { + // First remove references to this scene stored in the instances. + for (final GridCell cell : this.grid.cells) { + for (final ModelInstance instance : cell.instances) { + instance.scene = null; + } + } + + // Then remove references to the instances. + this.grid.clear(); + } + + @Override + protected void innerUpdate(final float dt, final int frame) { + this.visibleCells = 0; + this.visibleInstances = 0; + + // Update and collect all of the visible instances. + for (final GridCell cell : this.grid.cells) { + if (cell.isVisible(this.camera)) { + this.visibleCells += 1; + + for (final ModelInstance instance : new ArrayList<>(cell.instances)) { +// final ModelInstance instance = cell.instances.get(i); + if (instance.rendered && (instance.cullFrame < frame) && instance.isVisible(this.camera)) { + instance.cullFrame = frame; + + if (instance.updateFrame < frame) { + instance.update(dt, this); + if (!instance.rendered) { + // it became hidden while it updated + continue; + } + } + + if (instance.isBatched()) { + if (this.currentBatchedInstance < this.batchedInstances.size()) { + this.batchedInstances.set(this.currentBatchedInstance++, instance); + } + else { + this.batchedInstances.add(instance); + this.currentBatchedInstance++; + } + } + else { + if (this.currentInstance < this.instances.size()) { + this.instances.set(this.currentInstance++, instance); + } + else { + this.instances.add(instance); + this.currentInstance++; + } + } + + this.visibleInstances += 1; + } + } + } + } + } +} diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java index 1ec84c0..b3975f1 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/Geoset.java @@ -21,9 +21,13 @@ public class Geoset { public boolean hasAlphaAnim; public boolean hasColorAnim; public boolean hasObjectAnim; + private final int openGLSkinType; + private final int skinStride; + private final int boneCountOffsetBytes; public Geoset(final MdxModel model, final int index, final int positionOffset, final int normalOffset, - final int uvOffset, final int skinOffset, final int faceOffset, final int vertices, final int elements) { + final int uvOffset, final int skinOffset, final int faceOffset, final int vertices, final int elements, + final int openGLSkinType, final int skinStride, final int boneCountOffsetBytes) { this.model = model; this.index = index; this.positionOffset = positionOffset; @@ -33,6 +37,9 @@ public class Geoset { this.faceOffset = faceOffset; this.vertices = vertices; this.elements = elements; + this.openGLSkinType = openGLSkinType; + this.skinStride = skinStride; + this.boneCountOffsetBytes = boneCountOffsetBytes; for (final GeosetAnimation geosetAnimation : model.getGeosetAnimations()) { if (geosetAnimation.geosetId == index) { @@ -96,8 +103,9 @@ public class Geoset { shader.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset); // shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset); shader.setVertexAttribute("a_uv", 2, GL20.GL_FLOAT, false, 0, this.uvOffset + (coordId * this.vertices * 8)); - shader.setVertexAttribute("a_bones", 4, GL20.GL_UNSIGNED_BYTE, false, 5, this.skinOffset); - shader.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 5, this.skinOffset + 4); + shader.setVertexAttribute("a_bones", 4, this.openGLSkinType, false, this.skinStride, this.skinOffset); + shader.setVertexAttribute("a_boneNumber", 1, this.openGLSkinType, false, this.skinStride, + this.skinOffset + this.boneCountOffsetBytes); } public void bindExtended(final ShaderProgram shader, final int coordId) { @@ -105,9 +113,11 @@ public class Geoset { shader.setVertexAttribute("a_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset); shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset); shader.setVertexAttribute("a_uv", 2, GL20.GL_FLOAT, false, 0, this.uvOffset + (coordId * this.vertices * 8)); - shader.setVertexAttribute("a_bones", 4, GL20.GL_UNSIGNED_BYTE, false, 9, this.skinOffset); - shader.setVertexAttribute("a_extendedBones", 4, GL20.GL_UNSIGNED_BYTE, false, 9, this.skinOffset + 4); - shader.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 9, this.skinOffset + 8); + shader.setVertexAttribute("a_bones", 4, this.openGLSkinType, false, this.skinStride, this.skinOffset); + shader.setVertexAttribute("a_extendedBones", 4, this.openGLSkinType, false, this.skinStride, + this.skinOffset + (this.boneCountOffsetBytes / 2)); + shader.setVertexAttribute("a_boneNumber", 1, this.openGLSkinType, false, this.skinStride, + this.skinOffset + this.boneCountOffsetBytes); } public void render() { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java index f3b22f8..8495a08 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/MdxModel.java @@ -58,7 +58,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model { @Override public ModelInstance createInstance(final int type) { - if (type == 1) { + if ((type == 1) && false) { return new MdxSimpleInstance(this); } else { @@ -176,7 +176,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model { } // Geosets - SetupGeosets.setupGeosets(this, parser.getGeosets()); + SetupGeosets.setupGeosets(this, parser.getGeosets(), parser.getBones().size() >= 256); this.pivotPoints = parser.getPivotPoints(); @@ -344,4 +344,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model { return this.eventObjects; } + public List getBones() { + return this.bones; + } + } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java index 31e473d..ad214d8 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/mdx/SetupGeosets.java @@ -12,8 +12,8 @@ public class SetupGeosets { private static final int EXTENDED_BATCH = 1; private static final int REFORGED_BATCH = 2; - public static void setupGeosets(final MdxModel model, - final List geosets) { + public static void setupGeosets(final MdxModel model, final List geosets, + final boolean bigNodeSpace) { if (geosets.size() > 0) { final GL20 gl = model.viewer.gl; int positionBytes = 0; @@ -23,6 +23,12 @@ public class SetupGeosets { int faceBytes = 0; final int[] batchTypes = new int[geosets.size()]; + final int extendedBatchStride = bigNodeSpace ? 36 : 9; + final int normalBatchStride = bigNodeSpace ? 20 : 5; + final int openGLSkinType = bigNodeSpace ? GL20.GL_UNSIGNED_INT : GL20.GL_UNSIGNED_BYTE; + final int normalBatchBoneCountOffsetBytes = bigNodeSpace ? 16 : 4; + final int extendedBatchBoneCountOffsetBytes = bigNodeSpace ? 32 : 8; + for (int i = 0, l = geosets.size(); i < l; i++) { final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i); @@ -48,12 +54,12 @@ public class SetupGeosets { } if (biggestGroup > 4) { - skinBytes += vertices * 9; + skinBytes += vertices * extendedBatchStride; batchTypes[i] = EXTENDED_BATCH; } else { - skinBytes += vertices * 5; + skinBytes += vertices * normalBatchStride; batchTypes[i] = NORMAL_BATCH; } @@ -80,17 +86,31 @@ public class SetupGeosets { for (int i = 0, l = geosets.size(); i < l; i++) { final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i); + final int batchType = batchTypes[i]; if (true /* geoset.lod == 0 */) { final float[] positions = geoset.getVertices(); final float[] normals = geoset.getNormals(); final float[][] uvSets = geoset.getUvSets(); final int[] faces = geoset.getFaces(); - byte[] skin = null; + int[] skin = null; final int vertices = geoset.getVertices().length / 3; - final int batchType = batchTypes[i]; + + int maxBones; + int skinStride; + int boneCountOffsetBytes; + if (batchType == EXTENDED_BATCH) { + maxBones = 8; + skinStride = extendedBatchStride; + boneCountOffsetBytes = extendedBatchBoneCountOffsetBytes; + } + else { + maxBones = 4; + skinStride = normalBatchStride; + boneCountOffsetBytes = normalBatchBoneCountOffsetBytes; + } if (batchType == REFORGED_BATCH) { - // skin = geoset.skin; + // skin = geoset.skin; // THIS IS NOT IMPLEMENTED } else { final long[] matrixIndices = geoset.getMatrixIndices(); @@ -102,12 +122,8 @@ public class SetupGeosets { // That being said, there are a few models with geosets that need more, for // example the Water Elemental. // These geosets use a different shader, which support up to 8 bones per vertex. - int maxBones = 4; - if (batchType == EXTENDED_BATCH) { - maxBones = 8; - } - skin = new byte[vertices * (maxBones + 1)]; + skin = new int[vertices * (maxBones + 1)]; // Slice the matrix groups for (final long size : geoset.getMatrixGroups()) { @@ -130,17 +146,18 @@ public class SetupGeosets { final int bones = Math.min(matrixGroup.length, maxBones); for (int j = 0; j < bones; j++) { - skin[offset + j] = (byte) (matrixGroup[j] + 1); // 1 is added to diffrentiate + skin[offset + j] = (int) (matrixGroup[j] + 1); // 1 is added to diffrentiate // between matrix 0, and no matrix. } - skin[offset + maxBones] = (byte) bones; + skin[offset + maxBones] = bones; } } } final Geoset vGeoset = new Geoset(model, model.getGeosets().size(), positionOffset, normalOffset, - uvOffset, skinOffset, faceOffset, vertices, faces.length); + uvOffset, skinOffset, faceOffset, vertices, faces.length, openGLSkinType, skinStride, + boneCountOffsetBytes); model.getGeosets().add(vGeoset); @@ -173,8 +190,9 @@ public class SetupGeosets { } // Skin. - gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, RenderMathUtils.wrap(skin)); - skinOffset += skin.length * 1; + gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, + bigNodeSpace ? RenderMathUtils.wrap(skin) : RenderMathUtils.wrapAsBytes(skin)); + skinOffset += skin.length * (bigNodeSpace ? 4 : 1); // Faces. gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length, diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java index d993d68..e18308c 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/War3MapViewer.java @@ -533,7 +533,7 @@ public class War3MapViewer extends ModelViewer { if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { path = path.substring(0, path.length() - 4); } - if (row.readSLKTagInt("fileVerFlags") == 2) { + if ((row.readSLKTagInt("fileVerFlags") == 2) && this.dataSource.has(path + "_V1.mdx")) { path += "_V1"; } @@ -804,6 +804,7 @@ public class War3MapViewer extends ModelViewer { } public List selectUnit(final float x, final float y, final boolean toggle) { + System.out.println("world: " + x + "," + y); final float[] ray = rayHeap; mousePosHeap.set(x, y); this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java index 46ade03..284a61b 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/environment/Terrain.java @@ -1025,10 +1025,14 @@ public class Terrain { final int shadowSize = columns * rows; final byte[] shadowData = new byte[columns * rows]; if (this.viewer.mapMpq.has("war3map.shd")) { - final InputStream shadowSource = this.viewer.mapMpq.getResourceAsStream("war3map.shd"); - final byte[] buffer = IOUtils.toByteArray(shadowSource); + final byte[] buffer; + + try (final InputStream shadowSource = this.viewer.mapMpq.getResourceAsStream("war3map.shd")) { + buffer = IOUtils.toByteArray(shadowSource); + } + for (int i = 0; i < shadowSize; i++) { - shadowData[i] = (byte) (buffer[i] / 2); + shadowData[i] = (byte) ((buffer[i] & 0xFF) / 2f); } } diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderAttackProjectile.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderAttackProjectile.java index 3f92efd..f1091d0 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderAttackProjectile.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderAttackProjectile.java @@ -102,7 +102,7 @@ public class RenderAttackProjectile { this.modelInstance.setLocation(this.x, this.y, this.z); this.modelInstance.localRotation.setFromAxisRad(0, 0, 1, this.yaw); this.modelInstance.rotate(pitchHeap.setFromAxisRad(0, -1, 0, this.pitch)); - war3MapViewer.worldScene.grid.moved(this.modelInstance); + war3MapViewer.worldScene.grid.moved(this.modelInstance, this.x, this.y); final boolean everythingDone = this.simulationProjectile.isDone() && this.modelInstance.sequenceEnded; if (everythingDone) { diff --git a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java index 09b6ea3..88649fb 100644 --- a/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java +++ b/core/src/com/etheller/warsmash/viewer5/handlers/w3x/rendersim/RenderUnit.java @@ -176,7 +176,7 @@ public class RenderUnit { } this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360; this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing)); - map.worldScene.grid.moved(this.instance); + map.worldScene.grid.moved(this.instance, this.location[0], this.location[1]); final MdxComplexInstance mdxComplexInstance = this.instance; final COrder currentOrder = this.simulationUnit.getCurrentOrder(); if (this.simulationUnit.getLife() <= 0) { diff --git a/fdfparser/.gitignore b/fdfparser/.gitignore new file mode 100644 index 0000000..84c048a --- /dev/null +++ b/fdfparser/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/AnchorDefinition.java b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/AnchorDefinition.java index 7a27ec6..1385223 100644 --- a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/AnchorDefinition.java +++ b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/AnchorDefinition.java @@ -22,4 +22,9 @@ public class AnchorDefinition { public float getY() { return this.y; } + + @Override + public String toString() { + return "AnchorDefinition [myPoint=" + this.myPoint + ", x=" + this.x + ", y=" + this.y + "]"; + } } diff --git a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/FrameDefinition.java b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/FrameDefinition.java index 1225719..b4f58c5 100644 --- a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/FrameDefinition.java +++ b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/FrameDefinition.java @@ -8,6 +8,9 @@ import java.util.Map; import java.util.Set; import com.etheller.warsmash.parsers.fdf.datamodel.fields.FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor.GetFloatFieldVisitor; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor.GetStringFieldVisitor; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor.GetVector4FieldVisitor; /** * Pretty sure this is probably not how it works in-game but this silly @@ -58,6 +61,14 @@ public class FrameDefinition { this.flags.add(flag); } + public boolean has(final String flag) { + return this.flags.contains(flag); + } + + public FrameDefinitionField get(final String fieldName) { + return this.nameToField.get(fieldName); + } + @Override public String toString() { return "FrameDefinition [frameClass=" + this.frameClass + ", frameType=" + this.frameType + ", name=" @@ -65,4 +76,51 @@ public class FrameDefinition { + this.nameToField + ", setPoints=" + this.setPoints + ", anchors=" + this.anchors + "]"; } + public String getFrameType() { + return this.frameType; + } + + public String getName() { + return this.name; + } + + public FrameClass getFrameClass() { + return this.frameClass; + } + + public List getInnerFrames() { + return this.innerFrames; + } + + public List getAnchors() { + return this.anchors; + } + + public List getSetPoints() { + return this.setPoints; + } + + public String getString(final String id) { + final FrameDefinitionField frameDefinitionField = this.nameToField.get(id); + if (frameDefinitionField != null) { + return frameDefinitionField.visit(GetStringFieldVisitor.INSTANCE); + } + return null; + } + + public Float getFloat(final String id) { + final FrameDefinitionField frameDefinitionField = this.nameToField.get(id); + if (frameDefinitionField != null) { + return frameDefinitionField.visit(GetFloatFieldVisitor.INSTANCE); + } + return null; + } + + public Vector4Definition getVector4(final String id) { + final FrameDefinitionField frameDefinitionField = this.nameToField.get(id); + if (frameDefinitionField != null) { + return frameDefinitionField.visit(GetVector4FieldVisitor.INSTANCE); + } + return null; + } } diff --git a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetFloatFieldVisitor.java b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetFloatFieldVisitor.java new file mode 100644 index 0000000..38a3084 --- /dev/null +++ b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetFloatFieldVisitor.java @@ -0,0 +1,56 @@ +package com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor; + +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FloatFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FontFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FrameDefinitionFieldVisitor; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.StringFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.StringPairFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.TextJustifyFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector2FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector3FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector4FrameDefinitionField; + +public class GetFloatFieldVisitor implements FrameDefinitionFieldVisitor { + public static GetFloatFieldVisitor INSTANCE = new GetFloatFieldVisitor(); + + @Override + public Float accept(final StringFrameDefinitionField field) { + return null; + } + + @Override + public Float accept(final StringPairFrameDefinitionField field) { + return null; + } + + @Override + public Float accept(final FloatFrameDefinitionField field) { + return field.getValue(); + } + + @Override + public Float accept(final Vector3FrameDefinitionField field) { + return null; + } + + @Override + public Float accept(final Vector4FrameDefinitionField field) { + return null; + } + + @Override + public Float accept(final Vector2FrameDefinitionField field) { + return null; + } + + @Override + public Float accept(final FontFrameDefinitionField field) { + return null; + } + + @Override + public Float accept(final TextJustifyFrameDefinitionField field) { + return null; + } + +} diff --git a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetStringFieldVisitor.java b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetStringFieldVisitor.java new file mode 100644 index 0000000..b90b83d --- /dev/null +++ b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetStringFieldVisitor.java @@ -0,0 +1,56 @@ +package com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor; + +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FloatFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FontFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FrameDefinitionFieldVisitor; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.StringFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.StringPairFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.TextJustifyFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector2FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector3FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector4FrameDefinitionField; + +public class GetStringFieldVisitor implements FrameDefinitionFieldVisitor { + public static GetStringFieldVisitor INSTANCE = new GetStringFieldVisitor(); + + @Override + public String accept(final StringFrameDefinitionField field) { + return field.getValue(); + } + + @Override + public String accept(final StringPairFrameDefinitionField field) { + return null; + } + + @Override + public String accept(final FloatFrameDefinitionField field) { + return null; + } + + @Override + public String accept(final Vector3FrameDefinitionField field) { + return null; + } + + @Override + public String accept(final Vector4FrameDefinitionField field) { + return null; + } + + @Override + public String accept(final Vector2FrameDefinitionField field) { + return null; + } + + @Override + public String accept(final FontFrameDefinitionField field) { + return null; + } + + @Override + public String accept(final TextJustifyFrameDefinitionField field) { + return null; + } + +} diff --git a/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetVector4FieldVisitor.java b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetVector4FieldVisitor.java new file mode 100644 index 0000000..7bc33c7 --- /dev/null +++ b/fdfparser/src/com/etheller/warsmash/parsers/fdf/datamodel/fields/visitor/GetVector4FieldVisitor.java @@ -0,0 +1,57 @@ +package com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor; + +import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FloatFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FontFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.FrameDefinitionFieldVisitor; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.StringFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.StringPairFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.TextJustifyFrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector2FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector3FrameDefinitionField; +import com.etheller.warsmash.parsers.fdf.datamodel.fields.Vector4FrameDefinitionField; + +public class GetVector4FieldVisitor implements FrameDefinitionFieldVisitor { + public static GetVector4FieldVisitor INSTANCE = new GetVector4FieldVisitor(); + + @Override + public Vector4Definition accept(final StringFrameDefinitionField field) { + return null; + } + + @Override + public Vector4Definition accept(final StringPairFrameDefinitionField field) { + return null; + } + + @Override + public Vector4Definition accept(final FloatFrameDefinitionField field) { + return null; + } + + @Override + public Vector4Definition accept(final Vector3FrameDefinitionField field) { + return null; + } + + @Override + public Vector4Definition accept(final Vector4FrameDefinitionField field) { + return field.getValue(); + } + + @Override + public Vector4Definition accept(final Vector2FrameDefinitionField field) { + return null; + } + + @Override + public Vector4Definition accept(final FontFrameDefinitionField field) { + return null; + } + + @Override + public Vector4Definition accept(final TextJustifyFrameDefinitionField field) { + return null; + } + +} diff --git a/jassparser/antlr-src/Jass.g4 b/jassparser/antlr-src/Jass.g4 new file mode 100644 index 0000000..595643c --- /dev/null +++ b/jassparser/antlr-src/Jass.g4 @@ -0,0 +1,169 @@ +/** + * Define a grammar called Hello + */ +grammar Jass; + +@header { + package com.etheller.interpreter; +} + + +program : + newlines + | + newlines_opt + typeDefinitionBlock + (block)* + (functionBlock)* + ; + +typeDefinition : + TYPE ID EXTENDS ID newlines + ; + +type : + ID # BasicType + | + ID ARRAY # ArrayType + | + 'nothing' # NothingType + ; + +global : + type ID newlines # BasicGlobal + | + type ID assignTail newlines # DefinitionGlobal + ; + +assignTail: + EQUALS expression; + +expression: + ID # ReferenceExpression + | + STRING_LITERAL #StringLiteralExpression + | + INTEGER #IntegerLiteralExpression + | + FUNCTION ID #FunctionReferenceExpression + | + NULL # NullExpression + | + TRUE # TrueExpression + | + FALSE # FalseExpression + | + ID '[' expression ']' # ArrayReferenceExpression + | + functionExpression # FunctionCallExpression + | + '(' expression ')' # ParentheticalExpression + ; + +functionExpression: + ID '(' argsList ')' + ; + +argsList: + expression # SingleArgument + | + expression ',' argsList # ListArgument + | + #EmptyArgument + ; + +//#booleanExpression: +// simpleArithmeticExpression # PassBooleanThroughExpression +// | + +statement: + CALL functionExpression newlines #CallStatement + | + SET ID EQUALS expression newlines #SetStatement + | + SET ID '[' expression ']' EQUALS expression newlines # ArrayedAssignmentStatement + | + RETURN expression newlines # ReturnStatement + ; + +param: + type ID; + +paramList: + param # SingleParameter + | + param ',' paramList # ListParameter + | + 'nothing' # NothingParameter + ; + +globalsBlock : + GLOBALS newlines (global)* ENDGLOBALS newlines ; + +typeDefinitionBlock : + (typeDefinition)* + ; + +nativeBlock: + NATIVE ID TAKES paramList RETURNS type newlines + ; + +block: + globalsBlock + | + nativeBlock + ; + +functionBlock: + FUNCTION ID TAKES paramList RETURNS type newlines (statement)* ENDFUNCTION newlines + ; + +newlines: + NEWLINES + | + EOF; + +newlines_opt: + NEWLINES + | + EOF + | + ; + +EQUALS : '='; + + +GLOBALS : 'globals' ; // globals +ENDGLOBALS : 'endglobals' ; // end globals block + +NATIVE : 'native' ; + +FUNCTION : 'function' ; // function +TAKES : 'takes' ; // takes +RETURNS : 'returns' ; +ENDFUNCTION : 'endfunction' ; // endfunction + +CALL : 'call' ; +SET : 'set' ; +RETURN : 'return' ; + +ARRAY : 'array' ; + +TYPE : 'type'; + +EXTENDS : 'extends'; + +STRING_LITERAL : ('"'.*?'"'); + +INTEGER : [0]|([1-9][0-9]*) ; + +NULL : 'null' ; +TRUE : 'true' ; +FALSE : 'false' ; + +ID : ([a-zA-Z_][a-zA-Z_0-9]*) ; // match identifiers + +WS : [ \t]+ -> skip ; // skip spaces, tabs + +NEWLINES : NEWLINE+; +fragment NEWLINE : '\r' '\n' | '\n' | '\r' | ('//'.*?'\n'); \ No newline at end of file diff --git a/jassparser/build.gradle b/jassparser/build.gradle new file mode 100644 index 0000000..e12bfa4 --- /dev/null +++ b/jassparser/build.gradle @@ -0,0 +1,49 @@ +apply plugin: "antlr" + +sourceCompatibility = 1.8 +[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' + +sourceSets.main.java.srcDirs = [ "src/", "build/generated-src" ] +sourceSets.main.antlr.srcDirs = [ "antlr-src/" ] + +project.ext.mainClassName = "com.etheller.warsmash.jassparser.Main" + +task run(dependsOn: classes, type: JavaExec) { + main = project.mainClassName + classpath = sourceSets.main.runtimeClasspath + standardInput = System.in + ignoreExitValue = true +} + +task dist(type: Jar) { + from files(sourceSets.main.output.classesDir) + from files(sourceSets.main.output.resourcesDir) + from {configurations.compile.collect {zipTree(it)}} + + manifest { + attributes 'Main-Class': project.mainClassName + } +} + +dist.dependsOn classes + +eclipse.project { + name = appName + "-jassparser" +} + +task afterEclipseImport(description: "Post processing after project generation", group: "IDE") { + doLast { + def classpath = new XmlParser().parse(file(".classpath")) + def writer = new FileWriter(file(".classpath")) + def printer = new XmlNodePrinter(new PrintWriter(writer)) + printer.setPreserveWhitespace(true) + printer.print(classpath) + } +} + + +generateGrammarSource { + maxHeapSize = "64m" + arguments += ["-visitor", "-no-listener"] + outputDirectory = file("build/generated-src/com/etheller/warsmash/jassparser") +} \ No newline at end of file diff --git a/jassparser/src/com/etheller/interpreter/ast/Assignable.java b/jassparser/src/com/etheller/interpreter/ast/Assignable.java new file mode 100644 index 0000000..bf98b50 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/Assignable.java @@ -0,0 +1,29 @@ +package com.etheller.interpreter.ast; + +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; +import com.etheller.interpreter.ast.value.visitor.JassTypeGettingValueVisitor; + +public class Assignable { + private JassValue value; + private final JassType type; + + public Assignable(final JassType type) { + this.type = type; + } + + public void setValue(final JassValue value) { + if (value.visit(JassTypeGettingValueVisitor.getInstance()) != type) { + throw new RuntimeException("Incompatible types"); + } + this.value = value; + } + + public JassValue getValue() { + return value; + } + + public JassType getType() { + return type; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/JassRunner.java b/jassparser/src/com/etheller/interpreter/ast/JassRunner.java new file mode 100644 index 0000000..5470c69 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/JassRunner.java @@ -0,0 +1,50 @@ +package com.etheller.interpreter.ast; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import com.etheller.interpreter.JassLexer; +import com.etheller.interpreter.JassParser; +import com.etheller.interpreter.ast.visitors.JassProgramVisitor; + +public class JassRunner { + public static final boolean REPORT_SYNTAX_ERRORS = true; + + public static void main(final String[] args) { + if (args.length < 1) { + System.err.println("Usage: [...]"); + return; + } + final JassProgramVisitor jassProgramVisitor = new JassProgramVisitor(); + for (final String arg : args) { + try { + final JassLexer lexer = new JassLexer(CharStreams.fromFileName(arg)); + final JassParser parser = new JassParser(new CommonTokenStream(lexer)); + parser.addErrorListener(new BaseErrorListener() { + @Override + public void syntaxError(final Recognizer recognizer, final Object offendingSymbol, + final int line, final int charPositionInLine, final String msg, + final RecognitionException e) { + if (!REPORT_SYNTAX_ERRORS) { + return; + } + + String sourceName = recognizer.getInputStream().getSourceName(); + if (!sourceName.isEmpty()) { + sourceName = String.format("%s:%d:%d: ", sourceName, line, charPositionInLine); + } + + System.err.println(sourceName + "line " + line + ":" + charPositionInLine + " " + msg); + } + }); + jassProgramVisitor.visit(parser.program()); + } catch (final Exception e) { + e.printStackTrace(); + } + } + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/expression/ArrayRefJassExpression.java b/jassparser/src/com/etheller/interpreter/ast/expression/ArrayRefJassExpression.java new file mode 100644 index 0000000..1ae050e --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/expression/ArrayRefJassExpression.java @@ -0,0 +1,38 @@ +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.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.JassValue; +import com.etheller.interpreter.ast.value.visitor.ArrayJassValueVisitor; +import com.etheller.interpreter.ast.value.visitor.IntegerJassValueVisitor; + +public class ArrayRefJassExpression implements JassExpression { + private final String identifier; + private final JassExpression indexExpression; + + public ArrayRefJassExpression(final String identifier, final JassExpression indexExpression) { + this.identifier = identifier; + this.indexExpression = indexExpression; + } + + @Override + public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) { + Assignable variable = localScope.getAssignableLocal(identifier); + final JassValue index = indexExpression.evaluate(globalScope, localScope); + if (variable == null) { + variable = globalScope.getAssignableGlobal(identifier); + } + if (variable.getValue() == null) { + throw new RuntimeException("Unable to use subscript on uninitialized variable"); + } + final ArrayJassValue arrayValue = variable.getValue().visit(ArrayJassValueVisitor.getInstance()); + if (arrayValue != null) { + return arrayValue.get(index.visit(IntegerJassValueVisitor.getInstance())); + } else { + throw new RuntimeException("Not an array"); + } + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/expression/FunctionCallJassExpression.java b/jassparser/src/com/etheller/interpreter/ast/expression/FunctionCallJassExpression.java new file mode 100644 index 0000000..9cdece9 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/expression/FunctionCallJassExpression.java @@ -0,0 +1,34 @@ +package com.etheller.interpreter.ast.expression; + +import java.util.ArrayList; +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.value.JassValue; + +public class FunctionCallJassExpression implements JassExpression { + private final String functionName; + private final List arguments; + + public FunctionCallJassExpression(final String functionName, final List arguments) { + this.functionName = functionName; + this.arguments = arguments; + } + + @Override + public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) { + final JassFunction functionByName = globalScope.getFunctionByName(functionName); + if (functionByName == null) { + throw new RuntimeException("Undefined function: " + functionName); + } + final List evaluatedExpressions = new ArrayList<>(); + for (final JassExpression expr : arguments) { + final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope); + evaluatedExpressions.add(evaluatedExpression); + } + return functionByName.call(evaluatedExpressions, globalScope); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/expression/FunctionReferenceJassExpression.java b/jassparser/src/com/etheller/interpreter/ast/expression/FunctionReferenceJassExpression.java new file mode 100644 index 0000000..0e9a2b9 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/expression/FunctionReferenceJassExpression.java @@ -0,0 +1,25 @@ +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.value.CodeJassValue; +import com.etheller.interpreter.ast.value.JassValue; + +public class FunctionReferenceJassExpression implements JassExpression { + private final String identifier; + + public FunctionReferenceJassExpression(final String identifier) { + this.identifier = identifier; + } + + @Override + public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) { + final JassFunction functionByName = globalScope.getFunctionByName(identifier); + if (functionByName == null) { + throw new RuntimeException("Unable to find function: " + identifier); + } + return new CodeJassValue(functionByName); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/expression/JassExpression.java b/jassparser/src/com/etheller/interpreter/ast/expression/JassExpression.java new file mode 100644 index 0000000..edc47af --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/expression/JassExpression.java @@ -0,0 +1,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.value.JassValue; + +public interface JassExpression { + JassValue evaluate(GlobalScope globalScope, LocalScope localScope); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/expression/LiteralJassExpression.java b/jassparser/src/com/etheller/interpreter/ast/expression/LiteralJassExpression.java new file mode 100644 index 0000000..c989de6 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/expression/LiteralJassExpression.java @@ -0,0 +1,19 @@ +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.value.JassValue; + +public class LiteralJassExpression implements JassExpression { + private final JassValue value; + + public LiteralJassExpression(final JassValue value) { + this.value = value; + } + + @Override + public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) { + return value; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/expression/ReferenceJassExpression.java b/jassparser/src/com/etheller/interpreter/ast/expression/ReferenceJassExpression.java new file mode 100644 index 0000000..b952a4e --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/expression/ReferenceJassExpression.java @@ -0,0 +1,24 @@ +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.value.JassValue; + +public class ReferenceJassExpression implements JassExpression { + private final String identifier; + + public ReferenceJassExpression(final String identifier) { + this.identifier = identifier; + } + + @Override + public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope) { + final Assignable local = localScope.getAssignableLocal(identifier); + if (local == null) { + return globalScope.getGlobal(identifier); + } + return local.getValue(); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/function/AbstractJassFunction.java b/jassparser/src/com/etheller/interpreter/ast/function/AbstractJassFunction.java new file mode 100644 index 0000000..d132b75 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/function/AbstractJassFunction.java @@ -0,0 +1,44 @@ +package com.etheller.interpreter.ast.function; + +import java.util.List; + +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.scope.LocalScope; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; + +/** + * Not a native + * + * @author Eric + * + */ +public abstract class AbstractJassFunction implements JassFunction { + protected final List parameters; + protected final JassType returnType; + + public AbstractJassFunction(final List parameters, final JassType returnType) { + this.parameters = parameters; + this.returnType = returnType; + } + + @Override + public final JassValue call(final List arguments, final GlobalScope globalScope) { + if (arguments.size() != parameters.size()) { + throw new RuntimeException("Invalid number of arguments passed to function"); + } + final LocalScope localScope = new LocalScope(); + for (int i = 0; i < parameters.size(); i++) { + final JassParameter parameter = parameters.get(i); + final JassValue argument = arguments.get(i); + if (!parameter.matchesType(argument)) { + throw new RuntimeException("Invalid type for specified argument"); + } + localScope.createLocal(parameter.getIdentifier(), parameter.getType(), argument); + } + return innerCall(arguments, globalScope, localScope); + } + + protected abstract JassValue innerCall(final List arguments, final GlobalScope globalScope, + final LocalScope localScope); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/function/JassFunction.java b/jassparser/src/com/etheller/interpreter/ast/function/JassFunction.java new file mode 100644 index 0000000..b5b2ec0 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/function/JassFunction.java @@ -0,0 +1,10 @@ +package com.etheller.interpreter.ast.function; + +import java.util.List; + +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.value.JassValue; + +public interface JassFunction { + JassValue call(List arguments, GlobalScope globalScope); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/function/JassNativeManager.java b/jassparser/src/com/etheller/interpreter/ast/function/JassNativeManager.java new file mode 100644 index 0000000..59242b3 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/function/JassNativeManager.java @@ -0,0 +1,33 @@ +package com.etheller.interpreter.ast.function; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.value.JassType; + +public class JassNativeManager { + private final Map nameToNativeCode; + private final Set registeredNativeNames = new HashSet<>(); + + public JassNativeManager() { + this.nameToNativeCode = new HashMap<>(); + } + + public void registerNativeCode(final String name, final List parameters, final JassType returnType, + final GlobalScope globals) { + if (this.registeredNativeNames.contains(name)) { + throw new RuntimeException("Native already registered: " + name); + } + final JassFunction nativeCode = this.nameToNativeCode.remove(name); + globals.defineFunction(name, new NativeJassFunction(parameters, returnType, name, nativeCode)); + this.registeredNativeNames.add(name); + } + + public void checkUnregisteredNatives() { + // TODO maybe do this later + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/function/JassParameter.java b/jassparser/src/com/etheller/interpreter/ast/function/JassParameter.java new file mode 100644 index 0000000..1c59957 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/function/JassParameter.java @@ -0,0 +1,27 @@ +package com.etheller.interpreter.ast.function; + +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; +import com.etheller.interpreter.ast.value.visitor.JassTypeGettingValueVisitor; + +public class JassParameter { + private final JassType type; + private final String identifier; + + public JassParameter(final JassType type, final String identifier) { + this.type = type; + this.identifier = identifier; + } + + public String getIdentifier() { + return identifier; + } + + public JassType getType() { + return type; + } + + public boolean matchesType(final JassValue value) { + return type == value.visit(JassTypeGettingValueVisitor.getInstance()); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/function/NativeJassFunction.java b/jassparser/src/com/etheller/interpreter/ast/function/NativeJassFunction.java new file mode 100644 index 0000000..14605bf --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/function/NativeJassFunction.java @@ -0,0 +1,26 @@ +package com.etheller.interpreter.ast.function; + +import java.util.List; + +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.scope.LocalScope; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; + +public class NativeJassFunction extends AbstractJassFunction { + private final String name; + private final JassFunction implementation; + + public NativeJassFunction(final List parameters, final JassType returnType, final String name, + final JassFunction impl) { + super(parameters, returnType); + this.name = name; + implementation = impl; + } + + @Override + protected JassValue innerCall(final List arguments, final GlobalScope globalScope, + final LocalScope localScope) { + return implementation.call(arguments, globalScope); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/function/UserJassFunction.java b/jassparser/src/com/etheller/interpreter/ast/function/UserJassFunction.java new file mode 100644 index 0000000..105a6da --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/function/UserJassFunction.java @@ -0,0 +1,44 @@ +package com.etheller.interpreter.ast.function; + +import java.util.List; + +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.scope.LocalScope; +import com.etheller.interpreter.ast.statement.JassStatement; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; +import com.etheller.interpreter.ast.value.visitor.JassTypeGettingValueVisitor; + +/** + * Not a native + * + * @author Eric + * + */ +public final class UserJassFunction extends AbstractJassFunction { + private final List statements; + + public UserJassFunction(final List statements, final List parameters, + final JassType returnType) { + super(parameters, returnType); + this.statements = statements; + } + + @Override + public JassValue innerCall(final List arguments, final GlobalScope globalScope, + final LocalScope localScope) { + for (final JassStatement statement : statements) { + final JassValue returnValue = statement.execute(globalScope, localScope); + if (returnValue != null) { + if (returnValue.visit(JassTypeGettingValueVisitor.getInstance()) != returnType) { + throw new RuntimeException("Invalid return type"); + } + return returnValue; + } + } + if (JassType.NOTHING != returnType) { + throw new RuntimeException("Invalid return type"); + } + return null; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/scope/GlobalScope.java b/jassparser/src/com/etheller/interpreter/ast/scope/GlobalScope.java new file mode 100644 index 0000000..5ad1924 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/scope/GlobalScope.java @@ -0,0 +1,94 @@ +package com.etheller.interpreter.ast.scope; + +import java.util.HashMap; +import java.util.Map; + +import com.etheller.interpreter.ast.Assignable; +import com.etheller.interpreter.ast.function.JassFunction; +import com.etheller.interpreter.ast.value.ArrayJassType; +import com.etheller.interpreter.ast.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; +import com.etheller.interpreter.ast.value.PrimitiveJassType; +import com.etheller.interpreter.ast.value.visitor.ArrayPrimitiveTypeVisitor; + +public final class GlobalScope { + private final Map globals = new HashMap<>(); + private final Map functions = new HashMap<>(); + private final Map types = new HashMap<>(); + + public void createGlobalArray(final String name, final JassType type) { + final Assignable assignable = new Assignable(type); + assignable.setValue(new ArrayJassValue((ArrayJassType) type)); // TODO less bad code + globals.put(name, assignable); + } + + public void createGlobal(final String name, final JassType type) { + globals.put(name, new Assignable(type)); + } + + public void createGlobal(final String name, final JassType type, final JassValue value) { + final Assignable assignable = new Assignable(type); + assignable.setValue(value); + globals.put(name, assignable); + } + + public void setGlobal(final String name, final JassValue value) { + final Assignable assignable = globals.get(name); + if (assignable == null) { + throw new RuntimeException("Undefined global: " + name); + } + if (assignable.getType().visit(ArrayPrimitiveTypeVisitor.getInstance()) != null) { + throw new RuntimeException("Unable to assign array variable: " + name); + } + assignable.setValue(value); + } + + public JassValue getGlobal(final String name) { + final Assignable global = globals.get(name); + if (global == null) { + throw new RuntimeException("Undefined global: " + name); + } + return global.getValue(); + } + + public Assignable getAssignableGlobal(final String name) { + return globals.get(name); + } + + public void defineFunction(final String name, final JassFunction function) { + functions.put(name, function); + } + + public JassFunction getFunctionByName(final String name) { + return functions.get(name); + } + + public PrimitiveJassType parseType(final String text) { + if (text.equals("string")) { + return JassType.STRING; + } else if (text.equals("integer")) { + return JassType.INTEGER; + } else if (text.equals("boolean")) { + return JassType.BOOLEAN; + } else if (text.equals("real")) { + return JassType.REAL; + } else if (text.equals("code")) { + return JassType.CODE; + } else if (text.equals("nothing")) { + return JassType.NOTHING; + } else { + throw new RuntimeException("Unknown type: " + text); + } + } + + public JassType parseArrayType(final String primitiveTypeName) { + final String arrayTypeName = primitiveTypeName + " array"; + JassType arrayType = types.get(arrayTypeName); + if (arrayType == null) { + arrayType = new ArrayJassType(parseType(primitiveTypeName)); + types.put(arrayTypeName, arrayType); + } + return arrayType; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/scope/LocalScope.java b/jassparser/src/com/etheller/interpreter/ast/scope/LocalScope.java new file mode 100644 index 0000000..8ab2f01 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/scope/LocalScope.java @@ -0,0 +1,42 @@ +package com.etheller.interpreter.ast.scope; + +import java.util.HashMap; +import java.util.Map; + +import com.etheller.interpreter.ast.Assignable; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValue; + +public final class LocalScope { + private final Map locals = new HashMap<>(); + + public void createLocal(final String name, final JassType type) { + locals.put(name, new Assignable(type)); + } + + public void createLocal(final String name, final JassType type, final JassValue value) { + final Assignable assignable = new Assignable(type); + assignable.setValue(value); + locals.put(name, assignable); + } + + public void setLocal(final String name, final JassValue value) { + final Assignable assignable = locals.get(name); + if (assignable == null) { + throw new RuntimeException("Undefined local variable: " + name); + } + assignable.setValue(value); + } + + public JassValue getLocal(final String name) { + final Assignable local = locals.get(name); + if (local == null) { + throw new RuntimeException("Undefined local variable: " + name); + } + return local.getValue(); + } + + public Assignable getAssignableLocal(final String name) { + return locals.get(name); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/scope/TypeDefinition.java b/jassparser/src/com/etheller/interpreter/ast/scope/TypeDefinition.java new file mode 100644 index 0000000..1331a96 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/scope/TypeDefinition.java @@ -0,0 +1,11 @@ +package com.etheller.interpreter.ast.scope; + +public class TypeDefinition { + private final String name; + private final String supertype; + + public TypeDefinition(final String name, final String supertype) { + this.name = name; + this.supertype = supertype; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java new file mode 100644 index 0000000..501f5f6 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassArrayedAssignmentStatement.java @@ -0,0 +1,44 @@ +package com.etheller.interpreter.ast.statement; + +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.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.JassValue; +import com.etheller.interpreter.ast.value.visitor.ArrayJassValueVisitor; +import com.etheller.interpreter.ast.value.visitor.IntegerJassValueVisitor; + +public class JassArrayedAssignmentStatement implements JassStatement { + private final String identifier; + private final JassExpression indexExpression; + private final JassExpression expression; + + public JassArrayedAssignmentStatement(final String identifier, final JassExpression indexExpression, + final JassExpression expression) { + this.identifier = identifier; + this.indexExpression = indexExpression; + this.expression = expression; + } + + @Override + public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) { + Assignable variable = localScope.getAssignableLocal(identifier); + final JassValue index = indexExpression.evaluate(globalScope, localScope); + if (variable == null) { + variable = globalScope.getAssignableGlobal(identifier); + } + if (variable.getValue() == null) { + throw new RuntimeException("Unable to assign uninitialized array"); + } + final ArrayJassValue arrayValue = variable.getValue().visit(ArrayJassValueVisitor.getInstance()); + if (arrayValue != null) { + arrayValue.set(index.visit(IntegerJassValueVisitor.getInstance()), + expression.evaluate(globalScope, localScope)); + } else { + throw new RuntimeException("Not an array"); + } + return null; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassCallStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassCallStatement.java new file mode 100644 index 0000000..5a52634 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassCallStatement.java @@ -0,0 +1,37 @@ +package com.etheller.interpreter.ast.statement; + +import java.util.ArrayList; +import java.util.List; + +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.value.JassValue; + +public class JassCallStatement implements JassStatement { + private final String functionName; + private final List arguments; + + public JassCallStatement(final String functionName, final List arguments) { + this.functionName = functionName; + this.arguments = arguments; + } + + @Override + public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) { + final JassFunction functionByName = globalScope.getFunctionByName(functionName); + if (functionByName == null) { + throw new RuntimeException("Undefined function: " + functionName); + } + final List evaluatedExpressions = new ArrayList<>(); + for (final JassExpression expr : arguments) { + final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope); + evaluatedExpressions.add(evaluatedExpression); + } + functionByName.call(evaluatedExpressions, globalScope); + // throw away return value + return null; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassReturnStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassReturnStatement.java new file mode 100644 index 0000000..404074f --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassReturnStatement.java @@ -0,0 +1,20 @@ +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.value.JassValue; + +public class JassReturnStatement implements JassStatement { + private final JassExpression expression; + + public JassReturnStatement(final JassExpression expression) { + this.expression = expression; + } + + @Override + public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) { + return expression.evaluate(globalScope, localScope); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java new file mode 100644 index 0000000..35d182a --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassSetStatement.java @@ -0,0 +1,29 @@ +package com.etheller.interpreter.ast.statement; + +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.value.JassValue; + +public class JassSetStatement implements JassStatement { + private final String identifier; + private final JassExpression expression; + + public JassSetStatement(final String identifier, final JassExpression expression) { + this.identifier = identifier; + this.expression = expression; + } + + @Override + public JassValue execute(final GlobalScope globalScope, final LocalScope localScope) { + final Assignable local = localScope.getAssignableLocal(identifier); + if (local != null) { + local.setValue(expression.evaluate(globalScope, localScope)); + } else { + globalScope.setGlobal(identifier, expression.evaluate(globalScope, localScope)); + } + return null; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/statement/JassStatement.java b/jassparser/src/com/etheller/interpreter/ast/statement/JassStatement.java new file mode 100644 index 0000000..196f415 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/statement/JassStatement.java @@ -0,0 +1,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.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); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassType.java b/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassType.java new file mode 100644 index 0000000..0027c3a --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassType.java @@ -0,0 +1,18 @@ +package com.etheller.interpreter.ast.value; + +public class ArrayJassType implements JassType { + private final PrimitiveJassType primitiveType; + + public ArrayJassType(final PrimitiveJassType primitiveType) { + this.primitiveType = primitiveType; + } + + public PrimitiveJassType getPrimitiveType() { + return primitiveType; + } + + @Override + public TYPE visit(final JassTypeVisitor visitor) { + return visitor.accept(this); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java new file mode 100644 index 0000000..158d8d3 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/ArrayJassValue.java @@ -0,0 +1,34 @@ +package com.etheller.interpreter.ast.value; + +import com.etheller.interpreter.ast.value.visitor.JassTypeGettingValueVisitor; + +public class ArrayJassValue implements JassValue { + private final JassValue[] data = new JassValue[8192]; // that's the array size in JASS + private final ArrayJassType type; + + public ArrayJassValue(final ArrayJassType type) { + this.type = type; + } + + @Override + public TYPE visit(final JassValueVisitor visitor) { + return visitor.accept(this); + } + + public void set(final int index, final JassValue value) { + if (value.visit(JassTypeGettingValueVisitor.getInstance()) != type.getPrimitiveType()) { + throw new IllegalStateException( + "Illegal type for assignment to " + type.getPrimitiveType().getName() + " array"); + } + data[index] = value; + } + + public JassValue get(final int index) { + return data[index]; + } + + public ArrayJassType getType() { + return type; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/BooleanJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/BooleanJassValue.java new file mode 100644 index 0000000..e044000 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/BooleanJassValue.java @@ -0,0 +1,18 @@ +package com.etheller.interpreter.ast.value; + +public class BooleanJassValue implements JassValue { + private final boolean value; + + public BooleanJassValue(final boolean value) { + this.value = value; + } + + public boolean getValue() { + return value; + } + + @Override + public TYPE visit(final JassValueVisitor visitor) { + return visitor.accept(this); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/CodeJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/CodeJassValue.java new file mode 100644 index 0000000..2858184 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/CodeJassValue.java @@ -0,0 +1,21 @@ +package com.etheller.interpreter.ast.value; + +import com.etheller.interpreter.ast.function.JassFunction; + +public class CodeJassValue implements JassValue { + private final JassFunction value; + + public CodeJassValue(final JassFunction value) { + this.value = value; + } + + public JassFunction getValue() { + return value; + } + + @Override + public TYPE visit(final JassValueVisitor visitor) { + return visitor.accept(this); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/IntegerJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/IntegerJassValue.java new file mode 100644 index 0000000..bec6cfe --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/IntegerJassValue.java @@ -0,0 +1,18 @@ +package com.etheller.interpreter.ast.value; + +public class IntegerJassValue implements JassValue { + private final int value; + + public IntegerJassValue(final int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + @Override + public TYPE visit(final JassValueVisitor visitor) { + return visitor.accept(this); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/JassType.java b/jassparser/src/com/etheller/interpreter/ast/value/JassType.java new file mode 100644 index 0000000..7b1beb8 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/JassType.java @@ -0,0 +1,12 @@ +package com.etheller.interpreter.ast.value; + +public interface JassType { + TYPE visit(JassTypeVisitor visitor); + + public static final PrimitiveJassType INTEGER = new PrimitiveJassType("integer"); + public static final PrimitiveJassType STRING = new PrimitiveJassType("string"); + public static final PrimitiveJassType CODE = new PrimitiveJassType("code"); + public static final PrimitiveJassType REAL = new PrimitiveJassType("real"); + public static final PrimitiveJassType BOOLEAN = new PrimitiveJassType("boolean"); + public static final PrimitiveJassType NOTHING = new PrimitiveJassType("nothing"); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/JassTypeVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/JassTypeVisitor.java new file mode 100644 index 0000000..031e921 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/JassTypeVisitor.java @@ -0,0 +1,7 @@ +package com.etheller.interpreter.ast.value; + +public interface JassTypeVisitor { + TYPE accept(PrimitiveJassType primitiveType); + + TYPE accept(ArrayJassType arrayType); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/JassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/JassValue.java new file mode 100644 index 0000000..c1c0c43 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/JassValue.java @@ -0,0 +1,5 @@ +package com.etheller.interpreter.ast.value; + +public interface JassValue { + TYPE visit(JassValueVisitor visitor); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/JassValueVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/JassValueVisitor.java new file mode 100644 index 0000000..e02f265 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/JassValueVisitor.java @@ -0,0 +1,15 @@ +package com.etheller.interpreter.ast.value; + +public interface JassValueVisitor { + TYPE accept(IntegerJassValue value); + + TYPE accept(RealJassValue value); + + TYPE accept(BooleanJassValue value); + + TYPE accept(StringJassValue value); + + TYPE accept(CodeJassValue value); + + TYPE accept(ArrayJassValue value); +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/PrimitiveJassType.java b/jassparser/src/com/etheller/interpreter/ast/value/PrimitiveJassType.java new file mode 100644 index 0000000..21ba0b0 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/PrimitiveJassType.java @@ -0,0 +1,19 @@ +package com.etheller.interpreter.ast.value; + +public class PrimitiveJassType implements JassType { + private final String name; + + public PrimitiveJassType(final String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public TYPE visit(final JassTypeVisitor visitor) { + return visitor.accept(this); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/RealJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/RealJassValue.java new file mode 100644 index 0000000..49636dc --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/RealJassValue.java @@ -0,0 +1,18 @@ +package com.etheller.interpreter.ast.value; + +public class RealJassValue implements JassValue { + private final double value; + + public RealJassValue(final double value) { + this.value = value; + } + + public double getValue() { + return value; + } + + @Override + public TYPE visit(final JassValueVisitor visitor) { + return visitor.accept(this); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/StringJassValue.java b/jassparser/src/com/etheller/interpreter/ast/value/StringJassValue.java new file mode 100644 index 0000000..d37c98f --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/StringJassValue.java @@ -0,0 +1,18 @@ +package com.etheller.interpreter.ast.value; + +public class StringJassValue implements JassValue { + private final String value; + + public StringJassValue(final String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + @Override + public TYPE visit(final JassValueVisitor visitor) { + return visitor.accept(this); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayJassValueVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayJassValueVisitor.java new file mode 100644 index 0000000..f3d43d7 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayJassValueVisitor.java @@ -0,0 +1,48 @@ +package com.etheller.interpreter.ast.value.visitor; + +import com.etheller.interpreter.ast.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.BooleanJassValue; +import com.etheller.interpreter.ast.value.CodeJassValue; +import com.etheller.interpreter.ast.value.IntegerJassValue; +import com.etheller.interpreter.ast.value.JassValueVisitor; +import com.etheller.interpreter.ast.value.RealJassValue; +import com.etheller.interpreter.ast.value.StringJassValue; + +public class ArrayJassValueVisitor implements JassValueVisitor { + private static final ArrayJassValueVisitor INSTANCE = new ArrayJassValueVisitor(); + + public static ArrayJassValueVisitor getInstance() { + return INSTANCE; + } + + @Override + public ArrayJassValue accept(final IntegerJassValue value) { + return null; + } + + @Override + public ArrayJassValue accept(final RealJassValue value) { + return null; + } + + @Override + public ArrayJassValue accept(final BooleanJassValue value) { + return null; + } + + @Override + public ArrayJassValue accept(final StringJassValue value) { + return null; + } + + @Override + public ArrayJassValue accept(final CodeJassValue value) { + return null; + } + + @Override + public ArrayJassValue accept(final ArrayJassValue value) { + return value; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayPrimitiveTypeVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayPrimitiveTypeVisitor.java new file mode 100644 index 0000000..70b543c --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/visitor/ArrayPrimitiveTypeVisitor.java @@ -0,0 +1,24 @@ +package com.etheller.interpreter.ast.value.visitor; + +import com.etheller.interpreter.ast.value.ArrayJassType; +import com.etheller.interpreter.ast.value.JassTypeVisitor; +import com.etheller.interpreter.ast.value.PrimitiveJassType; + +public class ArrayPrimitiveTypeVisitor implements JassTypeVisitor { + private static final ArrayPrimitiveTypeVisitor INSTANCE = new ArrayPrimitiveTypeVisitor(); + + public static ArrayPrimitiveTypeVisitor getInstance() { + return INSTANCE; + } + + @Override + public PrimitiveJassType accept(final PrimitiveJassType primitiveType) { + return null; + } + + @Override + public PrimitiveJassType accept(final ArrayJassType arrayType) { + return arrayType.getPrimitiveType(); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/visitor/IntegerJassValueVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/visitor/IntegerJassValueVisitor.java new file mode 100644 index 0000000..6d99a02 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/visitor/IntegerJassValueVisitor.java @@ -0,0 +1,48 @@ +package com.etheller.interpreter.ast.value.visitor; + +import com.etheller.interpreter.ast.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.BooleanJassValue; +import com.etheller.interpreter.ast.value.CodeJassValue; +import com.etheller.interpreter.ast.value.IntegerJassValue; +import com.etheller.interpreter.ast.value.JassValueVisitor; +import com.etheller.interpreter.ast.value.RealJassValue; +import com.etheller.interpreter.ast.value.StringJassValue; + +public class IntegerJassValueVisitor implements JassValueVisitor { + private static final IntegerJassValueVisitor INSTANCE = new IntegerJassValueVisitor(); + + public static IntegerJassValueVisitor getInstance() { + return INSTANCE; + } + + @Override + public Integer accept(final IntegerJassValue value) { + return value.getValue(); + } + + @Override + public Integer accept(final RealJassValue value) { + return (int) value.getValue(); + } + + @Override + public Integer accept(final BooleanJassValue value) { + return 0; + } + + @Override + public Integer accept(final StringJassValue value) { + return 0; + } + + @Override + public Integer accept(final CodeJassValue value) { + return 0; + } + + @Override + public Integer accept(final ArrayJassValue value) { + return 0; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/visitor/JassFunctionJassValueVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/visitor/JassFunctionJassValueVisitor.java new file mode 100644 index 0000000..6d9cf1b --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/visitor/JassFunctionJassValueVisitor.java @@ -0,0 +1,49 @@ +package com.etheller.interpreter.ast.value.visitor; + +import com.etheller.interpreter.ast.function.JassFunction; +import com.etheller.interpreter.ast.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.BooleanJassValue; +import com.etheller.interpreter.ast.value.CodeJassValue; +import com.etheller.interpreter.ast.value.IntegerJassValue; +import com.etheller.interpreter.ast.value.JassValueVisitor; +import com.etheller.interpreter.ast.value.RealJassValue; +import com.etheller.interpreter.ast.value.StringJassValue; + +public class JassFunctionJassValueVisitor implements JassValueVisitor { + private static final JassFunctionJassValueVisitor INSTANCE = new JassFunctionJassValueVisitor(); + + public static JassFunctionJassValueVisitor getInstance() { + return INSTANCE; + } + + @Override + public JassFunction accept(final IntegerJassValue value) { + return null; + } + + @Override + public JassFunction accept(final RealJassValue value) { + return null; + } + + @Override + public JassFunction accept(final BooleanJassValue value) { + return null; + } + + @Override + public JassFunction accept(final StringJassValue value) { + return null; + } + + @Override + public JassFunction accept(final CodeJassValue value) { + return value.getValue(); + } + + @Override + public JassFunction accept(final ArrayJassValue value) { + return null; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/visitor/JassTypeGettingValueVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/visitor/JassTypeGettingValueVisitor.java new file mode 100644 index 0000000..2f1f1ab --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/visitor/JassTypeGettingValueVisitor.java @@ -0,0 +1,49 @@ +package com.etheller.interpreter.ast.value.visitor; + +import com.etheller.interpreter.ast.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.BooleanJassValue; +import com.etheller.interpreter.ast.value.CodeJassValue; +import com.etheller.interpreter.ast.value.IntegerJassValue; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.JassValueVisitor; +import com.etheller.interpreter.ast.value.RealJassValue; +import com.etheller.interpreter.ast.value.StringJassValue; + +public class JassTypeGettingValueVisitor implements JassValueVisitor { + public static JassTypeGettingValueVisitor INSTANCE = new JassTypeGettingValueVisitor(); + + public static JassTypeGettingValueVisitor getInstance() { + return INSTANCE; + } + + @Override + public JassType accept(final IntegerJassValue value) { + return JassType.INTEGER; + } + + @Override + public JassType accept(final RealJassValue value) { + return JassType.REAL; + } + + @Override + public JassType accept(final BooleanJassValue value) { + return JassType.BOOLEAN; + } + + @Override + public JassType accept(final StringJassValue value) { + return JassType.STRING; + } + + @Override + public JassType accept(final CodeJassValue value) { + return JassType.CODE; + } + + @Override + public JassType accept(final ArrayJassValue value) { + return value.getType(); + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/value/visitor/StringJassValueVisitor.java b/jassparser/src/com/etheller/interpreter/ast/value/visitor/StringJassValueVisitor.java new file mode 100644 index 0000000..e589d0b --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/value/visitor/StringJassValueVisitor.java @@ -0,0 +1,48 @@ +package com.etheller.interpreter.ast.value.visitor; + +import com.etheller.interpreter.ast.value.ArrayJassValue; +import com.etheller.interpreter.ast.value.BooleanJassValue; +import com.etheller.interpreter.ast.value.CodeJassValue; +import com.etheller.interpreter.ast.value.IntegerJassValue; +import com.etheller.interpreter.ast.value.JassValueVisitor; +import com.etheller.interpreter.ast.value.RealJassValue; +import com.etheller.interpreter.ast.value.StringJassValue; + +public class StringJassValueVisitor implements JassValueVisitor { + private static final StringJassValueVisitor INSTANCE = new StringJassValueVisitor(); + + public static StringJassValueVisitor getInstance() { + return INSTANCE; + } + + @Override + public String accept(final IntegerJassValue value) { + return null; + } + + @Override + public String accept(final RealJassValue value) { + return null; + } + + @Override + public String accept(final BooleanJassValue value) { + return null; + } + + @Override + public String accept(final StringJassValue value) { + return value.getValue(); + } + + @Override + public String accept(final CodeJassValue value) { + return null; + } + + @Override + public String accept(final ArrayJassValue value) { + return null; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/ArgumentExpressionHandler.java b/jassparser/src/com/etheller/interpreter/ast/visitors/ArgumentExpressionHandler.java new file mode 100644 index 0000000..8ec596e --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/ArgumentExpressionHandler.java @@ -0,0 +1,15 @@ +package com.etheller.interpreter.ast.visitors; + +public class ArgumentExpressionHandler { + protected JassArgumentsVisitor argumentsVisitor; + protected JassExpressionVisitor expressionVisitor; + + public void setJassArgumentsVisitor(final JassArgumentsVisitor jassArgumentsVisitor) { + this.argumentsVisitor = jassArgumentsVisitor; + } + + public void setJassExpressionVisitor(final JassExpressionVisitor jassExpressionVisitor) { + this.expressionVisitor = jassExpressionVisitor; + } + +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassArgumentsVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassArgumentsVisitor.java new file mode 100644 index 0000000..ce9cdd5 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassArgumentsVisitor.java @@ -0,0 +1,38 @@ +package com.etheller.interpreter.ast.visitors; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.EmptyArgumentContext; +import com.etheller.interpreter.JassParser.ListArgumentContext; +import com.etheller.interpreter.JassParser.SingleArgumentContext; +import com.etheller.interpreter.ast.expression.JassExpression; + +public class JassArgumentsVisitor extends JassBaseVisitor> { + private final ArgumentExpressionHandler argumentExpressionHandler; + + public JassArgumentsVisitor(final ArgumentExpressionHandler argumentExpressionHandler) { + this.argumentExpressionHandler = argumentExpressionHandler; + } + + @Override + public List visitSingleArgument(final SingleArgumentContext ctx) { + final List list = new LinkedList<>(); + list.add(argumentExpressionHandler.expressionVisitor.visit(ctx.expression())); + return list; + } + + @Override + public List visitListArgument(final ListArgumentContext ctx) { + final List list = visit(ctx.argsList()); + list.add(0, argumentExpressionHandler.expressionVisitor.visit(ctx.expression())); + return list; + } + + @Override + public List visitEmptyArgument(final EmptyArgumentContext ctx) { + return Collections.EMPTY_LIST; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassExpressionVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassExpressionVisitor.java new file mode 100644 index 0000000..21dcf35 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassExpressionVisitor.java @@ -0,0 +1,77 @@ +package com.etheller.interpreter.ast.visitors; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.ArrayReferenceExpressionContext; +import com.etheller.interpreter.JassParser.FalseExpressionContext; +import com.etheller.interpreter.JassParser.FunctionCallExpressionContext; +import com.etheller.interpreter.JassParser.FunctionReferenceExpressionContext; +import com.etheller.interpreter.JassParser.IntegerLiteralExpressionContext; +import com.etheller.interpreter.JassParser.ParentheticalExpressionContext; +import com.etheller.interpreter.JassParser.ReferenceExpressionContext; +import com.etheller.interpreter.JassParser.StringLiteralExpressionContext; +import com.etheller.interpreter.JassParser.TrueExpressionContext; +import com.etheller.interpreter.ast.expression.ArrayRefJassExpression; +import com.etheller.interpreter.ast.expression.FunctionCallJassExpression; +import com.etheller.interpreter.ast.expression.FunctionReferenceJassExpression; +import com.etheller.interpreter.ast.expression.JassExpression; +import com.etheller.interpreter.ast.expression.LiteralJassExpression; +import com.etheller.interpreter.ast.expression.ReferenceJassExpression; +import com.etheller.interpreter.ast.value.BooleanJassValue; +import com.etheller.interpreter.ast.value.IntegerJassValue; +import com.etheller.interpreter.ast.value.StringJassValue; + +public class JassExpressionVisitor extends JassBaseVisitor { + private final ArgumentExpressionHandler argumentExpressionHandler; + + public JassExpressionVisitor(final ArgumentExpressionHandler argumentExpressionHandler) { + this.argumentExpressionHandler = argumentExpressionHandler; + } + + @Override + public JassExpression visitReferenceExpression(final ReferenceExpressionContext ctx) { + return new ReferenceJassExpression(ctx.ID().getText()); + } + + @Override + public JassExpression visitParentheticalExpression(final ParentheticalExpressionContext ctx) { + return visit(ctx.expression()); + } + + @Override + public JassExpression visitStringLiteralExpression(final StringLiteralExpressionContext ctx) { + final String stringLiteralText = ctx.STRING_LITERAL().getText(); + return new LiteralJassExpression( + new StringJassValue(stringLiteralText.substring(1, stringLiteralText.length() - 1))); + } + + @Override + public JassExpression visitIntegerLiteralExpression(final IntegerLiteralExpressionContext ctx) { + return new LiteralJassExpression(new IntegerJassValue(Integer.parseInt(ctx.INTEGER().getText()))); + } + + @Override + public JassExpression visitFunctionReferenceExpression(final FunctionReferenceExpressionContext ctx) { + return new FunctionReferenceJassExpression(ctx.ID().getText()); + } + + @Override + public JassExpression visitArrayReferenceExpression(final ArrayReferenceExpressionContext ctx) { + return new ArrayRefJassExpression(ctx.ID().getText(), visit(ctx.expression())); + } + + @Override + public JassExpression visitFalseExpression(final FalseExpressionContext ctx) { + return new LiteralJassExpression(new BooleanJassValue(false)); + } + + @Override + public JassExpression visitTrueExpression(final TrueExpressionContext ctx) { + return new LiteralJassExpression(new BooleanJassValue(true)); + } + + @Override + public JassExpression visitFunctionCallExpression(final FunctionCallExpressionContext ctx) { + return new FunctionCallJassExpression(ctx.functionExpression().ID().getText(), + argumentExpressionHandler.argumentsVisitor.visit(ctx.functionExpression().argsList())); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassGlobalsVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassGlobalsVisitor.java new file mode 100644 index 0000000..347d954 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassGlobalsVisitor.java @@ -0,0 +1,49 @@ +package com.etheller.interpreter.ast.visitors; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.BasicGlobalContext; +import com.etheller.interpreter.JassParser.DefinitionGlobalContext; +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.scope.LocalScope; +import com.etheller.interpreter.ast.value.JassType; +import com.etheller.interpreter.ast.value.PrimitiveJassType; +import com.etheller.interpreter.ast.value.visitor.ArrayPrimitiveTypeVisitor; + +public class JassGlobalsVisitor extends JassBaseVisitor { + private static final LocalScope EMPTY_LOCAL_SCOPE = new LocalScope(); + private final GlobalScope globals; + private final JassTypeVisitor jassTypeVisitor; + private final JassExpressionVisitor jassExpressionVisitor; + + public JassGlobalsVisitor(final GlobalScope globals, final JassTypeVisitor jassTypeVisitor, + final JassExpressionVisitor jassExpressionVisitor) { + this.globals = globals; + this.jassTypeVisitor = jassTypeVisitor; + this.jassExpressionVisitor = jassExpressionVisitor; + } + + @Override + public Void visitBasicGlobal(final BasicGlobalContext ctx) { + final JassType type = jassTypeVisitor.visit(ctx.type()); + final PrimitiveJassType arrayPrimType = type.visit(ArrayPrimitiveTypeVisitor.getInstance()); + if (arrayPrimType != null) { + globals.createGlobalArray(ctx.ID().getText(), type); + } else { + globals.createGlobal(ctx.ID().getText(), type); + } + return null; + } + + @Override + public Void visitDefinitionGlobal(final DefinitionGlobalContext ctx) { + final JassType type = jassTypeVisitor.visit(ctx.type()); + final PrimitiveJassType arrayPrimType = type.visit(ArrayPrimitiveTypeVisitor.getInstance()); + if (arrayPrimType != null) { + globals.createGlobalArray(ctx.ID().getText(), type); + } else { + globals.createGlobal(ctx.ID().getText(), type, + jassExpressionVisitor.visit(ctx.assignTail().expression()).evaluate(globals, EMPTY_LOCAL_SCOPE)); + } + return null; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassParametersVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassParametersVisitor.java new file mode 100644 index 0000000..89cde26 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassParametersVisitor.java @@ -0,0 +1,38 @@ +package com.etheller.interpreter.ast.visitors; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.ListParameterContext; +import com.etheller.interpreter.JassParser.NothingParameterContext; +import com.etheller.interpreter.JassParser.SingleParameterContext; +import com.etheller.interpreter.ast.function.JassParameter; + +public class JassParametersVisitor extends JassBaseVisitor> { + private final JassTypeVisitor typeVisitor; + + public JassParametersVisitor(final JassTypeVisitor typeVisitor) { + this.typeVisitor = typeVisitor; + } + + @Override + public List visitSingleParameter(final SingleParameterContext ctx) { + final List list = new LinkedList<>(); + list.add(new JassParameter(typeVisitor.visit(ctx.param().type()), ctx.param().ID().getText())); + return list; + } + + @Override + public List visitListParameter(final ListParameterContext ctx) { + final List list = visit(ctx.paramList()); + list.add(0, new JassParameter(typeVisitor.visit(ctx.param().type()), ctx.param().ID().getText())); + return list; + } + + @Override + public List visitNothingParameter(final NothingParameterContext ctx) { + return Collections.EMPTY_LIST; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassProgramVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassProgramVisitor.java new file mode 100644 index 0000000..6162427 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassProgramVisitor.java @@ -0,0 +1,87 @@ +package com.etheller.interpreter.ast.visitors; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.BlockContext; +import com.etheller.interpreter.JassParser.FunctionBlockContext; +import com.etheller.interpreter.JassParser.GlobalContext; +import com.etheller.interpreter.JassParser.ProgramContext; +import com.etheller.interpreter.JassParser.StatementContext; +import com.etheller.interpreter.JassParser.TypeDefinitionContext; +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.statement.JassStatement; + +public class JassProgramVisitor extends JassBaseVisitor { + private final GlobalScope globals = new GlobalScope(); + private final JassNativeManager jassNativeManager = new JassNativeManager(); + private final Map typeToSuperType = new HashMap<>(); + private final JassTypeVisitor jassTypeVisitor = new JassTypeVisitor(this.globals); + private final ArgumentExpressionHandler argumentExpressionHandler = new ArgumentExpressionHandler(); + private final JassExpressionVisitor jassExpressionVisitor = new JassExpressionVisitor( + this.argumentExpressionHandler); + private final JassArgumentsVisitor jassArgumentsVisitor = new JassArgumentsVisitor(this.argumentExpressionHandler); + { + this.argumentExpressionHandler.setJassArgumentsVisitor(this.jassArgumentsVisitor); + this.argumentExpressionHandler.setJassExpressionVisitor(this.jassExpressionVisitor); + } + private final JassGlobalsVisitor jassGlobalsVisitor = new JassGlobalsVisitor(this.globals, this.jassTypeVisitor, + this.jassExpressionVisitor); + private final JassParametersVisitor jassParametersVisitor = new JassParametersVisitor(this.jassTypeVisitor); + private final JassStatementVisitor jassStatementVisitor = new JassStatementVisitor(this.argumentExpressionHandler); + + @Override + public Void visitBlock(final BlockContext ctx) { + if (ctx.globalsBlock() != null) { + for (final GlobalContext globalContext : ctx.globalsBlock().global()) { + this.jassGlobalsVisitor.visit(globalContext); + } + } + else if (ctx.nativeBlock() != null) { + this.jassNativeManager.registerNativeCode(ctx.nativeBlock().ID().getText(), + this.jassParametersVisitor.visit(ctx.nativeBlock().paramList()), + this.jassTypeVisitor.visit(ctx.nativeBlock().type()), this.globals); + } + return null; + } + + @Override + public Void visitProgram(final ProgramContext ctx) { + for (final TypeDefinitionContext typeDefinitionContext : ctx.typeDefinitionBlock().typeDefinition()) { + this.typeToSuperType.put(typeDefinitionContext.ID(0).getText(), typeDefinitionContext.ID(1).getText()); + } + for (final BlockContext blockContext : ctx.block()) { + visit(blockContext); + } + for (final FunctionBlockContext functionBlockContext : ctx.functionBlock()) { + final List statements = new ArrayList<>(); + for (final StatementContext statementContext : functionBlockContext.statement()) { + statements.add(this.jassStatementVisitor.visit(statementContext)); + } + final UserJassFunction userJassFunction = new UserJassFunction(statements, + this.jassParametersVisitor.visit(functionBlockContext.paramList()), + this.jassTypeVisitor.visit(functionBlockContext.type())); + this.globals.defineFunction(functionBlockContext.ID().getText(), userJassFunction); + } + final JassFunction mainFunction = this.globals.getFunctionByName("main"); + if (mainFunction != null) { + mainFunction.call(Collections.EMPTY_LIST, this.globals); + } + return null; + } + + public GlobalScope getGlobals() { + return this.globals; + } + + public JassNativeManager getJassNativeManager() { + return this.jassNativeManager; + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassStatementVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassStatementVisitor.java new file mode 100644 index 0000000..44cabed --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassStatementVisitor.java @@ -0,0 +1,44 @@ +package com.etheller.interpreter.ast.visitors; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.ArrayedAssignmentStatementContext; +import com.etheller.interpreter.JassParser.CallStatementContext; +import com.etheller.interpreter.JassParser.ReturnStatementContext; +import com.etheller.interpreter.JassParser.SetStatementContext; +import com.etheller.interpreter.ast.statement.JassArrayedAssignmentStatement; +import com.etheller.interpreter.ast.statement.JassCallStatement; +import com.etheller.interpreter.ast.statement.JassReturnStatement; +import com.etheller.interpreter.ast.statement.JassSetStatement; +import com.etheller.interpreter.ast.statement.JassStatement; + +public class JassStatementVisitor extends JassBaseVisitor { + private final ArgumentExpressionHandler argumentExpressionHandler; + + public JassStatementVisitor(final ArgumentExpressionHandler argumentExpressionHandler) { + this.argumentExpressionHandler = argumentExpressionHandler; + } + + @Override + public JassStatement visitCallStatement(final CallStatementContext ctx) { + return new JassCallStatement(ctx.functionExpression().ID().getText(), + argumentExpressionHandler.argumentsVisitor.visit(ctx.functionExpression().argsList())); + } + + @Override + public JassStatement visitSetStatement(final SetStatementContext ctx) { + return new JassSetStatement(ctx.ID().getText(), + argumentExpressionHandler.expressionVisitor.visit(ctx.expression())); + } + + @Override + public JassStatement visitReturnStatement(final ReturnStatementContext ctx) { + return new JassReturnStatement(argumentExpressionHandler.expressionVisitor.visit(ctx.expression())); + } + + @Override + public JassStatement visitArrayedAssignmentStatement(final ArrayedAssignmentStatementContext ctx) { + return new JassArrayedAssignmentStatement(ctx.ID().getText(), + argumentExpressionHandler.expressionVisitor.visit(ctx.expression(0)), + argumentExpressionHandler.expressionVisitor.visit(ctx.expression(1))); + } +} diff --git a/jassparser/src/com/etheller/interpreter/ast/visitors/JassTypeVisitor.java b/jassparser/src/com/etheller/interpreter/ast/visitors/JassTypeVisitor.java new file mode 100644 index 0000000..d4530b0 --- /dev/null +++ b/jassparser/src/com/etheller/interpreter/ast/visitors/JassTypeVisitor.java @@ -0,0 +1,31 @@ +package com.etheller.interpreter.ast.visitors; + +import com.etheller.interpreter.JassBaseVisitor; +import com.etheller.interpreter.JassParser.ArrayTypeContext; +import com.etheller.interpreter.JassParser.BasicTypeContext; +import com.etheller.interpreter.JassParser.NothingTypeContext; +import com.etheller.interpreter.ast.scope.GlobalScope; +import com.etheller.interpreter.ast.value.JassType; + +public class JassTypeVisitor extends JassBaseVisitor { + private final GlobalScope globals; + + public JassTypeVisitor(final GlobalScope globals) { + this.globals = globals; + } + + @Override + public JassType visitArrayType(final ArrayTypeContext ctx) { + return globals.parseArrayType(ctx.ID().getText()); + } + + @Override + public JassType visitBasicType(final BasicTypeContext ctx) { + return globals.parseType(ctx.ID().getText()); + } + + @Override + public JassType visitNothingType(final NothingTypeContext ctx) { + return JassType.NOTHING; + } +} diff --git a/settings.gradle b/settings.gradle index c398e3a..5d318b0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include 'desktop', 'core', 'fdfparser' \ No newline at end of file +include 'desktop', 'core', 'fdfparser', 'jassparser' \ No newline at end of file