Build behavior first draft

This commit is contained in:
Retera 2020-11-10 22:44:22 -05:00
parent bc9f7d37a0
commit 4fbb7adeaf
16 changed files with 411 additions and 280 deletions

View File

@ -55,8 +55,8 @@ import com.etheller.warsmash.viewer5.handlers.w3x.ui.MeleeUI;
import com.etheller.warsmash.viewer5.handlers.w3x.ui.command.CommandErrorListener;
public class WarsmashGdxMapGame extends ApplicationAdapter implements CanvasProvider, InputProcessor {
private static final boolean ENABLE_AUDIO = false;
private static final boolean ENABLE_MUSIC = false;
private static final boolean ENABLE_AUDIO = true;
private static final boolean ENABLE_MUSIC = true;
private DataSource codebase;
private War3MapViewer viewer;
private final Rectangle tempRect = new Rectangle();

View File

@ -524,7 +524,8 @@ public class War3MapViewer extends ModelViewer {
@Override
public CUnit createUnit(final CSimulation simulation, final War3ID typeId, final int playerIndex,
final float x, final float y, final float facing) {
return null;
return createNewUnit(War3MapViewer.this.allObjectData, typeId, x, y, 0f, playerIndex,
(float) Math.toRadians(facing));
}
}, this.terrain.pathingGrid, this.terrain.getEntireMap(), this.seededRandom, w3iFile.getPlayers());
@ -756,176 +757,21 @@ public class War3MapViewer extends ModelViewer {
private void loadUnitsAndItems(final Warcraft3MapObjectData modifications) throws IOException {
final War3Map mpq = this.mapMpq;
this.soundsetNameToSoundset = new HashMap<>();
if (this.dataSource.has("war3mapUnits.doo")) {
final War3MapUnitsDoo dooFile = mpq.readUnits();
final Map<String, UnitSoundset> soundsetNameToSoundset = new HashMap<>();
// Collect the units and items data.
UnitSoundset soundset = null;
for (final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit : dooFile.getUnits()) {
MutableGameObject row = null;
String path = null;
Splat unitShadowSplat = null;
BufferedImage buildingPathingPixelMap = null;
final War3ID unitId = unit.getId();
final float unitX = unit.getLocation()[0];
final float unitY = unit.getLocation()[1];
final float unitZ = unit.getLocation()[2];
final int playerIndex = unit.getPlayer();
final float unitAngle = unit.getAngle();
// Hardcoded?
WorldEditorDataType type = null;
if (sloc.equals(unit.getId())) {
// path = "Objects\\StartLocation\\StartLocation.mdx";
type = null; /// ??????
this.startLocations[unit.getPlayer()] = new Vector2(unit.getLocation()[0], unit.getLocation()[1]);
}
else {
row = modifications.getUnits().get(unit.getId());
if (row == null) {
row = modifications.getItems().get(unit.getId());
if (row != null) {
type = WorldEditorDataType.ITEM;
path = row.getFieldAsString(ITEM_FILE, 0);
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
path = path.substring(0, path.length() - 4);
}
final String unitShadow = "Shadow";
if ((unitShadow != null) && !"_".equals(unitShadow)) {
final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp";
final float shadowX = 50;
final float shadowY = 50;
final float shadowWidth = 128;
final float shadowHeight = 128;
if (!this.terrain.splats.containsKey(texture)) {
final Splat splat = new Splat();
splat.opacity = 0.5f;
this.terrain.splats.put(texture, splat);
}
final float x = unit.getLocation()[0] - shadowX;
final float y = unit.getLocation()[1] - shadowY;
this.terrain.splats.get(texture).locations
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
unitShadowSplat = this.terrain.splats.get(texture);
}
path += ".mdx";
}
}
else {
type = WorldEditorDataType.UNITS;
path = row.getFieldAsString(UNIT_FILE, 0);
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
path = path.substring(0, path.length() - 4);
}
if ((row.readSLKTagInt("fileVerFlags") == 2) && this.dataSource.has(path + "_V1.mdx")) {
path += "_V1";
}
path += ".mdx";
final String uberSplat = row.getFieldAsString(UBER_SPLAT, 0);
if (uberSplat != null) {
final Element uberSplatInfo = this.terrain.uberSplatTable.get(uberSplat);
if (uberSplatInfo != null) {
final String texturePath = uberSplatInfo.getField("Dir") + "\\"
+ uberSplatInfo.getField("file") + ".blp";
if (!this.terrain.splats.containsKey(texturePath)) {
this.terrain.splats.put(texturePath, new Splat());
}
final float x = unit.getLocation()[0];
final float y = unit.getLocation()[1];
final float s = uberSplatInfo.getFieldFloatValue("Scale");
this.terrain.splats.get(texturePath).locations
.add(new float[] { x - s, y - s, x + s, y + s, 1 });
}
}
final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0);
if ((unitShadow != null) && !"_".equals(unitShadow)) {
final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp";
final float shadowX = row.getFieldAsFloat(UNIT_SHADOW_X, 0);
final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0);
final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0);
final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0);
if (this.mapMpq.has(texture)) {
if (!this.terrain.splats.containsKey(texture)) {
final Splat splat = new Splat();
splat.opacity = 0.5f;
this.terrain.splats.put(texture, splat);
}
final float x = unit.getLocation()[0] - shadowX;
final float y = unit.getLocation()[1] - shadowY;
this.terrain.splats.get(texture).locations
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
unitShadowSplat = this.terrain.splats.get(texture);
}
}
final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0);
if ((buildingShadow != null) && !"_".equals(buildingShadow)) {
this.terrain.addShadow(buildingShadow, unit.getLocation()[0], unit.getLocation()[1]);
}
buildingPathingPixelMap = getBuildingPathingPixelMap(row);
if (buildingPathingPixelMap != null) {
this.terrain.pathingGrid.blitPathingOverlayTexture(unit.getLocation()[0],
unit.getLocation()[1], (int) Math.toDegrees(unit.getAngle()),
buildingPathingPixelMap);
}
final String soundName = row.getFieldAsString(UNIT_SOUNDSET, 0);
UnitSoundset unitSoundset = soundsetNameToSoundset.get(soundName);
if (unitSoundset == null) {
unitSoundset = new UnitSoundset(this.dataSource, this.unitAckSoundsTable, soundName);
soundsetNameToSoundset.put(soundName, unitSoundset);
}
soundset = unitSoundset;
}
}
if (path != null) {
final MdxModel model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
MdxModel portraitModel;
final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx";
if (this.dataSource.has(portraitPath)) {
portraitModel = (MdxModel) this.load(portraitPath, this.mapPathSolver, this.solverParams);
}
else {
portraitModel = model;
}
if (type == WorldEditorDataType.UNITS) {
final float angle = (float) Math.toDegrees(unit.getAngle());
final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), unit.getPlayer(),
unit.getLocation()[0], unit.getLocation()[1], angle, buildingPathingPixelMap);
final RenderUnitTypeData typeData = getUnitTypeData(unit.getId(), row);
final RenderUnit renderUnit = new RenderUnit(this, model, row, unit, soundset, portraitModel,
simulationUnit, typeData);
this.unitToRenderPeer.put(simulationUnit, renderUnit);
this.units.add(renderUnit);
if (unitShadowSplat != null) {
unitShadowSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
public void accept(final SplatMover t) {
renderUnit.shadow = t;
}
});
}
}
else {
this.items.add(new RenderItem(this, model, row, unit, soundset, portraitModel)); // TODO store
// somewhere
if (unitShadowSplat != null) {
unitShadowSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
public void accept(final SplatMover t) {
}
});
}
}
}
else {
System.err.println("Unknown unit ID: " + unit.getId());
}
createNewUnit(modifications, unitId, unitX, unitY, unitZ, playerIndex, unitAngle);
}
}
@ -935,6 +781,187 @@ public class War3MapViewer extends ModelViewer {
this.anyReady = true;
}
private CUnit createNewUnit(final Warcraft3MapObjectData modifications, final War3ID unitId, float unitX,
float unitY, final float unitZ, final int playerIndex, final float unitAngle) {
UnitSoundset soundset = null;
MutableGameObject row = null;
String path = null;
Splat unitShadowSplat = null;
BufferedImage buildingPathingPixelMap = null;
final float unitVertexScale = 1.0f;
// Hardcoded?
WorldEditorDataType type = null;
if (sloc.equals(unitId)) {
// path = "Objects\\StartLocation\\StartLocation.mdx";
type = null; /// ??????
this.startLocations[playerIndex] = new Vector2(unitX, unitY);
}
else {
row = modifications.getUnits().get(unitId);
if (row == null) {
row = modifications.getItems().get(unitId);
if (row != null) {
type = WorldEditorDataType.ITEM;
path = row.getFieldAsString(ITEM_FILE, 0);
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
path = path.substring(0, path.length() - 4);
}
final String unitShadow = "Shadow";
if ((unitShadow != null) && !"_".equals(unitShadow)) {
final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp";
final float shadowX = 50;
final float shadowY = 50;
final float shadowWidth = 128;
final float shadowHeight = 128;
if (!this.terrain.splats.containsKey(texture)) {
final Splat splat = new Splat();
splat.opacity = 0.5f;
this.terrain.splats.put(texture, splat);
}
final float x = unitX - shadowX;
final float y = unitY - shadowY;
this.terrain.splats.get(texture).locations
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
unitShadowSplat = this.terrain.splats.get(texture);
}
path += ".mdx";
}
}
else {
type = WorldEditorDataType.UNITS;
path = getUnitModelPath(row);
buildingPathingPixelMap = getBuildingPathingPixelMap(row);
if (buildingPathingPixelMap != null) {
unitX = Math.round(unitX / 64f) * 64f;
unitY = Math.round(unitY / 64f) * 64f;
this.terrain.pathingGrid.blitPathingOverlayTexture(unitX, unitY, (int) Math.toDegrees(unitAngle),
buildingPathingPixelMap);
}
final String uberSplat = row.getFieldAsString(UBER_SPLAT, 0);
if (uberSplat != null) {
final Element uberSplatInfo = this.terrain.uberSplatTable.get(uberSplat);
if (uberSplatInfo != null) {
final String texturePath = uberSplatInfo.getField("Dir") + "\\" + uberSplatInfo.getField("file")
+ ".blp";
if (!this.terrain.splats.containsKey(texturePath)) {
this.terrain.splats.put(texturePath, new Splat());
}
final float x = unitX;
final float y = unitY;
final float s = uberSplatInfo.getFieldFloatValue("Scale");
this.terrain.splats.get(texturePath).locations
.add(new float[] { x - s, y - s, x + s, y + s, 1 });
}
}
final String unitShadow = row.getFieldAsString(UNIT_SHADOW, 0);
if ((unitShadow != null) && !"_".equals(unitShadow)) {
final String texture = "ReplaceableTextures\\Shadows\\" + unitShadow + ".blp";
final float shadowX = row.getFieldAsFloat(UNIT_SHADOW_X, 0);
final float shadowY = row.getFieldAsFloat(UNIT_SHADOW_Y, 0);
final float shadowWidth = row.getFieldAsFloat(UNIT_SHADOW_W, 0);
final float shadowHeight = row.getFieldAsFloat(UNIT_SHADOW_H, 0);
if (this.mapMpq.has(texture)) {
if (!this.terrain.splats.containsKey(texture)) {
final Splat splat = new Splat();
splat.opacity = 0.5f;
this.terrain.splats.put(texture, splat);
}
final float x = unitX - shadowX;
final float y = unitY - shadowY;
this.terrain.splats.get(texture).locations
.add(new float[] { x, y, x + shadowWidth, y + shadowHeight, 3 });
unitShadowSplat = this.terrain.splats.get(texture);
}
}
final String buildingShadow = row.getFieldAsString(BUILDING_SHADOW, 0);
if ((buildingShadow != null) && !"_".equals(buildingShadow)) {
this.terrain.addShadow(buildingShadow, unitX, unitY);
}
final String soundName = row.getFieldAsString(UNIT_SOUNDSET, 0);
UnitSoundset unitSoundset = this.soundsetNameToSoundset.get(soundName);
if (unitSoundset == null) {
unitSoundset = new UnitSoundset(this.dataSource, this.unitAckSoundsTable, soundName);
this.soundsetNameToSoundset.put(soundName, unitSoundset);
}
soundset = unitSoundset;
}
}
if (path != null) {
final MdxModel model = (MdxModel) this.load(path, this.mapPathSolver, this.solverParams);
MdxModel portraitModel;
final String portraitPath = path.substring(0, path.length() - 4) + "_portrait.mdx";
if (this.dataSource.has(portraitPath)) {
portraitModel = (MdxModel) this.load(portraitPath, this.mapPathSolver, this.solverParams);
}
else {
portraitModel = model;
}
if (type == WorldEditorDataType.UNITS) {
final float angle = (float) Math.toDegrees(unitAngle);
final CUnit simulationUnit = this.simulation.createUnit(row.getAlias(), playerIndex, unitX, unitY,
angle, buildingPathingPixelMap);
final RenderUnitTypeData typeData = getUnitTypeData(unitId, row);
final RenderUnit renderUnit = new RenderUnit(this, model, row, unitX, unitY, unitZ, playerIndex,
soundset, portraitModel, simulationUnit, typeData);
this.unitToRenderPeer.put(simulationUnit, renderUnit);
this.units.add(renderUnit);
if (unitShadowSplat != null) {
unitShadowSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
public void accept(final SplatMover t) {
renderUnit.shadow = t;
}
});
}
return simulationUnit;
}
else {
this.items
.add(new RenderItem(this, model, row, unitX, unitY, unitZ, unitAngle, soundset, portraitModel)); // TODO
// store
// somewhere
if (unitShadowSplat != null) {
unitShadowSplat.unitMapping.add(new Consumer<SplatModel.SplatMover>() {
@Override
public void accept(final SplatMover t) {
}
});
}
}
}
else {
System.err.println("Unknown unit ID: " + unitId);
}
return null;
}
public String getUnitModelPath(final MutableGameObject row) {
String path;
path = row.getFieldAsString(UNIT_FILE, 0);
if (path.toLowerCase().endsWith(".mdl") || path.toLowerCase().endsWith(".mdx")) {
path = path.substring(0, path.length() - 4);
}
if ((row.readSLKTagInt("fileVerFlags") == 2) && this.dataSource.has(path + "_V1.mdx")) {
path += "_V1";
}
path += ".mdx";
return path;
}
private BufferedImage getBuildingPathingPixelMap(final MutableGameObject row) {
BufferedImage buildingPathingPixelMap = null;
final String pathingTexture = row.getFieldAsString(UNIT_PATHING, 0);
@ -962,7 +989,7 @@ public class War3MapViewer extends ModelViewer {
return buildingPathingPixelMap;
}
private RenderUnitTypeData getUnitTypeData(final War3ID key, final MutableGameObject row) {
public RenderUnitTypeData getUnitTypeData(final War3ID key, final MutableGameObject row) {
RenderUnitTypeData unitTypeData = this.unitIdToTypeData.get(key);
if (unitTypeData == null) {
unitTypeData = new RenderUnitTypeData(row.getFieldAsFloat(MAX_PITCH, 0), row.getFieldAsFloat(MAX_ROLL, 0),
@ -1295,6 +1322,7 @@ public class War3MapViewer extends ModelViewer {
private WorldEditStrings worldEditStrings;
private Warcraft3MapObjectData allObjectData;
private AbilityDataUI abilityDataUI;
private Map<String, UnitSoundset> soundsetNameToSoundset;
/**
* Returns a power of two size for the given target capacity.
@ -1388,6 +1416,10 @@ public class War3MapViewer extends ModelViewer {
return this.uiSounds;
}
public Warcraft3MapObjectData getAllObjectData() {
return this.allObjectData;
}
public float getWalkableRenderHeight(final float x, final float y) {
this.walkableObjectsTree.intersect(x, y, this.walkablesIntersector.reset(x, y));
return this.walkablesIntersector.z;

View File

@ -20,23 +20,19 @@ public class RenderItem {
public float radius;
public UnitSoundset soundset;
public final MdxModel portraitModel;
public int playerIndex;
public RenderItem(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit, final UnitSoundset soundset,
public RenderItem(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final float x,
final float y, final float z, final float angle, final UnitSoundset soundset,
final MdxModel portraitModel) {
this.portraitModel = portraitModel;
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
final float[] location = unit.getLocation();
System.arraycopy(location, 0, this.location, 0, 3);
instance.move(location);
final float angle = unit.getAngle();
this.location[0] = x;
this.location[1] = y;
this.location[2] = z;
instance.move(this.location);
// instance.localRotation.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle);
instance.rotate(new Quaternion().setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle));
instance.scale(unit.getScale());
this.playerIndex = unit.getPlayer();
instance.setTeamColor(this.playerIndex);
instance.setScene(map.worldScene);
if (row != null) {

View File

@ -30,7 +30,7 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitAnimationListe
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
public class RenderUnit {
private static final Quaternion tempQuat = new Quaternion();
public static final Quaternion tempQuat = new Quaternion();
private static final War3ID RED = War3ID.fromString("uclr");
private static final War3ID GREEN = War3ID.fromString("uclg");
private static final War3ID BLUE = War3ID.fromString("uclb");
@ -67,25 +67,25 @@ public class RenderUnit {
private boolean boneCorpse;
private final RenderUnitTypeData typeData;
public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row,
final com.etheller.warsmash.parsers.w3x.unitsdoo.Unit unit, final UnitSoundset soundset,
public RenderUnit(final War3MapViewer map, final MdxModel model, final MutableGameObject row, final float x,
final float y, final float z, final int playerIndex, final UnitSoundset soundset,
final MdxModel portraitModel, final CUnit simulationUnit, final RenderUnitTypeData typeData) {
this.portraitModel = portraitModel;
this.simulationUnit = simulationUnit;
this.typeData = typeData;
final MdxComplexInstance instance = (MdxComplexInstance) model.addInstance();
final float[] location = unit.getLocation();
System.arraycopy(location, 0, this.location, 0, 3);
instance.move(location);
this.location[0] = x;
this.location[1] = y;
this.location[2] = z;
instance.move(this.location);
this.facing = simulationUnit.getFacing();
final float angle = (float) Math.toRadians(this.facing);
// instance.localRotation.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle);
this.x = simulationUnit.getX();
this.y = simulationUnit.getY();
instance.rotate(tempQuat.setFromAxisRad(RenderMathUtils.VEC3_UNIT_Z, angle));
instance.scale(unit.getScale());
this.playerIndex = unit.getPlayer() & 0xFFFF;
this.playerIndex = playerIndex & 0xFFFF;
instance.setTeamColor(this.playerIndex);
instance.setScene(map.worldScene);
this.unitAnimationListenerImpl = new UnitAnimationListenerImpl(instance);

View File

@ -24,6 +24,8 @@ public class CGameplayConstants {
private final float gameDayHours;
private final float gameDayLength;
private final float structureDecayTime;
private final float buildingAngle;
private final float rootAngle;
public CGameplayConstants(final DataTable parsedDataTable) {
final Element miscData = parsedDataTable.get("Misc");
@ -41,6 +43,9 @@ public class CGameplayConstants {
this.gameDayHours = miscData.getFieldFloatValue("DayHours");
this.gameDayLength = miscData.getFieldFloatValue("DayLength");
this.buildingAngle = miscData.getFieldFloatValue("BuildingAngle");
this.rootAngle = miscData.getFieldFloatValue("RootAngle");
final CDefenseType[] defenseTypeOrder = { CDefenseType.SMALL, CDefenseType.MEDIUM, CDefenseType.LARGE,
CDefenseType.FORT, CDefenseType.NORMAL, CDefenseType.HERO, CDefenseType.DIVINE, CDefenseType.NONE, };
this.damageBonusTable = new float[CAttackType.values().length][defenseTypeOrder.length];
@ -111,4 +116,12 @@ public class CGameplayConstants {
public float getStructureDecayTime() {
return this.structureDecayTime;
}
public float getBuildingAngle() {
return this.buildingAngle;
}
public float getRootAngle() {
return this.rootAngle;
}
}

View File

@ -34,6 +34,7 @@ public class CSimulation {
private final CAbilityData abilityData;
private final CUnitData unitData;
private final List<CUnit> units;
private final List<CUnit> newUnits;
private final List<CPlayer> players;
private final List<CAttackProjectile> projectiles;
private final List<CAttackProjectile> newProjectiles;
@ -60,6 +61,7 @@ public class CSimulation {
this.abilityData = new CAbilityData(parsedAbilityData);
this.unitData = new CUnitData(parsedUnitData, this.abilityData);
this.units = new ArrayList<>();
this.newUnits = new ArrayList<>();
this.projectiles = new ArrayList<>();
this.newProjectiles = new ArrayList<>();
this.handleIdAllocator = new HandleIdAllocator();
@ -119,7 +121,7 @@ public class CSimulation {
final float facing, final BufferedImage buildingPathingPixelMap) {
final CUnit unit = this.unitData.create(this, playerIndex, typeId, x, y, facing, buildingPathingPixelMap,
this.simulationRenderController, this.handleIdAllocator);
this.units.add(unit);
this.newUnits.add(unit);
this.handleIdToUnit.put(unit.getHandleId(), unit);
for (final CAbility ability : unit.getAbilities()) {
this.handleIdToAbility.put(ability.getHandleId(), ability);
@ -128,6 +130,11 @@ public class CSimulation {
return unit;
}
public CUnit createUnit(final War3ID typeId, final int playerIndex, final float x, final float y,
final float facing) {
return this.simulationRenderController.createUnit(this, typeId, playerIndex, x, y, facing);
}
public CUnit getUnit(final int handleId) {
return this.handleIdToUnit.get(handleId);
}
@ -175,6 +182,8 @@ public class CSimulation {
this.simulationRenderController.removeUnit(unit);
}
}
this.units.addAll(this.newUnits);
this.newUnits.clear();
final Iterator<CAttackProjectile> projectileIterator = this.projectiles.iterator();
while (projectileIterator.hasNext()) {
final CAttackProjectile projectile = projectileIterator.next();

View File

@ -462,53 +462,63 @@ public class CUnit extends CWidget {
final CUnit targetUnit = (CUnit) target;
final CUnitType targetUnitType = targetUnit.getUnitType();
if (targetUnitType.isBuilding() && (targetUnitType.getBuildingPathingPixelMap() != null)) {
final float relativeOffsetX = getX() - target.getX();
final float relativeOffsetY = getY() - target.getY();
final int rotation = ((int) targetUnit.getFacing() + 450) % 360;
final BufferedImage buildingPathingPixelMap = targetUnitType.getBuildingPathingPixelMap();
final int gridWidth = ((rotation % 180) != 0) ? buildingPathingPixelMap.getHeight()
: buildingPathingPixelMap.getWidth();
final int gridHeight = ((rotation % 180) != 0) ? buildingPathingPixelMap.getWidth()
: buildingPathingPixelMap.getHeight();
final int relativeGridX = (int) Math.floor(relativeOffsetX / 32f) + (gridWidth / 2);
final int relativeGridY = (int) Math.floor(relativeOffsetY / 32f) + (gridHeight / 2);
final int rangeInCells = (int) Math.floor(range / 32f);
final int rangeInCellsSquare = rangeInCells * rangeInCells;
int minCheckX = relativeGridX - rangeInCells;
int minCheckY = relativeGridY - rangeInCells;
int maxCheckX = relativeGridX + rangeInCells;
int maxCheckY = relativeGridY + rangeInCells;
if ((minCheckX < gridWidth) && (maxCheckX >= 0)) {
if ((minCheckY < gridHeight) && (maxCheckY >= 0)) {
if (minCheckX < 0) {
minCheckX = 0;
}
if (minCheckY < 0) {
minCheckY = 0;
}
if (maxCheckX > (gridWidth - 1)) {
maxCheckX = gridWidth - 1;
}
if (maxCheckY > (gridHeight - 1)) {
maxCheckY = gridHeight - 1;
}
for (int checkX = minCheckX; checkX <= maxCheckX; checkX++) {
for (int checkY = minCheckY; checkY <= maxCheckY; checkY++) {
final int dx = relativeGridX - checkX;
final int dy = relativeGridY - checkY;
if (((dx * dx) + (dy * dy)) <= rangeInCellsSquare) {
if (((getRGBFromPixelData(buildingPathingPixelMap, checkX, checkY, rotation)
& 0xFF0000) >>> 16) > 127) {
return true;
}
}
final float targetX = target.getX();
final float targetY = target.getY();
if (canReachToPathing(range, targetUnit.getFacing(), buildingPathingPixelMap, targetX, targetY)) {
return true;
}
}
}
return distance <= range;
}
public boolean canReachToPathing(final float range, final float rotationForPathing,
final BufferedImage buildingPathingPixelMap, final float targetX, final float targetY) {
final int rotation = ((int) rotationForPathing + 450) % 360;
final float relativeOffsetX = getX() - targetX;
final float relativeOffsetY = getY() - targetY;
final int gridWidth = ((rotation % 180) != 0) ? buildingPathingPixelMap.getHeight()
: buildingPathingPixelMap.getWidth();
final int gridHeight = ((rotation % 180) != 0) ? buildingPathingPixelMap.getWidth()
: buildingPathingPixelMap.getHeight();
final int relativeGridX = (int) Math.floor(relativeOffsetX / 32f) + (gridWidth / 2);
final int relativeGridY = (int) Math.floor(relativeOffsetY / 32f) + (gridHeight / 2);
final int rangeInCells = (int) Math.floor(range / 32f);
final int rangeInCellsSquare = rangeInCells * rangeInCells;
int minCheckX = relativeGridX - rangeInCells;
int minCheckY = relativeGridY - rangeInCells;
int maxCheckX = relativeGridX + rangeInCells;
int maxCheckY = relativeGridY + rangeInCells;
if ((minCheckX < gridWidth) && (maxCheckX >= 0)) {
if ((minCheckY < gridHeight) && (maxCheckY >= 0)) {
if (minCheckX < 0) {
minCheckX = 0;
}
if (minCheckY < 0) {
minCheckY = 0;
}
if (maxCheckX > (gridWidth - 1)) {
maxCheckX = gridWidth - 1;
}
if (maxCheckY > (gridHeight - 1)) {
maxCheckY = gridHeight - 1;
}
for (int checkX = minCheckX; checkX <= maxCheckX; checkX++) {
for (int checkY = minCheckY; checkY <= maxCheckY; checkY++) {
final int dx = relativeGridX - checkX;
final int dy = relativeGridY - checkY;
if (((dx * dx) + (dy * dy)) <= rangeInCellsSquare) {
if (((getRGBFromPixelData(buildingPathingPixelMap, checkX, checkY, rotation)
& 0xFF0000) >>> 16) > 127) {
return true;
}
}
}
}
}
}
return distance <= range;
return false;
}
private int getRGBFromPixelData(final BufferedImage buildingPathingPixelMap, final int checkX, final int checkY,

View File

@ -10,12 +10,11 @@ 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.orders.OrderIds;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.players.CAllianceType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.test.IAbility;
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.TargetType;
public class CAbilityAttack implements CAbility, IAbility {
public class CAbilityAttack implements CAbility {
private final int handleId;
public CAbilityAttack(final int handleId) {

View File

@ -78,6 +78,6 @@ public abstract class AbstractCAbilityBuild extends AbstractCAbility implements
@Override
public boolean checkBeforeQueue(final CSimulation game, final CUnit caster, final int orderId) {
return false;
return true;
}
}

View File

@ -9,9 +9,11 @@ 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.CAbilityVisitor;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.build.CBehaviorOrcBuild;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.orders.OrderIds;
public class CAbilityOrcBuild extends AbstractCAbilityBuild {
private CBehaviorOrcBuild buildBehavior;
public CAbilityOrcBuild(final int handleId, final List<War3ID> structuresBuilt) {
super(handleId, structuresBuilt);
@ -24,6 +26,7 @@ public class CAbilityOrcBuild extends AbstractCAbilityBuild {
@Override
public void onAdd(final CSimulation game, final CUnit unit) {
this.buildBehavior = new CBehaviorOrcBuild(unit);
}
@Override
@ -37,8 +40,7 @@ public class CAbilityOrcBuild extends AbstractCAbilityBuild {
@Override
public CBehavior begin(final CSimulation game, final CUnit caster, final int orderId, final Vector2 point) {
// TODO Auto-generated method stub
return null;
return this.buildBehavior.reset(point.x, point.y, orderId, getBaseOrderId());
}
@Override

View File

@ -39,12 +39,8 @@ public class CBehaviorAttack extends CAbstractRangedWidgetTargetBehavior {
@Override
public boolean isWithinRange(final CSimulation simulation) {
float range = this.unitAttack.getRange();
if ((this.target instanceof CUnit) && (((CUnit) this.target).isMoving()) && (simulation
.getGameTurnTick() < this.unit.getCooldownEndTime() /*
* only apply range motion buffer if they were
* already in range and attacked
*/)) {
range += this.unitAttack.getRangeMotionBuffer() + 1000;
if (simulation.getGameTurnTick() < this.unit.getCooldownEndTime()) {
range += this.unitAttack.getRangeMotionBuffer();
}
return this.unit.canReach(this.target, range)
&& (this.unit.distance(this.target) >= this.unit.getUnitType().getMinimumAttackRange());

View File

@ -88,7 +88,7 @@ public class CBehaviorMove implements CBehavior {
@Override
public CBehavior update(final CSimulation simulation) {
if ((this.rangedBehavior != null) && this.rangedBehavior.isWithinRange(simulation)) {
return this.rangedBehavior;
return this.rangedBehavior.update(simulation);
}
final float prevX = this.unit.getX();
final float prevY = this.unit.getY();

View File

@ -1,35 +1,43 @@
package com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.build;
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.behaviors.CAbstractRangedPointTargetBehavior;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.behaviors.CBehavior;
public class CBehaviorBuild extends CAbstractRangedPointTargetBehavior {
private int orderId;
public class CBehaviorOrcBuild extends CAbstractRangedPointTargetBehavior {
private int highlightOrderId;
private War3ID orderId;
public CBehaviorBuild(final CUnit unit) {
public CBehaviorOrcBuild(final CUnit unit) {
super(unit);
}
public CBehavior reset(final float targetX, final float targetY, final int orderId) {
this.orderId = orderId;
public CBehavior reset(final float targetX, final float targetY, final int orderId, final int highlightOrderId) {
this.highlightOrderId = highlightOrderId;
this.orderId = new War3ID(orderId);
return innerReset(targetX, targetY);
}
@Override
public boolean isWithinRange(final CSimulation simulation) {
return this.unit.distance(this.targetX, this.targetY) <= 23525;
final CUnitType unitType = simulation.getUnitData().getUnitType(this.orderId);
return this.unit.canReachToPathing(0, simulation.getGameplayConstants().getBuildingAngle(),
unitType.getBuildingPathingPixelMap(), this.targetX, this.targetY);
}
@Override
public int getHighlightOrderId() {
return this.orderId;
return this.highlightOrderId;
}
@Override
protected CBehavior update(final CSimulation simulation, final boolean withinRange) {
return this;
simulation.createUnit(this.orderId, this.unit.getPlayerIndex(), this.targetX, this.targetY,
simulation.getGameplayConstants().getBuildingAngle());
return this.unit.pollNextOrderBehavior(simulation);
}
@Override

View File

@ -137,11 +137,11 @@ public class CommandCardIcon extends AbstractRenderableFrame {
this.commandCardCommandListener.openMenu(this.orderId);
}
else {
this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.orderId);
this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.orderId, false);
}
}
else if (button == Input.Buttons.RIGHT) {
this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.autoCastOrderId);
this.commandCardCommandListener.startUsingAbility(this.abilityHandleId, this.autoCastOrderId, true);
}
}

View File

@ -1,5 +1,6 @@
package com.etheller.warsmash.viewer5.handlers.w3x.ui;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -30,8 +31,11 @@ import com.etheller.warsmash.parsers.fdf.frames.TextureFrame;
import com.etheller.warsmash.parsers.fdf.frames.UIFrame;
import com.etheller.warsmash.parsers.jass.Jass2.RootFrameListener;
import com.etheller.warsmash.parsers.mdlx.Layer.FilterMode;
import com.etheller.warsmash.units.manager.MutableObjectData;
import com.etheller.warsmash.util.FastNumberFormat;
import com.etheller.warsmash.util.ImageUtils;
import com.etheller.warsmash.util.RenderMathUtils;
import com.etheller.warsmash.util.War3ID;
import com.etheller.warsmash.util.WarsmashConstants;
import com.etheller.warsmash.viewer5.Scene;
import com.etheller.warsmash.viewer5.handlers.mdx.MdxComplexInstance;
@ -55,10 +59,12 @@ import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnit;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitClassification;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitFilterFunction;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitStateListener;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CUnitType;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.CWidget;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbility;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityAttack;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.CAbilityView;
import com.etheller.warsmash.viewer5.handlers.w3x.simulation.abilities.build.AbstractCAbilityBuild;
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.CodeKeyType;
@ -139,6 +145,8 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
private CAbilityView activeCommand;
private int activeCommandOrderId;
private RenderUnit activeCommandUnit;
private MdxComplexInstance cursorModelInstance = null;
private BufferedImage cursorModelPathing;
private int selectedSoundCount = 0;
private final ActiveCommandUnitTargetFilter activeCommandUnitTargetFilter;
@ -373,7 +381,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
@Override
public void startUsingAbility(final int abilityHandleId, final int orderId) {
public void startUsingAbility(final int abilityHandleId, final int orderId, final boolean rightClick) {
// TODO not O(N)
CAbilityView abilityToUse = null;
for (final CAbility ability : this.selectedUnit.getSimulationUnit().getAbilities()) {
@ -411,6 +419,9 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.unitOrderListener.issueImmediateOrder(this.selectedUnit.getSimulationUnit().getHandleId(),
abilityHandleId, orderId, isShiftDown());
}
if (rightClick) {
this.war3MapViewer.getUiSounds().getSound("AutoCastButtonClick").play(this.uiScene.audioContext, 0, 0);
}
}
@Override
@ -434,8 +445,10 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
public void update(final float deltaTime) {
this.portrait.update();
int mouseX = Gdx.input.getX();
int mouseY = Gdx.input.getY();
final int baseMouseX = Gdx.input.getX();
int mouseX = baseMouseX;
final int baseMouseY = Gdx.input.getY();
int mouseY = baseMouseY;
final int minX = this.uiViewport.getScreenX();
final int maxX = minX + this.uiViewport.getScreenWidth();
final int minY = this.uiViewport.getScreenY();
@ -458,38 +471,85 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
this.cursorFrame.setFramePointY(FramePoint.BOTTOM, screenCoordsVector.y);
if (this.activeCommand != null) {
this.cursorFrame.setSequence("Target");
}
else if (down) {
if (left) {
this.cursorFrame.setSequence("Scroll Down Left");
}
else if (right) {
this.cursorFrame.setSequence("Scroll Down Right");
if (this.activeCommand instanceof AbstractCAbilityBuild) {
boolean justLoaded = false;
if (this.cursorModelInstance == null) {
final MutableObjectData unitData = this.war3MapViewer.getAllObjectData().getUnits();
final War3ID buildingTypeId = new War3ID(this.activeCommandOrderId);
final String unitModelPath = this.war3MapViewer.getUnitModelPath(unitData.get(buildingTypeId));
final MdxModel model = (MdxModel) this.war3MapViewer.load(unitModelPath,
this.war3MapViewer.mapPathSolver, this.war3MapViewer.solverParams);
this.cursorModelInstance = (MdxComplexInstance) model.addInstance();
this.cursorModelInstance.setVertexColor(new float[] { 1, 1, 1, 0.5f });
this.cursorModelInstance.rotate(RenderUnit.tempQuat.setFromAxis(RenderMathUtils.VEC3_UNIT_Z,
this.war3MapViewer.simulation.getGameplayConstants().getBuildingAngle()));
this.cursorModelInstance.setAnimationSpeed(0f);
justLoaded = true;
final CUnitType buildingUnitType = this.war3MapViewer.simulation.getUnitData()
.getUnitType(buildingTypeId);
this.cursorModelPathing = buildingUnitType.getBuildingPathingPixelMap();
}
this.war3MapViewer.getClickLocation(clickLocationTemp, baseMouseX,
Gdx.graphics.getHeight() - baseMouseY);
if (this.cursorModelPathing != null) {
clickLocationTemp.x = Math.round(clickLocationTemp.x / 64f) * 64f;
clickLocationTemp.y = Math.round(clickLocationTemp.y / 64f) * 64f;
clickLocationTemp.z = this.war3MapViewer.terrain.getGroundHeight(clickLocationTemp.x,
clickLocationTemp.y);
}
this.cursorModelInstance.setLocation(clickLocationTemp);
SequenceUtils.randomSequence(this.cursorModelInstance, PrimaryTag.STAND);
this.cursorFrame.setVisible(false);
if (justLoaded) {
this.cursorModelInstance.setScene(this.war3MapViewer.worldScene);
}
}
else {
this.cursorFrame.setSequence("Scroll Down");
if (this.cursorModelInstance != null) {
this.cursorModelInstance.detach();
this.cursorModelInstance = null;
this.cursorFrame.setVisible(true);
}
this.cursorFrame.setSequence("Target");
}
}
else if (up) {
if (left) {
this.cursorFrame.setSequence("Scroll Up Left");
}
else if (right) {
this.cursorFrame.setSequence("Scroll Up Right");
}
else {
this.cursorFrame.setSequence("Scroll Up");
}
}
else if (left) {
this.cursorFrame.setSequence("Scroll Left");
}
else if (right) {
this.cursorFrame.setSequence("Scroll Right");
}
else {
this.cursorFrame.setSequence("Normal");
if (this.cursorModelInstance != null) {
this.cursorModelInstance.detach();
this.cursorModelInstance = null;
this.cursorFrame.setVisible(true);
}
if (down) {
if (left) {
this.cursorFrame.setSequence("Scroll Down Left");
}
else if (right) {
this.cursorFrame.setSequence("Scroll Down Right");
}
else {
this.cursorFrame.setSequence("Scroll Down");
}
}
else if (up) {
if (left) {
this.cursorFrame.setSequence("Scroll Up Left");
}
else if (right) {
this.cursorFrame.setSequence("Scroll Up Right");
}
else {
this.cursorFrame.setSequence("Scroll Up");
}
}
else if (left) {
this.cursorFrame.setSequence("Scroll Left");
}
else if (right) {
this.cursorFrame.setSequence("Scroll Right");
}
else {
this.cursorFrame.setSequence("Normal");
}
}
final float groundHeight = Math.max(
this.war3MapViewer.terrain.getGroundHeight(this.cameraManager.target.x, this.cameraManager.target.y),
@ -866,6 +926,7 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
}
this.selectedSoundCount = 0;
if (!shiftDown) {
this.subMenuOrderIdStack.clear();
this.activeCommandUnit = null;
this.activeCommand = null;
this.activeCommandOrderId = -1;
@ -896,7 +957,12 @@ public class MeleeUI implements CUnitStateListener, CommandButtonListener, Comma
portraitTalk();
}
this.selectedSoundCount = 0;
if (this.activeCommand instanceof AbstractCAbilityBuild) {
this.war3MapViewer.getUiSounds().getSound("PlaceBuildingDefault")
.play(this.uiScene.audioContext, 0, 0);
}
if (!shiftDown) {
this.subMenuOrderIdStack.clear();
this.activeCommandUnit = null;
this.activeCommand = null;
this.activeCommandOrderId = -1;

View File

@ -1,7 +1,7 @@
package com.etheller.warsmash.viewer5.handlers.w3x.ui.command;
public interface CommandCardCommandListener {
void startUsingAbility(int abilityHandleId, int orderId);
void startUsingAbility(int abilityHandleId, int orderId, boolean rightClick);
void openMenu(int orderId);
}