diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index 59e8c6d50..3f0090988 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -109,7 +109,7 @@ dependencies { implementation 'com.github.albfernandez:juniversalchardet:2.4.0' implementation 'org.xerial:sqlite-jdbc:3.46.1.3' implementation 'com.github.inwc3:jmpq3:e28f6999c0' - implementation 'com.github.inwc3:wc3libs:548f34a424' + implementation 'com.github.inwc3:wc3libs:ac41f780a5' implementation 'com.github.wurstscript:wurst-project-config:348fcd4ef5' implementation 'org.slf4j:slf4j-api:2.0.17' implementation 'ch.qos.logback:logback-classic:1.5.20' diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/DestructableMock.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/DestructableMock.java index d4206dc77..670200e83 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/DestructableMock.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/DestructableMock.java @@ -10,6 +10,7 @@ public class DestructableMock { public ILconstReal face; public ILconstReal scale; public ILconstInt variation; + public ILconstReal life = ILconstReal.create(100); public DestructableMock(ILconstInt objectId, ILconstReal x, ILconstReal y, ILconstReal face, ILconstReal scale, ILconstInt variation) { this.objectId = objectId; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/PlayerMock.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/PlayerMock.java index 4df239749..8da649545 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/PlayerMock.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/PlayerMock.java @@ -10,6 +10,7 @@ public class PlayerMock { public final ILconstInt id; public ILconst playerColor = ILconstNull.instance(); public final HashMap techMaxAllowed = new HashMap<>(); + public final HashMap playerStates = new HashMap<>(); public PlayerMock(ILconstInt p) { this.id = p; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/UnitMock.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/UnitMock.java index 9b7998d17..0c8adaa42 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/UnitMock.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/mocks/UnitMock.java @@ -4,12 +4,18 @@ import de.peeeq.wurstscript.intermediatelang.ILconstReal; import de.peeeq.wurstscript.intermediatelang.IlConstHandle; +import java.util.HashMap; + public class UnitMock { public IlConstHandle owner; public ILconstInt unitid; public ILconstReal x; public ILconstReal y; public ILconstReal face; + public boolean removed; + public final HashMap states = new HashMap<>(); + public final HashMap abilityLevels = new HashMap<>(); + public ILconstInt currentOrder = ILconstInt.create(0); public UnitMock(IlConstHandle owner, ILconstInt unitid, ILconstReal x, ILconstReal y, ILconstReal face) { this.owner = owner; @@ -17,5 +23,9 @@ public UnitMock(IlConstHandle owner, ILconstInt unitid, ILconstReal x, ILconstRe this.x = x; this.y = y; this.face = face; + states.put("unitstate0", ILconstReal.create(100)); + states.put("unitstate1", ILconstReal.create(100)); + states.put("unitstate2", ILconstReal.create(0)); + states.put("unitstate3", ILconstReal.create(0)); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/ConversionProvider.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/ConversionProvider.java index d9d892fb4..994fff89f 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/ConversionProvider.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/ConversionProvider.java @@ -4,13 +4,21 @@ import de.peeeq.wurstscript.intermediatelang.IlConstHandle; import de.peeeq.wurstscript.intermediatelang.interpreter.AbstractInterpreter; +import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Map; public class ConversionProvider extends Provider { + private static final Map enumHandles = new HashMap<>(); + public ConversionProvider(AbstractInterpreter interpreter) { super(interpreter); } + public static IlConstHandle enumHandle(String typeName, int value) { + return enumHandles.computeIfAbsent(typeName + value, key -> new IlConstHandle(key, value)); + } + public IlConstHandle ConvertRace(ILconstInt i) { return new IlConstHandle("race" + i, new LinkedHashSet<>()); } @@ -32,7 +40,7 @@ public IlConstHandle ConvertFGameState(ILconstInt i) { } public IlConstHandle ConvertPlayerState(ILconstInt i) { - return new IlConstHandle("playerstate" + i, new LinkedHashSet<>()); + return enumHandle("playerstate", i.getVal()); } public IlConstHandle ConvertPlayerScore(ILconstInt i) { @@ -44,7 +52,7 @@ public IlConstHandle ConvertPlayerGameResult(ILconstInt i) { } public IlConstHandle ConvertUnitState(ILconstInt i) { - return new IlConstHandle("unitstate" + i, new LinkedHashSet<>()); + return enumHandle("unitstate", i.getVal()); } public IlConstHandle ConvertUnitIntegerField(ILconstInt i) { @@ -128,7 +136,7 @@ public IlConstHandle ConvertMapDensity(ILconstInt i) { } public IlConstHandle ConvertMapControl(ILconstInt i) { - return new IlConstHandle("mapcontrol" + i, new LinkedHashSet<>()); + return enumHandle("mapcontrol", i.getVal()); } public IlConstHandle ConvertPlayerColor(ILconstInt i) { @@ -136,7 +144,7 @@ public IlConstHandle ConvertPlayerColor(ILconstInt i) { } public IlConstHandle ConvertPlayerSlotState(ILconstInt i) { - return new IlConstHandle("playerslotstate" + i, new LinkedHashSet<>()); + return enumHandle("playerslotstate", i.getVal()); } public IlConstHandle ConvertVolumeGroup(ILconstInt i) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/DestructableProvider.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/DestructableProvider.java index 61ecfb7c7..ea85290a1 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/DestructableProvider.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/DestructableProvider.java @@ -19,6 +19,22 @@ public void RemoveDestructable(IlConstHandle destructable) { } public void KillDestructable(IlConstHandle destructable) { + DestructableMock destructableMock = destructableOrNull(destructable); + if (destructableMock != null) { + destructableMock.life = ILconstReal.create(0); + } + } + + public void SetDestructableLife(IlConstHandle destructable, ILconstReal life) { + DestructableMock destructableMock = destructableOrNull(destructable); + if (destructableMock != null) { + destructableMock.life = life; + } + } + + public ILconstReal GetDestructableLife(IlConstHandle destructable) { + DestructableMock destructableMock = destructableOrNull(destructable); + return destructableMock == null ? ILconstReal.create(0) : destructableMock.life; } public ILconstReal GetDestructableX(IlConstHandle destructable) { @@ -28,4 +44,11 @@ public ILconstReal GetDestructableX(IlConstHandle destructable) { public ILconstReal GetDestructableY(IlConstHandle destructable) { return ((DestructableMock)destructable.getObj()).y; } + + private DestructableMock destructableOrNull(IlConstHandle destructable) { + if (destructable == null || !(destructable.getObj() instanceof DestructableMock)) { + return null; + } + return (DestructableMock) destructable.getObj(); + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/PlayerProvider.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/PlayerProvider.java index 18cd5dbbd..61620d8e8 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/PlayerProvider.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/PlayerProvider.java @@ -23,6 +23,28 @@ public ILconstInt GetPlayerId(IlConstHandle p) { return p != null ? ((PlayerMock) p.getObj()).id : ILconstInt.create(-1); } + public void SetPlayerState(IlConstHandle player, IlConstHandle playerstate, ILconstInt value) { + if (player == null || playerstate == null) { + return; + } + ((PlayerMock) player.getObj()).playerStates.put(playerstate.print(), value); + } + + public ILconstInt GetPlayerState(IlConstHandle player, IlConstHandle playerstate) { + if (player == null || playerstate == null) { + return ILconstInt.create(0); + } + return ((PlayerMock) player.getObj()).playerStates.getOrDefault(playerstate.print(), ILconstInt.create(0)); + } + + public IlConstHandle GetPlayerSlotState(IlConstHandle player) { + return ConversionProvider.enumHandle("playerslotstate", 1); + } + + public IlConstHandle GetPlayerController(IlConstHandle player) { + return ConversionProvider.enumHandle("mapcontrol", 0); + } + public ILconstInt GetPlayerNeutralPassive() { // fake value return new ILconstInt(31); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/UnitProvider.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/UnitProvider.java index 433d0ad94..28eca76c7 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/UnitProvider.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/jassinterpreter/providers/UnitProvider.java @@ -1,9 +1,12 @@ package de.peeeq.wurstio.jassinterpreter.providers; import de.peeeq.wurstio.objectreader.ObjectHelper; +import de.peeeq.wurstio.jassinterpreter.mocks.DestructableMock; import de.peeeq.wurstio.jassinterpreter.mocks.UnitMock; +import de.peeeq.wurstscript.intermediatelang.ILconst; import de.peeeq.wurstscript.intermediatelang.ILconstBool; import de.peeeq.wurstscript.intermediatelang.ILconstInt; +import de.peeeq.wurstscript.intermediatelang.ILconstNull; import de.peeeq.wurstscript.intermediatelang.ILconstReal; import de.peeeq.wurstscript.intermediatelang.ILconstString; import de.peeeq.wurstscript.intermediatelang.IlConstHandle; @@ -22,8 +25,43 @@ public IlConstHandle CreateUnit(IlConstHandle owner, ILconstInt unitid, ILconstR return new IlConstHandle(NameProvider.getRandomName("unit"), new UnitMock(owner, unitid, x, y, face)); } + public ILconst GetOwningPlayer(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + return unitMock == null ? ILconstNull.instance() : unitMock.owner; + } + public ILconstInt GetUnitTypeId(IlConstHandle unit) { - return ((UnitMock)unit.getObj()).unitid; + UnitMock unitMock = unitOrNull(unit); + return unitMock == null ? ILconstInt.create(0) : unitMock.unitid; + } + + public ILconstReal GetUnitX(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + return unitMock == null ? ILconstReal.create(0) : unitMock.x; + } + + public ILconstReal GetUnitY(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + return unitMock == null ? ILconstReal.create(0) : unitMock.y; + } + + public void SetUnitX(IlConstHandle unit, ILconstReal x) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock != null) { + unitMock.x = x; + } + } + + public void SetUnitY(IlConstHandle unit, ILconstReal y) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock != null) { + unitMock.y = y; + } + } + + public ILconstReal GetUnitFacing(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + return unitMock == null ? ILconstReal.create(0) : unitMock.face; } public ILconstString GetUnitName(IlConstHandle unit) { @@ -67,9 +105,107 @@ public ILconstBool IsUnitType(IlConstHandle whichUnit, IlConstHandle whichUnitTy } public void RemoveUnit(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock != null) { + unitMock.removed = true; + } userDataMap.remove(unit); } + public void KillUnit(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock != null) { + unitMock.states.put("unitstate0", ILconstReal.create(0)); + } + } + + public ILconstReal GetUnitState(IlConstHandle unit, IlConstHandle unitstate) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock == null) { + return ILconstReal.create(0); + } + return unitMock.states.getOrDefault(unitStateKey(unitstate), ILconstReal.create(0)); + } + + public void SetUnitState(IlConstHandle unit, IlConstHandle unitstate, ILconstReal value) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock != null) { + unitMock.states.put(unitStateKey(unitstate), value); + } + } + + public ILconstReal GetWidgetLife(IlConstHandle widget) { + UnitMock unitMock = unitOrNull(widget); + if (unitMock != null) { + return unitMock.states.getOrDefault("unitstate0", ILconstReal.create(0)); + } + DestructableMock destructableMock = destructableOrNull(widget); + return destructableMock == null ? ILconstReal.create(0) : destructableMock.life; + } + + public void SetWidgetLife(IlConstHandle widget, ILconstReal newLife) { + UnitMock unitMock = unitOrNull(widget); + if (unitMock != null) { + unitMock.states.put("unitstate0", newLife); + return; + } + DestructableMock destructableMock = destructableOrNull(widget); + if (destructableMock != null) { + destructableMock.life = newLife; + } + } + + public ILconstBool UnitAddAbility(IlConstHandle unit, ILconstInt abilityId) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock == null) { + return ILconstBool.FALSE; + } + unitMock.abilityLevels.putIfAbsent(abilityId.getVal(), ILconstInt.create(1)); + return ILconstBool.TRUE; + } + + public ILconstBool UnitRemoveAbility(IlConstHandle unit, ILconstInt abilityId) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock == null) { + return ILconstBool.FALSE; + } + return ILconstBool.instance(unitMock.abilityLevels.remove(abilityId.getVal()) != null); + } + + public ILconstInt GetUnitAbilityLevel(IlConstHandle unit, ILconstInt abilityId) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock == null) { + return ILconstInt.create(0); + } + return unitMock.abilityLevels.getOrDefault(abilityId.getVal(), ILconstInt.create(0)); + } + + public ILconstInt SetUnitAbilityLevel(IlConstHandle unit, ILconstInt abilityId, ILconstInt level) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock == null) { + return ILconstInt.create(0); + } + unitMock.abilityLevels.put(abilityId.getVal(), level); + return level; + } + + public ILconstBool IssueImmediateOrderById(IlConstHandle unit, ILconstInt orderId) { + return issueOrder(unit, orderId); + } + + public ILconstBool IssuePointOrderById(IlConstHandle unit, ILconstInt orderId, ILconstReal x, ILconstReal y) { + return issueOrder(unit, orderId); + } + + public ILconstBool IssueTargetOrderById(IlConstHandle unit, ILconstInt orderId, IlConstHandle target) { + return issueOrder(unit, orderId); + } + + public ILconstInt GetUnitCurrentOrder(IlConstHandle unit) { + UnitMock unitMock = unitOrNull(unit); + return unitMock == null ? ILconstInt.create(0) : unitMock.currentOrder; + } + public ILconstInt GetUnitUserData(IlConstHandle unit) { return unit == null ? ILconstInt.create(0) : userDataMap.getOrDefault(unit, ILconstInt.create(0)); } @@ -80,4 +216,31 @@ public void SetUnitUserData(IlConstHandle unit, ILconstInt userData) { } userDataMap.put(unit, userData); } + + private ILconstBool issueOrder(IlConstHandle unit, ILconstInt orderId) { + UnitMock unitMock = unitOrNull(unit); + if (unitMock == null) { + return ILconstBool.FALSE; + } + unitMock.currentOrder = orderId; + return ILconstBool.TRUE; + } + + private UnitMock unitOrNull(IlConstHandle unit) { + if (unit == null || !(unit.getObj() instanceof UnitMock)) { + return null; + } + return (UnitMock) unit.getObj(); + } + + private DestructableMock destructableOrNull(IlConstHandle destructable) { + if (destructable == null || !(destructable.getObj() instanceof DestructableMock)) { + return null; + } + return (DestructableMock) destructable.getObj(); + } + + private String unitStateKey(IlConstHandle unitstate) { + return unitstate == null ? "unitstate0" : unitstate.print(); + } } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/InterpreterTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/InterpreterTests.java index a5f897bd8..e9cc630c7 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/InterpreterTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/InterpreterTests.java @@ -76,6 +76,130 @@ public void setPlayerTechMaxAllowed() { ); } + @Test + public void playerStateAndSlotNatives() { + test().withStdLib().executeProg(true).testLua(false).lines( + "package Test", + "init", + " let gold = ConvertPlayerState(1)", + " let lumber = ConvertPlayerState(2)", + " let foodCap = ConvertPlayerState(4)", + " let foodUsed = ConvertPlayerState(5)", + " SetPlayerState(Player(1), gold, 250)", + " SetPlayerState(Player(1), lumber, 125)", + " SetPlayerState(Player(1), foodCap, 12)", + " SetPlayerState(Player(1), foodUsed, 7)", + " if GetPlayerState(Player(1), gold) != 250", + " testFail(\"gold\")", + " if GetPlayerState(Player(1), lumber) != 125", + " testFail(\"lumber\")", + " if GetPlayerState(Player(1), foodCap) != 12", + " testFail(\"food cap\")", + " if GetPlayerState(Player(1), foodUsed) != 7", + " testFail(\"food used\")", + " if GetPlayerSlotState(Player(1)) != ConvertPlayerSlotState(1)", + " testFail(\"slot\")", + " if GetPlayerController(Player(1)) != ConvertMapControl(0)", + " testFail(\"controller\")", + " testSuccess()" + ); + } + + @Test + public void unitStateAbilityAndOrderNatives() { + test().withStdLib().executeProg(true).testLua(false).lines( + "package Test", + "init", + " let u = CreateUnit(Player(2), 'hfoo', 12.5, -3.25, 90.0)", + " let target = CreateUnit(Player(3), 'hbar', 0.0, 0.0, 0.0)", + " if GetOwningPlayer(u) != Player(2)", + " testFail(\"owner\")", + " if GetUnitTypeId(u) != 'hfoo'", + " testFail(\"type\")", + " if GetUnitX(u) != 12.5", + " testFail(\"x\")", + " if GetUnitY(u) != -3.25", + " testFail(\"y\")", + " SetUnitX(u, 7.0)", + " SetUnitY(u, 8.0)", + " if GetUnitX(u) != 7.0 or GetUnitY(u) != 8.0", + " testFail(\"move\")", + " SetUnitState(u, UNIT_STATE_LIFE, 33.0)", + " if GetUnitState(u, UNIT_STATE_LIFE) != 33.0", + " testFail(\"life\")", + " if GetWidgetLife(u) != 33.0", + " testFail(\"widget life\")", + " SetWidgetLife(u, 44.0)", + " if GetUnitState(u, UNIT_STATE_LIFE) != 44.0", + " testFail(\"set widget life\")", + " KillUnit(u)", + " if GetWidgetLife(u) != 0.0", + " testFail(\"kill\")", + " if not UnitAddAbility(u, 'Afoo')", + " testFail(\"add ability\")", + " if GetUnitAbilityLevel(u, 'Afoo') != 1", + " testFail(\"ability default\")", + " if SetUnitAbilityLevel(u, 'Afoo', 3) != 3", + " testFail(\"set ability return\")", + " if GetUnitAbilityLevel(u, 'Afoo') != 3", + " testFail(\"ability level\")", + " if not UnitRemoveAbility(u, 'Afoo')", + " testFail(\"remove ability\")", + " if GetUnitAbilityLevel(u, 'Afoo') != 0", + " testFail(\"ability removed\")", + " if not IssueImmediateOrderById(u, 851971)", + " testFail(\"immediate order\")", + " if GetUnitCurrentOrder(u) != 851971", + " testFail(\"immediate current\")", + " if not IssuePointOrderById(u, 851986, 1.0, 2.0)", + " testFail(\"point order\")", + " if GetUnitCurrentOrder(u) != 851986", + " testFail(\"point current\")", + " if not IssueTargetOrderById(u, 852000, target)", + " testFail(\"target order\")", + " if GetUnitCurrentOrder(u) != 852000", + " testFail(\"target current\")", + " RemoveUnit(u)", + " if GetUnitTypeId(u) != 'hfoo'", + " testFail(\"removed handle\")", + " testSuccess()" + ); + } + + @Test + public void getOwningPlayerNullUnitReturnsWurstNull() { + test().withStdLib().executeProg(true).testLua(false).lines( + "package Test", + "init", + " if GetOwningPlayer(null) != null", + " testFail(\"owning player null\")", + " testSuccess()" + ); + } + + @Test + public void destructableWidgetLifeNatives() { + test().withStdLib().executeProg(true).testLua(false).lines( + "package Test", + "init", + " let d = CreateDestructable('LTlt', 1.0, 2.0, 0.0, 1.0, 0)", + " if GetWidgetLife(d) != 100.0", + " testFail(\"default widget life\")", + " SetWidgetLife(d, 72.0)", + " if GetWidgetLife(d) != 72.0", + " testFail(\"widget life\")", + " if GetDestructableLife(d) != 72.0", + " testFail(\"destructable life after widget set\")", + " SetDestructableLife(d, 55.0)", + " if GetWidgetLife(d) != 55.0", + " testFail(\"widget life after destructable set\")", + " KillDestructable(d)", + " if GetWidgetLife(d) != 0.0", + " testFail(\"kill destructable\")", + " testSuccess()" + ); + } + @Test public void unitAndAbilityInfoNatives() { test().withStdLib().executeProg(true).testLua(false).lines(