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 {
compile project(":fdfparser")
compile project(":jassparser")
compile "com.badlogicgames.gdx:gdx:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
@ -85,6 +86,15 @@ project(":fdfparser") {
}
}
project(":jassparser") {
apply plugin: "antlr"
dependencies {
antlr "org.antlr:antlr4:$antlrVersion" // use antlr version 4
}
}
tasks.eclipse.doLast {
delete ".project"
}

View File

@ -7,6 +7,7 @@ import java.util.Arrays;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
@ -18,6 +19,7 @@ import com.etheller.warsmash.datasources.DataSource;
import com.etheller.warsmash.datasources.DataSourceDescriptor;
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
import com.etheller.warsmash.parsers.mdlx.Sequence;
import com.etheller.warsmash.util.DataSourceFileHandle;
import com.etheller.warsmash.viewer5.Camera;
import com.etheller.warsmash.viewer5.CanvasProvider;
import com.etheller.warsmash.viewer5.ModelViewer;
@ -72,7 +74,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
this.cameraManager = new CameraManager();
this.cameraManager.setupCamera(scene);
this.mainModel = (MdxModel) this.viewer.load("Buildings\\Other\\TempArtB\\TempArtB.mdx",
this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3d\\MainMenu3d.mdx",
// this.mainModel = (MdxModel) this.viewer.load("Abilities\\Spells\\Orc\\FeralSpirit\\feralspirittarget.mdx",
new PathSolver() {
@Override
@ -88,7 +90,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
// System.out.println(Arrays.toString(evt.keyFrames));
// System.out.println(evt.name);
// this.modelCamera = this.mainModel.cameras.get(0);
this.modelCamera = this.mainModel.cameras.get(0);
this.mainInstance = (MdxComplexInstance) this.mainModel.addInstance(0);
@ -96,14 +98,21 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
int animIndex = 0;
for (final Sequence s : this.mainModel.getSequences()) {
if (s.getName().toLowerCase().startsWith("walk")) {
if (s.getName().toLowerCase().startsWith("stand")) {
animIndex = this.mainModel.getSequences().indexOf(s);
break;
}
}
this.mainInstance.setSequence(animIndex);
this.mainInstance.setSequenceLoopMode(0);
final Music music = Gdx.audio
.newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\MainScreen.mp3"));
music.setVolume(0.2f);
music.setLooping(true);
music.play();
// acolytesHarvestingSceneJoke2(scene);
System.out.println("Loaded");
@ -489,7 +498,7 @@ public class WarsmashGdxGame extends ApplicationAdapter implements CanvasProvide
WarsmashGdxGame.this.cameraPositionTemp[1], WarsmashGdxGame.this.cameraPositionTemp[2]);
this.target.add(WarsmashGdxGame.this.cameraTargetTemp[0], WarsmashGdxGame.this.cameraTargetTemp[1],
WarsmashGdxGame.this.cameraTargetTemp[2]);
this.camera.perspective(WarsmashGdxGame.this.modelCamera.fieldOfView,
this.camera.perspective(WarsmashGdxGame.this.modelCamera.fieldOfView * 0.75f,
Gdx.graphics.getWidth() / (float) Gdx.graphics.getHeight(),
WarsmashGdxGame.this.modelCamera.nearClippingPlane,
WarsmashGdxGame.this.modelCamera.farClippingPlane);

View File

@ -14,7 +14,6 @@ import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30;
@ -37,6 +36,11 @@ import com.etheller.warsmash.datasources.CompoundDataSourceDescriptor;
import com.etheller.warsmash.datasources.DataSource;
import com.etheller.warsmash.datasources.DataSourceDescriptor;
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.fdf.datamodel.AnchorDefinition;
import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.units.Element;
import com.etheller.warsmash.util.DataSourceFileHandle;
import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.util.WarsmashConstants;
@ -93,7 +97,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private boolean showTalentTree;
private final List<Message> messages = new LinkedList<>();
private MdxModel timeIndicator;
/*
* (non-Javadoc)
*
* @see com.badlogic.gdx.ApplicationAdapter#create()
*/
@Override
public void create() {
@ -126,7 +136,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.viewer.worldScene.enableAudio();
this.viewer.enableAudio();
try {
this.viewer.loadMap("ReforgedGeorgeVacation.w3x");
this.viewer.loadMap("PeasantTest.w3x");
}
catch (final IOException e) {
throw new RuntimeException(e);
@ -143,6 +153,9 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.portraitCameraManager = new CameraManager();
this.portraitCameraManager.setupCamera(this.portraitScene);
this.uiScene = this.viewer.addScene();
this.uiScene.alpha = true;
// this.mainModel = (MdxModel) this.viewer.load("UI\\Glues\\MainMenu\\MainMenu3D_exp\\MainMenu3D_exp.mdx",
this.portraitScene.camera.viewport(new Rectangle(100, 0, 6400, 48));
@ -151,6 +164,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
final float w = Gdx.graphics.getWidth();
final float h = Gdx.graphics.getHeight();
this.tempRect.x = 0;
this.tempRect.y = 0;
this.tempRect.width = w;
this.tempRect.height = h;
this.uiScene.camera.viewport(this.tempRect);
this.uiScene.camera.ortho(0, 0.8f, 0, 0.6f, 0, 1);
final FreeTypeFontGenerator fontGenerator = new FreeTypeFontGenerator(
new DataSourceFileHandle(this.viewer.dataSource, "fonts\\FRIZQT__.TTF"));
final FreeTypeFontParameter fontParam = new FreeTypeFontParameter();
@ -212,13 +232,13 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
Gdx.input.setInputProcessor(this);
final Music music = Gdx.audio
.newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3"));
music.setVolume(0.2f);
music.setLooping(true);
music.play();
// final Music music = Gdx.audio
// .newMusic(new DataSourceFileHandle(this.viewer.dataSource, "Sound\\Music\\mp3Music\\DarkAgents.mp3"));
// music.setVolume(0.2f);
// music.setLooping(true);
// music.play();
this.minimap = new Rectangle(35, 7, 305, 272);
this.minimap = new Rectangle(18.75f, 13.75f, 278.75f, 276.25f);
final float worldWidth = (this.viewer.terrain.columns - 1);
final float worldHeight = this.viewer.terrain.rows - 1;
final float worldSize = Math.max(worldWidth, worldHeight);
@ -234,6 +254,33 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.shapeRenderer = new ShapeRenderer();
this.talentTreeWindow = new Rectangle(100, 300, 1400, 800);
final Element skin = GameUI.loadSkin(this.viewer.dataSource, "Human");
this.gameUI = new GameUI(this.viewer.dataSource, skin, this.uiViewport);
String timeIndicatorPath = skin.getField("TimeOfDayIndicator");
if (!this.viewer.dataSource.has(timeIndicatorPath)) {
final int lastDotIndex = timeIndicatorPath.lastIndexOf('.');
if (lastDotIndex >= 0) {
timeIndicatorPath = timeIndicatorPath.substring(0, lastDotIndex);
}
timeIndicatorPath += ".mdx";
}
this.timeIndicator = (MdxModel) this.viewer.load(timeIndicatorPath, this.viewer.mapPathSolver,
this.viewer.solverParams);
final MdxComplexInstance timeIndicatorInstance = (MdxComplexInstance) this.timeIndicator.addInstance();
timeIndicatorInstance.setScene(this.uiScene);
timeIndicatorInstance.setSequence(0);
timeIndicatorInstance.setSequenceLoopMode(2);
try {
this.gameUI.loadTOCFile("UI\\FrameDef\\FrameDef.toc");
}
catch (final IOException e) {
throw new RuntimeException(e);
}
this.gameUI.createSimpleFrame("ConsoleUI", this.gameUI, 0);
final UIFrame resourceBarFrame = this.gameUI.createSimpleFrame("ResourceBarFrame", this.gameUI, 0);
resourceBarFrame.addAnchor(new AnchorDefinition(FramePoint.TOPRIGHT, 0, 0));
this.gameUI.positionBounds(this.uiViewport);
}
@Override
@ -270,60 +317,35 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.uiViewport.apply();
this.batch.setProjectionMatrix(this.uiCamera.combined);
this.batch.begin();
this.gameUI.render(this.batch);
this.font.setColor(Color.YELLOW);
final String fpsString = "FPS: " + Gdx.graphics.getFramesPerSecond();
this.glyphLayout.setText(this.font, fpsString);
this.font.draw(this.batch, fpsString, (this.uiViewport.getWorldWidth() - this.glyphLayout.width) / 2, 1100);
// this.batch.draw(this.consoleUITexture, 0, 0, this.uiViewport.getWorldWidth(), 320);
this.batch.draw(this.minimapTexture, 35, 7, 305, 272);
this.batch.draw(this.minimapTexture, this.minimap.x, this.minimap.y, this.minimap.width, this.minimap.height);
if (this.selectedUnit != null) {
this.font24.setColor(Color.WHITE);
final String name = this.viewer.simulation.getUnitData()
.getName(this.selectedUnit.getSimulationUnit().getTypeId());
this.glyphLayout.setText(this.font24, name);
this.font24.draw(this.batch, name, ((this.uiViewport.getWorldWidth() - this.glyphLayout.width) / 2) + 100,
200);
this.font20.setColor(Color.YELLOW);
this.font20.draw(this.batch, "Attack:", 600, 120);
this.font20.draw(this.batch, "Defense:", 600, 98);
this.font20.draw(this.batch, "Speed:", 600, 76);
this.font20.setColor(Color.WHITE);
int messageIndex = 0;
for (final Message message : this.messages) {
this.font20.draw(this.batch, message.text, 100, 400 + (25 * (messageIndex++)));
}
this.font20.setColor(Color.WHITE);
final int dmgMin = this.viewer.simulation.getUnitData()
.getA1MinDamage(this.selectedUnit.getSimulationUnit().getTypeId());
final int dmgMax = this.viewer.simulation.getUnitData()
.getA1MaxDamage(this.selectedUnit.getSimulationUnit().getTypeId());
final int def = this.viewer.simulation.getUnitData()
.getDefense(this.selectedUnit.getSimulationUnit().getTypeId());
this.font20.draw(this.batch, Integer.toString(dmgMin) + " - " + Integer.toString(dmgMax), 700, 120);
this.font20.draw(this.batch, Integer.toString(def), 700, 98);
this.font20.draw(this.batch, Integer.toString(this.selectedUnit.getSimulationUnit().getSpeed()), 700, 76);
final COrder currentOrder = this.selectedUnit.getSimulationUnit().getCurrentOrder();
for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) {
this.batch.draw(commandCardIcon.getTexture(), 1225 + (70 * commandCardIcon.getX()),
160 - (70 * commandCardIcon.getY()), 64, 64);
this.batch.draw(commandCardIcon.getTexture(), 1235 + (86.8f * commandCardIcon.getX()),
190 - (88 * commandCardIcon.getY()), 78f, 78f);
if (((currentOrder != null) && (currentOrder.getOrderId() == commandCardIcon.getOrderId()))
|| ((currentOrder == null) && (commandCardIcon.getOrderId() == CAbilityStop.ORDER_ID))) {
final int blendDstFunc = this.batch.getBlendDstFunc();
final int blendSrcFunc = this.batch.getBlendSrcFunc();
this.batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE);
this.batch.draw(this.activeButtonTexture, 1225 + (70 * commandCardIcon.getX()),
160 - (70 * commandCardIcon.getY()), 64, 64);
this.batch.draw(this.activeButtonTexture, 1235 + (86.8f * commandCardIcon.getX()),
190 - (88 * commandCardIcon.getY()), 78f, 78f);
this.batch.setBlendFunction(blendSrcFunc, blendDstFunc);
}
}
this.batch.draw(this.solidGreenTexture, 413, 34, 122 * (this.selectedUnit.getSimulationUnit().getLife()
/ this.selectedUnit.getSimulationUnit().getMaximumLife()), 7);
}
for (final RenderUnit unit : this.viewer.units) {
if (unit.playerIndex >= WarsmashConstants.MAX_PLAYERS) {
@ -387,9 +409,11 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
public void resize(final int width, final int height) {
super.resize(width, height);
this.tempRect.x = 0;
this.tempRect.y = 0;
this.tempRect.width = width;
this.tempRect.height = height;
final float topHeight = 0.02666f * height;
final float bottomHeight = 0.21333f * height;
this.tempRect.y = (int) bottomHeight;
this.tempRect.height = height - (int) (topHeight + bottomHeight);
this.cameraManager.camera.viewport(this.tempRect);
final float portraitTestWidth = (100 / 640f) * width;
final float portraitTestHeight = (100 / 480f) * height;
@ -397,15 +421,21 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
this.uiViewport.update(width, height);
this.uiCamera.position.set(this.uiCamera.viewportWidth / 2, this.uiCamera.viewportHeight / 2, 0);
positionPortrait();
this.tempRect.x = this.uiViewport.getScreenX();
this.tempRect.y = this.uiViewport.getScreenY();
this.tempRect.width = this.uiViewport.getScreenWidth();
this.tempRect.height = this.uiViewport.getScreenHeight();
this.uiScene.camera.viewport(this.tempRect);
this.uiScene.camera.ortho(0f, 0.8f, 0f, 0.6f, -1f, 1);
positionPortrait();
}
private void positionPortrait() {
this.projectionTemp1.x = 385;
this.projectionTemp1.y = 0;
this.projectionTemp2.x = 385 + 180;
this.projectionTemp2.y = 177;
this.projectionTemp1.x = 422;
this.projectionTemp1.y = 57;
this.projectionTemp2.x = 422 + 167;
this.projectionTemp2.y = 57 + 170;
this.uiViewport.project(this.projectionTemp1);
this.uiViewport.project(this.projectionTemp2);
@ -492,7 +522,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
WarsmashGdxMapGame.this.cameraPositionTemp[1], WarsmashGdxMapGame.this.cameraPositionTemp[2]);
this.target.add(WarsmashGdxMapGame.this.cameraTargetTemp[0],
WarsmashGdxMapGame.this.cameraTargetTemp[1], WarsmashGdxMapGame.this.cameraTargetTemp[2]);
this.camera.perspective(this.modelCamera.fieldOfView, this.camera.getAspect(),
this.camera.perspective(this.modelCamera.fieldOfView * 0.75f, this.camera.getAspect(),
this.modelCamera.nearClippingPlane, this.modelCamera.farClippingPlane);
}
@ -509,6 +539,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
private Scene portraitScene;
private Texture minimapTexture;
private Rectangle talentTreeWindow;
private GameUI gameUI;
private Scene uiScene;
@Override
public boolean keyDown(final int keycode) {
@ -551,6 +583,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
@Override
public boolean touchDown(final int screenX, final int screenY, final int pointer, final int button) {
final float worldScreenY = getHeight() - screenY;
System.out.println(screenX + "," + screenY);
clickLocationTemp2.x = screenX;
@ -559,8 +592,8 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
if (this.selectedUnit != null) {
for (final CommandCardIcon commandCardIcon : this.selectedUnit.getCommandCardIcons()) {
if (new Rectangle(1225 + (70 * commandCardIcon.getX()), 160 - (70 * commandCardIcon.getY()), 64, 64)
.contains(clickLocationTemp2)) {
if (new Rectangle(1235 + (86.8f * commandCardIcon.getX()), 190 - (88 * commandCardIcon.getY()), 78f,
78f).contains(clickLocationTemp2)) {
if (button == Input.Buttons.RIGHT) {
this.messages.add(new Message(Gdx.input.getCurrentEventTime(), "Right mouse click"));
}
@ -581,14 +614,14 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
return false;
}
if (button == Input.Buttons.RIGHT) {
final RenderUnit rayPickUnit = this.viewer.rayPickUnit(screenX, screenY);
final RenderUnit rayPickUnit = this.viewer.rayPickUnit(screenX, worldScreenY);
if ((rayPickUnit != null) && (rayPickUnit.playerIndex != this.selectedUnit.playerIndex)) {
if (this.viewer.orderSmart(rayPickUnit)) {
StandSequence.randomPortraitTalkSequence(this.portraitInstance);
}
}
else {
this.viewer.getClickLocation(clickLocationTemp, screenX, screenY);
this.viewer.getClickLocation(clickLocationTemp, screenX, (int) worldScreenY);
System.out.println(clickLocationTemp);
this.viewer.showConfirmation(clickLocationTemp, 0, 1, 0);
final int x = (int) ((clickLocationTemp.x - this.viewer.terrain.centerOffset[0]) / 128);
@ -601,7 +634,7 @@ public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProv
}
}
else {
final List<RenderUnit> selectedUnits = this.viewer.selectUnit(screenX, screenY, false);
final List<RenderUnit> selectedUnits = this.viewer.selectUnit(screenX, worldScreenY, false);
if (!selectedUnits.isEmpty()) {
final RenderUnit unit = selectedUnits.get(0);
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.Set;
import com.etheller.warsmash.util.WorldEditStrings;
import com.etheller.warsmash.util.StringBundle;
public class DataTable implements ObjectData {
private static final boolean DEBUG = false;
Map<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;
}

View File

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

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;
public class WorldEditStrings {
public class WorldEditStrings implements StringBundle {
private ResourceBundle bundle;
private ResourceBundle bundlegs;
@ -30,6 +30,7 @@ public class WorldEditStrings {
}
}
@Override
public String getString(String string) {
try {
while (string.toUpperCase().startsWith("WESTRING")) {
@ -60,6 +61,7 @@ public class WorldEditStrings {
}
}
@Override
public String getStringCaseSensitive(final String key) {
try {
return this.bundle.getString(key);

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

View File

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

View File

@ -89,17 +89,31 @@ public abstract class ModelInstance extends Node {
super.recalculateTransformation();
if (this.scene != null) {
this.scene.grid.moved(this);
this.scene.grid.moved(this, this.worldLocation.x, this.worldLocation.y);
}
}
public boolean isVisible(final Camera camera) {
if (true) {
return true;
// can't just use world location if it moves
float x, y, z;
if (this.dirty) {
// TODO this is an incorrect, predicted location for dirty case
if ((this.parent != null) && !this.dontInheritTranslation) {
x = this.parent.localLocation.x + this.localLocation.x;
y = this.parent.localLocation.y + this.localLocation.y;
z = this.parent.localLocation.z + this.localLocation.z;
}
else {
x = this.localLocation.x;
y = this.localLocation.y;
z = this.localLocation.z;
}
}
else {
x = this.worldLocation.x;
y = this.worldLocation.y;
z = this.worldLocation.z;
}
final float x = this.worldLocation.x;
final float y = this.worldLocation.y;
final float z = this.worldLocation.z;
final Bounds bounds = this.model.bounds;
final Vector4[] planes = camera.planes;

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
* over those behaviors other than a boolean on/off toggle for audio.
*/
public class Scene {
public abstract class Scene {
public final ModelViewer viewer;
public final Camera camera;
public Grid grid;
public int visibleCells;
public int visibleInstances;
public int updatedParticles;
public boolean audioEnabled;
public AudioContext audioContext;
public final List<ModelInstance> instances;
public final int currentInstance;
public int currentInstance;
public final List<ModelInstance> batchedInstances;
public final int currentBatchedInstance;
public int currentBatchedInstance;
public final EmittedObjectUpdater emitterObjectUpdater;
public final Map<TextureMapper, RenderBatch> batches;
public final Comparator<ModelInstance> instanceDepthComparator;
@ -64,10 +61,7 @@ public class Scene {
final CanvasProvider canvas = viewer.canvas;
this.viewer = viewer;
this.camera = new Camera();
this.grid = new Grid(-100000, -100000, 200000, 200000, 200000, 200000);
this.visibleCells = 0;
this.visibleInstances = 0;
this.updatedParticles = 0;
this.audioEnabled = false;
@ -117,8 +111,7 @@ public class Scene {
// Only allow instances that are actually ok to be added the scene.
if (instance.model.ok) {
this.grid.moved(instance);
instanceMoved(instance, instance.worldLocation.x, instance.worldLocation.y);
return true;
}
}
@ -126,9 +119,11 @@ public class Scene {
return false;
}
public abstract void instanceMoved(ModelInstance instance, float x, float y);
public boolean removeInstance(final ModelInstance instance) {
if (instance.scene == this) {
this.grid.remove(instance);
innerRemove(instance);
instance.scene = null;
this.instances.remove(instance);
@ -138,17 +133,9 @@ public class Scene {
return false;
}
public void clear() {
// First remove references to this scene stored in the instances.
for (final GridCell cell : this.grid.cells) {
for (final ModelInstance instance : cell.instances) {
instance.scene = null;
}
}
protected abstract void innerRemove(ModelInstance instance);
// Then remove references to the instances.
this.grid.clear();
}
public abstract void clear();
public boolean detach() {
if (this.viewer != null) {
@ -191,54 +178,10 @@ public class Scene {
final int frame = this.viewer.frame;
int currentInstance = 0;
int currentBatchedInstance = 0;
final int currentInstance = 0;
final int currentBatchedInstance = 0;
this.visibleCells = 0;
this.visibleInstances = 0;
// Update and collect all of the visible instances.
for (final GridCell cell : this.grid.cells) {
if (cell.isVisible(this.camera)) {
this.visibleCells += 1;
for (final ModelInstance instance : new ArrayList<>(cell.instances)) {
// final ModelInstance instance = cell.instances.get(i);
if (instance.rendered && (instance.cullFrame < frame) && instance.isVisible(this.camera)) {
instance.cullFrame = frame;
if (instance.updateFrame < frame) {
instance.update(dt, this);
if (!instance.rendered) {
// it became hidden while it updated
continue;
}
}
if (instance.isBatched()) {
if (currentBatchedInstance < this.batchedInstances.size()) {
this.batchedInstances.set(currentBatchedInstance++, instance);
}
else {
this.batchedInstances.add(instance);
currentBatchedInstance++;
}
}
else {
if (currentInstance < this.instances.size()) {
this.instances.set(currentInstance++, instance);
}
else {
this.instances.add(instance);
currentInstance++;
}
}
this.visibleInstances += 1;
}
}
}
}
innerUpdate(dt, frame);
for (int i = this.batchedInstances.size() - 1; i >= currentBatchedInstance; i--) {
this.batchedInstances.remove(i);
@ -253,6 +196,8 @@ public class Scene {
this.updatedParticles = this.emitterObjectUpdater.objects.size();
}
protected abstract void innerUpdate(float dt, int frame);
public void startFrame() {
final GL20 gl = this.viewer.gl;
final Rectangle viewport = this.camera.rect;

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

View File

@ -58,7 +58,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
@Override
public ModelInstance createInstance(final int type) {
if (type == 1) {
if ((type == 1) && false) {
return new MdxSimpleInstance(this);
}
else {
@ -176,7 +176,7 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
}
// Geosets
SetupGeosets.setupGeosets(this, parser.getGeosets());
SetupGeosets.setupGeosets(this, parser.getGeosets(), parser.getBones().size() >= 256);
this.pivotPoints = parser.getPivotPoints();
@ -344,4 +344,8 @@ public class MdxModel extends com.etheller.warsmash.viewer5.Model<MdxHandler> {
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 REFORGED_BATCH = 2;
public static void setupGeosets(final MdxModel model,
final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets) {
public static void setupGeosets(final MdxModel model, final List<com.etheller.warsmash.parsers.mdlx.Geoset> geosets,
final boolean bigNodeSpace) {
if (geosets.size() > 0) {
final GL20 gl = model.viewer.gl;
int positionBytes = 0;
@ -23,6 +23,12 @@ public class SetupGeosets {
int faceBytes = 0;
final int[] batchTypes = new int[geosets.size()];
final int extendedBatchStride = bigNodeSpace ? 36 : 9;
final int normalBatchStride = bigNodeSpace ? 20 : 5;
final int openGLSkinType = bigNodeSpace ? GL20.GL_UNSIGNED_INT : GL20.GL_UNSIGNED_BYTE;
final int normalBatchBoneCountOffsetBytes = bigNodeSpace ? 16 : 4;
final int extendedBatchBoneCountOffsetBytes = bigNodeSpace ? 32 : 8;
for (int i = 0, l = geosets.size(); i < l; i++) {
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
@ -48,12 +54,12 @@ public class SetupGeosets {
}
if (biggestGroup > 4) {
skinBytes += vertices * 9;
skinBytes += vertices * extendedBatchStride;
batchTypes[i] = EXTENDED_BATCH;
}
else {
skinBytes += vertices * 5;
skinBytes += vertices * normalBatchStride;
batchTypes[i] = NORMAL_BATCH;
}
@ -80,17 +86,31 @@ public class SetupGeosets {
for (int i = 0, l = geosets.size(); i < l; i++) {
final com.etheller.warsmash.parsers.mdlx.Geoset geoset = geosets.get(i);
final int batchType = batchTypes[i];
if (true /* geoset.lod == 0 */) {
final float[] positions = geoset.getVertices();
final float[] normals = geoset.getNormals();
final float[][] uvSets = geoset.getUvSets();
final int[] faces = geoset.getFaces();
byte[] skin = null;
int[] skin = null;
final int vertices = geoset.getVertices().length / 3;
final int batchType = batchTypes[i];
int maxBones;
int skinStride;
int boneCountOffsetBytes;
if (batchType == EXTENDED_BATCH) {
maxBones = 8;
skinStride = extendedBatchStride;
boneCountOffsetBytes = extendedBatchBoneCountOffsetBytes;
}
else {
maxBones = 4;
skinStride = normalBatchStride;
boneCountOffsetBytes = normalBatchBoneCountOffsetBytes;
}
if (batchType == REFORGED_BATCH) {
// skin = geoset.skin;
// skin = geoset.skin; // THIS IS NOT IMPLEMENTED
}
else {
final long[] matrixIndices = geoset.getMatrixIndices();
@ -102,12 +122,8 @@ public class SetupGeosets {
// That being said, there are a few models with geosets that need more, for
// example the Water Elemental.
// These geosets use a different shader, which support up to 8 bones per vertex.
int maxBones = 4;
if (batchType == EXTENDED_BATCH) {
maxBones = 8;
}
skin = new byte[vertices * (maxBones + 1)];
skin = new int[vertices * (maxBones + 1)];
// Slice the matrix groups
for (final long size : geoset.getMatrixGroups()) {
@ -130,17 +146,18 @@ public class SetupGeosets {
final int bones = Math.min(matrixGroup.length, maxBones);
for (int j = 0; j < bones; j++) {
skin[offset + j] = (byte) (matrixGroup[j] + 1); // 1 is added to diffrentiate
skin[offset + j] = (int) (matrixGroup[j] + 1); // 1 is added to diffrentiate
// between matrix 0, and no matrix.
}
skin[offset + maxBones] = (byte) bones;
skin[offset + maxBones] = bones;
}
}
}
final Geoset vGeoset = new Geoset(model, model.getGeosets().size(), positionOffset, normalOffset,
uvOffset, skinOffset, faceOffset, vertices, faces.length);
uvOffset, skinOffset, faceOffset, vertices, faces.length, openGLSkinType, skinStride,
boneCountOffsetBytes);
model.getGeosets().add(vGeoset);
@ -173,8 +190,9 @@ public class SetupGeosets {
}
// Skin.
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length, RenderMathUtils.wrap(skin));
skinOffset += skin.length * 1;
gl.glBufferSubData(GL20.GL_ARRAY_BUFFER, skinOffset, skin.length,
bigNodeSpace ? RenderMathUtils.wrap(skin) : RenderMathUtils.wrapAsBytes(skin));
skinOffset += skin.length * (bigNodeSpace ? 4 : 1);
// Faces.
gl.glBufferSubData(GL20.GL_ELEMENT_ARRAY_BUFFER, faceOffset, faces.length,

View File

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

View File

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

View File

@ -102,7 +102,7 @@ public class RenderAttackProjectile {
this.modelInstance.setLocation(this.x, this.y, this.z);
this.modelInstance.localRotation.setFromAxisRad(0, 0, 1, this.yaw);
this.modelInstance.rotate(pitchHeap.setFromAxisRad(0, -1, 0, this.pitch));
war3MapViewer.worldScene.grid.moved(this.modelInstance);
war3MapViewer.worldScene.grid.moved(this.modelInstance, this.x, this.y);
final boolean everythingDone = this.simulationProjectile.isDone() && this.modelInstance.sequenceEnded;
if (everythingDone) {

View File

@ -176,7 +176,7 @@ public class RenderUnit {
}
this.facing = (((this.facing + angleToAdd) % 360) + 360) % 360;
this.instance.setLocalRotation(tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z, this.facing));
map.worldScene.grid.moved(this.instance);
map.worldScene.grid.moved(this.instance, this.location[0], this.location[1]);
final MdxComplexInstance mdxComplexInstance = this.instance;
final COrder currentOrder = this.simulationUnit.getCurrentOrder();
if (this.simulationUnit.getLife() <= 0) {

1
fdfparser/.gitignore vendored Normal file
View File

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

View File

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

View File

@ -8,6 +8,9 @@ import java.util.Map;
import java.util.Set;
import com.etheller.warsmash.parsers.fdf.datamodel.fields.FrameDefinitionField;
import com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor.GetFloatFieldVisitor;
import com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor.GetStringFieldVisitor;
import com.etheller.warsmash.parsers.fdf.datamodel.fields.visitor.GetVector4FieldVisitor;
/**
* Pretty sure this is probably not how it works in-game but this silly
@ -58,6 +61,14 @@ public class FrameDefinition {
this.flags.add(flag);
}
public boolean has(final String flag) {
return this.flags.contains(flag);
}
public FrameDefinitionField get(final String fieldName) {
return this.nameToField.get(fieldName);
}
@Override
public String toString() {
return "FrameDefinition [frameClass=" + this.frameClass + ", frameType=" + this.frameType + ", name="
@ -65,4 +76,51 @@ public class FrameDefinition {
+ this.nameToField + ", setPoints=" + this.setPoints + ", anchors=" + this.anchors + "]";
}
public String getFrameType() {
return this.frameType;
}
public String getName() {
return this.name;
}
public FrameClass getFrameClass() {
return this.frameClass;
}
public List<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'