mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-17 00:56:07 +09:00
still wip modularisation, game somehow boots
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package net.torvald.terrarum.modulebasegame.gameworld
|
||||
|
||||
import net.torvald.terrarum.gameactors.ActorID
|
||||
|
||||
/**
|
||||
* The whole world is economically isolated system. Economy will be important to make player keep playing,
|
||||
* when all the necessary contents are set and implemented to the production.
|
||||
*
|
||||
* Design goal: keep the inflation rate low, but not negative (Single market)
|
||||
* OR, give each faction (establishment) its own economy and watch them prosper/doomed (DF style)
|
||||
*
|
||||
* Created by minjaesong on 2017-04-23.
|
||||
*/
|
||||
class GameEconomy {
|
||||
|
||||
val transactionHistory = TransanctionHistory()
|
||||
|
||||
}
|
||||
|
||||
class TransanctionHistory {
|
||||
|
||||
private val entries = ArrayList<TransanctionHistory>()
|
||||
|
||||
/**
|
||||
* @param to set 0 to indicate the money was lost to void
|
||||
*/
|
||||
data class TransactionEntry(val from: ActorID, val to: ActorID, val amount: Long) {
|
||||
override fun toString() = "$from -> $to; $amount"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
package net.torvald.terrarum.modulebasegame.gameworld
|
||||
|
||||
import com.badlogic.gdx.graphics.Color
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import net.torvald.terrarum.Terrarum
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.AnyPlayer
|
||||
import net.torvald.terrarum.roundInt
|
||||
import net.torvald.terrarum.worlddrawer.BlocksDrawer
|
||||
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
|
||||
import net.torvald.terrarum.blockproperties.Block
|
||||
import net.torvald.terrarum.blockproperties.BlockCodex
|
||||
import net.torvald.terrarum.gameworld.FluidCodex
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.modulebasegame.Ingame
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2016-08-03.
|
||||
*/
|
||||
object WorldSimulator {
|
||||
/**
|
||||
* In tiles;
|
||||
* square width/height = field * 2
|
||||
*/
|
||||
const val FLUID_UPDATING_SQUARE_RADIUS = 64 // larger value will have dramatic impact on performance
|
||||
const private val DOUBLE_RADIUS = FLUID_UPDATING_SQUARE_RADIUS * 2
|
||||
|
||||
private val fluidMap = Array<ByteArray>(DOUBLE_RADIUS, { ByteArray(DOUBLE_RADIUS) })
|
||||
private val fluidTypeMap = Array<ByteArray>(DOUBLE_RADIUS, { ByteArray(DOUBLE_RADIUS) })
|
||||
|
||||
const val DISPLACE_CAP = 4
|
||||
const val FLUID_MAX = 16
|
||||
|
||||
var updateXFrom = 0
|
||||
var updateXTo = 0
|
||||
var updateYFrom = 0
|
||||
var updateYTo = 0
|
||||
|
||||
val colourNone = Color(0x808080FF.toInt())
|
||||
val colourWater = Color(0x66BBFFFF.toInt())
|
||||
|
||||
private val world = (Terrarum.ingame!! as Ingame).world
|
||||
|
||||
operator fun invoke(p: AnyPlayer?, delta: Float) {
|
||||
if (p != null) {
|
||||
updateXFrom = p.hitbox.centeredX.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
||||
updateYFrom = p.hitbox.centeredY.div(FeaturesDrawer.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
|
||||
updateXTo = updateXFrom + DOUBLE_RADIUS
|
||||
updateYTo = updateYFrom + DOUBLE_RADIUS
|
||||
}
|
||||
|
||||
moveFluids(delta)
|
||||
displaceFallables(delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
////////////////////
|
||||
// build fluidmap //
|
||||
////////////////////
|
||||
purgeFluidMap()
|
||||
worldToFluidMap(world)
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// displace fluids. Record displacements into the fluidMap //
|
||||
/////////////////////////////////////////////////////////////
|
||||
for (y in updateYFrom..updateYTo) {
|
||||
for (x in updateXFrom..updateXTo) {
|
||||
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
|
||||
val tileBottom = world.getTileFromTerrain(x, y + 1) ?: Block.STONE
|
||||
val tileLeft = world.getTileFromTerrain(x - 1, y) ?: Block.STONE
|
||||
val tileRight = world.getTileFromTerrain(x + 1, y) ?: Block.STONE
|
||||
if (tile.isFluid()) {
|
||||
|
||||
// move down if not obstructed
|
||||
/*if (!tileBottom.isSolid()) {
|
||||
val drainage = drain(world, x, y, DISPLACE_CAP)
|
||||
pour(world, x, y + 1, drainage)
|
||||
}
|
||||
// left and right both open
|
||||
else if (!tileLeft.isSolid() && !tileRight.isSolid()) {
|
||||
// half-breaker
|
||||
val moreToTheRight = HQRNG().nextBoolean()
|
||||
val displacement = drain(world, x, y, DISPLACE_CAP)
|
||||
|
||||
if (displacement.isEven()) {
|
||||
pour(world, x - 1, y, displacement shr 1)
|
||||
pour(world, x + 1, y, displacement shr 1)
|
||||
}
|
||||
else {
|
||||
pour(world, x - 1, y, (displacement shr 1) + if (moreToTheRight) 0 else 1)
|
||||
pour(world, x + 1, y, (displacement shr 1) + if (moreToTheRight) 1 else 0)
|
||||
}
|
||||
}
|
||||
// left open
|
||||
else if (!tileLeft.isSolid()) {
|
||||
val displacement = drain(world, x, y, DISPLACE_CAP)
|
||||
pour(world, x - 1, y, displacement)
|
||||
}
|
||||
// right open
|
||||
else if (!tileRight.isSolid()) {
|
||||
val displacement = drain(world, x, y, DISPLACE_CAP)
|
||||
pour(world, x + 1, y, displacement)
|
||||
}
|
||||
// nowhere open; do default (fill top)
|
||||
else {
|
||||
pour(world, x, y - 1, DISPLACE_CAP)
|
||||
}*/
|
||||
if (!tileBottom.isSolid()) {
|
||||
pour(x, y + 1, drain(x, y, FLUID_MAX))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// replace fluids in the map according to fluidMap //
|
||||
/////////////////////////////////////////////////////
|
||||
fluidMapToWorld(world)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.isFallable()) {
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun disperseHeat(delta: Float) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun drawFluidMapDebug(batch: SpriteBatch) {
|
||||
batch.color = colourWater
|
||||
|
||||
for (y in 0..fluidMap.size - 1) {
|
||||
for (x in 0..fluidMap[0].size - 1) {
|
||||
val data = fluidMap[y][x]
|
||||
if (BlocksDrawer.tileInCamera(x + updateXFrom, y + updateYFrom)) {
|
||||
if (data == 0.toByte())
|
||||
batch.color = colourNone
|
||||
else
|
||||
batch.color = colourWater
|
||||
|
||||
Terrarum.fontSmallNumbers.draw(batch,
|
||||
data.toString(),
|
||||
updateXFrom.plus(x).times(FeaturesDrawer.TILE_SIZE).toFloat()
|
||||
+ if (data < 10) 4f else 0f,
|
||||
updateYFrom.plus(y).times(FeaturesDrawer.TILE_SIZE) + 4f
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
//if (data > 0) println(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun purgeFluidMap() {
|
||||
for (y in 1..DOUBLE_RADIUS) {
|
||||
for (x in 1..DOUBLE_RADIUS) {
|
||||
fluidMap[y - 1][x - 1] = 0
|
||||
fluidTypeMap[y - 1][x - 1] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun worldToFluidMap(world: GameWorld) {
|
||||
for (y in updateYFrom..updateYTo) {
|
||||
for (x in updateXFrom..updateXTo) {
|
||||
val tile = world.getTileFromTerrain(x, y) ?: Block.STONE
|
||||
if (tile.isFluid()) {
|
||||
fluidMap[y - updateYFrom][x - updateXFrom] = tile.fluidLevel().toByte()
|
||||
fluidTypeMap[y - updateYFrom][x - updateXFrom] = tile.fluidType().toByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fluidMapToWorld(world: GameWorld) {
|
||||
for (y in 0..fluidMap.size - 1) {
|
||||
for (x in 0..fluidMap[0].size - 1) {
|
||||
placeFluid(world, updateXFrom + x, updateYFrom + y
|
||||
, FluidCodex.FLUID_WATER, fluidMap[y][x] - 1
|
||||
)
|
||||
// FIXME test code: deals with water only!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.isFluid() = BlockCodex[this].isFluid
|
||||
fun Int.isSolid() = this.fluidLevel() == FLUID_MAX || BlockCodex[this].isSolid
|
||||
//fun Int.viscosity() = BlockCodex[this].
|
||||
fun Int.fluidLevel() = if (!this.isFluid()) 0 else (this % FLUID_MAX).plus(1)
|
||||
fun Int.fluidType() = (this / 16) // 0 - 255, 255 being water, 254 being lava
|
||||
fun Int.isEven() = (this and 0x01) == 0
|
||||
fun Int.isFallable() = BlockCodex[this].isFallable
|
||||
|
||||
private fun placeFluid(world: GameWorld, x: Int, y: Int, fluidType: Byte, amount: Int) {
|
||||
if (world.layerTerrain.isInBound(x, y)) {
|
||||
if (amount > 0 && !world.getTileFromTerrain(x, y)!!.isSolid()) {
|
||||
world.setTileTerrain(x, y, fluidType, amount - 1)
|
||||
}
|
||||
else if (amount == 0 && world.getTileFromTerrain(x, y)!!.isFluid()) {
|
||||
world.setTileTerrain(x, y, Block.AIR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x and y: world tile coord
|
||||
* @return amount of fluid actually drained.
|
||||
* (intended drainage - this) will give you how much fluid is not yet drained.
|
||||
* TODO add fluidType support
|
||||
*/
|
||||
private fun drain(x: Int, y: Int, amount: Int): Int {
|
||||
val displacement = Math.min(fluidMap[y - updateYFrom][x - updateXFrom].toInt(), amount)
|
||||
|
||||
fluidMap[y - updateYFrom][x - updateXFrom] =
|
||||
(fluidMap[y - updateYFrom][x - updateXFrom] - displacement).toByte()
|
||||
|
||||
return displacement
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x and y: world tile coord
|
||||
* TODO add fluidType support
|
||||
*/
|
||||
private fun pour(x: Int, y: Int, amount: Int) {
|
||||
/**
|
||||
* @param x and y: world tile coord
|
||||
* @return spillage
|
||||
* TODO add fluidType support
|
||||
*/
|
||||
fun pourInternal(worldXpos: Int, worldYPos: Int, volume: Int): Int {
|
||||
var spil = 0
|
||||
|
||||
val addrX = worldXpos - updateXFrom
|
||||
val addrY = worldYPos - updateYFrom
|
||||
|
||||
if (addrX >= 0 && addrY >= 0 && addrX < DOUBLE_RADIUS && addrY < DOUBLE_RADIUS) {
|
||||
fluidMap[addrY][addrX] = (fluidMap[addrY][addrX] + volume).toByte()
|
||||
if (fluidMap[addrY][addrX] > FLUID_MAX) {
|
||||
spil = fluidMap[addrY][addrX] - FLUID_MAX
|
||||
fluidMap[addrY][addrX] = FLUID_MAX.toByte()
|
||||
}
|
||||
}
|
||||
|
||||
return spil
|
||||
}
|
||||
|
||||
// pour the fluid
|
||||
var spillage = pourInternal(x, y, amount)
|
||||
|
||||
if (spillage <= 0) return
|
||||
|
||||
// deal with the spillage
|
||||
|
||||
val tileUp = world.getTileFromTerrain(x - updateXFrom, y - updateYFrom - 1)
|
||||
val tileDown = world.getTileFromTerrain(x - updateXFrom, y - updateYFrom + 1)
|
||||
|
||||
// try to fill downward
|
||||
if (tileDown != null && !tileDown.isSolid()) {
|
||||
spillage = pourInternal(x, y + 1, spillage)
|
||||
}
|
||||
// else, try to fill upward. if there is no space, just discard
|
||||
if (spillage >= 0 && tileUp != null && !tileUp.isSolid()) {
|
||||
pourInternal(x, y - 1, spillage)
|
||||
}
|
||||
}
|
||||
}
|
||||
187
src/net/torvald/terrarum/modulebasegame/gameworld/WorldTime.kt
Normal file
187
src/net/torvald/terrarum/modulebasegame/gameworld/WorldTime.kt
Normal file
@@ -0,0 +1,187 @@
|
||||
package net.torvald.terrarum.modulebasegame.gameworld
|
||||
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.GameDate
|
||||
|
||||
/**
|
||||
* The World Calendar implementation of Dwarven Calendar, except:
|
||||
* - the year begins with Mondag instead of Sundag (which is ISO standard)
|
||||
* - the first month is Opal instead of Granite (to reduce confusion)
|
||||
*
|
||||
*
|
||||
* Please also see:
|
||||
* https://en.wikipedia.org/wiki/World_Calendar
|
||||
* http://dwarffortresswiki.org/index.php/DF2014:Calendar
|
||||
*
|
||||
* And there is no AM/PM concept, 22-hour clock is forced; no leap years.
|
||||
* (AM 12 is still 00h in this system, again, to reduce confusion)
|
||||
*
|
||||
*
|
||||
* Calendar
|
||||
*
|
||||
* |Mo|Ty|Mi|To|Fr|La|Su|Ve|
|
||||
* |--|--|--|--|--|--|--|--|
|
||||
* | 1| 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|31| 1| 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| 1| 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|31|
|
||||
*
|
||||
* Verddag only appears on the last day of the year (31st Moonstone)
|
||||
*
|
||||
* (Check please:)
|
||||
* - Equinox/Solstice always occur on 21st day of the month
|
||||
*
|
||||
*
|
||||
* Created by minjaesong on 2016-01-24.
|
||||
*/
|
||||
class WorldTime(initTime: Long = 0L) {
|
||||
var TIME_T = 0L // Epoch: Year 125, 1st Opal, 0h00:00 (Mondag) // 125-01-01
|
||||
private set
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
inline val dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7)
|
||||
get() = if (yearlyDays == YEAR_DAYS - 1) 7 else yearlyDays % 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 realMillisec: Double = 0.0
|
||||
@Transient private val REAL_SEC_TO_GAME_SECS = 60
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
inline val currentTimeAsGameDate: GameDate
|
||||
get() = GameDate(years, yearlyDays)
|
||||
|
||||
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 HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC
|
||||
|
||||
val YEAR_DAYS: Int = 365
|
||||
val QUARTER_LENGTH = 91 // as per The World Calendar
|
||||
|
||||
val EPOCH_YEAR = 125
|
||||
|
||||
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 = 2342643// 29 days, 12 hours, 44 minutes, and 3 seconds in-game calendar
|
||||
}
|
||||
|
||||
fun update(delta: Float) {
|
||||
//time
|
||||
realMillisec += delta * 1000.0
|
||||
if (realMillisec >= 1000.0 / REAL_SEC_TO_GAME_SECS) {
|
||||
realMillisec -= 1000.0 / 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]
|
||||
|
||||
inline fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
|
||||
inline fun Long.abs() = Math.abs(this)
|
||||
|
||||
/** Format: "%A, %d %B %Y %X" */
|
||||
fun getFormattedTime() = "${getDayNameShort()}, " +
|
||||
"$days " +
|
||||
"${getMonthNameShort()} " +
|
||||
"$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]
|
||||
|
||||
override fun toString() = getFormattedTime()
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package net.torvald.terrarum.modulebasegame.gameworld
|
||||
|
||||
import net.torvald.terrarum.modulebasegame.gameactors.GameDate
|
||||
|
||||
/**
|
||||
* 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")
|
||||
|
||||
val currentTimeAsGameDate: GameDate
|
||||
get() = GameDate(years, yearlyDays)
|
||||
|
||||
@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]
|
||||
}
|
||||
Reference in New Issue
Block a user