refactoring around

This commit is contained in:
minjaesong
2021-08-06 13:34:17 +09:00
parent 9578488962
commit edc3d53f4e
15 changed files with 44 additions and 244 deletions

View File

@@ -1,6 +1,7 @@
package net.torvald.terrarum.modulebasegame.gameworld
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.serialise.ReadLayerDataZip
/**
@@ -12,17 +13,8 @@ class GameWorldExtension : GameWorld {
internal constructor(worldIndex: Int, layerData: ReadLayerDataZip.LayerData, creationTIME_T: Long, lastPlayTIME_T: Long, totalPlayTime: Int) : super(worldIndex, layerData, creationTIME_T, lastPlayTIME_T, totalPlayTime)
/** Extended world time */
val worldTime: WorldTime
val economy = GameEconomy()
override var TIME_T: Long
get() = worldTime.TIME_T
set(value) { worldTime.TIME_T = value }
override var dayLength: Int
get() = WorldTime.DAY_LENGTH
set(value) { throw UnsupportedOperationException() }
// delegated properties //
/*val layerWall: MapLayer; get() = baseworld.layerWall
@@ -44,14 +36,6 @@ class GameWorldExtension : GameWorld {
val damageDataArray: ByteArray; get() = baseworld.damageDataArray*/
init {
worldTime = WorldTime( // Year EPOCH (125), Month 1, Day 1 is implied
7 * WorldTime.HOUR_SEC +
30L * WorldTime.MINUTE_SEC
)
}
fun updateWorldTime(delta: Float) {
worldTime.update(delta)
}
}

View File

@@ -1,480 +0,0 @@
package net.torvald.terrarum.modulebasegame.gameworld
import com.badlogic.gdx.Input
import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.gameitem.ItemID
import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import org.khelekore.prtree.*
import kotlin.math.roundToInt
/**
* Created by minjaesong on 2016-08-03.
*/
object WorldSimulator {
private val DEBUG_STEPPING_MODE = false // use period key
// FLUID-RELATED STUFFS //
/**
* In tiles;
* square width/height = field * 2
*/
// TODO: increase the radius and then MULTITHREAD
const val FLUID_UPDATING_SQUARE_RADIUS = 80 // larger value will have dramatic impact on performance
const private val DOUBLE_RADIUS = FLUID_UPDATING_SQUARE_RADIUS * 2
// maps are separated as old-new for obvious reason, also it'll allow concurrent modification
private val fluidMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
private val fluidTypeMap = Array(DOUBLE_RADIUS, { Array<FluidType>(DOUBLE_RADIUS) { Fluid.NULL } })
private val fluidNewMap = Array(DOUBLE_RADIUS, { FloatArray(DOUBLE_RADIUS) })
private val fluidNewTypeMap = Array(DOUBLE_RADIUS, { Array<FluidType>(DOUBLE_RADIUS) { Fluid.NULL } })
const val FLUID_MAX_MASS = 1f // The normal, un-pressurized mass of a full water cell
const val FLUID_MAX_COMP = 0.02f // How much excess water a cell can store, compared to the cell above it. A tile of fluid can contain more than MaxMass water.
const val FLUID_MIN_MASS = 0.0001f //Ignore cells that are almost dry
const val minFlow = 0.01f
const val maxSpeed = 1f // max units of water moved out of one block to another, per timestamp
// END OF FLUID-RELATED STUFFS
/** Top-left point */
var updateXFrom = 0
/** Bottom-right point */
var updateXTo = 0
/** Top-left point */
var updateYFrom = 0
/** Bottom-right point */
var updateYTo = 0
private val ingame: IngameInstance
get() = Terrarum.ingame!!
private val world: GameWorld
get() = ingame.world
private lateinit var actorsRTree: PRTree<ActorWithBody>
fun resetForThisFrame() {
}
/** Must be called BEFORE the actors update -- actors depend on the R-Tree for various things */
operator fun invoke(player: ActorHumanoid?, delta: Float) {
// build the r-tree that will be used during a single frame of updating
actorsRTree = PRTree(actorMBRConverter, 24)
actorsRTree.load(ingame.actorContainerActive.filterIsInstance<ActorWithBody>())
//printdbg(this, "============================")
if (player != null) {
updateXFrom = player.hitbox.centeredX.div(TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundToInt()
updateYFrom = player.hitbox.centeredY.div(TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundToInt()
updateXTo = updateXFrom + DOUBLE_RADIUS
updateYTo = updateYFrom + DOUBLE_RADIUS
}
//moveFluids(delta)
displaceFallables(delta)
simulateWires(delta)
//printdbg(this, "============================")
}
/**
* @return list of actors under the bounding box given, list may be empty if no actor is under the point.
*/
fun getActorsAt(startPoint: Point2d, endPoint: Point2d): List<ActorWithBody> {
val outList = ArrayList<ActorWithBody>()
actorsRTree.find(startPoint.x, startPoint.y, endPoint.x, endPoint.y, outList)
return outList
}
fun getActorsAt(worldX: Double, worldY: Double): List<ActorWithBody> {
val outList = ArrayList<ActorWithBody>()
actorsRTree.find(worldX, worldY, worldX + 1.0, worldY + 1.0, outList)
return outList
}
/** Will use centre point of the actors
* @return List of DistanceResult, list may be empty */
fun findKNearestActors(from: ActorWithBody, maxHits: Int): List<DistanceResult<ActorWithBody>> {
return actorsRTree.nearestNeighbour(actorDistanceCalculator, null, maxHits, object : PointND {
override fun getDimensions(): Int = 2
override fun getOrd(axis: Int): Double = when(axis) {
0 -> from.hitbox.centeredX
1 -> from.hitbox.centeredY
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
})
}
/** Will use centre point of the actors
* @return Pair of: the actor, distance from the actor; null if none found */
fun findNearestActors(from: ActorWithBody): DistanceResult<ActorWithBody>? {
val t = findKNearestActors(from, 1)
return if (t.isNotEmpty())
t[0]
else
null
}
/**
* displace fluids. Note that the code assumes the gravity pulls things downward ONLY,
* which means you'll need to modify the code A LOT if you're going to implement zero- or
* reverse-gravity.
*
* Procedure: CP world fluidmap -> sim on fluidmap -> CP fluidmap world
* TODO multithread
*/
fun moveFluids(delta: Float) {
makeFluidMapFromWorld()
simCompression()
if (AppLoader.IS_DEVELOPMENT_BUILD) {
monitorIllegalFluidSetup() // non-air non-zero fluid is kinda inevitable
}
fluidmapToWorld()
}
fun isFlowable(type: FluidType, worldX: Int, worldY: Int): Boolean {
val fluid = world.getFluid(worldX, worldY)
val tile = world.getTileFromTerrain(worldX, worldY)
// true if target's type is the same as mine, or it's NULL (air)
return ((fluid.type sameAs type || fluid.type sameAs Fluid.NULL) && !BlockCodex[tile].isSolid)
}
/**
* displace fallable tiles. It is scanned bottom-left first. To achieve the sens ofreal
* falling, each tiles are displaced by ONLY ONE TILE below.
*/
fun displaceFallables(delta: Float) {
/*for (y in updateYFrom..updateYTo) {
for (x in updateXFrom..updateXTo) {
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
val tileBelow = world.getTileFromTerrain(x, y + 1) ?: Block.STONE
if (tile.maxSupport()) {
// displace fluid. This statement must precede isSolid()
if (tileBelow.isFluid()) {
// remove tileThis to create air pocket
world.setTileTerrain(x, y, Block.AIR)
pour(x, y, drain(x, y, tileBelow.fluidLevel().toInt()))
// place our tile
world.setTileTerrain(x, y + 1, tile)
}
else if (!tileBelow.isSolid()) {
world.setTileTerrain(x, y, Block.AIR)
world.setTileTerrain(x, y + 1, tile)
}
}
}
}*/
// displace fallables (TODO implement blocks with fallable supports e.g. scaffolding)
// only displace SINGLE BOTTOMMOST block on single X-coord (this doesn't mean they must fall only one block)
// so that the "falling" should be visible to the end user
if (!DEBUG_STEPPING_MODE || DEBUG_STEPPING_MODE && KeyToggler.isOn (Input.Keys.PERIOD)) {
for (x in updateXFrom..updateXTo) {
var fallDownCounter = 0
var fallableStackProcessed = false
// one "stack" is a contiguous fallable blocks, regardless of the actual block number
// when you are simulating the gradual falling, it is natural to process all the "stacks" at the same run,
// otherwise you'll get an artefact.
for (y in updateYTo downTo updateYFrom) {
val currentTile = world.getTileFromTerrain(x, y)
val prop = BlockCodex[currentTile]
val isAir = currentTile == Block.AIR
val support = prop.maxSupport
val isFallable = support != -1
// mark the beginnig of the new "stack"
if (fallableStackProcessed && !isFallable) {
fallableStackProcessed = false
} // do not chain with "else if"
// process the gradual falling of the selected "stack"
if (!fallableStackProcessed && fallDownCounter != 0 && isFallable) {
// replace blocks
world.setTileTerrain(x, y, Block.AIR, true)
world.setTileTerrain(x, y + fallDownCounter, currentTile, true)
fallableStackProcessed = true
}
else if (!isAir) {
fallDownCounter = 0
}
else if (!isFallable && fallDownCounter < FALLABLE_MAX_FALL_SPEED) {
fallDownCounter += 1
}
}
}
if (DEBUG_STEPPING_MODE) {
KeyToggler.forceSet(Input.Keys.PERIOD, false)
}
}
}
fun disperseHeat(delta: Float) {
}
/*
Explanation of get_stable_state_b (well, kind-of) :
if x <= 1, all water goes to the lower cell
* a = 0
* b = 1
if x > 1 & x < 2*MaxMass + MaxCompress, the lower cell should have MaxMass + (upper_cell/MaxMass) * MaxCompress
b = MaxMass + (a/MaxMass)*MaxCompress
a = x - b
->
b = MaxMass + ((x - b)/MaxMass)*MaxCompress ->
b = MaxMass + (x*MaxCompress - b*MaxCompress)/MaxMass
b*MaxMass = MaxMass^2 + (x*MaxCompress - b*MaxCompress)
b*(MaxMass + MaxCompress) = MaxMass*MaxMass + x*MaxCompress
* b = (MaxMass*MaxMass + x*MaxCompress)/(MaxMass + MaxCompress)
* a = x - b;
if x >= 2 * MaxMass + MaxCompress, the lower cell should have upper+MaxCompress
b = a + MaxCompress
a = x - b
->
b = x - b + MaxCompress ->
2b = x + MaxCompress ->
* b = (x + MaxCompress)/2
* a = x - b
*/
private fun getStableStateB(totalMass: Float): Float {
if (totalMass <= 1)
return 1f
else if (totalMass < 2f * FLUID_MAX_MASS + FLUID_MAX_COMP)
return (FLUID_MAX_MASS * FLUID_MAX_MASS + totalMass * FLUID_MAX_COMP) / (FLUID_MAX_MASS + FLUID_MAX_COMP)
else
return (totalMass + FLUID_MAX_COMP) / 2f
}
private fun simCompression() {
// before data: fluidMap/fluidTypeMap
// after data: fluidNewMap/fluidNewTypeMap
// FIXME water doesn't disappear when they should
// FIXME >as it turns out, fluid FUCKING MULTIPLIES themselves (wut D:)
for (y in 1 until fluidMap.size - 1) {
for (x in 1 until fluidMap[0].size - 1) {
val worldX = x + updateXFrom
val worldY = y + updateYFrom
val remainingType = fluidTypeMap[y][x]
// check solidity
if (!isFlowable(remainingType, worldX, worldY)) continue
// check if the fluid is a same kind
//if (!isFlowable(type, worldX, worldY))) continue
// Custom push-only flow
var flow = 0f
var remainingMass = fluidMap[y][x]
//val remainingType = fluidTypeMap[y][x]
if (remainingMass <= 0) continue
// The block below this one
if (isFlowable(remainingType, worldX, worldY + 1)) {
flow = getStableStateB(remainingMass + fluidMap[y + 1][x]) - fluidMap[y + 1][x]
if (flow > minFlow) {
flow *= 0.5f // leads to smoother flow
}
flow = flow.coerceIn(0f, minOf(maxSpeed, remainingMass))
fluidNewMap[y][x] -= flow
fluidNewMap[y + 1][x] += flow
fluidNewTypeMap[y + 1][x] = remainingType
remainingMass -= flow
}
if (remainingMass <= 0) continue
// Left
if (isFlowable(remainingType, worldX - 1, worldY)) {
// Equalise the amount fo water in this block and its neighbour
flow = (fluidMap[y][x] - fluidMap[y][x - 1]) / 4f
if (flow > minFlow) {
flow *= 0.5f
}
flow = flow.coerceIn(0f, remainingMass)
fluidNewMap[y][x] -= flow
fluidNewMap[y][x - 1] += flow
fluidNewTypeMap[y][x - 1] = remainingType
remainingMass -= flow
}
if (remainingMass <= 0) continue
// Right
if (isFlowable(remainingType, worldX + 1, worldY)) {
// Equalise the amount fo water in this block and its neighbour
flow = (fluidMap[y][x] - fluidMap[y][x + 1]) / 4f
if (flow > minFlow) {
flow *= 0.5f
}
flow = flow.coerceIn(0f, remainingMass)
fluidNewMap[y][x] -= flow
fluidNewMap[y][x + 1] += flow
fluidNewTypeMap[y][x + 1] = remainingType
remainingMass -= flow
}
if (remainingMass <= 0) continue
// Up; only compressed water flows upwards
if (isFlowable(remainingType, worldX, worldY - 1)) {
flow = remainingMass - getStableStateB(remainingMass + fluidMap[y - 1][x])
if (flow > minFlow) {
flow *= 0.5f
}
flow = flow.coerceIn(0f, minOf(maxSpeed, remainingMass))
fluidNewMap[y][x] -= flow
fluidNewMap[y - 1][x] += flow
fluidNewTypeMap[y - 1][x] = remainingType
remainingMass -= flow
}
}
}
}
private val FALLABLE_MAX_FALL_SPEED = 2
private fun monitorIllegalFluidSetup() {
for (y in fluidMap.indices) {
for (x in fluidMap[0].indices) {
val fluidData = world.getFluid(x + updateXFrom, y + updateYFrom)
if (fluidData.amount < 0f) {
throw InternalError("Negative amount of fluid at (${x + updateXFrom},${y + updateYFrom}): $fluidData")
}
}
}
}
private fun makeFluidMapFromWorld() {
//printdbg(this, "Scan area: ($updateXFrom,$updateYFrom)..(${updateXFrom + fluidMap[0].size},${updateYFrom + fluidMap.size})")
for (y in fluidMap.indices) {
for (x in fluidMap[0].indices) {
val fluidData = world.getFluid(x + updateXFrom, y + updateYFrom)
fluidMap[y][x] = fluidData.amount
fluidTypeMap[y][x] = fluidData.type
fluidNewMap[y][x] = fluidData.amount
fluidNewTypeMap[y][x] = fluidData.type
/*if (x + updateXFrom == 60 && y + updateYFrom == 256) {
printdbg(this, "making array amount ${fluidData.amount} for (60,256)")
}*/
}
}
}
private fun fluidmapToWorld() {
for (y in fluidMap.indices) {
for (x in fluidMap[0].indices) {
world.setFluid(x + updateXFrom, y + updateYFrom, fluidNewTypeMap[y][x], fluidNewMap[y][x])
}
}
}
fun ItemID.isFallable() = BlockCodex[this].maxSupport
private val actorMBRConverter = object : MBRConverter<ActorWithBody> {
override fun getDimensions(): Int = 2
override fun getMin(axis: Int, t: ActorWithBody): Double =
when (axis) {
0 -> t.hitbox.startX
1 -> t.hitbox.startY
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
override fun getMax(axis: Int, t: ActorWithBody): Double =
when (axis) {
0 -> t.hitbox.endX
1 -> t.hitbox.endY
else -> throw IllegalArgumentException("nonexistent axis $axis for ${dimensions}-dimensional object")
}
}
// simple euclidean norm, squared
private val actorDistanceCalculator = DistanceCalculator<ActorWithBody> { t: ActorWithBody, p: PointND ->
val dist1 = (p.getOrd(0) - t.hitbox.centeredX).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
// ROUNDWORLD implementation
val dist2 = (p.getOrd(0) - (t.hitbox.centeredX - world.width * TILE_SIZE)).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
val dist3 = (p.getOrd(0) - (t.hitbox.centeredX + world.width * TILE_SIZE)).sqr() + (p.getOrd(1) - t.hitbox.centeredY).sqr()
minOf(dist1, minOf(dist2, dist3))
}
private fun simulateWires(delta: Float) {
}
private enum class WireConStatus { THRU, END, BRANCH }
private val wireConToStatus = arrayOf(
WireConStatus.END, // 0000
WireConStatus.END, // 0001
WireConStatus.END, // 0010
WireConStatus.THRU,// 0011
WireConStatus.END, // 0100
WireConStatus.THRU,// 0101
WireConStatus.THRU,// 0110
WireConStatus.BRANCH,// 0111
WireConStatus.END, // 1000
WireConStatus.THRU,// 1001
WireConStatus.THRU,// 1010
WireConStatus.BRANCH,// 1011
WireConStatus.THRU,// 1100
WireConStatus.BRANCH,// 1101
WireConStatus.BRANCH,// 1110
WireConStatus.BRANCH // 1111
)
data class wireGraphBranch(
val x: Int,
val y: Int,
val con: Byte
)
}

View File

@@ -1,222 +0,0 @@
package net.torvald.terrarum.modulebasegame.gameworld
import net.torvald.terrarum.gameworld.fmod
/**
* Please also see:
* https://en.wikipedia.org/wiki/World_Calendar
*
* There is no AM/PM concept, 24-hour clock is forced; no leap years.
* An ingame day should last 22 real-life minutes.
*
* ## The Yearly Calendar
*
* A calendar tailored to this very game. A year is consisted of 4 seasons (month),
* and each season last fixed length of 30 days, leap years does not occur.
*
* =========================
* |Mo|Ty|Mi|To|Fr|La|Su|Ve|
* |--|--|--|--|--|--|--|--|
* | 1| 2| 3| 4| 5| 6| 7| | <- Spring
* | 8| 9|10|11|12|13|14| |
* |15|16|17|18|19|20|21| |
* |22|23|24|25|26|27|28| |
* |29|30| 1| 2| 3| 4| 5| | <- Summer
* | 6| 7| 8| 9|10|11|12| |
* |13|14|15|16|17|18|19| |
* |20|21|22|23|24|25|26| |
* |27|28|29|30| 1| 2| 3| | <- Autumn
* | 4| 5| 6| 7| 8| 9|10| |
* |11|12|13|14|15|16|17| |
* |18|19|20|21|22|23|24| |
* |25|26|27|28|29|30| 1| | <- Winter
* | 2| 3| 4| 5| 6| 7| 8| |
* | 9|10|11|12|13|14|15| |
* |16|17|18|19|20|21|22| |
* |23|24|25|26|27|28|29|30|
* =========================
*
* - A year is 120 days, 8th day of the week (Verddag, Winter 30th) does occur as in The World calendar.
* - Starting day of the week is monday (Mondag).
* - Spring 1st is the New Year holiday, Winter 30th is the New Year's Eve holiday.
* - Human-readable date format is always Year-MonthName-Date, no matter where you (the real-life you) come from.
* For number-only format, months are enumerated from 1.
* (Spring-1, Summer-2, Autumn-3, Winter-4) E.g. 0125-Wint-07, or 0125-04-07. For more details, please refer to the
* internal functions `getFormattedTime()`, `getShortTime()`, and `getFilenameTime()`
* - Preferred computerised date format is YearMonthDate. E.g. 01250407
* - Rest of the format (e.g. time intervals) follows ISO 8601 standard.
*
* (Check please:)
* - Equinox/Solstice always occur on 21st day of the month
*
*
* Created by minjaesong on 2016-01-24.
*/
class WorldTime(initTime: Long = 0L) {
/** It is not recommended to directly modify the TIME_T. Use provided methods instead. */
var TIME_T = 0L // Epoch: Year 125 Spring 1st, 0h00:00 (Mondag) // 125-01-01
init {
TIME_T = initTime
}
inline val seconds: Int // 0 - 59
get() = TIME_T.toPositiveInt() % MINUTE_SEC
inline val minutes: Int // 0 - 59
get() = TIME_T.div(MINUTE_SEC).abs().toInt() % HOUR_MIN
inline val hours: Int // 0 - 21
get() = TIME_T.div(HOUR_SEC).abs().toInt() % HOURS_PER_DAY
// The World Calendar implementation
/*inline val yearlyDays: Int // 0 - 364
get() = (TIME_T.toPositiveInt().div(DAY_LENGTH) % YEAR_DAYS)
inline val days: Int // 1 - 31
get() = quarterlyDays + 1 -
if (quarterlyMonthOffset == 0) 0
else if (quarterlyMonthOffset == 1) 31
else 61
inline val months: Int // 1 - 12
get() = if (yearlyDays == YEAR_DAYS - 1) 12 else
quarter * 3 + 1 +
if (quarterlyDays < 31) 0
else if (quarterlyDays < 61) 1
else 2
inline val years: Int
get() = TIME_T.div(YEAR_DAYS * DAY_LENGTH).abs().toInt() + EPOCH_YEAR
inline val quarter: Int // 0 - 3
get() = if (yearlyDays == YEAR_DAYS - 1) 3 else yearlyDays / QUARTER_LENGTH
inline val quarterlyDays: Int // 0 - 90(91)
get() = if (yearlyDays == YEAR_DAYS - 1) 91 else (yearlyDays % QUARTER_LENGTH)
inline val quarterlyMonthOffset: Int // 0 - 2
get() = months.minus(1) % 3*/
// these functions won't need inlining for performance
val ordinalDay: Int // 0 - 119
get() = (TIME_T.div(DAY_LENGTH) fmod YEAR_DAYS.toLong()).toInt()
val calendarDay: Int // 1 - 30 fixed
get() = (ordinalDay % MONTH_LENGTH) + 1
val calendarMonth: Int // 1 - 4
get() = (ordinalDay / MONTH_LENGTH) + 1
val years: Int
get() = TIME_T.div(YEAR_DAYS * DAY_LENGTH).abs().toInt() + EPOCH_YEAR
val quarter = calendarMonth - 1 // 0 - 3
val dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7)
get() = if (ordinalDay == YEAR_DAYS - 1) 7 else ordinalDay % 7
var timeDelta: Int = 1
set(value) {
field = if (value < 0) 0 else value
}
inline val moonPhase: Double
get() = (TIME_T.plus(1700000L) % LUNAR_CYCLE).toDouble() / LUNAR_CYCLE
@Transient private var realSecAcc: Double = 0.0
@Transient private val REAL_SEC_TO_GAME_SECS = 1.0 / GAME_MIN_TO_REAL_SEC // how slow is real-life clock (second-wise) relative to the ingame one
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
"Mondag", "Tysdag", "Midtveke" //middle-week
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd'
)
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
// dwarven calendar of 12 monthes
/*val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
)
val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema",
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")*/
val MONTH_NAMES = arrayOf("Spring", "Summer", "Autumn", "Winter")
val MONTH_NAMES_SHORT = arrayOf("Spri", "Summ", "Autm", "Wint")
companion object {
/** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */
val DAY_LENGTH = 86400 //must be the multiple of 3600
val HOUR_SEC: Int = 3600
val MINUTE_SEC: Int = 60
val HOUR_MIN: Int = 60
val GAME_MIN_TO_REAL_SEC: Double = 720.0 / 11.0
val HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC
val YEAR_DAYS: Int = 120
val MONTH_LENGTH = 30 // ingame calendar specific
val EPOCH_YEAR = 125
/**
* Parse a time in the format of "8h30" (hour and minute) or "39882" (second) and return a time of day, in seconds
*/
fun parseTime(s: String): Int =
if (s.length >= 4 && s.contains('h')) {
s.toLowerCase().substringBefore('h').toInt() * HOUR_SEC +
s.toLowerCase().substringAfter('h').toInt() * MINUTE_SEC
}
else if (s.endsWith("h", true)) {
s.toLowerCase().substring(0, s.length - 1).toInt() * HOUR_SEC
}
else {
s.toInt()
}
val LUNAR_CYCLE: Int = 29 * DAY_LENGTH + 12 * HOUR_SEC + 44 * MINUTE_SEC + 3 // 29 days, 12 hours, 44 minutes, and 3 seconds in-game calendar
}
fun update(delta: Float) {
//time
realSecAcc += delta
if (realSecAcc >= REAL_SEC_TO_GAME_SECS) {
while (realSecAcc >= REAL_SEC_TO_GAME_SECS) {
realSecAcc -= REAL_SEC_TO_GAME_SECS
TIME_T += timeDelta
}
}
}
val todaySeconds: Int
get() = TIME_T.toPositiveInt() % DAY_LENGTH
fun setTimeOfToday(t: Int) {
TIME_T = TIME_T - todaySeconds + t
}
fun addTime(t: Int) {
TIME_T += t
}
val dayName: String
get() = DAY_NAMES[dayOfWeek]
fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
fun Long.abs() = Math.abs(this)
/** Format: "%A, %Y %B %d %X" */
fun getFormattedTime() = "${getDayNameShort()}, " +
"$years " +
"${getMonthNameFull()} " +
"$calendarDay " +
"${String.format("%02d", hours)}:" +
"${String.format("%02d", minutes)}:" +
"${String.format("%02d", seconds)}"
fun getShortTime() = "${years.toString().padStart(4, '0')}-${getMonthNameShort()}-${calendarDay.toString().padStart(2, '0')}"
fun getFilenameTime() = "${years.toString().padStart(4, '0')}${calendarMonth.toString().padStart(2, '0')}${calendarDay.toString().padStart(2, '0')}"
fun getDayNameFull() = DAY_NAMES[dayOfWeek]
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek]
fun getMonthNameFull() = MONTH_NAMES[calendarMonth - 1]
fun getMonthNameShort() = MONTH_NAMES_SHORT[calendarMonth - 1]
override fun toString() = getFormattedTime()
}

View File

@@ -1,195 +0,0 @@
package net.torvald.terrarum.modulebasegame.gameworld
/**
* The World Calendar implementation of Dwarven Calendar (we're talking about DF!)
*
* Please see:
* https://en.wikipedia.org/wiki/World_Calendar
* http://dwarffortresswiki.org/index.php/DF2014:Calendar
*
* Normal format for day is
* Tysdag 12th Granite
*
* And there is no AM/PM concept, 22-hour clock is forced.
*
* Created by minjaesong on 2016-01-24.
*/
@Deprecated("Are you even reading the name?")
class YeOldeWorldTime {
internal var seconds: Int // 0 - 59
internal var minutes: Int // 0 - 59
internal var hours: Int // 0 - 21
// days on the year
internal var yearlyDays: Int //NOT a calendar day
internal var days: Int // 1 - 31
internal var months: Int // 1 - 12
internal var years: Int // 1+
internal var dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7)
internal var timeDelta = 1
@Transient private var realMillisec: Int
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
"Mondag", "Tysdag", "Midvikdag" //From Islenska Miðvikudagur
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd'
)
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
val MONTH_NAMES = arrayOf(
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
)
val MONTH_NAMES_SHORT = arrayOf("Opal", "Obsi", "Gran", "Slat", "Fels", "Hema",
"Mala", "Gale", "Lime", "Sand", "Timb", "Moon")
@Transient val REAL_SEC_IN_MILLI = 1000
companion object {
/** Each day is 22-hour long */
val DAY_LENGTH = 79200 //must be the multiple of 3600
val HOUR_SEC: Int = 3600
val MINUTE_SEC: Int = 60
val HOUR_MIN: Int = 60
val GAME_MIN_TO_REAL_SEC: Float = 60f
val YEAR_DAYS: Int = 365
fun parseTime(s: String): Int =
if (s.length >= 4 && s.contains('h')) {
s.toLowerCase().substringBefore('h').toInt() * WorldTime.HOUR_SEC +
s.toLowerCase().substringAfter('h').toInt() * WorldTime.MINUTE_SEC
}
else if (s.endsWith("h", true)) {
s.toLowerCase().substring(0, s.length - 1).toInt() * WorldTime.HOUR_SEC
}
else {
s.toInt()
}
}
init {
// The day when the new world ever is being made.
// If we use Multiverse system (which replaces Terraria's "hack"
// as a reward rather than a cheat), time of current world's time is
// copied to the new world's. (it's Multi-nation rather than Multiverse)
seconds = 0
minutes = 30
hours = 8
yearlyDays = 73
days = 12
months = 3
years = 125
dayOfWeek = 1 // Tysdag
realMillisec = 0
}
fun update(delta: Int) {
val oldsec = seconds
//time
realMillisec += delta * timeDelta
val newsec = Math.round(GAME_MIN_TO_REAL_SEC / REAL_SEC_IN_MILLI.toFloat() * realMillisec.toFloat())
seconds = newsec
if (realMillisec >= REAL_SEC_IN_MILLI)
realMillisec -= REAL_SEC_IN_MILLI
kickVariables()
}
/**
* How much time has passed today, in seconds.
* 0 == 6 AM
* @return
*/
val elapsedSeconds: Int
get() = (HOUR_SEC * hours + MINUTE_SEC * minutes + seconds) % DAY_LENGTH
/** Sets time of this day. */
fun setTime(t: Int) {
days += t / DAY_LENGTH
hours = t / HOUR_SEC
minutes = (t - HOUR_SEC * hours) / MINUTE_SEC
seconds = t - minutes * MINUTE_SEC
yearlyDays += t / DAY_LENGTH
}
fun addTime(t: Int) {
setTime(elapsedSeconds + t)
}
fun setTimeDelta(d: Int) {
timeDelta = if (d < 0) 0 else d
}
val dayName: String
get() = DAY_NAMES[dayOfWeek]
private fun kickVariables() {
if (seconds >= MINUTE_SEC) {
seconds = 0
minutes += 1
}
if (minutes >= HOUR_MIN) {
minutes = 0
hours += 1
}
if (hours >= DAY_LENGTH / HOUR_SEC) {
hours = 0
days += 1
yearlyDays += 1
dayOfWeek += 1
}
//calendar (the world calendar)
if (dayOfWeek == 7) {
dayOfWeek = 0
}
if (months == 12 && days == 31) {
dayOfWeek = 7
}
if (months == 12 && days == 32) {
days = 1
months = 1
years++
}
else if ((months == 1 || months == 4 || months == 7 || months == 10) && days > 31) {
days = 1
months++
}
else if (days > 30) {
days = 1
months++
}
if (months > 12) {
months = 1
years++
yearlyDays = 1
}
}
/** Format: "%A %d %B %Y %X" */
fun getFormattedTime() = "${getDayNameFull()} " +
"$days " +
"${getMonthNameFull()} " +
"$years " +
"${String.format("%02d", hours)}:" +
"${String.format("%02d", minutes)}:" +
"${String.format("%02d", seconds)}"
fun getDayNameFull() = DAY_NAMES[dayOfWeek]
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek]
fun getMonthNameFull() = MONTH_NAMES[months - 1]
fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1]
}