mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
new WorldTime based on TIME_T
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ bin/*
|
|||||||
hs_err_pid*
|
hs_err_pid*
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/.idea/workspace.xml
|
||||||
|
|||||||
14
.idea/libraries/lib.xml
generated
14
.idea/libraries/lib.xml
generated
@@ -1,26 +1,12 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="lib">
|
<library name="lib">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$PROJECT_DIR$/lib/commons-codec-1.10.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/commons-csv-1.2.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/gson-2.5.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/ibxm.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/jinput.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/jnlp.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/jogg-0.0.7.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/jorbis-0.0.15.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/kotlin-reflect.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/kotlin-runtime.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/lwjgl.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/lwjgl_util.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/lib/slick.jar!/" />
|
|
||||||
<root url="file://$PROJECT_DIR$/lib" />
|
<root url="file://$PROJECT_DIR$/lib" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="http://slick.ninjacave.com/javadoc/" />
|
<root url="http://slick.ninjacave.com/javadoc/" />
|
||||||
<root url="file://$PROJECT_DIR$/lib/javadoc" />
|
<root url="file://$PROJECT_DIR$/lib/javadoc" />
|
||||||
<root url="file://$PROJECT_DIR$/lib/apidocs" />
|
<root url="file://$PROJECT_DIR$/lib/apidocs" />
|
||||||
<root url="https://commons.apache.org/proper/commons-csv/archives/1.1/apidocs/index.html" />
|
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<NATIVE>
|
<NATIVE>
|
||||||
<root url="file://$PROJECT_DIR$/lib" />
|
<root url="file://$PROJECT_DIR$/lib" />
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ internal object SetTime : ConsoleCommand {
|
|||||||
if (args.size == 2) {
|
if (args.size == 2) {
|
||||||
val timeToSet = WorldTime.parseTime(args[1])
|
val timeToSet = WorldTime.parseTime(args[1])
|
||||||
|
|
||||||
Terrarum.ingame!!.world.time.setTime(timeToSet)
|
Terrarum.ingame!!.world.time.setTimeOfToday(timeToSet)
|
||||||
|
|
||||||
Echo("Set time to ${Terrarum.ingame!!.world.time.elapsedSeconds} " +
|
Echo("Set time to ${Terrarum.ingame!!.world.time.todaySeconds} " +
|
||||||
"(${Terrarum.ingame!!.world.time.hours}h${formatMin(Terrarum.ingame!!.world.time.minutes)})")
|
"(${Terrarum.ingame!!.world.time.hours}h${formatMin(Terrarum.ingame!!.world.time.minutes)})")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printUsage()
|
printUsage()
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ internal object SetTimeDelta : ConsoleCommand {
|
|||||||
|
|
||||||
override fun execute(args: Array<String>) {
|
override fun execute(args: Array<String>) {
|
||||||
if (args.size == 2) {
|
if (args.size == 2) {
|
||||||
if (args[1].toInt() > HARD_LIMIT)
|
Terrarum.ingame!!.world.time.timeDelta = args[1].toInt()
|
||||||
EchoError("Delta too large -- acceptable delta is 0-60.")
|
|
||||||
|
|
||||||
Terrarum.ingame!!.world.time.setTimeDelta(args[1].toInt())
|
|
||||||
if (Terrarum.ingame!!.world.time.timeDelta == 0)
|
if (Terrarum.ingame!!.world.time.timeDelta == 0)
|
||||||
Echo("時間よ止まれ!ザ・ワルド!!")
|
Echo("時間よ止まれ!ザ・ワルド!!")
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ constructor(//properties
|
|||||||
terrainDamage = PairedMapLayer(width, height)
|
terrainDamage = PairedMapLayer(width, height)
|
||||||
wallDamage = PairedMapLayer(width, height)
|
wallDamage = PairedMapLayer(width, height)
|
||||||
|
|
||||||
time = WorldTime()
|
time = WorldTime(
|
||||||
|
71 * WorldTime.DAY_LENGTH +
|
||||||
|
7 * WorldTime.HOUR_SEC +
|
||||||
|
30L * WorldTime.MINUTE_SEC
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ package net.torvald.terrarum.gameworld
|
|||||||
import net.torvald.terrarum.gameactors.GameDate
|
import net.torvald.terrarum.gameactors.GameDate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The World Calendar implementation of Dwarven Calendar (we're talking about DF!)
|
* The World Calendar implementation of Dwarven Calendar (we're talking about DF!),
|
||||||
|
* except the year begins with Mondag instead of Sundag
|
||||||
*
|
*
|
||||||
* Please see:
|
* Please see:
|
||||||
* https://en.wikipedia.org/wiki/World_Calendar
|
* https://en.wikipedia.org/wiki/World_Calendar
|
||||||
@@ -12,36 +13,90 @@ import net.torvald.terrarum.gameactors.GameDate
|
|||||||
* Normal format for day is
|
* Normal format for day is
|
||||||
* Tysdag 12th Granite
|
* Tysdag 12th Granite
|
||||||
*
|
*
|
||||||
* And there is no AM/PM concept, 22-hour clock is forced.
|
* And there is no AM/PM concept, 22-hour clock is forced; no leap years.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
*
|
*
|
||||||
* Created by minjaesong on 16-01-24.
|
* Created by minjaesong on 16-01-24.
|
||||||
*/
|
*/
|
||||||
class WorldTime {
|
class WorldTime(initTime: Long = 0L) {
|
||||||
internal var TIME_T = 0L // TODO use it! Epoch: Year 125, 1st Granite, 0h00:00
|
var TIME_T = 0L // Epoch: Year 125, 1st Opal, 0h00:00 (Mondag) // 125-01-01
|
||||||
|
private set
|
||||||
|
|
||||||
internal var seconds: Int // 0 - 59
|
init {
|
||||||
internal var minutes: Int // 0 - 59
|
TIME_T = initTime
|
||||||
internal var hours: Int // 0 - 21
|
}
|
||||||
|
|
||||||
// days on the year
|
val seconds: Int // 0 - 59
|
||||||
internal var yearlyDays: Int //NOT a calendar day
|
get() = TIME_T.toPositiveInt() % MINUTE_SEC
|
||||||
|
val minutes: Int // 0 - 59
|
||||||
|
get() = TIME_T.div(MINUTE_SEC).abs().toInt() % HOUR_MIN
|
||||||
|
val hours: Int // 0 - 21
|
||||||
|
get() = TIME_T.div(HOUR_SEC).abs().toInt() % HOURS_PER_DAY
|
||||||
|
|
||||||
internal var days: Int // 1 - 31
|
val yearlyDays: Int // 0 - 364
|
||||||
internal var months: Int // 1 - 12
|
get() = (TIME_T.toPositiveInt().div(DAY_LENGTH) % YEAR_DAYS)
|
||||||
internal var years: Int // 1+
|
|
||||||
|
|
||||||
internal var dayOfWeek: Int //0: Mondag-The first day of weekday (0 - 7)
|
val days: Int // 1 - 31
|
||||||
|
get() = quarterlyDays + 1 -
|
||||||
|
if (quarterlyMonthOffset == 0) 0
|
||||||
|
else if (quarterlyMonthOffset == 1) 31
|
||||||
|
else 61
|
||||||
|
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
|
||||||
|
val years: Int
|
||||||
|
get() = TIME_T.div(YEAR_DAYS * DAY_LENGTH).abs().toInt() + EPOCH_YEAR
|
||||||
|
|
||||||
internal var timeDelta = 1
|
val quarter: Int // 0 - 3
|
||||||
|
get() = if (yearlyDays == YEAR_DAYS - 1) 3 else yearlyDays / QUARTER_LENGTH
|
||||||
|
val quarterlyDays: Int // 0 - 90(91)
|
||||||
|
get() = if (yearlyDays == YEAR_DAYS - 1) 91 else (yearlyDays % QUARTER_LENGTH)
|
||||||
|
val quarterlyMonthOffset: Int // 0 - 2
|
||||||
|
get() = months.minus(1) % 3
|
||||||
|
|
||||||
@Transient private var realMillisec: Int
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)
|
val DAY_NAMES = arrayOf(//daynames are taken from Nynorsk (å -> o)
|
||||||
"Mondag", "Tysdag", "Midvikdag" //From Islenska Miðvikudagur
|
"Mondag", "Tysdag", "Midvikdag" //From Islenska Miðvikudagur
|
||||||
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verdag" //From Norsk word 'verd'
|
, "Torsdag", "Fredag", "Laurdag", "Sundag", "Verddag" //From Norsk word 'verd'
|
||||||
)
|
)
|
||||||
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
|
val DAY_NAMES_SHORT = arrayOf("Mon", "Tys", "Mid", "Tor", "Fre", "Lau", "Sun", "Ver")
|
||||||
|
|
||||||
|
// FIXME Next to Granite is Felsite
|
||||||
|
|
||||||
val MONTH_NAMES = arrayOf(
|
val MONTH_NAMES = arrayOf(
|
||||||
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
|
"Opal", "Obsidian", "Granite", "Slate", "Felsite", "Hematite",
|
||||||
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
|
"Malachite", "Galena", "Limestone", "Sandstone", "Timber", "Moonstone"
|
||||||
@@ -52,8 +107,6 @@ class WorldTime {
|
|||||||
val currentTimeAsGameDate: GameDate
|
val currentTimeAsGameDate: GameDate
|
||||||
get() = GameDate(years, yearlyDays)
|
get() = GameDate(years, yearlyDays)
|
||||||
|
|
||||||
@Transient val REAL_SEC_IN_MILLI = 1000
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** Each day is 22-hour long */
|
/** Each day is 22-hour long */
|
||||||
val DAY_LENGTH = 79200 //must be the multiple of 3600
|
val DAY_LENGTH = 79200 //must be the multiple of 3600
|
||||||
@@ -62,8 +115,12 @@ class WorldTime {
|
|||||||
val MINUTE_SEC: Int = 60
|
val MINUTE_SEC: Int = 60
|
||||||
val HOUR_MIN: Int = 60
|
val HOUR_MIN: Int = 60
|
||||||
val GAME_MIN_TO_REAL_SEC: Float = 60f
|
val GAME_MIN_TO_REAL_SEC: Float = 60f
|
||||||
|
val HOURS_PER_DAY = DAY_LENGTH / HOUR_SEC
|
||||||
|
|
||||||
val YEAR_DAYS: Int = 365
|
val YEAR_DAYS: Int = 365
|
||||||
|
val QUARTER_LENGTH = 91 // as per The World Calendar
|
||||||
|
|
||||||
|
val EPOCH_YEAR = 125
|
||||||
|
|
||||||
fun parseTime(s: String): Int =
|
fun parseTime(s: String): Int =
|
||||||
if (s.length >= 4 && s.contains('h')) {
|
if (s.length >= 4 && s.contains('h')) {
|
||||||
@@ -78,111 +135,37 @@ class WorldTime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
seconds = 0
|
|
||||||
minutes = 30
|
|
||||||
hours = 8
|
|
||||||
yearlyDays = 73
|
|
||||||
days = 12
|
|
||||||
months = 3
|
|
||||||
years = 125
|
|
||||||
dayOfWeek = 1 // Tysdag
|
|
||||||
realMillisec = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fun update(delta: Int) {
|
fun update(delta: Int) {
|
||||||
val oldsec = seconds
|
|
||||||
|
|
||||||
//time
|
//time
|
||||||
realMillisec += delta * timeDelta
|
realMillisec += delta
|
||||||
val newsec = Math.round(GAME_MIN_TO_REAL_SEC.toFloat() / REAL_SEC_IN_MILLI.toFloat() * realMillisec.toFloat())
|
if (realMillisec >= 1000.0 / REAL_SEC_TO_GAME_SECS) {
|
||||||
seconds = newsec
|
realMillisec -= 1000.0 / REAL_SEC_TO_GAME_SECS
|
||||||
|
TIME_T += timeDelta
|
||||||
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. */
|
val todaySeconds: Int
|
||||||
fun setTime(t: Int) {
|
get() = TIME_T.toPositiveInt() % DAY_LENGTH
|
||||||
days += t / DAY_LENGTH
|
|
||||||
hours = t / HOUR_SEC
|
fun setTimeOfToday(t: Int) {
|
||||||
minutes = (t - HOUR_SEC * hours) / MINUTE_SEC
|
TIME_T = TIME_T - todaySeconds + t
|
||||||
seconds = t - minutes * MINUTE_SEC
|
|
||||||
yearlyDays += t / DAY_LENGTH
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addTime(t: Int) {
|
fun addTime(t: Int) {
|
||||||
setTime(elapsedSeconds + t)
|
TIME_T += t
|
||||||
}
|
|
||||||
|
|
||||||
fun setTimeDelta(d: Int) {
|
|
||||||
timeDelta = if (d < 0) 0 else d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val dayName: String
|
val dayName: String
|
||||||
get() = DAY_NAMES[dayOfWeek]
|
get() = DAY_NAMES[dayOfWeek]
|
||||||
|
|
||||||
private fun kickVariables() {
|
private fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
|
||||||
if (seconds >= MINUTE_SEC) {
|
private fun Long.abs() = Math.abs(this)
|
||||||
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" */
|
/** Format: "%A %d %B %Y %X" */
|
||||||
fun getFormattedTime() = "${getDayNameFull()} " +
|
fun getFormattedTime() = "${getDayNameShort()} " +
|
||||||
"$days " +
|
"$days " +
|
||||||
"${getMonthNameFull()} " +
|
"${getMonthNameShort()} " +
|
||||||
"$years " +
|
"$years " +
|
||||||
"${String.format("%02d", hours)}:" +
|
"${String.format("%02d", hours)}:" +
|
||||||
"${String.format("%02d", minutes)}:" +
|
"${String.format("%02d", minutes)}:" +
|
||||||
@@ -192,4 +175,6 @@ class WorldTime {
|
|||||||
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek]
|
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek]
|
||||||
fun getMonthNameFull() = MONTH_NAMES[months - 1]
|
fun getMonthNameFull() = MONTH_NAMES[months - 1]
|
||||||
fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1]
|
fun getMonthNameShort() = MONTH_NAMES_SHORT[months - 1]
|
||||||
|
|
||||||
|
override fun toString() = getFormattedTime()
|
||||||
}
|
}
|
||||||
199
src/net/torvald/terrarum/gameworld/WorldTime_old.kt
Normal file
199
src/net/torvald/terrarum/gameworld/WorldTime_old.kt
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
package net.torvald.terrarum.gameworld
|
||||||
|
|
||||||
|
import net.torvald.terrarum.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 16-01-24.
|
||||||
|
*/
|
||||||
|
class YeOldeWorldTime {
|
||||||
|
internal var TIME_T = 0L // TODO use it! Epoch: Year 125, 1st Granite, 0h00:00
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
@@ -127,7 +127,7 @@ class BasicDebugInfoWindow : UICanvas {
|
|||||||
|
|
||||||
printLineColumn(g, 2, 1, "VSync $ccG" + Terrarum.appgc.isVSyncRequested)
|
printLineColumn(g, 2, 1, "VSync $ccG" + Terrarum.appgc.isVSyncRequested)
|
||||||
printLineColumn(g, 2, 2, "Env colour temp $ccG" + FeaturesDrawer.colTemp)
|
printLineColumn(g, 2, 2, "Env colour temp $ccG" + FeaturesDrawer.colTemp)
|
||||||
printLineColumn(g, 2, 5, "Time $ccG${Terrarum.ingame!!.world.time.elapsedSeconds}" +
|
printLineColumn(g, 2, 5, "Time $ccG${Terrarum.ingame!!.world.time.todaySeconds.toString().padStart(5, '0')}" +
|
||||||
" (${Terrarum.ingame!!.world.time.getFormattedTime()})")
|
" (${Terrarum.ingame!!.world.time.getFormattedTime()})")
|
||||||
printLineColumn(g, 2, 6, "Mass $ccG${player.mass}")
|
printLineColumn(g, 2, 6, "Mass $ccG${player.mass}")
|
||||||
|
|
||||||
|
|||||||
@@ -1,516 +0,0 @@
|
|||||||
package net.torvald.terrarum.virtualcomputer.luaapi
|
|
||||||
|
|
||||||
import org.luaj.vm2.*
|
|
||||||
import org.luaj.vm2.lib.OneArgFunction
|
|
||||||
import org.luaj.vm2.lib.TwoArgFunction
|
|
||||||
import org.luaj.vm2.lib.ZeroArgFunction
|
|
||||||
import net.torvald.terrarum.Terrarum
|
|
||||||
import net.torvald.terrarum.toHex
|
|
||||||
import net.torvald.terrarum.virtualcomputer.computer.TerrarumComputer
|
|
||||||
import net.torvald.terrarum.virtualcomputer.luaapi.Term.Companion.checkIBM437
|
|
||||||
import java.io.*
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.NoSuchFileException
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* computer directory:
|
|
||||||
* .../computers/
|
|
||||||
* media/hda/ -> .../computers/<uuid for the hda>/
|
|
||||||
*
|
|
||||||
* Created by minjaesong on 16-09-17.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* NOTES:
|
|
||||||
* Don't convert '\' to '/'! Rev-slash is used for escape character in sh, and we're sh-compatible!
|
|
||||||
* Use .absoluteFile whenever possible; there's fuckin oddity! (http://bugs.java.com/bugdatabase/view_bug.do;:YfiG?bug_id=4483097)
|
|
||||||
*/
|
|
||||||
internal class Filesystem(globals: Globals, computer: TerrarumComputer) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
// load things. WARNING: THIS IS MANUAL!
|
|
||||||
globals["fs"] = LuaValue.tableOf()
|
|
||||||
globals["fs"]["list"] = ListFiles(computer) // CC compliant
|
|
||||||
globals["fs"]["exists"] = FileExists(computer) // CC/OC compliant
|
|
||||||
globals["fs"]["isDir"] = IsDirectory(computer) // CC compliant
|
|
||||||
globals["fs"]["isFile"] = IsFile(computer)
|
|
||||||
globals["fs"]["isReadOnly"] = IsReadOnly(computer) // CC compliant
|
|
||||||
globals["fs"]["getSize"] = GetSize(computer) // CC compliant
|
|
||||||
globals["fs"]["mkdir"] = Mkdir(computer)
|
|
||||||
globals["fs"]["mv"] = Mv(computer)
|
|
||||||
globals["fs"]["cp"] = Cp(computer)
|
|
||||||
globals["fs"]["rm"] = Rm(computer)
|
|
||||||
globals["fs"]["concat"] = ConcatPath(computer) // OC compliant
|
|
||||||
globals["fs"]["open"] = OpenFile(computer) //CC compliant
|
|
||||||
globals["fs"]["parent"] = GetParentDir(computer)
|
|
||||||
// fs.dofile defined in BOOT
|
|
||||||
// fs.fetchText defined in ROMLIB
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun ensurePathSanity(path: LuaValue) {
|
|
||||||
if (path.checkIBM437().contains(Regex("""\.\.""")))
|
|
||||||
throw LuaError("'..' on path is not supported.")
|
|
||||||
if (!isValidFilename(path.checkIBM437()))
|
|
||||||
throw IOException("path contains invalid characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
var isCaseInsensitive: Boolean
|
|
||||||
|
|
||||||
init {
|
|
||||||
try {
|
|
||||||
val uuid = UUID.randomUUID().toString()
|
|
||||||
val lowerCase = File(Terrarum.currentSaveDir, uuid + "oc_rox")
|
|
||||||
val upperCase = File(Terrarum.currentSaveDir, uuid + "OC_ROX")
|
|
||||||
// This should NEVER happen but could also lead to VERY weird bugs, so we
|
|
||||||
// make sure the files don't exist.
|
|
||||||
if (lowerCase.exists()) lowerCase.delete()
|
|
||||||
if (upperCase.exists()) upperCase.delete()
|
|
||||||
|
|
||||||
lowerCase.createNewFile()
|
|
||||||
|
|
||||||
val insensitive = upperCase.exists()
|
|
||||||
lowerCase.delete()
|
|
||||||
|
|
||||||
isCaseInsensitive = insensitive
|
|
||||||
|
|
||||||
println("[Filesystem] Case insensitivity: $isCaseInsensitive")
|
|
||||||
}
|
|
||||||
catch (e: IOException) {
|
|
||||||
System.err.println("[Filesystem] Couldn't determine if the file system is case sensitive, falling back to insensitive.")
|
|
||||||
e.printStackTrace(System.out)
|
|
||||||
isCaseInsensitive = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Worst-case: we're on Windows or using a FAT32 partition mounted in *nix.
|
|
||||||
// Note: we allow / as the path separator and expect all \s to be converted
|
|
||||||
// accordingly before the path is passed to the file system.
|
|
||||||
private val invalidChars = Regex("""[<>:"|?*\u0000-\u001F]""") // original OC uses Set(); we use regex
|
|
||||||
|
|
||||||
fun isValidFilename(name: String) = !name.contains(invalidChars)
|
|
||||||
|
|
||||||
fun String.validatePath() : String {
|
|
||||||
if (!isValidFilename(this)) {
|
|
||||||
throw IOException("path contains invalid characters")
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/** actual directory: <appdata>/Saves/<savename>/computers/<drivename>/
|
|
||||||
* directs media/ directory to /<uuid> directory
|
|
||||||
*/
|
|
||||||
fun TerrarumComputer.getRealPath(luapath: LuaValue) : String {
|
|
||||||
// direct mounted paths to real path
|
|
||||||
val computerDir = Terrarum.currentSaveDir.absolutePath + "/computers/"
|
|
||||||
/* if not begins with "(/?)media/", direct to boot
|
|
||||||
* else, to corresponding drives
|
|
||||||
*
|
|
||||||
* List of device names (these are auto-mounted. why? primitivism :p):
|
|
||||||
* = hda - hdd: hard disks
|
|
||||||
* = fd1 - fd4: floppy drives
|
|
||||||
* = sda: whatever external drives, usually a CD
|
|
||||||
* = boot: current boot device
|
|
||||||
*/
|
|
||||||
|
|
||||||
// remove first '/' in path
|
|
||||||
var path = luapath.checkIBM437().validatePath()
|
|
||||||
if (path.startsWith('/')) path = path.substring(1)
|
|
||||||
|
|
||||||
val finalPath: String
|
|
||||||
|
|
||||||
if (path.startsWith("media/")) {
|
|
||||||
val device = path.substring(6, 9)
|
|
||||||
val subPath = path.substring(9)
|
|
||||||
finalPath = computerDir + this.computerValue.getAsString("device") + subPath
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
finalPath = computerDir + this.computerValue.getAsString("boot") + "/" + path
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove trailing slash
|
|
||||||
return if (finalPath.endsWith("\\") || finalPath.endsWith("/"))
|
|
||||||
finalPath.substring(0, finalPath.length - 1)
|
|
||||||
else
|
|
||||||
finalPath
|
|
||||||
}
|
|
||||||
|
|
||||||
fun combinePath(base: String, local: String) : String {
|
|
||||||
return "$base$local".replace("//", "/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param cname == UUID of the drive
|
|
||||||
*
|
|
||||||
* actual directory: <appdata>/Saves/<savename>/computers/<drivename>/
|
|
||||||
*/
|
|
||||||
class ListFiles(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
println("ListFiles: got path ${path.checkIBM437()}")
|
|
||||||
|
|
||||||
val table = LuaTable()
|
|
||||||
val file = File(computer.getRealPath(path)).absoluteFile
|
|
||||||
try {
|
|
||||||
file.list().forEachIndexed { i, s -> table.insert(i, LuaValue.valueOf(s)) }
|
|
||||||
}
|
|
||||||
catch (e: NullPointerException) {}
|
|
||||||
return table
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Don't use this. Use isFile */
|
|
||||||
class FileExists(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(Files.exists(Paths.get(computer.getRealPath(path)).toAbsolutePath()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsDirectory(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
val isDir = Files.isDirectory(Paths.get(computer.getRealPath(path)).toAbsolutePath())
|
|
||||||
val exists = Files.exists(Paths.get(computer.getRealPath(path)).toAbsolutePath())
|
|
||||||
|
|
||||||
return LuaValue.valueOf(isDir || exists)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsFile(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
// check if the path is file by checking:
|
|
||||||
// 1. isfile
|
|
||||||
// 2. canwrite
|
|
||||||
// 3. length
|
|
||||||
// Why? Our Java simply wants to fuck you.
|
|
||||||
|
|
||||||
val path = Paths.get(computer.getRealPath(path)).toAbsolutePath()
|
|
||||||
var result = false
|
|
||||||
result = Files.isRegularFile(path)
|
|
||||||
|
|
||||||
if (!result) result = Files.isWritable(path)
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
try { result = Files.size(path) > 0 }
|
|
||||||
catch (e: NoSuchFileException) { result = false }
|
|
||||||
|
|
||||||
return LuaValue.valueOf(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IsReadOnly(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(!Files.isWritable(Paths.get(computer.getRealPath(path)).toAbsolutePath()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** we have 4GB file size limit */
|
|
||||||
class GetSize(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(Files.size(Paths.get(computer.getRealPath(path)).toAbsolutePath()).toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO class GetFreeSpace
|
|
||||||
|
|
||||||
/**
|
|
||||||
* difference with ComputerCraft: it returns boolean, true on successful.
|
|
||||||
*/
|
|
||||||
class Mkdir(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(File(computer.getRealPath(path)).absoluteFile.mkdir())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* moves a directory, overwrites the target
|
|
||||||
*/
|
|
||||||
class Mv(val computer: TerrarumComputer) : TwoArgFunction() {
|
|
||||||
override fun call(from: LuaValue, to: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(from)
|
|
||||||
Filesystem.ensurePathSanity(to)
|
|
||||||
|
|
||||||
val fromFile = File(computer.getRealPath(from)).absoluteFile
|
|
||||||
var success = fromFile.copyRecursively(
|
|
||||||
File(computer.getRealPath(to)).absoluteFile, overwrite = true
|
|
||||||
)
|
|
||||||
if (success) success = fromFile.deleteRecursively()
|
|
||||||
else return LuaValue.valueOf(false)
|
|
||||||
return LuaValue.valueOf(success)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* copies a directory, overwrites the target
|
|
||||||
* difference with ComputerCraft: it returns boolean, true on successful.
|
|
||||||
*/
|
|
||||||
class Cp(val computer: TerrarumComputer) : TwoArgFunction() {
|
|
||||||
override fun call(from: LuaValue, to: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(from)
|
|
||||||
Filesystem.ensurePathSanity(to)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(
|
|
||||||
File(computer.getRealPath(from)).absoluteFile.copyRecursively(
|
|
||||||
File(computer.getRealPath(to)).absoluteFile, overwrite = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* difference with ComputerCraft: it returns boolean, true on successful.
|
|
||||||
*/
|
|
||||||
class Rm(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(
|
|
||||||
File(computer.getRealPath(path)).absoluteFile.deleteRecursively()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConcatPath(val computer: TerrarumComputer) : TwoArgFunction() {
|
|
||||||
override fun call(base: LuaValue, local: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(base)
|
|
||||||
Filesystem.ensurePathSanity(local)
|
|
||||||
|
|
||||||
val combinedPath = combinePath(base.checkIBM437().validatePath(), local.checkIBM437().validatePath())
|
|
||||||
return LuaValue.valueOf(combinedPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mode: r, rb, w, wb, a, ab
|
|
||||||
*
|
|
||||||
* Difference: TEXT MODE assumes CP437 instead of UTF-8!
|
|
||||||
*
|
|
||||||
* When you have opened a file you must always close the file handle, or else data may not be saved.
|
|
||||||
*
|
|
||||||
* FILE class in CC:
|
|
||||||
* (when you look thru them using file = fs.open("./test", "w")
|
|
||||||
*
|
|
||||||
* file = {
|
|
||||||
* close = function()
|
|
||||||
* -- write mode
|
|
||||||
* write = function(string)
|
|
||||||
* flush = function() -- write, keep the handle
|
|
||||||
* writeLine = function(string) -- text mode
|
|
||||||
* -- read mode
|
|
||||||
* readLine = function() -- text mode
|
|
||||||
* readAll = function()
|
|
||||||
* -- binary read mode
|
|
||||||
* read = function() -- read single byte. return: number or nil
|
|
||||||
* -- binary write mode
|
|
||||||
* write = function(byte)
|
|
||||||
* writeBytes = function(string as bytearray)
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
class OpenFile(val computer: TerrarumComputer) : TwoArgFunction() {
|
|
||||||
override fun call(path: LuaValue, mode: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val mode = mode.checkIBM437().toLowerCase()
|
|
||||||
val luaClass = LuaTable()
|
|
||||||
val file = File(computer.getRealPath(path)).absoluteFile
|
|
||||||
|
|
||||||
if (mode.contains(Regex("""[aw]""")) && !file.canWrite())
|
|
||||||
throw LuaError("Cannot open file for " +
|
|
||||||
"${if (mode.startsWith('w')) "read" else "append"} mode" +
|
|
||||||
": is readonly.")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when (mode) {
|
|
||||||
"r" -> {
|
|
||||||
try {
|
|
||||||
val fr = FileReader(file)
|
|
||||||
luaClass["close"] = FileClassClose(fr)
|
|
||||||
luaClass["readLine"] = FileClassReadLine(fr)
|
|
||||||
luaClass["readAll"] = FileClassReadAll(file.toPath())
|
|
||||||
}
|
|
||||||
catch (e: FileNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw LuaError("$path: no such file.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"rb" -> {
|
|
||||||
try {
|
|
||||||
val fis = FileInputStream(file)
|
|
||||||
luaClass["close"] = FileClassClose(fis)
|
|
||||||
luaClass["read"] = FileClassReadByte(fis)
|
|
||||||
luaClass["readAll"] = FileClassReadAll(file.toPath())
|
|
||||||
}
|
|
||||||
catch (e: FileNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw LuaError("$path: no such file.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"w", "a" -> {
|
|
||||||
try {
|
|
||||||
val fw = FileWriter(file, (mode.startsWith('a')))
|
|
||||||
luaClass["close"] = FileClassClose(fw)
|
|
||||||
luaClass["write"] = FileClassPrintText(fw)
|
|
||||||
luaClass["writeLine"] = FileClassPrintlnText(fw)
|
|
||||||
luaClass["flush"] = FileClassFlush(fw)
|
|
||||||
}
|
|
||||||
catch (e: FileNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw LuaError("$path: is a directory.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"wb", "ab" -> {
|
|
||||||
try {
|
|
||||||
val fos = FileOutputStream(file, (mode.startsWith('a')))
|
|
||||||
luaClass["close"] = FileClassClose(fos)
|
|
||||||
luaClass["write"] = FileClassWriteByte(fos)
|
|
||||||
luaClass["writeBytes"] = FileClassWriteBytes(fos)
|
|
||||||
luaClass["flush"] = FileClassFlush(fos)
|
|
||||||
}
|
|
||||||
catch (e: FileNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
throw LuaError("$path: is a directory.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return luaClass
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GetParentDir(val computer: TerrarumComputer) : OneArgFunction() {
|
|
||||||
override fun call(path: LuaValue) : LuaValue {
|
|
||||||
Filesystem.ensurePathSanity(path)
|
|
||||||
|
|
||||||
var pathSB = StringBuilder(path.checkIBM437())
|
|
||||||
|
|
||||||
// backward travel, drop chars until '/' has encountered
|
|
||||||
while (!pathSB.endsWith('/'))
|
|
||||||
pathSB.deleteCharAt(pathSB.lastIndex - 1)
|
|
||||||
|
|
||||||
// drop trailing '/'
|
|
||||||
if (pathSB.endsWith('/'))
|
|
||||||
pathSB.deleteCharAt(pathSB.lastIndex - 1)
|
|
||||||
|
|
||||||
return LuaValue.valueOf(pathSB.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
// OpenFile implementations //
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
private class FileClassClose(val fo: Any) : ZeroArgFunction() {
|
|
||||||
override fun call() : LuaValue {
|
|
||||||
if (fo is FileOutputStream)
|
|
||||||
fo.close()
|
|
||||||
else if (fo is FileWriter)
|
|
||||||
fo.close()
|
|
||||||
else if (fo is FileReader)
|
|
||||||
fo.close()
|
|
||||||
else if (fo is FileInputStream)
|
|
||||||
fo.close()
|
|
||||||
else
|
|
||||||
throw IllegalArgumentException("Unacceptable file output: must be either Input/OutputStream or Reader/Writer.")
|
|
||||||
|
|
||||||
return LuaValue.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassWriteByte(val fos: FileOutputStream) : OneArgFunction() {
|
|
||||||
override fun call(byte: LuaValue) : LuaValue {
|
|
||||||
fos.write(byte.checkint())
|
|
||||||
|
|
||||||
return LuaValue.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassWriteBytes(val fos: FileOutputStream) : OneArgFunction() {
|
|
||||||
override fun call(byteString: LuaValue) : LuaValue {
|
|
||||||
val byteString = byteString.checkIBM437()
|
|
||||||
val bytearr = ByteArray(byteString.length, { byteString[it].toByte() })
|
|
||||||
fos.write(bytearr)
|
|
||||||
|
|
||||||
return LuaValue.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassPrintText(val fw: FileWriter) : OneArgFunction() {
|
|
||||||
override fun call(string: LuaValue) : LuaValue {
|
|
||||||
val text = string.checkIBM437()
|
|
||||||
fw.write(text)
|
|
||||||
return LuaValue.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassPrintlnText(val fw: FileWriter) : OneArgFunction() {
|
|
||||||
override fun call(string: LuaValue) : LuaValue {
|
|
||||||
val text = string.checkIBM437() + "\n"
|
|
||||||
fw.write(text)
|
|
||||||
return LuaValue.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassFlush(val fo: Any) : ZeroArgFunction() {
|
|
||||||
override fun call() : LuaValue {
|
|
||||||
if (fo is FileOutputStream)
|
|
||||||
fo.flush()
|
|
||||||
else if (fo is FileWriter)
|
|
||||||
fo.flush()
|
|
||||||
else
|
|
||||||
throw IllegalArgumentException("Unacceptable file output: must be either OutputStream or Writer.")
|
|
||||||
|
|
||||||
return LuaValue.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassReadByte(val fis: FileInputStream) : ZeroArgFunction() {
|
|
||||||
override fun call() : LuaValue {
|
|
||||||
val readByte = fis.read()
|
|
||||||
return if (readByte == -1) LuaValue.NIL else LuaValue.valueOf(readByte)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassReadAllBytes(val path: Path) : ZeroArgFunction() {
|
|
||||||
override fun call() : LuaValue {
|
|
||||||
val byteArr = Files.readAllBytes(path)
|
|
||||||
val s: String = java.lang.String(byteArr, "IBM437").toString()
|
|
||||||
return LuaValue.valueOf(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FileClassReadAll(val path: Path) : ZeroArgFunction() {
|
|
||||||
override fun call() : LuaValue {
|
|
||||||
return FileClassReadAllBytes(path).call()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** returns NO line separator! */
|
|
||||||
private class FileClassReadLine(val fr: FileReader) : ZeroArgFunction() {
|
|
||||||
val scanner = Scanner(fr.readText()) // no closing; keep the scanner status persistent
|
|
||||||
|
|
||||||
override fun call() : LuaValue {
|
|
||||||
return if (scanner.hasNextLine()) LuaValue.valueOf(scanner.nextLine())
|
|
||||||
else LuaValue.NIL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -91,7 +91,7 @@ object WeatherMixer {
|
|||||||
)
|
)
|
||||||
Terrarum.ingame!!.addParticle(rainParticle)
|
Terrarum.ingame!!.addParticle(rainParticle)
|
||||||
}
|
}
|
||||||
globalLightNow.set(getGlobalLightOfTime(world.time.elapsedSeconds).darker(0.3f))
|
globalLightNow.set(getGlobalLightOfTime(world.time.todaySeconds).darker(0.3f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ object WeatherMixer {
|
|||||||
fun render(g: Graphics) {
|
fun render(g: Graphics) {
|
||||||
|
|
||||||
// we will not care for nextSkybox for now
|
// we will not care for nextSkybox for now
|
||||||
val timeNow = Terrarum.ingame!!.world.time.elapsedSeconds
|
val timeNow = Terrarum.ingame!!.world.time.todaySeconds
|
||||||
val skyboxColourMap = currentWeather.skyboxGradColourMap
|
val skyboxColourMap = currentWeather.skyboxGradColourMap
|
||||||
val lightColourMap = currentWeather.globalLightColourMap
|
val lightColourMap = currentWeather.globalLightColourMap
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user