mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
176 lines
6.3 KiB
Kotlin
176 lines
6.3 KiB
Kotlin
package net.torvald.terrarum.modulebasegame
|
|
|
|
import net.torvald.terrarum.App.printdbg
|
|
import net.torvald.terrarum.BlockCodex
|
|
import net.torvald.terrarum.ItemCodex
|
|
import net.torvald.terrarum.gameactors.Actor
|
|
import net.torvald.terrarum.gameitems.isBlock
|
|
import net.torvald.terrarum.gameitems.isDynamic
|
|
import net.torvald.terrarum.gameitems.isWall
|
|
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
|
|
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
|
|
import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
|
|
import net.torvald.util.SortedArrayList
|
|
import kotlin.reflect.full.declaredFunctions
|
|
import kotlin.reflect.full.findAnnotation
|
|
|
|
/**
|
|
* @param versionAnnotation If the version of the savegame meets the condition, the filter will be run
|
|
* - `all`: applies to all versions
|
|
* - `0.3.1`: applies to any past versions up to 0.3.1
|
|
* - `0.3.1+`: applies to any future versions including 0.3.1
|
|
* - `0.3.1!`: applies to verson 0.3.1 only
|
|
* - `0.3.1-0.3.4`: applies to version 0.3.1 to 0.3.4
|
|
* - `0.3.1-0.3.4;0.4.3-0.4.5`: applies to (0.3.1 to 0.3.4) and (0.4.3-0.4.5)
|
|
* - `0.3.1-0.3.4;0.5.0+`: applies to (0.3.1 to 0.3.4) and (0.5.0 and onward)
|
|
*
|
|
* Following syntaxes are considered as undefined behaviours:
|
|
* - `0.3.1-0.3.4;0.5.0`
|
|
*/
|
|
annotation class AppliedVersion(val versionAnnotation: String)
|
|
|
|
/**
|
|
* Created by minjaesong on 2023-09-29.
|
|
*/
|
|
internal object SavegameMigrator {
|
|
|
|
private fun Char.isNotAsciiDigit() = this.code !in 0x30..0x39
|
|
|
|
private fun toVersionIndex(a_dot_b_dot_c: String): Long {
|
|
val abc = a_dot_b_dot_c.split('.').map { it.toLong() }
|
|
if (abc.size != 3) throw IllegalArgumentException("Parse error: $a_dot_b_dot_c")
|
|
|
|
return abc[0].shl(48) or abc[1].shl(24) or abc[2]
|
|
}
|
|
|
|
private fun annotationMatches(fileVersion: Long, annotation: AppliedVersion?): Boolean {
|
|
if (annotation == null) return false
|
|
if (annotation.versionAnnotation.equals("all")) return true
|
|
|
|
var matched = false
|
|
|
|
annotation.versionAnnotation.split(";").forEach { query ->
|
|
// is range?
|
|
if (query.contains('-')) {
|
|
val from0 = query.substringBefore('-')
|
|
val to0 = query.substringAfter('-')
|
|
if (from0.last().isNotAsciiDigit() || to0.last().isNotAsciiDigit())
|
|
throw IllegalArgumentException("Illegal query '$query'")
|
|
|
|
val from = toVersionIndex(from0)
|
|
val to = toVersionIndex(to0)
|
|
|
|
if (from > to) throw IllegalArgumentException("Illegal query '$query'")
|
|
|
|
matched = matched or (fileVersion in from..to)
|
|
}
|
|
else if (query.endsWith('+') || query.endsWith('!')) {
|
|
val operator = query.last()
|
|
val specVersion = toVersionIndex(query.substringBefore(operator))
|
|
|
|
matched = matched or when (operator) {
|
|
'+' -> (fileVersion >= specVersion)
|
|
'!' -> (fileVersion == specVersion)
|
|
else -> throw IllegalArgumentException("Unknown operator '$operator' for query '$query'")
|
|
}
|
|
}
|
|
else {
|
|
matched = matched or (fileVersion <= toVersionIndex(query))
|
|
}
|
|
}
|
|
|
|
return matched
|
|
}
|
|
|
|
operator fun invoke(worldVersion: Long, playerVersion: Long, actors0: SortedArrayList<Actor>) {
|
|
if (worldVersion == playerVersion) {
|
|
this::class.declaredFunctions.filter {
|
|
annotationMatches(worldVersion, it.findAnnotation())
|
|
}.sortedBy { it.name }.forEach { func ->
|
|
printdbg(this, func.toString())
|
|
actors0.forEach { actor -> func.call(this, actor) }
|
|
}
|
|
}
|
|
else {
|
|
val nonPlayers = ArrayList<Actor>()
|
|
val players = ArrayList<IngamePlayer>()
|
|
for (actor in actors0) {
|
|
if (actor is IngamePlayer) players.add(actor)
|
|
else nonPlayers.add(actor)
|
|
}
|
|
|
|
this::class.declaredFunctions.filter {
|
|
annotationMatches(worldVersion, it.findAnnotation())
|
|
}.forEach { func ->
|
|
nonPlayers.forEach { actor -> func.call(this, actor) }
|
|
}
|
|
|
|
this::class.declaredFunctions.filter {
|
|
annotationMatches(playerVersion, it.findAnnotation())
|
|
}.forEach { func ->
|
|
players.forEach { player -> func.call(this, player) }
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/***********************************/
|
|
/* INSERT MIGRATION FUNCTIONS HERE */
|
|
/***********************************/
|
|
|
|
|
|
@AppliedVersion("all")
|
|
fun b_mergeUnlitBlocksAndFillInInventory(actor: Actor) {
|
|
if (ItemCodex.isEmpty()) throw Error("ItemCodex is empty")
|
|
|
|
if (actor is Pocketed) {
|
|
val oldItemEquipped = actor.inventory.itemEquipped.copyOf()
|
|
val oldQuickSlot = actor.inventory.quickSlot.copyOf()
|
|
val oldItems = actor.inventory.clear()
|
|
|
|
|
|
oldItems.forEach { (itm, qty) ->
|
|
if (itm.isBlock() || itm.isWall()) {
|
|
if (!BlockCodex[itm].hasTag("AIR") && !BlockCodex[itm].isActorBlock) {
|
|
actor.inventory.add(itm, qty)
|
|
}
|
|
}
|
|
else {
|
|
actor.inventory.add(itm, qty)
|
|
}
|
|
}
|
|
|
|
oldItemEquipped.forEachIndexed { index, id0 ->
|
|
if (id0?.isDynamic() == true)
|
|
actor.inventory.itemEquipped[index] = id0
|
|
else {
|
|
val id = FixtureInventory.filterItem(ItemCodex[id0])
|
|
actor.inventory.itemEquipped[index] = id?.dynamicID
|
|
}
|
|
}
|
|
|
|
oldQuickSlot.forEachIndexed { index, id0 ->
|
|
if (id0?.isDynamic() == true)
|
|
actor.inventory.quickSlot[index] = id0
|
|
else {
|
|
val id = FixtureInventory.filterItem(ItemCodex[id0])
|
|
actor.inventory.quickSlot[index] = id?.dynamicID
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@AppliedVersion("0.4.0")
|
|
fun a_updatePlayerEncumbrance(actor: Actor) {
|
|
if (actor is IngamePlayer && actor.actorValue.getAsInt("encumbrance") == 1000) {
|
|
actor.actorValue.set("encumbrance", 50000)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
} |