mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-15 04:54:05 +09:00
850 lines
32 KiB
Kotlin
850 lines
32 KiB
Kotlin
package net.torvald.terrarum.worlddrawer
|
|
|
|
import com.badlogic.gdx.Gdx
|
|
import com.badlogic.gdx.graphics.*
|
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
|
import com.badlogic.gdx.graphics.glutils.ShaderProgram
|
|
import com.badlogic.gdx.math.Matrix4
|
|
import net.torvald.terrarum.gameworld.GameWorld
|
|
import net.torvald.terrarum.gameworld.PairedMapLayer
|
|
import net.torvald.terrarum.blockproperties.Block
|
|
import net.torvald.terrarum.blockproperties.BlockCodex
|
|
import net.torvald.terrarum.*
|
|
import net.torvald.terrarum.AppLoader.printdbg
|
|
import net.torvald.terrarum.ceilInt
|
|
import net.torvald.terrarum.gameworld.fmod
|
|
import net.torvald.terrarum.itemproperties.ItemCodex.ITEM_TILES
|
|
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
|
|
import java.io.BufferedOutputStream
|
|
import java.io.File
|
|
import java.io.FileOutputStream
|
|
import java.util.zip.GZIPInputStream
|
|
|
|
|
|
/**
|
|
* Sub-portion of IngameRenderer. You are not supposed to directly deal with this.
|
|
*
|
|
* The terrain texture atlas is HARD CODED as "4096x4096, on which 256x256 tiles are contained"
|
|
* in the shader (tiling.frag). This will not be a problem in the base game, but if you are modifying
|
|
* this engine for your project, you must edit the shader program accordingly.
|
|
*
|
|
* Created by minjaesong on 2016-01-19.
|
|
*/
|
|
internal object BlocksDrawer {
|
|
lateinit var world: GameWorld
|
|
|
|
|
|
private val TILE_SIZE = FeaturesDrawer.TILE_SIZE
|
|
private val TILE_SIZEF = FeaturesDrawer.TILE_SIZE.toFloat()
|
|
|
|
val tilesTerrain: TextureRegionPack
|
|
val tilesWire: TextureRegionPack
|
|
val tileItemWall: TextureRegionPack
|
|
|
|
//val tileItemWall = Image(TILE_SIZE * 16, TILE_SIZE * GameWorld.TILES_SUPPORTED / 16) // 4 MB
|
|
|
|
|
|
val wallOverlayColour = Color(2f/3f, 2f/3f, 2f/3f, 1f)
|
|
|
|
const val breakAnimSteps = 10
|
|
|
|
val WALL = GameWorld.WALL
|
|
val TERRAIN = GameWorld.TERRAIN
|
|
val WIRE = GameWorld.WIRE
|
|
|
|
private const val NEARBY_TILE_KEY_UP = 0
|
|
private const val NEARBY_TILE_KEY_RIGHT = 1
|
|
private const val NEARBY_TILE_KEY_DOWN = 2
|
|
private const val NEARBY_TILE_KEY_LEFT = 3
|
|
|
|
private const val NEARBY_TILE_CODE_UP = 1
|
|
private const val NEARBY_TILE_CODE_RIGHT = 2
|
|
private const val NEARBY_TILE_CODE_DOWN = 4
|
|
private const val NEARBY_TILE_CODE_LEFT = 8
|
|
|
|
|
|
private const val GZIP_READBUF_SIZE = 8192
|
|
|
|
|
|
private lateinit var terrainTilesBuffer: Array<IntArray>
|
|
private lateinit var wallTilesBuffer: Array<IntArray>
|
|
private lateinit var wireTilesBuffer: Array<IntArray>
|
|
private lateinit var tilesBuffer: Pixmap
|
|
|
|
|
|
private lateinit var tilesQuad: Mesh
|
|
private val shader = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/tiling.frag"))
|
|
|
|
init {
|
|
// hard-coded as tga.gz
|
|
val gzFileList = listOf("blocks/terrain.tga.gz", "blocks/wire.tga.gz")
|
|
val gzTmpFName = listOf("tmp_terrain.tga", "tmp_wire.tga")
|
|
// unzip GZIP temporarily
|
|
gzFileList.forEachIndexed { index, filename ->
|
|
val terrainTexFile = ModMgr.getGdxFile("basegame", filename)
|
|
val gzi = GZIPInputStream(terrainTexFile.read(GZIP_READBUF_SIZE))
|
|
val wholeFile = gzi.readBytes()
|
|
gzi.close()
|
|
val fos = BufferedOutputStream(FileOutputStream(gzTmpFName[index]))
|
|
fos.write(wholeFile)
|
|
fos.flush()
|
|
fos.close()
|
|
}
|
|
|
|
val terrainPixMap = Pixmap(Gdx.files.internal(gzTmpFName[0]))
|
|
val wirePixMap = Pixmap(Gdx.files.internal(gzTmpFName[1]))
|
|
|
|
// delete temp files
|
|
gzTmpFName.forEach { File(it).delete() }
|
|
|
|
tilesTerrain = TextureRegionPack(Texture(terrainPixMap), TILE_SIZE, TILE_SIZE)
|
|
tilesTerrain.texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
|
tilesWire = TextureRegionPack(Texture(wirePixMap), TILE_SIZE, TILE_SIZE)
|
|
tilesWire.texture.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
|
|
|
// also dispose unused temp files
|
|
//terrainPixMap.dispose() // commented: tileItemWall needs it
|
|
wirePixMap.dispose()
|
|
|
|
|
|
|
|
|
|
// create item_wall images
|
|
// --> make pixmap
|
|
val tileItemImgPixMap = Pixmap(TILE_SIZE * 16, TILE_SIZE * GameWorld.TILES_SUPPORTED / 16, Pixmap.Format.RGBA8888)
|
|
tileItemImgPixMap.pixels.rewind()
|
|
|
|
for (tileID in ITEM_TILES) {
|
|
|
|
val tileX = (tileID % 16) * 16
|
|
val tileY = tileID / 16
|
|
val tile = tilesTerrain.get(tileX, tileY)
|
|
|
|
|
|
// slow memory copy :\ I'm afraid I can't random-access bytebuffer...
|
|
for (scanline in 0 until tileItemImgPixMap.height) {
|
|
for (x in 0 until TILE_SIZE) {
|
|
val pixel = terrainPixMap.getPixel(tileX + x, scanline)
|
|
tileItemImgPixMap.drawPixel(x + TILE_SIZE * (tileID % 16), scanline, pixel)
|
|
}
|
|
}
|
|
}
|
|
tileItemImgPixMap.pixels.rewind()
|
|
// turn pixmap into texture
|
|
tileItemWall = TextureRegionPack(Texture(tileItemImgPixMap), TILE_SIZE, TILE_SIZE)
|
|
|
|
|
|
|
|
tileItemImgPixMap.dispose()
|
|
terrainPixMap.dispose() // finally
|
|
}
|
|
|
|
/**
|
|
* Connectivity group 01 : artificial tiles
|
|
* It holds different shading rule to discriminate with group 02, index 0 is single tile.
|
|
* These are the tiles that only connects to itself, will not connect to colour variants
|
|
*/
|
|
private val TILES_CONNECT_SELF = hashSetOf(
|
|
Block.GLASS_CRUDE,
|
|
Block.GLASS_CLEAN,
|
|
Block.ILLUMINATOR_BLACK,
|
|
Block.ILLUMINATOR_BLUE,
|
|
Block.ILLUMINATOR_BROWN,
|
|
Block.ILLUMINATOR_CYAN,
|
|
Block.ILLUMINATOR_FUCHSIA,
|
|
Block.ILLUMINATOR_GREEN,
|
|
Block.ILLUMINATOR_GREEN_DARK,
|
|
Block.ILLUMINATOR_GREY_DARK,
|
|
Block.ILLUMINATOR_GREY_LIGHT,
|
|
Block.ILLUMINATOR_GREY_MED,
|
|
Block.ILLUMINATOR_ORANGE,
|
|
Block.ILLUMINATOR_PURPLE,
|
|
Block.ILLUMINATOR_RED,
|
|
Block.ILLUMINATOR_TAN,
|
|
Block.ILLUMINATOR_WHITE,
|
|
Block.ILLUMINATOR_YELLOW,
|
|
Block.ILLUMINATOR_BLACK_OFF,
|
|
Block.ILLUMINATOR_BLUE_OFF,
|
|
Block.ILLUMINATOR_BROWN_OFF,
|
|
Block.ILLUMINATOR_CYAN_OFF,
|
|
Block.ILLUMINATOR_FUCHSIA_OFF,
|
|
Block.ILLUMINATOR_GREEN_OFF,
|
|
Block.ILLUMINATOR_GREEN_DARK_OFF,
|
|
Block.ILLUMINATOR_GREY_DARK_OFF,
|
|
Block.ILLUMINATOR_GREY_LIGHT_OFF,
|
|
Block.ILLUMINATOR_GREY_MED_OFF,
|
|
Block.ILLUMINATOR_ORANGE_OFF,
|
|
Block.ILLUMINATOR_PURPLE_OFF,
|
|
Block.ILLUMINATOR_RED_OFF,
|
|
Block.ILLUMINATOR_TAN_OFF,
|
|
Block.ILLUMINATOR_WHITE_OFF,
|
|
Block.ILLUMINATOR_YELLOW,
|
|
Block.DAYLIGHT_CAPACITOR,
|
|
|
|
Block.ORE_COPPER,
|
|
Block.ORE_IRON,
|
|
Block.ORE_GOLD,
|
|
Block.ORE_SILVER,
|
|
Block.ORE_ILMENITE,
|
|
Block.ORE_AURICHALCUM
|
|
)
|
|
|
|
/**
|
|
* To interact with external modules
|
|
*/
|
|
@JvmStatic fun addConnectSelf(blockID: Int): Boolean {
|
|
return TILES_CONNECT_SELF.add(blockID)
|
|
}
|
|
|
|
/**
|
|
* Connectivity group 02 : natural tiles
|
|
* It holds different shading rule to discriminate with group 01, index 0 is middle tile.
|
|
*/
|
|
private val TILES_CONNECT_MUTUAL = hashSetOf(
|
|
Block.STONE,
|
|
Block.STONE_QUARRIED,
|
|
Block.STONE_TILE_WHITE,
|
|
Block.STONE_BRICKS,
|
|
Block.DIRT,
|
|
Block.GRASS,
|
|
Block.GRASSWALL,
|
|
Block.PLANK_BIRCH,
|
|
Block.PLANK_BLOODROSE,
|
|
Block.PLANK_EBONY,
|
|
Block.PLANK_NORMAL,
|
|
Block.SAND,
|
|
Block.SAND_WHITE,
|
|
Block.SAND_RED,
|
|
Block.SAND_DESERT,
|
|
Block.SAND_BLACK,
|
|
Block.SAND_GREEN,
|
|
Block.GRAVEL,
|
|
Block.GRAVEL_GREY,
|
|
Block.SNOW,
|
|
Block.ICE_NATURAL,
|
|
Block.ICE_MAGICAL,
|
|
|
|
Block.SANDSTONE,
|
|
Block.SANDSTONE_BLACK,
|
|
Block.SANDSTONE_DESERT,
|
|
Block.SANDSTONE_RED,
|
|
Block.SANDSTONE_WHITE,
|
|
Block.SANDSTONE_GREEN,
|
|
|
|
Block.WATER,
|
|
Block.WATER_1,
|
|
Block.WATER_2,
|
|
Block.WATER_3,
|
|
Block.WATER_4,
|
|
Block.WATER_5,
|
|
Block.WATER_6,
|
|
Block.WATER_7,
|
|
Block.WATER_8,
|
|
Block.WATER_9,
|
|
Block.WATER_10,
|
|
Block.WATER_11,
|
|
Block.WATER_12,
|
|
Block.WATER_13,
|
|
Block.WATER_14,
|
|
Block.WATER_15,
|
|
Block.LAVA,
|
|
Block.LAVA_1,
|
|
Block.LAVA_2,
|
|
Block.LAVA_3,
|
|
Block.LAVA_4,
|
|
Block.LAVA_5,
|
|
Block.LAVA_6,
|
|
Block.LAVA_7,
|
|
Block.LAVA_8,
|
|
Block.LAVA_9,
|
|
Block.LAVA_10,
|
|
Block.LAVA_11,
|
|
Block.LAVA_12,
|
|
Block.LAVA_13,
|
|
Block.LAVA_14,
|
|
Block.LAVA_15
|
|
)
|
|
|
|
/**
|
|
* To interact with external modules
|
|
*/
|
|
@JvmStatic fun addConnectMutual(blockID: Int): Boolean {
|
|
return TILES_CONNECT_MUTUAL.add(blockID)
|
|
}
|
|
|
|
/**
|
|
* Torches, levers, switches, ...
|
|
*/
|
|
private val TILES_WALL_STICKER = hashSetOf(
|
|
Block.TORCH,
|
|
Block.TORCH_FROST,
|
|
Block.TORCH_OFF,
|
|
Block.TORCH_FROST_OFF
|
|
)
|
|
|
|
/**
|
|
* To interact with external modules
|
|
*/
|
|
@JvmStatic fun addWallSticker(blockID: Int): Boolean {
|
|
return TILES_WALL_STICKER.add(blockID)
|
|
}
|
|
|
|
/**
|
|
* platforms, ...
|
|
*/
|
|
private val TILES_WALL_STICKER_CONNECT_SELF = hashSetOf(
|
|
Block.PLATFORM_BIRCH,
|
|
Block.PLATFORM_BLOODROSE,
|
|
Block.PLATFORM_EBONY,
|
|
Block.PLATFORM_STONE,
|
|
Block.PLATFORM_WOODEN
|
|
)
|
|
|
|
/**
|
|
* To interact with external modules
|
|
*/
|
|
@JvmStatic fun addWallStickerConnectSelf(blockID: Int): Boolean {
|
|
return TILES_WALL_STICKER_CONNECT_SELF.add(blockID)
|
|
}
|
|
|
|
/**
|
|
* Tiles that half-transparent and has hue
|
|
* will blend colour using colour multiplication
|
|
* i.e. red hues get lost if you dive into the water
|
|
*/
|
|
private val TILES_BLEND_MUL = hashSetOf(
|
|
Block.WATER,
|
|
Block.WATER_1,
|
|
Block.WATER_2,
|
|
Block.WATER_3,
|
|
Block.WATER_4,
|
|
Block.WATER_5,
|
|
Block.WATER_6,
|
|
Block.WATER_7,
|
|
Block.WATER_8,
|
|
Block.WATER_9,
|
|
Block.WATER_10,
|
|
Block.WATER_11,
|
|
Block.WATER_12,
|
|
Block.WATER_13,
|
|
Block.WATER_14,
|
|
Block.WATER_15,
|
|
Block.LAVA,
|
|
Block.LAVA_1,
|
|
Block.LAVA_2,
|
|
Block.LAVA_3,
|
|
Block.LAVA_4,
|
|
Block.LAVA_5,
|
|
Block.LAVA_6,
|
|
Block.LAVA_7,
|
|
Block.LAVA_8,
|
|
Block.LAVA_9,
|
|
Block.LAVA_10,
|
|
Block.LAVA_11,
|
|
Block.LAVA_12,
|
|
Block.LAVA_13,
|
|
Block.LAVA_14,
|
|
Block.LAVA_15
|
|
)
|
|
|
|
/**
|
|
* To interact with external modules
|
|
*/
|
|
@JvmStatic fun addBlendMul(blockID: Int): Boolean {
|
|
return TILES_BLEND_MUL.add(blockID)
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////
|
|
// NO draw lightmap using colour filter, actors must also be hidden behind the darkness
|
|
///////////////////////////////////////////
|
|
|
|
internal fun renderWall(projectionMatrix: Matrix4) {
|
|
// blend normal
|
|
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
|
Gdx.gl.glEnable(GL20.GL_BLEND)
|
|
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
|
|
|
drawTiles(WALL, false)
|
|
renderUsingBuffer(WALL, projectionMatrix)
|
|
}
|
|
|
|
internal fun renderTerrain(projectionMatrix: Matrix4) {
|
|
// blend normal
|
|
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
|
Gdx.gl.glEnable(GL20.GL_BLEND)
|
|
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
|
|
|
drawTiles(TERRAIN, false) // regular tiles
|
|
renderUsingBuffer(TERRAIN, projectionMatrix)
|
|
}
|
|
|
|
internal fun renderFront(projectionMatrix: Matrix4, drawWires: Boolean) {
|
|
// blend mul
|
|
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
|
Gdx.gl.glEnable(GL20.GL_BLEND)
|
|
Gdx.gl.glBlendFunc(GL20.GL_DST_COLOR, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
|
|
|
|
|
drawTiles(TERRAIN, true) // blendmul tiles
|
|
renderUsingBuffer(TERRAIN, projectionMatrix)
|
|
|
|
|
|
|
|
|
|
// blend normal
|
|
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
|
|
Gdx.gl.glEnable(GL20.GL_BLEND)
|
|
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA)
|
|
|
|
if (drawWires) {
|
|
drawTiles(WIRE, false)
|
|
renderUsingBuffer(WIRE, projectionMatrix)
|
|
}
|
|
}
|
|
|
|
private val tileDrawLightThreshold = 2f / LightmapRenderer.MUL
|
|
|
|
private fun canIHazRender(mode: Int, x: Int, y: Int) =
|
|
//(world.getTileFrom(mode, x, y) != 0) // not an air tile
|
|
//&&
|
|
|
|
// for WALLs; else: ret true
|
|
if (mode == WALL) { // DRAW WHEN it is visible and 'is a lip'
|
|
( BlockCodex[world.getTileFromTerrain(x, y) ?: 0].isClear ||
|
|
!
|
|
((!BlockCodex[world.getTileFromTerrain(x, y - 1) ?: 0].isClear && !BlockCodex[world.getTileFromTerrain(x, y + 1) ?: 0].isClear)
|
|
&&
|
|
(!BlockCodex[world.getTileFromTerrain(x - 1, y) ?: 0].isClear && !BlockCodex[world.getTileFromTerrain(x + 1, y + 1) ?: 0].isClear)
|
|
)
|
|
)
|
|
}
|
|
else
|
|
true
|
|
|
|
// end
|
|
|
|
private fun hasLightNearby(x: Int, y: Int) = ( // check if light level of nearby or this tile is illuminated
|
|
LightmapRenderer.getHighestRGB(x, y) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x - 1, y) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x + 1, y) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x, y - 1) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x, y + 1) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x - 1, y - 1) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x + 1, y + 1) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x + 1, y - 1) ?: 0f >= tileDrawLightThreshold ||
|
|
LightmapRenderer.getHighestRGB(x - 1, y + 1) ?: 0f >= tileDrawLightThreshold
|
|
)
|
|
|
|
/**
|
|
* Writes to buffer. Actual draw code must be called after this operation.
|
|
*/
|
|
private fun drawTiles(mode: Int, drawModeTilesBlendMul: Boolean) {
|
|
val for_y_start = WorldCamera.y / TILE_SIZE
|
|
val for_y_end = for_y_start + tilesBuffer.height - 1//clampHTile(for_y_start + (WorldCamera.height / TILE_SIZE) + 2)
|
|
|
|
val for_x_start = WorldCamera.x / TILE_SIZE
|
|
val for_x_end = for_x_start + tilesBuffer.width - 1//for_x_start + (WorldCamera.width / TILE_SIZE) + 3
|
|
|
|
// loop
|
|
for (y in for_y_start..for_y_end) {
|
|
for (x in for_x_start..for_x_end) {
|
|
|
|
val thisTile: Int?
|
|
if (mode % 3 == WALL)
|
|
thisTile = world.getTileFromWall(x, y)
|
|
else if (mode % 3 == TERRAIN)
|
|
thisTile = world.getTileFromTerrain(x, y)
|
|
else if (mode % 3 == WIRE)
|
|
thisTile = world.getTileFromWire(x, y)
|
|
else
|
|
throw IllegalArgumentException()
|
|
|
|
val noDamageLayer = mode % 3 == WIRE
|
|
|
|
// draw a tile, but only when illuminated
|
|
try {
|
|
//if (canIHazRender(mode, x, y)) {
|
|
|
|
//if (!hasLightNearby(x, y)) {
|
|
// // draw black patch
|
|
// if (thisTile == 0)
|
|
// writeToBuffer(mode, x - for_x_start, y - for_y_start, 0, 0)
|
|
// else
|
|
// writeToBuffer(mode, x - for_x_start, y - for_y_start, 2, 0)
|
|
//}
|
|
//else {
|
|
|
|
val nearbyTilesInfo: Int
|
|
if (isPlatform(thisTile)) {
|
|
nearbyTilesInfo = getNearbyTilesInfoPlatform(x, y)
|
|
}
|
|
else if (isWallSticker(thisTile)) {
|
|
nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y)
|
|
}
|
|
else if (isConnectMutual(thisTile)) {
|
|
nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode)
|
|
}
|
|
else if (isConnectSelf(thisTile)) {
|
|
nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile)
|
|
}
|
|
else {
|
|
nearbyTilesInfo = 0
|
|
}
|
|
|
|
|
|
val thisTileX = if (!noDamageLayer)
|
|
PairedMapLayer.RANGE * ((thisTile ?: 0) % PairedMapLayer.RANGE) + nearbyTilesInfo
|
|
else
|
|
nearbyTilesInfo
|
|
|
|
val thisTileY = (thisTile ?: 0) / PairedMapLayer.RANGE
|
|
|
|
val breakage = if (mode == TERRAIN) world.getTerrainDamage(x, y) else world.getWallDamage(x, y)
|
|
val maxHealth = BlockCodex[world.getTileFromTerrain(x, y)].strength
|
|
val breakingStage = (breakage / maxHealth).times(breakAnimSteps).roundInt()
|
|
|
|
|
|
|
|
// draw a tile
|
|
if (drawModeTilesBlendMul) {
|
|
// while iterating through, only the some tiles are actually eligible to be drawn as MUL,
|
|
// so obviously when we caught not eligible tile, we need to skip that by marking as Tile No. zero
|
|
|
|
if (isBlendMul(thisTile)) {
|
|
writeToBuffer(mode, x - for_x_start, y - for_y_start, thisTileX, thisTileY, breakingStage)
|
|
}
|
|
else {
|
|
writeToBuffer(mode, x - for_x_start, y - for_y_start, 0, 0, 0)
|
|
}
|
|
}
|
|
else {
|
|
// do NOT add "if (!isBlendMul(thisTile))"!
|
|
// or else they will not look like they should be when backed with wall
|
|
writeToBuffer(mode, x - for_x_start, y - for_y_start, thisTileX, thisTileY, breakingStage)
|
|
}
|
|
|
|
// draw a breakage
|
|
/*if (mode == TERRAIN || mode == WALL) {
|
|
val breakage = if (mode == TERRAIN) world.getTerrainDamage(x, y) else world.getWallDamage(x, y)
|
|
val maxHealth = BlockCodex[world.getTileFromTerrain(x, y)].strength
|
|
val stage = (breakage / maxHealth).times(breakAnimSteps).roundInt()
|
|
// actual drawing
|
|
if (stage > 0) {
|
|
writeToBuffer(mode, x - for_x_start, y - for_y_start, 5 + stage, 0)
|
|
}
|
|
}*/
|
|
|
|
|
|
//} // end if (is illuminated)
|
|
//} // end if (not an air)
|
|
} catch (e: NullPointerException) {
|
|
// do nothing. WARNING: This exception handling may hide erratic behaviour completely.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
* @param x
|
|
* *
|
|
* @param y
|
|
* *
|
|
* @return binary [0-15] 1: up, 2: right, 4: down, 8: left
|
|
*/
|
|
internal fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int?): Int {
|
|
val nearbyTiles = IntArray(4)
|
|
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906
|
|
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: Block.NULL
|
|
|
|
// try for
|
|
var ret = 0
|
|
for (i in 0..3) {
|
|
if (nearbyTiles[i] == mark) {
|
|
ret += 1 shl i // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
internal fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
|
|
val nearbyTiles = IntArray(4)
|
|
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(mode, x - 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(mode, x + 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_UP] = world.getTileFrom(mode, x , y - 1) ?: 4906
|
|
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(mode, x , y + 1) ?: Block.NULL
|
|
|
|
// try for
|
|
var ret = 0
|
|
for (i in 0..3) {
|
|
try {
|
|
if (!BlockCodex[nearbyTiles[i]].isSolid &&
|
|
!BlockCodex[nearbyTiles[i]].isFluid) {
|
|
ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
|
}
|
|
} catch (e: ArrayIndexOutOfBoundsException) {
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
internal fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
|
|
val nearbyTiles = IntArray(4)
|
|
val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP
|
|
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_DOWN] = world.getTileFrom(TERRAIN, x , y + 1) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_BACK] = world.getTileFrom(WALL, x , y) ?: Block.NULL
|
|
|
|
try {
|
|
if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid)
|
|
// has tile on the bottom
|
|
return 3
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid
|
|
&& BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid)
|
|
// has tile on both sides
|
|
return 0
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid)
|
|
// has tile on the right
|
|
return 2
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid)
|
|
// has tile on the left
|
|
return 1
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_BACK]].isSolid)
|
|
// has tile on the back
|
|
return 0
|
|
else
|
|
return 3
|
|
} catch (e: ArrayIndexOutOfBoundsException) {
|
|
return if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_DOWN]].isSolid)
|
|
// has tile on the bottom
|
|
3 else 0
|
|
}
|
|
}
|
|
|
|
internal fun getNearbyTilesInfoPlatform(x: Int, y: Int): Int {
|
|
val nearbyTiles = IntArray(4)
|
|
nearbyTiles[NEARBY_TILE_KEY_LEFT] = world.getTileFrom(TERRAIN, x - 1, y) ?: Block.NULL
|
|
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = world.getTileFrom(TERRAIN, x + 1, y) ?: Block.NULL
|
|
|
|
if ((BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid &&
|
|
BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid) ||
|
|
isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT]) &&
|
|
isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // LR solid || LR platform
|
|
return 0
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT]) &&
|
|
!BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // L solid and not platform && R not solid and not platform
|
|
return 4
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT]) &&
|
|
!BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) // R solid and not platform && L not solid and nto platform
|
|
return 6
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) // L solid && L not platform
|
|
return 3
|
|
else if (BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // R solid && R not platform
|
|
return 5
|
|
else if ((BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid ||
|
|
isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) &&
|
|
!BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) // L solid or platform && R not solid and not platform
|
|
return 1
|
|
else if ((BlockCodex[nearbyTiles[NEARBY_TILE_KEY_RIGHT]].isSolid ||
|
|
isPlatform(nearbyTiles[NEARBY_TILE_KEY_RIGHT])) &&
|
|
!BlockCodex[nearbyTiles[NEARBY_TILE_KEY_LEFT]].isSolid &&
|
|
!isPlatform(nearbyTiles[NEARBY_TILE_KEY_LEFT])) // R solid or platform && L not solid and not platform
|
|
return 2
|
|
else
|
|
return 7
|
|
}
|
|
|
|
/**
|
|
* Raw format of RGBA8888, where RGB portion actually encodes the absolute tile number and A is always 255.
|
|
*
|
|
* @return Raw colour bits in RGBA8888 format
|
|
*/
|
|
private fun sheetXYToTilemapColour(mode: Int, sheetX: Int, sheetY: Int, breakage: Int): Int = when (mode) {
|
|
// the tail ".or(255)" is there to write 1.0 to the A channel (remember, return type is RGBA)
|
|
|
|
TERRAIN, WALL ->
|
|
(tilesTerrain.horizontalCount * sheetY + sheetX).shl(8).or(255) or // the actual tile bits
|
|
breakage.and(15).shl(28) // breakage bits
|
|
WIRE -> (tilesWire.horizontalCount * sheetY + sheetX).shl(8).or(255)
|
|
else -> throw IllegalArgumentException()
|
|
}
|
|
|
|
private fun writeToBuffer(mode: Int, bufferPosX: Int, bufferPosY: Int, sheetX: Int, sheetY: Int, breakage: Int) {
|
|
val sourceBuffer = when(mode) {
|
|
TERRAIN -> terrainTilesBuffer
|
|
WALL -> wallTilesBuffer
|
|
WIRE -> wireTilesBuffer
|
|
else -> throw IllegalArgumentException()
|
|
}
|
|
|
|
|
|
sourceBuffer[bufferPosY][bufferPosX] = sheetXYToTilemapColour(mode, sheetX, sheetY, breakage)
|
|
}
|
|
|
|
private fun renderUsingBuffer(mode: Int, projectionMatrix: Matrix4) {
|
|
//Gdx.gl.glClearColor(.094f, .094f, .094f, 0f)
|
|
//Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
|
|
|
|
|
|
//val tilesInHorizontal = tilesBuffer.width
|
|
//val tilesInVertical = tilesBuffer.height
|
|
|
|
|
|
val tileAtlas = when (mode) {
|
|
TERRAIN, WALL -> tilesTerrain
|
|
WIRE -> tilesWire
|
|
else -> throw IllegalArgumentException()
|
|
}
|
|
val sourceBuffer = when(mode) {
|
|
TERRAIN -> terrainTilesBuffer
|
|
WALL -> wallTilesBuffer
|
|
WIRE -> wireTilesBuffer
|
|
else -> throw IllegalArgumentException()
|
|
}
|
|
val vertexColour = when (mode) {
|
|
TERRAIN, WIRE -> Color.WHITE
|
|
WALL -> wallOverlayColour
|
|
else -> throw IllegalArgumentException()
|
|
}
|
|
|
|
|
|
// write to colour buffer
|
|
for (y in 0 until tilesBuffer.height) {
|
|
for (x in 0 until tilesBuffer.width) {
|
|
val color = sourceBuffer[y][x]
|
|
tilesBuffer.setColor(color)
|
|
tilesBuffer.drawPixel(x, y)
|
|
}
|
|
}
|
|
|
|
|
|
val tilesBufferAsTex = Texture(tilesBuffer)
|
|
tilesBufferAsTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest)
|
|
tilesBufferAsTex.bind(1) // trying 1 and 0...
|
|
tileAtlas.texture.bind(0) // for some fuck reason, it must be bound as last
|
|
|
|
shader.begin()
|
|
shader.setUniformMatrix("u_projTrans", projectionMatrix)//camera.combined)
|
|
shader.setUniformf("colourFilter", vertexColour)
|
|
shader.setUniformf("screenDimension", Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat())
|
|
shader.setUniformi("tilesAtlas", 0)
|
|
shader.setUniformi("tilemap", 1)
|
|
shader.setUniformi("tilemapDimension", tilesBuffer.width, tilesBuffer.height)
|
|
shader.setUniformf("tilesInAxes", tilesInHorizontal.toFloat(), tilesInVertical.toFloat())
|
|
shader.setUniformi("cameraTranslation", WorldCamera.x fmod TILE_SIZE, WorldCamera.y fmod TILE_SIZE)
|
|
/*shader hard-code*/shader.setUniformi("tilesInAtlas", tileAtlas.horizontalCount, tileAtlas.verticalCount) //depends on the tile atlas
|
|
/*shader hard-code*/shader.setUniformi("atlasTexSize", tileAtlas.texture.width, tileAtlas.texture.height) //depends on the tile atlas
|
|
tilesQuad.render(shader, GL20.GL_TRIANGLES)
|
|
shader.end()
|
|
|
|
//tilesBufferAsTex.dispose()
|
|
}
|
|
|
|
private var oldScreenW = 0
|
|
private var oldScreenH = 0
|
|
|
|
var tilesInHorizontal = -1; private set
|
|
var tilesInVertical = -1; private set
|
|
|
|
fun resize(screenW: Int, screenH: Int) {
|
|
tilesInHorizontal = (screenW.toFloat() / TILE_SIZE).ceilInt() + 1
|
|
tilesInVertical = (screenH.toFloat() / TILE_SIZE).ceilInt() + 1
|
|
|
|
val oldTH = (oldScreenW.toFloat() / TILE_SIZE).ceilInt() + 1
|
|
val oldTV = (oldScreenH.toFloat() / TILE_SIZE).ceilInt() + 1
|
|
|
|
// only update if it's really necessary
|
|
if (oldTH != tilesInHorizontal || oldTV != tilesInVertical) {
|
|
terrainTilesBuffer = Array<IntArray>(tilesInVertical, { kotlin.IntArray(tilesInHorizontal) })
|
|
wallTilesBuffer = Array<IntArray>(tilesInVertical, { kotlin.IntArray(tilesInHorizontal) })
|
|
wireTilesBuffer = Array<IntArray>(tilesInVertical, { kotlin.IntArray(tilesInHorizontal) })
|
|
|
|
tilesBuffer = Pixmap(tilesInHorizontal, tilesInVertical, Pixmap.Format.RGB888)
|
|
}
|
|
|
|
if (oldScreenW != screenW || oldScreenH != screenH) {
|
|
tilesQuad = Mesh(
|
|
true, 4, 6,
|
|
VertexAttribute.Position(),
|
|
VertexAttribute.ColorUnpacked(),
|
|
VertexAttribute.TexCoords(0)
|
|
)
|
|
|
|
tilesQuad.setVertices(floatArrayOf( // WARNING! not ususal quads; TexCoords of Y is flipped
|
|
0f, 0f, 0f, 1f, 1f, 1f, 1f, 0f, 0f,
|
|
screenW.toFloat(), 0f, 0f, 1f, 1f, 1f, 1f, 1f, 0f,
|
|
screenW.toFloat(), screenH.toFloat(), 0f, 1f, 1f, 1f, 1f, 1f, 1f,
|
|
0f, screenH.toFloat(), 0f, 1f, 1f, 1f, 1f, 0f, 1f
|
|
))
|
|
tilesQuad.setIndices(shortArrayOf(0, 1, 2, 2, 3, 0))
|
|
}
|
|
|
|
oldScreenW = screenW
|
|
oldScreenH = screenH
|
|
|
|
|
|
printdbg(this, "Resize event")
|
|
|
|
}
|
|
|
|
fun clampH(x: Int): Int {
|
|
if (x < 0) {
|
|
return 0
|
|
} else if (x > world.height * TILE_SIZE) {
|
|
return world.height * TILE_SIZE
|
|
} else {
|
|
return x
|
|
}
|
|
}
|
|
|
|
fun clampWTile(x: Int): Int {
|
|
if (x < 0) {
|
|
return 0
|
|
} else if (x > world.width) {
|
|
return world.width
|
|
} else {
|
|
return x
|
|
}
|
|
}
|
|
|
|
fun clampHTile(x: Int): Int {
|
|
if (x < 0) {
|
|
return 0
|
|
} else if (x > world.height) {
|
|
return world.height
|
|
} else {
|
|
return x
|
|
}
|
|
}
|
|
|
|
fun getRenderStartX(): Int = WorldCamera.x / TILE_SIZE
|
|
fun getRenderStartY(): Int = WorldCamera.y / TILE_SIZE
|
|
|
|
fun getRenderEndX(): Int = clampWTile(getRenderStartX() + (WorldCamera.width / TILE_SIZE) + 2)
|
|
fun getRenderEndY(): Int = clampHTile(getRenderStartY() + (WorldCamera.height / TILE_SIZE) + 2)
|
|
|
|
fun isConnectSelf(b: Int?): Boolean = TILES_CONNECT_SELF.contains(b)
|
|
fun isConnectMutual(b: Int?): Boolean = TILES_CONNECT_MUTUAL.contains(b)
|
|
fun isWallSticker(b: Int?): Boolean = TILES_WALL_STICKER.contains(b)
|
|
fun isPlatform(b: Int?): Boolean = TILES_WALL_STICKER_CONNECT_SELF.contains(b)
|
|
fun isBlendMul(b: Int?): Boolean = TILES_BLEND_MUL.contains(b)
|
|
|
|
fun tileInCamera(x: Int, y: Int) =
|
|
x >= WorldCamera.x.div(TILE_SIZE) && y >= WorldCamera.y.div(TILE_SIZE) &&
|
|
x <= WorldCamera.x.plus(WorldCamera.width).div(TILE_SIZE) && y <= WorldCamera.y.plus(WorldCamera.width).div(TILE_SIZE)
|
|
}
|