mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-15 08:06:06 +09:00
966 lines
32 KiB
Kotlin
966 lines
32 KiB
Kotlin
package net.torvald.terrarum.modulebasegame
|
|
|
|
import com.badlogic.gdx.Gdx
|
|
import com.badlogic.gdx.InputAdapter
|
|
import com.badlogic.gdx.graphics.Color
|
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
|
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
|
import net.torvald.terrarum.*
|
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
|
import net.torvald.terrarum.blockproperties.Block
|
|
import net.torvald.terrarum.blockproperties.BlockPropUtil
|
|
import net.torvald.terrarum.gameactors.*
|
|
import net.torvald.terrarum.gamecontroller.TerrarumKeyboardEvent
|
|
import net.torvald.terrarum.gameitems.ItemID
|
|
import net.torvald.terrarum.gameparticles.ParticleBase
|
|
import net.torvald.terrarum.gameworld.BlockLayerI16
|
|
import net.torvald.terrarum.gameworld.GameWorld
|
|
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
|
|
import net.torvald.terrarum.gameworld.WorldTime
|
|
import net.torvald.terrarum.modulebasegame.ui.UIBuildingMakerBlockChooser
|
|
import net.torvald.terrarum.modulebasegame.ui.UIBuildingMakerPenMenu
|
|
import net.torvald.terrarum.modulebasegame.ui.UIPaletteSelector
|
|
import net.torvald.terrarum.modulebasegame.ui.UIScreenZoom
|
|
import net.torvald.terrarum.serialise.Common
|
|
import net.torvald.terrarum.serialise.PointOfInterest
|
|
import net.torvald.terrarum.serialise.POILayer
|
|
import net.torvald.terrarum.ui.Toolkit
|
|
import net.torvald.terrarum.weather.WeatherMixer
|
|
import net.torvald.terrarum.ui.UINSMenu
|
|
import net.torvald.terrarum.utils.OrePlacement
|
|
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
|
import net.torvald.terrarum.worlddrawer.LightmapRenderer
|
|
import net.torvald.terrarum.worlddrawer.WorldCamera
|
|
import net.torvald.util.CircularArray
|
|
import java.io.File
|
|
|
|
/**
|
|
* Created by minjaesong on 2018-07-06.
|
|
*/
|
|
class BuildingMaker(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|
|
|
private val menuYaml = Yaml("""
|
|
- File
|
|
- New Flat ter. : net.torvald.terrarum.modulebasegame.YamlCommandNewFlatTerrain
|
|
- New Rand. ter.
|
|
- Export… : net.torvald.terrarum.modulebasegame.YamlCommandToolExportTest
|
|
- Import… : net.torvald.terrarum.modulebasegame.YamlCommandToolImportTest
|
|
- Exit to Title : net.torvald.terrarum.modulebasegame.YamlCommandExit
|
|
- Edit
|
|
- Clear Selections : net.torvald.terrarum.modulebasegame.YamlCommandClearSelection
|
|
- Time
|
|
- Dawn : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeDawn
|
|
- Sunrise : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeSunrise
|
|
- Morning : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeMorning
|
|
- Noon : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeNoon
|
|
- Dusk : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeDusk
|
|
- Sunset : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeSunset
|
|
- Night : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeNight
|
|
- Midnight : net.torvald.terrarum.modulebasegame.YamlCommandSetTimeMidnight
|
|
- Set…
|
|
""".trimIndent())
|
|
|
|
lateinit var gameWorld: GameWorld
|
|
|
|
override val musicGovernor = TerrarumMusicGovernor()
|
|
|
|
init {
|
|
gameUpdateGovernor = ConsistentUpdateRate
|
|
YamlCommandNewFlatTerrain().invoke(arrayOf(this))
|
|
}
|
|
|
|
internal fun reset() {
|
|
gameUpdateGovernor.reset()
|
|
WeatherMixer.forceSolarElev = 5.0
|
|
}
|
|
|
|
|
|
override var actorNowPlaying: ActorHumanoid? = MovableWorldCamera(this)
|
|
|
|
val uiToolbox = UINSMenu("Menu", 100, menuYaml)
|
|
val uiPaletteSelector = UIPaletteSelector(this)
|
|
val uiPalette = UIBuildingMakerBlockChooser(this)
|
|
val uiPenMenu = UIBuildingMakerPenMenu(this)
|
|
|
|
val uiGetPoiName = UIBuildingMakerGetFilename() // used for both import and export
|
|
|
|
val uiContainer = UIContainer()
|
|
|
|
val keyboardUsedByTextInput: Boolean
|
|
get() = uiGetPoiName.textInput.isEnabled
|
|
|
|
private val pensMustShowSelection = arrayOf(
|
|
PENMODE_MARQUEE, PENMODE_MARQUEE_ERASE
|
|
)
|
|
|
|
var currentPenMode = PENMODE_PENCIL
|
|
set(value) {
|
|
field = value
|
|
if (value in pensMustShowSelection) {
|
|
showSelection = true
|
|
}
|
|
}
|
|
var currentPenTarget = PENTARGET_TERRAIN
|
|
|
|
val selection = ArrayList<Long>()
|
|
|
|
val blockMarkings = CommonResourcePool.getAsTextureRegionPack("blockmarkings_common")
|
|
internal var showSelection = true
|
|
val blockPointingCursor = object : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = PhysProperties.MOBILE_OBJECT) {
|
|
|
|
override var referenceID: ActorID = 1048575 // custom refID
|
|
override val hitbox = Hitbox(0.0, 0.0, 16.0, 16.0)
|
|
|
|
|
|
init {
|
|
this.actorValue[AVKey.LUMR] = 1.0
|
|
this.actorValue[AVKey.LUMG] = 1.0
|
|
}
|
|
|
|
override fun drawBody(batch: SpriteBatch) {
|
|
batch.color = toolCursorColour[currentPenMode]
|
|
batch.draw(blockMarkings.get(currentPenMode, 0), hitbox.startX.toFloat(), hitbox.startY.toFloat())
|
|
}
|
|
|
|
override fun drawGlow(batch: SpriteBatch) { }
|
|
|
|
override fun dispose() {
|
|
}
|
|
|
|
override fun update(delta: Float) {
|
|
hitbox.setPosition(
|
|
Terrarum.mouseTileX * 16.0,
|
|
Terrarum.mouseTileY * 16.0
|
|
)
|
|
}
|
|
|
|
override fun onActorValueChange(key: String, value: Any?) { }
|
|
|
|
override fun run() {
|
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
|
}
|
|
}
|
|
|
|
private val blockMarkerColour = Color(0xe0e0e0ff.toInt())
|
|
|
|
internal fun blockPosToRefID(x: Int, y: Int) = 1048576 + (y * gameWorld.width + x)
|
|
|
|
private var _testMarkerDrawCalls = 0L
|
|
|
|
private fun generateNewBlockMarkerVisible(x: Int, y: Int) = object : ActorWithBody(Actor.RenderOrder.OVERLAY, physProp = PhysProperties.MOBILE_OBJECT) {
|
|
override var referenceID: ActorID = blockPosToRefID(x, y) // custom refID
|
|
override val hitbox = Hitbox(x * 16.0, y * 16.0, 16.0, 16.0)
|
|
|
|
override fun drawBody(batch: SpriteBatch) {
|
|
batch.color = blockMarkerColour
|
|
drawSpriteInGoodPosition(blockMarkings.get(2,0), batch)
|
|
}
|
|
|
|
private fun drawSpriteInGoodPosition(sprite: TextureRegion, batch: SpriteBatch) {
|
|
val leftsidePadding = world!!.width.times(TILE_SIZE) - WorldCamera.width.ushr(1)
|
|
val rightsidePadding = WorldCamera.width.ushr(1)
|
|
|
|
if (hitbox.startX in WorldCamera.x - hitbox.width..WorldCamera.x + WorldCamera.width.toDouble() &&
|
|
hitbox.startY in WorldCamera.y - hitbox.height..WorldCamera.y + WorldCamera.height.toDouble()) {
|
|
if (WorldCamera.xCentre > leftsidePadding && hitbox.centeredX <= rightsidePadding) {
|
|
// camera center neg, actor center pos
|
|
batch.draw(sprite,
|
|
hitbox.startX.toFloat() + world!!.width * TILE_SIZE,
|
|
hitbox.startY.toFloat()
|
|
)
|
|
}
|
|
else if (WorldCamera.xCentre < rightsidePadding && hitbox.centeredY >= leftsidePadding) {
|
|
// camera center pos, actor center neg
|
|
batch.draw(sprite,
|
|
hitbox.startX.toFloat() - world!!.width * TILE_SIZE,
|
|
hitbox.startY.toFloat()
|
|
)
|
|
}
|
|
else {
|
|
batch.draw(sprite,
|
|
hitbox.startX.toFloat(),
|
|
hitbox.startY.toFloat()
|
|
)
|
|
}
|
|
|
|
_testMarkerDrawCalls += 1
|
|
}
|
|
}
|
|
|
|
override fun drawGlow(batch: SpriteBatch) { }
|
|
|
|
override fun update(delta: Float) { }
|
|
|
|
override fun onActorValueChange(key: String, value: Any?) { }
|
|
|
|
override fun run() {
|
|
TODO("not implemented")
|
|
}
|
|
|
|
override fun dispose() {
|
|
|
|
}
|
|
}
|
|
|
|
internal fun addBlockMarker(x: Int, y: Int) {
|
|
try {
|
|
val a = generateNewBlockMarkerVisible(x, y)
|
|
if (!theGameHasActor(a.referenceID)) {
|
|
selection.add(toAddr(x, y))
|
|
actorsRenderOverlay.add(a)
|
|
queueActorAddition(a)
|
|
}
|
|
}
|
|
catch (e: Error) { }
|
|
}
|
|
|
|
internal fun removeBlockMarker(x: Int, y: Int) {
|
|
try {
|
|
selection.remove(toAddr(x, y))
|
|
val a = getActorByID(blockPosToRefID(x, y))
|
|
actorsRenderOverlay.remove(a)
|
|
queueActorRemoval(a)
|
|
}
|
|
catch (e: Throwable) {
|
|
// no actor to erase, do nothing
|
|
// e.printStackTrace()
|
|
}
|
|
}
|
|
|
|
internal var imported: PointOfInterest? = null
|
|
|
|
companion object {
|
|
const val PENMODE_PENCIL = 0
|
|
const val PENMODE_PENCIL_ERASE = 1
|
|
const val PENMODE_MARQUEE = 2
|
|
const val PENMODE_MARQUEE_ERASE = 3
|
|
const val PENMODE_EYEDROPPER = 4
|
|
const val PENMODE_IMPORT = 8
|
|
|
|
const val PENTARGET_TERRAIN = 1
|
|
const val PENTARGET_WALL = 2
|
|
const val PENTARGET_ORE = 4
|
|
|
|
val toolCursorColour = arrayOf(
|
|
Color.YELLOW,
|
|
Color.YELLOW,
|
|
Color.MAGENTA,
|
|
Color.MAGENTA,
|
|
Color.WHITE,
|
|
Color.WHITE,
|
|
Color.WHITE,
|
|
Color.WHITE,
|
|
Color.MAGENTA,
|
|
)
|
|
|
|
const val DEFAULT_POI_NAME = "The Yucky Panopticon"
|
|
}
|
|
|
|
private val actorsRenderOverlay = ArrayList<ActorWithBody>() // can be hidden (e.g. hide sel.)
|
|
private val essentialOverlays = ArrayList<ActorWithBody>()
|
|
|
|
init {
|
|
essentialOverlays.add(blockPointingCursor)
|
|
|
|
uiContainer.add(
|
|
uiToolbox,
|
|
uiPaletteSelector,
|
|
notifier,
|
|
uiPalette,
|
|
uiPenMenu,
|
|
uiGetPoiName,
|
|
UIScreenZoom(),
|
|
)
|
|
|
|
|
|
|
|
uiToolbox.setPosition(0, 0)
|
|
uiToolbox.isVisible = true
|
|
uiToolbox.invocationArgument = arrayOf(this)
|
|
|
|
uiPaletteSelector.setPosition(0, App.scr.height - uiPaletteSelector.height)
|
|
uiPaletteSelector.isVisible = true
|
|
|
|
notifier.setPosition(
|
|
(Toolkit.drawWidth - notifier.width) / 2,
|
|
App.scr.height - notifier.height - App.scr.tvSafeGraphicsHeight
|
|
)
|
|
|
|
|
|
actorNowPlaying?.setPosition(512 * 16.0, 149 * 16.0)
|
|
|
|
|
|
uiPalette.setPosition(200, 100)
|
|
|
|
|
|
IngameRenderer.setRenderedWorld(gameWorld)
|
|
WeatherMixer.internalReset(this)
|
|
}
|
|
|
|
override fun show() {
|
|
Gdx.input.inputProcessor = BuildingMakerController(this)
|
|
super.show()
|
|
}
|
|
|
|
|
|
private var disableMouseClick = false
|
|
var mousePrimaryJustDown = false; private set
|
|
|
|
override fun render(updateRate: Float) {
|
|
super.render(updateRate)
|
|
|
|
|
|
|
|
Gdx.graphics.setTitle(TerrarumIngame.getCanonicalTitle())
|
|
|
|
|
|
// ASYNCHRONOUS UPDATE AND RENDER //
|
|
gameUpdateGovernor.update(Gdx.graphics.deltaTime, App.UPDATE_RATE, updateGame, renderGame
|
|
)
|
|
App.setDebugTime("Ingame.Render - (Light + Tiling)",
|
|
((App.debugTimers["Ingame.Render"]) ?: 0) -
|
|
(
|
|
((App.debugTimers["Renderer.Lanterns"]) ?: 0) +
|
|
((App.debugTimers["Renderer.LightPrecalc"]) ?: 0) +
|
|
((App.debugTimers["Renderer.LightRuns"]) ?: 0) +
|
|
((App.debugTimers["Renderer.LightToScreen"]) ?: 0) +
|
|
((App.debugTimers["Renderer.Tiling"]) ?: 0)
|
|
)
|
|
)
|
|
|
|
|
|
|
|
if (!Gdx.input.isButtonPressed(App.getConfigInt("config_mouseprimary"))) {
|
|
disableMouseClick = false
|
|
}
|
|
}
|
|
|
|
private var mouseOnUI = false
|
|
internal var tappedOnUI = false // when true, even if the UI is closed, pen won't work unless your pen is lifted
|
|
// must be set to TRUE by UIs
|
|
|
|
private val updateGame = { delta: Float ->
|
|
|
|
blockPointingCursor.update(delta)
|
|
|
|
if (!keyboardUsedByTextInput) {
|
|
actorNowPlaying?.update(delta)
|
|
}
|
|
|
|
var overwriteMouseOnUI = false
|
|
uiContainer.forEach {
|
|
it?.update(delta)
|
|
if (it?.isVisible == true && it?.mouseUp == true) {
|
|
overwriteMouseOnUI = true
|
|
}
|
|
}
|
|
|
|
mouseOnUI = (overwriteMouseOnUI || uiPenMenu.isVisible /*|| uiPalette.isVisible*/ || uiGetPoiName.isVisible)
|
|
|
|
|
|
WorldCamera.update(world, actorNowPlaying)
|
|
|
|
|
|
// make pen work HERE
|
|
// when LEFT mouse is down
|
|
if (!tappedOnUI && Terrarum.mouseDown && !mouseOnUI) {
|
|
|
|
makePenWork(Terrarum.mouseTileX, Terrarum.mouseTileY)
|
|
// TODO drag support using bresenham's algo
|
|
// for some reason it just doesn't work...
|
|
}
|
|
else if (!uiPenMenu.isVisible && Gdx.input.isButtonPressed(App.getConfigInt("config_mousesecondary"))) {
|
|
// open pen menu
|
|
// position the menu to where the cursor is
|
|
uiPenMenu.posX = Terrarum.mouseScreenX - uiPenMenu.width / 2
|
|
uiPenMenu.posY = Terrarum.mouseScreenY - uiPenMenu.height / 2
|
|
uiPenMenu.posX = uiPenMenu.posX.coerceIn(0, App.scr.width - uiPenMenu.width)
|
|
uiPenMenu.posY = uiPenMenu.posY.coerceIn(0, App.scr.height - uiPenMenu.height)
|
|
|
|
// actually open
|
|
uiPenMenu.setAsOpen()
|
|
}
|
|
|
|
|
|
actorAdditionQueue.forEach { forceAddActor(it.first, it.second) }
|
|
actorAdditionQueue.clear()
|
|
actorRemovalQueue.forEach { forceRemoveActor(it.first, it.second) }
|
|
actorRemovalQueue.clear()
|
|
|
|
|
|
BlockPropUtil.dynamicLumFuncTickClock()
|
|
WeatherMixer.update(delta, actorNowPlaying, gameWorld)
|
|
if (WORLD_UPDATE_TIMER % 2 == 1) {
|
|
fillUpWiresBuffer()
|
|
}
|
|
|
|
|
|
musicGovernor.update(this, delta)
|
|
}
|
|
|
|
|
|
private val maxRenderableWires = ReferencingRanges.ACTORS_WIRES.last - ReferencingRanges.ACTORS_WIRES.first + 1
|
|
private val wireActorsContainer = Array(maxRenderableWires) { WireActor(ReferencingRanges.ACTORS_WIRES.first + it).let {
|
|
forceAddActor(it)
|
|
/*^let*/ it
|
|
} }
|
|
var selectedWireRenderClass = ""
|
|
private var oldSelectedWireRenderClass = ""
|
|
|
|
|
|
private fun fillUpWiresBuffer() {
|
|
val for_y_start = (WorldCamera.y.toFloat() / TILE_SIZE).floorToInt() - LightmapRenderer.LIGHTMAP_OVERRENDER
|
|
val for_y_end = for_y_start + BlocksDrawer.tilesInVertical + 2* LightmapRenderer.LIGHTMAP_OVERRENDER
|
|
|
|
val for_x_start = (WorldCamera.x.toFloat() / TILE_SIZE).floorToInt() - LightmapRenderer.LIGHTMAP_OVERRENDER
|
|
val for_x_end = for_x_start + BlocksDrawer.tilesInHorizontal + 2* LightmapRenderer.LIGHTMAP_OVERRENDER
|
|
|
|
var wiringCounter = 0
|
|
for (y in for_y_start..for_y_end) {
|
|
for (x in for_x_start..for_x_end) {
|
|
if (wiringCounter >= maxRenderableWires) break
|
|
|
|
val (wires, nodes) = world.getAllWiresFrom(x, y)
|
|
|
|
wires?.forEach {
|
|
val wireActor = wireActorsContainer[wiringCounter]
|
|
|
|
wireActor.setWire(it, x, y, nodes!![it]!!.cnx)
|
|
|
|
if (WireCodex[it].renderClass == selectedWireRenderClass || selectedWireRenderClass == "wire_render_all") {
|
|
wireActor.renderOrder = Actor.RenderOrder.OVERLAY
|
|
}
|
|
else {
|
|
wireActor.renderOrder = Actor.RenderOrder.BEHIND
|
|
}
|
|
|
|
wireActor.isUpdate = true
|
|
wireActor.isVisible = true
|
|
wireActor.forceDormant = false
|
|
|
|
wiringCounter += 1
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
for (i in wiringCounter until maxRenderableWires) {
|
|
wireActorsContainer[i].isUpdate = false
|
|
wireActorsContainer[i].isVisible = false
|
|
wireActorsContainer[i].forceDormant = true
|
|
}
|
|
}
|
|
|
|
private val particles = CircularArray<ParticleBase>(16, true)
|
|
|
|
private val renderGame = { delta: Float ->
|
|
_testMarkerDrawCalls = 0L
|
|
|
|
IngameRenderer.invoke(false,
|
|
screenZoom,
|
|
listOf(),
|
|
listOf(),
|
|
listOf(),
|
|
listOf(),
|
|
if (showSelection) actorsRenderOverlay + essentialOverlays else essentialOverlays,
|
|
particles,
|
|
uiContainer = uiContainer
|
|
)
|
|
|
|
App.setDebugTime("Test.MarkerDrawCalls", _testMarkerDrawCalls)
|
|
}
|
|
|
|
override fun resize(width: Int, height: Int) {
|
|
IngameRenderer.resize(App.scr.width, App.scr.height)
|
|
val drawWidth = Toolkit.drawWidth
|
|
|
|
uiToolbox.setPosition(0, 0)
|
|
notifier.setPosition(
|
|
(Toolkit.drawWidth - notifier.width) / 2,
|
|
App.scr.height - notifier.height - App.scr.tvSafeGraphicsHeight
|
|
)
|
|
|
|
println("[BuildingMaker] Resize event")
|
|
}
|
|
|
|
fun setPencilColour(itemID: ItemID) {
|
|
uiPaletteSelector.fore = itemID
|
|
currentPenMode = PENMODE_PENCIL
|
|
currentPenTarget = PENTARGET_TERRAIN // TERRAIN is arbitrary chosen to prevent possible conflict; for the pencil itself this property does nothing
|
|
}
|
|
|
|
override fun dispose() {
|
|
// blockMarkings.dispose()
|
|
uiPenMenu.dispose()
|
|
uiGetPoiName.dispose()
|
|
musicGovernor.dispose()
|
|
}
|
|
|
|
fun getPoiNameForExport(w: Int, h: Int, callback: (String) -> Unit) {
|
|
uiGetPoiName.let {
|
|
it.title = "Export"
|
|
it.labelDo = "Export"
|
|
it.text = listOf("WH: $w\u00D7$h", "Name of the POI:")
|
|
it.confirmCallback = { s ->
|
|
callback(s)
|
|
disableMouseClick = true
|
|
}
|
|
it.cancelCallback = {
|
|
disableMouseClick = true
|
|
}
|
|
it.setPosition(
|
|
240,
|
|
32
|
|
)
|
|
it.reset()
|
|
it.setAsOpen()
|
|
}
|
|
}
|
|
|
|
fun getPoiNameForImport(callback: (String) -> Unit) {
|
|
uiGetPoiName.let {
|
|
it.title = "Import"
|
|
it.labelDo = "Import"
|
|
it.text = listOf("Name of the POI:")
|
|
it.confirmCallback = { s ->
|
|
callback(s)
|
|
disableMouseClick = true
|
|
}
|
|
it.cancelCallback = {
|
|
disableMouseClick = true
|
|
}
|
|
it.setPosition(
|
|
240,
|
|
32
|
|
)
|
|
it.reset()
|
|
it.setAsOpen()
|
|
}
|
|
}
|
|
|
|
private fun getNearbyTilesPos8(x: Int, y: Int): Array<Point2i> {
|
|
return arrayOf(
|
|
Point2i(x + 1, y),
|
|
Point2i(x + 1, y + 1),
|
|
Point2i(x, y + 1),
|
|
Point2i(x - 1, y + 1),
|
|
Point2i(x - 1, y),
|
|
Point2i(x - 1, y - 1),
|
|
Point2i(x, y - 1),
|
|
Point2i(x + 1, y - 1)
|
|
)
|
|
}
|
|
private fun getNearbyOres8(x: Int, y: Int): List<OrePlacement> {
|
|
return getNearbyTilesPos8(x, y).map { world.getTileFromOre(it.x, it.y) }
|
|
}
|
|
|
|
private fun makePenWork(x: Int, y: Int) {
|
|
val world = gameWorld
|
|
val palSelection = uiPaletteSelector.fore
|
|
|
|
if (!disableMouseClick) {
|
|
when (currentPenMode) {
|
|
// test paint terrain layer
|
|
PENMODE_PENCIL -> {
|
|
if (palSelection.startsWith("wall@"))
|
|
world.setTileWall(x, y, palSelection.substring(5), true)
|
|
else if (palSelection.startsWith("ore@")) {
|
|
// get autotiling placement
|
|
val autotiled = getNearbyOres8(x, y).foldIndexed(0) { index, acc, placement ->
|
|
acc or (placement.item == palSelection).toInt(index)
|
|
}
|
|
val placement = BlocksDrawer.connectLut16[autotiled]
|
|
|
|
world.setTileOre(x, y, palSelection, placement)
|
|
}
|
|
else
|
|
world.setTileTerrain(x, y, palSelection, true)
|
|
}
|
|
|
|
PENMODE_PENCIL_ERASE -> {
|
|
if (currentPenTarget and PENTARGET_TERRAIN != 0)
|
|
world.setTileTerrain(x, y, Block.AIR, true)
|
|
if (currentPenTarget and PENTARGET_WALL != 0)
|
|
world.setTileWall(x, y, Block.AIR, true)
|
|
if (currentPenTarget and PENTARGET_ORE != 0)
|
|
world.setTileOre(x, y, Block.NULL, 0)
|
|
}
|
|
|
|
PENMODE_EYEDROPPER -> {
|
|
uiPaletteSelector.fore = if (world.getTileFromTerrain(x, y) == Block.AIR)
|
|
"wall@" + world.getTileFromWall(x, y)
|
|
else
|
|
world.getTileFromTerrain(x, y)
|
|
}
|
|
|
|
PENMODE_MARQUEE -> {
|
|
addBlockMarker(x, y)
|
|
}
|
|
|
|
PENMODE_MARQUEE_ERASE -> {
|
|
removeBlockMarker(x, y)
|
|
}
|
|
|
|
PENMODE_IMPORT -> {
|
|
importPoi(x, y)
|
|
disableMouseClick = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun importPoi(x: Int, y: Int) {
|
|
imported?.let {
|
|
it.placeOnWorld(listOf(it.layers.first().name), world, x, y) // TODO show layer list and ticker using right click?
|
|
}
|
|
}
|
|
|
|
override fun inputStrobed(e: TerrarumKeyboardEvent) {
|
|
uiContainer.forEach {
|
|
it?.inputStrobed(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
class BuildingMakerController(val screen: BuildingMaker) : InputAdapter() {
|
|
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
|
screen.uiContainer.forEach { it?.touchUp(screenX, screenY, pointer, button) }
|
|
screen.tappedOnUI = false
|
|
return true
|
|
}
|
|
|
|
override fun keyTyped(character: Char): Boolean {
|
|
screen.uiContainer.forEach { it?.keyTyped(character) }
|
|
return true
|
|
}
|
|
|
|
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
|
screen.uiContainer.forEach { it?.scrolled(amountX, amountY) }
|
|
return true
|
|
}
|
|
|
|
override fun keyUp(keycode: Int): Boolean {
|
|
screen.uiContainer.forEach { it?.keyUp(keycode) }
|
|
return true
|
|
}
|
|
|
|
override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean {
|
|
screen.uiContainer.forEach { it?.touchDragged(screenX, screenY, pointer) }
|
|
return true
|
|
}
|
|
|
|
override fun keyDown(keycode: Int): Boolean {
|
|
screen.uiContainer.forEach { it?.keyDown(keycode) }
|
|
return true
|
|
}
|
|
|
|
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
|
|
screen.uiContainer.forEach { it?.touchDown(screenX, screenY, pointer, button) }
|
|
return true
|
|
}
|
|
}
|
|
|
|
class MovableWorldCamera(val parent: BuildingMaker) : ActorHumanoid(0, physProp = PhysProperties.MOBILE_OBJECT) {
|
|
|
|
init {
|
|
referenceID = Terrarum.PLAYER_REF_ID
|
|
isNoClip = true
|
|
|
|
setHitboxDimension(1, 1, 0, 0)
|
|
|
|
|
|
actorValue[AVKey.SPEED] = 8.0
|
|
actorValue[AVKey.SPEEDBUFF] = 1.0
|
|
actorValue[AVKey.ACCEL] = ActorHumanoid.WALK_ACCEL_BASE
|
|
actorValue[AVKey.ACCELBUFF] = 4.0
|
|
actorValue[AVKey.JUMPPOWER] = 0.0
|
|
actorValue[AVKey.FRICTIONMULT] = 4.0
|
|
}
|
|
|
|
// TODO resize-aware
|
|
private var coerceInStart = Point2d(
|
|
(App.scr.width - hitbox.width) / 2.0,
|
|
(App.scr.height - hitbox.height) / 2.0
|
|
)
|
|
private var coerceInEnd = Point2d(
|
|
parent.world.width * TILE_SIZE - (App.scr.width - hitbox.width) / 2.0,
|
|
parent.world.height * TILE_SIZE - (App.scr.height - hitbox.height) / 2.0
|
|
)
|
|
|
|
override fun update(delta: Float) {
|
|
super.update(delta)
|
|
|
|
// confine the camera so it won't wrap
|
|
this.hitbox.hitboxStart.setCoerceIn(coerceInStart, coerceInEnd)
|
|
}
|
|
|
|
override fun drawBody(batch: SpriteBatch) {
|
|
}
|
|
|
|
override fun drawGlow(batch: SpriteBatch) {
|
|
}
|
|
|
|
override fun onActorValueChange(key: String, value: Any?) {
|
|
}
|
|
|
|
}
|
|
|
|
class YamlCommandExit : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
App.setScreen(TitleScreen(App.batch))
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeDawn : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("6h00"))
|
|
WeatherMixer.forceSolarElev = -3.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeSunrise : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("6h00"))
|
|
WeatherMixer.forceSolarElev = 0.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeMorning : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("6h00"))
|
|
WeatherMixer.forceSolarElev = 5.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeNoon : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("12h00"))
|
|
WeatherMixer.forceSolarElev = 30.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeDusk : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("18h00"))
|
|
WeatherMixer.forceSolarElev = 3.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeSunset : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("18h00"))
|
|
WeatherMixer.forceSolarElev = 0.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeNight : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("18h00"))
|
|
WeatherMixer.forceSolarElev = -8.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandSetTimeMidnight : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).gameWorld.worldTime.setTimeOfToday(WorldTime.parseTime("0h00"))
|
|
WeatherMixer.forceSolarElev = -30.0
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolPencil : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).currentPenMode = BuildingMaker.PENMODE_PENCIL
|
|
(args[0] as BuildingMaker).currentPenTarget = BuildingMaker.PENTARGET_TERRAIN
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolPencilErase : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).currentPenMode = BuildingMaker.PENMODE_PENCIL_ERASE
|
|
(args[0] as BuildingMaker).currentPenTarget = BuildingMaker.PENTARGET_TERRAIN
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolPencilEraseWall : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).currentPenMode = BuildingMaker.PENMODE_PENCIL_ERASE
|
|
(args[0] as BuildingMaker).currentPenTarget = BuildingMaker.PENTARGET_WALL
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolEyedropper : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).currentPenMode = BuildingMaker.PENMODE_EYEDROPPER
|
|
(args[0] as BuildingMaker).currentPenTarget = BuildingMaker.PENTARGET_TERRAIN + BuildingMaker.PENTARGET_WALL
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolMarquee : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).currentPenMode = BuildingMaker.PENMODE_MARQUEE
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolMarqueeErase : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).currentPenMode = BuildingMaker.PENMODE_MARQUEE_ERASE
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolMarqueeClear : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).selection.toList().forEach {
|
|
val (x, y) = (it % 4294967296).toInt() to (it / 4294967296).toInt()
|
|
(args[0] as BuildingMaker).removeBlockMarker(x, y)
|
|
}
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolToggleMarqueeOverlay : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
(args[0] as BuildingMaker).showSelection = !(args[0] as BuildingMaker).showSelection
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolExportTest : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
val ui = (args[0] as BuildingMaker)
|
|
if (ui.selection.isEmpty()) return
|
|
|
|
val marked = HashSet<Long>()
|
|
fun isMarked(x: Int, y: Int) = marked.contains(toAddr(x, y))
|
|
|
|
|
|
// get the bounding box
|
|
var minX = 2147483647
|
|
var minY = 2147483647
|
|
var maxX = -1
|
|
var maxY = -1
|
|
ui.selection.forEach {
|
|
val (x, y) = (it % 4294967296).toInt() to (it / 4294967296).toInt()
|
|
|
|
if (x < minX) minX = x
|
|
if (y < minY) minY = y
|
|
if (x > maxX) maxX = x
|
|
if (y > maxY) maxY = y
|
|
|
|
marked.add(it)
|
|
}
|
|
|
|
ui.getPoiNameForExport(maxX - minX + 1, maxY - minY + 1) { name ->
|
|
// prepare POI
|
|
val poi = PointOfInterest(
|
|
name,
|
|
maxX - minX + 1,
|
|
maxY - minY + 1,
|
|
ui.world.tileNumberToNameMap,
|
|
ui.world.tileNameToNumberMap
|
|
)
|
|
val layer = POILayer(name)
|
|
val terr = BlockLayerI16(poi.w, poi.h)
|
|
val wall = BlockLayerI16(poi.w, poi.h)
|
|
layer.blockLayer = arrayListOf(terr, wall)
|
|
poi.layers.add(layer)
|
|
|
|
for (x in minX..maxX) {
|
|
for (y in minY..maxY) {
|
|
if (isMarked(x, y)) {
|
|
terr.unsafeSetTile(x - minX, y - minY, ui.world.getTileFromTerrainRaw(x, y))
|
|
wall.unsafeSetTile(x - minX, y - minY, ui.world.getTileFromWallRaw(x, y))
|
|
}
|
|
else {
|
|
terr.unsafeSetTile(x - minX, y - minY, -1)
|
|
wall.unsafeSetTile(x - minX, y - minY, -1)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// process POI for export
|
|
val json = Common.jsoner
|
|
val jsonStr = json.toJson(poi)
|
|
|
|
|
|
val dir = App.defaultDir + "/Exports/"
|
|
val dirAsFile = File(dir)
|
|
if (!dirAsFile.exists()) {
|
|
dirAsFile.mkdir()
|
|
}
|
|
|
|
File(dirAsFile, "$name.poi").writeText(jsonStr)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun Point2i.toAddr() = toAddr(this.x, this.y)
|
|
private fun toAddr(x: Int, y: Int) = (y.toLong().shl(32) or x.toLong().and(0xFFFFFFFFL))
|
|
|
|
class YamlCommandClearSelection : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
val ui = (args[0] as BuildingMaker)
|
|
try {
|
|
(ui.selection.clone() as ArrayList<Long>).forEach { i ->
|
|
ui.removeBlockMarker((i % 4294967296).toInt(), (i / 4294967296).toInt())
|
|
}
|
|
}
|
|
catch (e: NullPointerException) {}
|
|
}
|
|
}
|
|
|
|
class YamlCommandNewFlatTerrain : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
YamlCommandClearSelection().invoke(args)
|
|
val ui = (args[0] as BuildingMaker)
|
|
|
|
println("[BuildingMaker] Generating builder world...")
|
|
|
|
val timeNow = System.currentTimeMillis() / 1000
|
|
ui.gameWorld = GameWorld(90*12, 90*4, timeNow, timeNow)
|
|
|
|
for (y in 0 until ui.gameWorld.height) {
|
|
ui.gameWorld.setTileWall(0, y, Block.ILLUMINATOR_RED, true)
|
|
ui.gameWorld.setTileWall(ui.gameWorld.width - 1, y, Block.ILLUMINATOR_RED, true)
|
|
ui.gameWorld.setTileTerrain(0, y, Block.ILLUMINATOR_RED_OFF, true)
|
|
ui.gameWorld.setTileTerrain(ui.gameWorld.width - 1, y, Block.ILLUMINATOR_RED_OFF, true)
|
|
}
|
|
|
|
for (y in 150 until ui.gameWorld.height) {
|
|
for (x in 1 until ui.gameWorld.width - 1) {
|
|
// wall layer
|
|
ui.gameWorld.setTileWall(x, y, Block.DIRT, true)
|
|
|
|
// terrain layer
|
|
ui.gameWorld.setTileTerrain(x, y, if (y == 150) Block.GRASS else Block.DIRT, true)
|
|
}
|
|
}
|
|
ui.world = ui.gameWorld
|
|
|
|
|
|
// set time to summer morning
|
|
ui.gameWorld.worldTime.addTime(WorldTime.DAY_LENGTH * 32)
|
|
ui.gameWorld.worldTime.setTimeOfToday(18062)
|
|
|
|
ui.reset()
|
|
}
|
|
}
|
|
|
|
class YamlCommandToolImportTest : YamlInvokable {
|
|
override fun invoke(args: Array<Any>) {
|
|
val ui = (args[0] as BuildingMaker)
|
|
ui.getPoiNameForImport { name ->
|
|
YamlCommandClearSelection().invoke(args)
|
|
|
|
val jsonFile = File(App.defaultDir, "/Exports/$name.poi")
|
|
if (jsonFile.exists()) {
|
|
val json = jsonFile.readText()
|
|
|
|
val poi = Common.jsoner.fromJson(PointOfInterest().javaClass, json).also {
|
|
it.getReadyToBeUsed(ui.world.tileNameToNumberMap)
|
|
}
|
|
|
|
ui.imported = poi
|
|
ui.currentPenMode = BuildingMaker.PENMODE_IMPORT
|
|
}
|
|
}
|
|
}
|
|
} |