Add full support for auto tiles both regular ones and animated
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package com.bartlomiejpluta.base.api.map.layer.autotile;
|
||||
|
||||
import com.bartlomiejpluta.base.api.map.layer.base.Layer;
|
||||
|
||||
public interface AutoTileLayer extends Layer {
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
|
||||
import com.bartlomiejpluta.base.engine.project.serial.ProjectDeserializer;
|
||||
import com.bartlomiejpluta.base.engine.util.reflection.ClassLoader;
|
||||
import com.bartlomiejpluta.base.engine.world.animation.manager.AnimationManager;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.manager.AutoTileManager;
|
||||
import com.bartlomiejpluta.base.engine.world.character.manager.CharacterManager;
|
||||
import com.bartlomiejpluta.base.engine.world.character.manager.CharacterSetManager;
|
||||
import com.bartlomiejpluta.base.engine.world.icon.manager.IconManager;
|
||||
@@ -35,6 +36,7 @@ public class DefaultContextManager implements ContextManager {
|
||||
private final ProjectConfiguration configuration;
|
||||
private final ProjectDeserializer projectDeserializer;
|
||||
private final TileSetManager tileSetManager;
|
||||
private final AutoTileManager autoTileManager;
|
||||
private final MapManager mapManager;
|
||||
private final ImageManager imageManager;
|
||||
private final CharacterSetManager characterSetManager;
|
||||
@@ -58,6 +60,7 @@ public class DefaultContextManager implements ContextManager {
|
||||
|
||||
log.info("Registering project assets");
|
||||
project.getTileSetAssets().forEach(tileSetManager::registerAsset);
|
||||
project.getAutoTileSetAssets().forEach(autoTileManager::registerAsset);
|
||||
project.getMapAssets().forEach(mapManager::registerAsset);
|
||||
project.getImageAssets().forEach(imageManager::registerAsset);
|
||||
project.getCharacterSetAssets().forEach(characterSetManager::registerAsset);
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.bartlomiejpluta.base.engine.audio.asset.SoundAsset;
|
||||
import com.bartlomiejpluta.base.engine.gui.asset.FontAsset;
|
||||
import com.bartlomiejpluta.base.engine.gui.asset.WidgetDefinitionAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.asset.AutoTileSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.character.asset.CharacterSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.icon.asset.IconSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
|
||||
@@ -28,6 +29,9 @@ public class Project {
|
||||
@NonNull
|
||||
private final List<TileSetAsset> tileSetAssets;
|
||||
|
||||
@NonNull
|
||||
private final List<AutoTileSetAsset> autoTileSetAssets;
|
||||
|
||||
@NonNull
|
||||
private final List<GameMapAsset> mapAssets;
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.bartlomiejpluta.base.engine.gui.asset.FontAsset;
|
||||
import com.bartlomiejpluta.base.engine.gui.asset.WidgetDefinitionAsset;
|
||||
import com.bartlomiejpluta.base.engine.project.model.Project;
|
||||
import com.bartlomiejpluta.base.engine.world.animation.asset.AnimationAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.asset.AutoTileSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.character.asset.CharacterSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.icon.asset.IconSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
|
||||
@@ -28,6 +29,7 @@ public class ProtobufProjectDeserializer extends ProjectDeserializer {
|
||||
.name(proto.getName())
|
||||
.runner(proto.getRunner())
|
||||
.tileSetAssets(proto.getTileSetsList().stream().map(this::parseTileSetAsset).collect(toList()))
|
||||
.autoTileSetAssets(proto.getAutoTilesList().stream().map(this::parseAutoTileSetAsset).collect(toList()))
|
||||
.mapAssets(proto.getMapsList().stream().map(this::parseGameMapAsset).collect(toList()))
|
||||
.imageAssets(proto.getImagesList().stream().map(this::parseImageAsset).collect(toList()))
|
||||
.characterSetAssets(proto.getCharacterSetsList().stream().map(this::parseCharacterSetAsset).collect(toList()))
|
||||
@@ -39,6 +41,10 @@ public class ProtobufProjectDeserializer extends ProjectDeserializer {
|
||||
.build();
|
||||
}
|
||||
|
||||
private AutoTileSetAsset parseAutoTileSetAsset(ProjectProto.AutoTileSetAsset proto) {
|
||||
return new AutoTileSetAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns());
|
||||
}
|
||||
|
||||
private TileSetAsset parseTileSetAsset(ProjectProto.TileSetAsset proto) {
|
||||
return new TileSetAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.bartlomiejpluta.base.engine.world.autotile.asset;
|
||||
|
||||
import com.bartlomiejpluta.base.engine.common.asset.Asset;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
@Getter
|
||||
public class AutoTileSetAsset extends Asset {
|
||||
private final int rows;
|
||||
private final int columns;
|
||||
|
||||
public AutoTileSetAsset(@NonNull String uid, @NonNull String source, int rows, int columns) {
|
||||
super(uid, source);
|
||||
this.rows = rows;
|
||||
this.columns = columns;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.bartlomiejpluta.base.engine.world.autotile.manager;
|
||||
|
||||
import com.bartlomiejpluta.base.engine.common.init.Initializable;
|
||||
import com.bartlomiejpluta.base.engine.common.manager.AssetManager;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.asset.AutoTileSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTileSet;
|
||||
|
||||
public interface AutoTileManager extends Initializable, AssetManager<AutoTileSetAsset, AutoTileSet> {
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.bartlomiejpluta.base.engine.world.autotile.manager;
|
||||
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
|
||||
import com.bartlomiejpluta.base.engine.error.AppException;
|
||||
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
|
||||
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.asset.AutoTileSetAsset;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTileSet;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultAutoTileSetManager implements AutoTileManager {
|
||||
private final TextureManager textureManager;
|
||||
private final MeshManager meshManager;
|
||||
private final Map<String, AutoTileSet> autoTiles = new HashMap<>();
|
||||
private final Map<String, AutoTileSetAsset> assets = new HashMap<>();
|
||||
private final ProjectConfiguration configuration;
|
||||
private Mesh mesh;
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.mesh = meshManager.createQuad(1, 1, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerAsset(AutoTileSetAsset asset) {
|
||||
log.info("Registering [{}] auto tile set asset under UID: [{}]", asset.getSource(), asset.getUid());
|
||||
assets.put(asset.getUid(), asset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoTileSetAsset getAsset(String uid) {
|
||||
return assets.get(uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoTileSet loadObject(String uid) {
|
||||
var autoTile = autoTiles.get(uid);
|
||||
|
||||
if (autoTile == null) {
|
||||
var asset = assets.get(uid);
|
||||
|
||||
if (asset == null) {
|
||||
throw new AppException("The auto tile set asset with UID: [%s] does not exist", uid);
|
||||
}
|
||||
|
||||
var source = configuration.projectFile("autotiles", asset.getSource());
|
||||
var texture = textureManager.loadTexture(source, asset.getRows() * AutoTileSet.ROWS, asset.getColumns() * AutoTileSet.COLUMNS);
|
||||
autoTile = new AutoTileSet(texture, mesh, asset.getRows(), asset.getColumns());
|
||||
log.info("Loading auto tile set from assets to cache under the key: [{}]", uid);
|
||||
autoTiles.put(uid, autoTile);
|
||||
}
|
||||
|
||||
return autoTile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.bartlomiejpluta.base.engine.world.autotile.model;
|
||||
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.base.engine.world.object.Sprite;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector2fc;
|
||||
import org.joml.Vector2ic;
|
||||
|
||||
@Getter
|
||||
public class AutoSubTile extends Sprite {
|
||||
private final Vector2fc tileSpriteSize;
|
||||
private final Vector2f tileScale = new Vector2f(1, 1);
|
||||
|
||||
public AutoSubTile(Mesh mesh, AutoTileSet autoTileSet) {
|
||||
super(mesh, Material.textured(autoTileSet.getTexture()));
|
||||
|
||||
tileSpriteSize = material.getTexture().getSpriteSize();
|
||||
|
||||
super.setScale(tileSpriteSize.x() * tileScale.x, tileSpriteSize.y() * tileScale.y);
|
||||
}
|
||||
|
||||
public void recalculate(@NonNull Vector2ic spritePosition) {
|
||||
material.setSpritePosition(spritePosition.y(), spritePosition.x());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScale(float scale) {
|
||||
this.tileScale.x = scale;
|
||||
this.tileScale.y = scale;
|
||||
super.setScale(tileSpriteSize.x() * scale, tileSpriteSize.y() * scale);
|
||||
}
|
||||
|
||||
public void setScale(float scaleX, float scaleY) {
|
||||
this.tileScale.x = scaleX;
|
||||
this.tileScale.y = scaleY;
|
||||
super.setScale(tileSpriteSize.x() * scaleX, tileSpriteSize.y() * scaleY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScaleX() {
|
||||
return tileScale.x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleX(float scaleX) {
|
||||
this.tileScale.x = scaleX;
|
||||
super.setScaleX(tileSpriteSize.x() * scaleX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getScaleY() {
|
||||
return tileScale.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleY(float scaleY) {
|
||||
this.tileScale.y = scaleY;
|
||||
super.setScaleY(tileSpriteSize.x() * scaleY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.bartlomiejpluta.base.engine.world.autotile.model;
|
||||
|
||||
import com.bartlomiejpluta.base.api.camera.Camera;
|
||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.base.internal.render.Renderable;
|
||||
import com.bartlomiejpluta.base.internal.render.ShaderManager;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
public class AutoTile implements Renderable {
|
||||
private final AutoTileSet autoTileSet;
|
||||
private final AutoSubTile topLeftSubTile;
|
||||
private final AutoSubTile topRightSubTile;
|
||||
private final AutoSubTile bottomLeftSubTile;
|
||||
private final AutoSubTile bottomRightSubTile;
|
||||
|
||||
@Getter
|
||||
private int setId;
|
||||
|
||||
public AutoTile(@NonNull Mesh mesh, @NonNull AutoTileSet autoTileSet, int setId) {
|
||||
this.topLeftSubTile = new AutoSubTile(mesh, autoTileSet);
|
||||
this.topRightSubTile = new AutoSubTile(mesh, autoTileSet);
|
||||
this.bottomLeftSubTile = new AutoSubTile(mesh, autoTileSet);
|
||||
this.bottomRightSubTile = new AutoSubTile(mesh, autoTileSet);
|
||||
this.autoTileSet = autoTileSet;
|
||||
this.setId = setId;
|
||||
}
|
||||
|
||||
public void setCoordinates(int column, int row) {
|
||||
var x = column * autoTileSet.getTileSize().x() * 2;
|
||||
var y = row * autoTileSet.getTileSize().y() * 2;
|
||||
|
||||
topLeftSubTile.setPosition(x, y);
|
||||
topRightSubTile.setPosition(x + autoTileSet.getTileSize().x(), y);
|
||||
bottomLeftSubTile.setPosition(x, y + autoTileSet.getTileSize().y());
|
||||
bottomRightSubTile.setPosition(x + autoTileSet.getTileSize().x(), y + autoTileSet.getTileSize().y());
|
||||
}
|
||||
|
||||
public void regularTile(Integer setId, int topLeft, int topRight, int bottomLeft, int bottomRight) {
|
||||
if (setId == null) {
|
||||
setId = this.setId;
|
||||
}
|
||||
|
||||
topLeftSubTile.recalculate(autoTileSet.getTopLeftSubTiles()[setId][topLeft]);
|
||||
topRightSubTile.recalculate(autoTileSet.getTopRightSubTiles()[setId][topRight]);
|
||||
bottomLeftSubTile.recalculate(autoTileSet.getBottomLeftSubTiles()[setId][bottomLeft]);
|
||||
bottomRightSubTile.recalculate(autoTileSet.getBottomRightSubTiles()[setId][bottomRight]);
|
||||
}
|
||||
|
||||
public void islandTile(Integer setId) {
|
||||
if (setId == null) {
|
||||
setId = this.setId;
|
||||
}
|
||||
|
||||
topLeftSubTile.recalculate(autoTileSet.getIslandSubTiles()[setId][0]);
|
||||
topRightSubTile.recalculate(autoTileSet.getIslandSubTiles()[setId][1]);
|
||||
bottomLeftSubTile.recalculate(autoTileSet.getIslandSubTiles()[setId][2]);
|
||||
bottomRightSubTile.recalculate(autoTileSet.getIslandSubTiles()[setId][3]);
|
||||
}
|
||||
|
||||
public void shiftTileSet() {
|
||||
this.setId = (setId + 1) % autoTileSet.getSetsCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
|
||||
topLeftSubTile.render(screen, camera, shaderManager);
|
||||
topRightSubTile.render(screen, camera, shaderManager);
|
||||
bottomLeftSubTile.render(screen, camera, shaderManager);
|
||||
bottomRightSubTile.render(screen, camera, shaderManager);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package com.bartlomiejpluta.base.engine.world.autotile.model;
|
||||
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import org.joml.Vector2fc;
|
||||
import org.joml.Vector2i;
|
||||
import org.joml.Vector2ic;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
// Algorithm source: https://love2d.org/forums/viewtopic.php?t=7826
|
||||
public class AutoTileSet {
|
||||
public static final int ROWS = 6;
|
||||
public static final int COLUMNS = 4;
|
||||
|
||||
private static final Vector2ic ISLAND_TILE = new Vector2i(0, 0);
|
||||
private static final Vector2ic CROSS_TILE = new Vector2i(1, 0);
|
||||
private static final Vector2ic TOP_LEFT_TILE = new Vector2i(0, 1);
|
||||
private static final Vector2ic TOP_RIGHT_TILE = new Vector2i(1, 1);
|
||||
private static final Vector2ic BOTTOM_LEFT_TILE = new Vector2i(0, 2);
|
||||
private static final Vector2ic BOTTOM_RIGHT_TILE = new Vector2i(1, 2);
|
||||
|
||||
@Getter
|
||||
private final Vector2ic[][] islandSubTiles;
|
||||
|
||||
@Getter
|
||||
private final Vector2ic[][] topLeftSubTiles;
|
||||
|
||||
@Getter
|
||||
private final Vector2ic[][] topRightSubTiles;
|
||||
|
||||
@Getter
|
||||
private final Vector2ic[][] bottomLeftSubTiles;
|
||||
|
||||
@Getter
|
||||
private final Vector2ic[][] bottomRightSubTiles;
|
||||
|
||||
@Getter
|
||||
private final Texture texture;
|
||||
private final Mesh mesh;
|
||||
|
||||
@Getter
|
||||
private final Vector2fc tileSize;
|
||||
private final int rows;
|
||||
private final int columns;
|
||||
|
||||
@Getter
|
||||
private final int setsCount;
|
||||
|
||||
public AutoTileSet(@NonNull Texture texture, @NonNull Mesh mesh, int rows, int columns) {
|
||||
this.texture = texture;
|
||||
this.mesh = mesh;
|
||||
this.rows = rows;
|
||||
this.columns = columns;
|
||||
this.tileSize = texture.getSpriteSize();
|
||||
this.setsCount = rows * columns;
|
||||
|
||||
var islandSubTiles = new LinkedList<Vector2ic[]>();
|
||||
var topLeftSubTiles = new LinkedList<Vector2ic[]>();
|
||||
var topRightSubTiles = new LinkedList<Vector2ic[]>();
|
||||
var bottomLeftSubTiles = new LinkedList<Vector2ic[]>();
|
||||
var bottomRightSubTiles = new LinkedList<Vector2ic[]>();
|
||||
|
||||
for (int setId = 0; setId < setsCount; ++setId) {
|
||||
var crossSubTiles = cutSubTiles(setId, CROSS_TILE);
|
||||
var topLeftTileSubTiles = cutSubTiles(setId, TOP_LEFT_TILE);
|
||||
var topRightTileSubTiles = cutSubTiles(setId, TOP_RIGHT_TILE);
|
||||
var bottomLeftTileSubTiles = cutSubTiles(setId, BOTTOM_LEFT_TILE);
|
||||
var bottomRightTileSubTiles = cutSubTiles(setId, BOTTOM_RIGHT_TILE);
|
||||
|
||||
/*
|
||||
* Indexes:
|
||||
* 0 - No connected tiles
|
||||
* 1 - Left tile is connected
|
||||
* 2 - Right tile is connected
|
||||
* 3 - Left and Right tiles are connected
|
||||
* 4 - Left, Right, and Center tiles are connected.
|
||||
*/
|
||||
var tl3 = crossSubTiles[0];
|
||||
var tr3 = crossSubTiles[1];
|
||||
var bl3 = crossSubTiles[2];
|
||||
var br3 = crossSubTiles[3];
|
||||
|
||||
var tl0 = topLeftTileSubTiles[0];
|
||||
var tr2 = topLeftTileSubTiles[1];
|
||||
var bl1 = topLeftTileSubTiles[2];
|
||||
var br4 = topLeftTileSubTiles[3];
|
||||
|
||||
var tl1 = topRightTileSubTiles[0];
|
||||
var tr0 = topRightTileSubTiles[1];
|
||||
var bl4 = topRightTileSubTiles[2];
|
||||
var br2 = topRightTileSubTiles[3];
|
||||
|
||||
var tl2 = bottomLeftTileSubTiles[0];
|
||||
var tr4 = bottomLeftTileSubTiles[1];
|
||||
var bl0 = bottomLeftTileSubTiles[2];
|
||||
var br1 = bottomLeftTileSubTiles[3];
|
||||
|
||||
var tl4 = bottomRightTileSubTiles[0];
|
||||
var tr1 = bottomRightTileSubTiles[1];
|
||||
var bl2 = bottomRightTileSubTiles[2];
|
||||
var br0 = bottomRightTileSubTiles[3];
|
||||
|
||||
islandSubTiles.add(cutSubTiles(setId, ISLAND_TILE));
|
||||
topLeftSubTiles.add(new Vector2ic[]{tl0, tl1, tl2, tl3, tl4});
|
||||
topRightSubTiles.add(new Vector2ic[]{tr0, tr1, tr2, tr3, tr4});
|
||||
bottomLeftSubTiles.add(new Vector2ic[]{bl0, bl1, bl2, bl3, bl4});
|
||||
bottomRightSubTiles.add(new Vector2ic[]{br0, br1, br2, br3, br4});
|
||||
}
|
||||
|
||||
this.islandSubTiles = islandSubTiles.toArray(new Vector2ic[islandSubTiles.size()][]);
|
||||
this.topLeftSubTiles = topLeftSubTiles.toArray(new Vector2ic[topLeftSubTiles.size()][]);
|
||||
this.topRightSubTiles = topRightSubTiles.toArray(new Vector2ic[topRightSubTiles.size()][]);
|
||||
this.bottomLeftSubTiles = bottomLeftSubTiles.toArray(new Vector2ic[bottomLeftSubTiles.size()][]);
|
||||
this.bottomRightSubTiles = bottomRightSubTiles.toArray(new Vector2ic[bottomRightSubTiles.size()][]);
|
||||
}
|
||||
|
||||
private Vector2ic[] cutSubTiles(int setId, Vector2ic tile) {
|
||||
var topLeft = getTile(setId, tile.y() * 2, tile.x() * 2);
|
||||
var topRight = getTile(setId, tile.y() * 2, tile.x() * 2 + 1);
|
||||
var bottomLeft = getTile(setId, tile.y() * 2 + 1, tile.x() * 2);
|
||||
var bottomRight = getTile(setId, tile.y() * 2 + 1, tile.x() * 2 + 1);
|
||||
|
||||
return new Vector2ic[]{topLeft, topRight, bottomLeft, bottomRight};
|
||||
}
|
||||
|
||||
private Vector2ic getTile(int setId, int row, int column) {
|
||||
return new Vector2i(((setId / columns) * ROWS) + row, ((setId % columns) * COLUMNS) + column);
|
||||
}
|
||||
|
||||
public AutoTile createTile(int setId) {
|
||||
return new AutoTile(mesh, this, setId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.bartlomiejpluta.base.engine.world.map.layer.autotile;
|
||||
|
||||
import com.bartlomiejpluta.base.api.camera.Camera;
|
||||
import com.bartlomiejpluta.base.api.map.layer.autotile.AutoTileLayer;
|
||||
import com.bartlomiejpluta.base.api.map.model.GameMap;
|
||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTile;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTileSet;
|
||||
import com.bartlomiejpluta.base.engine.world.map.layer.base.BaseLayer;
|
||||
import com.bartlomiejpluta.base.internal.render.ShaderManager;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
// Algorithm source: https://love2d.org/forums/viewtopic.php?t=7826
|
||||
public class DefaultAutoTileLayer extends BaseLayer implements AutoTileLayer {
|
||||
private final AutoTileSet autoTileSet;
|
||||
private final AutoTile[][] layer;
|
||||
private boolean animated;
|
||||
private double animationDuration;
|
||||
private double accumulator;
|
||||
|
||||
public DefaultAutoTileLayer(@NonNull GameMap map, @NonNull AutoTileSet autoTileSet, int rows, int columns, boolean animated, double animationDuration) {
|
||||
super(map);
|
||||
this.autoTileSet = autoTileSet;
|
||||
this.animated = animated;
|
||||
this.animationDuration = animationDuration;
|
||||
layer = new AutoTile[rows][columns];
|
||||
Arrays.stream(layer).forEach(tiles -> Arrays.fill(tiles, null));
|
||||
}
|
||||
|
||||
public void setTile(int row, int column, int setId) {
|
||||
var tile = autoTileSet.createTile(setId);
|
||||
tile.setCoordinates(column, row);
|
||||
layer[row][column] = tile;
|
||||
}
|
||||
|
||||
public void clearTile(int row, int column) {
|
||||
layer[row][column] = null;
|
||||
}
|
||||
|
||||
public void recalculate(Integer setId) {
|
||||
for (int row = 0; row < map.getRows(); ++row) {
|
||||
for (int column = 0; column < map.getColumns(); ++column) {
|
||||
recalculateTile(setId, row, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void recalculateTile(Integer setId, int row, int column) {
|
||||
if (layer[row][column] == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var topLeft = 0;
|
||||
var topRight = 0;
|
||||
var bottomLeft = 0;
|
||||
var bottomRight = 0;
|
||||
|
||||
var tile = layer[row][column];
|
||||
|
||||
if (animated) {
|
||||
tile.shiftTileSet();
|
||||
}
|
||||
|
||||
// Top
|
||||
if (row > 0 && layer[row - 1][column] != null) {
|
||||
topLeft += 2;
|
||||
topRight += 1;
|
||||
}
|
||||
|
||||
// Bottom
|
||||
if (row < map.getRows() - 1 && layer[row + 1][column] != null) {
|
||||
bottomLeft += 1;
|
||||
bottomRight += 2;
|
||||
}
|
||||
|
||||
// Left
|
||||
if (column > 0 && layer[row][column - 1] != null) {
|
||||
topLeft += 1;
|
||||
bottomLeft += 2;
|
||||
}
|
||||
|
||||
// Right
|
||||
if (column < map.getColumns() - 1 && layer[row][column + 1] != null) {
|
||||
topRight += 2;
|
||||
bottomRight += 1;
|
||||
}
|
||||
|
||||
// Top left
|
||||
if (row > 0 && column > 0 && layer[row - 1][column - 1] != null && topLeft == 3) {
|
||||
topLeft = 4;
|
||||
}
|
||||
|
||||
// Top right
|
||||
if (row > 0 && column < map.getColumns() - 1 && layer[row - 1][column + 1] != null && topRight == 3) {
|
||||
topRight = 4;
|
||||
}
|
||||
|
||||
// Bottom left
|
||||
if (row < map.getRows() - 1 && column > 0 && layer[row + 1][column - 1] != null && bottomLeft == 3) {
|
||||
bottomLeft = 4;
|
||||
}
|
||||
|
||||
// Bottom right
|
||||
if (row < map.getRows() - 1 && column < map.getColumns() - 1 && layer[row + 1][column + 1] != null && bottomRight == 3) {
|
||||
bottomRight = 4;
|
||||
}
|
||||
|
||||
if (topLeft == 0 && topRight == 0 && bottomLeft == 0 && bottomRight == 0) {
|
||||
tile.islandTile(setId);
|
||||
return;
|
||||
}
|
||||
|
||||
tile.regularTile(setId, topLeft, topRight, bottomLeft, bottomRight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
super.update(dt);
|
||||
|
||||
if (animated) {
|
||||
accumulator += dt;
|
||||
|
||||
if (accumulator > animationDuration) {
|
||||
accumulator = 0;
|
||||
recalculate(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(Screen screen, Camera camera, ShaderManager shaderManager) {
|
||||
for (var row : layer) {
|
||||
for (var tile : row) {
|
||||
if (tile != null) {
|
||||
tile.render(screen, camera, shaderManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.render(screen, camera, shaderManager);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import com.bartlomiejpluta.base.api.map.layer.tile.TileLayer;
|
||||
import com.bartlomiejpluta.base.api.map.model.GameMap;
|
||||
import com.bartlomiejpluta.base.api.screen.Screen;
|
||||
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.model.AutoTileSet;
|
||||
import com.bartlomiejpluta.base.engine.world.map.layer.autotile.DefaultAutoTileLayer;
|
||||
import com.bartlomiejpluta.base.engine.world.map.layer.color.DefaultColorLayer;
|
||||
import com.bartlomiejpluta.base.engine.world.map.layer.image.DefaultImageLayer;
|
||||
import com.bartlomiejpluta.base.engine.world.map.layer.object.DefaultObjectLayer;
|
||||
@@ -118,6 +120,13 @@ public class DefaultGameMap implements Renderable, Updatable, GameMap {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public DefaultAutoTileLayer createAutoTileLayer(@NonNull AutoTileSet autoTileSet, boolean animated, double animationDuration) {
|
||||
var layer = new DefaultAutoTileLayer(this, autoTileSet, rows, columns, animated, animationDuration);
|
||||
layers.add(layer);
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
public ImageLayer createImageLayer(Image image, float opacity, float x, float y, float scaleX, float scaleY, ImageLayerMode mode, boolean parallax) {
|
||||
var layer = new DefaultImageLayer(this, image, opacity, x, y, scaleX, scaleY, mode, parallax);
|
||||
layers.add(layer);
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.bartlomiejpluta.base.api.map.layer.image.ImageLayerMode;
|
||||
import com.bartlomiejpluta.base.api.map.layer.object.PassageAbility;
|
||||
import com.bartlomiejpluta.base.engine.error.AppException;
|
||||
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
|
||||
import com.bartlomiejpluta.base.engine.world.autotile.manager.AutoTileManager;
|
||||
import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager;
|
||||
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
|
||||
import com.bartlomiejpluta.base.engine.world.tileset.manager.TileSetManager;
|
||||
@@ -21,6 +22,7 @@ import java.io.InputStream;
|
||||
public class ProtobufMapDeserializer extends MapDeserializer {
|
||||
private final MeshManager meshManager;
|
||||
private final TileSetManager tileSetManager;
|
||||
private final AutoTileManager autoTileManager;
|
||||
private final ImageManager imageManager;
|
||||
|
||||
@Override
|
||||
@@ -36,6 +38,8 @@ public class ProtobufMapDeserializer extends MapDeserializer {
|
||||
private void deserializeLayer(DefaultGameMap map, GameMapProto.Layer proto) {
|
||||
if (proto.hasTileLayer()) {
|
||||
deserializeTileLayer(map, proto);
|
||||
} else if (proto.hasAutoTileLayer()) {
|
||||
deserializeAutoTileLayer(map, proto);
|
||||
} else if (proto.hasObjectLayer()) {
|
||||
deserializeObjectLayer(map, proto);
|
||||
} else if (proto.hasColorLayer()) {
|
||||
@@ -63,6 +67,28 @@ public class ProtobufMapDeserializer extends MapDeserializer {
|
||||
}
|
||||
}
|
||||
|
||||
private void deserializeAutoTileLayer(DefaultGameMap map, GameMapProto.Layer proto) {
|
||||
var autoTileSet = autoTileManager.loadObject(proto.getAutoTileLayer().getAutotileUID());
|
||||
var animated = proto.getAutoTileLayer().getAnimated();
|
||||
var animationDuration = proto.getAutoTileLayer().getAnimationDuration();
|
||||
|
||||
var layer = map.createAutoTileLayer(autoTileSet, animated, animationDuration);
|
||||
var columns = map.getColumns();
|
||||
var tiles = proto.getAutoTileLayer().getTilesList();
|
||||
|
||||
for (var i = 0; i < tiles.size(); ++i) {
|
||||
var tile = tiles.get(i);
|
||||
|
||||
if (tile == 0) {
|
||||
layer.clearTile(i / columns, i % columns);
|
||||
} else {
|
||||
layer.setTile(i / columns, i % columns, tile - 1);
|
||||
}
|
||||
}
|
||||
|
||||
layer.recalculate(null);
|
||||
}
|
||||
|
||||
private void deserializeObjectLayer(DefaultGameMap map, GameMapProto.Layer proto) {
|
||||
var layer = map.createObjectLayer();
|
||||
var columns = map.getColumns();
|
||||
|
||||
Reference in New Issue
Block a user