Bunch of updates to Jass VM, added a bunch more natives and features to support them

This commit is contained in:
Retera 2021-09-07 00:32:04 -04:00
parent a26a2cc248
commit fdc78a105e
123 changed files with 6026 additions and 629 deletions

View File

@ -1,5 +1,5 @@
[DataSources] [DataSources]
Count=9 Count=8
Type00=MPQ Type00=MPQ
Path00="D:\Games\Warcraft III Patch 1.22\war3.mpq" Path00="D:\Games\Warcraft III Patch 1.22\war3.mpq"
Type01=MPQ Type01=MPQ
@ -8,13 +8,11 @@ Type02=MPQ
Path02="D:\Games\Warcraft III Patch 1.22\War3xlocal.mpq" Path02="D:\Games\Warcraft III Patch 1.22\War3xlocal.mpq"
Type03=MPQ Type03=MPQ
Path03="D:\Games\Warcraft III Patch 1.22\War3Patch.mpq" Path03="D:\Games\Warcraft III Patch 1.22\War3Patch.mpq"
Type04=MPQ Type04=Folder
Path04="D:\Games\Warcraft III Patch 1.22\Warsmash\War3Mod.mpq" Path04="..\..\resources"
Type05=Folder Type05=Folder
Path05="..\..\resources" Path05="D:\Backups\Warsmash\Data"
Type06=Folder Type06=Folder
Path06="D:\Backups\Warsmash\Data" Path06="D:\Games\Warcraft III Patch 1.22\Maps"
Type07=Folder Type07=Folder
Path07="D:\Games\Warcraft III Patch 1.22\Maps" Path07="."
Type08=Folder
Path08="."

View File

@ -30,12 +30,12 @@ import com.etheller.warsmash.datasources.DataSourceDescriptor;
import com.etheller.warsmash.datasources.FolderDataSourceDescriptor; import com.etheller.warsmash.datasources.FolderDataSourceDescriptor;
import com.etheller.warsmash.datasources.MpqDataSourceDescriptor; import com.etheller.warsmash.datasources.MpqDataSourceDescriptor;
import com.etheller.warsmash.parsers.fdf.GameUI; import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.jass.Jass2;
import com.etheller.warsmash.parsers.jass.Jass2.CommonEnvironment;
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener; import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
import com.etheller.warsmash.units.DataTable; import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.Element; import com.etheller.warsmash.units.Element;
import com.etheller.warsmash.util.DataSourceFileHandle;
import com.etheller.warsmash.util.ImageUtils; import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.Model; import com.etheller.warsmash.viewer5.Model;
import com.etheller.warsmash.viewer5.ModelInstance; import com.etheller.warsmash.viewer5.ModelInstance;
import com.etheller.warsmash.viewer5.ModelViewer; import com.etheller.warsmash.viewer5.ModelViewer;
@ -53,7 +53,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI;
public class WarsmashGdxMapScreen implements InputProcessor, Screen { public class WarsmashGdxMapScreen implements InputProcessor, Screen {
public static final boolean ENABLE_AUDIO = true; public static final boolean ENABLE_AUDIO = true;
private static final boolean ENABLE_MUSIC = true;
private final War3MapViewer viewer; private final War3MapViewer viewer;
private final Rectangle tempRect = new Rectangle(); private final Rectangle tempRect = new Rectangle();
@ -76,6 +75,7 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen {
private final WarsmashGdxMultiScreenGame screenManager; private final WarsmashGdxMultiScreenGame screenManager;
private final WarsmashGdxMenuScreen menuScreen; private final WarsmashGdxMenuScreen menuScreen;
private final CPlayerUnitOrderListener uiOrderListener; private final CPlayerUnitOrderListener uiOrderListener;
private CommonEnvironment commonEnv;
public WarsmashGdxMapScreen(final War3MapViewer mapViewer, final WarsmashGdxMultiScreenGame screenManager, public WarsmashGdxMapScreen(final War3MapViewer mapViewer, final WarsmashGdxMultiScreenGame screenManager,
final WarsmashGdxMenuScreen menuScreen, final CPlayerUnitOrderListener uiOrderListener) { final WarsmashGdxMenuScreen menuScreen, final CPlayerUnitOrderListener uiOrderListener) {
@ -150,7 +150,8 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen {
if (width < ((height * 4) / 3)) { if (width < ((height * 4) / 3)) {
aspect3By4Width = width; aspect3By4Width = width;
aspect3By4Height = (width * 3) / 4; aspect3By4Height = (width * 3) / 4;
} else { }
else {
aspect3By4Width = (height * 4) / 3; aspect3By4Width = (height * 4) / 3;
aspect3By4Height = height; aspect3By4Height = height;
} }
@ -171,13 +172,6 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen {
this.shapeRenderer = new ShapeRenderer(); this.shapeRenderer = new ShapeRenderer();
// Jass2.loadJUI(this.codebase, this.uiViewport, fontGenerator, this.uiScene, this.viewer,
// new RootFrameListener() {
// @Override
// public void onCreate(final GameUI rootFrame) {
// WarsmashGdxMapGame.this.gameUI = rootFrame;
// }
// }, "Scripts\\common.jui", "Scripts\\melee.jui");
final Element cameraRatesElement = this.viewer.miscData.get("CameraRates"); final Element cameraRatesElement = this.viewer.miscData.get("CameraRates");
final CameraRates cameraRates = new CameraRates(cameraRatesElement.getFieldFloatValue("AOA"), final CameraRates cameraRates = new CameraRates(cameraRatesElement.getFieldFloatValue("AOA"),
cameraRatesElement.getFieldFloatValue("FOV"), cameraRatesElement.getFieldFloatValue("Rotation"), cameraRatesElement.getFieldFloatValue("FOV"), cameraRatesElement.getFieldFloatValue("Rotation"),
@ -188,22 +182,6 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen {
@Override @Override
public void onCreate(final GameUI rootFrame) { public void onCreate(final GameUI rootFrame) {
WarsmashGdxMapScreen.this.viewer.setGameUI(rootFrame); WarsmashGdxMapScreen.this.viewer.setGameUI(rootFrame);
if (ENABLE_MUSIC) {
final String musicField = rootFrame
.getSkinField("Music_V" + WarsmashConstants.GAME_VERSION);
final String[] musics = musicField.split(";");
String musicPath = musics[(int) (Math.random() * musics.length)];
if (false) {
musicPath = "Sound\\Music\\mp3Music\\PH1.mp3";
}
final Music music = Gdx.audio.newMusic(
new DataSourceFileHandle(WarsmashGdxMapScreen.this.viewer.dataSource, musicPath));
music.setVolume(1.0f);
music.setLooping(true);
music.play();
WarsmashGdxMapScreen.this.currentMusic = music;
}
} }
}, this.uiOrderListener, new Runnable() { }, this.uiOrderListener, new Runnable() {
@Override @Override
@ -224,9 +202,12 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen {
try { try {
this.viewer.loadAfterUI(); this.viewer.loadAfterUI();
} catch (final IOException e) { }
catch (final IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
this.commonEnv = Jass2.loadCommon(this.viewer.mapMpq, this.uiViewport, this.uiScene, this.viewer, this.meleeUI,
"Scripts\\common.j", "Scripts\\Blizzard.j", "war3map.j");
} }
public static DataSource parseDataSources(final DataTable warsmashIni) { public static DataSource parseDataSources(final DataTable warsmashIni) {
@ -508,8 +489,6 @@ public class WarsmashGdxMapScreen implements InputProcessor, Screen {
@Override @Override
public void hide() { public void hide() {
if (this.currentMusic != null) { this.meleeUI.gameClosed();
this.currentMusic.stop();
}
} }
} }

View File

@ -52,7 +52,6 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MenuUI;
public class WarsmashGdxMenuScreen implements InputProcessor, Screen, SingleModelScreen { public class WarsmashGdxMenuScreen implements InputProcessor, Screen, SingleModelScreen {
private static final boolean ENABLE_AUDIO = true; private static final boolean ENABLE_AUDIO = true;
private static final boolean ENABLE_MUSIC = true;
private DataSource codebase; private DataSource codebase;
private MdxViewer viewer; private MdxViewer viewer;
private MdxModel model; private MdxModel model;
@ -185,7 +184,7 @@ public class WarsmashGdxMenuScreen implements InputProcessor, Screen, SingleMode
public void onCreate(final GameUI rootFrame) { public void onCreate(final GameUI rootFrame) {
// WarsmashGdxMapGame.this.viewer.setGameUI(rootFrame); // WarsmashGdxMapGame.this.viewer.setGameUI(rootFrame);
if (ENABLE_MUSIC) { if (WarsmashConstants.ENABLE_MUSIC) {
final String musicField = rootFrame final String musicField = rootFrame
.getSkinField("GlueMusic_V" + WarsmashConstants.GAME_VERSION); .getSkinField("GlueMusic_V" + WarsmashConstants.GAME_VERSION);
final String[] musics = musicField.split(";"); final String[] musics = musicField.split(";");

View File

@ -14,6 +14,9 @@ public interface ClientToServerListener {
void issueDropItemAtPointOrder(SocketAddress sourceAddress, int unitHandleId, int abilityHandleId, int orderId, void issueDropItemAtPointOrder(SocketAddress sourceAddress, int unitHandleId, int abilityHandleId, int orderId,
int targetHandleId, float x, float y, final boolean queue); int targetHandleId, float x, float y, final boolean queue);
void issueDropItemAtTargetOrder(SocketAddress sourceAddress, int unitHandleId, int abilityHandleId, int orderId,
int targetHandleId, int targetHeroHandleId, final boolean queue);
void issueImmediateOrder(SocketAddress sourceAddress, int unitHandleId, int abilityHandleId, int orderId, void issueImmediateOrder(SocketAddress sourceAddress, int unitHandleId, int abilityHandleId, int orderId,
boolean queue); boolean queue);

View File

@ -5,6 +5,7 @@ public class ClientToServerProtocol {
public static final int ISSUE_TARGET_ORDER = 1; public static final int ISSUE_TARGET_ORDER = 1;
public static final int ISSUE_POINT_ORDER = 2; public static final int ISSUE_POINT_ORDER = 2;
public static final int ISSUE_DROP_ITEM_ORDER = 3; public static final int ISSUE_DROP_ITEM_ORDER = 3;
public static final int ISSUE_DROP_ITEM_ON_TARGET_ORDER = 9;
public static final int ISSUE_IMMEDIATE_ORDER = 4; public static final int ISSUE_IMMEDIATE_ORDER = 4;
public static final int UNIT_CANCEL_TRAINING = 5; public static final int UNIT_CANCEL_TRAINING = 5;
public static final int FINISHED_TURN = 6; public static final int FINISHED_TURN = 6;

View File

@ -12,6 +12,9 @@ public interface ServerToClientListener {
void issueDropItemAtPointOrder(int playerIndex, int unitHandleId, int abilityHandleId, int orderId, void issueDropItemAtPointOrder(int playerIndex, int unitHandleId, int abilityHandleId, int orderId,
int targetHandleId, float x, float y, final boolean queue); int targetHandleId, float x, float y, final boolean queue);
void issueDropItemAtTargetOrder(int playerIndex, int unitHandleId, int abilityHandleId, int orderId,
int targetHandleId, int targetHeroHandleId, final boolean queue);
void issueImmediateOrder(int playerIndex, int unitHandleId, int abilityHandleId, int orderId, boolean queue); void issueImmediateOrder(int playerIndex, int unitHandleId, int abilityHandleId, int orderId, boolean queue);
void unitCancelTrainingItem(int playerIndex, int unitHandleId, int cancelIndex); void unitCancelTrainingItem(int playerIndex, int unitHandleId, int cancelIndex);

View File

@ -5,6 +5,7 @@ public class ServerToClientProtocol {
public static final int ISSUE_TARGET_ORDER = 1; public static final int ISSUE_TARGET_ORDER = 1;
public static final int ISSUE_POINT_ORDER = 2; public static final int ISSUE_POINT_ORDER = 2;
public static final int ISSUE_DROP_ITEM_ORDER = 3; public static final int ISSUE_DROP_ITEM_ORDER = 3;
public static final int ISSUE_DROP_ITEM_ON_TARGET_ORDER = 10;
public static final int ISSUE_IMMEDIATE_ORDER = 4; public static final int ISSUE_IMMEDIATE_ORDER = 4;
public static final int UNIT_CANCEL_TRAINING = 5; public static final int UNIT_CANCEL_TRAINING = 5;
public static final int FINISHED_TURN = 6; public static final int FINISHED_TURN = 6;

View File

@ -3,7 +3,10 @@ package com.etheller.warsmash.networking;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.*; import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.etheller.warsmash.networking.udp.OrderedUdpClient; import com.etheller.warsmash.networking.udp.OrderedUdpClient;
@ -53,18 +56,21 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
int currentServerTurnInProgress = latestCompletedTurn + 1; final int currentServerTurnInProgress = WarsmashClient.this.latestCompletedTurn + 1;
if(currentServerTurnInProgress > latestLocallyRequestedTurn) { if (currentServerTurnInProgress > WarsmashClient.this.latestLocallyRequestedTurn) {
queuedMessages.add(new QueuedMessage(latestCompletedTurn) { WarsmashClient.this.queuedMessages.add(new QueuedMessage(WarsmashClient.this.latestCompletedTurn) {
@Override @Override
public void run() { public void run() {
executor.issueTargetOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, queue); executor.issueTargetOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, queue);
} }
}); });
} else if(currentServerTurnInProgress == latestLocallyRequestedTurn) { }
else if (currentServerTurnInProgress == WarsmashClient.this.latestLocallyRequestedTurn) {
executor.issueTargetOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, queue); executor.issueTargetOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, queue);
} else { }
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < " + latestLocallyRequestedTurn); else {
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < "
+ WarsmashClient.this.latestLocallyRequestedTurn);
} }
} }
}); });
@ -77,18 +83,22 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
int currentServerTurnInProgress = latestCompletedTurn + 1; final int currentServerTurnInProgress = WarsmashClient.this.latestCompletedTurn + 1;
if(currentServerTurnInProgress > latestLocallyRequestedTurn) { if (currentServerTurnInProgress > WarsmashClient.this.latestLocallyRequestedTurn) {
queuedMessages.add(new QueuedMessage(latestCompletedTurn) { WarsmashClient.this.queuedMessages.add(new QueuedMessage(WarsmashClient.this.latestCompletedTurn) {
@Override @Override
public void run() { public void run() {
executor.issuePointOrder(unitHandleId, abilityHandleId, orderId, x, y, queue); executor.issuePointOrder(unitHandleId, abilityHandleId, orderId, x, y, queue);
} }
}); });
} else if(currentServerTurnInProgress == latestLocallyRequestedTurn) { }
executor.issuePointOrder(unitHandleId, abilityHandleId, orderId, x, y, queue);; else if (currentServerTurnInProgress == WarsmashClient.this.latestLocallyRequestedTurn) {
} else { executor.issuePointOrder(unitHandleId, abilityHandleId, orderId, x, y, queue);
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < " + latestLocallyRequestedTurn); ;
}
else {
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < "
+ WarsmashClient.this.latestLocallyRequestedTurn);
} }
} }
}); });
@ -102,18 +112,52 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
int currentServerTurnInProgress = latestCompletedTurn + 1; final int currentServerTurnInProgress = WarsmashClient.this.latestCompletedTurn + 1;
if(currentServerTurnInProgress > latestLocallyRequestedTurn) { if (currentServerTurnInProgress > WarsmashClient.this.latestLocallyRequestedTurn) {
queuedMessages.add(new QueuedMessage(latestCompletedTurn) { WarsmashClient.this.queuedMessages.add(new QueuedMessage(WarsmashClient.this.latestCompletedTurn) {
@Override @Override
public void run() { public void run() {
executor.issueDropItemAtPointOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, x, y, queue); executor.issueDropItemAtPointOrder(unitHandleId, abilityHandleId, orderId, targetHandleId,
x, y, queue);
} }
}); });
} else if(currentServerTurnInProgress == latestLocallyRequestedTurn) { }
executor.issueDropItemAtPointOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, x, y, queue); else if (currentServerTurnInProgress == WarsmashClient.this.latestLocallyRequestedTurn) {
} else { executor.issueDropItemAtPointOrder(unitHandleId, abilityHandleId, orderId, targetHandleId, x, y,
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < " + latestLocallyRequestedTurn); queue);
}
else {
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < "
+ WarsmashClient.this.latestLocallyRequestedTurn);
}
}
});
}
@Override
public void issueDropItemAtTargetOrder(final int playerIndex, final int unitHandleId, final int abilityHandleId,
final int orderId, final int targetHandleId, final int targetHeroHandleId, final boolean queue) {
final CPlayerUnitOrderExecutor executor = getExecutor(playerIndex);
Gdx.app.postRunnable(new Runnable() {
@Override
public void run() {
final int currentServerTurnInProgress = WarsmashClient.this.latestCompletedTurn + 1;
if (currentServerTurnInProgress > WarsmashClient.this.latestLocallyRequestedTurn) {
WarsmashClient.this.queuedMessages.add(new QueuedMessage(WarsmashClient.this.latestCompletedTurn) {
@Override
public void run() {
executor.issueDropItemAtTargetOrder(unitHandleId, abilityHandleId, orderId, targetHandleId,
targetHeroHandleId, queue);
}
});
}
else if (currentServerTurnInProgress == WarsmashClient.this.latestLocallyRequestedTurn) {
executor.issueDropItemAtTargetOrder(unitHandleId, abilityHandleId, orderId, targetHandleId,
targetHeroHandleId, queue);
}
else {
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < "
+ WarsmashClient.this.latestLocallyRequestedTurn);
} }
} }
}); });
@ -126,18 +170,21 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
int currentServerTurnInProgress = latestCompletedTurn + 1; final int currentServerTurnInProgress = WarsmashClient.this.latestCompletedTurn + 1;
if(currentServerTurnInProgress > latestLocallyRequestedTurn) { if (currentServerTurnInProgress > WarsmashClient.this.latestLocallyRequestedTurn) {
queuedMessages.add(new QueuedMessage(latestCompletedTurn) { WarsmashClient.this.queuedMessages.add(new QueuedMessage(WarsmashClient.this.latestCompletedTurn) {
@Override @Override
public void run() { public void run() {
executor.issueImmediateOrder(unitHandleId, abilityHandleId, orderId, queue); executor.issueImmediateOrder(unitHandleId, abilityHandleId, orderId, queue);
} }
}); });
} else if(currentServerTurnInProgress == latestLocallyRequestedTurn) { }
else if (currentServerTurnInProgress == WarsmashClient.this.latestLocallyRequestedTurn) {
executor.issueImmediateOrder(unitHandleId, abilityHandleId, orderId, queue); executor.issueImmediateOrder(unitHandleId, abilityHandleId, orderId, queue);
} else { }
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < " + latestLocallyRequestedTurn); else {
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < "
+ WarsmashClient.this.latestLocallyRequestedTurn);
} }
} }
}); });
@ -149,18 +196,21 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@Override @Override
public void run() { public void run() {
int currentServerTurnInProgress = latestCompletedTurn + 1; final int currentServerTurnInProgress = WarsmashClient.this.latestCompletedTurn + 1;
if(currentServerTurnInProgress > latestLocallyRequestedTurn) { if (currentServerTurnInProgress > WarsmashClient.this.latestLocallyRequestedTurn) {
queuedMessages.add(new QueuedMessage(latestCompletedTurn) { WarsmashClient.this.queuedMessages.add(new QueuedMessage(WarsmashClient.this.latestCompletedTurn) {
@Override @Override
public void run() { public void run() {
executor.unitCancelTrainingItem(unitHandleId, cancelIndex); executor.unitCancelTrainingItem(unitHandleId, cancelIndex);
} }
}); });
} else if(currentServerTurnInProgress == latestLocallyRequestedTurn) { }
else if (currentServerTurnInProgress == WarsmashClient.this.latestLocallyRequestedTurn) {
executor.unitCancelTrainingItem(unitHandleId, cancelIndex); executor.unitCancelTrainingItem(unitHandleId, cancelIndex);
} else { }
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < " + latestLocallyRequestedTurn); else {
System.err.println("Turn tick system mismatch: " + currentServerTurnInProgress + " < "
+ WarsmashClient.this.latestLocallyRequestedTurn);
} }
} }
}); });
@ -168,7 +218,7 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
@Override @Override
public void finishedTurn(final int gameTurnTick) { public void finishedTurn(final int gameTurnTick) {
if(WarsmashConstants.VERBOSE_LOGGING) { if (WarsmashConstants.VERBOSE_LOGGING) {
System.out.println("finishedTurn " + gameTurnTick); System.out.println("finishedTurn " + gameTurnTick);
} }
Gdx.app.postRunnable(new Runnable() { Gdx.app.postRunnable(new Runnable() {
@ -183,9 +233,10 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
public void turnCompleted(final int gameTurnTick) { public void turnCompleted(final int gameTurnTick) {
this.writer.finishedTurn(gameTurnTick); this.writer.finishedTurn(gameTurnTick);
this.writer.send(); this.writer.send();
latestLocallyRequestedTurn = gameTurnTick; this.latestLocallyRequestedTurn = gameTurnTick;
while(!queuedMessages.isEmpty() && queuedMessages.peek().messageTurnTick == latestLocallyRequestedTurn) { while (!this.queuedMessages.isEmpty()
queuedMessages.poll().run(); && (this.queuedMessages.peek().messageTurnTick == this.latestLocallyRequestedTurn)) {
this.queuedMessages.poll().run();
} }
} }
@ -207,8 +258,10 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
@Override @Override
public void heartbeat() { public void heartbeat() {
// Not doing anything here at the moment. The act of the server sending us that packet // Not doing anything here at the moment. The act of the server sending us that
// will let the middle layer UDP system know to re-request any lost packets based // packet
// will let the middle layer UDP system know to re-request any lost packets
// based
// on the heartbeat seq no. But at app layer, here, we can ignore it. // on the heartbeat seq no. But at app layer, here, we can ignore it.
System.out.println("got heartbeat() from server"); System.out.println("got heartbeat() from server");
} }
@ -223,16 +276,17 @@ public class WarsmashClient implements ServerToClientListener, GameTurnManager {
} }
private static abstract class QueuedMessage implements Runnable { private static abstract class QueuedMessage implements Runnable {
private int messageTurnTick; private final int messageTurnTick;
public QueuedMessage(int messageTurnTick) { public QueuedMessage(final int messageTurnTick) {
this.messageTurnTick = messageTurnTick; this.messageTurnTick = messageTurnTick;
} }
public final int getMessageTurnTick() { public final int getMessageTurnTick() {
return messageTurnTick; return this.messageTurnTick;
} }
@Override
public abstract void run(); public abstract void run();
} }
} }

View File

@ -30,6 +30,14 @@ public class WarsmashClientSendingOrderListener implements CPlayerUnitOrderListe
this.writer.send(); this.writer.send();
} }
@Override
public void issueDropItemAtTargetOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final int targetItemHandleId, final int targetHeroHandleId, final boolean queue) {
this.writer.issueDropItemAtTargetOrder(unitHandleId, abilityHandleId, orderId, targetItemHandleId,
targetHeroHandleId, queue);
this.writer.send();
}
@Override @Override
public void issueImmediateOrder(final int unitHandleId, final int abilityHandleId, final int orderId, public void issueImmediateOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final boolean queue) { final boolean queue) {

View File

@ -53,6 +53,19 @@ public class WarsmashClientWriter {
this.sendBuffer.put(queue ? (byte) 1 : (byte) 0); this.sendBuffer.put(queue ? (byte) 1 : (byte) 0);
} }
public void issueDropItemAtTargetOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final int targetHandleId, final int targetHeroHandleId, final boolean queue) {
this.sendBuffer.clear();
this.sendBuffer.putInt(4 + 4 + 4 + 4 + 4 + 4 + 1);
this.sendBuffer.putInt(ClientToServerProtocol.ISSUE_DROP_ITEM_ON_TARGET_ORDER);
this.sendBuffer.putInt(unitHandleId);
this.sendBuffer.putInt(abilityHandleId);
this.sendBuffer.putInt(orderId);
this.sendBuffer.putInt(targetHandleId);
this.sendBuffer.putInt(targetHeroHandleId);
this.sendBuffer.put(queue ? (byte) 1 : (byte) 0);
}
public void issueImmediateOrder(final int unitHandleId, final int abilityHandleId, final int orderId, public void issueImmediateOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final boolean queue) { final boolean queue) {
this.sendBuffer.clear(); this.sendBuffer.clear();

View File

@ -108,6 +108,22 @@ public class WarsmashServer implements ClientToServerListener {
}); });
} }
@Override
public void issueDropItemAtTargetOrder(final SocketAddress sourceAddress, final int unitHandleId,
final int abilityHandleId, final int orderId, final int targetHandleId, final int targetHeroHandleId,
final boolean queue) {
System.out.println("issueDropItemAtTargetOrder from " + sourceAddress);
final int playerIndex = getPlayerIndex(sourceAddress);
this.turnActions.add(new Runnable() {
@Override
public void run() {
WarsmashServer.this.writer.issueDropItemAtTargetOrder(playerIndex, unitHandleId, abilityHandleId,
orderId, targetHandleId, targetHeroHandleId, queue);
WarsmashServer.this.writer.send();
}
});
}
@Override @Override
public void issueImmediateOrder(final SocketAddress sourceAddress, final int unitHandleId, public void issueImmediateOrder(final SocketAddress sourceAddress, final int unitHandleId,
final int abilityHandleId, final int orderId, final boolean queue) { final int abilityHandleId, final int orderId, final boolean queue) {

View File

@ -60,6 +60,17 @@ public class WarsmashServerParser implements OrderedUdpServerListener {
targetHandleId, x, y, queue); targetHandleId, x, y, queue);
break; break;
} }
case ClientToServerProtocol.ISSUE_DROP_ITEM_ON_TARGET_ORDER: {
final int unitHandleId = buffer.getInt();
final int abilityHandleId = buffer.getInt();
final int orderId = buffer.getInt();
final int targetHandleId = buffer.getInt();
final int targetHeroHandleId = buffer.getInt();
final boolean queue = buffer.get() == 1;
this.listener.issueDropItemAtTargetOrder(sourceAddress, unitHandleId, abilityHandleId, orderId,
targetHandleId, targetHeroHandleId, queue);
break;
}
case ClientToServerProtocol.ISSUE_IMMEDIATE_ORDER: { case ClientToServerProtocol.ISSUE_IMMEDIATE_ORDER: {
final int unitHandleId = buffer.getInt(); final int unitHandleId = buffer.getInt();
final int abilityHandleId = buffer.getInt(); final int abilityHandleId = buffer.getInt();

View File

@ -63,6 +63,21 @@ public class WarsmashServerWriter implements ServerToClientListener {
this.sendBuffer.put(queue ? (byte) 1 : (byte) 0); this.sendBuffer.put(queue ? (byte) 1 : (byte) 0);
} }
@Override
public void issueDropItemAtTargetOrder(final int playerIndex, final int unitHandleId, final int abilityHandleId,
final int orderId, final int targetHandleId, final int targetHeroHandleId, final boolean queue) {
this.sendBuffer.clear();
this.sendBuffer.putInt(4 + 4 + 4 + 4 + 4 + 4 + 4 + 1);
this.sendBuffer.putInt(ServerToClientProtocol.ISSUE_DROP_ITEM_ON_TARGET_ORDER);
this.sendBuffer.putInt(playerIndex);
this.sendBuffer.putInt(unitHandleId);
this.sendBuffer.putInt(abilityHandleId);
this.sendBuffer.putInt(orderId);
this.sendBuffer.putInt(targetHandleId);
this.sendBuffer.putInt(targetHeroHandleId);
this.sendBuffer.put(queue ? (byte) 1 : (byte) 0);
}
@Override @Override
public void issueImmediateOrder(final int playerIndex, final int unitHandleId, final int abilityHandleId, public void issueImmediateOrder(final int playerIndex, final int unitHandleId, final int abilityHandleId,
final int orderId, final boolean queue) { final int orderId, final boolean queue) {

View File

@ -1054,7 +1054,11 @@ public final class GameUI extends AbstractUIFrame implements UIFrame {
} }
public String getErrorString(final String key) { public String getErrorString(final String key) {
String errorString = this.errorStrings.getField(key, this.racialCommandIndex); final String errorString = this.errorStrings.getField(key, this.racialCommandIndex);
return getTrigStr(errorString);
}
public String getTrigStr(String errorString) {
if (errorString.startsWith("TRIGSTR_")) { if (errorString.startsWith("TRIGSTR_")) {
errorString = this.mapStrings.get(Integer.parseInt(errorString.substring(8))); errorString = this.mapStrings.get(Integer.parseInt(errorString.substring(8)));
} }

View File

@ -1,5 +1,7 @@
package com.etheller.warsmash.parsers.fdf.frames; package com.etheller.warsmash.parsers.fdf.frames;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint; import com.etheller.warsmash.parsers.fdf.datamodel.FramePoint;
import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition; import com.etheller.warsmash.parsers.fdf.datamodel.Vector4Definition;
@ -8,6 +10,7 @@ public class SimpleStatusBarFrame extends AbstractUIFrame {
private final TextureFrame barFrame; private final TextureFrame barFrame;
private final TextureFrame borderFrame; private final TextureFrame borderFrame;
private final float barInset; private final float barInset;
private float lastValue = Float.NaN;
public SimpleStatusBarFrame(final String name, final UIFrame parent, final boolean decorateFileNames, public SimpleStatusBarFrame(final String name, final UIFrame parent, final boolean decorateFileNames,
final boolean borderBelow, final float barInset) { final boolean borderBelow, final float barInset) {
@ -31,6 +34,14 @@ public class SimpleStatusBarFrame extends AbstractUIFrame {
} }
} }
@Override
protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) {
if (!Float.isNaN(this.lastValue)) {
this.barFrame.setWidth(((this.renderBounds.width - (this.barInset * 2)) * this.lastValue));
}
super.innerPositionBounds(gameUI, viewport);
}
public boolean isDecorateFileNames() { public boolean isDecorateFileNames() {
return this.decorateFileNames; return this.decorateFileNames;
} }
@ -38,6 +49,7 @@ public class SimpleStatusBarFrame extends AbstractUIFrame {
public void setValue(final float value) { public void setValue(final float value) {
this.barFrame.setTexCoord(0, value, 0, 1); this.barFrame.setTexCoord(0, value, 0, 1);
this.barFrame.setWidth(((this.renderBounds.width - (this.barInset * 2)) * value)); this.barFrame.setWidth(((this.renderBounds.width - (this.barInset * 2)) * value));
this.lastValue = value;
} }
public TextureFrame getBarFrame() { public TextureFrame getBarFrame() {

View File

@ -1,5 +1,6 @@
package com.etheller.warsmash.parsers.fdf.frames; package com.etheller.warsmash.parsers.fdf.frames;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout; import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
@ -135,4 +136,10 @@ public class SpriteFrame extends AbstractUIFrame {
} }
public void setVertexColor(final Color color) {
if (this.instance != null) {
this.instance.setVertexColor(color);
}
}
} }

View File

@ -28,6 +28,7 @@ public class StringFrame extends AbstractRenderableFrame {
private float alpha = 1.0f; private float alpha = 1.0f;
private final SimpleFrame internalFramesContainer; private final SimpleFrame internalFramesContainer;
private float predictedViewportHeight; private float predictedViewportHeight;
private float predictedViewportWidth;
static ShapeRenderer shapeRenderer = new ShapeRenderer(); static ShapeRenderer shapeRenderer = new ShapeRenderer();
private final Color fontHighlightColor; private final Color fontHighlightColor;
@ -425,6 +426,7 @@ public class StringFrame extends AbstractRenderableFrame {
this.internalFramesContainer.setWidth(usedWidthMax); this.internalFramesContainer.setWidth(usedWidthMax);
this.internalFramesContainer.setHeight(usedHeight); this.internalFramesContainer.setHeight(usedHeight);
this.predictedViewportHeight = (usedHeight - this.frameFont.getCapHeight()) + this.frameFont.getLineHeight(); this.predictedViewportHeight = (usedHeight - this.frameFont.getCapHeight()) + this.frameFont.getLineHeight();
this.predictedViewportWidth = usedWidthMax;
this.internalFramesContainer.clearFramePointAssignments(); this.internalFramesContainer.clearFramePointAssignments();
switch (this.justifyH) { switch (this.justifyH) {
@ -490,6 +492,10 @@ public class StringFrame extends AbstractRenderableFrame {
return this.predictedViewportHeight; return this.predictedViewportHeight;
} }
public float getPredictedViewportWidth() {
return this.predictedViewportWidth;
}
public BitmapFont getFrameFont() { public BitmapFont getFrameFont() {
return this.frameFont; return this.frameFont;
} }

File diff suppressed because it is too large Load Diff

View File

@ -64,13 +64,14 @@ public class CommonTriggerExecutionScope extends TriggerExecutionScope {
private CDestructable orderTargetDestructable; private CDestructable orderTargetDestructable;
private CItem orderTargetItem; private CItem orderTargetItem;
private CUnit orderTargetUnit; private CUnit orderTargetUnit;
private CWidget triggerWidget;
public CommonTriggerExecutionScope(final Trigger triggeringTrigger) { public CommonTriggerExecutionScope(final Trigger triggeringTrigger) {
super(triggeringTrigger); super(triggeringTrigger);
} }
public CommonTriggerExecutionScope(final TriggerExecutionScope parentScope) { public CommonTriggerExecutionScope(final Trigger triggeringTrigger, final TriggerExecutionScope parentScope) {
super(parentScope.getTriggeringTrigger()); super(triggeringTrigger);
if (parentScope instanceof CommonTriggerExecutionScope) { if (parentScope instanceof CommonTriggerExecutionScope) {
copyFrom((CommonTriggerExecutionScope) parentScope); copyFrom((CommonTriggerExecutionScope) parentScope);
} }
@ -134,6 +135,7 @@ public class CommonTriggerExecutionScope extends TriggerExecutionScope {
this.orderTargetDestructable = parentScope.orderTargetDestructable; this.orderTargetDestructable = parentScope.orderTargetDestructable;
this.orderTargetItem = parentScope.orderTargetItem; this.orderTargetItem = parentScope.orderTargetItem;
this.orderTargetUnit = parentScope.orderTargetUnit; this.orderTargetUnit = parentScope.orderTargetUnit;
this.triggerWidget = parentScope.triggerWidget;
} }
public CUnit getEnumUnit() { public CUnit getEnumUnit() {
@ -144,6 +146,10 @@ public class CommonTriggerExecutionScope extends TriggerExecutionScope {
return this.triggeringUnit; return this.triggeringUnit;
} }
public CWidget getTriggerWidget() {
return this.triggerWidget;
}
public CUnit getFilterUnit() { public CUnit getFilterUnit() {
return this.filterUnit; return this.filterUnit;
} }
@ -350,63 +356,121 @@ public class CommonTriggerExecutionScope extends TriggerExecutionScope {
public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope, public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope,
final CUnit filterUnit) { final CUnit filterUnit) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.filterUnit = filterUnit; scope.filterUnit = filterUnit;
return scope; return scope;
} }
public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope, final CUnit enumUnit) { public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope, final CUnit enumUnit) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.enumUnit = enumUnit; scope.enumUnit = enumUnit;
return scope; return scope;
} }
public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope,
final CItem filterItem) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.filterItem = filterItem;
return scope;
}
public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope, final CItem enumItem) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.enumItem = enumItem;
return scope;
}
public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope,
final CDestructable filterDestructable) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.filterDestructable = filterDestructable;
return scope;
}
public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope,
final CDestructable enumDestructable) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.enumDestructable = enumDestructable;
return scope;
}
public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope, public static CommonTriggerExecutionScope filterScope(final TriggerExecutionScope parentScope,
final CPlayerJass filterPlayer) { final CPlayerJass filterPlayer) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.filterPlayer = filterPlayer; scope.filterPlayer = filterPlayer;
return scope; return scope;
} }
public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope, public static CommonTriggerExecutionScope enumScope(final TriggerExecutionScope parentScope,
final CPlayerJass enumPlayer) { final CPlayerJass enumPlayer) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope.getTriggeringTrigger(),
parentScope);
scope.enumPlayer = enumPlayer; scope.enumPlayer = enumPlayer;
return scope; return scope;
} }
public static CommonTriggerExecutionScope expiringTimer(final CTimerJass cTimerJass) { public static CommonTriggerExecutionScope expiringTimer(final Trigger trigger, final CTimerJass cTimerJass) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(TriggerExecutionScope.EMPTY); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, TriggerExecutionScope.EMPTY);
scope.expiringTimer = cTimerJass; scope.expiringTimer = cTimerJass;
return scope; return scope;
} }
public static CommonTriggerExecutionScope unitEnterRegionScope(final TriggerExecutionScope parentScope, public static CommonTriggerExecutionScope unitEnterRegionScope(final Trigger trigger,
final CUnit enteringUnit, final CRegion triggeringRegion) { final TriggerExecutionScope parentScope, final CUnit enteringUnit, final CRegion triggeringRegion) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, parentScope);
scope.enteringUnit = enteringUnit; scope.enteringUnit = enteringUnit;
scope.triggeringRegion = triggeringRegion; scope.triggeringRegion = triggeringRegion;
return scope; return scope;
} }
public static CommonTriggerExecutionScope unitLeaveRegionScope(final TriggerExecutionScope parentScope, public static CommonTriggerExecutionScope unitLeaveRegionScope(final Trigger trigger,
final CUnit leavingUnit, final CRegion triggeringRegion) { final TriggerExecutionScope parentScope, final CUnit leavingUnit, final CRegion triggeringRegion) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(parentScope); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, parentScope);
scope.leavingUnit = leavingUnit; scope.leavingUnit = leavingUnit;
scope.triggeringRegion = triggeringRegion; scope.triggeringRegion = triggeringRegion;
return scope; return scope;
} }
public static CommonTriggerExecutionScope playerHeroLevelScope(final CUnit hero) { public static CommonTriggerExecutionScope playerHeroLevelScope(final Trigger trigger, final CUnit hero) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(TriggerExecutionScope.EMPTY); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, TriggerExecutionScope.EMPTY);
scope.levelingUnit = hero; scope.levelingUnit = hero;
return scope; return scope;
} }
public static CommonTriggerExecutionScope playerHeroRevivableScope(final CUnit hero) { public static CommonTriggerExecutionScope playerHeroRevivableScope(final Trigger trigger, final CUnit hero) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(TriggerExecutionScope.EMPTY); final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, TriggerExecutionScope.EMPTY);
scope.revivableUnit = hero; scope.revivableUnit = hero;
return scope; return scope;
} }
public static CommonTriggerExecutionScope playerUnitDeathScope(final Trigger trigger, final CUnit dyingUnit,
final CUnit killingUnit) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, TriggerExecutionScope.EMPTY);
scope.dyingUnit = dyingUnit;
scope.triggerWidget = dyingUnit;
scope.triggeringUnit = dyingUnit;
scope.killingUnit = killingUnit;
return scope;
}
public static CommonTriggerExecutionScope widgetTriggerScope(final Trigger trigger, final CWidget triggerWidget) {
final CommonTriggerExecutionScope scope = new CommonTriggerExecutionScope(trigger, TriggerExecutionScope.EMPTY);
scope.triggerWidget = triggerWidget;
return scope;
}
public static interface UnitEventScopeBuilder {
CommonTriggerExecutionScope create(Trigger trigger, CUnit unit);
}
public static interface WidgetEventScopeBuilder {
CommonTriggerExecutionScope create(Trigger trigger, CWidget unit);
}
} }

View File

@ -3,18 +3,22 @@ package com.etheller.warsmash.parsers.w3x.objectdata;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import com.etheller.warsmash.datasources.DataSource; import com.etheller.warsmash.datasources.DataSource;
import com.etheller.warsmash.units.DataTable; import com.etheller.warsmash.units.DataTable;
import com.etheller.warsmash.units.StandardObjectData; import com.etheller.warsmash.units.StandardObjectData;
import com.etheller.warsmash.units.StandardObjectData.WarcraftData; import com.etheller.warsmash.units.StandardObjectData.WarcraftData;
import com.etheller.warsmash.units.custom.ObjectDataChangeEntry;
import com.etheller.warsmash.units.custom.WTS; import com.etheller.warsmash.units.custom.WTS;
import com.etheller.warsmash.units.custom.WTSFile; import com.etheller.warsmash.units.custom.WTSFile;
import com.etheller.warsmash.units.custom.War3ObjectDataChangeset; import com.etheller.warsmash.units.custom.War3ObjectDataChangeset;
import com.etheller.warsmash.units.manager.MutableObjectData; import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType; import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WorldEditStrings; import com.etheller.warsmash.util.WorldEditStrings;
import com.google.common.io.LittleEndianDataInputStream; import com.google.common.io.LittleEndianDataInputStream;
@ -135,6 +139,27 @@ public final class Warcraft3MapObjectData {
if (dataSource.has("war3map.w3u")) { if (dataSource.has("war3map.w3u")) {
unitChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3u")), wts, unitChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3u")), wts,
inlineWTS); inlineWTS);
// push unit changes to items.... as a Reign of Chaos support...
Iterator<Entry<War3ID, ObjectDataChangeEntry>> entryIterator = unitChangeset.getOriginal().iterator();
while (entryIterator.hasNext()) {
final Entry<War3ID, ObjectDataChangeEntry> entry = entryIterator.next();
final String rawcodeString = entry.toString();
final String oldIdString = entry.getValue().getOldId().toString();
if ((standardUnits.get(oldIdString) == null) && (standardItems.get(oldIdString) != null)) {
itemChangeset.getOriginal().put(entry.getKey(), entry.getValue());
entryIterator.remove();
}
}
entryIterator = unitChangeset.getCustom().iterator();
while (entryIterator.hasNext()) {
final Entry<War3ID, ObjectDataChangeEntry> entry = entryIterator.next();
final String rawcodeString = entry.toString();
final String oldIdString = entry.getValue().getOldId().toString();
if ((standardUnits.get(oldIdString) == null) && (standardItems.get(oldIdString) != null)) {
itemChangeset.getCustom().put(entry.getKey(), entry.getValue());
entryIterator.remove();
}
}
} }
if (dataSource.has("war3map.w3t")) { if (dataSource.has("war3map.w3t")) {
itemChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3t")), wts, itemChangeset.load(new LittleEndianDataInputStream(dataSource.getResourceAsStream("war3map.w3t")), wts,

View File

@ -107,6 +107,10 @@ public class Corner {
return this.blight; return this.blight;
} }
public void setBlight(final boolean flag) {
this.blight = flag ? 0b00100000 : 0;
}
public int getWater() { public int getWater() {
return this.water; return this.water;
} }

View File

@ -6,7 +6,7 @@ public class WarsmashConstants {
* With version, we use 0 for RoC, 1 for TFT emulation, and probably 2+ or * With version, we use 0 for RoC, 1 for TFT emulation, and probably 2+ or
* whatever for custom mods and other stuff * whatever for custom mods and other stuff
*/ */
public static int GAME_VERSION = 0; public static int GAME_VERSION = 1;
public static final int REPLACEABLE_TEXTURE_LIMIT = 64; public static final int REPLACEABLE_TEXTURE_LIMIT = 64;
public static final float SIMULATION_STEP_TIME = 1 / 20f; public static final float SIMULATION_STEP_TIME = 1 / 20f;
public static final int PORT_NUMBER = 6115; public static final int PORT_NUMBER = 6115;
@ -36,4 +36,7 @@ public class WarsmashConstants {
// workaround to fix it if you need the local files // workaround to fix it if you need the local files
// to take priority over built-ins for tilesets. // to take priority over built-ins for tilesets.
public static final boolean FIX_FLAT_FILES_TILESET_LOADING = false; public static final boolean FIX_FLAT_FILES_TILESET_LOADING = false;
public static final boolean ENABLE_MUSIC = false;
public static final boolean LOAD_UNITS_FROM_WORLDEDIT_DATA = false;
public static final boolean LOCAL_TEMP_TEST_ALL_PLAYERS_PLAYING = true;
} }

View File

@ -78,12 +78,30 @@ public class AudioContext {
} }
public AudioPanner createPanner() { public AudioPanner createPanner() {
return createPanner(true);
}
public AudioPanner createPanner(final boolean stopWhenOutOfRange) {
if (!stopWhenOutOfRange) {
return new AudioPanner(this.listener) {
@Override
public void connect(final AudioDestination destination) {
}
@Override
public boolean isWithinListenerDistance() {
return true;
}
};
}
else {
return new AudioPanner(this.listener) { return new AudioPanner(this.listener) {
@Override @Override
public void connect(final AudioDestination destination) { public void connect(final AudioDestination destination) {
} }
}; };
} }
}
public AudioBufferSource createBufferSource() { public AudioBufferSource createBufferSource() {
return new AudioBufferSource(); return new AudioBufferSource();

View File

@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Quaternion; import com.badlogic.gdx.math.Quaternion;
@ -73,6 +74,7 @@ public class MdxComplexInstance extends ModelInstance {
private float blendTime; private float blendTime;
private float blendTimeRemaining; private float blendTimeRemaining;
public boolean additiveOverrideMeshMode = false; public boolean additiveOverrideMeshMode = false;
private boolean hasAnyUnselectableMesh = false;
public MdxComplexInstance(final MdxModel model) { public MdxComplexInstance(final MdxModel model) {
super(model); super(model);
@ -85,6 +87,9 @@ public class MdxComplexInstance extends ModelInstance {
this.geosetColors = new float[model.geosets.size()][]; this.geosetColors = new float[model.geosets.size()][];
for (int i = 0, l = model.geosets.size(); i < l; i++) { for (int i = 0, l = model.geosets.size(); i < l; i++) {
this.geosetColors[i] = new float[4]; this.geosetColors[i] = new float[4];
if (model.geosets.get(i).unselectable) {
this.hasAnyUnselectableMesh = true;
}
} }
this.layerAlphas = new float[model.layers.size()]; this.layerAlphas = new float[model.layers.size()];
@ -677,6 +682,17 @@ public class MdxComplexInstance extends ModelInstance {
return this; return this;
} }
/**
* Set the vertex color of this instance.
*/
public MdxComplexInstance setVertexColor(final Color color) {
this.vertexColor[0] = color.r;
this.vertexColor[1] = color.g;
this.vertexColor[2] = color.b;
this.vertexColor[3] = color.a;
return this;
}
public MdxComplexInstance setVertexAlpha(final float alpha) { public MdxComplexInstance setVertexAlpha(final float alpha) {
this.vertexColor[3] = alpha; this.vertexColor[3] = alpha;
@ -808,7 +824,7 @@ public class MdxComplexInstance extends ModelInstance {
intersected = true; intersected = true;
} }
} }
return intersected || intersectRayBounds(ray, intersection); return intersected || (!this.hasAnyUnselectableMesh && intersectRayBounds(ray, intersection));
} }
/** /**

View File

@ -1,5 +1,7 @@
package com.etheller.warsmash.viewer5.handlers.mdx; package com.etheller.warsmash.viewer5.handlers.mdx;
import com.etheller.warsmash.util.WarsmashConstants;
public class RibbonEmitter extends MdxEmitter<MdxComplexInstance, RibbonEmitterObject, Ribbon> { public class RibbonEmitter extends MdxEmitter<MdxComplexInstance, RibbonEmitterObject, Ribbon> {
public Ribbon first; public Ribbon first;
public Ribbon last; public Ribbon last;
@ -16,7 +18,8 @@ public class RibbonEmitter extends MdxEmitter<MdxComplexInstance, RibbonEmitterO
final RibbonEmitterObject emitterObject = this.emitterObject; final RibbonEmitterObject emitterObject = this.emitterObject;
// It doesn't make sense to emit more than 1 ribbon at the same time. // It doesn't make sense to emit more than 1 ribbon at the same time.
this.currentEmission = Math.min(this.currentEmission + (emitterObject.emissionRate * dt), 1); this.currentEmission = Math.min(this.currentEmission
+ (emitterObject.emissionRate * dt * WarsmashConstants.MODEL_DETAIL_PARTICLE_FACTOR), 1);
} }
} }

View File

@ -60,21 +60,30 @@ public final class UnitSound {
final UnitSound sound = new UnitSound(volume, pitch, pitchVariance, minDistance, maxDistance, distanceCutoff, final UnitSound sound = new UnitSound(volume, pitch, pitchVariance, minDistance, maxDistance, distanceCutoff,
looping); looping);
for (final String fileName : fileNames.split(",")) { for (final String fileName : fileNames.split(",")) {
String filePath = directoryBase + fileName; final String filePath = directoryBase + fileName;
final Sound newSound = createSound(dataSource, filePath);
if (newSound != null) {
sound.sounds.add(newSound);
}
}
return sound;
}
public static Sound createSound(final DataSource dataSource, String filePath) {
final int lastDotIndex = filePath.lastIndexOf('.'); final int lastDotIndex = filePath.lastIndexOf('.');
if (lastDotIndex != -1) { if (lastDotIndex != -1) {
filePath = filePath.substring(0, lastDotIndex); filePath = filePath.substring(0, lastDotIndex);
} }
Sound newSound = null;
if (dataSource.has(filePath + ".wav") || dataSource.has(filePath + ".flac")) { if (dataSource.has(filePath + ".wav") || dataSource.has(filePath + ".flac")) {
try { try {
sound.sounds.add(Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath + ".wav"))); newSound = Gdx.audio.newSound(new DataSourceFileHandle(dataSource, filePath + ".wav"));
} }
catch (final Exception exc) { catch (final Exception exc) {
exc.printStackTrace(); exc.printStackTrace();
} }
} }
} return newSound;
return sound;
} }
public UnitSound(final float volume, final float pitch, final float pitchVariation, final float minDistance, public UnitSound(final float volume, final float pitch, final float pitchVariation, final float minDistance,

View File

@ -91,6 +91,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderDestructable;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderDoodad; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderDoodad;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderEffect; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderEffect;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderItem; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderItem;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderSpellEffect;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnitTypeData; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnitTypeData;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget; import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderWidget;
@ -701,10 +702,36 @@ public class War3MapViewer extends AbstractMdxModelViewer {
@Override @Override
public CUnit createUnit(final CSimulation simulation, final War3ID typeId, final int playerIndex, public CUnit createUnit(final CSimulation simulation, final War3ID typeId, final int playerIndex,
final float x, final float y, final float facing) { final float x, final float y, final float facing) {
return createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, playerIndex, return (CUnit) createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, playerIndex,
playerIndex, (float) Math.toRadians(facing)); playerIndex, (float) Math.toRadians(facing));
} }
@Override
public CDestructable createDestructable(final War3ID typeId, final float x, final float y,
final float facing, final float scale, final int variation) {
return createDestructableZ(typeId, x, y, Math.max(getWalkableRenderHeight(x, y),
War3MapViewer.this.terrain.getGroundHeight(x, y)), facing, scale, variation);
}
@Override
public CDestructable createDestructableZ(final War3ID typeId, final float x, final float y,
final float z, final float facing, final float scale, final int variation) {
final MutableGameObject row = War3MapViewer.this.allObjectData.getDestructibles().get(typeId);
final float[] location3d = { x, y, z };
final float[] scale3d = { scale, scale, scale };
final RenderDestructable newDestructable = createNewDestructable(typeId, row, variation,
location3d, (float) Math.toRadians(facing), (short) 100, scale3d);
return newDestructable.getSimulationDestructable();
}
@Override
public CItem createItem(final CSimulation simulation, final War3ID typeId, final float x,
final float y) {
return (CItem) createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, -1, -1,
(float) Math.toRadians(
War3MapViewer.this.simulation.getGameplayConstants().getBuildingAngle()));
}
@Override @Override
public void spawnBuildingDeathEffect(final CUnit source) { public void spawnBuildingDeathEffect(final CUnit source) {
final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source); final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(source);
@ -777,10 +804,11 @@ public class War3MapViewer extends AbstractMdxModelViewer {
modelInstance.setTeamColor(unit.getPlayerIndex()); modelInstance.setTeamColor(unit.getPlayerIndex());
modelInstance.setLocation(renderUnit.location); modelInstance.setLocation(renderUnit.location);
modelInstance.setScene(War3MapViewer.this.worldScene); modelInstance.setScene(War3MapViewer.this.worldScene);
SequenceUtils.randomBirthSequence(modelInstance); final RenderSpellEffect renderAttackInstant = new RenderSpellEffect(modelInstance,
War3MapViewer.this.projectiles War3MapViewer.this,
.add(new RenderAttackInstant(modelInstance, War3MapViewer.this, (float) Math.toRadians(renderUnit.getSimulationUnit().getFacing()),
(float) Math.toRadians(renderUnit.getSimulationUnit().getFacing()))); RenderSpellEffect.DEFAULT_ANIMATION_QUEUE);
War3MapViewer.this.projectiles.add(renderAttackInstant);
} }
} }
@ -873,8 +901,6 @@ public class War3MapViewer extends AbstractMdxModelViewer {
loadSounds(); loadSounds();
this.terrain.createWaves(); this.terrain.createWaves();
loadLightsAndShading(tileset);
} }
public void spawnFxOnOrigin(final RenderUnit renderUnit, final String heroLevelUpArt) { public void spawnFxOnOrigin(final RenderUnit renderUnit, final String heroLevelUpArt) {
@ -941,16 +967,6 @@ public class War3MapViewer extends AbstractMdxModelViewer {
this.terrain.initShadows(); this.terrain.initShadows();
} }
private void loadLightsAndShading(final char tileset) {
// TODO this should be set by the war3map.j actually, not by the tileset, so the
// call to set day night models is just for testing to make the test look pretty
final Element defaultTerrainLights = this.worldEditData.get("TerrainLights");
final Element defaultUnitLights = this.worldEditData.get("UnitLights");
setDayNightModels(defaultTerrainLights.getField(Character.toString(tileset)),
defaultUnitLights.getField(Character.toString(tileset)));
}
private void loadDoodadsAndDestructibles(final Warcraft3MapObjectData modifications) throws IOException { private void loadDoodadsAndDestructibles(final Warcraft3MapObjectData modifications) throws IOException {
this.applyModificationFile(this.doodadsData, this.doodadMetaData, modifications.getDoodads(), this.applyModificationFile(this.doodadsData, this.doodadMetaData, modifications.getDoodads(),
WorldEditorDataType.DOODADS); WorldEditorDataType.DOODADS);
@ -960,105 +976,17 @@ public class War3MapViewer extends AbstractMdxModelViewer {
final War3MapDoo doo = this.mapMpq.readDoodads(); final War3MapDoo doo = this.mapMpq.readDoodads();
for (final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad : doo.getDoodads()) { for (final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad : doo.getDoodads()) {
WorldEditorDataType type = WorldEditorDataType.DOODADS; if ((doodad.getFlags() & 0x2) == 0) {
MutableGameObject row = modifications.getDoodads().get(doodad.getId()); continue;
if (row == null) {
row = modifications.getDestructibles().get(doodad.getId());
type = WorldEditorDataType.DESTRUCTIBLES;
}
if (row != null) {
BuildingShadow destructableShadow = null;
RemovablePathingMapInstance destructablePathing = null;
RemovablePathingMapInstance destructablePathingDeath = null;
String file = row.readSLKTag("file");
final int numVar = row.readSLKTagInt("numVar");
if (file.endsWith(".mdx") || file.endsWith(".mdl")) {
file = file.substring(0, file.length() - 4);
}
String fileVar = file;
file += ".mdx";
if (numVar > 1) {
fileVar += Math.min(doodad.getVariation(), numVar - 1);
}
fileVar += ".mdx";
final float maxPitch = row.readSLKTagFloat("maxPitch");
final float maxRoll = row.readSLKTagFloat("maxRoll");
if (type == WorldEditorDataType.DESTRUCTIBLES) {
final String shadowString = row.readSLKTag("shadow");
if ((shadowString != null) && (shadowString.length() > 0) && !"_".equals(shadowString)) {
destructableShadow = this.terrain.addShadow(shadowString, doodad.getLocation()[0],
doodad.getLocation()[1]);
}
final BufferedImage destructablePathingPixelMap = getDestructablePathingPixelMap(row);
if (destructablePathingPixelMap != null) {
destructablePathing = this.terrain.pathingGrid.createRemovablePathingOverlayTexture(
doodad.getLocation()[0], doodad.getLocation()[1],
(int) Math.toDegrees(doodad.getAngle()), destructablePathingPixelMap);
if (doodad.getLife() > 0) {
destructablePathing.add();
}
}
final BufferedImage destructablePathingDeathPixelMap = getDestructablePathingDeathPixelMap(row);
if (destructablePathingDeathPixelMap != null) {
destructablePathingDeath = this.terrain.pathingGrid.createRemovablePathingOverlayTexture(
doodad.getLocation()[0], doodad.getLocation()[1],
(int) Math.toDegrees(doodad.getAngle()), destructablePathingDeathPixelMap);
if (doodad.getLife() <= 0) {
destructablePathingDeath.add();
}
}
}
// First see if the model is local.
// Doodads referring to local models may have invalid variations, so if the
// variation doesn't exist, try without a variation.
String path;
if (this.mapMpq.has(fileVar)) {
path = fileVar;
}
else {
path = file;
}
MdxModel model;
if (this.mapMpq.has(path)) {
model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
}
else {
model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams);
}
if (type == WorldEditorDataType.DESTRUCTIBLES) {
final float x = doodad.getLocation()[0];
final float y = doodad.getLocation()[1];
final CDestructable simulationDestructable = this.simulation.createDestructable(row.getAlias(), x,
y, destructablePathing, destructablePathingDeath);
simulationDestructable.setLife(this.simulation,
simulationDestructable.getLife() * (doodad.getLife() / 100f));
final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, doodad, type,
maxPitch, maxRoll, doodad.getLife(), destructableShadow, simulationDestructable);
if (row.readSLKTagBoolean("walkable")) {
final float angle = doodad.getAngle();
final BoundingBox boundingBox = model.bounds.getBoundingBox();
final Rectangle renderDestructableBounds = getRotatedBoundingBox(x, y, angle, boundingBox);
System.out.println("ROTATED BOUNDS TO: " + renderDestructableBounds);
this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance,
renderDestructableBounds);
renderDestructable.walkableBounds = renderDestructableBounds;
}
this.widgets.add(renderDestructable);
this.destructableToRenderPeer.put(simulationDestructable, renderDestructable);
}
else {
this.doodads.add(new RenderDoodad(this, model, row, doodad, type, maxPitch, maxRoll));
}
} }
final War3ID doodadId = doodad.getId();
final int doodadVariation = doodad.getVariation();
final float[] location = doodad.getLocation();
final float facingRadians = doodad.getAngle();
final short lifePercent = doodad.getLife();
final float[] scale = doodad.getScale();
createDestructableOrDoodad(doodadId, modifications, doodadVariation, location, facingRadians, lifePercent,
scale);
} }
// Cliff/Terrain doodads. // Cliff/Terrain doodads.
@ -1124,6 +1052,125 @@ public class War3MapViewer extends AbstractMdxModelViewer {
this.anyReady = true; this.anyReady = true;
} }
private void createDoodad(final MutableGameObject row, final int doodadVariation, final float[] location,
final float facingRadians, final float[] scale) {
final MdxModel model = getDoodadModel(doodadVariation, row);
final float maxPitch = row.readSLKTagFloat("maxPitch");
final float maxRoll = row.readSLKTagFloat("maxRoll");
final float defScale = row.readSLKTagFloat("defScale");
final RenderDoodad renderDoodad = new RenderDoodad(this, model, row, location, scale, facingRadians, maxPitch,
maxRoll, defScale);
renderDoodad.instance.uniformScale(defScale);
this.doodads.add(renderDoodad);
}
private RenderDestructable createNewDestructable(final War3ID doodadId, final MutableGameObject row,
final int doodadVariation, final float[] location, final float facingRadians, final short lifePercent,
final float[] scale) {
BuildingShadow destructableShadow = null;
RemovablePathingMapInstance destructablePathing = null;
RemovablePathingMapInstance destructablePathingDeath = null;
final MdxModel model = getDoodadModel(doodadVariation, row);
final float maxPitch = row.readSLKTagFloat("maxPitch");
final float maxRoll = row.readSLKTagFloat("maxRoll");
final String shadowString = row.readSLKTag("shadow");
if ((shadowString != null) && (shadowString.length() > 0) && !"_".equals(shadowString)) {
destructableShadow = this.terrain.addShadow(shadowString, location[0], location[1]);
}
final BufferedImage destructablePathingPixelMap = getDestructablePathingPixelMap(row);
if (destructablePathingPixelMap != null) {
destructablePathing = this.terrain.pathingGrid.createRemovablePathingOverlayTexture(location[0],
location[1], (int) Math.toDegrees(facingRadians), destructablePathingPixelMap);
if (lifePercent > 0) {
destructablePathing.add();
}
}
final BufferedImage destructablePathingDeathPixelMap = getDestructablePathingDeathPixelMap(row);
if (destructablePathingDeathPixelMap != null) {
destructablePathingDeath = this.terrain.pathingGrid.createRemovablePathingOverlayTexture(location[0],
location[1], (int) Math.toDegrees(facingRadians), destructablePathingDeathPixelMap);
if (lifePercent <= 0) {
destructablePathingDeath.add();
}
}
final float x = location[0];
final float y = location[1];
final CDestructable simulationDestructable = this.simulation.internalCreateDestructable(row.getAlias(), x, y,
destructablePathing, destructablePathingDeath);
// Used to be this, but why: (float) Math.sqrt((scale[0]) * (scale[1]) *
// (scale[2]));
final float selectionScale = 1.0f;
simulationDestructable.setLife(this.simulation, simulationDestructable.getLife() * (lifePercent / 100f));
final RenderDestructable renderDestructable = new RenderDestructable(this, model, row, location, scale,
facingRadians, selectionScale, maxPitch, maxRoll, lifePercent, destructableShadow,
simulationDestructable);
if (row.readSLKTagBoolean("walkable")) {
final float angle = facingRadians;
final BoundingBox boundingBox = model.bounds.getBoundingBox();
final Rectangle renderDestructableBounds = getRotatedBoundingBox(x, y, angle, boundingBox);
this.walkableObjectsTree.add((MdxComplexInstance) renderDestructable.instance, renderDestructableBounds);
renderDestructable.walkableBounds = renderDestructableBounds;
}
this.widgets.add(renderDestructable);
this.destructableToRenderPeer.put(simulationDestructable, renderDestructable);
return renderDestructable;
}
private void createDestructableOrDoodad(final War3ID doodadId, final Warcraft3MapObjectData modifications,
final int doodadVariation, final float[] location, final float facingRadians, final short lifePercent,
final float[] scale) {
MutableGameObject row = modifications.getDoodads().get(doodadId);
if (row == null) {
row = modifications.getDestructibles().get(doodadId);
if (row != null) {
createNewDestructable(doodadId, row, doodadVariation, location, facingRadians, lifePercent, scale);
}
}
else {
createDoodad(row, doodadVariation, location, facingRadians, scale);
}
}
private MdxModel getDoodadModel(final int doodadVariation, final MutableGameObject row) {
String file = row.readSLKTag("file");
final int numVar = row.readSLKTagInt("numVar");
if (file.endsWith(".mdx") || file.endsWith(".mdl")) {
file = file.substring(0, file.length() - 4);
}
String fileVar = file;
file += ".mdx";
if (numVar > 1) {
fileVar += Math.min(doodadVariation, numVar - 1);
}
fileVar += ".mdx";
// First see if the model is local.
// Doodads referring to local models may have invalid variations, so if the
// variation doesn't exist, try without a variation.
String path;
if (this.mapMpq.has(fileVar)) {
path = fileVar;
}
else {
path = file;
}
MdxModel model;
if (this.mapMpq.has(path)) {
model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
}
else {
model = (MdxModel) this.load(fileVar, this.mapPathSolver, this.solverParams);
}
return model;
}
private Rectangle getRotatedBoundingBox(final float x, final float y, final float angle, private Rectangle getRotatedBoundingBox(final float x, final float y, final float angle,
final BoundingBox boundingBox) { final BoundingBox boundingBox) {
final float x1 = boundingBox.min.x; final float x1 = boundingBox.min.x;
@ -1170,7 +1217,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
this.soundsetNameToSoundset = new HashMap<>(); this.soundsetNameToSoundset = new HashMap<>();
if (this.dataSource.has("war3mapUnits.doo")) { if (this.dataSource.has("war3mapUnits.doo") && WarsmashConstants.LOAD_UNITS_FROM_WORLDEDIT_DATA) {
final War3MapUnitsDoo dooFile = mpq.readUnits(); final War3MapUnitsDoo dooFile = mpq.readUnits();
// Collect the units and items data. // Collect the units and items data.
@ -1184,9 +1231,10 @@ public class War3MapViewer extends AbstractMdxModelViewer {
final float unitAngle = unit.getAngle(); final float unitAngle = unit.getAngle();
final int editorConfigHitPointPercent = unit.getHitpoints(); final int editorConfigHitPointPercent = unit.getHitpoints();
final CUnit unitCreated = createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, final CWidget widgetCreated = createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex,
customTeamColor, unitAngle); customTeamColor, unitAngle);
if (unitCreated != null) { if (widgetCreated instanceof CUnit) {
final CUnit unitCreated = (CUnit) widgetCreated;
if (editorConfigHitPointPercent > 0) { if (editorConfigHitPointPercent > 0) {
unitCreated.setLife(this.simulation, unitCreated.setLife(this.simulation,
unitCreated.getMaximumLife() * (editorConfigHitPointPercent / 100f)); unitCreated.getMaximumLife() * (editorConfigHitPointPercent / 100f));
@ -1205,7 +1253,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
this.anyReady = true; this.anyReady = true;
} }
private CUnit createNewUnit(final Warcraft3MapObjectData modifications, final War3ID unitId, float unitX, private CWidget createNewUnit(final Warcraft3MapObjectData modifications, final War3ID unitId, float unitX,
float unitY, final float unitZ, final int playerIndex, int customTeamColor, final float unitAngle) { float unitY, final float unitZ, final int playerIndex, int customTeamColor, final float unitAngle) {
UnitSoundset soundset = null; UnitSoundset soundset = null;
MutableGameObject row = null; MutableGameObject row = null;
@ -1215,7 +1263,6 @@ public class War3MapViewer extends AbstractMdxModelViewer {
Splat buildingUberSplat = null; Splat buildingUberSplat = null;
SplatMover buildingUberSplatDynamicIngame = null; SplatMover buildingUberSplatDynamicIngame = null;
BufferedImage buildingPathingPixelMap = null; BufferedImage buildingPathingPixelMap = null;
final float unitVertexScale = 1.0f;
RemovablePathingMapInstance pathingInstance = null; RemovablePathingMapInstance pathingInstance = null;
BuildingShadow buildingShadowInstance = null; BuildingShadow buildingShadowInstance = null;
@ -1421,7 +1468,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
} }
else { else {
final CItem simulationItem = this.simulation.createItem(row.getAlias(), unitX, unitY); final CItem simulationItem = this.simulation.internalCreateItem(row.getAlias(), unitX, unitY);
final RenderItem renderItem = new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset, final RenderItem renderItem = new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset,
portraitModel, simulationItem); portraitModel, simulationItem);
this.widgets.add(renderItem); this.widgets.add(renderItem);
@ -1438,6 +1485,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
if (unitShadowSplatDynamicIngame != null) { if (unitShadowSplatDynamicIngame != null) {
renderItem.shadow = unitShadowSplatDynamicIngame; renderItem.shadow = unitShadowSplatDynamicIngame;
} }
return simulationItem;
} }
} }
else { else {
@ -1671,7 +1719,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
if (!localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.PASSIVE)) { if (!localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.PASSIVE)) {
allyKey = "e:"; allyKey = "e:";
} }
else if (localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.HELP_REQUEST)) { else if (localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.SHARED_CONTROL)) {
allyKey = "f:"; allyKey = "f:";
} }
} }
@ -1780,7 +1828,7 @@ public class War3MapViewer extends AbstractMdxModelViewer {
if (!localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.PASSIVE)) { if (!localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.PASSIVE)) {
allyKey = "e:"; allyKey = "e:";
} }
else if (localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.HELP_REQUEST)) { else if (localPlayer.hasAlliance(selectedUnitPlayerIndex, CAllianceType.SHARED_CONTROL)) {
allyKey = "f:"; allyKey = "f:";
} }
} }
@ -2246,4 +2294,32 @@ public class War3MapViewer extends AbstractMdxModelViewer {
public void setGameTurnManager(final GameTurnManager gameTurnManager) { public void setGameTurnManager(final GameTurnManager gameTurnManager) {
this.gameTurnManager = gameTurnManager; this.gameTurnManager = gameTurnManager;
} }
public RenderEffect addSpecialEffectTarget(final String modelName, final CWidget targetWidget,
final String attachPointName) {
if (targetWidget instanceof CUnit) {
final RenderUnit renderUnit = War3MapViewer.this.unitToRenderPeer.get(targetWidget);
final MdxModel spawnedEffectModel = (MdxModel) load(mdx(modelName), PathSolver.DEFAULT, null);
if (spawnedEffectModel != null) {
final MdxComplexInstance modelInstance = (MdxComplexInstance) spawnedEffectModel.addInstance();
modelInstance.setTeamColor(renderUnit.playerIndex);
modelInstance.setLocation(renderUnit.location);
modelInstance.setScene(War3MapViewer.this.worldScene);
final RenderSpellEffect renderAttackInstant = new RenderSpellEffect(modelInstance, War3MapViewer.this,
(float) Math.toRadians(renderUnit.getSimulationUnit().getFacing()),
RenderSpellEffect.DEFAULT_ANIMATION_QUEUE);
War3MapViewer.this.projectiles.add(renderAttackInstant);
return renderAttackInstant;
}
}
else if (targetWidget instanceof CItem) {
// TODO this is stupid api, who would do this?
throw new UnsupportedOperationException("API for addSpecialEffectTarget() on item is NYI");
}
else if (targetWidget instanceof CDestructable) {
// TODO this is stupid api, who would do this?
throw new UnsupportedOperationException("API for addSpecialEffectTarget() on destructable is NYI");
}
return null;
}
} }

View File

@ -3,6 +3,7 @@ package com.etheller.warsmash.viewer5.handlers.w3x.camera;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input; import com.badlogic.gdx.Input;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.viewer5.handlers.w3x.rendersim.RenderUnit;
public final class GameCameraManager extends CameraManager { public final class GameCameraManager extends CameraManager {
private static final CameraRates INFINITE_CAMERA_RATES = new CameraRates(Float.POSITIVE_INFINITY, private static final CameraRates INFINITE_CAMERA_RATES = new CameraRates(Float.POSITIVE_INFINITY,
@ -11,8 +12,13 @@ public final class GameCameraManager extends CameraManager {
private final CameraPreset[] presets; private final CameraPreset[] presets;
private final CameraRates cameraRates; private final CameraRates cameraRates;
public final CameraPanControls cameraPanControls; public final CameraPanControls cameraPanControls;
private Rectangle cameraBounds;
private int currentPreset = 0; private int currentPreset = 0;
private float fov; private float fov;
private RenderUnit targetControllerUnit;
private float targetControllerXOffset;
private float targetControllerYOffset;
private boolean targetControllerInheritOrientation;
public GameCameraManager(final CameraPreset[] presets, final CameraRates cameraRates) { public GameCameraManager(final CameraPreset[] presets, final CameraRates cameraRates) {
this.presets = presets; this.presets = presets;
@ -20,6 +26,10 @@ public final class GameCameraManager extends CameraManager {
this.cameraPanControls = new CameraPanControls(); this.cameraPanControls = new CameraPanControls();
} }
public void setCameraBounds(final Rectangle cameraBounds) {
this.cameraBounds = cameraBounds;
}
@Override @Override
public void updateCamera() { public void updateCamera() {
final CameraPreset cameraPreset = this.presets[this.currentPreset]; final CameraPreset cameraPreset = this.presets[this.currentPreset];
@ -30,8 +40,16 @@ public final class GameCameraManager extends CameraManager {
private void updateCamera(final CameraPreset cameraPreset, final CameraRates cameraRate) { private void updateCamera(final CameraPreset cameraPreset, final CameraRates cameraRate) {
this.quatHeap2.idt(); this.quatHeap2.idt();
this.quatHeap.idt(); this.quatHeap.idt();
this.horizontalAngle = applyAtRate(this.horizontalAngle, (float) Math.toRadians( final float newHorizontalAngle;
cameraPreset.getRotation(this.cameraPanControls.insertDown, this.cameraPanControls.deleteDown) - 90), if (this.targetControllerInheritOrientation && (this.targetControllerUnit != null)) {
newHorizontalAngle = this.targetControllerUnit.getFacing();
}
else {
newHorizontalAngle = (float) Math.toRadians(
cameraPreset.getRotation(this.cameraPanControls.insertDown, this.cameraPanControls.deleteDown)
- 90);
}
this.horizontalAngle = applyAtRate(this.horizontalAngle, newHorizontalAngle,
(float) Math.toRadians(cameraRate.rotation * 3)); (float) Math.toRadians(cameraRate.rotation * 3));
this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle); this.quatHeap.setFromAxisRad(0, 0, 1, this.horizontalAngle);
this.distance = applyAtRate(this.distance, Math.max(1200, cameraPreset.getDistance()), cameraRate.distance); this.distance = applyAtRate(this.distance, Math.max(1200, cameraPreset.getDistance()), cameraRate.distance);
@ -69,6 +87,11 @@ public final class GameCameraManager extends CameraManager {
} }
public void applyVelocity(final float deltaTime, boolean up, boolean down, boolean left, boolean right) { public void applyVelocity(final float deltaTime, boolean up, boolean down, boolean left, boolean right) {
if (this.targetControllerUnit != null) {
this.target.x = this.targetControllerUnit.getX() + this.targetControllerXOffset;
this.target.y = this.targetControllerUnit.getY() + this.targetControllerYOffset;
}
else {
final float velocityX; final float velocityX;
final float velocityY; final float velocityY;
up |= this.cameraPanControls.up; up |= this.cameraPanControls.up;
@ -104,6 +127,21 @@ public final class GameCameraManager extends CameraManager {
velocityX = 0; velocityX = 0;
} }
this.target.add(velocityX * deltaTime, velocityY * deltaTime, 0); this.target.add(velocityX * deltaTime, velocityY * deltaTime, 0);
}
if (this.cameraBounds != null) {
if (this.target.x < this.cameraBounds.x) {
this.target.x = this.cameraBounds.x;
}
if (this.target.y < this.cameraBounds.y) {
this.target.y = this.cameraBounds.y;
}
if (this.target.x > (this.cameraBounds.x + this.cameraBounds.width)) {
this.target.x = this.cameraBounds.x + this.cameraBounds.width;
}
if (this.target.y > (this.cameraBounds.y + this.cameraBounds.height)) {
this.target.y = this.cameraBounds.y + this.cameraBounds.height;
}
}
} }
@ -176,4 +214,17 @@ public final class GameCameraManager extends CameraManager {
} }
return false; return false;
} }
public Rectangle getCameraBounds() {
return this.cameraBounds;
}
public void setTargetController(final RenderUnit targetControllerUnit, final float xoffset, final float yoffset,
final boolean inheritOrientation) {
this.targetControllerUnit = targetControllerUnit;
this.targetControllerXOffset = xoffset;
this.targetControllerYOffset = yoffset;
this.targetControllerInheritOrientation = inheritOrientation;
}
} }

View File

@ -252,6 +252,24 @@ public class PathingGrid {
return (short) (this.pathingGrid[index] | this.dynamicPathingOverlay[index]); return (short) (this.pathingGrid[index] | this.dynamicPathingOverlay[index]);
} }
public void setCellPathing(final int cellX, final int cellY, final short pathingValue) {
final int index = (cellY * this.pathingGridSizes[0]) + cellX;
this.pathingGrid[index] = pathingValue;
}
public void setCellBlighted(final int cellX, final int cellY, final boolean blighted) {
if (blighted) {
setCellPathing(cellX, cellY, (short) (getCellPathing(cellX, cellY) | (short) PathingFlags.BLIGHTED));
}
else {
setCellPathing(cellX, cellY, (short) (getCellPathing(cellX, cellY) & (short) ~PathingFlags.BLIGHTED));
}
}
public void setBlighted(final float x, final float y, final boolean blighted) {
setCellBlighted(getCellX(x), getCellY(y), blighted);
}
public boolean isPathable(final float x, final float y, final PathingType pathingType) { public boolean isPathable(final float x, final float y, final PathingType pathingType) {
return !PathingFlags.isPathingFlag(getPathing(x, y), pathingType.preventionFlag); return !PathingFlags.isPathingFlag(getPathing(x, y), pathingType.preventionFlag);
} }

View File

@ -406,6 +406,7 @@ public class Terrain {
this.centerOffset = w3eFile.getCenterOffset(); this.centerOffset = w3eFile.getCenterOffset();
this.uberSplatModels = new LinkedHashMap<>(); this.uberSplatModels = new LinkedHashMap<>();
this.uberSplatModelsList = new ArrayList<>(); this.uberSplatModelsList = new ArrayList<>();
this.defaultCameraBounds = w3iFile.getCameraBounds();
this.mapBounds = w3iFile.getCameraBoundsComplements(); this.mapBounds = w3iFile.getCameraBoundsComplements();
this.shaderMapBounds = new float[] { (this.mapBounds[0] * 128.0f) + this.centerOffset[0], this.shaderMapBounds = new float[] { (this.mapBounds[0] * 128.0f) + this.centerOffset[0],
(this.mapBounds[2] * 128.0f) + this.centerOffset[1], (this.mapBounds[2] * 128.0f) + this.centerOffset[1],
@ -724,7 +725,7 @@ public class Terrain {
System.out.println("cliff: " + this.corners[x][y].cliff); System.out.println("cliff: " + this.corners[x][y].cliff);
} }
private void updateGroundTextures(final Rectangle area) { public void updateGroundTextures(final Rectangle area) {
final Rectangle adjusted = new Rectangle(area.x - 1, area.y - 1, area.width + 2, area.height + 2); final Rectangle adjusted = new Rectangle(area.x - 1, area.y - 1, area.width + 2, area.height + 2);
final Rectangle updateArea = new Rectangle(); final Rectangle updateArea = new Rectangle();
Intersector.intersectRectangles(new Rectangle(0, 0, this.columns - 1, this.rows - 1), adjusted, updateArea); Intersector.intersectRectangles(new Rectangle(0, 0, this.columns - 1, this.rows - 1), adjusted, updateArea);
@ -1343,6 +1344,7 @@ public class Terrain {
public PathingGrid pathingGrid; public PathingGrid pathingGrid;
private final Rectangle shaderMapBoundsRectangle; private final Rectangle shaderMapBoundsRectangle;
private final Rectangle entireMapRectangle; private final Rectangle entireMapRectangle;
private final float[] defaultCameraBounds;
private static final class UnloadedTexture { private static final class UnloadedTexture {
private final int width; private final int width;
@ -1390,6 +1392,19 @@ public class Terrain {
return 0; return 0;
} }
public RenderCorner getCorner(final float x, final float y) {
final float userCellSpaceX = (x - this.centerOffset[0]) / 128.0f;
final float userCellSpaceY = (y - this.centerOffset[1]) / 128.0f;
final int cellX = (int) userCellSpaceX;
final int cellY = (int) userCellSpaceY;
if ((cellX >= 0) && (cellX < (this.mapSize[0] - 1)) && (cellY >= 0) && (cellY < (this.mapSize[1] - 1))) {
return this.corners[cellX][cellY];
}
return null;
}
public float getWaterHeight(final float x, final float y) { public float getWaterHeight(final float x, final float y) {
final float userCellSpaceX = (x - this.centerOffset[0]) / 128.0f; final float userCellSpaceX = (x - this.centerOffset[0]) / 128.0f;
final float userCellSpaceY = (y - this.centerOffset[1]) / 128.0f; final float userCellSpaceY = (y - this.centerOffset[1]) / 128.0f;
@ -1564,4 +1579,8 @@ public class Terrain {
return (char) ('A' + layerHeightOffset); return (char) ('A' + layerHeightOffset);
} }
} }
public float[] getDefaultCameraBounds() {
return this.defaultCameraBounds;
}
} }

View File

@ -1,13 +1,15 @@
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim; package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
import java.util.EnumSet;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel; import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag; import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.SecondaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils; import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover; import com.etheller.warsmash.viewer5.handlers.w3x.SplatModel.SplatMover;
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer; import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
@ -34,10 +36,10 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget {
private String replaceableTextureFile; private String replaceableTextureFile;
public RenderDestructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row, public RenderDestructable(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type, final float[] location3D, final float[] scale3D, final float facingRadians, final float selectionScale,
final float maxPitch, final float maxRoll, final float life, final BuildingShadow destructableShadow, final float maxPitch, final float maxRoll, final float life, final BuildingShadow destructableShadow,
final CDestructable simulationDestructable) { final CDestructable simulationDestructable) {
super(map, model, row, doodad, type, maxPitch, maxRoll); super(map, model, row, location3D, scale3D, facingRadians, maxPitch, maxRoll, selectionScale);
this.life = simulationDestructable.getLife(); this.life = simulationDestructable.getLife();
this.destructableShadow = destructableShadow; this.destructableShadow = destructableShadow;
this.simulationDestructable = simulationDestructable; this.simulationDestructable = simulationDestructable;
@ -187,4 +189,44 @@ public class RenderDestructable extends RenderDoodad implements RenderWidget {
public SplatMover getSelectionCircle() { public SplatMover getSelectionCircle() {
return this.selectionCircle; return this.selectionCircle;
} }
public CDestructable getSimulationDestructable() {
return this.simulationDestructable;
}
public UnitAnimationListenerImpl getUnitAnimationListenerImpl() {
return this.unitAnimationListenerImpl;
}
public void setAnimation(final String sequence) {
final EnumSet<PrimaryTag> primaryTags = EnumSet.noneOf(PrimaryTag.class);
PrimaryTag bestPrimaryTag = null;
final EnumSet<SecondaryTag> secondaryTags = EnumSet.noneOf(SecondaryTag.class);
TokenLoop: for (final String token : sequence.split("\\s+")) {
final String upperCaseToken = token.toUpperCase();
for (final PrimaryTag primaryTag : PrimaryTag.values()) {
if (upperCaseToken.equals(primaryTag.name())) {
primaryTags.add(primaryTag);
bestPrimaryTag = primaryTag;
continue TokenLoop;
}
}
for (final SecondaryTag secondaryTag : SecondaryTag.values()) {
if (upperCaseToken.equals(secondaryTag.name())) {
secondaryTags.add(secondaryTag);
continue TokenLoop;
}
}
break;
}
this.dead = this.simulationDestructable.isDead();
this.life = this.simulationDestructable.getLife();
this.unitAnimationListenerImpl.playAnimation(true, bestPrimaryTag, secondaryTags, 1.0f, true);
}
public void notifyLifeRestored() {
this.dead = this.simulationDestructable.isDead();
this.life = this.simulationDestructable.getLife();
this.unitAnimationListenerImpl.playAnimation(true, getAnimation(), SequenceUtils.EMPTY, 1.0f, true);
}
} }

View File

@ -2,7 +2,6 @@ package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
import com.badlogic.gdx.math.Quaternion; import com.badlogic.gdx.math.Quaternion;
import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject; import com.etheller.warsmash.units.manager.MutableObjectData.MutableGameObject;
import com.etheller.warsmash.units.manager.MutableObjectData.WorldEditorDataType;
import com.etheller.warsmash.util.RenderMathUtils; import com.etheller.warsmash.util.RenderMathUtils;
import com.etheller.warsmash.viewer5.ModelInstance; import com.etheller.warsmash.viewer5.ModelInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance; import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
@ -23,8 +22,8 @@ public class RenderDoodad {
protected float selectionScale; protected float selectionScale;
public RenderDoodad(final War3MapViewer map, final MdxModel model, final MutableGameObject row, public RenderDoodad(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.doo.Doodad doodad, final WorldEditorDataType type, final float[] location3D, final float[] scale3D, final float facingRadians, final float maxPitch,
final float maxPitch, final float maxRoll) { final float maxRoll, final float selectionScale) {
this.maxPitch = maxPitch; this.maxPitch = maxPitch;
this.maxRoll = maxRoll; this.maxRoll = maxRoll;
final boolean isSimple = row.readSLKTagBoolean("lightweight"); final boolean isSimple = row.readSLKTagBoolean("lightweight");
@ -38,7 +37,7 @@ public class RenderDoodad {
((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP); ((MdxComplexInstance) instance).setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
} }
instance.move(doodad.getLocation()); instance.move(location3D);
// TODO: the following pitch/roll system is a heuristic, and we probably want to // TODO: the following pitch/roll system is a heuristic, and we probably want to
// revisit it later. // revisit it later.
// Specifically, I was pretty convinced that whichever is applied first // Specifically, I was pretty convinced that whichever is applied first
@ -46,10 +45,9 @@ public class RenderDoodad {
// to find the angle used for the other of the two // to find the angle used for the other of the two
// (instead of measuring down from an imaginary flat ground plane, as we do // (instead of measuring down from an imaginary flat ground plane, as we do
// currently). // currently).
final float facingRadians = doodad.getAngle();
float pitch, roll; float pitch, roll;
this.x = doodad.getLocation()[0]; this.x = location3D[0];
this.y = doodad.getLocation()[1]; this.y = location3D[1];
{ {
if (!map.terrain.inPlayableArea(this.x, this.y)) { if (!map.terrain.inPlayableArea(this.x, this.y)) {
((MdxComplexInstance) instance).setVertexColor(VERTEX_COLOR_BLACK); ((MdxComplexInstance) instance).setVertexColor(VERTEX_COLOR_BLACK);
@ -76,16 +74,8 @@ public class RenderDoodad {
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch)); instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Y, -pitch));
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll)); instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_X, roll));
// instance.rotate(new Quaternion().setEulerAnglesRad(facingRadians, 0, 0)); // instance.rotate(new Quaternion().setEulerAnglesRad(facingRadians, 0, 0));
final float[] scale = doodad.getScale(); instance.scale(scale3D);
instance.scale(scale); this.selectionScale = selectionScale;
if (type == WorldEditorDataType.DOODADS) {
final float defScale = row.readSLKTagFloat("defScale");
instance.uniformScale(defScale);
this.selectionScale = defScale;
}
else {
this.selectionScale = (float) Math.sqrt((scale[0]) * (scale[1]) * (scale[2]));
}
instance.setScene(map.worldScene); instance.setScene(map.worldScene);
this.instance = instance; this.instance = instance;

View File

@ -92,6 +92,7 @@ public class RenderItem implements RenderWidget {
this.instance.show(); this.instance.show();
if (this.shadow != null) { if (this.shadow != null) {
this.shadow.show(map.terrain.centerOffset); this.shadow.show(map.terrain.centerOffset);
this.shadow.setLocation(this.location[0], this.location[1], map.terrain.centerOffset);
} }
} }
} }
@ -111,12 +112,10 @@ public class RenderItem implements RenderWidget {
final float prevY = this.location[1]; final float prevY = this.location[1];
final float simulationX = this.simulationItem.getX(); final float simulationX = this.simulationItem.getX();
final float simulationY = this.simulationItem.getY(); final float simulationY = this.simulationItem.getY();
final float simDx = simulationX - this.location[0]; final float dx = simulationX - prevX;
final float simDy = simulationY - this.location[1]; final float dy = simulationY - prevY;
this.location[0] = simulationX; this.location[0] = simulationX;
this.location[1] = simulationY; this.location[1] = simulationY;
final float dx = this.location[0] - prevX;
final float dy = this.location[1] - prevY;
final float groundHeight; final float groundHeight;
// land units will have their feet pass under the surface of the water, so items // land units will have their feet pass under the surface of the water, so items
// here are in the same place // here are in the same place
@ -150,7 +149,7 @@ public class RenderItem implements RenderWidget {
@Override @Override
public float getSelectionScale() { public float getSelectionScale() {
return 1.0f; return 64.0f;
} }
@Override @Override
@ -202,4 +201,8 @@ public class RenderItem implements RenderWidget {
public SplatMover getSelectionCircle() { public SplatMover getSelectionCircle() {
return this.selectionCircle; return this.selectionCircle;
} }
public CItem getSimulationItem() {
return this.simulationItem;
}
} }

View File

@ -0,0 +1,53 @@
package com.etheller.warsmash.viewer5.handlers.w3x.rendersim;
import java.util.List;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxModel;
import com.etheller.warsmash.viewer5.handlers.mdx.Sequence;
import com.etheller.warsmash.viewer5.handlers.mdx.SequenceLoopMode;
import com.etheller.warsmash.viewer5.handlers.w3x.AnimationTokens.PrimaryTag;
import com.etheller.warsmash.viewer5.handlers.w3x.IndexedSequence;
import com.etheller.warsmash.viewer5.handlers.w3x.SequenceUtils;
import com.etheller.warsmash.viewer5.handlers.w3x.War3MapViewer;
public class RenderSpellEffect implements RenderEffect {
public static final PrimaryTag[] DEFAULT_ANIMATION_QUEUE = { PrimaryTag.BIRTH, PrimaryTag.STAND, PrimaryTag.DEATH };
private final MdxComplexInstance modelInstance;
private final PrimaryTag[] animationQueue;
private int animationQueueIndex;
private final List<Sequence> sequences;
public RenderSpellEffect(final MdxComplexInstance modelInstance, final War3MapViewer war3MapViewer, final float yaw,
final PrimaryTag[] animationQueue) {
this.modelInstance = modelInstance;
this.animationQueue = animationQueue;
final MdxModel model = (MdxModel) this.modelInstance.model;
this.sequences = model.getSequences();
this.modelInstance.setSequenceLoopMode(SequenceLoopMode.NEVER_LOOP);
this.modelInstance.localRotation.setFromAxisRad(0, 0, 1, yaw);
this.modelInstance.sequenceEnded = true;
playNextAnimation();
}
@Override
public boolean updateAnimations(final War3MapViewer war3MapViewer, final float deltaTime) {
playNextAnimation();
final boolean everythingDone = this.animationQueueIndex >= this.animationQueue.length;
if (everythingDone) {
war3MapViewer.worldScene.removeInstance(this.modelInstance);
}
return everythingDone;
}
private void playNextAnimation() {
while (this.modelInstance.sequenceEnded && (this.animationQueueIndex < this.animationQueue.length)) {
final IndexedSequence sequence = SequenceUtils.selectSequence(this.animationQueue[this.animationQueueIndex],
SequenceUtils.EMPTY, this.sequences, true);
if ((sequence != null) && (sequence.index != -1)) {
this.modelInstance.setSequence(sequence.index);
}
this.animationQueueIndex++;
}
}
}

View File

@ -563,4 +563,17 @@ public class RenderUnit implements RenderWidget {
return this.selectionCircle; return this.selectionCircle;
} }
public boolean groupsWith(final RenderUnit selectedUnit) {
return this.simulationUnit.getUnitType() == selectedUnit.getSimulationUnit().getUnitType();
}
public void setPlayerColor(final int ordinal) {
this.playerIndex = ordinal;
getInstance().setTeamColor(ordinal);
}
public float getFacing() {
return this.facing;
}
} }

View File

@ -29,6 +29,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbi
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade.CAbilityUpgrade;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
@ -238,6 +239,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
final int autoCastOrderId, final boolean autoCastActive, final boolean menuButton, int goldCost, final int autoCastOrderId, final boolean autoCastActive, final boolean menuButton, int goldCost,
int lumberCost, int foodCost) { int lumberCost, int foodCost) {
ability.checkCanUse(this.game, this.unit, orderId, this.previewCallback.reset()); ability.checkCanUse(this.game, this.unit, orderId, this.previewCallback.reset());
if (!this.previewCallback.techtreeMaxReached) {
final boolean active = ((this.unit.getCurrentBehavior() != null) final boolean active = ((this.unit.getCurrentBehavior() != null)
&& (orderId == this.unit.getCurrentBehavior().getHighlightOrderId())); && (orderId == this.unit.getCurrentBehavior().getHighlightOrderId()));
final boolean disabled = ((ability != null) && ability.isDisabled()) || this.previewCallback.disabled; final boolean disabled = ((ability != null) && ability.isDisabled()) || this.previewCallback.disabled;
@ -256,6 +258,7 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
autoCastOrderId, active, autoCastActive, menuButton, toolTip, uberTip, iconUI.getHotkey(), goldCost, autoCastOrderId, active, autoCastActive, menuButton, toolTip, uberTip, iconUI.getHotkey(), goldCost,
lumberCost, foodCost); lumberCost, foodCost);
} }
}
@Override @Override
public Void accept(final CAbilityBuildInProgress ability) { public Void accept(final CAbilityBuildInProgress ability) {
@ -332,12 +335,37 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
return null; return null;
} }
@Override
public Void accept(final CAbilityUpgrade ability) {
if ((this.menuBaseOrderId == 0) && ability.isIconShowing()) {
for (final War3ID unitType : ability.getUpgradesTo()) {
final IconUI unitUI = this.abilityDataUI.getUnitUI(unitType);
if (unitUI != null) {
final CUnitType simulationUnitType = this.game.getUnitData().getUnitType(unitType);
addCommandButton(ability, unitUI, ability.getHandleId(), unitType.getValue(), 0, false, false,
simulationUnitType.getGoldCost(), simulationUnitType.getLumberCost(),
simulationUnitType.getFoodUsed());
}
}
if (this.unit.getBuildQueueTypes()[0] != null) {
if (!this.hasCancel) {
this.hasCancel = true;
addCommandButton(ability, this.abilityDataUI.getCancelTrainUI(), ability.getHandleId(),
OrderIds.cancel, 0, false, false);
}
}
}
return null;
}
private final class CommandCardActivationReceiverPreviewCallback implements AbilityActivationReceiver { private final class CommandCardActivationReceiverPreviewCallback implements AbilityActivationReceiver {
private boolean disabled; private boolean disabled;
private boolean techtreeMaxReached;
private final StringBuilder requirementsTextBuilder = new StringBuilder(); private final StringBuilder requirementsTextBuilder = new StringBuilder();
public CommandCardActivationReceiverPreviewCallback reset() { public CommandCardActivationReceiverPreviewCallback reset() {
this.disabled = false; this.disabled = false;
this.techtreeMaxReached = false;
this.requirementsTextBuilder.setLength(0); this.requirementsTextBuilder.setLength(0);
return this; return this;
} }
@ -374,6 +402,11 @@ public class CommandCardPopulatingAbilityVisitor implements CAbilityVisitor<Void
this.requirementsTextBuilder.append("|n"); this.requirementsTextBuilder.append("|n");
} }
@Override
public void techtreeMaximumReached() {
this.techtreeMaxReached = true;
}
@Override @Override
public void casterMovementDisabled() { public void casterMovementDisabled() {

View File

@ -61,17 +61,34 @@ public class CDestructable extends CWidget {
@Override @Override
public void damage(final CSimulation simulation, final CUnit source, final CAttackType attackType, public void damage(final CSimulation simulation, final CUnit source, final CAttackType attackType,
final String weaponType, final float damage) { final String weaponType, final float damage) {
if (isInvulnerable()) {
return;
}
final boolean wasDead = isDead(); final boolean wasDead = isDead();
this.life -= damage; this.life -= damage;
simulation.destructableDamageEvent(this, weaponType, this.destType.getArmorType());
if (!wasDead && isDead()) { if (!wasDead && isDead()) {
kill(simulation);
}
}
private void kill(final CSimulation simulation) {
if (this.pathingInstance != null) { if (this.pathingInstance != null) {
this.pathingInstance.remove(); this.pathingInstance.remove();
} }
if (this.pathingInstanceDeath != null) { if (this.pathingInstanceDeath != null) {
this.pathingInstanceDeath.add(); this.pathingInstanceDeath.add();
} }
fireDeathEvents(simulation);
}
@Override
public void setLife(final CSimulation simulation, final float life) {
final boolean wasDead = isDead();
super.setLife(simulation, life);
if (isDead() && !wasDead) {
kill(simulation);
} }
simulation.destructableDamageEvent(this, weaponType, this.destType.getArmorType());
} }
@Override @Override
@ -80,10 +97,12 @@ public class CDestructable extends CWidget {
if (targetsAllowed.containsAll(this.destType.getTargetedAs())) { if (targetsAllowed.containsAll(this.destType.getTargetedAs())) {
if (isDead()) { if (isDead()) {
return targetsAllowed.contains(CTargetType.DEAD); return targetsAllowed.contains(CTargetType.DEAD);
} else { }
else {
return !targetsAllowed.contains(CTargetType.DEAD) || targetsAllowed.contains(CTargetType.ALIVE); return !targetsAllowed.contains(CTargetType.DEAD) || targetsAllowed.contains(CTargetType.ALIVE);
} }
} else { }
else {
System.err.println("Not targeting because " + targetsAllowed + " does not contain all of " System.err.println("Not targeting because " + targetsAllowed + " does not contain all of "
+ this.destType.getTargetedAs()); + this.destType.getTargetedAs());
} }

View File

@ -13,6 +13,7 @@ public class CItem extends CWidget {
private final War3ID typeId; private final War3ID typeId;
private final CItemType itemType; private final CItemType itemType;
private boolean hidden; private boolean hidden;
private boolean invulnerable;
public CItem(final int handleId, final float x, final float y, final float life, final War3ID typeId, public CItem(final int handleId, final float x, final float y, final float life, final War3ID typeId,
final CItemType itemTypeInstance) { final CItemType itemTypeInstance) {
@ -34,8 +35,15 @@ public class CItem extends CWidget {
@Override @Override
public void damage(final CSimulation simulation, final CUnit source, final CAttackType attackType, public void damage(final CSimulation simulation, final CUnit source, final CAttackType attackType,
final String weaponType, final float damage) { final String weaponType, final float damage) {
if (this.invulnerable) {
return;
}
final boolean wasDead = isDead();
this.life -= damage; this.life -= damage;
simulation.itemDamageEvent(this, weaponType, this.itemType.getArmorType()); simulation.itemDamageEvent(this, weaponType, this.itemType.getArmorType());
if (isDead() && !wasDead) {
fireDeathEvents(simulation);
}
} }
@Override @Override
@ -75,7 +83,7 @@ public class CItem extends CWidget {
@Override @Override
public float getMaxLife() { public float getMaxLife() {
return itemType.getMaxLife(); return this.itemType.getMaxLife();
} }
public void setPointAndCheckUnstuck(final float newX, final float newY, final CSimulation game) { public void setPointAndCheckUnstuck(final float newX, final float newY, final CSimulation game) {
@ -107,7 +115,11 @@ public class CItem extends CWidget {
@Override @Override
public boolean isInvulnerable() { public boolean isInvulnerable() {
return false; return this.invulnerable;
}
public void setInvulernable(final boolean invulnerable) {
this.invulnerable = invulnerable;
} }
} }

View File

@ -13,6 +13,8 @@ public interface CPlayerStateListener {
void heroDeath(); void heroDeath();
void heroTokensChanged();
public static final class CPlayerStateNotifier extends SubscriberSetNotifier<CPlayerStateListener> public static final class CPlayerStateNotifier extends SubscriberSetNotifier<CPlayerStateListener>
implements CPlayerStateListener { implements CPlayerStateListener {
@Override @Override
@ -49,5 +51,12 @@ public interface CPlayerStateListener {
listener.heroDeath(); listener.heroDeath();
} }
} }
@Override
public void heroTokensChanged() {
for (final CPlayerStateListener listener : set) {
listener.heroTokensChanged();
}
}
} }
} }

View File

@ -45,6 +45,8 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayerJass
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CRace;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionManager; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionManager;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.timers.CTimer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.timers.CTimer;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEventsWar3;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.enumtypes.CPlayerSlotState;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.SimulationRenderController;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener; import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
@ -79,6 +81,7 @@ public class CSimulation implements CPlayerAPI {
private transient CommandErrorListener commandErrorListener; private transient CommandErrorListener commandErrorListener;
private final CRegionManager regionManager; private final CRegionManager regionManager;
private final List<TimeOfDayVariableEvent> timeOfDayVariableEvents = new ArrayList<>(); private final List<TimeOfDayVariableEvent> timeOfDayVariableEvents = new ArrayList<>();
private boolean timeOfDaySuspended;
public CSimulation(final War3MapConfig config, final DataTable miscData, final MutableObjectData parsedUnitData, public CSimulation(final War3MapConfig config, final DataTable miscData, final MutableObjectData parsedUnitData,
final MutableObjectData parsedItemData, final MutableObjectData parsedDestructableData, final MutableObjectData parsedItemData, final MutableObjectData parsedDestructableData,
@ -115,6 +118,9 @@ public class CSimulation implements CPlayerAPI {
final CRace defaultRace = CRace.UNDEAD; final CRace defaultRace = CRace.UNDEAD;
final CPlayer newPlayer = new CPlayer(defaultRace, new float[] { startLoc.getX(), startLoc.getY() }, final CPlayer newPlayer = new CPlayer(defaultRace, new float[] { startLoc.getX(), startLoc.getY() },
configPlayer); configPlayer);
if (WarsmashConstants.LOCAL_TEMP_TEST_ALL_PLAYERS_PLAYING) {
newPlayer.setSlotState(CPlayerSlotState.PLAYING);
}
this.players.add(newPlayer); this.players.add(newPlayer);
} }
this.players.get(this.players.size() - 4).setName(miscData.getLocalizedString("WESTRING_PLAYER_NA")); this.players.get(this.players.size() - 4).setName(miscData.getLocalizedString("WESTRING_PLAYER_NA"));
@ -164,6 +170,7 @@ public class CSimulation implements CPlayerAPI {
if (nextTimer.getEngineFireTick() > timer.getEngineFireTick()) { if (nextTimer.getEngineFireTick() > timer.getEngineFireTick()) {
listIterator.previous(); listIterator.previous();
listIterator.add(timer); listIterator.add(timer);
break;
} }
} }
this.activeTimers.add(timer); this.activeTimers.add(timer);
@ -187,7 +194,7 @@ public class CSimulation implements CPlayerAPI {
return unit; return unit;
} }
public CDestructable createDestructable(final War3ID typeId, final float x, final float y, public CDestructable internalCreateDestructable(final War3ID typeId, final float x, final float y,
final RemovablePathingMapInstance pathingInstance, final RemovablePathingMapInstance pathingInstanceDeath) { final RemovablePathingMapInstance pathingInstance, final RemovablePathingMapInstance pathingInstanceDeath) {
final CDestructable dest = this.destructableData.create(this, typeId, x, y, this.handleIdAllocator, final CDestructable dest = this.destructableData.create(this, typeId, x, y, this.handleIdAllocator,
pathingInstance, pathingInstanceDeath); pathingInstance, pathingInstanceDeath);
@ -198,18 +205,32 @@ public class CSimulation implements CPlayerAPI {
return dest; return dest;
} }
public CItem createItem(final War3ID alias, final float unitX, final float unitY) { public CItem internalCreateItem(final War3ID alias, final float unitX, final float unitY) {
final CItem item = this.itemData.create(this, alias, unitX, unitY, this.handleIdAllocator.createId()); final CItem item = this.itemData.create(this, alias, unitX, unitY, this.handleIdAllocator.createId());
this.handleIdToItem.put(item.getHandleId(), item); this.handleIdToItem.put(item.getHandleId(), item);
this.items.add(item); this.items.add(item);
return item; return item;
} }
public CItem createItem(final War3ID alias, final float unitX, final float unitY) {
return this.simulationRenderController.createItem(this, alias, unitX, unitY);
}
public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y, public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y,
final float facing) { final float facing) {
return this.simulationRenderController.createUnit(this, typeId, playerIndex, x, y, facing); return this.simulationRenderController.createUnit(this, typeId, playerIndex, x, y, facing);
} }
public CDestructable createDestructable(final War3ID typeId, final float x, final float y, final float facing,
final float scale, final int variation) {
return this.simulationRenderController.createDestructable(typeId, x, y, facing, scale, variation);
}
public CDestructable createDestructableZ(final War3ID typeId, final float x, final float y, final float z,
final float facing, final float scale, final int variation) {
return this.simulationRenderController.createDestructableZ(typeId, x, y, z, facing, scale, variation);
}
public CUnit getUnit(final int handleId) { public CUnit getUnit(final int handleId) {
return this.handleIdToUnit.get(handleId); return this.handleIdToUnit.get(handleId);
} }
@ -288,8 +309,10 @@ public class CSimulation implements CPlayerAPI {
} }
this.gameTurnTick++; this.gameTurnTick++;
final float timeOfDayBefore = this.getGameTimeOfDay(); final float timeOfDayBefore = this.getGameTimeOfDay();
if (!this.timeOfDaySuspended) {
this.currentGameDayTimeElapsed = (this.currentGameDayTimeElapsed + WarsmashConstants.SIMULATION_STEP_TIME) this.currentGameDayTimeElapsed = (this.currentGameDayTimeElapsed + WarsmashConstants.SIMULATION_STEP_TIME)
% this.gameplayConstants.getGameDayLength(); % this.gameplayConstants.getGameDayLength();
}
final float timeOfDayAfter = this.getGameTimeOfDay(); final float timeOfDayAfter = this.getGameTimeOfDay();
while (!this.activeTimers.isEmpty() && (this.activeTimers.peek().getEngineFireTick() <= this.gameTurnTick)) { while (!this.activeTimers.isEmpty() && (this.activeTimers.peek().getEngineFireTick() <= this.gameTurnTick)) {
this.activeTimers.pop().fire(this); this.activeTimers.pop().fire(this);
@ -327,6 +350,11 @@ public class CSimulation implements CPlayerAPI {
* this.gameplayConstants.getGameDayHours(); * this.gameplayConstants.getGameDayHours();
} }
public void setGameTimeOfDay(final float value) {
final float elapsed = value / this.gameplayConstants.getGameDayHours();
this.currentGameDayTimeElapsed = elapsed * this.gameplayConstants.getGameDayLength();
}
public int getGameTurnTick() { public int getGameTurnTick() {
return this.gameTurnTick; return this.gameTurnTick;
} }
@ -482,6 +510,16 @@ public class CSimulation implements CPlayerAPI {
}; };
} }
public RemovableTriggerEvent registerGameEvent(final GlobalScope globalScope, final Trigger trigger,
final JassGameEventsWar3 gameEvent) {
System.err.println("Game event not yet implemented: " + gameEvent);
return new RemovableTriggerEvent() {
@Override
public void remove() {
}
};
}
public void heroDeathEvent(final CUnit cUnit) { public void heroDeathEvent(final CUnit cUnit) {
this.simulationRenderController.heroDeathEvent(cUnit); this.simulationRenderController.heroDeathEvent(cUnit);
} }
@ -524,4 +562,19 @@ public class CSimulation implements CPlayerAPI {
} }
return RemovableTriggerEvent.DO_NOTHING; return RemovableTriggerEvent.DO_NOTHING;
} }
public void setAllItemTypeSlots(final int slots) {
System.err.println(
"Ignoring call to set all item type slots to: " + slots + " (marketplace is not yet implemented)");
}
public void setAllUnitTypeSlots(final int slots) {
System.err.println(
"Ignoring call to set all unit type slots to: " + slots + " (marketplace is not yet implemented)");
}
public void setTimeOfDaySuspended(final boolean flag) {
this.timeOfDaySuspended = flag;
}
} }

View File

@ -38,6 +38,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorStop; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorStop;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CWeaponType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.attacks.CUnitAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrder; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrder;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget;
@ -49,6 +50,8 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegion; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegion;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionEnumFunction; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionEnumFunction;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionManager; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.region.CRegionManager;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.state.CUnitState;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.unit.CUnitTypeJass;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityTargetCheckReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.BooleanAbilityTargetCheckReceiver;
@ -263,6 +266,7 @@ public class CUnit extends CWidget {
break; break;
} }
} }
this.stateListenersUpdates.clear();
if (isDead()) { if (isDead()) {
if (this.collisionRectangle != null) { if (this.collisionRectangle != null) {
// Moved this here because doing it on "kill" was able to happen in some cases // Moved this here because doing it on "kill" was able to happen in some cases
@ -988,6 +992,8 @@ public class CUnit extends CWidget {
} }
} }
} }
fireDeathEvents(simulation);
simulation.getPlayer(this.playerIndex).fireUnitDeathEvents(this, source);
} }
public boolean canReach(final AbilityTarget target, final float range) { public boolean canReach(final AbilityTarget target, final float range) {
@ -1610,6 +1616,15 @@ public class CUnit extends CWidget {
this.defaultBehavior = defaultBehavior; this.defaultBehavior = defaultBehavior;
} }
public CAbilityGoldMine getGoldMineData() {
for (final CAbility ability : this.abilities) {
if (ability instanceof CAbilityGoldMine) {
return ((CAbilityGoldMine) ability);
}
}
return null;
}
public int getGold() { public int getGold() {
for (final CAbility ability : this.abilities) { for (final CAbility ability : this.abilities) {
if (ability instanceof CAbilityGoldMine) { if (ability instanceof CAbilityGoldMine) {
@ -1744,4 +1759,141 @@ public class CUnit extends CWidget {
return this.updateType; return this.updateType;
} }
} }
public void cancelUpgrade(final CSimulation game) {
throw new RuntimeException("NYI");
}
public void beginUpgrade(final CSimulation game, final War3ID rawcode) {
}
public void setUnitState(final CSimulation game, final CUnitState whichUnitState, final float value) {
switch (whichUnitState) {
case LIFE:
setLife(game, value);
break;
case MANA:
setMana(value);
break;
case MAX_LIFE:
setMaximumLife((int) value);
break;
case MAX_MANA:
setMaximumMana((int) value);
break;
}
}
public float getUnitState(final CSimulation game, final CUnitState whichUnitState) {
switch (whichUnitState) {
case LIFE:
return getLife();
case MANA:
return getMana();
case MAX_LIFE:
return getMaximumLife();
case MAX_MANA:
return getMaximumMana();
}
return 0;
}
public boolean isUnitType(final CUnitTypeJass whichUnitType) {
switch (whichUnitType) {
case HERO:
return getHeroData() != null;
case DEAD:
return isDead();
case STRUCTURE:
return isBuilding();
case FLYING:
return getUnitType().getTargetedAs().contains(CTargetType.AIR);
case GROUND:
return getUnitType().getTargetedAs().contains(CTargetType.GROUND);
case ATTACKS_FLYING:
for (final CUnitAttack attack : getAttacks()) {
if (attack.getTargetsAllowed().contains(CTargetType.AIR)) {
return true;
}
}
return false;
case ATTACKS_GROUND:
for (final CUnitAttack attack : getAttacks()) {
if (attack.getTargetsAllowed().contains(CTargetType.GROUND)) {
return true;
}
}
return false;
case MELEE_ATTACKER:
boolean hasAttacks = false;
for (final CUnitAttack attack : getAttacks()) {
if (attack.getWeaponType() != CWeaponType.NORMAL) {
return false;
}
hasAttacks = true;
}
return hasAttacks;
case RANGED_ATTACKER:
for (final CUnitAttack attack : getAttacks()) {
if (attack.getWeaponType() != CWeaponType.NORMAL) {
return true;
}
}
return false;
case GIANT:
return getUnitType().getClassifications().contains(CUnitClassification.GIANT);
case SUMMONED:
return getUnitType().getClassifications().contains(CUnitClassification.SUMMONED);
case STUNNED:
return getCurrentBehavior().getHighlightOrderId() == OrderIds.stunned;
case PLAGUED:
throw new UnsupportedOperationException(
"cannot ask engine if unit is plagued: plague is not yet implemented");
case SNARED:
throw new UnsupportedOperationException(
"cannot ask engine if unit is snared: snare is not yet implemented");
case UNDEAD:
return getUnitType().getClassifications().contains(CUnitClassification.UNDEAD);
case MECHANICAL:
return getUnitType().getClassifications().contains(CUnitClassification.MECHANICAL);
case PEON:
return getUnitType().getClassifications().contains(CUnitClassification.PEON);
case SAPPER:
return getUnitType().getClassifications().contains(CUnitClassification.SAPPER);
case TOWNHALL:
return getUnitType().getClassifications().contains(CUnitClassification.TOWNHALL);
case ANCIENT:
return this.unitType.getClassifications().contains(CUnitClassification.ANCIENT);
case TAUREN:
return getUnitType().getClassifications().contains(CUnitClassification.TAUREN);
case POISONED:
throw new UnsupportedOperationException(
"cannot ask engine if unit is poisoned: poison is not yet implemented");
case POLYMORPHED:
throw new UnsupportedOperationException(
"cannot ask engine if unit is POLYMORPHED: POLYMORPHED is not yet implemented");
case SLEEPING:
throw new UnsupportedOperationException(
"cannot ask engine if unit is SLEEPING: SLEEPING is not yet implemented");
case RESISTANT:
throw new UnsupportedOperationException(
"cannot ask engine if unit is RESISTANT: RESISTANT is not yet implemented");
case ETHEREAL:
throw new UnsupportedOperationException(
"cannot ask engine if unit is ETHEREAL: ETHEREAL is not yet implemented");
case MAGIC_IMMUNE:
throw new UnsupportedOperationException(
"cannot ask engine if unit is MAGIC_IMMUNE: MAGIC_IMMUNE is not yet implemented");
}
return false;
}
} }

View File

@ -50,6 +50,7 @@ public class CUnitType {
private final List<War3ID> structuresBuilt; private final List<War3ID> structuresBuilt;
private final List<War3ID> unitsTrained; private final List<War3ID> unitsTrained;
private final List<War3ID> researchesAvailable; private final List<War3ID> researchesAvailable;
private final List<War3ID> upgradesTo;
private final CUnitRace unitRace; private final CUnitRace unitRace;
private final int goldCost; private final int goldCost;
private final int lumberCost; private final int lumberCost;
@ -85,8 +86,8 @@ public class CUnitType {
final CDefenseType defenseType, final float impactZ, final BufferedImage buildingPathingPixelMap, final CDefenseType defenseType, final float impactZ, final BufferedImage buildingPathingPixelMap,
final float deathTime, final EnumSet<CTargetType> targetedAs, final float defaultAcquisitionRange, final float deathTime, final EnumSet<CTargetType> targetedAs, final float defaultAcquisitionRange,
final float minimumAttackRange, final List<War3ID> structuresBuilt, final List<War3ID> unitsTrained, final float minimumAttackRange, final List<War3ID> structuresBuilt, final List<War3ID> unitsTrained,
final List<War3ID> researchesAvailable, final CUnitRace unitRace, final int goldCost, final int lumberCost, final List<War3ID> researchesAvailable, final List<War3ID> upgradesTo, final CUnitRace unitRace,
final int foodUsed, final int foodMade, final int buildTime, final int goldCost, final int lumberCost, final int foodUsed, final int foodMade, final int buildTime,
final EnumSet<CBuildingPathingType> preventedPathingTypes, final EnumSet<CBuildingPathingType> preventedPathingTypes,
final EnumSet<CBuildingPathingType> requiredPathingTypes, final float propWindow, final float turnRate, final EnumSet<CBuildingPathingType> requiredPathingTypes, final float propWindow, final float turnRate,
final List<CUnitTypeRequirement> requirements, final int level, final boolean hero, final int strength, final List<CUnitTypeRequirement> requirements, final int level, final boolean hero, final int strength,
@ -122,6 +123,7 @@ public class CUnitType {
this.structuresBuilt = structuresBuilt; this.structuresBuilt = structuresBuilt;
this.unitsTrained = unitsTrained; this.unitsTrained = unitsTrained;
this.researchesAvailable = researchesAvailable; this.researchesAvailable = researchesAvailable;
this.upgradesTo = upgradesTo;
this.unitRace = unitRace; this.unitRace = unitRace;
this.goldCost = goldCost; this.goldCost = goldCost;
this.lumberCost = lumberCost; this.lumberCost = lumberCost;
@ -262,6 +264,10 @@ public class CUnitType {
return this.researchesAvailable; return this.researchesAvailable;
} }
public List<War3ID> getUpgradesTo() {
return this.upgradesTo;
}
public CUnitRace getRace() { public CUnitRace getRace() {
return this.unitRace; return this.unitRace;
} }

View File

@ -1,11 +1,20 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation; package com.etheller.warsmash.viewer5.handlers.w3x.simulation;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Rectangle;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.trigger.RemovableTriggerEvent;
import com.etheller.interpreter.ast.scope.trigger.Trigger;
import com.etheller.warsmash.parsers.jass.scope.CommonTriggerExecutionScope;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEventsWar3;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.unit.CWidgetEvent;
public abstract class CWidget implements AbilityTarget { public abstract class CWidget implements AbilityTarget {
protected static final Rectangle tempRect = new Rectangle(); protected static final Rectangle tempRect = new Rectangle();
@ -13,6 +22,8 @@ public abstract class CWidget implements AbilityTarget {
private float x; private float x;
private float y; private float y;
protected float life; protected float life;
private final EnumMap<JassGameEventsWar3, List<CWidgetEvent>> eventTypeToEvents = new EnumMap<>(
JassGameEventsWar3.class);
public CWidget(final int handleId, final float x, final float y, final float life) { public CWidget(final int handleId, final float x, final float y, final float life) {
this.handleId = handleId; this.handleId = handleId;
@ -78,4 +89,49 @@ public abstract class CWidget implements AbilityTarget {
} }
public abstract boolean isInvulnerable(); public abstract boolean isInvulnerable();
public void fireDeathEvents(final CSimulation simulation) {
fireEvents(CommonTriggerExecutionScope::widgetTriggerScope, JassGameEventsWar3.EVENT_WIDGET_DEATH);
}
private List<CWidgetEvent> getOrCreateEventList(final JassGameEventsWar3 eventType) {
List<CWidgetEvent> playerEvents = this.eventTypeToEvents.get(eventType);
if (playerEvents == null) {
playerEvents = new ArrayList<>();
this.eventTypeToEvents.put(eventType, playerEvents);
}
return playerEvents;
}
private List<CWidgetEvent> getEventList(final JassGameEventsWar3 eventType) {
return this.eventTypeToEvents.get(eventType);
}
public RemovableTriggerEvent addEvent(final GlobalScope globalScope, final Trigger whichTrigger,
final JassGameEventsWar3 eventType) {
final CWidgetEvent playerEvent = new CWidgetEvent(globalScope, this, whichTrigger, eventType, null);
getOrCreateEventList(eventType).add(playerEvent);
return playerEvent;
}
public void removeEvent(final CWidgetEvent playerEvent) {
final List<CWidgetEvent> eventList = getEventList(playerEvent.getEventType());
if (eventList != null) {
eventList.remove(playerEvent);
}
}
private void fireEvents(final CommonTriggerExecutionScope.WidgetEventScopeBuilder eventScopeBuilder,
final JassGameEventsWar3 eventType) {
final List<CWidgetEvent> eventList = getEventList(eventType);
if (eventList != null) {
for (final CWidgetEvent event : eventList) {
event.fire(this, eventScopeBuilder.create(event.getTrigger(), this));
}
}
}
public RemovableTriggerEvent addDeathEvent(final GlobalScope globalScope, final Trigger whichTrigger) {
return addEvent(globalScope, whichTrigger, JassGameEventsWar3.EVENT_WIDGET_DEATH);
}
} }

View File

@ -1,5 +1,6 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
@ -43,6 +44,9 @@ public class CAbilityAttack extends AbstractCAbility {
return; return;
} }
} }
else if (target instanceof CDestructable) {
// fall thru to below
}
else { else {
receiver.orderIdNotAccepted(); receiver.orderIdNotAccepted();
return; return;

View File

@ -14,6 +14,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbi
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade.CAbilityUpgrade;
/** /**
* A visitor for the lowest level inherent types of an ability. It's a bit of a * A visitor for the lowest level inherent types of an ability. It's a bit of a
@ -48,6 +49,8 @@ public interface CAbilityVisitor<T> {
T accept(CAbilityQueue ability); T accept(CAbilityQueue ability);
T accept(CAbilityUpgrade ability);
T accept(CAbilityReviveHero ability); T accept(CAbilityReviveHero ability);
T accept(GenericSingleIconActiveAbility ability); T accept(GenericSingleIconActiveAbility ability);

View File

@ -42,7 +42,8 @@ public abstract class AbstractCAbilityBuild extends AbstractCAbility implements
if (unitType != null) { if (unitType != null) {
final CPlayer player = game.getPlayer(unit.getPlayerIndex()); final CPlayer player = game.getPlayer(unit.getPlayerIndex());
final List<CUnitTypeRequirement> requirements = unitType.getRequirements(); final List<CUnitTypeRequirement> requirements = unitType.getRequirements();
boolean requirementsMet = true; final boolean techtreeAllowedByMax = player.isTechtreeAllowedByMax(orderIdAsRawtype);
boolean requirementsMet = techtreeAllowedByMax;
for (final CUnitTypeRequirement requirement : requirements) { for (final CUnitTypeRequirement requirement : requirements) {
if (player.getTechtreeUnlocked(requirement.getRequirement()) < requirement.getRequiredLevel()) { if (player.getTechtreeUnlocked(requirement.getRequirement()) < requirement.getRequiredLevel()) {
requirementsMet = false; requirementsMet = false;
@ -69,10 +70,15 @@ public abstract class AbstractCAbilityBuild extends AbstractCAbility implements
} }
} }
else { else {
if (techtreeAllowedByMax) {
for (final CUnitTypeRequirement requirement : requirements) { for (final CUnitTypeRequirement requirement : requirements) {
receiver.missingRequirement(requirement.getRequirement(), requirement.getRequiredLevel()); receiver.missingRequirement(requirement.getRequirement(), requirement.getRequiredLevel());
} }
} }
else {
receiver.techtreeMaximumReached();
}
}
} }
else { else {
receiver.useOk(); receiver.useOk();
@ -108,7 +114,8 @@ public abstract class AbstractCAbilityBuild extends AbstractCAbility implements
} }
@Override @Override
public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId,
final AbilityTarget target) {
return true; return true;
} }

View File

@ -187,7 +187,7 @@ public class CAbilityHero extends AbstractCAbility {
} }
public void addXp(final CSimulation simulation, final CUnit unit, final int xp) { public void addXp(final CSimulation simulation, final CUnit unit, final int xp) {
this.xp += xp; this.xp += xp * simulation.getPlayer(unit.getPlayerIndex()).getHandicapXP();
final CGameplayConstants gameplayConstants = simulation.getGameplayConstants(); final CGameplayConstants gameplayConstants = simulation.getGameplayConstants();
while ((this.heroLevel < gameplayConstants.getMaxHeroLevel()) while ((this.heroLevel < gameplayConstants.getMaxHeroLevel())
&& (this.xp >= gameplayConstants.getNeedHeroXPSum(this.heroLevel))) { && (this.xp >= gameplayConstants.getNeedHeroXPSum(this.heroLevel))) {

View File

@ -18,7 +18,9 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.types.CAb
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorDropItem; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorDropItem;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorGetItem; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorGetItem;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory.CBehaviorGiveItemToHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
@ -31,6 +33,7 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
private final List<CAbility>[] itemsHeldAbilities; private final List<CAbility>[] itemsHeldAbilities;
private CBehaviorGetItem behaviorGetItem; private CBehaviorGetItem behaviorGetItem;
private CBehaviorDropItem behaviorDropItem; private CBehaviorDropItem behaviorDropItem;
private CBehaviorGiveItemToHero behaviorGiveItem;
public CAbilityInventory(final int handleId, final War3ID alias, final boolean canDropItems, public CAbilityInventory(final int handleId, final War3ID alias, final boolean canDropItems,
final boolean canGetItems, final boolean canUseItems, final boolean dropItemsOnDeath, final boolean canGetItems, final boolean canUseItems, final boolean dropItemsOnDeath,
@ -51,6 +54,7 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
public void onAdd(final CSimulation game, final CUnit unit) { public void onAdd(final CSimulation game, final CUnit unit) {
this.behaviorGetItem = new CBehaviorGetItem(unit, this); this.behaviorGetItem = new CBehaviorGetItem(unit, this);
this.behaviorDropItem = new CBehaviorDropItem(unit, this); this.behaviorDropItem = new CBehaviorDropItem(unit, this);
this.behaviorGiveItem = new CBehaviorGiveItemToHero(unit, this);
} }
@Override @Override
@ -81,16 +85,37 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
} }
} }
else if ((orderId >= OrderIds.itemuse00) && (orderId <= OrderIds.itemuse05)) { else if ((orderId >= OrderIds.itemuse00) && (orderId <= OrderIds.itemuse05)) {
final CAbility cAbility = this.itemsHeldAbilities[orderId - OrderIds.itemuse00].get(0); final int slot = orderId - OrderIds.itemuse00;
final CAbility cAbility = this.itemsHeldAbilities[slot].get(0);
int forwardedOrderId = orderId; int forwardedOrderId = orderId;
if (cAbility instanceof GenericSingleIconActiveAbility) { if (cAbility instanceof GenericSingleIconActiveAbility) {
forwardedOrderId = ((GenericSingleIconActiveAbility) cAbility).getBaseOrderId(); forwardedOrderId = ((GenericSingleIconActiveAbility) cAbility).getBaseOrderId();
} }
cAbility.checkBeforeQueue(game, caster, forwardedOrderId, target); final boolean checkResult = cAbility.checkBeforeQueue(game, caster, forwardedOrderId, target);
if (!checkResult) {
// we will never call begin, so we need to consume a charge of perishables here
// assuming this is a no-queue instant use perishable... later if we have some
// other weird case where "check before queue" false is supposed to mean you
// can't use the skill, then this would consume charges without using it, and
// that would be stupid but I don't think we will do that since checkCanUse
// should be failing at that point. So then we should have never called
// checkBeforeQueue.
final CItem cItem = this.itemsHeld[slot];
consumePerishableCharge(game, caster, slot, cItem);
}
return checkResult;
} }
return super.checkBeforeQueue(game, caster, orderId, target); return super.checkBeforeQueue(game, caster, orderId, target);
} }
private void consumePerishableCharge(final CSimulation game, final CUnit caster, final int slot,
final CItem cItem) {
if (cItem.getItemType().isPerishable()) {
dropItem(game, caster, slot, caster.getX(), caster.getY(), false);
game.removeItem(cItem);
}
}
@Override @Override
public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) { public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) {
@ -121,6 +146,11 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
return this.behaviorDropItem.reset(itemToDrop, target); return this.behaviorDropItem.reset(itemToDrop, target);
} }
public CBehavior beginDropItem(final CSimulation game, final CUnit caster, final int orderId,
final CItem itemToDrop, final CUnit targetHero) {
return this.behaviorGiveItem.reset(itemToDrop, targetHero);
}
@Override @Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId,
final AbilityPointTarget point) { final AbilityPointTarget point) {
@ -133,10 +163,7 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
final int slot = orderId - OrderIds.itemuse00; final int slot = orderId - OrderIds.itemuse00;
final CBehavior behavior = this.itemsHeldAbilities[slot].get(0).beginNoTarget(game, caster, orderId); final CBehavior behavior = this.itemsHeldAbilities[slot].get(0).beginNoTarget(game, caster, orderId);
final CItem cItem = this.itemsHeld[slot]; final CItem cItem = this.itemsHeld[slot];
if (cItem.getItemType().isPerishable()) { consumePerishableCharge(game, caster, slot, cItem);
dropItem(game, caster, slot, caster.getX(), caster.getY(), false);
game.removeItem(cItem);
}
return behavior; return behavior;
} }
@ -172,9 +199,26 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
receiver.orderIdNotAccepted(); receiver.orderIdNotAccepted();
} }
} }
else if (orderId == OrderIds.dropitem) {
if (target instanceof CUnit) {
final CUnit hero = (CUnit) target;
if ((hero.getInventoryData() != null) && game.getPlayer(hero.getPlayerIndex())
.hasAlliance(unit.getPlayerIndex(), CAllianceType.PASSIVE)) {
receiver.targetOk(target);
}
else {
receiver.orderIdNotAccepted(); receiver.orderIdNotAccepted();
} }
} }
else {
receiver.orderIdNotAccepted();
}
}
else {
receiver.orderIdNotAccepted();
}
}
}
public int getSlot(final CItem target) { public int getSlot(final CItem target) {
int slot = -1; int slot = -1;
@ -189,9 +233,12 @@ public class CAbilityInventory extends AbstractGenericNoIconAbility {
@Override @Override
public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, public void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityPointTarget target, final AbilityTargetCheckReceiver<AbilityPointTarget> receiver) { final AbilityPointTarget target, final AbilityTargetCheckReceiver<AbilityPointTarget> receiver) {
if (orderId == OrderIds.dropitem) { if (orderId != OrderIds.dropitem) {
receiver.orderIdNotAccepted(); receiver.orderIdNotAccepted();
} }
else {
receiver.targetOk(target);
}
} }
@Override @Override

View File

@ -48,7 +48,8 @@ public final class CAbilityQueue extends AbstractCAbility {
if (unitType != null) { if (unitType != null) {
final CPlayer player = game.getPlayer(unit.getPlayerIndex()); final CPlayer player = game.getPlayer(unit.getPlayerIndex());
final List<CUnitTypeRequirement> requirements = unitType.getRequirements(); final List<CUnitTypeRequirement> requirements = unitType.getRequirements();
boolean requirementsMet = true; final boolean techtreeAllowedByMax = player.isTechtreeAllowedByMax(orderIdAsRawtype);
boolean requirementsMet = techtreeAllowedByMax;
for (final CUnitTypeRequirement requirement : requirements) { for (final CUnitTypeRequirement requirement : requirements) {
if (player.getTechtreeUnlocked(requirement.getRequirement()) < requirement.getRequiredLevel()) { if (player.getTechtreeUnlocked(requirement.getRequirement()) < requirement.getRequiredLevel()) {
requirementsMet = false; requirementsMet = false;
@ -74,10 +75,15 @@ public final class CAbilityQueue extends AbstractCAbility {
} }
} }
else { else {
if (techtreeAllowedByMax) {
for (final CUnitTypeRequirement requirement : requirements) { for (final CUnitTypeRequirement requirement : requirements) {
receiver.missingRequirement(requirement.getRequirement(), requirement.getRequiredLevel()); receiver.missingRequirement(requirement.getRequirement(), requirement.getRequiredLevel());
} }
} }
else {
receiver.techtreeMaximumReached();
}
}
} }
else { else {
receiver.useOk(); receiver.useOk();
@ -116,7 +122,8 @@ public final class CAbilityQueue extends AbstractCAbility {
} }
@Override @Override
public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId, AbilityTarget target) { public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId,
final AbilityTarget target) {
return true; return true;
} }

View File

@ -1,5 +1,171 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade;
public class CAbilityUpgrade { import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitTypeRequirement;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.AbstractCAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CPlayer;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityActivationReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.AbilityTargetCheckReceiver;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.util.ResourceType;
public final class CAbilityUpgrade extends AbstractCAbility {
private final Set<War3ID> upgradesTo;
public CAbilityUpgrade(final int handleId, final List<War3ID> upgradesTo) {
super(handleId);
this.upgradesTo = new LinkedHashSet<>(upgradesTo);
}
public Set<War3ID> getUpgradesTo() {
return this.upgradesTo;
}
@Override
protected void innerCheckCanUse(final CSimulation game, final CUnit unit, final int orderId,
final AbilityActivationReceiver receiver) {
final War3ID orderIdAsRawtype = new War3ID(orderId);
if (this.upgradesTo.contains(orderIdAsRawtype) && (unit.getBuildQueue()[0] == null)) {
final CUnitType unitType = game.getUnitData().getUnitType(orderIdAsRawtype);
if (unitType != null) {
final CPlayer player = game.getPlayer(unit.getPlayerIndex());
final List<CUnitTypeRequirement> requirements = unitType.getRequirements();
final boolean techtreeAllowedByMax = player.isTechtreeAllowedByMax(orderIdAsRawtype);
boolean requirementsMet = techtreeAllowedByMax;
for (final CUnitTypeRequirement requirement : requirements) {
if (player.getTechtreeUnlocked(requirement.getRequirement()) < requirement.getRequiredLevel()) {
requirementsMet = false;
}
}
if (requirementsMet) {
if (player.getGold() >= unitType.getGoldCost()) {
if (player.getLumber() >= unitType.getLumberCost()) {
if ((unitType.getFoodUsed() == 0)
|| ((player.getFoodUsed() + unitType.getFoodUsed()) <= player.getFoodCap())) {
receiver.useOk();
}
else {
receiver.notEnoughResources(ResourceType.FOOD);
}
}
else {
receiver.notEnoughResources(ResourceType.LUMBER);
}
}
else {
receiver.notEnoughResources(ResourceType.GOLD);
}
}
else {
if (techtreeAllowedByMax) {
for (final CUnitTypeRequirement requirement : requirements) {
receiver.missingRequirement(requirement.getRequirement(), requirement.getRequiredLevel());
}
}
else {
receiver.techtreeMaximumReached();
}
}
}
else {
receiver.useOk();
}
}
else {
/// ???
receiver.useOk();
}
}
@Override
public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId, final CWidget target,
final AbilityTargetCheckReceiver<CWidget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public final void checkCanTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityPointTarget target, final AbilityTargetCheckReceiver<AbilityPointTarget> receiver) {
receiver.orderIdNotAccepted();
}
@Override
public final void checkCanTargetNoTarget(final CSimulation game, final CUnit unit, final int orderId,
final AbilityTargetCheckReceiver<Void> receiver) {
if (this.upgradesTo.contains(new War3ID(orderId))) {
receiver.targetOk(null);
}
else if (orderId == OrderIds.cancel) {
receiver.targetOk(null);
}
else {
receiver.orderIdNotAccepted();
}
}
@Override
public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId,
final AbilityTarget target) {
return true;
}
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
}
@Override
public void onRemove(final CSimulation game, final CUnit unit) {
}
@Override
public void onTick(final CSimulation game, final CUnit unit) {
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final CWidget target) {
return null;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId,
final AbilityPointTarget point) {
return null;
}
@Override
public CBehavior beginNoTarget(final CSimulation game, final CUnit caster, final int orderId) {
if (orderId == OrderIds.cancel) {
caster.cancelUpgrade(game);
}
else {
final War3ID rawcode = new War3ID(orderId);
if (this.upgradesTo.contains(rawcode)) {
caster.beginUpgrade(game, rawcode);
}
}
return null;
}
@Override
public <T> T visit(final CAbilityVisitor<T> visitor) {
return visitor.accept(this);
}
@Override
public void onCancelFromQueue(final CSimulation game, final CUnit unit, final int orderId) {
}
} }

View File

@ -18,6 +18,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CAbi
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade.CAbilityUpgrade;
public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisitor<Void> { public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisitor<Void> {
public static final AbilityDisableWhileUnderConstructionVisitor INSTANCE = new AbilityDisableWhileUnderConstructionVisitor(); public static final AbilityDisableWhileUnderConstructionVisitor INSTANCE = new AbilityDisableWhileUnderConstructionVisitor();
@ -107,7 +108,14 @@ public class AbilityDisableWhileUnderConstructionVisitor implements CAbilityVisi
} }
@Override @Override
public Void accept(CAbilityReviveHero ability) { public Void accept(final CAbilityUpgrade ability) {
ability.setDisabled(true);
ability.setIconShowing(false);
return null;
}
@Override
public Void accept(final CAbilityReviveHero ability) {
ability.setDisabled(true); ability.setDisabled(true);
ability.setIconShowing(false); ability.setIconShowing(false);
return null; return null;

View File

@ -0,0 +1,75 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.inventory;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetStillAliveVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CAbstractRangedBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
public class CBehaviorGiveItemToHero extends CAbstractRangedBehavior {
private final CAbilityInventory inventory;
private CItem targetItem;
private CUnit targetHero;
public CBehaviorGiveItemToHero(final CUnit unit, final CAbilityInventory inventory) {
super(unit);
this.inventory = inventory;
}
public CBehaviorGiveItemToHero reset(final CItem targetItem, final CUnit targetHero) {
innerReset(targetHero);
this.targetItem = targetItem;
this.targetHero = targetHero;
return this;
}
@Override
public boolean isWithinRange(final CSimulation simulation) {
return this.unit.canReach(this.target, simulation.getGameplayConstants().getGiveItemRange());
}
@Override
public void endMove(final CSimulation game, final boolean interrupted) {
}
@Override
public void begin(final CSimulation game) {
}
@Override
public void end(final CSimulation game, final boolean interrupted) {
}
@Override
public int getHighlightOrderId() {
return OrderIds.dropitem;
}
@Override
protected CBehavior update(final CSimulation simulation, final boolean withinFacingWindow) {
this.inventory.dropItem(simulation, this.unit, this.targetItem, this.target.getX(), this.target.getY(), true);
if (this.targetHero.getInventoryData() != null) {
this.targetHero.getInventoryData().giveItem(simulation, this.targetHero, this.targetItem, false);
}
return this.unit.pollNextOrderBehavior(simulation);
}
@Override
protected CBehavior updateOnInvalidTarget(final CSimulation simulation) {
return this.unit.pollNextOrderBehavior(simulation);
}
@Override
protected boolean checkTargetStillValid(final CSimulation simulation) {
return this.target.visit(AbilityTargetStillAliveVisitor.INSTANCE);
}
@Override
protected void resetBeforeMoving(final CSimulation simulation) {
}
}

View File

@ -28,6 +28,9 @@ public enum CDefenseType implements CodeKeyType {
if (upperCaseTypeString.equals("HEAVY")) { if (upperCaseTypeString.equals("HEAVY")) {
return LARGE; return LARGE;
} }
if (upperCaseTypeString.trim().isEmpty()) {
System.err.println("bad");
}
return valueOf(upperCaseTypeString); return valueOf(upperCaseTypeString);
} }
} }

View File

@ -176,6 +176,10 @@ public abstract class CBasePlayer implements CPlayerJass {
return this.slotState; return this.slotState;
} }
public void setSlotState(final CPlayerSlotState slotState) {
this.slotState = slotState;
}
@Override @Override
public int getTaxRate(final int otherPlayerIndex, final CPlayerState whichResource) { public int getTaxRate(final int otherPlayerIndex, final CPlayerState whichResource) {
final Integer taxRate = this.taxRates[otherPlayerIndex].get(whichResource); final Integer taxRate = this.taxRates[otherPlayerIndex].get(whichResource);

View File

@ -34,6 +34,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.hero.CPri
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityQueue;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityRally;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.queue.CAbilityReviveHero;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.upgrade.CAbilityUpgrade;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CAttackType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CDefenseType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.combat.CTargetType;
@ -144,6 +145,7 @@ public class CUnitData {
private static final War3ID STRUCTURES_BUILT = War3ID.fromString("ubui"); private static final War3ID STRUCTURES_BUILT = War3ID.fromString("ubui");
private static final War3ID UNITS_TRAINED = War3ID.fromString("utra"); private static final War3ID UNITS_TRAINED = War3ID.fromString("utra");
private static final War3ID RESEARCHES_AVAILABLE = War3ID.fromString("ures"); private static final War3ID RESEARCHES_AVAILABLE = War3ID.fromString("ures");
private static final War3ID UPGRADES_TO = War3ID.fromString("uupt");
private static final War3ID REVIVES_HEROES = War3ID.fromString("urev"); private static final War3ID REVIVES_HEROES = War3ID.fromString("urev");
private static final War3ID UNIT_RACE = War3ID.fromString("urac"); private static final War3ID UNIT_RACE = War3ID.fromString("urac");
@ -242,9 +244,13 @@ public class CUnitData {
} }
final List<War3ID> unitsTrained = unitTypeInstance.getUnitsTrained(); final List<War3ID> unitsTrained = unitTypeInstance.getUnitsTrained();
final List<War3ID> researchesAvailable = unitTypeInstance.getResearchesAvailable(); final List<War3ID> researchesAvailable = unitTypeInstance.getResearchesAvailable();
final List<War3ID> upgradesTo = unitTypeInstance.getUpgradesTo();
if (!unitsTrained.isEmpty() || !researchesAvailable.isEmpty()) { if (!unitsTrained.isEmpty() || !researchesAvailable.isEmpty()) {
unit.add(simulation, new CAbilityQueue(handleIdAllocator.createId(), unitsTrained, researchesAvailable)); unit.add(simulation, new CAbilityQueue(handleIdAllocator.createId(), unitsTrained, researchesAvailable));
} }
if (!upgradesTo.isEmpty()) {
unit.add(simulation, new CAbilityUpgrade(handleIdAllocator.createId(), upgradesTo));
}
if (unitTypeInstance.isRevivesHeroes()) { if (unitTypeInstance.isRevivesHeroes()) {
unit.add(simulation, new CAbilityReviveHero(handleIdAllocator.createId())); unit.add(simulation, new CAbilityReviveHero(handleIdAllocator.createId()));
} }
@ -466,6 +472,15 @@ public class CUnitData {
} }
} }
final String upgradesToString = unitType.getFieldAsString(UPGRADES_TO, 0);
final String[] upgradesToStringItems = upgradesToString.trim().split(",");
final List<War3ID> upgradesTo = new ArrayList<>();
for (final String upgradesToStringItem : upgradesToStringItems) {
if (upgradesToStringItem.length() == 4) {
upgradesTo.add(War3ID.fromString(upgradesToStringItem));
}
}
final String researchesAvailableString = unitType.getFieldAsString(RESEARCHES_AVAILABLE, 0); final String researchesAvailableString = unitType.getFieldAsString(RESEARCHES_AVAILABLE, 0);
final String[] researchesAvailableStringItems = researchesAvailableString.trim().split(","); final String[] researchesAvailableStringItems = researchesAvailableString.trim().split(",");
final List<War3ID> researchesAvailable = new ArrayList<>(); final List<War3ID> researchesAvailable = new ArrayList<>();
@ -541,11 +556,11 @@ public class CUnitData {
unitTypeInstance = new CUnitType(unitName, legacyName, typeId, life, manaInitial, manaMaximum, speed, unitTypeInstance = new CUnitType(unitName, legacyName, typeId, life, manaInitial, manaMaximum, speed,
defense, abilityList, isBldg, movementType, moveHeight, collisionSize, classifications, attacks, defense, abilityList, isBldg, movementType, moveHeight, collisionSize, classifications, attacks,
armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime, targetedAs, armorType, raise, decay, defenseType, impactZ, buildingPathingPixelMap, deathTime, targetedAs,
acquisitionRange, minimumAttackRange, structuresBuilt, unitsTrained, researchesAvailable, unitRace, acquisitionRange, minimumAttackRange, structuresBuilt, unitsTrained, researchesAvailable,
goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes, requiredPathingTypes, upgradesTo, unitRace, goldCost, lumberCost, foodUsed, foodMade, buildTime, preventedPathingTypes,
propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus, agility, agiPlus, requiredPathingTypes, propWindow, turnRate, requirements, unitLevel, hero, strength, strPlus,
intelligence, intPlus, primaryAttribute, heroAbilityList, heroProperNames, properNamesCount, agility, agiPlus, intelligence, intPlus, primaryAttribute, heroAbilityList, heroProperNames,
canFlee, priority, revivesHeroes); properNamesCount, canFlee, priority, revivesHeroes);
this.unitIdToUnitType.put(typeId, unitTypeInstance); this.unitIdToUnitType.put(typeId, unitTypeInstance);
this.jassLegacyNameToUnitId.put(legacyName, typeId); this.jassLegacyNameToUnitId.put(legacyName, typeId);
} }

View File

@ -0,0 +1,105 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.inventory.CAbilityInventory;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
public class COrderDropItemAtTargetWidget implements COrder {
private final int abilityHandleId;
private final int orderId;
private final int itemHandleId;
private final int targetHeroHandleId;
private final boolean queued;
public COrderDropItemAtTargetWidget(final int abilityHandleId, final int orderId, final int itemHandleId,
final int targetHeroHandleId, final boolean queued) {
this.abilityHandleId = abilityHandleId;
this.orderId = orderId;
this.itemHandleId = itemHandleId;
this.targetHeroHandleId = targetHeroHandleId;
this.queued = queued;
}
@Override
public int getAbilityHandleId() {
return this.abilityHandleId;
}
@Override
public int getOrderId() {
return this.orderId;
}
@Override
public CWidget getTarget(final CSimulation game) {
final CWidget target = game.getWidget(this.targetHeroHandleId);
return target;
}
@Override
public boolean isQueued() {
return this.queued;
}
@Override
public CBehavior begin(final CSimulation game, final CUnit caster) {
final CAbilityInventory ability = (CAbilityInventory) game.getAbility(this.abilityHandleId);
ability.checkCanUse(game, caster, this.orderId, this.abilityActivationReceiver.reset());
if (this.abilityActivationReceiver.isUseOk()) {
final CItem itemToDrop = (CItem) game.getWidget(this.itemHandleId);
final CUnit targetHero = (CUnit) game.getWidget(this.targetHeroHandleId);
return ability.beginDropItem(game, caster, this.orderId, itemToDrop, targetHero);
}
else {
game.getCommandErrorListener(caster.getPlayerIndex())
.showCommandError(this.abilityActivationReceiver.getMessage());
return caster.pollNextOrderBehavior(game);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + this.abilityHandleId;
result = (prime * result) + this.itemHandleId;
result = (prime * result) + this.orderId;
result = (prime * result) + (this.queued ? 1231 : 1237);
result = (prime * result) + this.targetHeroHandleId;
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final COrderDropItemAtTargetWidget other = (COrderDropItemAtTargetWidget) obj;
if (this.abilityHandleId != other.abilityHandleId) {
return false;
}
if (this.itemHandleId != other.itemHandleId) {
return false;
}
if (this.orderId != other.orderId) {
return false;
}
if (this.queued != other.queued) {
return false;
}
if (this.targetHeroHandleId != other.targetHeroHandleId) {
return false;
}
return true;
}
}

View File

@ -50,7 +50,7 @@ public class COrderTargetWidget implements COrder {
if (abilityActivationReceiver.isUseOk()) { if (abilityActivationReceiver.isUseOk()) {
final CWidget target = game.getWidget(this.targetHandleId); final CWidget target = game.getWidget(this.targetHandleId);
final StringMsgTargetCheckReceiver<CWidget> targetReceiver = (StringMsgTargetCheckReceiver<CWidget>) targetCheckReceiver; final StringMsgTargetCheckReceiver<CWidget> targetReceiver = (StringMsgTargetCheckReceiver<CWidget>) targetCheckReceiver;
ability.checkCanTarget(game, caster, this.orderId, target, targetReceiver); ability.checkCanTarget(game, caster, this.orderId, target, targetReceiver.reset());
if (targetReceiver.getTarget() != null) { if (targetReceiver.getTarget() != null) {
return ability.begin(game, caster, this.orderId, targetReceiver.getTarget()); return ability.begin(game, caster, this.orderId, targetReceiver.getTarget());
} }

View File

@ -9,10 +9,12 @@ import java.util.Map;
import com.etheller.interpreter.ast.scope.GlobalScope; import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.trigger.RemovableTriggerEvent; import com.etheller.interpreter.ast.scope.trigger.RemovableTriggerEvent;
import com.etheller.interpreter.ast.scope.trigger.Trigger; import com.etheller.interpreter.ast.scope.trigger.Trigger;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
import com.etheller.warsmash.parsers.jass.scope.CommonTriggerExecutionScope; import com.etheller.warsmash.parsers.jass.scope.CommonTriggerExecutionScope;
import com.etheller.warsmash.util.War3ID; import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener.CPlayerStateNotifier; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CPlayerStateListener.CPlayerStateNotifier;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.config.CBasePlayer; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.config.CBasePlayer;
@ -21,19 +23,37 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEve
public class CPlayer extends CBasePlayer { public class CPlayer extends CBasePlayer {
private final CRace race; private final CRace race;
private final float[] startLocation; private final float[] startLocation;
private int gold = 500; private int gold;
private int lumber = 150; private int lumber;
private int heroTokens;
private int foodCap; private int foodCap;
private int foodUsed; private int foodUsed;
private int foodCapCeiling = 101; // TODO should not have a default, I put 101 to make it stand out
private final Map<War3ID, Integer> rawcodeToTechtreeUnlocked = new HashMap<>(); private final Map<War3ID, Integer> rawcodeToTechtreeUnlocked = new HashMap<>();
private final Map<War3ID, Integer> rawcodeToTechtreeMaxAllowed = new HashMap<>();
private final List<CUnit> heroes = new ArrayList<>(); private final List<CUnit> heroes = new ArrayList<>();
private final EnumMap<JassGameEventsWar3, List<CPlayerEvent>> eventTypeToEvents = new EnumMap<>( private final EnumMap<JassGameEventsWar3, List<CPlayerEvent>> eventTypeToEvents = new EnumMap<>(
JassGameEventsWar3.class); JassGameEventsWar3.class);
// Player state data
private boolean givesBounty = false;
private boolean alliedVictory = false;
private int gameResult;
private int placed;
private boolean observerOnDeath;
private boolean observer;
private boolean unfollowable;
private int goldUpkeepRate;
private int lumberUpkeepRate;
private int goldGathered;
private int lumberGathered;
private boolean noCreepSleep;
// if you use triggers for this then the transient tag here becomes really // if you use triggers for this then the transient tag here becomes really
// questionable -- it already was -- but I meant for those to inform us // questionable -- it already was -- but I meant for those to inform us
// which fields shouldn't be persisted if we do game state save later // which fields shouldn't be persisted if we do game state save later
private transient CPlayerStateNotifier stateNotifier = new CPlayerStateNotifier(); private transient CPlayerStateNotifier stateNotifier = new CPlayerStateNotifier();
private float handicapXP;
public CPlayer(final CRace race, final float[] startLocation, final CBasePlayer configPlayer) { public CPlayer(final CRace race, final float[] startLocation, final CBasePlayer configPlayer) {
super(configPlayer); super(configPlayer);
@ -57,6 +77,10 @@ public class CPlayer extends CBasePlayer {
return this.lumber; return this.lumber;
} }
public int getHeroTokens() {
return this.heroTokens;
}
public int getFoodCap() { public int getFoodCap() {
return this.foodCap; return this.foodCap;
} }
@ -65,6 +89,10 @@ public class CPlayer extends CBasePlayer {
return this.foodUsed; return this.foodUsed;
} }
public int getFoodCapCeiling() {
return this.foodCapCeiling;
}
public float[] getStartLocation() { public float[] getStartLocation() {
return this.startLocation; return this.startLocation;
} }
@ -79,11 +107,21 @@ public class CPlayer extends CBasePlayer {
this.stateNotifier.lumberChanged(); this.stateNotifier.lumberChanged();
} }
public void setHeroTokens(final int heroTokens) {
this.heroTokens = heroTokens;
this.stateNotifier.heroTokensChanged();
}
public void setFoodCap(final int foodCap) { public void setFoodCap(final int foodCap) {
this.foodCap = foodCap; this.foodCap = foodCap;
this.stateNotifier.foodChanged(); this.stateNotifier.foodChanged();
} }
public void setFoodCapCeiling(final int foodCapCeiling) {
this.foodCapCeiling = foodCapCeiling;
this.stateNotifier.foodChanged();
}
public void setFoodUsed(final int foodUsed) { public void setFoodUsed(final int foodUsed) {
this.foodUsed = foodUsed; this.foodUsed = foodUsed;
this.stateNotifier.foodChanged(); this.stateNotifier.foodChanged();
@ -117,6 +155,18 @@ public class CPlayer extends CBasePlayer {
} }
} }
public void setTechtreeMaxAllowed(final War3ID war3id, final int maximum) {
this.rawcodeToTechtreeMaxAllowed.put(war3id, maximum);
}
public int getTechtreeMaxAllowed(final War3ID war3id) {
final Integer maxAllowed = this.rawcodeToTechtreeMaxAllowed.get(war3id);
if (maxAllowed != null) {
return maxAllowed;
}
return -1;
}
public void addStateListener(final CPlayerStateListener listener) { public void addStateListener(final CPlayerStateListener listener) {
this.stateNotifier.subscribe(listener); this.stateNotifier.subscribe(listener);
} }
@ -169,16 +219,17 @@ public class CPlayer extends CBasePlayer {
public void onHeroDeath(final CUnit hero) { public void onHeroDeath(final CUnit hero) {
this.stateNotifier.heroDeath(); this.stateNotifier.heroDeath();
firePlayerUnitEvents(hero, CommonTriggerExecutionScope.playerHeroRevivableScope(hero), firePlayerUnitEvents(hero, CommonTriggerExecutionScope::playerHeroRevivableScope,
JassGameEventsWar3.EVENT_PLAYER_HERO_REVIVABLE); JassGameEventsWar3.EVENT_PLAYER_HERO_REVIVABLE);
} }
private void firePlayerUnitEvents(final CUnit hero, final CommonTriggerExecutionScope eventScope, private void firePlayerUnitEvents(final CUnit hero,
final CommonTriggerExecutionScope.UnitEventScopeBuilder eventScopeBuilder,
final JassGameEventsWar3 eventType) { final JassGameEventsWar3 eventType) {
final List<CPlayerEvent> eventList = getEventList(eventType); final List<CPlayerEvent> eventList = getEventList(eventType);
if (eventList != null) { if (eventList != null) {
for (final CPlayerEvent event : eventList) { for (final CPlayerEvent event : eventList) {
event.fire(hero, eventScope); event.fire(hero, eventScopeBuilder.create(event.getTrigger(), hero));
} }
} }
} }
@ -188,10 +239,20 @@ public class CPlayer extends CBasePlayer {
} }
public void fireHeroLevelEvents(final CUnit hero) { public void fireHeroLevelEvents(final CUnit hero) {
firePlayerUnitEvents(hero, CommonTriggerExecutionScope.playerHeroRevivableScope(hero), firePlayerUnitEvents(hero, CommonTriggerExecutionScope::playerHeroRevivableScope,
JassGameEventsWar3.EVENT_PLAYER_HERO_LEVEL); JassGameEventsWar3.EVENT_PLAYER_HERO_LEVEL);
} }
public void fireUnitDeathEvents(final CUnit dyingUnit, final CUnit killingUnit) {
final List<CPlayerEvent> eventList = getEventList(JassGameEventsWar3.EVENT_PLAYER_UNIT_DEATH);
if (eventList != null) {
for (final CPlayerEvent event : eventList) {
event.fire(dyingUnit,
CommonTriggerExecutionScope.playerUnitDeathScope(event.getTrigger(), dyingUnit, killingUnit));
}
}
}
private List<CPlayerEvent> getOrCreateEventList(final JassGameEventsWar3 eventType) { private List<CPlayerEvent> getOrCreateEventList(final JassGameEventsWar3 eventType) {
List<CPlayerEvent> playerEvents = this.eventTypeToEvents.get(eventType); List<CPlayerEvent> playerEvents = this.eventTypeToEvents.get(eventType);
if (playerEvents == null) { if (playerEvents == null) {
@ -208,7 +269,14 @@ public class CPlayer extends CBasePlayer {
@Override @Override
public RemovableTriggerEvent addEvent(final GlobalScope globalScope, final Trigger whichTrigger, public RemovableTriggerEvent addEvent(final GlobalScope globalScope, final Trigger whichTrigger,
final JassGameEventsWar3 eventType) { final JassGameEventsWar3 eventType) {
final CPlayerEvent playerEvent = new CPlayerEvent(globalScope, this, whichTrigger, eventType); final CPlayerEvent playerEvent = new CPlayerEvent(globalScope, this, whichTrigger, eventType, null);
getOrCreateEventList(eventType).add(playerEvent);
return playerEvent;
}
public RemovableTriggerEvent addUnitEvent(final GlobalScope globalScope, final Trigger whichTrigger,
final JassGameEventsWar3 eventType, final TriggerBooleanExpression filter) {
final CPlayerEvent playerEvent = new CPlayerEvent(globalScope, this, whichTrigger, eventType, filter);
getOrCreateEventList(eventType).add(playerEvent); getOrCreateEventList(eventType).add(playerEvent);
return playerEvent; return playerEvent;
} }
@ -220,4 +288,126 @@ public class CPlayer extends CBasePlayer {
eventList.remove(playerEvent); eventList.remove(playerEvent);
} }
} }
public void setPlayerState(final CSimulation simulation, final CPlayerState whichPlayerState, final int value) {
switch (whichPlayerState) {
case GAME_RESULT:
this.gameResult = value;
break;
case RESOURCE_GOLD:
setGold(value);
break;
case RESOURCE_LUMBER:
setLumber(value);
break;
case RESOURCE_HERO_TOKENS:
setHeroTokens(value);
break;
case RESOURCE_FOOD_CAP:
setFoodCap(value);
break;
case RESOURCE_FOOD_USED:
setFoodUsed(value);
break;
case FOOD_CAP_CEILING:
setFoodCapCeiling(value);
break;
case ALLIED_VICTORY:
this.alliedVictory = (value != 0);
break;
case GIVES_BOUNTY:
this.givesBounty = (value != 0);
break;
case PLACED:
this.placed = value;
case OBSERVER_ON_DEATH:
this.observerOnDeath = (value != 0);
case OBSERVER:
this.observer = (value != 0);
case UNFOLLOWABLE:
this.unfollowable = (value != 0);
case GOLD_UPKEEP_RATE:
this.goldUpkeepRate = value;
break;
case LUMBER_UPKEEP_RATE:
this.lumberUpkeepRate = value;
break;
case GOLD_GATHERED:
this.goldGathered = value;
break;
case LUMBER_GATHERED:
this.goldGathered = value;
break;
case NO_CREEP_SLEEP:
this.noCreepSleep = (value != 0);
break;
default:
break;
}
}
public int getPlayerState(final CSimulation simulation, final CPlayerState whichPlayerState) {
switch (whichPlayerState) {
case GAME_RESULT:
return this.gameResult;
case RESOURCE_GOLD:
return getGold();
case RESOURCE_LUMBER:
return getLumber();
case RESOURCE_HERO_TOKENS:
return getHeroTokens();
case RESOURCE_FOOD_CAP:
return getFoodCap();
case RESOURCE_FOOD_USED:
return getFoodUsed();
case FOOD_CAP_CEILING:
return getFoodCapCeiling();
case ALLIED_VICTORY:
return this.alliedVictory ? 1 : 0;
case GIVES_BOUNTY:
return this.givesBounty ? 1 : 0;
case PLACED:
return this.placed;
case OBSERVER_ON_DEATH:
return this.observerOnDeath ? 1 : 0;
case OBSERVER:
return this.observer ? 1 : 0;
case UNFOLLOWABLE:
return this.unfollowable ? 1 : 0;
case GOLD_UPKEEP_RATE:
return this.goldUpkeepRate;
case LUMBER_UPKEEP_RATE:
return this.lumberUpkeepRate;
case GOLD_GATHERED:
return this.goldGathered;
case LUMBER_GATHERED:
return this.lumberGathered;
case NO_CREEP_SLEEP:
return this.noCreepSleep ? 1 : 0;
default:
return 0;
}
}
public boolean isObserver() {
return this.observer;
}
public boolean isTechtreeAllowedByMax(final War3ID techtree) {
final int techtreeMaxAllowed = getTechtreeMaxAllowed(techtree);
if (techtreeMaxAllowed > 0) {
if (getTechtreeUnlocked(techtree) >= techtreeMaxAllowed) {
return false;
}
}
return true;
}
public void setHandicapXP(final float handicapXP) {
this.handicapXP = handicapXP;
}
public float getHandicapXP() {
return this.handicapXP;
}
} }

View File

@ -4,6 +4,8 @@ import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope; import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.RemovableTriggerEvent; import com.etheller.interpreter.ast.scope.trigger.RemovableTriggerEvent;
import com.etheller.interpreter.ast.scope.trigger.Trigger; import com.etheller.interpreter.ast.scope.trigger.Trigger;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
import com.etheller.warsmash.parsers.jass.scope.CommonTriggerExecutionScope;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEventsWar3; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEventsWar3;
@ -12,13 +14,15 @@ public class CPlayerEvent implements RemovableTriggerEvent {
private final CPlayerJass player; private final CPlayerJass player;
private final Trigger trigger; private final Trigger trigger;
private final JassGameEventsWar3 eventType; private final JassGameEventsWar3 eventType;
private final TriggerBooleanExpression filter;
public CPlayerEvent(final GlobalScope globalScope, final CPlayerJass player, final Trigger trigger, public CPlayerEvent(final GlobalScope globalScope, final CPlayerJass player, final Trigger trigger,
final JassGameEventsWar3 eventType) { final JassGameEventsWar3 eventType, final TriggerBooleanExpression filter) {
this.globalScope = globalScope; this.globalScope = globalScope;
this.player = player; this.player = player;
this.trigger = trigger; this.trigger = trigger;
this.eventType = eventType; this.eventType = eventType;
this.filter = filter;
} }
public Trigger getTrigger() { public Trigger getTrigger() {
@ -35,6 +39,13 @@ public class CPlayerEvent implements RemovableTriggerEvent {
} }
public void fire(final CUnit hero, final TriggerExecutionScope scope) { public void fire(final CUnit hero, final TriggerExecutionScope scope) {
this.trigger.evaluate(this.globalScope, scope); if (this.filter != null) {
if (!this.filter.evaluate(this.globalScope, CommonTriggerExecutionScope.filterScope(scope, hero))) {
return;
}
}
if (this.trigger.evaluate(this.globalScope, scope)) {
this.trigger.execute(this.globalScope, scope);
}
} }
} }

View File

@ -1,6 +1,7 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.players; package com.etheller.warsmash.viewer5.handlers.w3x.simulation.players;
public enum CPlayerState { public enum CPlayerState {
GAME_RESULT,
// current resource levels // current resource levels
// //
RESOURCE_GOLD, RESOURCE_GOLD,

View File

@ -5,6 +5,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorHoldPosition; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehaviorHoldPosition;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderDropItemAtPoint; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderDropItemAtPoint;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderDropItemAtTargetWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderNoTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetPoint; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetPoint;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetWidget; import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.COrderTargetWidget;
@ -38,6 +39,16 @@ public class CPlayerUnitOrderExecutor implements CPlayerUnitOrderListener {
} }
} }
@Override
public void issueDropItemAtTargetOrder(final int unitHandleId, final int abilityHandleId, final int orderId,
final int targetItemHandleId, final int targetHeroHandleId, final boolean queue) {
final CUnit unit = this.game.getUnit(unitHandleId);
if (this.playerIndex == unit.getPlayerIndex()) {
unit.order(this.game, new COrderDropItemAtTargetWidget(abilityHandleId, orderId, targetItemHandleId,
targetHeroHandleId, queue), queue);
}
}
@Override @Override
public void issuePointOrder(final int unitHandleId, final int abilityHandleId, final int orderId, final float x, public void issuePointOrder(final int unitHandleId, final int abilityHandleId, final int orderId, final float x,
final float y, final boolean queue) { final float y, final boolean queue) {

View File

@ -8,6 +8,9 @@ public interface CPlayerUnitOrderListener {
void issueDropItemAtPointOrder(int unitHandleId, int abilityHandleId, int orderId, int targetHandleId, float x, void issueDropItemAtPointOrder(int unitHandleId, int abilityHandleId, int orderId, int targetHandleId, float x,
float y, final boolean queue); float y, final boolean queue);
void issueDropItemAtTargetOrder(int unitHandleId, int abilityHandleId, int orderId, int targetItemHandleId,
int targetHeroHandleId, final boolean queue);
void issueImmediateOrder(int unitHandleId, int abilityHandleId, int orderId, boolean queue); void issueImmediateOrder(int unitHandleId, int abilityHandleId, int orderId, boolean queue);
void unitCancelTrainingItem(int unitHandleId, int cancelIndex); void unitCancelTrainingItem(int unitHandleId, int cancelIndex);

View File

@ -14,7 +14,7 @@ public enum CRace {
this.id = id; this.id = id;
} }
public static CRace[] VALUES = values(); public static CRace[] VALUES = { null, HUMAN, ORC, UNDEAD, NIGHTELF, DEMON, null, OTHER };
public int getId() { public int getId() {
return this.id; return this.id;

View File

@ -20,10 +20,10 @@ public class CRegionTriggerEnter {
} }
public void fire(final CUnit unit, final CRegion region) { public void fire(final CUnit unit, final CRegion region) {
if (this.filter.evaluate(this.globalScope, if ((this.filter == null) || this.filter.evaluate(this.globalScope,
CommonTriggerExecutionScope.filterScope(TriggerExecutionScope.EMPTY, unit))) { CommonTriggerExecutionScope.filterScope(TriggerExecutionScope.EMPTY, unit))) {
final CommonTriggerExecutionScope eventScope = CommonTriggerExecutionScope final CommonTriggerExecutionScope eventScope = CommonTriggerExecutionScope
.unitEnterRegionScope(TriggerExecutionScope.EMPTY, unit, region); .unitEnterRegionScope(this.trigger, TriggerExecutionScope.EMPTY, unit, region);
if (this.trigger.evaluate(this.globalScope, eventScope)) { if (this.trigger.evaluate(this.globalScope, eventScope)) {
this.trigger.execute(this.globalScope, eventScope); this.trigger.execute(this.globalScope, eventScope);
} }

View File

@ -23,7 +23,7 @@ public class CRegionTriggerLeave {
if (this.filter.evaluate(this.globalScope, if (this.filter.evaluate(this.globalScope,
CommonTriggerExecutionScope.filterScope(TriggerExecutionScope.EMPTY, unit))) { CommonTriggerExecutionScope.filterScope(TriggerExecutionScope.EMPTY, unit))) {
final CommonTriggerExecutionScope eventScope = CommonTriggerExecutionScope final CommonTriggerExecutionScope eventScope = CommonTriggerExecutionScope
.unitLeaveRegionScope(TriggerExecutionScope.EMPTY, unit, region); .unitLeaveRegionScope(this.trigger, TriggerExecutionScope.EMPTY, unit, region);
if (this.trigger.evaluate(this.globalScope, eventScope)) { if (this.trigger.evaluate(this.globalScope, eventScope)) {
this.trigger.execute(this.globalScope, eventScope); this.trigger.execute(this.globalScope, eventScope);
} }

View File

@ -0,0 +1,19 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.sound;
public class CMIDISound implements CSound {
private final String soundLabel;
private final int fadeInRate;
private final int fadeOutRate;
public CMIDISound(final String soundLabel, final int fadeInRate, final int fadeOutRate) {
this.soundLabel = soundLabel;
this.fadeInRate = fadeInRate;
this.fadeOutRate = fadeOutRate;
}
@Override
public void start() {
System.err.println(
"Not starting MIDI sound because we don't have a LibGDX API to play those: " + this.soundLabel);
}
}

View File

@ -0,0 +1,5 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.sound;
public interface CSound {
void start();
}

View File

@ -0,0 +1,64 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.sound;
import com.badlogic.gdx.audio.Sound;
import com.etheller.warsmash.viewer5.AudioBufferSource;
import com.etheller.warsmash.viewer5.AudioContext;
import com.etheller.warsmash.viewer5.AudioPanner;
public class CSoundFilename implements CSound {
private final Sound sound;
private final boolean looping;
private final boolean stopWhenOutOfRange;
private final int fadeInRate;
private final int fadeOutRate;
private final AudioContext audioContext;
private float x;
private float y;
private float z;
private final float volume = 1.0f;
private final float pitch = 1.0f;
private final float minDistance = 99999;
private final float distanceCutoff = 99999;
private final String eaxSetting;
public CSoundFilename(final Sound sound, final AudioContext audioContext, final boolean looping,
final boolean stopWhenOutOfRange, final int fadeInRate, final int fadeOutRate, final String eaxSetting) {
this.sound = sound;
this.audioContext = audioContext;
this.looping = looping;
this.stopWhenOutOfRange = stopWhenOutOfRange;
this.fadeInRate = fadeInRate;
this.fadeOutRate = fadeOutRate;
this.eaxSetting = eaxSetting;
}
public void setPosition(final float x, final float y, final float z) {
this.x = x;
this.y = y;
this.z = z;
}
@Override
public void start() {
if (this.audioContext == null) {
return;
}
final AudioPanner panner = this.audioContext.createPanner(this.stopWhenOutOfRange);
final AudioBufferSource source = this.audioContext.createBufferSource();
// Panner settings
panner.setPosition(this.x, this.y, this.z);
panner.setDistances(this.distanceCutoff, this.minDistance);
panner.connect(this.audioContext.destination);
// Source.
source.buffer = this.sound;
source.connect(panner);
// Make a sound.
source.start(0, this.volume, this.pitch, this.looping);
}
}

View File

@ -0,0 +1,32 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.sound;
import com.etheller.warsmash.viewer5.AudioContext;
import com.etheller.warsmash.viewer5.handlers.w3x.UnitSound;
public class CSoundFromLabel implements CSound {
private final UnitSound sound;
private final boolean looping;
private final boolean is3d;
private final boolean stopWhenOutOfRange;
private final int fadeInRate;
private final int fadeOutRate;
private final AudioContext audioContext;
public CSoundFromLabel(final UnitSound sound, final AudioContext audioContext, final boolean looping,
final boolean is3d, final boolean stopWhenOutOfRange, final int fadeInRate, final int fadeOutRate) {
this.sound = sound;
this.audioContext = audioContext;
this.looping = looping;
this.is3d = is3d;
this.stopWhenOutOfRange = stopWhenOutOfRange;
this.fadeInRate = fadeInRate;
this.fadeOutRate = fadeOutRate;
}
@Override
public void start() {
this.sound.play(this.audioContext, 0, 0, 0);
}
}

View File

@ -24,9 +24,10 @@ public class CTimerJass extends CTimer {
@Override @Override
public void onFire() { public void onFire() {
final CommonTriggerExecutionScope executionScope = CommonTriggerExecutionScope.expiringTimer(this); final CommonTriggerExecutionScope handlerScope = CommonTriggerExecutionScope.expiringTimer(null, this);
this.handlerFunc.call(Collections.emptyList(), this.jassGlobalScope, executionScope); this.handlerFunc.call(Collections.emptyList(), this.jassGlobalScope, handlerScope);
for (final Trigger trigger : this.eventTriggers) { for (final Trigger trigger : this.eventTriggers) {
final CommonTriggerExecutionScope executionScope = CommonTriggerExecutionScope.expiringTimer(trigger, this);
if (trigger.evaluate(this.jassGlobalScope, executionScope)) { if (trigger.evaluate(this.jassGlobalScope, executionScope)) {
trigger.execute(this.jassGlobalScope, executionScope); trigger.execute(this.jassGlobalScope, executionScope);
} }

View File

@ -26,7 +26,8 @@ public enum CDamageType {
DEMOLITION, DEMOLITION,
SLOW_POISON, SLOW_POISON,
SPIRIT_LINK, SPIRIT_LINK,
SHADOW_STRIKE; SHADOW_STRIKE,
UNIVERSAL;
public static CDamageType[] VALUES = values(); public static CDamageType[] VALUES = values();
} }

View File

@ -0,0 +1,87 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.unit;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.scope.trigger.RemovableTriggerEvent;
import com.etheller.interpreter.ast.scope.trigger.Trigger;
import com.etheller.interpreter.ast.scope.trigger.TriggerBooleanExpression;
import com.etheller.warsmash.parsers.jass.scope.CommonTriggerExecutionScope;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CDestructable;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CItem;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityPointTarget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.targeting.AbilityTargetVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.trigger.JassGameEventsWar3;
public class CWidgetEvent implements RemovableTriggerEvent {
private final GlobalScope globalScope;
private final CWidget widget;
private final Trigger trigger;
private final JassGameEventsWar3 eventType;
private final TriggerBooleanExpression filter;
public CWidgetEvent(final GlobalScope globalScope, final CWidget widget, final Trigger trigger,
final JassGameEventsWar3 eventType, final TriggerBooleanExpression filter) {
this.globalScope = globalScope;
this.widget = widget;
this.trigger = trigger;
this.eventType = eventType;
this.filter = filter;
}
public Trigger getTrigger() {
return this.trigger;
}
public JassGameEventsWar3 getEventType() {
return this.eventType;
}
@Override
public void remove() {
this.widget.removeEvent(this);
}
public void fire(final CWidget triggerWidget, final TriggerExecutionScope scope) {
if (this.filter != null) {
if (!this.filter.evaluate(this.globalScope, triggerWidget.visit(ScopeBuilder.INSTANCE.reset(scope)))) {
return;
}
}
if (this.trigger.evaluate(this.globalScope, scope)) {
this.trigger.execute(this.globalScope, scope);
}
}
private static final class ScopeBuilder implements AbilityTargetVisitor<CommonTriggerExecutionScope> {
public static final ScopeBuilder INSTANCE = new ScopeBuilder();
private TriggerExecutionScope parentScope;
public ScopeBuilder reset(final TriggerExecutionScope parentScope) {
this.parentScope = parentScope;
return this;
}
@Override
public CommonTriggerExecutionScope accept(final AbilityPointTarget target) {
throw new IllegalStateException("what?");
}
@Override
public CommonTriggerExecutionScope accept(final CUnit target) {
return CommonTriggerExecutionScope.filterScope(this.parentScope, target);
}
@Override
public CommonTriggerExecutionScope accept(final CDestructable target) {
return CommonTriggerExecutionScope.filterScope(this.parentScope, target);
}
@Override
public CommonTriggerExecutionScope accept(final CItem target) {
return CommonTriggerExecutionScope.filterScope(this.parentScope, target);
}
}
}

View File

@ -16,4 +16,6 @@ public interface AbilityActivationReceiver {
void cargoCapacityUnavailable(); void cargoCapacityUnavailable();
void disabled(); void disabled();
void techtreeMaximumReached();
} }

View File

@ -26,6 +26,11 @@ public class BooleanAbilityActivationReceiver implements AbilityActivationReceiv
this.ok = false; this.ok = false;
} }
@Override
public void techtreeMaximumReached() {
this.ok = false;
}
@Override @Override
public void casterMovementDisabled() { public void casterMovementDisabled() {
this.ok = false; this.ok = false;

View File

@ -64,6 +64,11 @@ public class MeleeUIAbilityActivationReceiver implements AbilityActivationReceiv
this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit);
} }
@Override
public void techtreeMaximumReached() {
this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit);
}
@Override @Override
public void casterMovementDisabled() { public void casterMovementDisabled() {
this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit); this.genericError.onClick(this.commandErrorListener, this.worldSceneAudioContext, this.commandedUnit);

View File

@ -22,6 +22,13 @@ public interface SimulationRenderController {
CUnit createUnit(CSimulation simulation, final War3ID typeId, final int playerIndex, final float x, final float y, CUnit createUnit(CSimulation simulation, final War3ID typeId, final int playerIndex, final float x, final float y,
final float facing); final float facing);
CItem createItem(CSimulation simulation, final War3ID typeId, final float x, final float y);
CDestructable createDestructable(War3ID typeId, float x, float y, float facing, float scale, int variation);
CDestructable createDestructableZ(War3ID typeId, float x, float y, float z, float facing, float scale,
int variation);
void createInstantAttackEffect(CSimulation cSimulation, CUnit source, CUnitAttackInstant attack, CWidget target); void createInstantAttackEffect(CSimulation cSimulation, CUnit source, CUnitAttackInstant attack, CWidget target);
void spawnDamageSound(CWidget damagedDestructable, String weaponSound, String armorType); void spawnDamageSound(CWidget damagedDestructable, String weaponSound, String armorType);

View File

@ -40,6 +40,11 @@ public class StringMsgAbilityActivationReceiver implements AbilityActivationRece
this.message = "NOTEXTERN: Requires " + type; this.message = "NOTEXTERN: Requires " + type;
} }
@Override
public void techtreeMaximumReached() {
this.message = "NOTEXTERN: Techtree maximum reached.";
}
@Override @Override
public void cargoCapacityUnavailable() { public void cargoCapacityUnavailable() {
this.message = "NOTEXTERN: Cargo capacity unavailable."; this.message = "NOTEXTERN: Cargo capacity unavailable.";

View File

@ -10,6 +10,7 @@ public enum MenuCursorState {
SCROLL_DOWN_RIGHT("Scroll Down Right"), SCROLL_DOWN_RIGHT("Scroll Down Right"),
SCROLL_UP_LEFT("Scroll Up Left"), SCROLL_UP_LEFT("Scroll Up Left"),
SCROLL_UP_RIGHT("Scroll Up Right"), SCROLL_UP_RIGHT("Scroll Up Right"),
SELECT("Select"),
TARGET_CURSOR(null), // handled specially TARGET_CURSOR(null), // handled specially
HOLD_ITEM("HoldItem"); HOLD_ITEM("HoldItem");
private String animationName; private String animationName;

View File

@ -0,0 +1,215 @@
package com.etheller.warsmash.viewer5.handlers.w3x.ui;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.fdf.frames.AbstractRenderableFrame;
import com.etheller.warsmash.parsers.fdf.frames.SimpleStatusBarFrame;
import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.ClickableActionFrame;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.MultiSelectionIconListener;
public class MultiSelectionIcon extends AbstractRenderableFrame implements ClickableActionFrame {
public static final float HP_BAR_HEIGHT_RATIO = 0.175f;
public static final float HP_BAR_SPACING_RATIO = 0.02f;
private TextureFrame iconFrame;
private final MultiSelectionIconListener clickListener;
private float defaultWidth;
private float defaultHeight;
private final int queueIconIndexId;
private String toolTip;
private String uberTip;
private boolean focused;
private SimpleStatusBarFrame hpBarFrame;
private SimpleStatusBarFrame manaBarFrame;
public MultiSelectionIcon(final String name, final UIFrame parent, final MultiSelectionIconListener clickListener,
final int queueIconIndexId) {
super(name, parent);
this.clickListener = clickListener;
this.queueIconIndexId = queueIconIndexId;
}
public void set(final TextureFrame iconFrame, final SimpleStatusBarFrame hpBarFrame,
final SimpleStatusBarFrame manaBarFrame) {
this.iconFrame = iconFrame;
this.hpBarFrame = hpBarFrame;
this.manaBarFrame = manaBarFrame;
setVisible(true);
}
public void clear() {
setVisible(false);
}
public void setTexture(final Texture texture) {
this.iconFrame.setTexture(texture);
}
@Override
protected void innerPositionBounds(final GameUI gameUI, final Viewport viewport) {
this.iconFrame.positionBounds(gameUI, viewport);
this.hpBarFrame.positionBounds(gameUI, viewport);
this.manaBarFrame.positionBounds(gameUI, viewport);
}
@Override
protected void internalRender(final SpriteBatch batch, final BitmapFont baseFont, final GlyphLayout glyphLayout) {
this.iconFrame.render(batch, baseFont, glyphLayout);
this.hpBarFrame.render(batch, baseFont, glyphLayout);
this.manaBarFrame.render(batch, baseFont, glyphLayout);
}
@Override
public UIFrame touchDown(final float screenX, final float screenY, final int button) {
if (isVisible() && this.renderBounds.contains(screenX, screenY)) {
return this;
}
return super.touchDown(screenX, screenY, button);
}
@Override
public UIFrame touchUp(final float screenX, final float screenY, final int button) {
if (isVisible() && this.renderBounds.contains(screenX, screenY)) {
return this;
}
return super.touchUp(screenX, screenY, button);
}
@Override
public void onClick(final int button) {
this.clickListener.multiSelectIconClicked(this.queueIconIndexId);
}
@Override
public void setWidth(final float width) {
this.defaultWidth = width;
super.setWidth(width);
}
@Override
public void setHeight(final float height) {
this.defaultHeight = height;
super.setHeight(height);
}
private void innerSetDimensions(final float newWidth, final float newHeight) {
this.iconFrame.setWidth(newWidth);
this.iconFrame.setHeight(newHeight);
this.hpBarFrame.setWidth(newWidth * 1.05f);
this.hpBarFrame.setHeight(newHeight * HP_BAR_HEIGHT_RATIO);
this.manaBarFrame.setWidth(newWidth * 1.05f);
this.manaBarFrame.setHeight(newHeight * HP_BAR_HEIGHT_RATIO);
}
public void showUnFocused(final GameUI gameUI, final Viewport uiViewport) {
final float newWidth = this.defaultWidth * 0.75f;
final float newHeight = this.defaultHeight * 0.75f;
innerSetDimensions(newWidth, newHeight);
positionBounds(gameUI, uiViewport);
this.focused = false;
}
public void showFocused(final GameUI gameUI, final Viewport uiViewport) {
innerSetDimensions(this.defaultWidth, this.defaultHeight);
positionBounds(gameUI, uiViewport);
this.focused = true;
}
public void showMousePressed(final GameUI gameUI, final Viewport uiViewport) {
final float ratio = this.focused ? 0.95f : 0.70f;
final float newWidth = this.defaultWidth * ratio;
final float newHeight = this.defaultHeight * ratio;
innerSetDimensions(newWidth, newHeight);
positionBounds(gameUI, uiViewport);
}
@Override
public void mouseDown(final GameUI gameUI, final Viewport uiViewport) {
this.clickListener.multiSelectIconPress(this.queueIconIndexId);
showMousePressed(gameUI, uiViewport);
}
public void showMouseReleased(final GameUI gameUI, final Viewport uiViewport) {
final float ratio = this.focused ? 1.00f : 0.75f;
final float newWidth = this.defaultWidth * ratio;
final float newHeight = this.defaultHeight * ratio;
innerSetDimensions(newWidth, newHeight);
positionBounds(gameUI, uiViewport);
}
@Override
public void mouseUp(final GameUI gameUI, final Viewport uiViewport) {
this.clickListener.multiSelectIconRelease(this.queueIconIndexId);
showMouseReleased(gameUI, uiViewport);
}
@Override
public void mouseEnter(final GameUI gameUI, final Viewport uiViewport) {
}
@Override
public void mouseExit(final GameUI gameUI, final Viewport uiViewport) {
}
@Override
public UIFrame getFrameChildUnderMouse(final float screenX, final float screenY) {
if (isVisible() && this.renderBounds.contains(screenX, screenY)) {
return this;
}
return null;
}
public void setToolTip(final String toolTip) {
this.toolTip = toolTip;
}
public void setUberTip(final String uberTip) {
this.uberTip = uberTip;
}
@Override
public String getToolTip() {
return this.toolTip;
}
@Override
public String getUberTip() {
return this.uberTip;
}
@Override
public int getToolTipFoodCost() {
return 0;
}
@Override
public int getToolTipGoldCost() {
return 0;
}
@Override
public int getToolTipLumberCost() {
return 0;
}
public void setLifeRatioRemaining(final float lifeRatioRemaining) {
this.hpBarFrame.getBarFrame().setColor(Math.min(1.0f, 2.0f - (lifeRatioRemaining * 2)),
Math.min(1.0f, lifeRatioRemaining * 2), 0, 1.0f);
this.hpBarFrame.setValue(lifeRatioRemaining);
}
public void setManaRatioRemaining(final float lifeRatioRemaining) {
this.manaBarFrame.setValue(lifeRatioRemaining);
}
public void setManaBarVisible(final boolean visible) {
this.manaBarFrame.setVisible(visible);
}
}

View File

@ -0,0 +1,9 @@
package com.etheller.warsmash.viewer5.handlers.w3x.ui.command;
public interface MultiSelectionIconListener {
void multiSelectIconClicked(int index);
void multiSelectIconPress(int index);
void multiSelectIconRelease(int index);
}

View File

@ -0,0 +1,46 @@
package com.etheller.warsmash.viewer5.handlers.w3x.ui.dialog;
import com.etheller.warsmash.parsers.fdf.GameUI;
import com.etheller.warsmash.parsers.fdf.frames.StringFrame;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CSimulation;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.timers.CTimer;
public class CTimerDialog {
private final CTimer timer;
private final UIFrame timerDialogFrame;
private final StringFrame valueFrame;
private final StringFrame titleFrame;
public CTimerDialog(final CTimer timer, final UIFrame timerDialogFrame, final StringFrame valueFrame,
final StringFrame titleFrame) {
this.timer = timer;
this.timerDialogFrame = timerDialogFrame;
this.valueFrame = valueFrame;
this.titleFrame = titleFrame;
}
public void setTitle(final GameUI rootFrame, final String title) {
rootFrame.setText(this.titleFrame, title);
}
public void setValue(final GameUI rootFrame, final String value) {
rootFrame.setText(this.valueFrame, value);
}
public void setVisible(final boolean visible) {
this.timerDialogFrame.setVisible(visible);
}
public void update(final GameUI rootFrame, final CSimulation simulation) {
if (this.timerDialogFrame.isVisible() && (this.timer != null)) {
final float remaining = this.timer.getRemaining(simulation);
final int secondsRemaining = (int) remaining;
final int minutes = secondsRemaining / 60;
final int seconds = secondsRemaining % 60;
rootFrame.setText(this.valueFrame, minutes + ":" + seconds);
}
}
}

View File

@ -34,17 +34,74 @@ global :
| |
CONSTANT? type ID assignTail newlines # DefinitionGlobal CONSTANT? type ID assignTail newlines # DefinitionGlobal
; ;
local :
LOCAL type ID newlines # BasicLocal
|
LOCAL type ID assignTail newlines # DefinitionLocal
;
assignTail: assignTail:
EQUALS expression; EQUALS expression;
expression: multDivExpression:
multDivExpression '*' baseExpression # MultiplicationExpression
|
multDivExpression '/' baseExpression # DivisionExpression
|
baseExpression # BaseMultiplicationExpression
;
simpleArithmeticExpression:
simpleArithmeticExpression '+' multDivExpression # AdditionExpression
|
simpleArithmeticExpression '-' multDivExpression # SubtrationExpression
|
multDivExpression # BaseAdditionExpression
;
boolComparisonExpression:
boolComparisonExpression '<' simpleArithmeticExpression # BooleanLessExpression
|
boolComparisonExpression '>' simpleArithmeticExpression # BooleanGreaterExpression
|
boolComparisonExpression '<=' simpleArithmeticExpression # BooleanLessOrEqualsExpression
|
boolComparisonExpression '>=' simpleArithmeticExpression # BooleanGreaterOrEqualsExpression
|
simpleArithmeticExpression # BaseBoolComparisonExpression
;
boolEqualityExpression:
boolEqualityExpression '==' boolComparisonExpression # EqualsExpression
|
boolEqualityExpression '!=' boolComparisonExpression # NotEqualsExpression
|
boolComparisonExpression # BaseBoolExpression
;
boolAndsExpression:
boolAndsExpression AND boolEqualityExpression # BooleanAndExpression
|
boolEqualityExpression # BaseBoolAndsExpression
;
boolExpression:
boolExpression OR boolAndsExpression # BooleanOrExpression
|
boolAndsExpression # BaseBoolOrsExpression
;
baseExpression:
ID # ReferenceExpression ID # ReferenceExpression
| |
STRING_LITERAL #StringLiteralExpression STRING_LITERAL #StringLiteralExpression
| |
INTEGER #IntegerLiteralExpression INTEGER #IntegerLiteralExpression
| |
RAWCODE #RawcodeLiteralExpression
|
REAL #RealLiteralExpression
|
FUNCTION ID #FunctionReferenceExpression FUNCTION ID #FunctionReferenceExpression
| |
NULL # NullExpression NULL # NullExpression
@ -59,9 +116,14 @@ expression:
| |
'(' expression ')' # ParentheticalExpression '(' expression ')' # ParentheticalExpression
| |
NOT expression # NotExpression NOT baseExpression # NotExpression
|
'-' baseExpression # NegateExpression
; ;
expression:
boolExpression;
functionExpression: functionExpression:
ID '(' argsList ')' ID '(' argsList ')'
| |
@ -72,6 +134,8 @@ argsList:
expression # SingleArgument expression # SingleArgument
| |
expression ',' argsList # ListArgument expression ',' argsList # ListArgument
|
# EmptyArgument
; ;
//#booleanExpression: //#booleanExpression:
@ -87,6 +151,14 @@ statement:
| |
RETURN expression newlines # ReturnStatement RETURN expression newlines # ReturnStatement
| |
RETURN newlines # ReturnNothingStatement
|
EXITWHEN expression newlines # ExitWhenStatement
|
local # LocalStatement
|
LOOP newlines statements ENDLOOP newlines # LoopStatement
|
IF ifStatementPartial # IfStatement IF ifStatementPartial # IfStatement
; ;
@ -182,16 +254,27 @@ ELSE : 'else';
ENDIF : 'endif'; ENDIF : 'endif';
ELSEIF : 'elseif'; ELSEIF : 'elseif';
CONSTANT : 'constant'; CONSTANT : 'constant';
LOCAL : 'local';
LOOP : 'loop';
ENDLOOP : 'endloop';
EXITWHEN : 'exitwhen';
STRING_LITERAL : ('"'.*?'"'); STRING_LITERAL : ('"'.*?'"');
INTEGER : [0]|([1-9][0-9]*) ; INTEGER : [0]|([1-9][0-9]*) ;
RAWCODE : ('\''.*?'\'');
REAL : (([0]|([1-9][0-9]*))'.'[0-9]*)|('.'([0]|([1-9][0-9]*))) ;
NULL : 'null' ; NULL : 'null' ;
TRUE : 'true' ; TRUE : 'true' ;
FALSE : 'false' ; FALSE : 'false' ;
NOT : 'not'; NOT : 'not';
OR : 'or';
AND : 'and';
ID : ([a-zA-Z_][a-zA-Z_0-9]*) ; // match identifiers ID : ([a-zA-Z_][a-zA-Z_0-9]*) ; // match identifiers

View File

@ -13,12 +13,20 @@ public class Assignable {
} }
public void setValue(final JassValue value) { public void setValue(final JassValue value) {
if (value == null) {
if (!this.type.isNullable()) {
throw new RuntimeException("Type " + this.type.getName() + " cannot be assigned to null!");
}
this.value = this.type.getNullValue();
}
else {
final JassType valueType = value.visit(JassTypeGettingValueVisitor.getInstance()); final JassType valueType = value.visit(JassTypeGettingValueVisitor.getInstance());
if (!this.type.isAssignableFrom(valueType)) { if (!this.type.isAssignableFrom(valueType)) {
throw new RuntimeException("Incompatible types " + valueType.getName() + " != " + this.type.getName()); throw new RuntimeException("Incompatible types " + valueType.getName() + " != " + this.type.getName());
} }
this.value = value; this.value = value;
} }
}
public JassValue getValue() { public JassValue getValue() {
return this.value; return this.value;

View File

@ -0,0 +1,38 @@
package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.ArithmeticJassValueVisitor;
import com.etheller.interpreter.ast.value.visitor.ArithmeticLeftHandNullJassValueVisitor;
public class ArithmeticJassExpression implements JassExpression {
private final JassExpression leftExpression;
private final JassExpression rightExpression;
private final ArithmeticSign arithmeticSign;
public ArithmeticJassExpression(final JassExpression leftExpression, final JassExpression rightExpression,
final ArithmeticSign arithmeticSign) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
this.arithmeticSign = arithmeticSign;
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
final JassValue leftValue = this.leftExpression.evaluate(globalScope, localScope, triggerScope);
final JassValue rightValue = this.rightExpression.evaluate(globalScope, localScope, triggerScope);
if (leftValue == null) {
if (rightValue == null) {
return this.arithmeticSign.apply((String) null, (String) null);
}
else {
return rightValue.visit(ArithmeticLeftHandNullJassValueVisitor.INSTANCE.reset(this.arithmeticSign));
}
}
return leftValue.visit(ArithmeticJassValueVisitor.INSTANCE.reset(rightValue, this.arithmeticSign));
}
}

View File

@ -0,0 +1,26 @@
package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.value.BooleanJassValue;
import com.etheller.interpreter.ast.value.CodeJassValue;
import com.etheller.interpreter.ast.value.HandleJassValue;
import com.etheller.interpreter.ast.value.IntegerJassValue;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.RealJassValue;
public interface ArithmeticSign {
JassValue apply(BooleanJassValue left, BooleanJassValue right);
JassValue apply(RealJassValue left, RealJassValue right);
JassValue apply(RealJassValue left, IntegerJassValue right);
JassValue apply(IntegerJassValue left, RealJassValue right);
JassValue apply(IntegerJassValue left, IntegerJassValue right);
JassValue apply(String left, String right);
JassValue apply(HandleJassValue left, HandleJassValue right);
JassValue apply(CodeJassValue left, CodeJassValue right);
}

View File

@ -0,0 +1,528 @@
package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.value.BooleanJassValue;
import com.etheller.interpreter.ast.value.CodeJassValue;
import com.etheller.interpreter.ast.value.HandleJassValue;
import com.etheller.interpreter.ast.value.IntegerJassValue;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.RealJassValue;
import com.etheller.interpreter.ast.value.StringJassValue;
public enum ArithmeticSigns implements ArithmeticSign {
ADD() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Cannot perform numeric arithmetic on boolean");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() + right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return new RealJassValue(left.getValue() + right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() + right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return new IntegerJassValue(left.getValue() + right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
return new StringJassValue(left + right);
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on handle");
}
@Override
public JassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on code");
}
},
SUBTRACT() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Cannot perform numeric arithmetic on boolean");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() - right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return new RealJassValue(left.getValue() - right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() - right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return new IntegerJassValue(left.getValue() - right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on string");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on handle");
}
@Override
public JassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on code");
}
},
MULTIPLY() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Cannot perform numeric arithmetic on boolean");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() * right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return new RealJassValue(left.getValue() * right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() * right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return new IntegerJassValue(left.getValue() * right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on string");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on handle");
}
@Override
public JassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on code");
}
},
DIVIDE() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Cannot perform numeric arithmetic on boolean");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() / right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return new RealJassValue(left.getValue() / right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return new RealJassValue(left.getValue() / right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return new IntegerJassValue(left.getValue() / right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on string");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on handle");
}
@Override
public JassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Cannot perform arithmetic on code");
}
},
OR() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
return BooleanJassValue.of(left.getValue() || right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on string");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on handle");
}
@Override
public JassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on code");
}
},
AND() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
return BooleanJassValue.of(left.getValue() && right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on number");
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on string");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on handle");
}
@Override
public JassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Cannot perform boolean arithmetic on code");
}
},
EQUALS() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
return BooleanJassValue.of(left.getValue() == right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return BooleanJassValue.of(Math.abs(left.getValue() - right.getValue()) <= 0.00001);
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(Math.abs(left.getValue() - right.getValue()) <= 0.00001);
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return BooleanJassValue.of(Math.abs(left.getValue() - right.getValue()) <= 0.00001);
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(Math.abs(left.getValue() - right.getValue()) <= 0.00001);
}
@Override
public BooleanJassValue apply(final String left, final String right) {
return BooleanJassValue.of(equals(left, right));
}
@Override
public BooleanJassValue apply(final HandleJassValue left, final HandleJassValue right) {
return BooleanJassValue.of(equals(left, right));
}
@Override
public BooleanJassValue apply(final CodeJassValue left, final CodeJassValue right) {
return BooleanJassValue.of(equals(left, right));
}
},
NOT_EQUALS() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
return BooleanJassValue.of(left.getValue() != right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() != right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() != right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() != right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() != right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
return BooleanJassValue.of(!equals(left, right));
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
return BooleanJassValue.of(!equals(left, right));
}
@Override
public BooleanJassValue apply(final CodeJassValue left, final CodeJassValue right) {
return BooleanJassValue.of(!equals(left, right));
}
},
LESS() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() < right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() < right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() < right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() < right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public BooleanJassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
},
LESS_OR_EQUALS() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() <= right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() <= right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() <= right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() <= right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public BooleanJassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
},
GREATER() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() > right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() > right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() > right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() > right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public BooleanJassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
},
GREATER_OR_EQUALS() {
@Override
public JassValue apply(final BooleanJassValue left, final BooleanJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final RealJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() >= right.getValue());
}
@Override
public JassValue apply(final RealJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() >= right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final RealJassValue right) {
return BooleanJassValue.of(left.getValue() >= right.getValue());
}
@Override
public JassValue apply(final IntegerJassValue left, final IntegerJassValue right) {
return BooleanJassValue.of(left.getValue() >= right.getValue());
}
@Override
public JassValue apply(final String left, final String right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public JassValue apply(final HandleJassValue left, final HandleJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
@Override
public BooleanJassValue apply(final CodeJassValue left, final CodeJassValue right) {
throw new UnsupportedOperationException("Invalid type for specified operator");
}
};
private static boolean equals(final String left, final String right) {
boolean equals;
if (left == null) {
if (right == null) {
equals = true;
}
else {
equals = false;
}
}
else {
equals = left.equals(right);
}
return equals;
}
private static boolean equals(final HandleJassValue left, final HandleJassValue right) {
return (left.getJavaValue() == right.getJavaValue()) && (left.getType() == right.getType());
}
private static boolean equals(final CodeJassValue left, final CodeJassValue right) {
return (left.getValue() == right.getValue());
}
}

View File

@ -30,7 +30,12 @@ public class FunctionCallJassExpression implements JassExpression {
final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope, triggerScope); final JassValue evaluatedExpression = expr.evaluate(globalScope, localScope, triggerScope);
evaluatedExpressions.add(evaluatedExpression); evaluatedExpressions.add(evaluatedExpression);
} }
try {
return functionByName.call(evaluatedExpressions, globalScope, triggerScope); return functionByName.call(evaluatedExpressions, globalScope, triggerScope);
} }
catch (final Exception exc) {
throw new RuntimeException("Function call by name failed for name: " + this.functionName, exc);
}
}
} }

View File

@ -0,0 +1,22 @@
package com.etheller.interpreter.ast.expression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.visitor.NegateJassValueVisitor;
public class NegateJassExpression implements JassExpression {
private final JassExpression expression;
public NegateJassExpression(final JassExpression expression) {
this.expression = expression;
}
@Override
public JassValue evaluate(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
return this.expression.evaluate(globalScope, localScope, triggerScope)
.visit(NegateJassValueVisitor.getInstance());
}
}

View File

@ -22,6 +22,9 @@ public class JassParameter {
} }
public boolean matchesType(final JassValue value) { public boolean matchesType(final JassValue value) {
if (value == null) {
return this.type.isNullable();
}
final JassType valueType = value.visit(JassTypeGettingValueVisitor.getInstance()); final JassType valueType = value.visit(JassTypeGettingValueVisitor.getInstance());
return this.type.isAssignableFrom(valueType); return this.type.isAssignableFrom(valueType);
} }

View File

@ -22,6 +22,13 @@ public class NativeJassFunction extends AbstractJassFunction {
@Override @Override
protected JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope, protected JassValue innerCall(final List<JassValue> arguments, final GlobalScope globalScope,
final TriggerExecutionScope triggerScope, final LocalScope localScope) { final TriggerExecutionScope triggerScope, final LocalScope localScope) {
if (this.implementation == null) {
System.err.println(
"Call to native function that was declared but had no native implementation: " + this.name);
return null;
// throw new UnsupportedOperationException(
// "Call to native function that was declared but had no native implementation: " + this.name);
}
return this.implementation.call(arguments, globalScope, triggerScope); return this.implementation.call(arguments, globalScope, triggerScope);
} }
} }

View File

@ -5,6 +5,7 @@ import java.util.List;
import com.etheller.interpreter.ast.scope.GlobalScope; import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope; import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope; import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.statement.JassReturnNothingStatement;
import com.etheller.interpreter.ast.statement.JassStatement; import com.etheller.interpreter.ast.statement.JassStatement;
import com.etheller.interpreter.ast.value.JassType; import com.etheller.interpreter.ast.value.JassType;
import com.etheller.interpreter.ast.value.JassValue; import com.etheller.interpreter.ast.value.JassValue;
@ -32,8 +33,14 @@ public final class UserJassFunction extends AbstractJassFunction {
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope); final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) { if (returnValue != null) {
if (returnValue.visit(JassTypeGettingValueVisitor.getInstance()) != this.returnType) { if (returnValue.visit(JassTypeGettingValueVisitor.getInstance()) != this.returnType) {
if ((this.returnType == JassType.NOTHING)
&& (returnValue == JassReturnNothingStatement.RETURN_NOTHING_NOTICE)) {
return null;
}
else {
throw new RuntimeException("Invalid return type"); throw new RuntimeException("Invalid return type");
} }
}
return returnValue; return returnValue;
} }
} }

View File

@ -69,7 +69,12 @@ public final class GlobalScope {
public void createGlobal(final String name, final JassType type, final JassValue value) { public void createGlobal(final String name, final JassType type, final JassValue value) {
final GlobalScopeAssignable assignable = new GlobalScopeAssignable(type, this); final GlobalScopeAssignable assignable = new GlobalScopeAssignable(type, this);
try {
assignable.setValue(value); assignable.setValue(value);
}
catch (final Exception exc) {
throw new RuntimeException("Global initialization failed for name: " + name, exc);
}
this.globals.put(name, assignable); this.globals.put(name, assignable);
} }

View File

@ -56,6 +56,9 @@ public class Trigger {
} }
public void execute(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) { public void execute(final GlobalScope globalScope, final TriggerExecutionScope triggerScope) {
if (!this.enabled) {
return;
}
for (final JassFunction action : this.actions) { for (final JassFunction action : this.actions) {
action.call(Collections.emptyList(), globalScope, triggerScope); action.call(Collections.emptyList(), globalScope, triggerScope);
} }

View File

@ -0,0 +1,32 @@
package com.etheller.interpreter.ast.statement;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.StringJassValue;
import com.etheller.interpreter.ast.value.visitor.BooleanJassValueVisitor;
public class JassExitWhenStatement implements JassStatement {
public static final StringJassValue LOOP_EXIT_NOTICE = new StringJassValue("EXIT");
private final int lineNo;
private final JassExpression expression;
public JassExitWhenStatement(final int lineNo, final JassExpression expression) {
this.lineNo = lineNo;
this.expression = expression;
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
if (this.expression.evaluate(globalScope, localScope, triggerScope)
.visit(BooleanJassValueVisitor.getInstance())) {
return LOOP_EXIT_NOTICE;
}
return null;
}
}

View File

@ -0,0 +1,33 @@
package com.etheller.interpreter.ast.statement;
import com.etheller.interpreter.ast.expression.JassExpression;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassType;
import com.etheller.interpreter.ast.value.JassValue;
public class JassLocalDefinitionStatement implements JassStatement {
private final String identifier;
private final JassExpression expression;
private final int lineNo;
private final JassType type;
public JassLocalDefinitionStatement(final int lineNo, final String identifier, final JassType type,
final JassExpression expression) {
this.lineNo = lineNo;
this.identifier = identifier;
this.type = type;
this.expression = expression;
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
localScope.createLocal(this.identifier, this.type,
this.expression.evaluate(globalScope, localScope, triggerScope));
return null;
}
}

View File

@ -0,0 +1,28 @@
package com.etheller.interpreter.ast.statement;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassType;
import com.etheller.interpreter.ast.value.JassValue;
public class JassLocalStatement implements JassStatement {
private final String identifier;
private final int lineNo;
private final JassType type;
public JassLocalStatement(final int lineNo, final String identifier, final JassType type) {
this.lineNo = lineNo;
this.identifier = identifier;
this.type = type;
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
localScope.createLocal(this.identifier, this.type);
return null;
}
}

View File

@ -0,0 +1,38 @@
package com.etheller.interpreter.ast.statement;
import java.util.List;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
public class JassLoopStatement implements JassStatement {
private final int lineNo;
private final List<JassStatement> statements;
public JassLoopStatement(final int lineNo, final List<JassStatement> statements) {
this.lineNo = lineNo;
this.statements = statements;
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
while (true) {
for (final JassStatement statement : this.statements) {
final JassValue returnValue = statement.execute(globalScope, localScope, triggerScope);
if (returnValue != null) {
if (returnValue == JassExitWhenStatement.LOOP_EXIT_NOTICE) {
return null;
}
else {
return returnValue;
}
}
}
}
}
}

View File

@ -0,0 +1,24 @@
package com.etheller.interpreter.ast.statement;
import com.etheller.interpreter.ast.scope.GlobalScope;
import com.etheller.interpreter.ast.scope.LocalScope;
import com.etheller.interpreter.ast.scope.TriggerExecutionScope;
import com.etheller.interpreter.ast.value.JassValue;
import com.etheller.interpreter.ast.value.StringJassValue;
public class JassReturnNothingStatement implements JassStatement {
public static final StringJassValue RETURN_NOTHING_NOTICE = new StringJassValue("nothing");
private final int lineNo;
public JassReturnNothingStatement(final int lineNo) {
this.lineNo = lineNo;
}
@Override
public JassValue execute(final GlobalScope globalScope, final LocalScope localScope,
final TriggerExecutionScope triggerScope) {
globalScope.setLineNumber(this.lineNo);
return RETURN_NOTHING_NOTICE;
}
}

View File

@ -27,4 +27,14 @@ public class ArrayJassType implements JassType {
public <TYPE> TYPE visit(final JassTypeVisitor<TYPE> visitor) { public <TYPE> TYPE visit(final JassTypeVisitor<TYPE> visitor) {
return visitor.accept(this); return visitor.accept(this);
} }
@Override
public boolean isNullable() {
return false;
}
@Override
public JassValue getNullValue() {
return null;
}
} }

Some files were not shown because too many files have changed in this diff Show More