mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-11 19:14:05 +09:00
finally working again: create new character
todo: make delete character work
This commit is contained in:
@@ -10,7 +10,7 @@ import com.badlogic.gdx.Input
|
|||||||
object DefaultConfig {
|
object DefaultConfig {
|
||||||
|
|
||||||
val hashMap = hashMapOf<String, Any>(
|
val hashMap = hashMapOf<String, Any>(
|
||||||
"jvm_xmx" to 8,
|
"jvm_xmx" to 4,
|
||||||
"jvm_extra_cmd" to "",
|
"jvm_extra_cmd" to "",
|
||||||
"displayfps" to 0, // 0: no limit, non-zero: limit
|
"displayfps" to 0, // 0: no limit, non-zero: limit
|
||||||
"displayfpsidle" to 0, // 0: no limit, non-zero: limit
|
"displayfpsidle" to 0, // 0: no limit, non-zero: limit
|
||||||
|
|||||||
@@ -39,10 +39,10 @@ class SavegameCollection(files0: List<DiskSkimmer>) {
|
|||||||
|
|
||||||
class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollection?) {
|
class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollection?) {
|
||||||
|
|
||||||
private lateinit var manualPlayer: DiskSkimmer
|
private var manualPlayer: DiskSkimmer? = null
|
||||||
private lateinit var manualWorld: DiskSkimmer
|
private var manualWorld: DiskSkimmer? = null
|
||||||
private lateinit var autoPlayer: DiskSkimmer
|
private var autoPlayer: DiskSkimmer? = null
|
||||||
private lateinit var autoWorld: DiskSkimmer
|
private var autoWorld: DiskSkimmer? = null
|
||||||
|
|
||||||
var status = 0 // 0: none available, 1: loadable manual save is newer than loadable auto; 2: loadable autosave is newer than loadable manual
|
var status = 0 // 0: none available, 1: loadable manual save is newer than loadable auto; 2: loadable autosave is newer than loadable manual
|
||||||
private set
|
private set
|
||||||
@@ -55,8 +55,8 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect
|
|||||||
|
|
||||||
if (player != null && world != null) {
|
if (player != null && world != null) {
|
||||||
|
|
||||||
printdbg(this, player.files.joinToString { it.diskFile.name })
|
printdbg(this, "player files: " + player.files.joinToString { it.diskFile.name })
|
||||||
printdbg(this, world.files.joinToString { it.diskFile.name })
|
printdbg(this, "world files:" + world.files.joinToString { it.diskFile.name })
|
||||||
|
|
||||||
// if a pair of files were saved successfully, they must have identical lastModifiedTime()
|
// if a pair of files were saved successfully, they must have identical lastModifiedTime()
|
||||||
var pc = 0; val pt = player.files[0].getLastModifiedTime()
|
var pc = 0; val pt = player.files[0].getLastModifiedTime()
|
||||||
@@ -75,7 +75,7 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect
|
|||||||
|
|
||||||
when (pcf.isAutosaved().toInt(1) or wcf.isAutosaved().toInt()) {
|
when (pcf.isAutosaved().toInt(1) or wcf.isAutosaved().toInt()) {
|
||||||
3 -> {
|
3 -> {
|
||||||
if (!::autoPlayer.isInitialized && !::autoWorld.isInitialized) {
|
if (autoPlayer == null && autoWorld == null) {
|
||||||
autoPlayer = pcf
|
autoPlayer = pcf
|
||||||
autoWorld = wcf
|
autoWorld = wcf
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect
|
|||||||
wc += 1
|
wc += 1
|
||||||
}
|
}
|
||||||
0 -> {
|
0 -> {
|
||||||
if (!::manualPlayer.isInitialized && !::manualWorld.isInitialized) {
|
if (manualPlayer == null && manualWorld == null) {
|
||||||
manualPlayer = pcf
|
manualPlayer = pcf
|
||||||
manualWorld = wcf
|
manualWorld = wcf
|
||||||
}
|
}
|
||||||
@@ -105,32 +105,25 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (::manualPlayer.isInitialized && ::manualWorld.isInitialized && ::autoPlayer.isInitialized && ::autoWorld.isInitialized)
|
if (manualPlayer != null && manualWorld != null && autoPlayer != null && autoWorld != null)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (::manualPlayer.isInitialized && ::manualWorld.isInitialized && ::autoPlayer.isInitialized && ::autoWorld.isInitialized) {
|
if (manualPlayer != null && manualWorld != null && autoPlayer != null && autoWorld != null) {
|
||||||
status = if (manualPlayer.getLastModifiedTime() > autoPlayer.getLastModifiedTime()) 1 else 2
|
status = if (manualPlayer!!.getLastModifiedTime() > autoPlayer!!.getLastModifiedTime()) 1 else 2
|
||||||
|
|
||||||
printdbg(this, "manualPlayer = ${manualPlayer.diskFile.path}")
|
|
||||||
printdbg(this, "manualWorld = ${manualWorld.diskFile.path}")
|
|
||||||
printdbg(this, "autoPlayer = ${autoPlayer.diskFile.path}")
|
|
||||||
printdbg(this, "autoWorld = ${autoWorld.diskFile.path}")
|
|
||||||
}
|
}
|
||||||
else if (::manualPlayer.isInitialized && ::manualWorld.isInitialized || ::autoPlayer.isInitialized && ::autoWorld.isInitialized) {
|
else if (manualPlayer != null && manualWorld != null || autoPlayer != null && autoWorld != null) {
|
||||||
status = 1
|
status = 1
|
||||||
if (::manualPlayer.isInitialized) {
|
|
||||||
printdbg(this, "manualPlayer = ${manualPlayer.diskFile.path}")
|
|
||||||
printdbg(this, "manualWorld = ${manualWorld.diskFile.path}")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printdbg(this, "autoPlayer = ${autoPlayer.diskFile.path}")
|
|
||||||
printdbg(this, "autoWorld = ${autoWorld.diskFile.path}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
status = 0
|
status = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printdbg(this, "manualPlayer = ${manualPlayer?.diskFile?.path}")
|
||||||
|
printdbg(this, "manualWorld = ${manualWorld?.diskFile?.path}")
|
||||||
|
printdbg(this, "autoPlayer = ${autoPlayer?.diskFile?.path}")
|
||||||
|
printdbg(this, "autoWorld = ${autoWorld?.diskFile?.path}")
|
||||||
|
printdbg(this, "status = $status")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,12 +142,20 @@ class SavegameCollectionPair(player: SavegameCollection?, world: SavegameCollect
|
|||||||
|
|
||||||
fun getManualSave(): DiskPair? {
|
fun getManualSave(): DiskPair? {
|
||||||
if (status == 0) return null
|
if (status == 0) return null
|
||||||
return DiskPair(manualPlayer, manualWorld)
|
return DiskPair(manualPlayer!!, manualWorld!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAutoSave(): DiskPair? {
|
fun getAutoSave(): DiskPair? {
|
||||||
if (status != 2) return null
|
if (status != 2) return null
|
||||||
return DiskPair(autoPlayer, autoWorld)
|
return DiskPair(autoPlayer!!, autoWorld!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLoadableSave(): DiskPair? {
|
||||||
|
if (status == 0) return null
|
||||||
|
return if (manualPlayer != null && manualWorld != null)
|
||||||
|
DiskPair(manualPlayer!!, manualWorld!!)
|
||||||
|
else
|
||||||
|
DiskPair(autoPlayer!!, autoWorld!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ object ScreencapNogui: ConsoleCommand {
|
|||||||
PixmapIO2.writeTGA(Gdx.files.absolute(App.defaultDir + "/Exports/${args[1]}.tga"), p, true)
|
PixmapIO2.writeTGA(Gdx.files.absolute(App.defaultDir + "/Exports/${args[1]}.tga"), p, true)
|
||||||
p.dispose()
|
p.dispose()
|
||||||
}
|
}
|
||||||
IngameRenderer.screencapRequested = true
|
IngameRenderer.requestScreencap()
|
||||||
Echo("FBO exported to$ccG Exports/${args[1]}.tga")
|
Echo("FBO exported to$ccG Exports/${args[1]}.tga")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import com.badlogic.gdx.utils.Disposable
|
|||||||
import com.badlogic.gdx.utils.GdxRuntimeException
|
import com.badlogic.gdx.utils.GdxRuntimeException
|
||||||
import net.torvald.random.HQRNG
|
import net.torvald.random.HQRNG
|
||||||
import net.torvald.terrarum.*
|
import net.torvald.terrarum.*
|
||||||
import net.torvald.terrarum.App.measureDebugTime
|
import net.torvald.terrarum.App.*
|
||||||
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
|
||||||
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF
|
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZEF
|
||||||
import net.torvald.terrarum.gameactors.ActorWithBody
|
import net.torvald.terrarum.gameactors.ActorWithBody
|
||||||
@@ -21,6 +21,7 @@ import net.torvald.terrarum.gameworld.GameWorld
|
|||||||
import net.torvald.terrarum.gameworld.fmod
|
import net.torvald.terrarum.gameworld.fmod
|
||||||
import net.torvald.terrarum.ui.Toolkit
|
import net.torvald.terrarum.ui.Toolkit
|
||||||
import net.torvald.terrarum.weather.WeatherMixer
|
import net.torvald.terrarum.weather.WeatherMixer
|
||||||
|
import net.torvald.terrarum.weather.WeatherMixer.render
|
||||||
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
||||||
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
|
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
|
||||||
import net.torvald.terrarum.worlddrawer.LightmapRenderer
|
import net.torvald.terrarum.worlddrawer.LightmapRenderer
|
||||||
@@ -365,13 +366,19 @@ object IngameRenderer : Disposable {
|
|||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
if (screencapRequested) {
|
if (screencapRequested) {
|
||||||
screencapRequested = false
|
printdbg(this, "Screencap was requested, processing...")
|
||||||
|
var hasError = false
|
||||||
try {
|
try {
|
||||||
screencapExportCallback(fboMixedOut)
|
screencapExportCallback(fboMixedOut)
|
||||||
}
|
}
|
||||||
catch (e: Throwable) {
|
catch (e: Throwable) {
|
||||||
|
printdbgerr(this, "An error occured while taking screencap:")
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
hasError = true
|
||||||
}
|
}
|
||||||
|
printdbg(this, "Screencap ${if (hasError) "failed" else "successful"}")
|
||||||
|
screencapBusy = false
|
||||||
|
screencapRequested = false
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
@@ -416,11 +423,16 @@ object IngameRenderer : Disposable {
|
|||||||
* This "screencap" will capture the game WITHOUT gui and postprocessors!
|
* This "screencap" will capture the game WITHOUT gui and postprocessors!
|
||||||
* To capture the entire game, use [App.requestScreenshot]
|
* To capture the entire game, use [App.requestScreenshot]
|
||||||
*/
|
*/
|
||||||
@Volatile internal var screencapRequested = false
|
@Volatile private var screencapRequested = false
|
||||||
@Volatile internal var fboRGBexportedLatch = false
|
@Volatile internal var screencapBusy = false; private set
|
||||||
@Volatile internal var screencapExportCallback: (FrameBuffer) -> Unit = {}
|
@Volatile internal var screencapExportCallback: (FrameBuffer) -> Unit = {}
|
||||||
@Volatile internal lateinit var fboRGBexport: Pixmap
|
@Volatile internal lateinit var fboRGBexport: Pixmap
|
||||||
|
|
||||||
|
fun requestScreencap() {
|
||||||
|
screencapRequested = true
|
||||||
|
screencapBusy = true
|
||||||
|
}
|
||||||
|
|
||||||
private fun drawToRGB(
|
private fun drawToRGB(
|
||||||
actorsRenderBehind: List<ActorWithBody>?,
|
actorsRenderBehind: List<ActorWithBody>?,
|
||||||
actorsRenderMiddle: List<ActorWithBody>?,
|
actorsRenderMiddle: List<ActorWithBody>?,
|
||||||
|
|||||||
@@ -420,10 +420,15 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
// 2. cannot sync up the "counter" to determine whether both are finished
|
// 2. cannot sync up the "counter" to determine whether both are finished
|
||||||
uiAutosaveNotifier.setAsOpen()
|
uiAutosaveNotifier.setAsOpen()
|
||||||
val saveTime_t = App.getTIME_T()
|
val saveTime_t = App.getTIME_T()
|
||||||
|
printdbg(this, "Immediate Save")
|
||||||
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, true, autosaveOnErrorAction) {
|
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.PLAYER, playerDisk, getPlayerSaveFiledesc(playerSavefileName), this, true, autosaveOnErrorAction) {
|
||||||
|
printdbg(this, "immediate save callback from PLAYER")
|
||||||
|
|
||||||
makeSavegameBackupCopy(getPlayerSaveFiledesc(playerSavefileName))
|
makeSavegameBackupCopy(getPlayerSaveFiledesc(playerSavefileName))
|
||||||
|
|
||||||
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.WORLD, worldDisk, getWorldSaveFiledesc(worldSavefileName), this, true, autosaveOnErrorAction) {
|
WriteSavegame.immediate(saveTime_t, WriteSavegame.SaveMode.WORLD, worldDisk, getWorldSaveFiledesc(worldSavefileName), this, true, autosaveOnErrorAction) {
|
||||||
|
printdbg(this, "immediate save callback from WORLD")
|
||||||
|
|
||||||
makeSavegameBackupCopy(getWorldSaveFiledesc(worldSavefileName)) // don't put it on the postInit() or render(); must be called using callback
|
makeSavegameBackupCopy(getWorldSaveFiledesc(worldSavefileName)) // don't put it on the postInit() or render(); must be called using callback
|
||||||
uiAutosaveNotifier.setAsClose()
|
uiAutosaveNotifier.setAsClose()
|
||||||
}
|
}
|
||||||
@@ -472,7 +477,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
|||||||
|
|
||||||
world.worldCreator = UUID.fromString(player.uuid.toString())
|
world.worldCreator = UUID.fromString(player.uuid.toString())
|
||||||
|
|
||||||
printdbg(this, "new woridIndex: ${world.worldIndex}")
|
printdbg(this, "new worldIndex: ${world.worldIndex}")
|
||||||
printdbg(this, "worldCurrentlyPlaying: ${player.worldCurrentlyPlaying}")
|
printdbg(this, "worldCurrentlyPlaying: ${player.worldCurrentlyPlaying}")
|
||||||
|
|
||||||
actorNowPlaying = player
|
actorNowPlaying = player
|
||||||
|
|||||||
@@ -1,36 +1,5 @@
|
|||||||
package net.torvald.terrarum.modulebasegame.serialise
|
package net.torvald.terrarum.modulebasegame.serialise
|
||||||
|
|
||||||
import net.torvald.gdx.graphics.PixmapIO2
|
|
||||||
import net.torvald.terrarum.App.printdbg
|
|
||||||
import net.torvald.terrarum.ItemCodex
|
|
||||||
import net.torvald.terrarum.ModMgr
|
|
||||||
import net.torvald.terrarum.ReferencingRanges.PREFIX_DYNAMICITEM
|
|
||||||
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
|
||||||
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
|
||||||
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
|
|
||||||
import net.torvald.terrarum.realestate.LandUtil
|
|
||||||
import net.torvald.terrarum.toInt
|
|
||||||
import net.torvald.terrarum.savegame.*
|
|
||||||
import net.torvald.terrarum.savegame.VDFileID.LOADORDER
|
|
||||||
import net.torvald.terrarum.savegame.VDFileID.PLAYER_SCREENSHOT
|
|
||||||
import net.torvald.terrarum.savegame.VDFileID.ROOT
|
|
||||||
import net.torvald.terrarum.savegame.VDFileID.SAVEGAMEINFO
|
|
||||||
import net.torvald.terrarum.savegame.VDFileID.THUMBNAIL
|
|
||||||
import net.torvald.terrarum.serialise.Common
|
|
||||||
import java.io.File
|
|
||||||
import java.util.zip.GZIPOutputStream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will happily overwrite existing entry
|
|
||||||
*/
|
|
||||||
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
|
||||||
disk.entries[file.entryID] = file
|
|
||||||
file.parentEntryID = 0
|
|
||||||
val dir = VDUtil.getAsDirectory(disk, 0)
|
|
||||||
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Runnable {
|
abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Runnable {
|
||||||
abstract fun save()
|
abstract fun save()
|
||||||
@@ -46,211 +15,4 @@ abstract class SavingThread(private val errorHandler: (Throwable) -> Unit) : Run
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const val SCREENCAP_WAIT_TRY_MAX = 256
|
||||||
* Created by minjaesong on 2021-09-14.
|
|
||||||
*/
|
|
||||||
class WorldSavingThread(
|
|
||||||
val time_t: Long,
|
|
||||||
val disk: VirtualDisk,
|
|
||||||
val outFile: File,
|
|
||||||
val ingame: TerrarumIngame,
|
|
||||||
val isAuto: Boolean,
|
|
||||||
val callback: () -> Unit,
|
|
||||||
val errorHandler: (Throwable) -> Unit
|
|
||||||
) : SavingThread(errorHandler) {
|
|
||||||
|
|
||||||
override fun save() {
|
|
||||||
|
|
||||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
|
||||||
disk.saveKind = VDSaveKind.WORLD_DATA
|
|
||||||
|
|
||||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
|
||||||
Thread.sleep(1L)
|
|
||||||
}
|
|
||||||
|
|
||||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
|
||||||
|
|
||||||
val playersList: List<IngamePlayer> = allTheActors.filterIsInstance<IngamePlayer>()
|
|
||||||
val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
|
|
||||||
val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) }
|
|
||||||
val cw = ingame.world.width / LandUtil.CHUNK_W
|
|
||||||
val ch = ingame.world.height / LandUtil.CHUNK_H
|
|
||||||
|
|
||||||
WriteSavegame.saveProgress = 0f
|
|
||||||
WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size
|
|
||||||
|
|
||||||
|
|
||||||
val tgaout = ByteArray64GrowableOutputStream()
|
|
||||||
val gzout = GZIPOutputStream(tgaout)
|
|
||||||
|
|
||||||
printdbg(this, "Writing metadata...")
|
|
||||||
|
|
||||||
val creation_t = ingame.world.creationTime
|
|
||||||
|
|
||||||
|
|
||||||
// Write subset of Ingame.ItemCodex
|
|
||||||
// The existing ItemCodex must be rewritten to clear out obsolete records
|
|
||||||
|
|
||||||
// We're assuming the dynamic item generated by players does exist in the world, and it's recorded
|
|
||||||
// into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable
|
|
||||||
// can be found in this world without need to look up the players
|
|
||||||
ingame.world.dynamicToStaticTable.clear()
|
|
||||||
ingame.world.dynamicItemInventory.clear()
|
|
||||||
actorsList.filterIsInstance<Pocketed>().forEach { actor ->
|
|
||||||
actor.inventory.forEach { (itemid, _) ->
|
|
||||||
|
|
||||||
printdbg(this, "World side dynamicitem: $itemid contained in $actor")
|
|
||||||
|
|
||||||
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
|
|
||||||
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
|
||||||
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actorsList.filterIsInstance<FixtureBase>().forEach { fixture ->
|
|
||||||
fixture.inventory?.forEach { (itemid, _) ->
|
|
||||||
|
|
||||||
printdbg(this, "World side dynamicitem: $itemid contained in $fixture")
|
|
||||||
|
|
||||||
if (itemid.startsWith("$PREFIX_DYNAMICITEM:")) {
|
|
||||||
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
|
||||||
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
|
||||||
IngameRenderer.fboRGBexport.dispose()
|
|
||||||
|
|
||||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
|
||||||
val thumb = DiskEntry(THUMBNAIL, ROOT, creation_t, time_t, thumbContent)
|
|
||||||
addFile(disk, thumb)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WriteSavegame.saveProgress += 1f
|
|
||||||
|
|
||||||
// Write World //
|
|
||||||
|
|
||||||
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
|
|
||||||
val world = DiskEntry(SAVEGAMEINFO, ROOT, creation_t, time_t, worldMeta)
|
|
||||||
addFile(disk, world)
|
|
||||||
|
|
||||||
WriteSavegame.saveProgress += 1f
|
|
||||||
|
|
||||||
|
|
||||||
for (layer in layers.indices) {
|
|
||||||
for (cx in 0 until cw) {
|
|
||||||
for (cy in 0 until ch) {
|
|
||||||
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong()
|
|
||||||
|
|
||||||
// Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}")
|
|
||||||
|
|
||||||
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
|
|
||||||
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
|
|
||||||
|
|
||||||
val entryContent = EntryFile(chunkBytes)
|
|
||||||
val entry = DiskEntry(entryID, ROOT, creation_t, time_t, entryContent)
|
|
||||||
// "W1L0-92,15"
|
|
||||||
addFile(disk, entry)
|
|
||||||
|
|
||||||
WriteSavegame.saveProgress += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Write Actors //
|
|
||||||
actorsList.forEachIndexed { count, it ->
|
|
||||||
// Echo("Writing actors... ${count+1}/${actorsList.size}")
|
|
||||||
|
|
||||||
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
|
||||||
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)
|
|
||||||
addFile(disk, actor)
|
|
||||||
|
|
||||||
WriteSavegame.saveProgress += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// write loadorder //
|
|
||||||
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
|
|
||||||
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
|
|
||||||
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
|
|
||||||
val loadOrderText = loadOrderBa64Writer.toByteArray64()
|
|
||||||
val loadOrderContents = EntryFile(loadOrderText)
|
|
||||||
addFile(disk, DiskEntry(LOADORDER, ROOT, creation_t, time_t, loadOrderContents))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Echo("Writing file to disk...")
|
|
||||||
|
|
||||||
disk.entries[0]!!.modificationDate = time_t
|
|
||||||
// entry zero MUST NOT be used to get lastPlayDate, but we'll update it anyway
|
|
||||||
// use entry -1 for that purpose!
|
|
||||||
disk.capacity = 0
|
|
||||||
VDUtil.dumpToRealMachine(disk, outFile)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
|
||||||
|
|
||||||
|
|
||||||
IngameRenderer.fboRGBexportedLatch = false
|
|
||||||
WriteSavegame.savingStatus = 255
|
|
||||||
|
|
||||||
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map
|
|
||||||
*
|
|
||||||
* Created by minjaesong on 2021-10-08
|
|
||||||
*/
|
|
||||||
class PlayerSavingThread(
|
|
||||||
val time_t: Long,
|
|
||||||
val disk: VirtualDisk,
|
|
||||||
val outFile: File,
|
|
||||||
val ingame: TerrarumIngame,
|
|
||||||
val isAuto: Boolean,
|
|
||||||
val callback: () -> Unit,
|
|
||||||
val errorHandler: (Throwable) -> Unit
|
|
||||||
) : SavingThread(errorHandler) {
|
|
||||||
|
|
||||||
override fun save() {
|
|
||||||
disk.saveMode = 2 * isAuto.toInt() // no quick
|
|
||||||
disk.saveKind = VDSaveKind.PLAYER_DATA
|
|
||||||
disk.capacity = 0L
|
|
||||||
|
|
||||||
WriteSavegame.saveProgress = 0f
|
|
||||||
|
|
||||||
// wait for screencap
|
|
||||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
|
||||||
Thread.sleep(1L)
|
|
||||||
}
|
|
||||||
|
|
||||||
// write screencap
|
|
||||||
val tgaout = ByteArray64GrowableOutputStream()
|
|
||||||
val gzout = GZIPOutputStream(tgaout)
|
|
||||||
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
|
||||||
IngameRenderer.fboRGBexport.dispose()
|
|
||||||
val thumbContent = EntryFile(tgaout.toByteArray64())
|
|
||||||
val thumb = DiskEntry(PLAYER_SCREENSHOT, ROOT, ingame.world.creationTime, time_t, thumbContent)
|
|
||||||
addFile(disk, thumb)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
printdbg(this, "Writing The Player...")
|
|
||||||
WritePlayer(ingame.actorGamer, disk, ingame, time_t)
|
|
||||||
disk.entries[0]!!.modificationDate = time_t
|
|
||||||
VDUtil.dumpToRealMachine(disk, outFile)
|
|
||||||
|
|
||||||
|
|
||||||
IngameRenderer.fboRGBexportedLatch = false
|
|
||||||
|
|
||||||
callback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.serialise
|
||||||
|
|
||||||
|
import net.torvald.gdx.graphics.PixmapIO2
|
||||||
|
import net.torvald.terrarum.App
|
||||||
|
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||||
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
|
import net.torvald.terrarum.savegame.*
|
||||||
|
import net.torvald.terrarum.toInt
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.GZIPOutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function called means the "Avatar" was not externally created and thus has no sprite-bodypart-name-to-entry-number-map
|
||||||
|
*
|
||||||
|
* Created by minjaesong on 2021-10-08
|
||||||
|
*/
|
||||||
|
class PlayerSavingThread(
|
||||||
|
val time_t: Long,
|
||||||
|
val disk: VirtualDisk,
|
||||||
|
val outFile: File,
|
||||||
|
val ingame: TerrarumIngame,
|
||||||
|
val isAuto: Boolean,
|
||||||
|
val callback: () -> Unit,
|
||||||
|
val errorHandler: (Throwable) -> Unit
|
||||||
|
) : SavingThread(errorHandler) {
|
||||||
|
/**
|
||||||
|
* Will happily overwrite existing entry
|
||||||
|
*/
|
||||||
|
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
||||||
|
disk.entries[file.entryID] = file
|
||||||
|
file.parentEntryID = 0
|
||||||
|
val dir = VDUtil.getAsDirectory(disk, 0)
|
||||||
|
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
App.printdbg(this, "outFile: ${outFile.path}")
|
||||||
|
|
||||||
|
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||||
|
disk.saveKind = VDSaveKind.PLAYER_DATA
|
||||||
|
disk.capacity = 0L
|
||||||
|
|
||||||
|
WriteSavegame.saveProgress = 0f
|
||||||
|
|
||||||
|
// wait for screencap
|
||||||
|
var emergencyStopCnt = 0
|
||||||
|
while (IngameRenderer.screencapBusy) {
|
||||||
|
// printdbg(this, "spinning for screencap to be taken")
|
||||||
|
Thread.sleep(4L)
|
||||||
|
emergencyStopCnt += 1
|
||||||
|
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
// write screencap
|
||||||
|
val tgaout = ByteArray64GrowableOutputStream()
|
||||||
|
val gzout = GZIPOutputStream(tgaout)
|
||||||
|
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||||
|
IngameRenderer.fboRGBexport.dispose()
|
||||||
|
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||||
|
val thumb =
|
||||||
|
DiskEntry(VDFileID.PLAYER_SCREENSHOT, VDFileID.ROOT, ingame.world.creationTime, time_t, thumbContent)
|
||||||
|
addFile(disk, thumb)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App.printdbg(this, "Writing The Player...")
|
||||||
|
WritePlayer(ingame.actorGamer, disk, ingame, time_t)
|
||||||
|
disk.entries[0]!!.modificationDate = time_t
|
||||||
|
VDUtil.dumpToRealMachine(disk, outFile)
|
||||||
|
|
||||||
|
|
||||||
|
// IngameRenderer.screencapBusy = false
|
||||||
|
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,10 +45,17 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
|
|
||||||
|
|
||||||
override fun save() {
|
override fun save() {
|
||||||
|
printdbg(this, "outFile: ${outFile.path}")
|
||||||
|
|
||||||
val skimmer = DiskSkimmer(outFile)
|
val skimmer = DiskSkimmer(outFile)
|
||||||
|
|
||||||
while (!IngameRenderer.fboRGBexportedLatch) {
|
// wait for screencap
|
||||||
Thread.sleep(1L)
|
var emergencyStopCnt = 0
|
||||||
|
while (IngameRenderer.screencapBusy) {
|
||||||
|
// printdbg(this, "spinning for screencap to be taken")
|
||||||
|
Thread.sleep(4L)
|
||||||
|
emergencyStopCnt += 1
|
||||||
|
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
||||||
}
|
}
|
||||||
|
|
||||||
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||||
@@ -147,7 +154,7 @@ class QuickSingleplayerWorldSavingThread(
|
|||||||
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||||
|
|
||||||
|
|
||||||
IngameRenderer.fboRGBexportedLatch = false
|
// IngameRenderer.screencapBusy = false
|
||||||
WriteSavegame.savingStatus = 255
|
WriteSavegame.savingStatus = 255
|
||||||
ingame.clearModifiedChunks()
|
ingame.clearModifiedChunks()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package net.torvald.terrarum.modulebasegame.serialise
|
||||||
|
|
||||||
|
import net.torvald.gdx.graphics.PixmapIO2
|
||||||
|
import net.torvald.terrarum.*
|
||||||
|
import net.torvald.terrarum.modulebasegame.IngameRenderer
|
||||||
|
import net.torvald.terrarum.modulebasegame.TerrarumIngame
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
||||||
|
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
|
||||||
|
import net.torvald.terrarum.realestate.LandUtil
|
||||||
|
import net.torvald.terrarum.savegame.*
|
||||||
|
import net.torvald.terrarum.serialise.Common
|
||||||
|
import java.io.File
|
||||||
|
import java.util.zip.GZIPOutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by minjaesong on 2021-09-14.
|
||||||
|
*/
|
||||||
|
class WorldSavingThread(
|
||||||
|
val time_t: Long,
|
||||||
|
val disk: VirtualDisk,
|
||||||
|
val outFile: File,
|
||||||
|
val ingame: TerrarumIngame,
|
||||||
|
val isAuto: Boolean,
|
||||||
|
val callback: () -> Unit,
|
||||||
|
val errorHandler: (Throwable) -> Unit
|
||||||
|
) : SavingThread(errorHandler) {
|
||||||
|
/**
|
||||||
|
* Will happily overwrite existing entry
|
||||||
|
*/
|
||||||
|
private fun addFile(disk: VirtualDisk, file: DiskEntry) {
|
||||||
|
disk.entries[file.entryID] = file
|
||||||
|
file.parentEntryID = 0
|
||||||
|
val dir = VDUtil.getAsDirectory(disk, 0)
|
||||||
|
if (!dir.contains(file.entryID)) dir.add(file.entryID)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun save() {
|
||||||
|
App.printdbg(this, "outFile: ${outFile.path}")
|
||||||
|
|
||||||
|
disk.saveMode = 2 * isAuto.toInt() // no quick
|
||||||
|
disk.saveKind = VDSaveKind.WORLD_DATA
|
||||||
|
|
||||||
|
// wait for screencap
|
||||||
|
var emergencyStopCnt = 0
|
||||||
|
while (IngameRenderer.screencapBusy) {
|
||||||
|
// printdbg(this, "spinning for screencap to be taken")
|
||||||
|
Thread.sleep(4L)
|
||||||
|
emergencyStopCnt += 1
|
||||||
|
if (emergencyStopCnt >= SCREENCAP_WAIT_TRY_MAX) throw InterruptedException("Waiting screencap to be taken for too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
val allTheActors = ingame.actorContainerActive.cloneToList() + ingame.actorContainerInactive.cloneToList()
|
||||||
|
|
||||||
|
val playersList: List<IngamePlayer> = allTheActors.filterIsInstance<IngamePlayer>()
|
||||||
|
val actorsList = allTheActors.filter { WriteWorld.actorAcceptable(it) }
|
||||||
|
val layers = intArrayOf(0,1).map { ingame.world.getLayer(it) }
|
||||||
|
val cw = ingame.world.width / LandUtil.CHUNK_W
|
||||||
|
val ch = ingame.world.height / LandUtil.CHUNK_H
|
||||||
|
|
||||||
|
WriteSavegame.saveProgress = 0f
|
||||||
|
WriteSavegame.saveProgressMax = 3f + (cw * ch * layers.size) + actorsList.size
|
||||||
|
|
||||||
|
|
||||||
|
val tgaout = ByteArray64GrowableOutputStream()
|
||||||
|
val gzout = GZIPOutputStream(tgaout)
|
||||||
|
|
||||||
|
App.printdbg(this, "Writing metadata...")
|
||||||
|
|
||||||
|
val creation_t = ingame.world.creationTime
|
||||||
|
|
||||||
|
|
||||||
|
// Write subset of Ingame.ItemCodex
|
||||||
|
// The existing ItemCodex must be rewritten to clear out obsolete records
|
||||||
|
|
||||||
|
// We're assuming the dynamic item generated by players does exist in the world, and it's recorded
|
||||||
|
// into the world's dynamicToStaticTable, therefore every item recorded into the world's dynamicToStaticTable
|
||||||
|
// can be found in this world without need to look up the players
|
||||||
|
ingame.world.dynamicToStaticTable.clear()
|
||||||
|
ingame.world.dynamicItemInventory.clear()
|
||||||
|
actorsList.filterIsInstance<Pocketed>().forEach { actor ->
|
||||||
|
actor.inventory.forEach { (itemid, _) ->
|
||||||
|
|
||||||
|
App.printdbg(this, "World side dynamicitem: $itemid contained in $actor")
|
||||||
|
|
||||||
|
if (itemid.startsWith("${ReferencingRanges.PREFIX_DYNAMICITEM}:")) {
|
||||||
|
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
||||||
|
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actorsList.filterIsInstance<FixtureBase>().forEach { fixture ->
|
||||||
|
fixture.inventory?.forEach { (itemid, _) ->
|
||||||
|
|
||||||
|
App.printdbg(this, "World side dynamicitem: $itemid contained in $fixture")
|
||||||
|
|
||||||
|
if (itemid.startsWith("${ReferencingRanges.PREFIX_DYNAMICITEM}:")) {
|
||||||
|
ingame.world.dynamicToStaticTable[itemid] = ItemCodex.dynamicToStaticID(itemid)
|
||||||
|
ingame.world.dynamicItemInventory[itemid] = ItemCodex[itemid]!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
PixmapIO2._writeTGA(gzout, IngameRenderer.fboRGBexport, true, true)
|
||||||
|
IngameRenderer.fboRGBexport.dispose()
|
||||||
|
|
||||||
|
val thumbContent = EntryFile(tgaout.toByteArray64())
|
||||||
|
val thumb = DiskEntry(VDFileID.THUMBNAIL, VDFileID.ROOT, creation_t, time_t, thumbContent)
|
||||||
|
addFile(disk, thumb)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
WriteSavegame.saveProgress += 1f
|
||||||
|
|
||||||
|
// Write World //
|
||||||
|
|
||||||
|
val worldMeta = EntryFile(WriteWorld.encodeToByteArray64(ingame, time_t, actorsList, playersList))
|
||||||
|
val world = DiskEntry(VDFileID.SAVEGAMEINFO, VDFileID.ROOT, creation_t, time_t, worldMeta)
|
||||||
|
addFile(disk, world)
|
||||||
|
|
||||||
|
WriteSavegame.saveProgress += 1f
|
||||||
|
|
||||||
|
|
||||||
|
for (layer in layers.indices) {
|
||||||
|
for (cx in 0 until cw) {
|
||||||
|
for (cy in 0 until ch) {
|
||||||
|
val chunkNumber = LandUtil.chunkXYtoChunkNum(ingame.world, cx, cy).toLong()
|
||||||
|
|
||||||
|
// Echo("Writing chunks... ${(cw*ch*layer) + chunkNumber + 1}/${cw*ch*layers.size}")
|
||||||
|
|
||||||
|
val chunkBytes = WriteWorld.encodeChunk(layers[layer]!!, cx, cy)
|
||||||
|
val entryID = 0x1_0000_0000L or layer.toLong().shl(24) or chunkNumber
|
||||||
|
|
||||||
|
val entryContent = EntryFile(chunkBytes)
|
||||||
|
val entry = DiskEntry(entryID, VDFileID.ROOT, creation_t, time_t, entryContent)
|
||||||
|
// "W1L0-92,15"
|
||||||
|
addFile(disk, entry)
|
||||||
|
|
||||||
|
WriteSavegame.saveProgress += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Write Actors //
|
||||||
|
actorsList.forEachIndexed { count, it ->
|
||||||
|
// Echo("Writing actors... ${count+1}/${actorsList.size}")
|
||||||
|
|
||||||
|
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
|
||||||
|
val actor = DiskEntry(it.referenceID.toLong(), VDFileID.ROOT, creation_t, time_t, actorContent)
|
||||||
|
addFile(disk, actor)
|
||||||
|
|
||||||
|
WriteSavegame.saveProgress += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// write loadorder //
|
||||||
|
val loadOrderBa64Writer = ByteArray64Writer(Common.CHARSET)
|
||||||
|
loadOrderBa64Writer.write(ModMgr.loadOrder.joinToString("\n"))
|
||||||
|
loadOrderBa64Writer.flush(); loadOrderBa64Writer.close()
|
||||||
|
val loadOrderText = loadOrderBa64Writer.toByteArray64()
|
||||||
|
val loadOrderContents = EntryFile(loadOrderText)
|
||||||
|
addFile(disk, DiskEntry(VDFileID.LOADORDER, VDFileID.ROOT, creation_t, time_t, loadOrderContents))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Echo("Writing file to disk...")
|
||||||
|
|
||||||
|
disk.entries[0]!!.modificationDate = time_t
|
||||||
|
// entry zero MUST NOT be used to get lastPlayDate, but we'll update it anyway
|
||||||
|
// use entry -1 for that purpose!
|
||||||
|
disk.capacity = 0
|
||||||
|
VDUtil.dumpToRealMachine(disk, outFile)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App.printdbg(this, "Game saved with size of ${outFile.length()} bytes")
|
||||||
|
|
||||||
|
|
||||||
|
// IngameRenderer.screencapBusy = false
|
||||||
|
WriteSavegame.savingStatus = 255
|
||||||
|
|
||||||
|
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,10 +43,7 @@ object WriteSavegame {
|
|||||||
else -> throw IllegalArgumentException("$mode")
|
else -> throw IllegalArgumentException("$mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
private fun installScreencap() {
|
||||||
savingStatus = 0
|
|
||||||
printdbg(this, "Save queued")
|
|
||||||
|
|
||||||
IngameRenderer.screencapExportCallback = { fb ->
|
IngameRenderer.screencapExportCallback = { fb ->
|
||||||
printdbg(this, "Generating thumbnail...")
|
printdbg(this, "Generating thumbnail...")
|
||||||
|
|
||||||
@@ -63,11 +60,19 @@ object WriteSavegame {
|
|||||||
IngameRenderer.fboRGBexport = p
|
IngameRenderer.fboRGBexport = p
|
||||||
//PixmapIO2._writeTGA(gzout, p, true, true)
|
//PixmapIO2._writeTGA(gzout, p, true, true)
|
||||||
//p.dispose()
|
//p.dispose()
|
||||||
IngameRenderer.fboRGBexportedLatch = true
|
|
||||||
|
|
||||||
printdbg(this, "Done thumbnail generation")
|
printdbg(this, "Done thumbnail generation")
|
||||||
}
|
}
|
||||||
IngameRenderer.screencapRequested = true
|
}
|
||||||
|
|
||||||
|
operator fun invoke(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
||||||
|
savingStatus = 0
|
||||||
|
printdbg(this, "Save queued")
|
||||||
|
|
||||||
|
installScreencap()
|
||||||
|
try { printdbg(this, "ScreencapExport installed: ${IngameRenderer.screencapExportCallback}") }
|
||||||
|
catch (e: UninitializedPropertyAccessException) { printdbg(this, "ScreencapExport installed: no") }
|
||||||
|
IngameRenderer.requestScreencap()
|
||||||
|
|
||||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||||
savingThread.start()
|
savingThread.start()
|
||||||
@@ -78,11 +83,14 @@ object WriteSavegame {
|
|||||||
|
|
||||||
|
|
||||||
fun immediate(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
fun immediate(time_t: Long, mode: SaveMode, disk: VirtualDisk, outFile: File, ingame: TerrarumIngame, isAuto: Boolean, errorHandler: (Throwable) -> Unit, callback: () -> Unit) {
|
||||||
|
|
||||||
savingStatus = 0
|
savingStatus = 0
|
||||||
|
|
||||||
printdbg(this, "Immediate save fired")
|
printdbg(this, "Immediate save fired")
|
||||||
|
|
||||||
|
installScreencap()
|
||||||
|
try { printdbg(this, "ScreencapExport installed: ${IngameRenderer.screencapExportCallback}") }
|
||||||
|
catch (e: UninitializedPropertyAccessException) { printdbg(this, "ScreencapExport installed: no") }
|
||||||
|
IngameRenderer.requestScreencap()
|
||||||
|
|
||||||
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
val savingThread = Thread(getSaveThread(time_t, mode, disk, outFile, ingame, isAuto, errorHandler, callback), "TerrarumBasegameGameSaveThread")
|
||||||
savingThread.start()
|
savingThread.start()
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
private val MODE_SAVE_MULTIPLE_CHOICES = 2
|
private val MODE_SAVE_MULTIPLE_CHOICES = 2
|
||||||
private val MODE_LOAD_DA_SHIT_ALREADY = 255
|
private val MODE_LOAD_DA_SHIT_ALREADY = 255
|
||||||
private val MODE_SAVE_DAMAGED = 256
|
private val MODE_SAVE_DAMAGED = 256
|
||||||
|
private val MODE_SAVE_DELETE = 512
|
||||||
|
|
||||||
private lateinit var loadables: SavegameCollectionPair
|
private lateinit var loadables: SavegameCollectionPair
|
||||||
|
|
||||||
@@ -229,7 +230,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
MODE_SAVE_DAMAGED
|
MODE_SAVE_DAMAGED
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val (p, w) = loadables.getManualSave()!!
|
val (p, w) = loadables.getLoadableSave()!!
|
||||||
UILoadGovernor.playerDisk = p; UILoadGovernor.worldDisk = w
|
UILoadGovernor.playerDisk = p; UILoadGovernor.worldDisk = w
|
||||||
|
|
||||||
if (loadables.newerSaveIsDamaged) {
|
if (loadables.newerSaveIsDamaged) {
|
||||||
@@ -329,10 +330,25 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
private var oldMode = -1
|
private var oldMode = -1
|
||||||
|
|
||||||
private val mode1Node = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse()
|
private val mode1Node = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse()
|
||||||
private val mode2Node = Yaml(UITitleRemoConYaml.injectedMenuSingleWorldSel).parse()
|
// private val mode2Node = Yaml(UITitleRemoConYaml.injectedMenuSingleWorldSel).parse()
|
||||||
|
|
||||||
private val menus = listOf(mode1Node, mode2Node)
|
// private val menus = listOf(mode1Node, mode2Node)
|
||||||
private val titles = listOf("CONTEXT_CHARACTER", "MENU_LABEL_WORLD")
|
|
||||||
|
private val deleteCharacterButton = UIItemTextButton(
|
||||||
|
this, "CONTEXT_CHARACTER_DELETE",
|
||||||
|
UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 11,
|
||||||
|
UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 3 + 16,
|
||||||
|
remoCon.width + UIRemoCon.UIRemoConElement.paddingLeft,
|
||||||
|
true,
|
||||||
|
inactiveCol = Toolkit.Theme.COL_RED,
|
||||||
|
activeCol = Toolkit.Theme.COL_REDD,
|
||||||
|
hitboxSize = UIRemoCon.UIRemoConElement.lineHeight - 2
|
||||||
|
).also {
|
||||||
|
it.clickOnceListener = { _,_ ->
|
||||||
|
mode = MODE_SAVE_DELETE
|
||||||
|
it.highlighted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// this UI will NOT persist; the parent of the mode1Node must be set using an absolute value (e.g. treeRoot, not remoCon.currentRemoConContents)
|
// this UI will NOT persist; the parent of the mode1Node must be set using an absolute value (e.g. treeRoot, not remoCon.currentRemoConContents)
|
||||||
@@ -341,22 +357,25 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
//printStackTrace(this)
|
//printStackTrace(this)
|
||||||
|
|
||||||
mode1Node.parent = remoCon.treeRoot
|
mode1Node.parent = remoCon.treeRoot
|
||||||
mode2Node.parent = mode1Node
|
// mode2Node.parent = mode1Node
|
||||||
|
|
||||||
mode1Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame"
|
mode1Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame"
|
||||||
mode2Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame"
|
// mode2Node.data = "MENU_MODE_SINGLEPLAYER : net.torvald.terrarum.modulebasegame.ui.UILoadSavegame"
|
||||||
|
|
||||||
// printdbg(this, "mode1Node parent: ${mode1Node.parent?.data}") // will be 'null' because the parent is the root node
|
// printdbg(this, "mode1Node parent: ${mode1Node.parent?.data}") // will be 'null' because the parent is the root node
|
||||||
// printdbg(this, "mode1Node data: ${mode1Node.data}")
|
// printdbg(this, "mode1Node data: ${mode1Node.data}")
|
||||||
// printdbg(this, "mode2Node data: ${mode2Node.data}")
|
// printdbg(this, "mode2Node data: ${mode2Node.data}")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun modeChangedHandler(mode: Int) {
|
private fun modeChangedHandler(mode: Int) {
|
||||||
remoCon.setNewRemoConContents(menus[mode])
|
printdbg(this, "Change mode: $oldMode -> $mode")
|
||||||
|
// remoCon.setNewRemoConContents(menus[mode])
|
||||||
|
remoCon.setNewRemoConContents(mode1Node)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateUI(delta: Float) {
|
override fun updateUI(delta: Float) {
|
||||||
if (mode == MODE_SELECT) {
|
if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||||
|
|
||||||
if (oldMode != mode) {
|
if (oldMode != mode) {
|
||||||
modeChangedHandler(mode)
|
modeChangedHandler(mode)
|
||||||
@@ -413,7 +432,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk)
|
LoadSavegame(UILoadGovernor.playerDisk!!, UILoadGovernor.worldDisk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mode == MODE_SELECT) {
|
else if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||||
batch.end()
|
batch.end()
|
||||||
|
|
||||||
val cells = getCells()
|
val cells = getCells()
|
||||||
@@ -474,11 +493,6 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
val saveTex = TextureRegion(Texture(savePixmap)); saveTex.flip(false, true)
|
val saveTex = TextureRegion(Texture(savePixmap)); saveTex.flip(false, true)
|
||||||
batch.inUse {
|
batch.inUse {
|
||||||
batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f)
|
batch.draw(saveTex, (width - uiWidth - 10) / 2f, 0f)
|
||||||
|
|
||||||
// draw texts
|
|
||||||
val loadGameTitleStr = Lang[titles[mode]]// + "$EMDASH$hash"
|
|
||||||
// "Game Load"
|
|
||||||
App.fontUITitle.draw(batch, loadGameTitleStr, (width - App.fontUITitle.getWidth(loadGameTitleStr)).div(2).toFloat(), titleTextPosY.toFloat())
|
|
||||||
// Control help
|
// Control help
|
||||||
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
|
App.fontGame.draw(batch, controlHelp, uiX.toFloat(), controlHelperY.toFloat())
|
||||||
}
|
}
|
||||||
@@ -506,11 +520,16 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
loadAutoThumbButton.render(batch, camera)
|
loadAutoThumbButton.render(batch, camera)
|
||||||
loadManualThumbButton.render(batch, camera)
|
loadManualThumbButton.render(batch, camera)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||||
|
deleteCharacterButton.render(batch, camera)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun keyDown(keycode: Int): Boolean {
|
override fun keyDown(keycode: Int): Boolean {
|
||||||
if (this.isVisible) {
|
if (this.isVisible && (mode == MODE_SELECT || mode == MODE_SAVE_DELETE)) {
|
||||||
val cells = getCells()
|
val cells = getCells()
|
||||||
|
|
||||||
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) {
|
if ((keycode == Input.Keys.UP || keycode == App.getConfigInt("control_key_up")) && scrollTarget > 0) {
|
||||||
@@ -531,6 +550,7 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
if (mode == MODE_SELECT) getCells().forEach { it.touchDown(screenX, screenY, pointer, button) }
|
if (mode == MODE_SELECT) getCells().forEach { it.touchDown(screenX, screenY, pointer, button) }
|
||||||
if (::loadAutoThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadAutoThumbButton.touchDown(screenX, screenY, pointer, button) }
|
if (::loadAutoThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadAutoThumbButton.touchDown(screenX, screenY, pointer, button) }
|
||||||
if (::loadManualThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadManualThumbButton.touchDown(screenX, screenY, pointer, button) }
|
if (::loadManualThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadManualThumbButton.touchDown(screenX, screenY, pointer, button) }
|
||||||
|
if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) deleteCharacterButton.touchDown(screenX, screenY, pointer, button)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,11 +558,12 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
|
|||||||
if (mode == MODE_SELECT) getCells().forEach { it.touchUp(screenX, screenY, pointer, button) }
|
if (mode == MODE_SELECT) getCells().forEach { it.touchUp(screenX, screenY, pointer, button) }
|
||||||
if (::loadAutoThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadAutoThumbButton.touchUp(screenX, screenY, pointer, button) }
|
if (::loadAutoThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadAutoThumbButton.touchUp(screenX, screenY, pointer, button) }
|
||||||
if (::loadManualThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadManualThumbButton.touchUp(screenX, screenY, pointer, button) }
|
if (::loadManualThumbButton.isInitialized && mode == MODE_SAVE_MULTIPLE_CHOICES) { loadManualThumbButton.touchUp(screenX, screenY, pointer, button) }
|
||||||
|
if (mode == MODE_SELECT || mode == MODE_SAVE_DELETE) deleteCharacterButton.touchDown(screenX, screenY, pointer, button)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
override fun scrolled(amountX: Float, amountY: Float): Boolean {
|
||||||
if (this.isVisible && mode == MODE_SELECT) {
|
if (this.isVisible && mode == MODE_SELECT || mode == MODE_SAVE_DELETE) {
|
||||||
val cells = getCells()
|
val cells = getCells()
|
||||||
|
|
||||||
if (amountY <= -1f && scrollTarget > 0) {
|
if (amountY <= -1f && scrollTarget > 0) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Camera
|
|||||||
import com.badlogic.gdx.graphics.Color
|
import com.badlogic.gdx.graphics.Color
|
||||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||||
import net.torvald.terrarum.App
|
import net.torvald.terrarum.App
|
||||||
|
import net.torvald.terrarum.App.printdbg
|
||||||
import net.torvald.terrarum.Second
|
import net.torvald.terrarum.Second
|
||||||
import net.torvald.terrarum.Terrarum
|
import net.torvald.terrarum.Terrarum
|
||||||
import net.torvald.terrarum.gameactors.AVKey
|
import net.torvald.terrarum.gameactors.AVKey
|
||||||
@@ -64,6 +65,7 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
|
|
||||||
|
|
||||||
val savingThread = Thread({
|
val savingThread = Thread({
|
||||||
|
printdbg(this, "Player saving thread fired")
|
||||||
|
|
||||||
disk.saveMode = 2 // auto, no quick
|
disk.saveMode = 2 // auto, no quick
|
||||||
disk.capacity = 0L
|
disk.capacity = 0L
|
||||||
@@ -79,9 +81,11 @@ class UINewCharacter(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
UILoadGovernor.playerDisk = DiskSkimmer(outFile)
|
UILoadGovernor.playerDisk = DiskSkimmer(outFile)
|
||||||
// comment above if chargen must send gamers back to the charcters list
|
// comment above if chargen must send gamers back to the charcters list
|
||||||
|
|
||||||
|
printdbg(this, "playerdisk: ${UILoadGovernor.playerDisk?.diskFile?.path}")
|
||||||
|
|
||||||
}, "TerrarumBasegameNewCharcterSaveThread")
|
}, "TerrarumBasegameNewCharcterSaveThread")
|
||||||
|
|
||||||
|
// savingThread.start()
|
||||||
// savingThread.join()
|
// savingThread.join()
|
||||||
|
|
||||||
remoCon.openUI(UINewWorld(remoCon, savingThread)) // let UINewWorld handle the character file generation
|
remoCon.openUI(UINewWorld(remoCon, savingThread)) // let UINewWorld handle the character file generation
|
||||||
|
|||||||
@@ -95,10 +95,11 @@ class UINewWorld(val remoCon: UIRemoCon) : UICanvas() {
|
|||||||
goButton.clickOnceListener = { _, _ ->
|
goButton.clickOnceListener = { _, _ ->
|
||||||
|
|
||||||
// after the save is complete, proceed to new world generation
|
// after the save is complete, proceed to new world generation
|
||||||
|
newPlayerCreationThread.start()
|
||||||
newPlayerCreationThread.join()
|
newPlayerCreationThread.join()
|
||||||
|
|
||||||
|
|
||||||
// printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}")
|
printdbg(this, "generate! Size=${sizeSelector.selection}, Name=${nameInput.getTextOrPlaceholder()}, Seed=${seedInput.getTextOrPlaceholder()}")
|
||||||
|
|
||||||
val ingame = TerrarumIngame(App.batch)
|
val ingame = TerrarumIngame(App.batch)
|
||||||
val player = ReadActor.invoke(UILoadGovernor.playerDisk!!, ByteArray64Reader(UILoadGovernor.playerDisk!!.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer
|
val player = ReadActor.invoke(UILoadGovernor.playerDisk!!, ByteArray64Reader(UILoadGovernor.playerDisk!!.getFile(SAVEGAMEINFO)!!.bytes, Common.CHARSET)) as IngamePlayer
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ object Toolkit : Disposable {
|
|||||||
val COL_SELECTED = Color(0x00f8ff_ff) // cyan, HIGHLY SATURATED
|
val COL_SELECTED = Color(0x00f8ff_ff) // cyan, HIGHLY SATURATED
|
||||||
val COL_MOUSE_UP = Color(0xfff066_ff.toInt()) // yellow (all yellows are of low saturation according to the colour science)
|
val COL_MOUSE_UP = Color(0xfff066_ff.toInt()) // yellow (all yellows are of low saturation according to the colour science)
|
||||||
val COL_DISABLED = Color(0xaaaaaaff.toInt())
|
val COL_DISABLED = Color(0xaaaaaaff.toInt())
|
||||||
|
val COL_RED = Color(0xff8888ff.toInt())
|
||||||
|
val COL_REDD = Color(0xff4448ff.toInt())
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Try this for alt colour set:
|
Try this for alt colour set:
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class UIAutosaveNotifier : UICanvas() {
|
|||||||
private var errored = false
|
private var errored = false
|
||||||
|
|
||||||
private var normalCol = Color.WHITE
|
private var normalCol = Color.WHITE
|
||||||
private var errorCol = Color(0xFF8888FF.toInt())
|
private var errorCol = Toolkit.Theme.COL_RED
|
||||||
|
|
||||||
override fun updateUI(delta: Float) {
|
override fun updateUI(delta: Float) {
|
||||||
spinnerTimer += delta
|
spinnerTimer += delta
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class UIItemTextLineInput(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val TEXTINPUT_COL_TEXT = Color.WHITE
|
val TEXTINPUT_COL_TEXT = Color.WHITE
|
||||||
val TEXTINPUT_COL_TEXT_NOMORE = Color(0xFF8888FF.toInt())
|
val TEXTINPUT_COL_TEXT_NOMORE = Toolkit.Theme.COL_RED
|
||||||
val TEXTINPUT_COL_TEXT_DISABLED = Toolkit.Theme.COL_DISABLED
|
val TEXTINPUT_COL_TEXT_DISABLED = Toolkit.Theme.COL_DISABLED
|
||||||
val TEXTINPUT_COL_BACKGROUND = Toolkit.Theme.COL_CELL_FILL
|
val TEXTINPUT_COL_BACKGROUND = Toolkit.Theme.COL_CELL_FILL
|
||||||
val TEXTINPUT_COL_BACKGROUND2 = Toolkit.Theme.COL_CELL_FILL.cpy()
|
val TEXTINPUT_COL_BACKGROUND2 = Toolkit.Theme.COL_CELL_FILL.cpy()
|
||||||
@@ -155,7 +155,7 @@ class UIItemTextLineInput(
|
|||||||
|
|
||||||
private var textColours = arrayOf(
|
private var textColours = arrayOf(
|
||||||
Color.WHITE,
|
Color.WHITE,
|
||||||
Color(0xff8888ff.toInt()),
|
Toolkit.Theme.COL_RED,
|
||||||
Color(0x888888ff.toInt())
|
Color(0x888888ff.toInt())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user