Snapshot after breaking scene, work on FDF, work on JASS

This commit is contained in:
Retera 2020-05-27 23:55:48 -04:00
parent 521aa51d95
commit 813e7c9d9b
86 changed files with 3134 additions and 175 deletions

View File

@ -66,6 +66,7 @@ project(":core") {
dependencies { dependencies {
compile project(":fdfparser") compile project(":fdfparser")
compile project(":jassparser")
compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-freetype:$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 { tasks.eclipse.doLast {
delete ".project" delete ".project"
} }

View File

@ -7,6 +7,7 @@ import java.util.Arrays;
import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; 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.DataSourceDescriptor;
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
import com.etheller.warsmash.parsers.mdlx.Sequence; import com.etheller.warsmash.parsers.mdlx.Sequence;
import com.etheller.warsmash.util.DataSourceFileHandle;
import com.etheller.warsmash.viewer5.Camera; import com.etheller.warsmash.viewer5.Camera;
import com.etheller.warsmash.viewer5.CanvasProvider; import com.etheller.warsmash.viewer5.CanvasProvider;
import com.etheller.warsmash.viewer5.ModelViewer; import com.etheller.warsmash.viewer5.ModelViewer;
@ -72,7 +74,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
this.cameraManager = new CameraManager(); this.cameraManager = new CameraManager();
this.cameraManager.setupCamera(scene); 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", // this.mainModel = (MdxModel) this.viewer.load("Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdx",
new PathSolver() { new PathSolver() {
@Override @Override
@ -88,7 +90,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
// System.out.println(Arrays.toString(evt.keyFrames)); // System.out.println(Arrays.toString(evt.keyFrames));
// System.out.println(evt.name); // 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); this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0);
@ -96,14 +98,21 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
int animIndex = 0; int animIndex = 0;
for (final Sequence s : this.mainModel.getSequences()) { 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); animIndex = this.mainModel.getSequences().indexOf(s);
break;
} }
} }
this.mainInstance.setSequence(animIndex); this.mainInstance.setSequence(animIndex);
this.mainInstance.setSequenceLoopMode(0); 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); // acolytesHarvestingSceneJoke2(scene);
System.out.println("Loaded"); System.out.println("Loaded");
@ -489,7 +498,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
WarsmashGdxGame.this.cameraPositionTemp[1], WarsmashGdxGame.this.cameraPositionTemp[2]); WarsmashGdxGame.this.cameraPositionTemp[1], WarsmashGdxGame.this.cameraPositionTemp[2]);
this.target.add(WarsmashGdxGame.this.cameraTargetTemp[0], WarsmashGdxGame.this.cameraTargetTemp[1], this.target.add(WarsmashGdxGame.this.cameraTargetTemp[0], WarsmashGdxGame.this.cameraTargetTemp[1],
WarsmashGdxGame.this.cameraTargetTemp[2]); 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(), Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(),
WarsmashGdxGame.this.modelCamera.nearClippingPlane, WarsmashGdxGame.this.modelCamera.nearClippingPlane,
WarsmashGdxGame.this.modelCamera.farClippingPlane); WarsmashGdxGame.this.modelCamera.farClippingPlane);

View File

@ -14,7 +14,6 @@ import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30; 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.DataSource;
import com.etheller.warsmash.datasources.DataSourceDescriptor; import com.etheller.warsmash.datasources.DataSourceDescriptor;
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; 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.DataSourceFileHandle;
import com.etheller.warsmash.util.ImageUtils; import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.util.WarsmashConstants; import com.etheller.warsmash.util.WarsmashConstants;
@ -93,7 +97,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private boolean showTalentTree; private boolean showTalentTree;
private final List<Message> messages = new LinkedList<>(); private final List<Message> messages = new LinkedList<>();
private MdxModel timeIndicator;
/*
* (non-Javadoc)
*
* @see com.badlogic.gdx.ApplicationAdapter#create()
*/
@Override @Override
public void create() { public void create() {
@ -126,7 +136,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.viewer.worldScene.enableAudio(); this.viewer.worldScene.enableAudio();
this.viewer.enableAudio(); this.viewer.enableAudio();
try { try {
this.viewer.loadMap("ReforgedGeorgeVacation.w3x"); this.viewer.loadMap("PeasantTest.w3x");
} }
catch (final IOException e) { catch (final IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -143,6 +153,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.portraitCameraManager = new CameraManager(); this.portraitCameraManager = new CameraManager();
this.portraitCameraManager.setupCamera(this.portraitScene); 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.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx",
this.portraitScene.camera.viewport(new Rectangle(100, 0, 6400, 48)); 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 w = Gdx.graphics.getWidth();
final float h = Gdx.graphics.getHeight(); 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( final FreeTypeFontGenerator fontGenerator = new FreeTypeFontGenerator(
new DataSourceFileHandle(this.viewer.dataSource, "fonts\\FRIZQT__.TTF")); new DataSourceFileHandle(this.viewer.dataSource, "fonts\\FRIZQT__.TTF"));
final FreeTypeFontParameter fontParam = new FreeTypeFontParameter(); final FreeTypeFontParameter fontParam = new FreeTypeFontParameter();
@ -212,13 +232,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
Gdx.input.setInputProcessor(this); Gdx.input.setInputProcessor(this);
final Music music = Gdx.audio // final Music music = Gdx.audio
.newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3")); // .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3"));
music.setVolume(0.2f); // music.setVolume(0.2f);
music.setLooping(true); // music.setLooping(true);
music.play(); // 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 worldWidth = (this.viewer.terrain.columns - 1);
final float worldHeight = this.viewer.terrain.rows - 1; final float worldHeight = this.viewer.terrain.rows - 1;
final float worldSize = Math.max(worldWidth, worldHeight); final float worldSize = Math.max(worldWidth, worldHeight);
@ -234,6 +254,33 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.shapeRenderer = new ShapeRenderer(); this.shapeRenderer = new ShapeRenderer();
this.talentTreeWindow = new Rectangle(100, 300, 1400, 800); 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 @Override
@ -270,60 +317,35 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.uiViewport.apply(); this.uiViewport.apply();
this.batch.setProjectionMatrix(this.uiCamera.combined); this.batch.setProjectionMatrix(this.uiCamera.combined);
this.batch.begin(); this.batch.begin();
this.gameUI.render(this.batch);
this.font.setColor(Color.YELLOW); this.font.setColor(Color.YELLOW);
final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond(); final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond();
this.glyphLayout.setText(this.font, fpsString); this.glyphLayout.setText(this.font, fpsString);
this.font.draw(this.batch, fpsString, (this.uiViewport.getWorldWidth() - this.glyphLayout.width) / 2, 1100); 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, this.minimap.x, this.minimap.y, this.minimap.width, this.minimap.height);
this.batch.draw(this.minimapTexture, 35, 7, 305, 272);
if (this.selectedUnit != null) { 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; int messageIndex = 0;
for (final Message message : this.messages) { for (final Message message : this.messages) {
this.font20.draw(this.batch, message.text, 100, 400 + (25 * (messageIndex++))); this.font20.draw(this.batch, message.text, 100, 400 + (25 * (messageIndex++)));
} }
this.font20.setColor(Color.WHITE); 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(); final COrder currentOrder = this.selectedUnit.getSimulationUnit().getCurrentOrder();
for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) { for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) {
this.batch.draw(commandCardIcon.getTexture(), 1225 + (70 * commandCardIcon.getX()), this.batch.draw(commandCardIcon.getTexture(), 1235 + (86.8f * commandCardIcon.getX()),
160 - (70 * commandCardIcon.getY()), 64, 64); 190 - (88 * commandCardIcon.getY()), 78f, 78f);
if (((currentOrder != null) && (currentOrder.getOrderId() == commandCardIcon.getOrderId())) if (((currentOrder != null) && (currentOrder.getOrderId() == commandCardIcon.getOrderId()))
|| ((currentOrder == null) && (commandCardIcon.getOrderId() == CAbilityStop.ORDER_ID))) { || ((currentOrder == null) && (commandCardIcon.getOrderId() == CAbilityStop.ORDER_ID))) {
final int blendDstFunc = this.batch.getBlendDstFunc(); final int blendDstFunc = this.batch.getBlendDstFunc();
final int blendSrcFunc = this.batch.getBlendSrcFunc(); final int blendSrcFunc = this.batch.getBlendSrcFunc();
this.batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE); this.batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
this.batch.draw(this.activeButtonTexture, 1225 + (70 * commandCardIcon.getX()), this.batch.draw(this.activeButtonTexture, 1235 + (86.8f * commandCardIcon.getX()),
160 - (70 * commandCardIcon.getY()), 64, 64); 190 - (88 * commandCardIcon.getY()), 78f, 78f);
this.batch.setBlendFunction(blendSrcFunc, blendDstFunc); 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) { for (final RenderUnit unit : this.viewer.units) {
if (unit.playerIndex >= WarsmashConstants.MAX_PLAYERS) { 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) { public void resize(final int width, final int height) {
super.resize(width, height); super.resize(width, height);
this.tempRect.x = 0; this.tempRect.x = 0;
this.tempRect.y = 0;
this.tempRect.width = width; 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); this.cameraManager.camera.viewport(this.tempRect);
final float portraitTestWidth = (100 / 640f) * width; final float portraitTestWidth = (100 / 640f) * width;
final float portraitTestHeight = (100 / 480f) * height; final float portraitTestHeight = (100 / 480f) * height;
@ -397,15 +421,21 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.uiViewport.update(width, height); this.uiViewport.update(width, height);
this.uiCamera.position.set(this.uiCamera.viewportWidth / 2, this.uiCamera.viewportHeight / 2, 0); 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() { private void positionPortrait() {
this.projectionTemp1.x = 385; this.projectionTemp1.x = 422;
this.projectionTemp1.y = 0; this.projectionTemp1.y = 57;
this.projectionTemp2.x = 385 + 180; this.projectionTemp2.x = 422 + 167;
this.projectionTemp2.y = 177; this.projectionTemp2.y = 57 + 170;
this.uiViewport.project(this.projectionTemp1); this.uiViewport.project(this.projectionTemp1);
this.uiViewport.project(this.projectionTemp2); this.uiViewport.project(this.projectionTemp2);
@ -492,7 +522,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
WarsmashGdxMapGame.this.cameraPositionTemp[1], WarsmashGdxMapGame.this.cameraPositionTemp[2]); WarsmashGdxMapGame.this.cameraPositionTemp[1], WarsmashGdxMapGame.this.cameraPositionTemp[2]);
this.target.add(WarsmashGdxMapGame.this.cameraTargetTemp[0], this.target.add(WarsmashGdxMapGame.this.cameraTargetTemp[0],
WarsmashGdxMapGame.this.cameraTargetTemp[1], WarsmashGdxMapGame.this.cameraTargetTemp[2]); 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); this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane);
} }
@ -509,6 +539,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private Scene portraitScene; private Scene portraitScene;
private Texture minimapTexture; private Texture minimapTexture;
private Rectangle talentTreeWindow; private Rectangle talentTreeWindow;
private GameUI gameUI;
private Scene uiScene;
@Override @Override
public boolean keyDown(final int keycode) { public boolean keyDown(final int keycode) {
@ -551,6 +583,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
@Override @Override
public boolean touchDown(final int screenX, final int screenY, final int pointer, final int button) { 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); System.out.println(screenX + "," + screenY);
clickLocationTemp2.x = screenX; clickLocationTemp2.x = screenX;
@ -559,8 +592,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
if (this.selectedUnit != null) { if (this.selectedUnit != null) {
for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) { for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) {
if (new Rectangle(1225 + (70 * commandCardIcon.getX()), 160 - (70 * commandCardIcon.getY()), 64, 64) if (new Rectangle(1235 + (86.8f * commandCardIcon.getX()), 190 - (88 * commandCardIcon.getY()), 78f,
.contains(clickLocationTemp2)) { 78f).contains(clickLocationTemp2)) {
if (button == Input.Buttons.RIGHT) { if (button == Input.Buttons.RIGHT) {
this.messages.add(new Message(Gdx.input.getCurrentEventTime(), "Right mouse click")); this.messages.add(new Message(Gdx.input.getCurrentEventTime(), "Right mouse click"));
} }
@ -581,14 +614,14 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
return false; return false;
} }
if (button == Input.Buttons.RIGHT) { 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 ((rayPickUnit != null) && (rayPickUnit.playerIndex != this.selectedUnit.playerIndex)) {
if (this.viewer.orderSmart(rayPickUnit)) { if (this.viewer.orderSmart(rayPickUnit)) {
StandSequence.randomPortraitTalkSequence(this.portraitInstance); StandSequence.randomPortraitTalkSequence(this.portraitInstance);
} }
} }
else { else {
this.viewer.getClickLocation(clickLocationTemp, screenX, screenY); this.viewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY);
System.out.println(clickLocationTemp); System.out.println(clickLocationTemp);
this.viewer.showConfirmation(clickLocationTemp, 0, 1, 0); this.viewer.showConfirmation(clickLocationTemp, 0, 1, 0);
final int x = (int) ((clickLocationTemp.x - this.viewer.terrain.centerOffset[0]) / 128); final int x = (int) ((clickLocationTemp.x - this.viewer.terrain.centerOffset[0]) / 128);
@ -601,7 +634,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
} }
} }
else { else {
final List<RenderUnit> selectedUnits = this.viewer.selectUnit(screenX, screenY, false); final List<RenderUnit> selectedUnits = this.viewer.selectUnit(screenX, worldScreenY, false);
if (!selectedUnits.isEmpty()) { if (!selectedUnits.isEmpty()) {
final RenderUnit unit = selectedUnits.get(0); final RenderUnit unit = selectedUnits.get(0);
this.selectedUnit = unit; this.selectedUnit = unit;

View File

@ -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;
}
}

View File

@ -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<String, Texture> 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);
}
}

View File

@ -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.<DataSourceDescriptor>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();
}
}
}

View File

@ -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<AnchorDefinition> 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;
}
}

View File

@ -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<UIFrame> 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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) {
}
}

View File

@ -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);
}

View File

@ -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.<DataSourceDescriptor>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();
}
}
}

View File

@ -11,16 +11,16 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.etheller.warsmash.util.WorldEditStrings; import com.etheller.warsmash.util.StringBundle;
public class DataTable implements ObjectData { public class DataTable implements ObjectData {
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
Map<StringKey, Element> dataTable = new LinkedHashMap<>(); Map<StringKey, Element> dataTable = new LinkedHashMap<>();
private final WorldEditStrings worldEditStrings; private final StringBundle worldEditStrings;
public DataTable(final WorldEditStrings worldEditStrings) { public DataTable(final StringBundle worldEditStrings) {
this.worldEditStrings = worldEditStrings; this.worldEditStrings = worldEditStrings;
} }

View File

@ -219,10 +219,10 @@ public enum RenderMathUtils {
} }
public static void unpackPlanes(final Vector4[] planes, final Matrix4 m) { 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], final float a00 = m.val[Matrix4.M00], a01 = m.val[Matrix4.M01], a02 = m.val[Matrix4.M02],
a03 = m.val[Matrix4.M30], a10 = m.val[Matrix4.M01], a11 = m.val[Matrix4.M11], a12 = m.val[Matrix4.M21], a03 = m.val[Matrix4.M03], a10 = m.val[Matrix4.M10], a11 = m.val[Matrix4.M11], a12 = m.val[Matrix4.M12],
a13 = m.val[Matrix4.M31], a20 = m.val[Matrix4.M02], a21 = m.val[Matrix4.M12], a22 = m.val[Matrix4.M22], a13 = m.val[Matrix4.M13], a20 = m.val[Matrix4.M20], a21 = m.val[Matrix4.M21], 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], 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]; a33 = m.val[Matrix4.M33];
// Left clipping plane // Left clipping plane
@ -293,7 +293,7 @@ public enum RenderMathUtils {
public static Vector3 unproject(final Vector3 out, final Vector3 v, final Matrix4 inverseMatrix, public static Vector3 unproject(final Vector3 out, final Vector3 v, final Matrix4 inverseMatrix,
final Rectangle viewport) { final Rectangle viewport) {
final float x = ((2 * (v.x - viewport.x)) / viewport.width) - 1; 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; final float z = (2 * v.z) - 1;
heap.set(x, y, z, 1); heap.set(x, y, z, 1);
@ -481,6 +481,23 @@ public enum RenderMathUtils {
return wrapper; 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) { public static Buffer wrap(final short[] cornerTextures) {
final ByteBuffer wrapper = ByteBuffer.allocateDirect(cornerTextures.length).order(ByteOrder.nativeOrder()); final ByteBuffer wrapper = ByteBuffer.allocateDirect(cornerTextures.length).order(ByteOrder.nativeOrder());
for (final short face : cornerTextures) { for (final short face : cornerTextures) {

View File

@ -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;
}
};
}

View File

@ -9,7 +9,7 @@ import java.util.ResourceBundle;
import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.datasources.DataSource;
public class WorldEditStrings { public class WorldEditStrings implements StringBundle {
private ResourceBundle bundle; private ResourceBundle bundle;
private ResourceBundle bundlegs; private ResourceBundle bundlegs;
@ -30,6 +30,7 @@ public class WorldEditStrings {
} }
} }
@Override
public String getString(String string) { public String getString(String string) {
try { try {
while (string.toUpperCase().startsWith("WESTRING")) { while (string.toUpperCase().startsWith("WESTRING")) {
@ -60,6 +61,7 @@ public class WorldEditStrings {
} }
} }
@Override
public String getStringCaseSensitive(final String key) { public String getStringCaseSensitive(final String key) {
try { try {
return this.bundle.getString(key); return this.bundle.getString(key);

View File

@ -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 Bounds bounds = instance.model.bounds;
final float x = (instance.worldLocation.x + bounds.x) - this.x; final float x = (upcomingX + bounds.x) - this.x;
final float y = (instance.worldLocation.y + bounds.y) - this.y; final float y = (upcomingY + bounds.y) - this.y;
final float r = bounds.r; final float r = bounds.r;
final Vector3 s = instance.worldScale; final Vector3 s = instance.worldScale;
int left = (int) (Math.floor((x - (r * s.x)) / this.cellWidth)); int left = (int) (Math.floor((x - (r * s.x)) / this.cellWidth));

View File

@ -40,6 +40,7 @@ public class GridCell {
if (true) { if (true) {
return true; return true;
} }
this.plane = RenderMathUtils.testCell(camera.planes, this.left, this.right, this.bottom, this.top, this.plane); this.plane = RenderMathUtils.testCell(camera.planes, this.left, this.right, this.bottom, this.top, this.plane);
return this.plane == -1; return this.plane == -1;

View File

@ -89,17 +89,31 @@ public abstract class ModelInstance extends Node {
super.recalculateTransformation(); super.recalculateTransformation();
if (this.scene != null) { 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) { public boolean isVisible(final Camera camera) {
if (true) { // can't just use world location if it moves
return true; 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 Bounds bounds = this.model.bounds;
final Vector4[] planes = camera.planes; final Vector4[] planes = camera.planes;

View File

@ -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 * 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. * 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 ModelViewer viewer;
public final Camera camera; public final Camera camera;
public Grid grid;
public int visibleCells;
public int visibleInstances;
public int updatedParticles; public int updatedParticles;
public boolean audioEnabled; public boolean audioEnabled;
public AudioContext audioContext; public AudioContext audioContext;
public final List<ModelInstance> instances; public final List<ModelInstance> instances;
public final int currentInstance; public int currentInstance;
public final List<ModelInstance> batchedInstances; public final List<ModelInstance> batchedInstances;
public final int currentBatchedInstance; public int currentBatchedInstance;
public final EmittedObjectUpdater emitterObjectUpdater; public final EmittedObjectUpdater emitterObjectUpdater;
public final Map<TextureMapper, RenderBatch> batches; public final Map<TextureMapper, RenderBatch> batches;
public final Comparator<ModelInstance> instanceDepthComparator; public final Comparator<ModelInstance> instanceDepthComparator;
@ -64,10 +61,7 @@ public class Scene {
final CanvasProvider canvas = viewer.canvas; final CanvasProvider canvas = viewer.canvas;
this.viewer = viewer; this.viewer = viewer;
this.camera = new Camera(); this.camera = new Camera();
this.grid = new Grid(-100000, -100000, 200000, 200000, 200000, 200000);
this.visibleCells = 0;
this.visibleInstances = 0;
this.updatedParticles = 0; this.updatedParticles = 0;
this.audioEnabled = false; this.audioEnabled = false;
@ -117,8 +111,7 @@ public class Scene {
// Only allow instances that are actually ok to be added the scene. // Only allow instances that are actually ok to be added the scene.
if (instance.model.ok) { if (instance.model.ok) {
this.grid.moved(instance); instanceMoved(instance, instance.worldLocation.x, instance.worldLocation.y);
return true; return true;
} }
} }
@ -126,9 +119,11 @@ public class Scene {
return false; return false;
} }
public abstract void instanceMoved(ModelInstance instance, float x, float y);
public boolean removeInstance(final ModelInstance instance) { public boolean removeInstance(final ModelInstance instance) {
if (instance.scene == this) { if (instance.scene == this) {
this.grid.remove(instance); innerRemove(instance);
instance.scene = null; instance.scene = null;
this.instances.remove(instance); this.instances.remove(instance);
@ -138,17 +133,9 @@ public class Scene {
return false; return false;
} }
public void clear() { protected abstract void innerRemove(ModelInstance instance);
// 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. public abstract void clear();
this.grid.clear();
}
public boolean detach() { public boolean detach() {
if (this.viewer != null) { if (this.viewer != null) {
@ -191,54 +178,10 @@ public class Scene {
final int frame = this.viewer.frame; final int frame = this.viewer.frame;
int currentInstance = 0; final int currentInstance = 0;
int currentBatchedInstance = 0; final int currentBatchedInstance = 0;
this.visibleCells = 0; innerUpdate(dt, frame);
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;
}
}
}
}
for (int i = this.batchedInstances.size() - 1; i >= currentBatchedInstance; i--) { for (int i = this.batchedInstances.size() - 1; i >= currentBatchedInstance; i--) {
this.batchedInstances.remove(i); this.batchedInstances.remove(i);
@ -253,6 +196,8 @@ public class Scene {
this.updatedParticles = this.emitterObjectUpdater.objects.size(); this.updatedParticles = this.emitterObjectUpdater.objects.size();
} }
protected abstract void innerUpdate(float dt, int frame);
public void startFrame() { public void startFrame() {
final GL20 gl = this.viewer.gl; final GL20 gl = this.viewer.gl;
final Rectangle viewport = this.camera.rect; final Rectangle viewport = this.camera.rect;

View File

@ -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<ModelInstance> 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++;
}
}
}
}
}
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -21,9 +21,13 @@ public class Geoset {
public boolean hasAlphaAnim; public boolean hasAlphaAnim;
public boolean hasColorAnim; public boolean hasColorAnim;
public boolean hasObjectAnim; 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, 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.model = model;
this.index = index; this.index = index;
this.positionOffset = positionOffset; this.positionOffset = positionOffset;
@ -33,6 +37,9 @@ public class Geoset {
this.faceOffset = faceOffset; this.faceOffset = faceOffset;
this.vertices = vertices; this.vertices = vertices;
this.elements = elements; this.elements = elements;
this.openGLSkinType = openGLSkinType;
this.skinStride = skinStride;
this.boneCountOffsetBytes = boneCountOffsetBytes;
for (final GeosetAnimation geosetAnimation : model.getGeosetAnimations()) { for (final GeosetAnimation geosetAnimation : model.getGeosetAnimations()) {
if (geosetAnimation.geosetId == index) { 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_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset);
// shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset); // 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_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_bones", 4, this.openGLSkinType, false, this.skinStride, this.skinOffset);
shader.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 5, this.skinOffset + 4); shader.setVertexAttribute("a_boneNumber", 1, this.openGLSkinType, false, this.skinStride,
this.skinOffset + this.boneCountOffsetBytes);
} }
public void bindExtended(final ShaderProgram shader, final int coordId) { 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_position", 3, GL20.GL_FLOAT, false, 0, this.positionOffset);
shader.setVertexAttribute("a_normal", 3, GL20.GL_FLOAT, false, 0, this.normalOffset); 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_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_bones", 4, this.openGLSkinType, false, this.skinStride, this.skinOffset);
shader.setVertexAttribute("a_extendedBones", 4, GL20.GL_UNSIGNED_BYTE, false, 9, this.skinOffset + 4); shader.setVertexAttribute("a_extendedBones", 4, this.openGLSkinType, false, this.skinStride,
shader.setVertexAttribute("a_boneNumber", 1, GL20.GL_UNSIGNED_BYTE, false, 9, this.skinOffset + 8); this.skinOffset + (this.boneCountOffsetBytes / 2));
shader.setVertexAttribute("a_boneNumber", 1, this.openGLSkinType, false, this.skinStride,
this.skinOffset + this.boneCountOffsetBytes);
} }
public void render() { public void render() {

View File

@ -58,7 +58,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
@Override @Override
public ModelInstance createInstance(final int type) { public ModelInstance createInstance(final int type) {
if (type == 1) { if ((type == 1) && false) {
return new MdxSimpleInstance(this); return new MdxSimpleInstance(this);
} }
else { else {
@ -176,7 +176,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
} }
// Geosets // Geosets
SetupGeosets.setupGeosets(this, parser.getGeosets()); SetupGeosets.setupGeosets(this, parser.getGeosets(), parser.getBones().size() >= 256);
this.pivotPoints = parser.getPivotPoints(); this.pivotPoints = parser.getPivotPoints();
@ -344,4 +344,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
return this.eventObjects; return this.eventObjects;
} }
public List<Bone> getBones() {
return this.bones;
}
} }

View File

@ -12,8 +12,8 @@ public class SetupGeosets {
private static final int EXTENDED_BATCH = 1; private static final int EXTENDED_BATCH = 1;
private static final int REFORGED_BATCH = 2; private static final int REFORGED_BATCH = 2;
public static void setupGeosets(final MdxModel model, public static void setupGeosets(final MdxModel model, final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets,
final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets) { final boolean bigNodeSpace) {
if (geosets.size() > 0) { if (geosets.size() > 0) {
final GL20 gl = model.viewer.gl; final GL20 gl = model.viewer.gl;
int positionBytes = 0; int positionBytes = 0;
@ -23,6 +23,12 @@ public class SetupGeosets {
int faceBytes = 0; int faceBytes = 0;
final int[] batchTypes = new int[geosets.size()]; 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++) { for (int i = 0, l = geosets.size(); i < l; i++) {
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i); final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
@ -48,12 +54,12 @@ public class SetupGeosets {
} }
if (biggestGroup > 4) { if (biggestGroup > 4) {
skinBytes += vertices * 9; skinBytes += vertices * extendedBatchStride;
batchTypes[i] = EXTENDED_BATCH; batchTypes[i] = EXTENDED_BATCH;
} }
else { else {
skinBytes += vertices * 5; skinBytes += vertices * normalBatchStride;
batchTypes[i] = NORMAL_BATCH; batchTypes[i] = NORMAL_BATCH;
} }
@ -80,17 +86,31 @@ public class SetupGeosets {
for (int i = 0, l = geosets.size(); i < l; i++) { for (int i = 0, l = geosets.size(); i < l; i++) {
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i); final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
final int batchType = batchTypes[i];
if (true /* geoset.lod == 0 */) { if (true /* geoset.lod == 0 */) {
final float[] positions = geoset.getVertices(); final float[] positions = geoset.getVertices();
final float[] normals = geoset.getNormals(); final float[] normals = geoset.getNormals();
final float[][] uvSets = geoset.getUvSets(); final float[][] uvSets = geoset.getUvSets();
final int[] faces = geoset.getFaces(); final int[] faces = geoset.getFaces();
byte[] skin = null; int[] skin = null;
final int vertices = geoset.getVertices().length / 3; 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) { if (batchType == REFORGED_BATCH) {
// skin = geoset.skin; // skin = geoset.skin; // THIS IS NOT IMPLEMENTED
} }
else { else {
final long[] matrixIndices = geoset.getMatrixIndices(); 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 // That being said, there are a few models with geosets that need more, for
// example the Water Elemental. // example the Water Elemental.
// These geosets use a different shader, which support up to 8 bones per vertex. // 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 // Slice the matrix groups
for (final long size : geoset.getMatrixGroups()) { for (final long size : geoset.getMatrixGroups()) {
@ -130,17 +146,18 @@ public class SetupGeosets {
final int bones = Math.min(matrixGroup.length, maxBones); final int bones = Math.min(matrixGroup.length, maxBones);
for (int j = 0; j < bones; j++) { 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. // 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, 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); model.getGeosets().add(vGeoset);
@ -173,8 +190,9 @@ public class SetupGeosets {
} }
// Skin. // Skin.
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, RenderMathUtils.wrap(skin)); gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length,
skinOffset += skin.length * 1; bigNodeSpace ? RenderMathUtils.wrap(skin) : RenderMathUtils.wrapAsBytes(skin));
skinOffset += skin.length * (bigNodeSpace ? 4 : 1);
// Faces. // Faces.
gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length, gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length,

View File

@ -533,7 +533,7 @@ public class War3MapViewer extends ModelViewer {
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) { if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
path = path.substring(0, path.length() - 4); 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"; path += "_V1";
} }
@ -804,6 +804,7 @@ public class War3MapViewer extends ModelViewer {
} }
public List<RenderUnit> selectUnit(final float x, final float y, final boolean toggle) { public List<RenderUnit> selectUnit(final float x, final float y, final boolean toggle) {
System.out.println("world: " + x + "," + y);
final float[] ray = rayHeap; final float[] ray = rayHeap;
mousePosHeap.set(x, y); mousePosHeap.set(x, y);
this.worldScene.camera.screenToWorldRay(ray, mousePosHeap); this.worldScene.camera.screenToWorldRay(ray, mousePosHeap);

View File

@ -1025,10 +1025,14 @@ public class Terrain {
final int shadowSize = columns * rows; final int shadowSize = columns * rows;
final byte[] shadowData = new byte[columns * rows]; final byte[] shadowData = new byte[columns * rows];
if (this.viewer.mapMpq.has("war3map.shd")) { if (this.viewer.mapMpq.has("war3map.shd")) {
final InputStream shadowSource = this.viewer.mapMpq.getResourceAsStream("war3map.shd"); final byte[] buffer;
final byte[] buffer = IOUtils.toByteArray(shadowSource);
try (final InputStream shadowSource = this.viewer.mapMpq.getResourceAsStream("war3map.shd")) {
buffer = IOUtils.toByteArray(shadowSource);
}
for (int i = 0; i < shadowSize; i++) { for (int i = 0; i < shadowSize; i++) {
shadowData[i] = (byte) (buffer[i] / 2); shadowData[i] = (byte) ((buffer[i] & 0xFF) / 2f);
} }
} }

View File

@ -102,7 +102,7 @@ public class RenderAttackProjectile {
this.modelInstance.setLocation(this.x, this.y, this.z); this.modelInstance.setLocation(this.x, this.y, this.z);
this.modelInstance.localRotation.setFromAxisRad(0, 0, 1, this.yaw); this.modelInstance.localRotation.setFromAxisRad(0, 0, 1, this.yaw);
this.modelInstance.rotate(pitchHeap.setFromAxisRad(0, -1, 0, this.pitch)); 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; final boolean everythingDone = this.simulationProjectile.isDone() && this.modelInstance.sequenceEnded;
if (everythingDone) { if (everythingDone) {

View File

@ -176,7 +176,7 @@ public class RenderUnit {
} }
this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360; this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360;
this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing)); 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 MdxComplexInstance mdxComplexInstance = this.instance;
final COrder currentOrder = this.simulationUnit.getCurrentOrder(); final COrder currentOrder = this.simulationUnit.getCurrentOrder();
if (this.simulationUnit.getLife() <= 0) { if (this.simulationUnit.getLife() <= 0) {

1
fdfparser/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build/

View File

@ -22,4 +22,9 @@ public class AnchorDefinition {
public float getY() { public float getY() {
return this.y; return this.y;
} }
@Override
public String toString() {
return "AnchorDefinition [myPoint=" + this.myPoint + ", x=" + this.x + ", y=" + this.y + "]";
}
} }

View File

@ -8,6 +8,9 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import com.etheller.warsmash.parsers.fdf.datamodel.fields.FrameDefinitionField; 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 * 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); 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 @Override
public String toString() { public String toString() {
return "FrameDefinition [frameClass=" + this.frameClass + ", frameType=" + this.frameType + ", name=" return "FrameDefinition [frameClass=" + this.frameClass + ", frameType=" + this.frameType + ", name="
@ -65,4 +76,51 @@ public class FrameDefinition {
+ this.nameToField + ", setPoints=" + this.setPoints + ", anchors=" + this.anchors + "]"; + 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<FrameDefinition> getInnerFrames() {
return this.innerFrames;
}
public List<AnchorDefinition> getAnchors() {
return this.anchors;
}
public List<SetPointDefinition> 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;
}
} }

View File

@ -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<Float> {
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;
}
}

View File

@ -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<String> {
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;
}
}

View File

@ -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<Vector4Definition> {
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;
}
}

View File

@ -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');

49
jassparser/build.gradle Normal file
View File

@ -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")
}

View File

@ -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;
}
}

View File

@ -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: <JassFiles> [<AdditionaFile>...]");
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();
}
}
}
}

View File

@ -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");
}
}
}

View File

@ -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<JassExpression> arguments;
public FunctionCallJassExpression(final String functionName, final List<JassExpression> 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<JassValue> evaluatedExpressions = new ArrayList<>();
for (final JassExpression expr : arguments) {
final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope);
evaluatedExpressions.add(evaluatedExpression);
}
return functionByName.call(evaluatedExpressions, globalScope);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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<JassParameter> parameters;
protected final JassType returnType;
public AbstractJassFunction(final List<JassParameter> parameters, final JassType returnType) {
this.parameters = parameters;
this.returnType = returnType;
}
@Override
public final JassValue call(final List<JassValue> 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<JassValue> arguments, final GlobalScope globalScope,
final LocalScope localScope);
}

View File

@ -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<JassValue> arguments, GlobalScope globalScope);
}

View File

@ -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<String, JassFunction> nameToNativeCode;
private final Set<String> registeredNativeNames = new HashSet<>();
public JassNativeManager() {
this.nameToNativeCode = new HashMap<>();
}
public void registerNativeCode(final String name, final List<JassParameter> 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
}
}

View File

@ -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());
}
}

View File

@ -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<JassParameter> parameters, final JassType returnType, final String name,
final JassFunction impl) {
super(parameters, returnType);
this.name = name;
implementation = impl;
}
@Override
protected JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope,
final LocalScope localScope) {
return implementation.call(arguments, globalScope);
}
}

View File

@ -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<JassStatement> statements;
public UserJassFunction(final List<JassStatement> statements, final List<JassParameter> parameters,
final JassType returnType) {
super(parameters, returnType);
this.statements = statements;
}
@Override
public JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope,
final LocalScope localScope) {
for (final JassStatement statement : statements) {
final JassValue returnValue = statement.execute(globalScope, localScope);
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;
}
}

View File

@ -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<String, Assignable> globals = new HashMap<>();
private final Map<String, JassFunction> functions = new HashMap<>();
private final Map<String, JassType> 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;
}
}

View File

@ -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<String, Assignable> 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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<JassExpression> arguments;
public JassCallStatement(final String functionName, final List<JassExpression> 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<JassValue> 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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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> TYPE visit(final JassTypeVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -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> TYPE visit(final JassValueVisitor<TYPE> 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;
}
}

View File

@ -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> TYPE visit(final JassValueVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -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> TYPE visit(final JassValueVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -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> TYPE visit(final JassValueVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -0,0 +1,12 @@
package com.etheller.interpreter.ast.value;
public interface JassType {
<TYPE> TYPE visit(JassTypeVisitor<TYPE> 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");
}

View File

@ -0,0 +1,7 @@
package com.etheller.interpreter.ast.value;
public interface JassTypeVisitor<TYPE> {
TYPE accept(PrimitiveJassType primitiveType);
TYPE accept(ArrayJassType arrayType);
}

View File

@ -0,0 +1,5 @@
package com.etheller.interpreter.ast.value;
public interface JassValue {
<TYPE> TYPE visit(JassValueVisitor<TYPE> visitor);
}

View File

@ -0,0 +1,15 @@
package com.etheller.interpreter.ast.value;
public interface JassValueVisitor<TYPE> {
TYPE accept(IntegerJassValue value);
TYPE accept(RealJassValue value);
TYPE accept(BooleanJassValue value);
TYPE accept(StringJassValue value);
TYPE accept(CodeJassValue value);
TYPE accept(ArrayJassValue value);
}

View File

@ -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> TYPE visit(final JassTypeVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -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> TYPE visit(final JassValueVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -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> TYPE visit(final JassValueVisitor<TYPE> visitor) {
return visitor.accept(this);
}
}

View File

@ -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<ArrayJassValue> {
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;
}
}

View File

@ -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<PrimitiveJassType> {
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();
}
}

View File

@ -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<Integer> {
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;
}
}

View File

@ -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<JassFunction> {
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;
}
}

View File

@ -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<JassType> {
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();
}
}

View File

@ -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<String> {
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;
}
}

View File

@ -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;
}
}

View File

@ -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<List<JassExpression>> {
private final ArgumentExpressionHandler argumentExpressionHandler;
public JassArgumentsVisitor(final ArgumentExpressionHandler argumentExpressionHandler) {
this.argumentExpressionHandler = argumentExpressionHandler;
}
@Override
public List<JassExpression> visitSingleArgument(final SingleArgumentContext ctx) {
final List<JassExpression> list = new LinkedList<>();
list.add(argumentExpressionHandler.expressionVisitor.visit(ctx.expression()));
return list;
}
@Override
public List<JassExpression> visitListArgument(final ListArgumentContext ctx) {
final List<JassExpression> list = visit(ctx.argsList());
list.add(0, argumentExpressionHandler.expressionVisitor.visit(ctx.expression()));
return list;
}
@Override
public List<JassExpression> visitEmptyArgument(final EmptyArgumentContext ctx) {
return Collections.EMPTY_LIST;
}
}

View File

@ -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<JassExpression> {
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()));
}
}

View File

@ -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<Void> {
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;
}
}

View File

@ -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<List<JassParameter>> {
private final JassTypeVisitor typeVisitor;
public JassParametersVisitor(final JassTypeVisitor typeVisitor) {
this.typeVisitor = typeVisitor;
}
@Override
public List<JassParameter> visitSingleParameter(final SingleParameterContext ctx) {
final List<JassParameter> list = new LinkedList<>();
list.add(new JassParameter(typeVisitor.visit(ctx.param().type()), ctx.param().ID().getText()));
return list;
}
@Override
public List<JassParameter> visitListParameter(final ListParameterContext ctx) {
final List<JassParameter> list = visit(ctx.paramList());
list.add(0, new JassParameter(typeVisitor.visit(ctx.param().type()), ctx.param().ID().getText()));
return list;
}
@Override
public List<JassParameter> visitNothingParameter(final NothingParameterContext ctx) {
return Collections.EMPTY_LIST;
}
}

View File

@ -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<Void> {
private final GlobalScope globals = new GlobalScope();
private final JassNativeManager jassNativeManager = new JassNativeManager();
private final Map<String, String> 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<JassStatement> 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;
}
}

View File

@ -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<JassStatement> {
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)));
}
}

View File

@ -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<JassType> {
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;
}
}

View File

@ -1 +1 @@
include 'desktop', 'core', 'fdfparser' include 'desktop', 'core', 'fdfparser', 'jassparser'