Compare commits

...

40 Commits

Author SHA1 Message Date
minjaesong
97a7a36030 version tag on app configuration 2023-08-17 15:47:31 +09:00
minjaesong
662069466a skybox size adjustment 2023-08-17 02:04:05 +09:00
minjaesong
52cff00338 commit 2023-08-16 22:39:31 +09:00
minjaesong
1a40334f8e auto zip for .app packaging 2023-08-16 22:26:27 +09:00
minjaesong
763f512419 script for assets_release 2023-08-16 22:17:09 +09:00
minjaesong
620a1c6956 GL_UNSIGNED_SHORT works just fine on macOS?! 2023-08-16 21:56:04 +09:00
minjaesong
5f4fcdba69 apple m chip workaround (2) 2023-08-16 21:46:54 +09:00
minjaesong
7a79f444b2 apple m chip workaround 2023-08-16 21:38:23 +09:00
minjaesong
e4b947ce69 Float16FrameBuffer 2023-08-16 17:51:31 +09:00
minjaesong
fdfec960ca reverse gravity adaptation of the last commit 2023-08-16 16:33:53 +09:00
minjaesong
75021ecfa2 fixing issue #48 2023-08-16 16:03:54 +09:00
minjaesong
c90ef21bfa calendar UI mouseover 2023-08-15 19:02:25 +09:00
minjaesong
3fce5d7e95 calendar UI 2023-08-15 14:58:50 +09:00
minjaesong
8db1228e70 calendar wip 2023-08-15 04:54:43 +09:00
minjaesong
5f7f724058 tileatlas: atlas size will automatically expand if it's too small 2023-08-14 18:47:39 +09:00
minjaesong
fab4179068 fixing issue #47 using new tag on blocks.csv 2023-08-14 18:16:05 +09:00
minjaesong
32803b6f18 incomplete fix for horizontal bouncing on T-shaped platform arrangement 2023-08-14 04:10:10 +09:00
minjaesong
f8f75fb7b6 options to change atlas texture size 2023-08-14 03:53:25 +09:00
minjaesong
9919a99032 fix: phys ccd would only trigger on its last iteration due to a stupid oversight 2023-08-13 16:54:46 +09:00
minjaesong
6a43d1a5bd calendar sprite removing a spring 2023-08-13 15:36:24 +09:00
minjaesong
24c971e4b8 oh wait thats not it 2023-08-11 21:08:35 +09:00
minjaesong
62f0fd7c68 hiding the phys artefact by forcefully holding down-key for long enough 2023-08-11 21:05:47 +09:00
minjaesong
3dec312989 phys glitch kinda fixed but i'm no longer sure about that 2023-08-11 17:03:51 +09:00
minjaesong
77b51a45dd no highp on blur shader 2023-08-11 13:03:45 +09:00
minjaesong
d1b4ce3404 something's fixed but have no idea what 2023-08-11 12:35:36 +09:00
minjaesong
fd7b88307c . 2023-08-11 09:29:30 +09:00
minjaesong
579b6b5b29 somehow fixed but now jumping while walking against wall changes jump behav 2023-08-11 01:04:34 +09:00
minjaesong
cef58f6a73 phys debugging; see L818@ActorWithBody.kt 2023-08-10 23:49:43 +09:00
minjaesong
c0c98c3b80 some locale changes; remoCon changes on load menu 2023-08-08 17:44:23 +09:00
minjaesong
88d844cc09 actor draw planting offset 2023-08-08 17:14:13 +09:00
minjaesong
2411db17a7 calendar fixture wip 2023-08-08 16:41:40 +09:00
minjaesong
53d372be38 there's no way #33 is fixed so easily but im committing it anyway 2023-08-08 12:09:22 +09:00
minjaesong
88831051c8 revised ingame date format 2023-08-08 09:01:34 +09:00
minjaesong
87d92ecb74 some random ideas for future self 2023-08-08 01:39:33 +09:00
minjaesong
6672dffdbc new passwd for new version 2023-08-07 14:45:07 +09:00
minjaesong
cd00ab4c7f fix: hq2x results graphical issue on some systems 2023-08-07 14:30:36 +09:00
minjaesong
014306c209 2k skybox tex; trilinear blending of atmos vars 2023-08-07 13:59:45 +09:00
minjaesong
30fb57eca3 skybox: two different setup for AM/PN 2023-08-06 18:37:56 +09:00
minjaesong
52ad8f0c46 improved skybox model 2023-08-05 17:20:35 +09:00
minjaesong
1b08039018 updating numbers for v0.3.3 2023-08-05 00:45:35 +09:00
72 changed files with 1723 additions and 812 deletions

View File

@@ -1,26 +1,17 @@
package net.torvald.terrarum.modulecomputers.gameactors package net.torvald.terrarum.modulecomputers.gameactors
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import kotlin.coroutines.*
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameactors.AVKey import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameactors.BlockBox import net.torvald.terrarum.modulebasegame.gameactors.BlockBox
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory import net.torvald.terrarum.modulebasegame.gameactors.FixtureInventory
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.modulecomputers.ui.UIHomeComputer
import net.torvald.terrarum.ui.UICanvas
import net.torvald.tsvm.* import net.torvald.tsvm.*
import net.torvald.tsvm.peripheral.AdapterConfig import net.torvald.tsvm.peripheral.AdapterConfig
import net.torvald.tsvm.peripheral.GraphicsAdapter import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.tsvm.peripheral.VMProgramRom import net.torvald.tsvm.peripheral.VMProgramRom
import net.torvald.unicode.*
/** /**
* Created by minjaesong on 2021-12-04. * Created by minjaesong on 2021-12-04.
@@ -131,77 +122,3 @@ class FixtureHomeComputer : FixtureBase {
} }
} }
internal class UIHomeComputer : UICanvas(
toggleKeyLiteral = Input.Keys.ESCAPE, // FIXME why do I have specify ESC for it to function? ESC should be work as the default key
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"),
) {
override var width = 640
override var height = 480
override var openCloseTime = 0f
private val drawOffX = (width - 560).div(2).toFloat()
private val drawOffY = (height - 448).div(2).toFloat()
private var batch: FlippingSpriteBatch
private var camera: OrthographicCamera
internal lateinit var vm: VM
internal lateinit var fixture: FixtureHomeComputer
init {
batch = FlippingSpriteBatch()
camera = OrthographicCamera(width.toFloat(), height.toFloat())
//val m = Matrix4()
//m.setToOrtho2D(0f, 0f, width.toFloat(), height.toFloat())
batch.projectionMatrix = camera.combined
}
private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false)
private val controlHelp =
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_T$KEYCAP_R Terminate\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_S Reset\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_Q SysRq"
override fun updateUI(delta: Float) {
}
override fun renderUI(otherBatch: SpriteBatch, otherCamera: Camera) {
otherBatch.end()
fbo.inAction(camera, batch) {
Gdx.gl.glClearColor(0f,0f,0f,1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) // to hide the crap might be there
(vm.peripheralTable[1].peripheral as? GraphicsAdapter)?.let { gpu ->
val clearCol = gpu.getBackgroundColour()
Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
gpu.render(Gdx.graphics.deltaTime, batch, drawOffX, drawOffY, true, fbo) // gpu.render will internally end() the fbo then begin() again before using the batch I've fed in
}
}
otherBatch.begin()
otherBatch.shader = null
blendNormalStraightAlpha(otherBatch)
otherBatch.color = Color.WHITE
otherBatch.draw(fbo.colorBufferTexture, posX.toFloat(), posY.toFloat(), width.toFloat(), height.toFloat())
otherBatch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(otherBatch, posX - 1, posY - 1, width + 2, height + 2)
App.fontGame.draw(otherBatch, controlHelp, posX, posY + height + 4)
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
fixture.startVM()
}
override fun dispose() {
fbo.dispose()
}
}

View File

@@ -0,0 +1,93 @@
package net.torvald.terrarum.modulecomputers.ui
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.glutils.FrameBuffer
import net.torvald.terrarum.App
import net.torvald.terrarum.FlippingSpriteBatch
import net.torvald.terrarum.blendNormalStraightAlpha
import net.torvald.terrarum.inAction
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulecomputers.gameactors.FixtureHomeComputer
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.tsvm.VM
import net.torvald.tsvm.peripheral.GraphicsAdapter
import net.torvald.unicode.*
internal class UIHomeComputer : UICanvas(
toggleKeyLiteral = Input.Keys.ESCAPE, // FIXME why do I have specify ESC for it to function? ESC should be work as the default key
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"),
) {
override var width = 640
override var height = 480
override var openCloseTime = 0f
private val drawOffX = (width - 560).div(2).toFloat()
private val drawOffY = (height - 448).div(2).toFloat()
private var batch: FlippingSpriteBatch
private var camera: OrthographicCamera
internal lateinit var vm: VM
internal lateinit var fixture: FixtureHomeComputer
init {
batch = FlippingSpriteBatch()
camera = OrthographicCamera(width.toFloat(), height.toFloat())
//val m = Matrix4()
//m.setToOrtho2D(0f, 0f, width.toFloat(), height.toFloat())
batch.projectionMatrix = camera.combined
}
private val fbo = FrameBuffer(Pixmap.Format.RGBA8888, width, height, false)
private val controlHelp =
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}\u3000 " +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_T$KEYCAP_R Terminate\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_S Reset\u3000" +
"$KEYCAP_CTRL$KEYCAP_SHIFT$KEYCAP_R$KEYCAP_Q SysRq"
override fun updateUI(delta: Float) {
}
override fun renderUI(otherBatch: SpriteBatch, otherCamera: Camera) {
otherBatch.end()
fbo.inAction(camera, batch) {
Gdx.gl.glClearColor(0f,0f,0f,1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) // to hide the crap might be there
(vm.peripheralTable[1].peripheral as? GraphicsAdapter)?.let { gpu ->
val clearCol = gpu.getBackgroundColour()
Gdx.gl.glClearColor(clearCol.r, clearCol.g, clearCol.b, clearCol.a)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
gpu.render(Gdx.graphics.deltaTime, batch, drawOffX, drawOffY, true, fbo) // gpu.render will internally end() the fbo then begin() again before using the batch I've fed in
}
}
otherBatch.begin()
otherBatch.shader = null
blendNormalStraightAlpha(otherBatch)
otherBatch.color = Color.WHITE
otherBatch.draw(fbo.colorBufferTexture, posX.toFloat(), posY.toFloat(), width.toFloat(), height.toFloat())
otherBatch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(otherBatch, posX - 1, posY - 1, width + 2, height + 2)
App.fontGame.draw(otherBatch, controlHelp, posX, posY + height + 4)
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
fixture.startVM()
}
override fun dispose() {
fbo.dispose()
}
}

Binary file not shown.

View File

@@ -23,6 +23,7 @@
"MENU_LABEL_STREAMING": "Livestreaming", "MENU_LABEL_STREAMING": "Livestreaming",
"MENU_LABEL_SYSTEM_INFO": "System Info", "MENU_LABEL_SYSTEM_INFO": "System Info",
"MENU_MODULES" : "Modules", "MENU_MODULES" : "Modules",
"MENU_OPTIONS_ATLAS_TEXTURE_SIZE": "Atlas Texture Size",
"MENU_OPTIONS_AUTOSAVE": "Autosave", "MENU_OPTIONS_AUTOSAVE": "Autosave",
"MENU_OPTIONS_BLUR": "Blur", "MENU_OPTIONS_BLUR": "Blur",
"MENU_OPTIONS_DEBUG_CONSOLE": "Debug Console", "MENU_OPTIONS_DEBUG_CONSOLE": "Debug Console",

View File

@@ -23,6 +23,7 @@
"MENU_LABEL_STREAMING": "실시간 방송", "MENU_LABEL_STREAMING": "실시간 방송",
"MENU_LABEL_SYSTEM_INFO": "시스템 정보", "MENU_LABEL_SYSTEM_INFO": "시스템 정보",
"MENU_MODULES" : "모듈", "MENU_MODULES" : "모듈",
"MENU_OPTIONS_ATLAS_TEXTURE_SIZE": "아틀라스 텍스처 크기",
"MENU_OPTIONS_AUTOSAVE": "자동 저장", "MENU_OPTIONS_AUTOSAVE": "자동 저장",
"MENU_OPTIONS_BLUR": "흐림", "MENU_OPTIONS_BLUR": "흐림",
"MENU_OPTIONS_DEBUG_CONSOLE": "디버그 콘솔", "MENU_OPTIONS_DEBUG_CONSOLE": "디버그 콘솔",

View File

@@ -1,5 +1,5 @@
"id";"drop";"spawn";"name";"shdr";"shdg";"shdb";"shduv";"str";"dsty";"mate";"solid";"wall";"grav";"dlfn";"fv";"fr";"lumr";"lumg";"lumb";"lumuv";"colour";"vscs";"refl";"tags" "id";"drop";"spawn";"name";"shdr";"shdg";"shdb";"shduv";"str";"dsty";"mate";"solid";"wall";"grav";"dlfn";"fv";"fr";"lumr";"lumg";"lumb";"lumuv";"colour";"vscs";"refl";"tags"
"0";"0";"0";"BLOCK_AIR";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"" "0";"0";"0";"BLOCK_AIR";"0.0312";"0.0312";"0.0312";"0.0312";"1";"1";"NULL";"0";"1";"N/A";"0";"0";"4";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"INCONSEQUENTIAL"
"16";"17";"17";"BLOCK_STONE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL" "16";"17";"17";"BLOCK_STONE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK,NATURAL"
"17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK" "17";"17";"17";"BLOCK_STONE_QUARRIED";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"ROCK"
"18";"18";"18";"BLOCK_STONE_TILE_WHITE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.18";"STONE" "18";"18";"18";"BLOCK_STONE_TILE_WHITE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.18";"STONE"
@@ -188,6 +188,7 @@
## Some tags are reserved for internal use, which are: ## Some tags are reserved for internal use, which are:
## - INTERNAL: denotes that the tile is internal-use. Will not be rendered unless debug window is on. ## - INTERNAL: denotes that the tile is internal-use. Will not be rendered unless debug window is on.
## - DORENDER: this internal tile must go through the standard-issue tile drawing routine. ## - DORENDER: this internal tile must go through the standard-issue tile drawing routine.
## - INCONSEQUENTIAL: denotes that this tile can be overwritten without dropping it. Usually used with flower tiles.
# #
# #
## References ## ## References ##
Can't render this file because it contains an unexpected character in line 179 and column 37.

View File

@@ -8,6 +8,7 @@ id;classname
8;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalEmitter 8;net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalEmitter
9;net.torvald.terrarum.modulebasegame.gameitems.WireCutterAll 9;net.torvald.terrarum.modulebasegame.gameitems.WireCutterAll
10;net.torvald.terrarum.modulebasegame.gameitems.ItemTypewriter 10;net.torvald.terrarum.modulebasegame.gameitems.ItemTypewriter
11;net.torvald.terrarum.modulebasegame.gameitems.ItemWallCalendar
256;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak 256;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
257;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony 257;net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
1 id classname
8 8 net.torvald.terrarum.modulebasegame.gameitems.ItemLogicSignalEmitter
9 9 net.torvald.terrarum.modulebasegame.gameitems.WireCutterAll
10 10 net.torvald.terrarum.modulebasegame.gameitems.ItemTypewriter
11 11 net.torvald.terrarum.modulebasegame.gameitems.ItemWallCalendar
12 256 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorOak
13 257 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorEbony
14 258 net.torvald.terrarum.modulebasegame.gameitems.ItemSwingingDoorBirch

View File

@@ -0,0 +1,34 @@
{
"MENU_CALENDAR_CALENDAR": "Calendar",
"MENU_CALENDAR_EVENTS": "Events",
"MENU_CALENDAR_ADD_NEW_EVENT": "Add New Event",
"CONTEXT_CALENDAR_SEASON_SPRING": "Spring",
"CONTEXT_CALENDAR_SEASON_SUMMER": "Summer",
"CONTEXT_CALENDAR_SEASON_AUTUMN": "Autumn",
"CONTEXT_CALENDAR_SEASON_WINTER": "Winter",
"CONTEXT_CALENDAR_SEASON_SPRI": "Spri",
"CONTEXT_CALENDAR_SEASON_SUMM": "Summ",
"CONTEXT_CALENDAR_SEASON_AUTM": "Autm",
"CONTEXT_CALENDAR_SEASON_WINT": "Wint",
"CONTEXT_CALENDAR_DATE_FORMAT_Y": "Year {0}",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD": "Year {0} {1} {2}",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD_DDD": "Year {0} {1} {2} {3}",
"CONTEXT_CALENDAR_DAY_MONDAG_DNT": "Mondag",
"CONTEXT_CALENDAR_DAY_TYSDAG_DNT": "Tysdag",
"CONTEXT_CALENDAR_DAY_MIDTVEKE_DNT": "Midtveke",
"CONTEXT_CALENDAR_DAY_TORSDAG_DNT": "Torsdag",
"CONTEXT_CALENDAR_DAY_FREDAG_DNT": "Fredag",
"CONTEXT_CALENDAR_DAY_LAURDAG_DNT": "Laurdag",
"CONTEXT_CALENDAR_DAY_SUNDAG_DNT": "Sundag",
"CONTEXT_CALENDAR_DAY_VERDDAG_DNT": "Verddag",
"CONTEXT_CALENDAR_DAY_MON_DNT": "Mon",
"CONTEXT_CALENDAR_DAY_TYS_DNT": "Tys",
"CONTEXT_CALENDAR_DAY_MID_DNT": "Mid",
"CONTEXT_CALENDAR_DAY_TOR_DNT": "Tor",
"CONTEXT_CALENDAR_DAY_FRE_DNT": "Fre",
"CONTEXT_CALENDAR_DAY_LAU_DNT": "Lau",
"CONTEXT_CALENDAR_DAY_SUN_DNT": "Sun",
"CONTEXT_CALENDAR_DAY_VER_DNT": "Ver",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD_SHORT_DNT": "ɣ{0}-{1}-{2}"
}

View File

@@ -1,23 +1,24 @@
{ {
"CONTEXT_GENERATOR_SEED": "Seed",
"CONTEXT_ITEM_MAP": "Map",
"CONTEXT_ITEM_TOOL_PLURAL": "Tools",
"CONTEXT_PLACE_COORDINATE": "Coordinate",
"CONTEXT_WORLD_COUNT": "Worlds: ",
"CONTEXT_WORLD_NEW": "New World", "CONTEXT_WORLD_NEW": "New World",
"MENU_LABEL_DELETE_WORLD": "Delete World", "MENU_LABEL_DELETE_WORLD": "Delete World",
"CONTEXT_WORLD_COUNT": "Worlds: ",
"GAME_INVENTORY_INGREDIENTS": "Ingredients",
"GAME_INVENTORY_POTIONS": "Potions",
"GAME_INVENTORY_BLOCKS": "Blocks",
"GAME_INVENTORY_WALLS": "Walls",
"CONTEXT_ITEM_TOOL_PLURAL": "Tools",
"GAME_INVENTORY_FAVORITES": "Favorites",
"GAME_INVENTORY_REGISTER": "Register",
"CONTEXT_ITEM_MAP": "Map",
"MENU_LABEL_MENU": "Menu", "MENU_LABEL_MENU": "Menu",
"CONTEXT_GENERATOR_SEED": "Seed", "MENU_LABEL_PREV_SAVES": "Previous Saves",
"MENU_LABEL_RENAME": "Rename",
"GAME_ACTION_CRAFT": "Craft",
"GAME_ACTION_GRAPPLE": "Grapple", "GAME_ACTION_GRAPPLE": "Grapple",
"GAME_ACTION_QUICKSEL": "Quick Select", "GAME_ACTION_QUICKSEL": "Quick Select",
"GAME_ACTION_CRAFT": "Craft",
"GAME_CRAFTING": "Crafting",
"GAME_CRAFTABLE_ITEMS": "Craftable Items",
"MENU_LABEL_RENAME": "Rename",
"GAME_ACTION_TELEPORT": "Teleport", "GAME_ACTION_TELEPORT": "Teleport",
"CONTEXT_PLACE_COORDINATE": "Coordinate" "GAME_CRAFTABLE_ITEMS": "Craftable Items",
"GAME_CRAFTING": "Crafting",
"GAME_INVENTORY_BLOCKS": "Blocks",
"GAME_INVENTORY_FAVORITES": "Favorites",
"GAME_INVENTORY_INGREDIENTS": "Ingredients",
"GAME_INVENTORY_POTIONS": "Potions",
"GAME_INVENTORY_REGISTER": "Register",
"GAME_INVENTORY_WALLS": "Walls"
} }

View File

@@ -1,18 +1,20 @@
{ {
"BLOCK_STONE_DEEP": "Deepstone",
"BLOCK_SCAFFOLDING_NORMAL": "Scaffolding",
"BLOCK_STONE_MARBLE": "Marble",
"ITEM_CALENDAR": "Calendar",
"ITEM_LOGIC_SIGNAL_EMITTER": "Logic Signal Emitter",
"ITEM_STORAGE_CHEST": "Storage Chest",
"ITEM_TIKI_TORCH": "Tiki Torch",
"ITEM_TYPEWRITER": "Typewriter",
"ITEM_WIRE": "Wire",
"ITEM_WIRE_CUTTER": "Wire Cutter",
"ACTORBLOCK_ALLOW_MOVE_DOWN": "Urist Arôlcustith", "ACTORBLOCK_ALLOW_MOVE_DOWN": "Urist Arôlcustith",
"ACTORBLOCK_FULL_COLLISION": "Urist Berdanrifot", "ACTORBLOCK_FULL_COLLISION": "Urist Berdanrifot",
"ACTORBLOCK_NO_COLLISION": "Urist Zafal", "ACTORBLOCK_NO_COLLISION": "Urist Zafal",
"ACTORBLOCK_NO_PASS_RIGHT": "Urist McPassLeft", "ACTORBLOCK_NO_PASS_RIGHT": "Urist McPassLeft",
"ACTORBLOCK_NO_PASS_LEFT": "Urist McPassRight", "ACTORBLOCK_NO_PASS_LEFT": "Urist McPassRight",
"ACTORBLOCK_TILING_PLACEHOLDER": "Urist Berdanurdim", "ACTORBLOCK_TILING_PLACEHOLDER": "Urist Berdanurdim"
"BLOCK_STONE_DEEP": "Deepstone",
"BLOCK_SCAFFOLDING_NORMAL": "Scaffolding",
"BLOCK_STONE_MARBLE": "Marble",
"ITEM_STORAGE_CHEST": "Storage Chest",
"ITEM_WIRE": "Wire",
"ITEM_WIRE_CUTTER": "Wire Cutter",
"ITEM_LOGIC_SIGNAL_EMITTER": "Logic Signal Emitter",
"ITEM_TIKI_TORCH": "Tiki Torch"
} }

View File

@@ -0,0 +1,16 @@
{
"MENU_CALENDAR_CALENDAR": "달력",
"MENU_CALENDAR_EVENTS": "일정",
"MENU_CALENDAR_ADD_NEW_EVENT": "새 일정 추가",
"CONTEXT_CALENDAR_SEASON_SPRING": "봄",
"CONTEXT_CALENDAR_SEASON_SUMMER": "여름",
"CONTEXT_CALENDAR_SEASON_AUTUMN": "가을",
"CONTEXT_CALENDAR_SEASON_WINTER": "겨울",
"CONTEXT_CALENDAR_SEASON_SPRI": "봄",
"CONTEXT_CALENDAR_SEASON_SUMM": "여름",
"CONTEXT_CALENDAR_SEASON_AUTM": "가을",
"CONTEXT_CALENDAR_SEASON_WINT": "겨울",
"CONTEXT_CALENDAR_DATE_FORMAT_Y": "{0}년",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD": "{0}년 {1} {2}일",
"CONTEXT_CALENDAR_DATE_FORMAT_YMD_DDD": "{0}년 {1} {2}일 {3}"
}

View File

@@ -1,24 +1,25 @@
{ {
"CONTEXT_GENERATOR_SEED": "시드",
"CONTEXT_ITEM_MAP": "지도",
"CONTEXT_ITEM_TOOL_PLURAL": "도구",
"CONTEXT_PLACE_COORDINATE": "좌표",
"CONTEXT_WORLD_COUNT": "새계: ",
"CONTEXT_WORLD_NEW": "새 세계", "CONTEXT_WORLD_NEW": "새 세계",
"MENU_LABEL_DELETE_WORLD": "새계 삭제", "MENU_LABEL_DELETE_WORLD": "새계 삭제",
"CONTEXT_WORLD_COUNT": "새계: ",
"MENU_MONITOR_CALI_TITLE": "모니터 확인",
"GAME_INVENTORY_INGREDIENTS": "재료",
"GAME_INVENTORY_POTIONS": "물약",
"GAME_INVENTORY_BLOCKS": "블록",
"GAME_INVENTORY_WALLS": "벽지",
"CONTEXT_ITEM_TOOL_PLURAL": "도구",
"GAME_INVENTORY_FAVORITES": "즐겨찾기",
"GAME_INVENTORY_REGISTER": "등록하기",
"MENU_LABEL_MENU": "메뉴", "MENU_LABEL_MENU": "메뉴",
"CONTEXT_ITEM_MAP": "지도", "MENU_LABEL_PREV_SAVES": "이전 세이브",
"CONTEXT_GENERATOR_SEED": "시드", "MENU_LABEL_RENAME": "이름 바꾸기",
"MENU_MONITOR_CALI_TITLE": "모니터 확인",
"GAME_ACTION_CRAFT": "제작하기",
"GAME_ACTION_GRAPPLE": "매달리기", "GAME_ACTION_GRAPPLE": "매달리기",
"GAME_ACTION_QUICKSEL": "빠른 선택", "GAME_ACTION_QUICKSEL": "빠른 선택",
"GAME_ACTION_CRAFT": "제작하기",
"GAME_CRAFTING": "제작",
"GAME_CRAFTABLE_ITEMS": "제작 가능한 아이템",
"MENU_LABEL_RENAME": "이름 바꾸기",
"GAME_ACTION_TELEPORT": "텔레포트하기", "GAME_ACTION_TELEPORT": "텔레포트하기",
"CONTEXT_PLACE_COORDINATE": "좌표" "GAME_CRAFTABLE_ITEMS": "제작 가능한 아이템",
"GAME_CRAFTING": "제작",
"GAME_INVENTORY_BLOCKS": "블록",
"GAME_INVENTORY_FAVORITES": "즐겨찾기",
"GAME_INVENTORY_INGREDIENTS": "재료",
"GAME_INVENTORY_POTIONS": "물약",
"GAME_INVENTORY_REGISTER": "등록하기",
"GAME_INVENTORY_WALLS": "벽지"
} }

View File

@@ -3,9 +3,11 @@
"BLOCK_SCAFFOLDING_NORMAL": "발판", "BLOCK_SCAFFOLDING_NORMAL": "발판",
"BLOCK_STONE_MARBLE": "대리석", "BLOCK_STONE_MARBLE": "대리석",
"ITEM_STORAGE_CHEST": "보관상자", "ITEM_CALENDAR": "달력",
"ITEM_WIRE": "전선",
"ITEM_WIRE_CUTTER": "전선 절단기",
"ITEM_LOGIC_SIGNAL_EMITTER": "신호발생기", "ITEM_LOGIC_SIGNAL_EMITTER": "신호발생기",
"ITEM_TIKI_TORCH": "티키 토치" "ITEM_STORAGE_CHEST": "보관상자",
"ITEM_TIKI_TORCH": "티키 토치",
"ITEM_TYPEWRITER": "타자기",
"ITEM_WIRE": "전선",
"ITEM_WIRE_CUTTER": "전선 절단기"
} }

Binary file not shown.

View File

@@ -3,7 +3,8 @@ if (( $EUID == 0 )); then echo "The build process is not meant to be run with ro
cd "${0%/*}" cd "${0%/*}"
SRCFILES="terrarummac_arm" SRCFILES="terrarummac_arm"
DESTDIR="out/TerrarumMac.arm.app" APPDIR="./TerrarumMac.arm.app"
DESTDIR="out/$APPDIR"
RUNTIME="runtime-osx-arm" RUNTIME="runtime-osx-arm"
if [ ! -d "../assets_release" ]; then if [ ! -d "../assets_release" ]; then
@@ -34,4 +35,8 @@ cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/ cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
cd "out"
rm $APPDIR.zip
7z a -tzip $APPDIR.zip $APPDIR
echo "Build successful: $DESTDIR" echo "Build successful: $DESTDIR"

View File

@@ -3,7 +3,8 @@ if (( $EUID == 0 )); then echo "The build process is not meant to be run with ro
cd "${0%/*}" cd "${0%/*}"
SRCFILES="terrarummac_x86" SRCFILES="terrarummac_x86"
DESTDIR="out/TerrarumMac.x86.app" APPDIR="./TerrarumMac.x86.app"
DESTDIR="out/$APPDIR"
RUNTIME="runtime-osx-x86" RUNTIME="runtime-osx-x86"
if [ ! -d "../assets_release" ]; then if [ ! -d "../assets_release" ]; then
@@ -34,4 +35,8 @@ cp -r "../assets_release" $DESTDIR/Contents/MacOS/
mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets mv $DESTDIR/Contents/MacOS/assets_release $DESTDIR/Contents/MacOS/assets
cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/ cp "../out/TerrarumBuild.jar" $DESTDIR/Contents/MacOS/out/
cd "out"
rm $APPDIR.zip
7z a -tzip $APPDIR.zip $APPDIR
echo "Build successful: $DESTDIR" echo "Build successful: $DESTDIR"

25
buildapp/make_assets_release.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
if (( $EUID == 0 )); then echo "The build process is not meant to be run with root privilege, exiting now." >&2; exit 1; fi
cd "${0%/*}"
DESTDIR="../assets_release"
rm -r $DESTDIR
cp -r "../assets" $DESTDIR
rm $DESTDIR/loopey.wav
rm $DESTDIR/ktGrepExample.kts
rm $DESTDIR/batchtest.txt
rm $DESTDIR/test_texture.tga
rm $DESTDIR/worldbacktest.tga
rm -r $DESTDIR/books
rm $DESTDIR/clut/skybox.tga
rm $DESTDIR/graphics/*.bat
rm $DESTDIR/keylayout/*.not_ime
rm $DESTDIR/mods/basegame/blocks/*.gz
rm $DESTDIR/mods/basegame/blocks/*.txt
rm -r $DESTDIR/mods/basegame/sounds
rm -r $DESTDIR/mods/dwarventech

View File

@@ -0,0 +1,72 @@
package com.badlogic.gdx.graphics.glutils;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.utils.GdxRuntimeException;
import net.torvald.terrarum.App;
// typealias Float16FrameBuffer = FloatFrameBuffer
/**
* Created by minjaesong on 2023-08-16.
*/
public class Float16FrameBuffer extends FrameBuffer {
Float16FrameBuffer () {
}
/** Creates a GLFrameBuffer from the specifications provided by bufferBuilder
*
* @param bufferBuilder **/
protected Float16FrameBuffer (GLFrameBufferBuilder<? extends GLFrameBuffer<Texture>> bufferBuilder) {
super(bufferBuilder);
}
/** Creates a new FrameBuffer with a float backing texture, having the given dimensions and potentially a depth buffer
* attached.
*
* @param width the width of the framebuffer in pixels
* @param height the height of the framebuffer in pixels
* @param hasDepth whether to attach a depth buffer
* @throws GdxRuntimeException in case the FrameBuffer could not be created */
public Float16FrameBuffer (int width, int height, boolean hasDepth) {
if (App.isAppleM) { // disable float framebuffer for Apple M chips
FrameBufferBuilder bufferBuilder = new FrameBufferBuilder(width, height);
bufferBuilder.addColorTextureAttachment(GL20.GL_RGBA, GL20.GL_RGBA, GL20.GL_UNSIGNED_SHORT); // but 16bpp int works perfectly?!
if (hasDepth) bufferBuilder.addBasicDepthRenderBuffer();
this.bufferBuilder = bufferBuilder;
}
else {
FloatFrameBufferBuilder bufferBuilder = new FloatFrameBufferBuilder(width, height);
bufferBuilder.addFloatAttachment(GL30.GL_RGBA16F, GL30.GL_RGBA, GL30.GL_FLOAT, false); // FIXME sporadic black screen on GL_RGBA16F? or maybe it was Plasma bugging out?
if (hasDepth) bufferBuilder.addBasicDepthRenderBuffer();
this.bufferBuilder = bufferBuilder;
}
build();
}
@Override
protected Texture createTexture (FrameBufferTextureAttachmentSpec attachmentSpec) {
if (App.isAppleM) {
GLOnlyTextureData data = new GLOnlyTextureData(bufferBuilder.width, bufferBuilder.height, 0, attachmentSpec.internalFormat,
attachmentSpec.format, attachmentSpec.type);
Texture result = new Texture(data);
result.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
return result;
}
else {
FloatTextureData data = new FloatTextureData(bufferBuilder.width, bufferBuilder.height, attachmentSpec.internalFormat,
attachmentSpec.format, attachmentSpec.type, attachmentSpec.isGpuOnly);
Texture result = new Texture(data);
result.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
result.setWrap(Texture.TextureWrap.ClampToEdge, Texture.TextureWrap.ClampToEdge);
return result;
}
}
}

View File

@@ -192,6 +192,16 @@ fun CIEXYZ.toColorRaw(): Color {
return Color(rgb.r, rgb.g, rgb.b, rgb.alpha) return Color(rgb.r, rgb.g, rgb.b, rgb.alpha)
} }
fun CIEXYZ.toYXY(): CIEYXY {
val dot = this.X + this.Y + this.Z
return CIEYXY(
this.Y,
this.X / dot,
this.Y / dot,
this.alpha
)
}
fun CIEYXY.toXYZ(): CIEXYZ { fun CIEYXY.toXYZ(): CIEXYZ {
return CIEXYZ(x * yy / y, yy, (1f - x - y) * yy / y) return CIEXYZ(x * yy / y, yy, (1f - x - y) * yy / y)
} }

View File

@@ -12,16 +12,14 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.unicode.EMDASH import net.torvald.unicode.EMDASH
import net.torvald.colourutil.* import net.torvald.colourutil.*
import net.torvald.parametricsky.datasets.DatasetCIEXYZ import net.torvald.parametricsky.datasets.DatasetCIEXYZ
import net.torvald.parametricsky.datasets.DatasetRGB
import net.torvald.parametricsky.datasets.DatasetSpectral
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.clut.Skybox
import net.torvald.terrarum.clut.Skybox.coerceInSmoothly
import net.torvald.terrarum.clut.Skybox.mapCircle
import net.torvald.terrarum.inUse import net.torvald.terrarum.inUse
import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI
import net.torvald.terrarum.modulebasegame.worldgenerator.TWO_PI
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.Dimension import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.GridLayout
import java.lang.Math.pow import java.lang.Math.pow
import javax.swing.* import javax.swing.*
import kotlin.math.* import kotlin.math.*
@@ -94,7 +92,16 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
if (turbidity <= 0) throw IllegalStateException() if (turbidity <= 0) throw IllegalStateException()
// we need to use different model-state to accommodate different albedo for each spectral band but oh well... // we need to use different model-state to accommodate different albedo for each spectral band but oh well...
genTexLoop(model) genTexLoop(model, elevation)
// println("$elevation\t${ymaxDisp.text}\t${ymaxDisp2.text}")
/*for (elev in -75..75) {
val elevation = Math.toRadians(elev.toDouble())
val model = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevation.abs())
genTexLoop(model, elevation)
println("$elev\t${ymaxDisp.text}\t${ymaxDisp2.text}")
}*/
val tex = Texture(oneScreen) val tex = Texture(oneScreen)
@@ -127,7 +134,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
} }
val outTexWidth = 1 val outTexWidth = 1
val outTexHeight = 256 val outTexHeight = 128
private fun Float.scaleFun() = private fun Float.scaleFun() =
(1f - 1f / 2f.pow(this/6f)) * 0.97f (1f - 1f / 2f.pow(this/6f)) * 0.97f
@@ -142,14 +149,20 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
) )
} }
else { else {
val elevation1 = -Math.toDegrees(elevation) // maths model: https://www.desmos.com/calculator/cwi7iyzygg
val elevation2 = -Math.toDegrees(elevation) / 28.5
val scale = (1f - (1f - 1f / 1.8.pow(elevation1)) * 0.97f).toFloat() val x = -Math.toDegrees(elevation).toFloat()
val scale2 = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat() // val elevation2 = -Math.toDegrees(elevation) / 28.5
val p = 3.5f
val q = 7.5f
val s = -0.2f
val f = (1f - (1f - 1f / 1.8f.pow(x)) * 0.97f).toFloat()
// val g = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat()
val h = ((x / q).pow(p) + 1f).pow(s)
CIEXYZ( CIEXYZ(
this.X.scaleFun() * scale * scale2, this.X.scaleFun() * f * h,
this.Y.scaleFun() * scale * scale2, this.Y.scaleFun() * f * h,
this.Z.scaleFun() * scale * scale2, this.Z.scaleFun() * f * h,
this.alpha this.alpha
) )
} }
@@ -161,7 +174,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
* Generated texture is as if you took the panorama picture of sky: up 70deg to horizon, east-south-west; * Generated texture is as if you took the panorama picture of sky: up 70deg to horizon, east-south-west;
* with sun not moving (sun is at exact south, sun's height is adjustable) * with sun not moving (sun is at exact south, sun's height is adjustable)
*/ */
private fun genTexLoop(state: ArHosekSkyModelState) { private fun genTexLoop(state: ArHosekSkyModelState, elevation: Double) {
fun normaliseY(y: Double): Float { fun normaliseY(y: Double): Float {
var v = y.coerceAtLeast(0.0) var v = y.coerceAtLeast(0.0)
@@ -175,6 +188,7 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val ys2 = ArrayList<Float>() val ys2 = ArrayList<Float>()
val halfHeight = oneScreen.height * 0.5 val halfHeight = oneScreen.height * 0.5
val elevationDeg = Math.toDegrees(elevation)
for (x in 0 until oneScreen.width) { for (x in 0 until oneScreen.width) {
for (y in 0 until oneScreen.height) { for (y in 0 until oneScreen.height) {
@@ -186,17 +200,19 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val theta = sqrt(xf*xf + yf*yf) * HALF_PI*/ val theta = sqrt(xf*xf + yf*yf) * HALF_PI*/
// AM-PM mapping (use with WIDTH=1) // AM-PM mapping (use with WIDTH=1)
var yf = (y * 2.0 / oneScreen.height) % 1.0 val yp = y % (oneScreen.height / 2)
if (elevation < 0) yf *= 1.0 - pow(-elevation / HALF_PI, 0.333) val yi = yp - 3
val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf)
val theta = yf * HALF_PI
val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI val gamma = if (y < halfHeight) HALF_PI else 3 * HALF_PI
val theta = yf.mapCircle() * HALF_PI
val xyz = CIEXYZ( val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat() ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 2).toFloat(),
) )
val xyz2 = xyz.scaleToFit(elevation) val xyz2 = xyz.scaleToFit(elevation)
ys.add(xyz.Y) ys.add(xyz.Y)
@@ -204,12 +220,12 @@ class Application(val WIDTH: Int, val HEIGHT: Int) : Game() {
val rgb = xyz2.toRGB().toColor() val rgb = xyz2.toRGB().toColor()
rgb.a = 1f rgb.a = 1f
val rgb2 = Color( /*val rgb2 = Color(
((rgb.r * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.r * 255f).roundToInt() xor 0xAA) / 255f,
((rgb.g * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.g * 255f).roundToInt() xor 0xAA) / 255f,
((rgb.b * 255f).roundToInt() xor 0xAA) / 255f, ((rgb.b * 255f).roundToInt() xor 0xAA) / 255f,
rgb.a rgb.a
) )*/
oneScreen.setColor(rgb) oneScreen.setColor(rgb)
oneScreen.drawPixel(x, y) oneScreen.drawPixel(x, y)

View File

@@ -62,9 +62,11 @@ public class App implements ApplicationListener {
public static final String GAME_NAME = TerrarumAppConfiguration.GAME_NAME; public static final String GAME_NAME = TerrarumAppConfiguration.GAME_NAME;
public static final long VERSION_RAW = TerrarumAppConfiguration.VERSION_RAW; public static final long VERSION_RAW = TerrarumAppConfiguration.VERSION_RAW;
public static final String VERSION_TAG = TerrarumAppConfiguration.VERSION_TAG;
public static final String getVERSION_STRING() { public static final String getVERSION_STRING() {
return String.format("%d.%d.%d", VERSION_RAW >>> 48, (VERSION_RAW & 0xffff000000L) >>> 24, VERSION_RAW & 0xffffffL); return String.format("%d.%d.%d", VERSION_RAW >>> 48, (VERSION_RAW & 0xffff000000L) >>> 24, VERSION_RAW & 0xffffffL) +
(VERSION_TAG.isBlank() ? "" : "-"+VERSION_TAG);
} }
/** /**
@@ -267,12 +269,13 @@ public class App implements ApplicationListener {
Gdx.gl20.glViewport(0, 0, width, height); Gdx.gl20.glViewport(0, 0, width, height);
} }
public static final float UPDATE_RATE = 1f / 64f; // apparent framerate will be limited by update rate public static final int TICKS = 64;
public static final float UPDATE_RATE = 1f / TICKS; // apparent framerate will be limited by update rate
private static float loadTimer = 0f; private static float loadTimer = 0f;
private static final float showupTime = 100f / 1000f; private static final float showupTime = 100f / 1000f;
private static FloatFrameBuffer renderFBO; private static Float16FrameBuffer renderFBO;
public static HashSet<File> tempFilePool = new HashSet<>(); public static HashSet<File> tempFilePool = new HashSet<>();
@@ -609,19 +612,21 @@ public class App implements ApplicationListener {
private static void processScreenshotRequest(FrameBuffer fb) { private static void processScreenshotRequest(FrameBuffer fb) {
if (screenshotRequested) { if (screenshotRequested) {
String msg = "Screenshot taken";
FrameBufferManager.begin(fb); FrameBufferManager.begin(fb);
try { try {
Pixmap p = Pixmap.createFromFrameBuffer(0, 0, fb.getWidth(), fb.getHeight()); Pixmap p = Pixmap.createFromFrameBuffer(0, 0, fb.getWidth(), fb.getHeight());
PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, 9, true); PixmapIO.writePNG(Gdx.files.absolute(defaultDir+"/Screenshot-"+String.valueOf(System.currentTimeMillis())+".png"), p, 9, true);
p.dispose(); p.dispose();
Terrarum.INSTANCE.getIngame().sendNotification("Screenshot taken");
} }
catch (Throwable e) { catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
Terrarum.INSTANCE.getIngame().sendNotification("Failed to take screenshot: "+e.getMessage()); msg = ("Failed to take screenshot: "+e.getMessage());
} }
FrameBufferManager.end(); FrameBufferManager.end();
screenshotRequested = false; screenshotRequested = false;
Terrarum.INSTANCE.getIngame().sendNotification(msg);
} }
} }
@@ -750,12 +755,12 @@ public class App implements ApplicationListener {
(renderFBO.getWidth() != scr.getWidth() || (renderFBO.getWidth() != scr.getWidth() ||
renderFBO.getHeight() != scr.getHeight()) renderFBO.getHeight() != scr.getHeight())
) { ) {
renderFBO = new FloatFrameBuffer( renderFBO = new Float16FrameBuffer(
scr.getWidth(), scr.getWidth(),
scr.getHeight(), scr.getHeight(),
false false
); );
postProcessorOutFBO2 = new FloatFrameBuffer( postProcessorOutFBO2 = new Float16FrameBuffer(
scr.getWidth() * 2, scr.getWidth() * 2,
scr.getHeight() * 2, scr.getHeight() * 2,
false false

View File

@@ -62,9 +62,12 @@ basegame
* e.g. 0x02010034 will be translated as 2.1.52 * e.g. 0x02010034 will be translated as 2.1.52
* *
*/ */
const val VERSION_RAW: Long = 0x0000_000003_000002 const val VERSION_RAW: Long = 0x0000_000003_000003
// Commit counts up to the Release 0.3.0: 2259 // Commit counts up to the Release 0.3.0: 2259
// Commit counts up to the Release 0.3.1: 2278 // Commit counts up to the Release 0.3.1: 2278
// Commit counts up to the Release 0.3.2: 2732
const val VERSION_TAG: String = "test001"
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
// CONFIGURATION FOR TILE MAKER // // CONFIGURATION FOR TILE MAKER //

View File

@@ -9,6 +9,7 @@ import com.badlogic.gdx.math.Matrix4
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.App.IS_DEVELOPMENT_BUILD
import net.torvald.terrarum.gamecontroller.KeyToggler import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.ui.BasicDebugInfoWindow import net.torvald.terrarum.ui.BasicDebugInfoWindow
import net.torvald.terrarum.ui.Toolkit import net.torvald.terrarum.ui.Toolkit
@@ -150,9 +151,10 @@ object TerrarumPostProcessor : Disposable {
} }
// draw dev build notifiers // draw dev build notifiers
if (App.IS_DEVELOPMENT_BUILD && Terrarum.ingame != null) { // omitting this screws up HQ2X render for some reason
if (Terrarum.ingame != null) {
batch.inUse { batch.inUse {
batch.color = safeAreaCol batch.color = if (IS_DEVELOPMENT_BUILD) safeAreaCol else colourNull
App.fontGame.draw(it, thisIsDebugStr, 5f, App.scr.height - 24f) App.fontGame.draw(it, thisIsDebugStr, 5f, App.scr.height - 24f)
} }
} }
@@ -192,6 +194,7 @@ object TerrarumPostProcessor : Disposable {
return outFBO return outFBO
} }
private val rng = HQRNG() private val rng = HQRNG()
private val colourNull = Color(0)
private fun Double.format(digits: Int) = "%.${digits}f".format(this) private fun Double.format(digits: Int) = "%.${digits}f".format(this)
private fun Float.format(digits: Int) = "%.${digits}f".format(this) private fun Float.format(digits: Int) = "%.${digits}f".format(this)

View File

@@ -209,8 +209,8 @@ class BlockCodex {
prop.isSolid = record.boolVal("solid") prop.isSolid = record.boolVal("solid")
//prop.isClear = record.boolVal("clear") //prop.isClear = record.boolVal("clear")
prop.isPlatform = prop.tags.contains("PLATFORM") prop.isPlatform = prop.hasTag("PLATFORM")
prop.isActorBlock = prop.tags.contains("ACTORBLOCK") prop.isActorBlock = prop.hasTag("ACTORBLOCK")
prop.isWallable = record.boolVal("wall") prop.isWallable = record.boolVal("wall")
prop.maxSupport = record.intVal("grav") prop.maxSupport = record.intVal("grav")

View File

@@ -75,6 +75,8 @@ class BlockProp {
BlockCodex[BlockCodex.tileToVirtual[id]!![offset]]._lumCol.lane(channel) BlockCodex[BlockCodex.tileToVirtual[id]!![offset]]._lumCol.lane(channel)
} }
fun hasTag(s: String) = tags.contains(s)
/** /**
* @param luminosity * @param luminosity
*/ */

View File

@@ -19,7 +19,7 @@ fun main() {
// y: increasing turbidity (1.0 .. 10.0, in steps of 0.333) // y: increasing turbidity (1.0 .. 10.0, in steps of 0.333)
// x: elevations (-75 .. 75 in steps of 1, then albedo of [0.1, 0.3, 0.5, 0.7, 0.9]) // x: elevations (-75 .. 75 in steps of 1, then albedo of [0.1, 0.3, 0.5, 0.7, 0.9])
val texh = Skybox.gradSize * Skybox.turbCnt val texh = Skybox.gradSize * Skybox.turbCnt
val texw = Skybox.elevCnt * Skybox.albedoCnt val texw = Skybox.elevCnt * Skybox.albedoCnt * 2
val TGA_HEADER_SIZE = 18 val TGA_HEADER_SIZE = 18
val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26) val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26)
@@ -44,6 +44,7 @@ fun main() {
println("Generating texture atlas ($texw x $texh)...") println("Generating texture atlas ($texw x $texh)...")
// write pixels // write pixels
for (gammaPair in 0..1) {
for (albedo0 in 0 until Skybox.albedoCnt) { for (albedo0 in 0 until Skybox.albedoCnt) {
val albedo = Skybox.albedos[albedo0] val albedo = Skybox.albedos[albedo0]
println("Albedo=$albedo") println("Albedo=$albedo")
@@ -55,10 +56,11 @@ fun main() {
val elevationRad = Math.toRadians(elevationDeg) val elevationRad = Math.toRadians(elevationDeg)
// println("... Elevation: $elevationDeg") // println("... Elevation: $elevationDeg")
val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs()) val state =
ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs())
for (yp in 0 until Skybox.gradSize) { for (yp in 0 until Skybox.gradSize) {
val yi = yp - 3 val yi = yp - 10
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)
@@ -72,15 +74,15 @@ fun main() {
// println("$yp\t$theta") // println("$yp\t$theta")
val xyz = CIEXYZ( val xyz = CIEXYZ(
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 0).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 1).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 1).toFloat(),
ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 2).toFloat() ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, (gammaPair * 2f + 1f) * HALF_PI, 2).toFloat()
) )
val xyz2 = xyz.scaleToFit(elevationDeg) val xyz2 = xyz.scaleToFit(elevationDeg)
val rgb = xyz2.toRGB().toColor() val rgb = xyz2.toRGB().toColor()
val colour = rgb.toIntBits().toLittle() val colour = rgb.toIntBits().toLittle()
val imgOffX = (albedo0 * Skybox.elevCnt + elev0) val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair
val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp) val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp)
val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX)
for (i in 0..3) { for (i in 0..3) {
@@ -90,10 +92,11 @@ fun main() {
} }
} }
} }
}
println("Atlas generation done!") println("Atlas generation done!")
File("./assets/mods/basegame/weathers/main_skybox.tga").writeBytes(bytes) File("./assets/clut/skybox.tga").writeBytes(bytes)
} }
private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work

View File

@@ -13,6 +13,8 @@ import net.torvald.parametricsky.ArHosekSkyModel
import net.torvald.terrarum.App import net.torvald.terrarum.App
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.abs import net.torvald.terrarum.abs
import net.torvald.terrarum.floorToInt
import net.torvald.terrarum.toInt
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import kotlin.math.* import kotlin.math.*
@@ -25,7 +27,7 @@ object Skybox : Disposable {
private const val PI = 3.141592653589793 private const val PI = 3.141592653589793
private const val TWO_PI = 6.283185307179586 private const val TWO_PI = 6.283185307179586
const val gradSize = 64 const val gradSize = 78
private lateinit var gradTexBinLowAlbedo: Array<TextureRegion> private lateinit var gradTexBinLowAlbedo: Array<TextureRegion>
private lateinit var gradTexBinHighAlbedo: Array<TextureRegion> private lateinit var gradTexBinHighAlbedo: Array<TextureRegion>
@@ -37,6 +39,7 @@ object Skybox : Disposable {
fun loadlut() { fun loadlut() {
tex = Texture(Gdx.files.internal("assets/clut/skybox.png")) tex = Texture(Gdx.files.internal("assets/clut/skybox.png"))
tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear)
tex.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat)
texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1) texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1)
texStripRegions = TextureRegionPack(tex, elevCnt, gradSize - 2, 0, 2, 0, 1) texStripRegions = TextureRegionPack(tex, elevCnt, gradSize - 2, 0, 2, 0, 1)
} }
@@ -50,28 +53,67 @@ object Skybox : Disposable {
}*/ }*/
// use external LUT // use external LUT
operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion { operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double, isAfternoon: Boolean): TextureRegion {
val elev = elevationDeg.coerceIn(-elevMax, elevMax).roundToInt().plus(elevMax).roundToInt() TODO()
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt()
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(turbDivisor).roundToInt()
//printdbg(this, "elev $elevationDeg->$elev; turb $turbidity->$turb; alb $albedo->$alb")
return texRegions.get(alb * elevCnt + elev, turb)
} }
fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): Pair<Texture, FloatArray> { data class SkyboxRenderInfo(
val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(turbDivisor).roundToInt() val texture: Texture,
val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(turbDivisor).roundToInt() val uvs: FloatArray,
val region = texStripRegions.get(alb, turb) val turbidityPoint: Float,
val albedoPoint: Float,
)
val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt).times((elevCnt - 1.0) / elevCnt) fun getUV(elevationDeg: Double, turbidity: Double, albedo: Double): SkyboxRenderInfo {
val turb = turbidity.coerceIn(turbiditiesD.first(), turbiditiesD.last()).minus(1.0).times(turbDivisor)
val turbLo = turb.floorToInt()
val turbHi = min(turbCnt - 1, turbLo + 1)
val alb = albedo.coerceIn(albedos.first(), albedos.last()).times(5.0)
val albLo = alb.floorToInt()
val albHi = min(albedoCnt - 1, albLo + 1)
val elev = elevationDeg.coerceIn(-elevMax, elevMax).plus(elevMax).div(elevations.last.toDouble()).div(albedoCnt * 2).times((elevCnt - 1.0) / elevCnt)
val u = region.u + (0.5f / tex.width) + elev.toFloat() // because of the nature of bilinear interpolation, half pixels from the edges must be discarded // A: morn, turbLow, albLow
// B: noon, turbLow, albLow
// C: morn, turbHigh, albLow
// D: noon, turbHigh, albLow
// E: morn, turbLow, albHigh
// F: noon, turbLow, albHigh
// G: morn, turbHigh, albHigh
// H: noon, turbHigh, albHigh
return tex to floatArrayOf( val regionA = texStripRegions.get(albLo + albedoCnt * 0, turbLo)
u, val regionB = texStripRegions.get(albLo + albedoCnt * 1, turbLo)
region.v, val regionC = texStripRegions.get(albLo + albedoCnt * 0, turbHi)
u, val regionD = texStripRegions.get(albLo + albedoCnt * 1, turbHi)
region.v2 val regionE = texStripRegions.get(albHi + albedoCnt * 0, turbLo)
val regionF = texStripRegions.get(albHi + albedoCnt * 1, turbLo)
val regionG = texStripRegions.get(albHi + albedoCnt * 0, turbHi)
val regionH = texStripRegions.get(albHi + albedoCnt * 1, turbHi)
// (0.5f / tex.width): because of the nature of bilinear interpolation, half pixels from the edges must be discarded
val uA = regionA.u + (0.5f / tex.width) + elev.toFloat()
val uB = regionB.u + (0.5f / tex.width) + elev.toFloat()
val uC = regionC.u + (0.5f / tex.width) + elev.toFloat()
val uD = regionD.u + (0.5f / tex.width) + elev.toFloat()
val uE = regionE.u + (0.5f / tex.width) + elev.toFloat()
val uF = regionF.u + (0.5f / tex.width) + elev.toFloat()
val uG = regionG.u + (0.5f / tex.width) + elev.toFloat()
val uH = regionH.u + (0.5f / tex.width) + elev.toFloat()
return SkyboxRenderInfo(
tex,
floatArrayOf(
uA, regionA.v, uA, regionA.v2,
uB, regionB.v, uB, regionB.v2,
uC, regionC.v, uC, regionC.v2,
uD, regionD.v, uD, regionD.v2,
uE, regionE.v, uE, regionE.v2,
uF, regionF.v, uF, regionF.v2,
uG, regionG.v, uG, regionG.v2,
uH, regionH.v, uH, regionH.v2,
),
(turb - turbLo).toFloat(),
(alb - albLo).toFloat(),
) )
} }
@@ -88,15 +130,20 @@ object Skybox : Disposable {
) )
} }
else { else {
val deg1 = (-elevationDeg / elevMax).pow(0.93).times(-elevMax) // maths model: https://www.desmos.com/calculator/cwi7iyzygg
val elevation1 = -deg1
val elevation2 = -deg1 / 28.5 val x = -elevationDeg.toFloat()
val scale = (1f - (1f - 1f / 1.8.pow(elevation1)) * 0.97f).toFloat() // val elevation2 = elevationDeg.toFloat() / 28.5f
val scale2 = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat() val p = 3.5f
val q = 7.5f
val s = -0.2f
val f = (1f - (1f - 1f / 1.8f.pow(x)) * 0.97f).toFloat()
// val g = (1.0 - (elevation2.pow(E) / E.pow(elevation2))*0.8).toFloat()
val h = ((x / q).pow(p) + 1f).pow(s)
CIEXYZ( CIEXYZ(
this.X.scaleFun() * scale * scale2, this.X.scaleFun() * f * h,
this.Y.scaleFun() * scale * scale2, this.Y.scaleFun() * f * h,
this.Z.scaleFun() * scale * scale2, this.Z.scaleFun() * f * h,
this.alpha this.alpha
) )
} }
@@ -105,15 +152,13 @@ object Skybox : Disposable {
val elevations = (0..150) val elevations = (0..150)
val elevMax = elevations.last / 2.0 val elevMax = elevations.last / 2.0
val elevationsD = elevations.map { -elevMax + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast) val elevationsD = elevations.map { -elevMax + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast)
val turbidities = (0..45) // 1, 1.2, 1.4, 1.6, ..., 10.0 val turbidities = (0..25) // 1, 1.2, 1.4, 1.6, ..., 6.0
val turbDivisor = 5.0 val turbDivisor = 5.0
val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor } val turbiditiesD = turbidities.map { 1.0 + it / turbDivisor }
val albedos = arrayOf(0.1, 0.3, 0.5, 0.7, 0.9) val albedos = arrayOf(0.0, 0.2, 0.4, 0.6, 0.8, 1.0)
val elevCnt = elevations.count() val elevCnt = elevations.count()
val turbCnt = turbidities.count() val turbCnt = turbidities.count()
val albedoCnt = albedos.size val albedoCnt = albedos.size
val albedoLow = 0.1
val albedoHight = 0.8 // for theoretical "winter wonderland"?
val gamma = HALF_PI val gamma = HALF_PI
internal fun Double.mapCircle() = sin(HALF_PI * this) internal fun Double.mapCircle() = sin(HALF_PI * this)
@@ -121,8 +166,7 @@ object Skybox : Disposable {
internal fun initiate() { internal fun initiate() {
printdbg(this, "Initialising skybox model") printdbg(this, "Initialising skybox model")
gradTexBinLowAlbedo = getTexturmaps(albedoLow) TODO()
gradTexBinHighAlbedo = getTexturmaps(albedoHight)
App.disposables.add(this) App.disposables.add(this)
@@ -170,6 +214,11 @@ object Skybox : Disposable {
/** /**
* To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl * To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl
*
* Sidenote: the original model involved two cosine curves, but since its Taylor series begins with x^2, I figured
* quadratic curve ought to be good enough, and the error against the original model was below 1/255 for
* reasonable range of p, and that's the reason I stopped at x^2 rather than also taking x^4 into the approximated
* model that is the code below.
*/ */
internal fun smoothLinear(p: Double, x0: Double): Double { internal fun smoothLinear(p: Double, x0: Double): Double {
val x = x0 - 0.5 val x = x0 - 0.5
@@ -198,7 +247,7 @@ object Skybox : Disposable {
// printdbg(this, "elev $elevationDeg turb $turbidity") // printdbg(this, "elev $elevationDeg turb $turbidity")
for (yp in 0 until gradSize) { for (yp in 0 until gradSize) {
val yi = yp - 3 val yi = yp - 10
val xf = -elevationDeg / 90.0 val xf = -elevationDeg / 90.0
var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95)

View File

@@ -36,7 +36,7 @@ internal object Authenticator : ConsoleCommand {
println("auth passwd: '$pwd'") println("auth passwd: '$pwd'")
println("hash: $hashedPwd") println("hash: $hashedPwd")
if ("09ccf5067db6f58265b004829e33e715e819ba0984f1e1fcef49c36fcd5f745f".equals(hashedPwd, ignoreCase = true)) { if ("2d962f949f55906ac47f16095ded190c9e44d95920259b8f36c2e54bd75df173".equals(hashedPwd, ignoreCase = true)) {
// beedle // beedle
val msg = if (a) "Locked" else "Authenticated" val msg = if (a) "Locked" else "Authenticated"
Echo(msg) Echo(msg)

View File

@@ -18,6 +18,7 @@ import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.gameworld.BlockAddress import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.worlddrawer.WorldCamera import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
@@ -554,7 +555,6 @@ open class ActorWithBody : Actor {
*/ */
if (!isNoCollideWorld) { if (!isNoCollideWorld) {
displaceHitbox() displaceHitbox()
//collisionInterpolatorRun()
} }
else { else {
stairPenaltyCounter = 999 stairPenaltyCounter = 999
@@ -673,16 +673,6 @@ open class ActorWithBody : Actor {
// this is "close enough" solution and not perfect. // this is "close enough" solution and not perfect.
} }
/**
* Event for collision (event gets fired when it collided with the world or other actors)
*
* This event may fired two or more times per update.
*/
open fun collided(other: Array<CollisionMessage>) {
}
data class CollisionMessage(val targetID: Int, val AkspfisWorld: Boolean)
/** /**
* Apply gravitation to the every falling body (unless not levitating) * Apply gravitation to the every falling body (unless not levitating)
* *
@@ -698,6 +688,7 @@ open class ActorWithBody : Actor {
} }
private fun displaceHitbox() { private fun displaceHitbox() {
val printdbg1 = true && App.IS_DEVELOPMENT_BUILD
// // HOW IT SHOULD WORK // // // // HOW IT SHOULD WORK // //
// //////////////////////// // ////////////////////////
// combineVeloToMoveDelta now // combineVeloToMoveDelta now
@@ -724,44 +715,6 @@ open class ActorWithBody : Actor {
if (world != null) { if (world != null) {
fun debug1(wut: Any?) {
// vvvvv set it true to make debug print work
if (false) printdbg(this, wut)
}
fun debug2(wut: Any?) {
// vvvvv set it true to make debug print work
if (false) printdbg(this, wut)
}
fun debug3(wut: Any?) {
// vvvvv set it true to make debug print work
if (false) printdbg(this, wut)
}
fun debug4(wut: Any?) {
// vvvvv set it true to make debug print work
if (false) printdbg(this, wut)
}
fun BlockAddress.isFeetTile(hitbox: Hitbox): Boolean {
val (x, y) = LandUtil.resolveBlockAddr(world!!, this)
val newTilewiseHitbox = Hitbox.fromTwoPoints(
hitbox.startX.div(TILE_SIZE).floorToDouble(),
hitbox.startY.div(TILE_SIZE).floorToDouble(),
hitbox.endX.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floorToDouble(),
hitbox.endY.minus(PHYS_EPSILON_DIST).div(TILE_SIZE).floorToDouble(),
true
)
// offset 1 pixel to the down so that friction would work
val yMatch = if (gravitation.y >= 0.0)
hitbox.endY.plus(A_PIXEL).div(TILE_SIZE).floorToInt()
else
hitbox.startY.minus(A_PIXEL).div(TILE_SIZE).floorToInt()
return y == yMatch && // copied from forEachFeetTileNum
(x in newTilewiseHitbox.startX.toInt()..newTilewiseHitbox.endX.toInt()) // copied from forEachOccupyingTilePos
}
fun Double.modTile() = this.div(TILE_SIZE).floorToInt().times(TILE_SIZE) fun Double.modTile() = this.div(TILE_SIZE).floorToInt().times(TILE_SIZE)
fun Double.modTileDelta() = this - this.modTile() fun Double.modTileDelta() = this - this.modTile()
@@ -777,10 +730,27 @@ open class ActorWithBody : Actor {
// the job of the ccd is that the "next hitbox" would not dig into the terrain greater than the tile size, // the job of the ccd is that the "next hitbox" would not dig into the terrain greater than the tile size,
// in which the modTileDelta returns a wrong value // in which the modTileDelta returns a wrong value
val vectorSum = (externalV + controllerV) val vectorSum = (externalV + controllerV)
val ccdSteps = (vectorSum.magnitude / TILE_SIZE).floorToInt().coerceIn(2, 16) // adaptive val ccdSteps = (vectorSum.magnitude / TILE_SIZE).ceilToInt().plus(1).times(2).coerceIn(0, 64) // adaptive
fun debug1(wut: Any?) {
// vvvvv set it true to make debug print work
if (printdbg1 && vectorSum.magnitudeSquared != 0.0) printdbg(this, wut)
}
fun debug2(wut: Any?) {
// vvvvv set it true to make debug print work
if (true && printdbg1 && vectorSum.magnitudeSquared != 0.0) printdbg(this, wut)
}
if (printdbg1 && vectorSum.magnitudeSquared != 0.0) println("")
debug1("Update Frame: ${INGAME.WORLD_UPDATE_TIMER}")
debug1("Hitbox: ${hitbox}")
debug1("vec dir: ${if (vectorSum.isZero) "." else Math.toDegrees(vectorSum.direction)} deg, value: $vectorSum, magnitude: ${vectorSum.magnitude}")
// * NEW idea: wall pushes the actors (ref. SM64 explained by dutch pancake) * // * NEW idea: wall pushes the actors (ref. SM64 explained by dutch pancake) *
// direction to push is determined by the velocity // direction to push is determined by the velocity
// proc: // proc:
@@ -793,63 +763,63 @@ open class ActorWithBody : Actor {
// ignore MOST of the codes below (it might be possible to recycle the structure??) // ignore MOST of the codes below (it might be possible to recycle the structure??)
// and the idea above has not yet implemented, and may never will. --Torvald, 2018-12-30 // and the idea above has not yet implemented, and may never will. --Torvald, 2018-12-30
val sixteenStep = (0..ccdSteps).map { hitbox.clone().translate(vectorSum * (it / ccdSteps.toDouble())) } // zeroth step is for special condition val downDown = if (this is ActorHumanoid) this.downButtonHeld > 0 else false
val sixteenStep = (0..ccdSteps).map { hitbox.clone().translate(vectorSum * (it / ccdSteps.toDouble())) }
var collidingStep: Int? = null var collidingStep: Int? = null
for (step in 1..ccdSteps) { for (step in 0..ccdSteps) {
val stepBox = sixteenStep[step] val stepBox = sixteenStep[step]
/*forEachOccupyingTilePos(stepBox) { val goingDownwardDirection = Math.toDegrees(vectorSum.direction).let { it > 0.0 && it < 180.0 }
val tileCoord = LandUtil.resolveBlockAddr(world!!, it) val goingDown = (vectorSum.y >= PHYS_EPSILON_VELO || (vectorSum.y.absoluteValue < PHYS_EPSILON_VELO && !isWalled(stepBox, COLLIDING_BOTTOM))) // TODO reverse gravity adaptation?
val tile = world!!.getTileFromTerrain(tileCoord.first, tileCoord.second) ?: Block.STONE
if (shouldICollideWithThis(tile) || (it.isFeetTile(stepBox) && shouldICollideWithThisFeet(tile))) { debug2("stepbox[$step]=$stepBox; goingDown=$goingDown, downDown=$downDown, goingDownwardDirection=$goingDownwardDirection")
collidingStep = step
}
}*/
// trying to use same function as the others, in an effort to eliminate the "contradiction" mentioned below if (collidingStep == null && isColliding(stepBox, goingDown && !downDown && goingDownwardDirection)) {
if (isColliding(stepBox, vectorSum.y > PHYS_EPSILON_DIST)) {
collidingStep = step collidingStep = step
} }
if (collidingStep != null) break // if (collidingStep != null) break
} }
var bounceX = false var bounceX = false
var bounceY = false var bounceY = false
var zeroX = false var zeroX = false
var zeroY = false var zeroY = false
val newHitbox = if (collidingStep == null) null else hitbox.clone().reassign(sixteenStep[collidingStep])
var staircaseStatus = 0
var stairHeightLeft = 0.0
var stairHeightRight = 0.0
val selfCollisionStatus = if (newHitbox == null) 0 else {
intArrayOf(1, 2, 4, 8).fold(0) { acc, state ->
val (isWalled, stairHeight) = isWalledStairs(newHitbox, state)
// also update staircaseStatus while we're iterating
if (state and COLLIDING_LR != 0) {
staircaseStatus = staircaseStatus or (state * (isWalled == 1).toInt())
if (state == COLLIDING_LEFT)
stairHeightLeft = stairHeight.toDouble()
else
stairHeightRight = stairHeight.toDouble()
}
acc or (state * isWalled.coerceAtMost(1)) // TODO reverse gravity adaptation?
}
}
// collision NOT detected // collision NOT detected
if (collidingStep == null) { if (collidingStep == null) {
debug1("== Collision step: no collision")
hitbox.translate(vectorSum) hitbox.translate(vectorSum)
// grounded = false // grounded = false
} }
// collision detected // collision detected
else { else {
debug1("== Collision step: $collidingStep / $ccdSteps") debug1("== Collision step: $collidingStep / $ccdSteps")
debug1("CCD hitbox: ${newHitbox}")
val newHitbox = newHitbox!!
val newHitbox = hitbox.reassign(sixteenStep[collidingStep])
var staircaseStatus = 0
var stairHeightLeft = 0.0
var stairHeightRight = 0.0
val selfCollisionStatus = intArrayOf(1,2,4,8).fold(0) { acc, state ->
// also update staircaseStatus while we're iterating
if (state and 5 != 0) {
isWalledStairs(newHitbox, state).let {
staircaseStatus = staircaseStatus or (state * (it.first == 1).toInt())
if (state == COLLIDING_LEFT)
stairHeightLeft = it.second.toDouble()
else
stairHeightRight = it.second.toDouble()
}
}
acc or (state * isWalledStairs(newHitbox, state).first.coerceAtMost(1))
}
// superseded by isWalledStairs-related codes // superseded by isWalledStairs-related codes
//if (isWalled(newHitbox, COLLIDING_LEFT)) selfCollisionStatus += COLL_LEFTSIDE // 1 //if (isWalled(newHitbox, COLLIDING_LEFT)) selfCollisionStatus += COLL_LEFTSIDE // 1
@@ -859,37 +829,51 @@ open class ActorWithBody : Actor {
// fixme UP and RIGHT && LEFT and DOWN bug // fixme UP and RIGHT && LEFT and DOWN bug
//println("collision: $selfCollisionStatus\tstaircasing: $staircaseStatus") debug1("collision: $selfCollisionStatus\tstaircasing: $staircaseStatus")
when (selfCollisionStatus) { when (selfCollisionStatus) {
0 -> { /*0 -> {
debug1("[ActorWBMovable] Contradiction -- collision detected by CCD, but isWalled() says otherwise") debug1("Contradiction -- collision detected by CCD, but isWalled() says otherwise")
} }*/
5 -> { /* 5 */ COLLIDING_LR -> {
zeroX = true zeroX = true
} }
10 -> { /* 10 */ COLLIDING_UD -> {
zeroY = true zeroY = true
} }
15 -> { /* 15 */ COLLIDING_ALLSIDE -> {
newHitbox.reassign(sixteenStep[0]); zeroX = true; zeroY = true newHitbox.reassign(sixteenStep[0]); zeroX = true; zeroY = true
debug1("reassining hitbox to ${sixteenStep[0]}")
} }
// one-side collision // one-side collision
1, 11 -> { /* 1, 11 */ COLLIDING_LEFT, COLLIDING_LEFT or COLLIDING_UD -> {
newHitbox.translatePosX(TILE_SIZE - newHitbox.startX.modTileDelta()); bounceX = true val t = TILE_SIZE - newHitbox.startX.modTileDelta()
newHitbox.translatePosX(t); bounceX = true
debug1("translate x by $t")
} }
4, 14 -> { /* 4, 14 */ COLLIDING_RIGHT, COLLIDING_RIGHT or COLLIDING_UD -> {
newHitbox.translatePosX(-newHitbox.endX.modTileDelta()); bounceX = true val t = -newHitbox.endX.modTileDelta()
newHitbox.translatePosX(t); bounceX = true
debug1("translate x by $t")
} }
8, 13 -> { /* 8, 13 */ COLLIDING_TOP, COLLIDING_TOP or COLLIDING_LR -> {
newHitbox.translatePosY(TILE_SIZE - newHitbox.startY.modTileDelta()); bounceY = true val t = TILE_SIZE - newHitbox.startY.modTileDelta()
newHitbox.translatePosY(t); bounceY = true
debug1("translate y by $t")
} }
2, 7 -> { /* 2, 7 */ COLLIDING_BOTTOM, COLLIDING_BOTTOM or COLLIDING_LR -> {
newHitbox.translatePosY(-newHitbox.endY.modTileDelta()); bounceY = true val t = -newHitbox.endY.modTileDelta()
newHitbox.translatePosY(t); bounceY = true
debug1("translate y by $t")
} }
} }
if (selfCollisionStatus == 0) {
debug1("== selfCollisionStatus was zero, behaving as if (collidingStep = null)")
hitbox.translate(vectorSum)
}
else {
// fire Collision Event with one/two/three-side collision // fire Collision Event with one/two/three-side collision
// for the ease of writing, this jumptable is separated from above. // for the ease of writing, this jumptable is separated from above.
when (selfCollisionStatus) { when (selfCollisionStatus) {
@@ -928,7 +912,6 @@ open class ActorWithBody : Actor {
newHitbox.startY newHitbox.startY
val angleOfIncidence = val angleOfIncidence =
if (selfCollisionStatus in listOf(3, 9)) if (selfCollisionStatus in listOf(3, 9))
vectorSum.direction.toPositiveRad() vectorSum.direction.toPositiveRad()
@@ -943,10 +926,15 @@ open class ActorWithBody : Actor {
(Vector2(offendingHitboxPointX, offendingHitboxPointY) - (Vector2(offendingHitboxPointX, offendingHitboxPointY) -
Vector2(offendingTileWorldX, offendingTileWorldY)).direction Vector2(offendingTileWorldX, offendingTileWorldY)).direction
debug1(
"incidentAngle: ${Math.toDegrees(angleOfIncidence)}°, threshold: ${
Math.toDegrees(
angleThreshold
)
}°"
)
debug1("vectorSum: $vectorSum, vectorDirRaw: ${vectorSum.direction / Math.PI}pi") debug1("offendingTileWorldY=$offendingTileWorldY, offendingHitboxPointY=$offendingHitboxPointY")
debug1("incidentAngle: ${angleOfIncidence / Math.PI}pi, threshold: ${angleThreshold / Math.PI}pi")
val displacementAbs = Vector2( val displacementAbs = Vector2(
(offendingTileWorldX - offendingHitboxPointX).abs(), (offendingTileWorldX - offendingHitboxPointX).abs(),
@@ -958,35 +946,57 @@ open class ActorWithBody : Actor {
val displacementUnitVector = val displacementUnitVector =
if (angleOfIncidence == angleThreshold) if (angleOfIncidence == angleThreshold)
-vectorSum -vectorSum.signum
else { else {
when (selfCollisionStatus) { when (selfCollisionStatus) {
3 -> if (angleOfIncidence > angleThreshold) Vector2(1.0, 0.0) else Vector2(0.0, -1.0) 3 -> if (angleOfIncidence > angleThreshold) Vector2(1.0, 0.0)
6 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, -1.0) else Vector2(-1.0, 0.0) else Vector2(
0.0,
-1.0
)
6 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, -1.0)
else Vector2(
-1.0,
0.0
)
9 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, 1.0) else Vector2(1.0, 0.0) 9 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, 1.0) else Vector2(1.0, 0.0)
12 -> if (angleOfIncidence > angleThreshold) Vector2(-1.0, 0.0) else Vector2(0.0, 1.0) 12 -> if (angleOfIncidence > angleThreshold) Vector2(-1.0, 0.0)
else Vector2(
0.0,
1.0
)
else -> throw InternalError("Blame hardware or universe") else -> throw InternalError("Blame hardware or universe")
} }
} }
val finalDisplacement = val finalDisplacement =
if (angleOfIncidence == angleThreshold) // if (angleOfIncidence == angleThreshold)
displacementUnitVector // displacementUnitVector
else // else
Vector2( Vector2(
displacementAbs.x * displacementUnitVector.x, displacementAbs.x * displacementUnitVector.x,
displacementAbs.y * displacementUnitVector.y displacementAbs.y * displacementUnitVector.y
) )
debug1("displacementAbs=$displacementAbs")
debug1("displacementUnitVector=$displacementUnitVector")
debug1("finalDisplacement=$finalDisplacement")
// adjust finalDisplacement for honest-to-god staircasing // adjust finalDisplacement for honest-to-god staircasing
if (physProp.useStairs && vectorSum.y <= 0.0 && staircaseStatus in listOf(1, 4) && selfCollisionStatus in (if (gravitation.y >= 0.0) listOf(3,6) else listOf(9, 12))) { if (physProp.useStairs && vectorSum.y <= 0.0 && staircaseStatus in listOf(1, 4) &&
selfCollisionStatus in (if (gravitation.y >= 0.0) listOf(3, 6) else listOf(9, 12))
) {
// remove Y displacement // remove Y displacement
// let original X velocity to pass-thru instead of snapping to tiles coded above // let original X velocity to pass-thru instead of snapping to tiles coded above
// pass-thru values are held by the vectorSum // pass-thru values are held by the vectorSum
//println("staircasing: $staircaseStatus for $selfCollisionStatus") debug1("staircasing: $staircaseStatus for $selfCollisionStatus")
val stairHeight = if (staircaseStatus == COLLIDING_LEFT) stairHeightLeft else stairHeightRight val stairHeight =
if (staircaseStatus == COLLIDING_LEFT) stairHeightLeft else stairHeightRight
finalDisplacement.y = -stairHeight finalDisplacement.y = -stairHeight
finalDisplacement.x = vectorSum.x finalDisplacement.x = vectorSum.x
@@ -999,7 +1009,8 @@ open class ActorWithBody : Actor {
// so we also zero the same exact value here for perfect hiding // so we also zero the same exact value here for perfect hiding
if (controllerV != null) { if (controllerV != null) {
val stairRatio = stairHeight / hitbox.height val stairRatio = stairHeight / hitbox.height
stairPenaltyVector = Math.pow(1.0 - (stairRatio), 90 * stairRatio).times(10).coerceIn(0.00005, 1.0) stairPenaltyVector =
Math.pow(1.0 - (stairRatio), 90 * stairRatio).times(10).coerceIn(0.00005, 1.0)
controllerV!!.x = 0.0 controllerV!!.x = 0.0
stairPenaltyCounter = 0 stairPenaltyCounter = 0
stairPenaltyMax = Math.pow(stairRatio, 2.4).times(166).roundToInt().coerceAtMost(64) stairPenaltyMax = Math.pow(stairRatio, 2.4).times(166).roundToInt().coerceAtMost(64)
@@ -1022,7 +1033,6 @@ open class ActorWithBody : Actor {
} }
// bounce X/Y // bounce X/Y
if (bounceX) { if (bounceX) {
externalV.x *= elasticity externalV.x *= elasticity
@@ -1043,6 +1053,7 @@ open class ActorWithBody : Actor {
hitbox.reassign(newHitbox) hitbox.reassign(newHitbox)
debug1("resulting hitbox: $newHitbox")
// slam-into-whatever damage (such dirty; much hack; wow) // slam-into-whatever damage (such dirty; much hack; wow)
@@ -1056,104 +1067,87 @@ open class ActorWithBody : Actor {
// grounded = true // grounded = true
// another platform-related hacks // another platform-related hacks
if (this is ActorHumanoid) downDownVirtually = false // if (this is ActorHumanoid) downButtonHeld = false
}
}// end of collision not detected }// end of collision not detected
return return
// if collision not detected, just don't care; it's not your job to apply moveDelta // if collision not detected, just don't care; it's not your job to apply moveDelta
} } // end of (world != null)
}
fun collisionInterpolatorRun() {
fun isWalled2(hitbox: Hitbox, option: Int): Boolean {
val newHB = Hitbox.fromTwoPoints(
hitbox.startX + A_PIXEL, hitbox.startY + A_PIXEL,
hitbox.endX - A_PIXEL, hitbox.endY - A_PIXEL
)
return isWalled(newHB, option)
}
// kinda works but the jump is inconsistent because of the nondeterministic nature of the values (doesn't get fixed to the integer value when collided)
if (world != null) {
val intpStep = 64.0
// make interpolation even if the "next" position is clear of collision
val testHitbox = hitbox.clone()
// divide the vectors by the constant
externalV /= intpStep
controllerV?.let { controllerV!! /= intpStep }
repeat(intpStep.toInt()) { // basically we don't care if we're already been encased (e.g. entrapped by falling sand)
// change the order and the player gets stuck in the vertical wall
// so leave as-is
testHitbox.translate(externalV + controllerV)
// vertical collision
if (isWalled2(testHitbox, COLLIDING_UD)) {
externalV.y *= elasticity // TODO also multiply the friction value
controllerV?.let { controllerV!!.y *= elasticity } // TODO also multiply the friction value
}
// horizontal collision
if (isWalled2(testHitbox, COLLIDING_LR)) {
externalV.x *= elasticity // TODO also multiply the friction value
controllerV?.let { controllerV!!.x *= elasticity } // TODO also multiply the friction value
}
}
hitbox.reassign(testHitbox)
// revert the division because the Acceleration depends on being able to integrate onto the velo values
// don't want to mess up with the value they're expecting
externalV *= intpStep
controllerV?.let { controllerV!! *= intpStep }
// TODO collision damage
}
} }
/** /**
* @see /work_files/hitbox_collision_detection_compensation.jpg * @see /work_files/hitbox_collision_detection_compensation.jpg
*/ */
private fun isColliding(hitbox: Hitbox, feet: Boolean = false): Boolean { private fun isColliding(hitbox: Hitbox, usePlatformDetection: Boolean = false): Boolean {
if (isNoCollideWorld) return false if (isNoCollideWorld) return false
// detectors are inside of the bounding box // detectors are inside of the bounding box
// CONFIRMED
val x1 = hitbox.startX val x1 = hitbox.startX
val x2 = hitbox.endX - A_PIXEL val y1 = hitbox.startY - if (gravitation.y < 0) HALF_PIXEL else 0.0
val y1 = hitbox.startY val x2 = hitbox.endX - PHYS_EPSILON_DIST
val y2 = hitbox.endY - A_PIXEL val y2 = hitbox.endY + if (gravitation.y >= 0) HALF_PIXEL else 0.0 // PLUS HALF PIXEL AND NOT MINUS EPSILON to fix issue #48 and #49
// this commands and the commands on isWalled WILL NOT match (1 px gap on endX/Y). THIS IS INTENTIONAL! // this commands and the commands on isWalled WILL NOT match (1 px gap on endX/Y). THIS IS INTENTIONAL!
val txStart = x1.plus(HALF_PIXEL).floorToInt() val txStart = x1/*.plus(HALF_PIXEL)*/.floorToInt()
val txEnd = x2.plus(HALF_PIXEL).floorToInt() val txEnd = x2/*.plus(HALF_PIXEL)*/.floorToInt()
val tyStart = y1.plus(HALF_PIXEL).floorToInt() val tyStart = y1/*.plus(HALF_PIXEL)*/.floorToInt()
val tyEnd = y2.plus(HALF_PIXEL).floorToInt() val tyEnd = y2/*.plus(HALF_PIXEL)*/.floorToInt()
return isCollidingInternalStairs(txStart, tyStart, txEnd, tyEnd, feet).first > 0 // return isCollidingInternalStairs(txStart, if (feet) tyEnd else tyStart, txEnd, tyEnd, feet).first > 0
return isCollidingInternalStairs(txStart, tyStart, txEnd, tyEnd, usePlatformDetection).first > 0
} }
private fun Hitbox.getWallDetection(option: Int): List<Double> {
val x1: Double
val x2: Double
val y1: Double
val y2: Double
when (option) {
COLLIDING_TOP -> {
x1 = this.startX
x2 = this.endX - PHYS_EPSILON_DIST
y1 = this.startY - A_PIXEL
y2 = y1
}
COLLIDING_BOTTOM -> {
x1 = this.startX
x2 = this.endX - PHYS_EPSILON_DIST
y1 = this.endY - PHYS_EPSILON_DIST + A_PIXEL
y2 = y1
}
COLLIDING_LEFT -> {
x1 = this.startX - A_PIXEL
x2 = x1
y1 = this.startY
y2 = this.endY - PHYS_EPSILON_DIST
}
COLLIDING_RIGHT -> {
x1 = this.endX - PHYS_EPSILON_DIST + A_PIXEL
x2 = x1
y1 = this.startY
y2 = this.endY - PHYS_EPSILON_DIST
}
else -> throw IllegalArgumentException("Unknown option $option")
}
return listOf(x1, x2, y1, y2)
}
private fun Int.popcnt() = Integer.bitCount(this)
/** /**
* @see /work_files/hitbox_collision_detection_compensation.jpg * @see /work_files/hitbox_collision_detection_compensation.jpg
*/ */
fun isWalled(hitbox: Hitbox, option: Int): Boolean { fun isWalled(hitbox: Hitbox, option: Int): Boolean {
val x1: Double
val x2: Double
val y1: Double
val y2: Double
/* /*
The structure: The structure:
@@ -1166,30 +1160,15 @@ open class ActorWithBody : Actor {
IMPORTANT AF NOTE: things are ASYMMETRIC! IMPORTANT AF NOTE: things are ASYMMETRIC!
*/ */
// AT LEAST THESE ARE CONFIRMED if (option.popcnt() == 1) {
if (option == COLLIDING_TOP) { val (x1, x2, y1, y2) = hitbox.getWallDetection(option)
x1 = hitbox.startX
x2 = hitbox.endX - A_PIXEL val txStart = x1/*.plus(HALF_PIXEL)*/.floorToInt()
y1 = hitbox.startY - A_PIXEL val txEnd = x2/*.plus(HALF_PIXEL)*/.floorToInt()
y2 = y1 val tyStart = y1/*.plus(HALF_PIXEL)*/.floorToInt()
} val tyEnd = y2/*.plus(HALF_PIXEL)*/.floorToInt()
else if (option == COLLIDING_BOTTOM) {
x1 = hitbox.startX return isCollidingInternalStairs(txStart, tyStart, txEnd, tyEnd, option == COLLIDING_BOTTOM).first == 2
x2 = hitbox.endX - A_PIXEL
y1 = hitbox.endY + A_PIXEL
y2 = y1
}
else if (option == COLLIDING_LEFT) {
x1 = hitbox.startX - A_PIXEL
x2 = x1
y1 = hitbox.startY
y2 = hitbox.endY - A_PIXEL
}
else if (option == COLLIDING_RIGHT) {
x1 = hitbox.endX + A_PIXEL
x2 = x1
y1 = hitbox.startY
y2 = hitbox.endY - A_PIXEL
} }
else if (option == COLLIDING_ALLSIDE) { else if (option == COLLIDING_ALLSIDE) {
return isWalled(hitbox, COLLIDING_LEFT) || isWalled(hitbox, COLLIDING_RIGHT) || return isWalled(hitbox, COLLIDING_LEFT) || isWalled(hitbox, COLLIDING_RIGHT) ||
@@ -1202,25 +1181,14 @@ open class ActorWithBody : Actor {
else if (option == COLLIDING_UD) { else if (option == COLLIDING_UD) {
return isWalled(hitbox, COLLIDING_BOTTOM) || isWalled(hitbox, COLLIDING_TOP) return isWalled(hitbox, COLLIDING_BOTTOM) || isWalled(hitbox, COLLIDING_TOP)
} }
else throw IllegalArgumentException() else throw IllegalArgumentException("$option")
val txStart = x1.plus(HALF_PIXEL).floorToInt()
val txEnd = x2.plus(HALF_PIXEL).floorToInt()
val tyStart = y1.plus(HALF_PIXEL).floorToInt()
val tyEnd = y2.plus(HALF_PIXEL).floorToInt()
return isCollidingInternalStairs(txStart, tyStart, txEnd, tyEnd, option == COLLIDING_BOTTOM).first == 2
} }
/** /**
* @return First int: 0 - no collision, 1 - staircasing, 2 - "bonk" to the wall; Second int: stair height * @return First int: 0 - no collision, 1 - staircasing, 2 - "bonk" to the wall; Second int: stair height
*/ */
private fun isWalledStairs(hitbox: Hitbox, option: Int): Pair<Int, Int> { private fun isWalledStairs(hitbox: Hitbox, option: Int): Pair<Int, Int> {
val x1: Double
val x2: Double
val y1: Double
val y2: Double
/* /*
The structure: The structure:
@@ -1233,30 +1201,15 @@ open class ActorWithBody : Actor {
IMPORTANT AF NOTE: things are ASYMMETRIC! IMPORTANT AF NOTE: things are ASYMMETRIC!
*/ */
// AT LEAST THESE ARE CONFIRMED if (option.popcnt() == 1) {
if (option == COLLIDING_TOP) { val (x1, x2, y1, y2) = hitbox.getWallDetection(option)
x1 = hitbox.startX
x2 = hitbox.endX - A_PIXEL val pxStart = x1/*.plus(HALF_PIXEL)*/.floorToInt()
y1 = hitbox.startY - A_PIXEL val pxEnd = x2/*.plus(HALF_PIXEL)*/.floorToInt()
y2 = y1 val pyStart = y1/*.plus(HALF_PIXEL)*/.floorToInt()
} val pyEnd = y2/*.plus(HALF_PIXEL)*/.floorToInt()
else if (option == COLLIDING_BOTTOM) {
x1 = hitbox.startX return isCollidingInternalStairs(pxStart, pyStart, pxEnd, pyEnd, option == COLLIDING_BOTTOM)
x2 = hitbox.endX - A_PIXEL
y1 = hitbox.endY + A_PIXEL
y2 = y1
}
else if (option == COLLIDING_LEFT) {
x1 = hitbox.startX - A_PIXEL
x2 = x1
y1 = hitbox.startY
y2 = hitbox.endY - A_PIXEL
}
else if (option == COLLIDING_RIGHT) {
x1 = hitbox.endX + A_PIXEL
x2 = x1
y1 = hitbox.startY
y2 = hitbox.endY - A_PIXEL
} }
else if (option == COLLIDING_ALLSIDE) { else if (option == COLLIDING_ALLSIDE) {
return max(max(isWalledStairs(hitbox, COLLIDING_LEFT).first, return max(max(isWalledStairs(hitbox, COLLIDING_LEFT).first,
@@ -1276,38 +1229,8 @@ open class ActorWithBody : Actor {
} }
else throw IllegalArgumentException("$option") else throw IllegalArgumentException("$option")
val pxStart = x1.plus(0.5f).floorToInt()
val pxEnd = x2.plus(0.5f).floorToInt()
val pyStart = y1.plus(0.5f).floorToInt()
val pyEnd = y2.plus(0.5f).floorToInt()
return isCollidingInternalStairs(pxStart, pyStart, pxEnd, pyEnd, gravitation.y >= 0.0 && option == COLLIDING_BOTTOM || gravitation.y < 0.0 && option == COLLIDING_TOP)
} }
/*private fun isCollidingInternal(txStart: Int, tyStart: Int, txEnd: Int, tyEnd: Int, feet: Boolean = false): Boolean {
if (world == null) return false
for (y in tyStart..tyEnd) {
for (x in txStart..txEnd) {
val tile = world!!.getTileFromTerrain(x, y)
if (feet) {
if (shouldICollideWithThisFeet(tile))
return true
}
else {
if (shouldICollideWithThis(tile))
return true
}
// this weird statement means that if's the condition is TRUE, return TRUE;
// if the condition is FALSE, do nothing and let succeeding code handle it.
}
}
return false
}*/
private val AUTO_CLIMB_STRIDE: Int private val AUTO_CLIMB_STRIDE: Int
get() = ((actorValue.getAsInt(AVKey.VERTSTRIDE) ?: 8) * scale).toInt() get() = ((actorValue.getAsInt(AVKey.VERTSTRIDE) ?: 8) * scale).toInt()
//private val AUTO_CLIMB_RATE: Int // we'll just climb stairs instantly to make things work wo worrying about the details //private val AUTO_CLIMB_RATE: Int // we'll just climb stairs instantly to make things work wo worrying about the details
@@ -1324,16 +1247,24 @@ open class ActorWithBody : Actor {
var stairHeight = 0 var stairHeight = 0
var hitFloor = false var hitFloor = false
// if (ys.last != ys.first && feet) throw InternalError("Feet mode collision but pyStart != pyEnd ($pyStart .. $pyEnd)")
val feetY = (pyEnd / TILE_SIZED).floorToInt() // round down toward negative infinity // TODO reverse gravity adaptation?
// if (feet && this is IngamePlayer) printdbg(this, "dim=($pxStart,$pyStart)-($pxEnd,$pyEnd), feetY=$feetY")
for (y in ys) { for (y in ys) {
val ty = (y / TILE_SIZED).floorToInt() // round down toward negative infinity
// if (this is IngamePlayer) printdbg(this, "ty=${ty}")
val isFeetTileHeight = (ty == feetY)
var hasFloor = false var hasFloor = false
for (x in pxStart..pxEnd) { for (x in pxStart..pxEnd) {
val tx = (x / TILE_SIZE) - (if (x < 0) 1 else 0) // round down toward negative infinity val tx = (x / TILE_SIZED).floorToInt() // round down toward negative infinity
val ty = (y / TILE_SIZE) - (if (y < 0) 1 else 0) // round down toward negative infinity
val tile = world!!.getTileFromTerrain(tx, ty) val tile = world!!.getTileFromTerrain(tx, ty)
if (feet) { if (feet && isFeetTileHeight) {
if (shouldICollideWithThisFeet(tile)) { if (shouldICollideWithThisFeet(tile)) {
hasFloor = true hasFloor = true
hitFloor = true hitFloor = true
@@ -1365,7 +1296,7 @@ open class ActorWithBody : Actor {
//println("-> $stairHeight") //println("-> $stairHeight")
// edge-detect mode // edge-detect mode
return if (yheight == 0) hitFloor.toInt() * 2 to stairHeight return if (yheight == 0) hitFloor.toInt(1) to stairHeight
// not an edge-detect && no collision // not an edge-detect && no collision
else if (stairHeight == 0) 0 to 0 else if (stairHeight == 0) 0 to 0
// there was collision and stairHeight <= AUTO_CLIMB_STRIDE // there was collision and stairHeight <= AUTO_CLIMB_STRIDE
@@ -1405,7 +1336,7 @@ open class ActorWithBody : Actor {
// platforms, moving downward AND not "going down" // platforms, moving downward AND not "going down"
(this is ActorHumanoid && BlockCodex[tile].isPlatform && (this is ActorHumanoid && BlockCodex[tile].isPlatform &&
externalV.y + (controllerV?.y ?: 0.0) >= 0.0 && externalV.y + (controllerV?.y ?: 0.0) >= 0.0 &&
!downDownVirtually && !this.isDownDown && this.axisY <= 0f) || this.downButtonHeld == 0 && this.axisY <= 0f) ||
// platforms, moving downward, for the case of NOT ActorHumanoid // platforms, moving downward, for the case of NOT ActorHumanoid
(this !is ActorHumanoid && BlockCodex[tile].isPlatform && (this !is ActorHumanoid && BlockCodex[tile].isPlatform &&
externalV.y + (controllerV?.y ?: 0.0) >= 0.0) externalV.y + (controllerV?.y ?: 0.0) >= 0.0)
@@ -1745,8 +1676,8 @@ open class ActorWithBody : Actor {
if (KeyToggler.isOn(Input.Keys.F9)) { if (KeyToggler.isOn(Input.Keys.F9)) {
val blockMark = CommonResourcePool.getAsTextureRegionPack("blockmarkings_common").get(0, 0) val blockMark = CommonResourcePool.getAsTextureRegionPack("blockmarkings_common").get(0, 0)
batch.color = HITBOX_COLOURS0 for (y in 0..intTilewiseHitbox.height.toInt() + 1) {
for (y in 0..intTilewiseHitbox.height.toInt()) { batch.color = if (y == intTilewiseHitbox.height.toInt() + 1) HITBOX_COLOURS1 else HITBOX_COLOURS0
for (x in 0..intTilewiseHitbox.width.toInt()) { for (x in 0..intTilewiseHitbox.width.toInt()) {
batch.draw(blockMark, batch.draw(blockMark,
(intTilewiseHitbox.startX.toFloat() + x) * TILE_SIZEF, (intTilewiseHitbox.startX.toFloat() + x) * TILE_SIZEF,
@@ -1755,6 +1686,7 @@ open class ActorWithBody : Actor {
} }
} }
batch.color = Color.WHITE
} }
} }
@@ -1762,9 +1694,12 @@ open class ActorWithBody : Actor {
if (world == null) return if (world == null) return
val offsetX = 0f val offsetX = 0f
val offsetY = 1f // plant to the ground val offsetY = 0f // for some reason this value must be zero to draw the actor planted to the ground
drawBodyInGoodPosition(hitbox.startX.toFloat(), hitbox.startY.toFloat()) { x, y -> val posX = hitbox.startX.plus(PHYS_EPSILON_DIST).toFloat()
val posY = hitbox.startY.plus(PHYS_EPSILON_DIST).toFloat()
drawBodyInGoodPosition(posX, posY) { x, y ->
sprite.render(batch, x + offsetX, y + offsetY, scale.toFloat()) sprite.render(batch, x + offsetX, y + offsetY, scale.toFloat())
} }
} }
@@ -1915,10 +1850,10 @@ open class ActorWithBody : Actor {
val tiles = ArrayList<ItemID?>() val tiles = ArrayList<ItemID?>()
// offset 1 pixel to the down so that friction would work // offset 1 pixel to the down so that friction would work
val y = hitbox.endY.plus(1.0).div(TILE_SIZE).floorToInt() val y = intTilewiseHitbox.height.toInt() + 1
for (x in hIntTilewiseHitbox.startX.toInt()..hIntTilewiseHitbox.endX.toInt()) { for (x in 0..intTilewiseHitbox.width.toInt()) {
tiles.add(world!!.getTileFromTerrain(x, y)) tiles.add(world!!.getTileFromTerrain(x + intTilewiseHitbox.startX.toInt(), y + intTilewiseHitbox.startY.toInt()))
} }
return tiles.forEach(consumer) return tiles.forEach(consumer)
@@ -1931,10 +1866,11 @@ open class ActorWithBody : Actor {
val tileProps = ArrayList<BlockProp?>() val tileProps = ArrayList<BlockProp?>()
// offset 1 pixel to the down so that friction would work // offset 1 pixel to the down so that friction would work
val y = hitbox.endY.plus(1.0).div(TILE_SIZE).floorToInt() // val y = hitbox.endY.plus(1.0).div(TILE_SIZE).floorToInt()
val y = intTilewiseHitbox.height.toInt() + 1
for (x in hIntTilewiseHitbox.startX.toInt()..hIntTilewiseHitbox.endX.toInt()) { for (x in 0..intTilewiseHitbox.width.toInt()) {
tileProps.add(BlockCodex[world!!.getTileFromTerrain(x, y)]) tileProps.add(BlockCodex[world!!.getTileFromTerrain(x + intTilewiseHitbox.startX.toInt(), y + intTilewiseHitbox.startY.toInt())])
} }
return tileProps.forEach(consumer) return tileProps.forEach(consumer)
@@ -1963,8 +1899,8 @@ open class ActorWithBody : Actor {
*/ */
@Transient const val GAME_TO_SI_ACC = (Terrarum.PHYS_TIME_FRAME * Terrarum.PHYS_TIME_FRAME) / METER @Transient const val GAME_TO_SI_ACC = (Terrarum.PHYS_TIME_FRAME * Terrarum.PHYS_TIME_FRAME) / METER
@Transient const val PHYS_EPSILON_DIST = 1.0 / 65536.0 @Transient const val PHYS_EPSILON_DIST = 1.0 / 4096.0
// @Transient const val PHYS_EPSILON_VELO = 1.0 / 8192.0 @Transient const val PHYS_EPSILON_VELO = 1.0 / 65536.0
/** /**
@@ -2005,6 +1941,7 @@ open class ActorWithBody : Actor {
@Transient private val HITBOX_COLOURS0 = Color(0xFF00FF88.toInt()) @Transient private val HITBOX_COLOURS0 = Color(0xFF00FF88.toInt())
@Transient private val HITBOX_COLOURS1 = Color(0xFFFF0088.toInt()) @Transient private val HITBOX_COLOURS1 = Color(0xFFFF0088.toInt())
fun isCloseEnough(a: Double, b: Double) = ((a / b).let { if (it.isNaN()) 0.0 else it } - 1).absoluteValue < PHYS_EPSILON_DIST fun isCloseEnough(a: Double, b: Double) = ((a / b).let { if (it.isNaN()) 0.0 else it } - 1).absoluteValue < PHYS_EPSILON_DIST
} }

View File

@@ -145,6 +145,9 @@ class Hitbox {
return this return this
} }
/**
* Resize the hitbox centred around the "canonical" point.
*/
fun canonicalResize(w: Double, h: Double): Hitbox { fun canonicalResize(w: Double, h: Double): Hitbox {
// sx_1 + 0.5w_1 = sx_2 + 0.5w_2 // equals because the final point must not move. sx_1: old start-x, sx_2: new start-x which is what we want // sx_1 + 0.5w_1 = sx_2 + 0.5w_2 // equals because the final point must not move. sx_1: old start-x, sx_2: new start-x which is what we want
// sx_2 = sx_1 + 0.5w_1 - 0.5w_2 // move variables to right-hand side to derive final value sx_2 // sx_2 = sx_1 + 0.5w_1 - 0.5w_2 // move variables to right-hand side to derive final value sx_2

View File

@@ -359,6 +359,8 @@ abstract class GameItem(val originalID: ItemID) : Comparable<GameItem>, Cloneabl
return this return this
} }
fun hasTag(s: String) = tags.contains(s)
companion object { companion object {

View File

@@ -1,8 +1,7 @@
package net.torvald.terrarum.gameworld package net.torvald.terrarum.gameworld
import net.torvald.terrarum.modulebasegame.worldgenerator.TWO_PI import net.torvald.terrarum.langpack.Lang
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin
/** /**
@@ -57,8 +56,10 @@ import kotlin.math.sin
*/ */
class WorldTime(initTime: Long = 0L) { class WorldTime(initTime: Long = 0L) {
@Transient private val TWO_PI = Math.PI * 2.0
/** It is not recommended to directly modify the TIME_T. Use provided methods instead. */ /** 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 var TIME_T = 0L // Epoch: Year 1 Spring 1st, 0h00:00 (Mondag) // 0001-01-01
init { init {
TIME_T = initTime TIME_T = initTime
@@ -151,21 +152,7 @@ class WorldTime(initTime: Long = 0L) {
@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 @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
// NOTE: ingame calendars (the fixture with GUI) should use symbols AND fullnames; the watch already uses shot daynames // NOTE: ingame calendars (the fixture with GUI) should use symbols AND fullnames; the watch already uses shot daynames
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 { companion object {
/** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */ /** Each day is displayed as 24 hours, but in real-life clock it's 22 mins long */
@@ -181,7 +168,7 @@ class WorldTime(initTime: Long = 0L) {
const val MONTH_LENGTH = 30 // ingame calendar specific const val MONTH_LENGTH = 30 // ingame calendar specific
const val EPOCH_YEAR = 125 const val EPOCH_YEAR = 1
val YEAR_SECONDS = DAY_LENGTH * YEAR_DAYS val YEAR_SECONDS = DAY_LENGTH * YEAR_DAYS
@@ -203,6 +190,31 @@ class WorldTime(initTime: Long = 0L) {
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 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
const val DIURNAL_MOTION_LENGTH = 86636f const val DIURNAL_MOTION_LENGTH = 86636f
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")
val DAY_NAMES_LANG_KEYS = DAY_NAMES.map { "CONTEXT_CALENDAR_DAY_${it}_DNT" }
val DAY_NAMES_SHORT_LANG_KEYS = DAY_NAMES_SHORT.map { "CONTEXT_CALENDAR_DAY_${it}_DNT" }
val MONTH_NAMES_LANG_KEYS = MONTH_NAMES.map { "CONTEXT_CALENDAR_SEASON_${it}" }
val MONTH_NAMES_SHORT_LANG_KEYS = MONTH_NAMES_SHORT.map { "CONTEXT_CALENDAR_SEASON_${it}" }
fun getDayName(index: Int) = Lang[DAY_NAMES_LANG_KEYS[index]]
fun getDayNameShort(index: Int) = Lang[DAY_NAMES_SHORT_LANG_KEYS[index]]
fun getMonthName(index: Int) = Lang[MONTH_NAMES_LANG_KEYS[index - 1]]
fun getMonthNameShort(index: Int) = Lang[MONTH_NAMES_SHORT_LANG_KEYS[index - 1]]
} }
fun update(delta: Float) { fun update(delta: Float) {
@@ -228,27 +240,30 @@ class WorldTime(initTime: Long = 0L) {
TIME_T += t TIME_T += t
} }
val dayName: String
get() = DAY_NAMES[dayOfWeek]
fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt() fun Long.toPositiveInt() = this.and(0x7FFFFFFF).toInt()
fun Long.abs() = Math.abs(this) fun Long.abs() = Math.abs(this)
/** Format: "%A, %Y %B %d %X" */ /** Format: "ɣ%Y %B %d %A, %X" */
fun getFormattedTime() = "${getDayNameShort()}, " + fun getFormattedTime() =
"$years " + "ɣ$years " +
"${getMonthNameFull()} " + "${getMonthNameFull()} " +
"$calendarDay " + "$calendarDay " +
"${getDayNameFull()}, " +
"${String.format("%02d", hours)}:" + "${String.format("%02d", hours)}:" +
"${String.format("%02d", minutes)}:" + "${String.format("%02d", minutes)}:" +
"${String.format("%02d", seconds)}" "${String.format("%02d", seconds)}"
fun getFormattedCalendarDay() =
"ɣ$years " +
"${getMonthNameFull()} " +
"$calendarDay " +
"${getDayNameFull()}"
fun getShortTime() = "${years.toString().padStart(4, '0')}-${getMonthNameShort()}-${calendarDay.toString().padStart(2, '0')}" 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 getFilenameTime() = "${years.toString().padStart(4, '0')}${calendarMonth.toString().padStart(2, '0')}${calendarDay.toString().padStart(2, '0')}"
fun getDayNameFull() = DAY_NAMES[dayOfWeek] fun getDayNameFull() = getDayName(dayOfWeek)
fun getDayNameShort() = DAY_NAMES_SHORT[dayOfWeek] fun getDayNameShort() = getDayNameShort(dayOfWeek)
fun getMonthNameFull() = MONTH_NAMES[calendarMonth - 1] fun getMonthNameFull() = getMonthName(calendarMonth)
fun getMonthNameShort() = MONTH_NAMES_SHORT[calendarMonth - 1] fun getMonthNameShort() = getMonthNameShort(calendarMonth)
override fun toString() = getFormattedTime() override fun toString() = getFormattedTime()
} }

View File

@@ -152,6 +152,15 @@ object Lang {
return sb.toString() return sb.toString()
} }
fun getAndUseTemplate(key: String, capitalise: Boolean = true, vararg arguments: Any?): String {
var raw = get(key, capitalise)
arguments.forEachIndexed { index, it0 ->
val it = if (capitalise) it0.toString().capitalize() else it0.toString()
raw = raw.replace("{${index}}", it)
}
return raw
}
/** /**
* Does NOT parse the operators * Does NOT parse the operators
*/ */

View File

@@ -4,11 +4,10 @@ import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.graphics.glutils.FrameBuffer
import com.badlogic.gdx.graphics.glutils.ShaderProgram import com.badlogic.gdx.graphics.glutils.ShaderProgram
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.badlogic.gdx.utils.GdxRuntimeException
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.* import net.torvald.terrarum.App.*
@@ -53,20 +52,20 @@ object IngameRenderer : Disposable {
private lateinit var blurWriteQuad2: Mesh private lateinit var blurWriteQuad2: Mesh
// private lateinit var blurWriteQuad4: Mesh // private lateinit var blurWriteQuad4: Mesh
private lateinit var lightmapFbo: FloatFrameBuffer private lateinit var lightmapFbo: Float16FrameBuffer
private lateinit var fboRGB: FloatFrameBuffer private lateinit var fboRGB: Float16FrameBuffer
private lateinit var fboRGB_lightMixed: FloatFrameBuffer private lateinit var fboRGB_lightMixed: Float16FrameBuffer
private lateinit var fboA: FloatFrameBuffer private lateinit var fboA: Float16FrameBuffer
private lateinit var fboA_lightMixed: FloatFrameBuffer private lateinit var fboA_lightMixed: Float16FrameBuffer
private lateinit var fboMixedOut: FloatFrameBuffer private lateinit var fboMixedOut: Float16FrameBuffer
private lateinit var rgbTex: TextureRegion private lateinit var rgbTex: TextureRegion
private lateinit var aTex: TextureRegion private lateinit var aTex: TextureRegion
private lateinit var mixedOutTex: TextureRegion private lateinit var mixedOutTex: TextureRegion
private lateinit var lightTex: TextureRegion private lateinit var lightTex: TextureRegion
private lateinit var blurTex: TextureRegion private lateinit var blurTex: TextureRegion
private lateinit var fboBlurHalf: FloatFrameBuffer private lateinit var fboBlurHalf: Float16FrameBuffer
// private lateinit var fboBlurQuarter: FloatFrameBuffer // private lateinit var fboBlurQuarter: Float16FrameBuffer
// you must have lightMixed FBO; otherwise you'll be reading from unbaked FBO and it freaks out GPU // you must have lightMixed FBO; otherwise you'll be reading from unbaked FBO and it freaks out GPU
@@ -694,7 +693,7 @@ object IngameRenderer : Disposable {
private const val KAWASE_POWER = 1.5f private const val KAWASE_POWER = 1.5f
fun processNoBlur(outFbo: FloatFrameBuffer) { fun processNoBlur(outFbo: Float16FrameBuffer) {
blurtex0.dispose() blurtex0.dispose()
@@ -710,7 +709,7 @@ object IngameRenderer : Disposable {
} }
} }
fun processKawaseBlur(outFbo: FloatFrameBuffer) { fun processKawaseBlur(outFbo: Float16FrameBuffer) {
blurtex0.dispose() blurtex0.dispose()
@@ -807,12 +806,12 @@ object IngameRenderer : Disposable {
//fboBlurQuarter.dispose() //fboBlurQuarter.dispose()
} }
fboRGB = FloatFrameBuffer(width, height, false) fboRGB = Float16FrameBuffer(width, height, false)
fboRGB_lightMixed = FloatFrameBuffer(width, height, false) fboRGB_lightMixed = Float16FrameBuffer(width, height, false)
fboA = FloatFrameBuffer(width, height, false) fboA = Float16FrameBuffer(width, height, false)
fboA_lightMixed = FloatFrameBuffer(width, height, false) fboA_lightMixed = Float16FrameBuffer(width, height, false)
fboMixedOut = FloatFrameBuffer(width, height, false) fboMixedOut = Float16FrameBuffer(width, height, false)
lightmapFbo = FloatFrameBuffer( lightmapFbo = Float16FrameBuffer(
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt(), LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt(),
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(), LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(),
false false
@@ -823,13 +822,13 @@ object IngameRenderer : Disposable {
blurTex = TextureRegion() blurTex = TextureRegion()
mixedOutTex = TextureRegion(fboMixedOut.colorBufferTexture) mixedOutTex = TextureRegion(fboMixedOut.colorBufferTexture)
fboBlurHalf = FloatFrameBuffer( fboBlurHalf = Float16FrameBuffer(
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2, LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2,
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2, LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 2,
false false
) )
/*fboBlurQuarter = FloatFrameBuffer( /*fboBlurQuarter = Float16FrameBuffer(
LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4, LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4,
LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4, LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt() / 4,
false false

View File

@@ -8,7 +8,7 @@ import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
import net.torvald.terrarum.* import net.torvald.terrarum.*
@@ -147,7 +147,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
internal lateinit var uiRemoCon: UIRemoCon internal lateinit var uiRemoCon: UIRemoCon
internal lateinit var uiFakeBlurOverlay: UICanvas internal lateinit var uiFakeBlurOverlay: UICanvas
private lateinit var worldFBO: FloatFrameBuffer private lateinit var worldFBO: Float16FrameBuffer
private val warning32bitJavaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/32_bit_warning.tga"))) private val warning32bitJavaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/32_bit_warning.tga")))
private val warningAppleRosettaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/apple_rosetta_warning.tga"))) private val warningAppleRosettaIcon = TextureRegion(Texture(Gdx.files.internal("assets/graphics/gui/apple_rosetta_warning.tga")))
@@ -262,7 +262,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
Gdx.input.inputProcessor = TitleScreenController(this) Gdx.input.inputProcessor = TitleScreenController(this)
worldFBO = FloatFrameBuffer(App.scr.width, App.scr.height, false) worldFBO = Float16FrameBuffer(App.scr.width, App.scr.height, false)
// load list of savegames // load list of savegames
printdbg(this, "update list of savegames") printdbg(this, "update list of savegames")

View File

@@ -14,16 +14,21 @@ import net.torvald.terrarum.modulebasegame.gameactors.Pocketed
*/ */
internal object Inventory : ConsoleCommand { internal object Inventory : ConsoleCommand {
private var targetID: ActorID = INGAME.actorNowPlaying?.referenceID ?: PLAYER_REF_ID private var targetID: ActorID = 0
private fun tryTargetActivePlayer() {
targetID = INGAME.actorNowPlaying?.referenceID ?: 0
}
override fun execute(args: Array<String>) { override fun execute(args: Array<String>) {
if (args.size == 1) { if (args.size == 1) {
printUsage() printUsage()
} }
else if (args[1] == "target") { else if (args[1] == "target") {
targetID = if (args[2].lowercase() == "player") (INGAME.actorNowPlaying?.referenceID ?: PLAYER_REF_ID) else args[2].toInt() if (args[2].lowercase() == "player") tryTargetActivePlayer() else targetID = args[2].toInt()
} }
else { else {
if (targetID == 0) tryTargetActivePlayer()
val actor = getActor() val actor = getActor()
if (actor != null) { if (actor != null) {
when (args[1]) { when (args[1]) {
@@ -36,7 +41,7 @@ internal object Inventory : ConsoleCommand {
} }
} }
else { else {
Echo("Actor $targetID is not Pocketed or does not exists") Echo("Actor $targetID is not Pocketed or does not exist")
} }
} }
} }

View File

@@ -12,7 +12,6 @@ import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameactors.*
import net.torvald.terrarum.gameactors.faction.Faction import net.torvald.terrarum.gameactors.faction.Faction
import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.GameItem
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.realestate.LandUtil import net.torvald.terrarum.realestate.LandUtil
import org.dyn4j.geometry.Vector2 import org.dyn4j.geometry.Vector2
@@ -116,6 +115,8 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
@Transient const val SPRITE_ROW_IDLE = 0 @Transient const val SPRITE_ROW_IDLE = 0
@Transient const val SPRITE_ROW_WALK = 1 @Transient const val SPRITE_ROW_WALK = 1
@Transient const val downDownMinLengthBase = 12
} }
//////////////////////////////// ////////////////////////////////
@@ -173,7 +174,7 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
var isUpDown = false; protected set var isUpDown = false; protected set
var isDownDown = false; protected set var isDownDown = false; protected set
var downDownVirtually = false; internal set var downButtonHeld = 0; internal set
var isLeftDown = false; protected set var isLeftDown = false; protected set
var isRightDown = false; protected set var isRightDown = false; protected set
var isJumpDown = false; protected set var isJumpDown = false; protected set
@@ -221,7 +222,7 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
if (isNoClip) { if (isNoClip) {
//grounded = true //grounded = true
// platformToIgnore = null // platformToIgnore = null
downDownVirtually = false downButtonHeld = 0
} }
// reset control box of AI // reset control box of AI
@@ -248,6 +249,9 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
} }
} }
private val downDownMinLength: Int
get() = (downDownMinLengthBase * (gravitation.y.abs() / 9.8).sqrt()).ceilToInt()
private fun updateGamerControlBox() { private fun updateGamerControlBox() {
if (isGamer) { if (isGamer) {
isUpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_up")) isUpDown = Gdx.input.isKeyPressed(App.getConfigInt("control_key_up"))
@@ -288,17 +292,14 @@ open class ActorHumanoid : ActorWithBody, Controllable, Pocketed, Factionable, L
} }
// platform-related hacks // platform-related hacks
// allow latching down downDownVirtually only when standing on a platform AND not jumping upwards if (isDownDown || downButtonHeld in 1 until downDownMinLength) {
val occupyingTileHasPlatform = bodyTiles.filterNotNull().any { it.isPlatform } downButtonHeld += 1
val feetTileHasPlatform = feetTiles.filterNotNull().any { it.isPlatform }
val feetTileIsAllPlatform = feetTiles.filterNotNull().all { it.isPlatform }
if (isDownDown && feetTileIsAllPlatform && (controllerV?.y ?: 0.0) >= 0.0) {// ||
// occupyingTileHasPlatform && !feetTileHasPlatform) { // FIXME commenting this out enables platform-ladder but falldown gets slowed down if the body passes thru the platform but I think this behav might be beneficial for player?
downDownVirtually = true
} }
if (downDownVirtually && !occupyingTileHasPlatform && !feetTileIsAllPlatform) { else {
downDownVirtually = false downButtonHeld = 0
} }
// TODO just disable "snap to ground" on collision solver if {player's body overlaps with the platform/downDownVirtually}?
// the point is: disable snap (or don't consider offending tiles as solid) for certain Y-pos only, tiles on Y+1 are still solid
} }
private inline val hasController: Boolean private inline val hasController: Boolean

View File

@@ -248,7 +248,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
forEachBlockbox { x, y, _, _ -> forEachBlockbox { x, y, _, _ ->
if (!hasCollision) { if (!hasCollision) {
val tile = world!!.getTileFromTerrain(x, y) val tile = world!!.getTileFromTerrain(x, y)
if (BlockCodex[tile].isSolid || BlockCodex[tile].isActorBlock) { if (!BlockCodex[tile].hasTag("INCONSEQUENTIAL")) {
hasCollision = true hasCollision = true
} }
} }
@@ -311,7 +311,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
forEachBlockbox { x, y, _, _ -> forEachBlockbox { x, y, _, _ ->
if (!hasCollision) { if (!hasCollision) {
val tile = world!!.getTileFromTerrain(x, y) val tile = world!!.getTileFromTerrain(x, y)
if (BlockCodex[tile].isSolid || BlockCodex[tile].isActorBlock) { if (!BlockCodex[tile].hasTag("INCONSEQUENTIAL")) {
hasCollision = true hasCollision = true
} }
} }

View File

@@ -0,0 +1,41 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase
import net.torvald.terrarum.modulebasegame.ui.UIWallCalendar
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* Created by minjaesong on 2023-08-08.
*/
class FixtureWallCalendar : FixtureBase {
constructor() : super(
BlockBox(BlockBox.NO_COLLISION, 1, 1),
nameFun = { Lang["ITEM_CALENDAR"] },
mainUI = UIWallCalendar()
) {
val itemImage = FixtureItemBase.getItemImageFromSingleImage("basegame", "sprites/fixtures/calendar.tga")
density = 600.0
setHitboxDimension(TILE_SIZE, TILE_SIZE, 0, 1)
makeNewSprite(TextureRegionPack(itemImage.texture, TILE_SIZE, TILE_SIZE)).let {
it.setRowsAndFrames(1,1)
}
actorValue[AVKey.BASEMASS] = 1.0
}
override var tooltipText: String?
get() = Lang.getAndUseTemplate("CONTEXT_CALENDAR_DATE_FORMAT_YMD_DDD", false,
world!!.worldTime.years,
world!!.worldTime.getMonthNameFull(),
world!!.worldTime.calendarDay,
world!!.worldTime.getDayNameFull()
)//INGAME.world.worldTime.getFormattedCalendarDay()
set(value) {}
}

View File

@@ -51,7 +51,7 @@ object BlockBase {
// return false if there is a "solid" tile already // return false if there is a "solid" tile already
if (isWall && BlockCodex[wallUnderCursor].isSolid || if (isWall && BlockCodex[wallUnderCursor].isSolid ||
!isWall && (BlockCodex[terrainUnderCursor].isSolid || BlockCodex[terrainUnderCursor].isActorBlock)) !isWall && !BlockCodex[terrainUnderCursor].hasTag("INCONSEQUENTIAL"))
return@mouseInInteractableRange -1L return@mouseInInteractableRange -1L
// filter passed, do the job // filter passed, do the job

View File

@@ -0,0 +1,29 @@
package net.torvald.terrarum.modulebasegame.gameitems
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.gameitems.ItemID
/**
* Created by minjaesong on 2023-08-08.
*/
class ItemWallCalendar(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.FixtureWallCalendar") {
override var dynamicID: ItemID = originalID
override val originalName = "ITEM_CALENDAR"
override var baseMass = 1.0
override var stackable = true
override var inventoryCategory = Category.MISC
override val isUnique = false
override val isDynamic = false
override val materialId = ""
override val itemImage: TextureRegion
get() = getItemImageFromSingleImage("basegame", "sprites/fixtures/calendar.tga")
override var baseToolSize: Double? = baseMass
init {
equipPosition = EquipPosition.HAND_GRIP
}
}

View File

@@ -133,7 +133,7 @@ class QuickSingleplayerWorldSavingThread(
// Write Actors // // Write Actors //
actorsList.forEachIndexed { count, it -> actorsList.forEachIndexed { count, it ->
printdbg(this, "Writing actors... ${count+1}/${actorsList.size}") printdbg(this, "Writing actors... ${count+1}/${actorsList.size} (${it.javaClass.canonicalName})")
val actorContent = EntryFile(WriteActor.encodeToByteArray64(it)) val actorContent = EntryFile(WriteActor.encodeToByteArray64(it))
val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent) val actor = DiskEntry(it.referenceID.toLong(), ROOT, creation_t, time_t, actorContent)

View File

@@ -56,10 +56,10 @@ object ControlPanelCommon {
val initialSel = optionsList.indexOf(App.getConfigString(optionName)) val initialSel = optionsList.indexOf(App.getConfigString(optionName))
println("labelFuns = ${labelFuns.map { it.invoke() }}") // println("labelFuns = ${labelFuns.map { it.invoke() }}")
println("optionsList = $optionsList") // println("optionsList = $optionsList")
println("optionName = $optionName; value = ${App.getConfigString(optionName)}") // println("optionName = $optionName; value = ${App.getConfigString(optionName)}")
println("initialSel = $initialSel") // println("initialSel = $initialSel")
if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigString(optionName)}' for option '$optionName' is not found on the options list") if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigString(optionName)}' for option '$optionName' is not found on the options list")
@@ -69,6 +69,19 @@ object ControlPanelCommon {
} }
} }
} }
else if (args.startsWith("spinnersel,")) {
val labelFuns = arg.subList(1, arg.size).map { { it } }
val optionsList = arg.subList(1, arg.size).map { it.toInt() }
val initialSel = optionsList.indexOf(App.getConfigInt(optionName))
if (initialSel < 0) throw IllegalArgumentException("config value '${App.getConfigInt(optionName)}' for option '$optionName' is not found on the options list")
UIItemTextSelector(parent, x, y, labelFuns, initialSel, CONFIG_SPINNER_WIDTH, clickToShowPalette = false, useSpinnerButtons = true) to { it: UIItem, optionStr: String ->
(it as UIItemTextSelector).selectionChangeListener = {
App.setConfig(optionStr, optionsList[it])
}
}
}
else if (args.startsWith("spinner,")) { else if (args.startsWith("spinner,")) {
UIItemSpinner(parent, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String -> UIItemSpinner(parent, x, y, App.getConfigInt(optionName), arg[1].toInt(), arg[2].toInt(), arg[3].toInt(), CONFIG_SPINNER_WIDTH, numberToTextFunction = { "${it.toLong()}" }) to { it: UIItem, optionStr: String ->
(it as UIItemSpinner).selectionChangeListener = { (it as UIItemSpinner).selectionChangeListener = {

View File

@@ -17,6 +17,10 @@ import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
*/ */
class UIBasicInfo() : UICanvas() { class UIBasicInfo() : UICanvas() {
init {
handler.allowESCtoClose = false
}
val player: ActorHumanoid? val player: ActorHumanoid?
get() = Terrarum.ingame?.actorNowPlaying get() = Terrarum.ingame?.actorNowPlaying

View File

@@ -14,6 +14,10 @@ import net.torvald.terrarum.ui.UICanvas
*/ */
class UICheatDetected : UICanvas() { class UICheatDetected : UICanvas() {
init {
handler.allowESCtoClose = false
}
override var width: Int override var width: Int
get() = App.scr.width get() = App.scr.width
set(value) { throw UnsupportedOperationException() } set(value) { throw UnsupportedOperationException() }

View File

@@ -125,7 +125,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
// If the player has the required item, use it; otherwise, will take an item from the ItemCodex // If the player has the required item, use it; otherwise, will take an item from the ItemCodex
player.itemList.filter { (itm, qty) -> player.itemList.filter { (itm, qty) ->
ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty
}.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.tags.contains(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed")) }.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.hasTag(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed"))
} }
else { else {
ingredient.key ingredient.key
@@ -234,7 +234,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
// don't rely on highlightedness of the button to determine the item on the button is the selected // don't rely on highlightedness of the button to determine the item on the button is the selected
// ingredient (because I don't fully trust my code lol) // ingredient (because I don't fully trust my code lol)
val targetItemToAlter = recipe.ingredients.filter { // altering recipe doesn't make sense if player selected a recipe that requires no tag-ingredients val targetItemToAlter = recipe.ingredients.filter { // altering recipe doesn't make sense if player selected a recipe that requires no tag-ingredients
(it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && gameItem.tags.contains(it.key)) (it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && gameItem.hasTag(it.key))
}.let { }.let {
if (it.size > 1) if (it.size > 1)
println("[UICrafting] Your recipe seems to have two similar ingredients defined\n" + println("[UICrafting] Your recipe seems to have two similar ingredients defined\n" +
@@ -245,7 +245,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
targetItemToAlter?.let { targetItemToAlter?.let {
val oldItem = _getItemListIngredients().getInventory().itemList.first { itemPair -> val oldItem = _getItemListIngredients().getInventory().itemList.first { itemPair ->
(it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && ItemCodex[itemPair.itm]!!.tags.contains(it.key)) (it.keyMode == CraftingCodex.CraftingItemKeyMode.TAG && ItemCodex[itemPair.itm]!!.hasTag(it.key))
} }
changeIngredient(oldItem, itemID) changeIngredient(oldItem, itemID)
refreshCraftButtonStatus() refreshCraftButtonStatus()
@@ -279,7 +279,7 @@ class UICrafting(val full: UIInventoryFull) : UICanvas(), HasInventory {
// If the player has the required item, use it; otherwise, will take an item from the ItemCodex // If the player has the required item, use it; otherwise, will take an item from the ItemCodex
val selectedItem = playerInventory.itemList.filter { (itm, qty) -> val selectedItem = playerInventory.itemList.filter { (itm, qty) ->
ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty ItemCodex[itm]?.tags?.contains(ingredient.key) == true && qty >= ingredient.qty
}.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.tags.contains(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed")) }.maxByOrNull { it.qty }?.itm ?: ((ItemCodex.itemCodex.firstNotNullOfOrNull { if (it.value.hasTag(ingredient.key)) it.key else null }) ?: throw NullPointerException("Item with tag '${ingredient.key}' not found. Possible cause: game or a module not updated or installed"))
// printdbg(this, "Adding ingredients by tag ${selectedItem} (${ingredient.qty})") // printdbg(this, "Adding ingredients by tag ${selectedItem} (${ingredient.qty})")
selectedItem selectedItem

View File

@@ -157,7 +157,6 @@ class UIInventoryFull(
//val INVENTORY_CELLS_OFFSET_Y: Int = 107 + (AppLoader.terrarumAppConfig.screenH - internalHeight) / 2 //val INVENTORY_CELLS_OFFSET_Y: Int = 107 + (AppLoader.terrarumAppConfig.screenH - internalHeight) / 2
init { init {
handler.allowESCtoClose = true
} }
private val SP = "\u3000 " private val SP = "\u3000 "

View File

@@ -235,7 +235,7 @@ class UILoadList(val full: UILoadSavegame) : UICanvas() {
if (showSpinner) { if (showSpinner) {
val spin = spinner.get(spinnerFrame % 8, spinnerFrame / 8) val spin = spinner.get(spinnerFrame % 8, spinnerFrame / 8)
val offX = UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 72 + 1 val offX = UIRemoCon.menubarOffX - UIRemoCon.UIRemoConElement.paddingLeft + 72 + 1
val offY = UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 3 + 16 val offY = UIRemoCon.menubarOffY - UIRemoCon.UIRemoConElement.lineHeight * 4 + 16
batch.draw(spin, offX.toFloat(), offY.toFloat()) batch.draw(spin, offX.toFloat(), offY.toFloat())
} }
} }

View File

@@ -90,8 +90,17 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
listOf(NullUI/*, transitionalAutosave*/) listOf(NullUI/*, transitionalAutosave*/)
) )
internal fun queueUpManageScr() { transitionPanel.setCentreUIto(0) } private val nodesForListing = Yaml(UITitleRemoConYaml.injectedMenuSingleCharSel).parse()
internal fun queueUpNewCharScr() { transitionPanel.setCentreUIto(1) } private val nodesForManage = Yaml(UITitleRemoConYaml.injectedMenuSingleSaveManage).parse()
internal fun queueUpManageScr() {
transitionPanel.setCentreUIto(0)
remoCon.setNewRemoConContents(nodesForManage)
}
internal fun queueUpNewCharScr() {
transitionPanel.setCentreUIto(1)
remoCon.setNewRemoConContents(nodesForListing)
}
// internal fun bringAutosaveSelectorUp() { transitionPanel.setRightUIto(1) } // internal fun bringAutosaveSelectorUp() { transitionPanel.setRightUIto(1) }
// internal fun takeAutosaveSelectorDown() { transitionPanel.setRightUIto(0) } // internal fun takeAutosaveSelectorDown() { transitionPanel.setRightUIto(0) }
@@ -102,6 +111,10 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
internal fun changePanelTo(index: Int) { internal fun changePanelTo(index: Int) {
transitionPanel.requestTransition(index) transitionPanel.requestTransition(index)
if (index == 1)
remoCon.setNewRemoConContents(nodesForManage)
else
remoCon.setNewRemoConContents(nodesForListing)
} }
override fun advanceMode(button: UIItem) { override fun advanceMode(button: UIItem) {
@@ -118,6 +131,9 @@ class UILoadSavegame(val remoCon: UIRemoCon) : Advanceable() {
// takeAutosaveSelectorDown() // takeAutosaveSelectorDown()
transitionPanel.show() transitionPanel.show()
nodesForListing.parent = remoCon.treeRoot
nodesForManage.parent = remoCon.treeRoot
} }
override fun hide() { override fun hide() {

View File

@@ -26,11 +26,14 @@ class UIPerformanceControlPanel(remoCon: UIRemoCon?) : UICanvas() {
arrayOf("", { Lang["MENU_OPTIONS_GAMEPLAY"] }, "h1"), arrayOf("", { Lang["MENU_OPTIONS_GAMEPLAY"] }, "h1"),
arrayOf("autosaveinterval", { Lang["MENU_OPTIONS_AUTOSAVE"] + " (${Lang["CONTEXT_TIME_MINUTE_PLURAL"]})" }, "spinnerimul,1,120,1,60000"), arrayOf("autosaveinterval", { Lang["MENU_OPTIONS_AUTOSAVE"] + " (${Lang["CONTEXT_TIME_MINUTE_PLURAL"]})" }, "spinnerimul,1,120,1,60000"),
arrayOf("notificationshowuptime", { Lang["MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION"] + " (${Lang["CONTEXT_TIME_SECOND_PLURAL"]})" }, "spinnerimul,2,10,1,1000"), arrayOf("notificationshowuptime", { Lang["MENU_OPTIONS_NOTIFICATION_DISPLAY_DURATION"] + " (${Lang["CONTEXT_TIME_SECOND_PLURAL"]})" }, "spinnerimul,2,10,1,1000"),
arrayOf("", { Lang["MENU_LABEL_GRAPHICS"] }, "h1"),
arrayOf("atlastexsize", { Lang["MENU_OPTIONS_ATLAS_TEXTURE_SIZE"] }, "spinnersel,1024,2048,4096,8192"),
arrayOf("", { Lang["MENU_LABEL_JVM_DNT"] }, "h1"), arrayOf("", { Lang["MENU_LABEL_JVM_DNT"] }, "h1"),
arrayOf("jvm_xmx", { Lang["MENU_OPTIONS_JVM_HEAP_MAX"] + " (GB)" }, "spinner,2,32,1"), arrayOf("jvm_xmx", { Lang["MENU_OPTIONS_JVM_HEAP_MAX"] + " (GB)" }, "spinner,2,32,1"),
arrayOf("jvm_extra_cmd", { Lang["MENU_LABEL_EXTRA_JVM_ARGUMENTS"] }, "typein"), arrayOf("jvm_extra_cmd", { Lang["MENU_LABEL_EXTRA_JVM_ARGUMENTS"] }, "typein"),
arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"), arrayOf("", { "(${Lang["MENU_LABEL_RESTART_REQUIRED"]})" }, "p"),
)) )
)
} }
override var height = ControlPanelCommon.getMenuHeight("basegame.performancecontrolpanel") override var height = ControlPanelCommon.getMenuHeight("basegame.performancecontrolpanel")

View File

@@ -18,6 +18,11 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2016-07-20. * Created by minjaesong on 2016-07-20.
*/ */
class UIQuickslotBar : UICanvas() { class UIQuickslotBar : UICanvas() {
init {
handler.allowESCtoClose = false
}
private val cellSize = ItemSlotImageFactory.slotImage.tileW // 38 private val cellSize = ItemSlotImageFactory.slotImage.tileW // 38
private val gutter = 10 - 6 // do -6 to get a gutter size of not-enlarged cells private val gutter = 10 - 6 // do -6 to get a gutter size of not-enlarged cells

View File

@@ -21,6 +21,11 @@ import kotlin.math.roundToInt
* Created by minjaesong on 2016-07-20. * Created by minjaesong on 2016-07-20.
*/ */
class UIQuickslotPie : UICanvas() { class UIQuickslotPie : UICanvas() {
init {
handler.allowESCtoClose = false
}
private val cellSize = ItemSlotImageFactory.slotImage.tileW private val cellSize = ItemSlotImageFactory.slotImage.tileW
private val slotCount = UIQuickslotBar.SLOT_COUNT private val slotCount = UIQuickslotBar.SLOT_COUNT

View File

@@ -21,6 +21,10 @@ class UIScreenZoom : UICanvas(
App.getConfigInt("control_key_zoom") App.getConfigInt("control_key_zoom")
) { ) {
init {
handler.allowESCtoClose = false
}
val zoomText = "${getKeycapPC(handler.toggleKeyLiteral!!)} $EMDASH Zoom Out" val zoomText = "${getKeycapPC(handler.toggleKeyLiteral!!)} $EMDASH Zoom Out"
override var width = App.fontGame.getWidth(zoomText) override var width = App.fontGame.getWidth(zoomText)

View File

@@ -41,6 +41,7 @@ object UITitleRemoConYaml {
// todo add MENU_IO_IMPORT // todo add MENU_IO_IMPORT
val injectedMenuSingleCharSel = """ val injectedMenuSingleCharSel = """
- MENU_IO_IMPORT
- CONTEXT_CHARACTER_NEW : net.torvald.terrarum.modulebasegame.ui.UINewCharacter - CONTEXT_CHARACTER_NEW : net.torvald.terrarum.modulebasegame.ui.UINewCharacter
- MENU_LABEL_RETURN - MENU_LABEL_RETURN
""" """
@@ -48,6 +49,12 @@ object UITitleRemoConYaml {
val injectedMenuSingleWorldSel = """ val injectedMenuSingleWorldSel = """
- CONTEXT_WORLD_NEW : net.torvald.terrarum.modulebasegame.ui.UINewWorld - CONTEXT_WORLD_NEW : net.torvald.terrarum.modulebasegame.ui.UINewWorld
- MENU_LABEL_RETURN - MENU_LABEL_RETURN
"""
val injectedMenuSingleSaveManage = """
- MENU_MODULES
- MENU_LABEL_PREV_SAVES
- MENU_LABEL_RETURN
""" """
operator fun invoke(hasSave: Boolean) = operator fun invoke(hasSave: Boolean) =

View File

@@ -14,6 +14,10 @@ import net.torvald.terrarum.ui.UICanvas
*/ */
class UITooltip : UICanvas() { class UITooltip : UICanvas() {
init {
handler.allowESCtoClose = false
}
override var openCloseTime: Second = 0f override var openCloseTime: Second = 0f
private val tooltipBackCol = Color.WHITE private val tooltipBackCol = Color.WHITE

View File

@@ -21,6 +21,10 @@ class UIVitalMetre(
val order: Int val order: Int
) : UICanvas() { ) : UICanvas() {
init {
handler.allowESCtoClose = false
}
init { init {
// semitransparent // semitransparent
color?.a = 0.91f color?.a = 0.91f

View File

@@ -0,0 +1,385 @@
package net.torvald.terrarum.modulebasegame.ui
import com.badlogic.gdx.graphics.Camera
import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.App
import net.torvald.terrarum.INGAME
import net.torvald.terrarum.RunningEnvironment
import net.torvald.terrarum.floorToInt
import net.torvald.terrarum.gameworld.WorldTime
import net.torvald.terrarum.gameworld.WorldTime.Companion.MONTH_LENGTH
import net.torvald.terrarum.langpack.Lang
import net.torvald.terrarum.ui.Toolkit
import net.torvald.terrarum.ui.UICanvas
import net.torvald.unicode.getKeycapPC
/**
* Created by minjaesong on 2023-08-15.
*/
class UIWallCalendar : UICanvas(
toggleKeyLiteral = App.getConfigInt("control_key_inventory"),
toggleButtonLiteral = App.getConfigInt("control_gamepad_start"),
) {
private val yearCellWidth = 200
private val cellWidth = 80
private val cellHeight = 24
override var width: Int = Toolkit.drawWidth
override var height: Int = App.scr.height
private val y = UIInventoryFull.INVENTORY_CELLS_OFFSET_Y() + 1 - 34
private val drawStartX = (Toolkit.drawWidth - cellWidth * 8) / 2 - 4
private val cellsStartY = y + 34
private val SP = "\u3000 "
val controlHelp: String
get() = if (App.environment == RunningEnvironment.PC)
"${getKeycapPC(App.getConfigInt("control_key_inventory"))} ${Lang["GAME_ACTION_CLOSE"]}"
else
"${App.gamepadLabelStart} ${Lang["GAME_ACTION_CLOSE"]}"
private var todayCell = -1
private val cellBackCols = listOf(
Color(0x3f1e22_C8), // OKLCh 14, 5, 18
Color(0x022f3a_C8), // OKLCh 218, 5, 18
Color(0x2d2b09_C8), // OKLCh 105, 5, 18
Color(0x252934_C8) // OKLCh 265, 2, 18
)
private val seasonMarkers = listOf(
7 to "CONTEXT_CALENDAR_SEASON_SPRING",
39 to "CONTEXT_CALENDAR_SEASON_SUMMER",
71 to "CONTEXT_CALENDAR_SEASON_AUTUMN",
103 to "CONTEXT_CALENDAR_SEASON_WINTER"
)
private var mouseOverCell = -1
private var mouseOverSeason = -1
override fun updateUI(delta: Float) {
mouseOverCell = if (relativeMouseX in drawStartX until drawStartX + 8 * (cellWidth + 1) &&
relativeMouseY in cellsStartY - 1 until cellsStartY - 1 + 17 * (cellHeight + 3)) {
val x = (relativeMouseX - drawStartX) / (cellWidth + 1)
val y = (relativeMouseY - cellsStartY + 1) / (cellHeight + 3)
// disable highlighting on invalid date (verddag and not winter 30)
if (x == 7 && y < 16) -1
else y * 8 + x
}
else -1
mouseOverSeason = when (mouseOverCell) {
-1 -> -1
in 0 until 34 -> 0
in 34 until 68 -> 1
in 68 until 102 -> 2
else -> 3
}
}
override fun renderUI(batch: SpriteBatch, camera: Camera) {
UIInventoryFull.drawBackground(batch, 1f)
val thisYear = INGAME.world.worldTime.years
val today = INGAME.world.worldTime.ordinalDay + 1
val todayOfWeek = INGAME.world.worldTime.dayOfWeek
// cell background
batch.color = Toolkit.Theme.COL_CELL_FILL
Toolkit.fillArea(batch, (width - yearCellWidth) / 2, y - 34, yearCellWidth, 24)
for (week in 0..7) {
Toolkit.fillArea(batch, drawStartX + (cellWidth + 1) * week + 1, y, cellWidth - 2, 24)
}
for (cellNum in 0 until 17 * 8) {
batch.color = when (cellNum) {
in 0 until 34 -> cellBackCols[0]
in 34 until 68 -> cellBackCols[1]
in 68 until 102 -> cellBackCols[2]
else -> cellBackCols[3]
}
if (cellNum % 8 != 7 || cellNum == 17 * 8 - 1) {
Toolkit.fillArea(batch, drawStartX + (cellWidth + 1) * (cellNum % 8) + 1, cellsStartY + (cellHeight + 3) * (cellNum / 8), cellWidth - 2, cellHeight)
}
}
// season name cell background
for (k in 0..3) {
batch.color = cellBackCols[k]
Toolkit.fillArea(batch, drawStartX + (cellWidth + 1) * 7 + 1, cellsStartY + (cellHeight + 3) * (k * 4), cellWidth - 2, (cellHeight + 3) * 4 - 3)
}
// cell border
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, (width - yearCellWidth) / 2 - 1, y - 35, yearCellWidth + 2, 26)
Toolkit.drawBoxBorder(batch, drawStartX, y - 1, 8 * (cellWidth + 1) - 1, 26)
for (week in 0..7) {
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * week, y - 1, cellWidth, 26)
}
// highlight a day name of mouse-up
batch.color = Toolkit.Theme.COL_MOUSE_UP
if (mouseOverCell >= 0) Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (mouseOverCell % 8), y - 1, cellWidth, 26)
// highlight today's week name
batch.color = Toolkit.Theme.COL_SELECTED
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * todayOfWeek, y - 1, cellWidth, 26)
// draw days grid
batch.color = Toolkit.Theme.COL_INACTIVE
Toolkit.drawBoxBorder(batch, drawStartX, cellsStartY - 1, 8 * (cellWidth + 1) - 1, 17 * (cellHeight + 3) - 1)
// non-season-name-cells
for (cellNum in 0 until 17 * 8) {
if (cellNum % 8 != 7 || cellNum == 17 * 8 - 1) {
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (cellNum % 8), cellsStartY + (cellHeight + 3) * (cellNum / 8) - 1, cellWidth, cellHeight + 2)
}
}
// season-name-cells
for (k in 0..3) {
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * 7, cellsStartY + (cellHeight + 3) * (k * 4) - 1, cellWidth, (cellHeight + 3) * 4 - 1)
}
// highlight a day of mouse-up
batch.color = Toolkit.Theme.COL_MOUSE_UP
if (mouseOverCell >= 0) Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (mouseOverCell % 8), cellsStartY + (cellHeight + 3) * (mouseOverCell / 8) - 1, cellWidth, cellHeight + 2)
// season border
batch.color = Toolkit.Theme.COL_MOUSE_UP
if (mouseOverSeason == 0) {
Toolkit.drawStraightLine(batch,
drawStartX,
cellsStartY - 2,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY - 1,
cellsStartY + 1 + (cellHeight + 3) * 5 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY - 1,
cellsStartY + 1 + (cellHeight + 3) * 4 - 3,
1,
true
)
}
if (mouseOverSeason in 0..1) {
Toolkit.drawStraightLine(
batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 5 - 3,
drawStartX + (cellWidth + 1) * 2 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 2 - 1,
cellsStartY + 1 + (cellHeight + 3) * 4 - 2,
cellsStartY + 1 + (cellHeight + 3) * 5 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 2,
cellsStartY + 1 + (cellHeight + 3) * 4 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
if (mouseOverSeason == 1) {
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY + 1 + (cellHeight + 3) * 5 - 2,
cellsStartY + 1 + (cellHeight + 3) * 9 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY + 1 + (cellHeight + 3) * 4 - 2,
cellsStartY + 1 + (cellHeight + 3) * 8 - 3,
1,
true
)
}
if (mouseOverSeason in 1..2) {
Toolkit.drawStraightLine(
batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 9 - 3,
drawStartX + (cellWidth + 1) * 4 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 4 - 1,
cellsStartY + 1 + (cellHeight + 3) * 8 - 2,
cellsStartY + 1 + (cellHeight + 3) * 9 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 4,
cellsStartY + 1 + (cellHeight + 3) * 8 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
if (mouseOverSeason == 2) {
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY + 1 + (cellHeight + 3) * 9 - 2,
cellsStartY + 1 + (cellHeight + 3) * 13 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY + 1 + (cellHeight + 3) * 8 - 2,
cellsStartY + 1 + (cellHeight + 3) * 12 - 3,
1,
true
)
}
if (mouseOverSeason in 2..3) {
Toolkit.drawStraightLine(
batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 13 - 3,
drawStartX + (cellWidth + 1) * 6 - 1,
1,
false
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 6 - 1,
cellsStartY + 1 + (cellHeight + 3) * 12 - 2,
cellsStartY + 1 + (cellHeight + 3) * 13 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 6,
cellsStartY + 1 + (cellHeight + 3) * 12 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
if (mouseOverSeason == 3) {
Toolkit.drawStraightLine(
batch,
drawStartX - 1,
cellsStartY + 1 + (cellHeight + 3) * 13 - 2,
cellsStartY + 1 + (cellHeight + 3) * 17 - 3,
1,
true
)
Toolkit.drawStraightLine(
batch,
drawStartX + (cellWidth + 1) * 8 - 1,
cellsStartY + 1 + (cellHeight + 3) * 12 - 2,
cellsStartY + 1 + (cellHeight + 3) * 17 - 3,
1,
true
)
Toolkit.drawStraightLine(batch,
drawStartX,
cellsStartY + 1 + (cellHeight + 3) * 17 - 3,
drawStartX + (cellWidth + 1) * 8 - 1,
1,
false
)
}
// cell texts
batch.color = Toolkit.Theme.COL_LIST_DEFAULT
Toolkit.drawTextCentered(batch, App.fontGame, Lang.getAndUseTemplate("CONTEXT_CALENDAR_DATE_FORMAT_Y", false, thisYear), yearCellWidth, (width - yearCellWidth) / 2, y - 34)
for (week in 0..7) {
// highlight this week and the mouse-up
batch.color = if (week == todayOfWeek) Toolkit.Theme.COL_SELECTED else if (week == mouseOverCell % 8) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_LIST_DEFAULT
val t = WorldTime.getDayName(week)
val tlen = App.fontGame.getWidth(t)
App.fontGame.draw(batch, t, drawStartX + (cellWidth + 1) * week + (cellWidth - tlen) / 2, y)
}
var dayAkku = 1
for (cellNum in 0 until 17 * 8) {
val day = if (cellNum == 17*8-1) 120 else if (cellNum % 8 == 7) 0 else dayAkku
if (day > 0) {
// highlight today and the mouse-up
batch.color = if (day == today) Toolkit.Theme.COL_SELECTED else if (cellNum == mouseOverCell) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_LIST_DEFAULT
val t = "${(day % MONTH_LENGTH).let { if (it == 0) MONTH_LENGTH else it }}".padStart(2, '\u2007')
App.fontGame.draw(batch, t, drawStartX + (cellWidth + 1) * (cellNum % 8) - 20 + cellWidth - 4, cellsStartY + (cellHeight + 3) * (cellNum / 8))
if (day == today) todayCell = cellNum
dayAkku += 1
}
}
// draw seasonal names
seasonMarkers.forEachIndexed { index, (cellNum, key) ->
batch.color = if (index == mouseOverSeason) Toolkit.Theme.COL_MOUSE_UP else Toolkit.Theme.COL_INACTIVE
Toolkit.drawTextCentered(batch, App.fontGame, Lang[key], cellWidth, drawStartX + (cellWidth + 1) * (cellNum % 8), cellsStartY + (cellHeight + 3) * (cellNum / 8) + ((cellHeight + 3) * 1.5f).floorToInt())
}
// highlight today cell
if (todayCell >= 0) {
batch.color = Toolkit.Theme.COL_SELECTED
Toolkit.drawBoxBorder(batch, drawStartX + (cellWidth + 1) * (todayCell % 8), cellsStartY + (cellHeight + 3) * (todayCell / 8) - 1, cellWidth, cellHeight + 2)
}
// control hints
batch.color = Color.WHITE
App.fontGame.draw(batch, controlHelp, drawStartX + 2, cellsStartY+ 17 * (cellHeight + 3) + 6)
}
override fun doOpening(delta: Float) {
super.doOpening(delta)
INGAME.pause()
INGAME.setTooltipMessage(null)
}
override fun doClosing(delta: Float) {
super.doClosing(delta)
INGAME.resume()
INGAME.setTooltipMessage(null)
}
override fun endOpening(delta: Float) {
super.endOpening(delta)
UIItemInventoryItemGrid.tooltipShowing.clear()
INGAME.setTooltipMessage(null) // required!
}
override fun endClosing(delta: Float) {
super.endClosing(delta)
UIItemInventoryItemGrid.tooltipShowing.clear()
INGAME.setTooltipMessage(null) // required!
}
override fun dispose() {
}
}

View File

@@ -165,7 +165,7 @@ class BasicDebugInfoWindow : UICanvas() {
App.fontSmallNumbers.draw(batch, "${if (player.jumping) "$ccG" else "$ccK"}JM", gap + 7f*(jX + 8), line(jY)) App.fontSmallNumbers.draw(batch, "${if (player.jumping) "$ccG" else "$ccK"}JM", gap + 7f*(jX + 8), line(jY))
App.fontSmallNumbers.draw(batch, "${if (player.isJumpDown) "$ccG" else "$ccK"}KY", gap + 7f*(jX + 8), line(jY+1)) App.fontSmallNumbers.draw(batch, "${if (player.isJumpDown) "$ccG" else "$ccK"}KY", gap + 7f*(jX + 8), line(jY+1))
App.fontSmallNumbers.draw(batch, "${if (player.downDownVirtually) "$ccG" else "$ccK"}$ARROW_DOWN", gap + 7f*(jX + 11), line(jY+1)) App.fontSmallNumbers.draw(batch, "${if (player.downButtonHeld > 0) "$ccG" else "$ccK"}$ARROW_DOWN", gap + 7f*(jX + 11), line(jY+1))
App.fontSmallNumbers.draw(batch, "$WIDTH$ccG${player.hitbox.width.toString().padEnd(5).substring(0,5).trim()}$ccY$HEIGHT$ccG${player.hitbox.height.toString().padEnd(5).substring(0,5)}", gap + 7f*(jX + 13), line(jY)) App.fontSmallNumbers.draw(batch, "$WIDTH$ccG${player.hitbox.width.toString().padEnd(5).substring(0,5).trim()}$ccY$HEIGHT$ccG${player.hitbox.height.toString().padEnd(5).substring(0,5)}", gap + 7f*(jX + 13), line(jY))
App.fontSmallNumbers.draw(batch, "$MASS$ccG${player.mass.toString().padEnd(8).substring(0,8)}", gap + 7f*(jX + 13), line(jY+1)) App.fontSmallNumbers.draw(batch, "$MASS$ccG${player.mass.toString().padEnd(8).substring(0,8)}", gap + 7f*(jX + 13), line(jY+1))

View File

@@ -5,7 +5,7 @@ import com.badlogic.gdx.graphics.*
import com.badlogic.gdx.graphics.g2d.BitmapFont import com.badlogic.gdx.graphics.g2d.BitmapFont
import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.g2d.SpriteBatch
import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.graphics.glutils.FloatFrameBuffer import com.badlogic.gdx.graphics.glutils.Float16FrameBuffer
import com.badlogic.gdx.utils.Disposable import com.badlogic.gdx.utils.Disposable
import com.jme3.math.FastMath import com.jme3.math.FastMath
import net.torvald.random.HQRNG import net.torvald.random.HQRNG
@@ -48,9 +48,9 @@ object Toolkit : Disposable {
private val shaderBoxDown = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxdown.frag") private val shaderBoxDown = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxdown.frag")
private val shaderBoxUp = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxup.frag") private val shaderBoxUp = App.loadShaderFromClasspath("shaders/default.vert", "shaders/boxup.frag")
private lateinit var fboBlur: FloatFrameBuffer private lateinit var fboBlur: Float16FrameBuffer
private lateinit var fboBlurHalf: FloatFrameBuffer private lateinit var fboBlurHalf: Float16FrameBuffer
private lateinit var fboBlurQuarter: FloatFrameBuffer private lateinit var fboBlurQuarter: Float16FrameBuffer
private lateinit var blurWriteQuad: Mesh private lateinit var blurWriteQuad: Mesh
private lateinit var blurWriteQuad2: Mesh private lateinit var blurWriteQuad2: Mesh
private lateinit var blurWriteQuad4: Mesh private lateinit var blurWriteQuad4: Mesh
@@ -326,17 +326,17 @@ object Toolkit : Disposable {
val fw = App.scr.width//MathUtils.nextPowerOfTwo(App.scr.width) val fw = App.scr.width//MathUtils.nextPowerOfTwo(App.scr.width)
val fh = App.scr.height//MathUtils.nextPowerOfTwo(App.scr.height) val fh = App.scr.height//MathUtils.nextPowerOfTwo(App.scr.height)
fboBlur = FloatFrameBuffer( fboBlur = Float16FrameBuffer(
fw, fw,
fh, fh,
false false
) )
fboBlurHalf = FloatFrameBuffer( fboBlurHalf = Float16FrameBuffer(
fw / 2, fw / 2,
fh / 2, fh / 2,
false false
) )
fboBlurQuarter = FloatFrameBuffer( fboBlurQuarter = Float16FrameBuffer(
fw / 4, fw / 4,
fh / 4, fh / 4,
false false

View File

@@ -30,7 +30,7 @@ class UIHandler(//var UI: UICanvas,
// UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int)) // UI positions itself? (you must g.flush() yourself after the g.translate(Int, Int))
var customPositioning: Boolean = false, // mainly used by vital meter var customPositioning: Boolean = false, // mainly used by vital meter
var doNotWarnConstant: Boolean = false, var doNotWarnConstant: Boolean = false,
internal var allowESCtoClose: Boolean = false, internal var allowESCtoClose: Boolean = true,
var uiTogglerFunctionDefault: ((UIHandler) -> Unit)? = null var uiTogglerFunctionDefault: ((UIHandler) -> Unit)? = null
): Disposable { ): Disposable {
@@ -214,7 +214,7 @@ void main() {
} }
// ESC is a master key for closing // ESC is a master key for closing
if (allowESCtoClose && Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE) && isOpened) { if (!alwaysVisible && allowESCtoClose && Gdx.input.isKeyJustPressed(Input.Keys.ESCAPE) && isOpened) {
setAsClose() setAsClose()
} }
} }

View File

@@ -23,7 +23,8 @@ class UIItemTextSelector(
initialSelection: Int, initialSelection: Int,
override val width: Int, override val width: Int,
private val drawBorder: Boolean = true, private val drawBorder: Boolean = true,
private val clickToShowPalette: Boolean = true private val clickToShowPalette: Boolean = true,
private val useSpinnerButtons: Boolean = false,
) : UIItem(parentUI, initialX, initialY) { ) : UIItem(parentUI, initialX, initialY) {
init { init {
@@ -127,6 +128,9 @@ class UIItemTextSelector(
else if (!Terrarum.mouseDown) mouseLatched = false else if (!Terrarum.mouseDown) mouseLatched = false
} }
private val leftIcon = if (useSpinnerButtons) labels.get(9,2) else labels.get(16,0)
private val rightIcon = if (useSpinnerButtons) labels.get(10,2) else labels.get(17,0)
override fun render(batch: SpriteBatch, camera: Camera) { override fun render(batch: SpriteBatch, camera: Camera) {
labelCache = labelfuns.map { it() } labelCache = labelfuns.map { it() }
@@ -184,12 +188,12 @@ class UIItemTextSelector(
// left button icon // left button icon
batch.color = if (mouseOnButton == 1 && mousePushed) Toolkit.Theme.COL_SELECTED batch.color = if (mouseOnButton == 1 && mousePushed) Toolkit.Theme.COL_SELECTED
else if (mouseOnButton == 1) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT else if (mouseOnButton == 1) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT
batch.draw(labels.get(16,0), posX + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f) batch.draw(leftIcon, posX + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f)
// right button icon // right button icon
batch.color = if (mouseOnButton == 2 && mousePushed) Toolkit.Theme.COL_SELECTED batch.color = if (mouseOnButton == 2 && mousePushed) Toolkit.Theme.COL_SELECTED
else if (mouseOnButton == 2) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT else if (mouseOnButton == 2) Toolkit.Theme.COL_MOUSE_UP else UIItemTextLineInput.TEXTINPUT_COL_TEXT
batch.draw(labels.get(17,0), posX + width - buttonW + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f) batch.draw(rightIcon, posX + width - buttonW + (buttonW - labels.tileW) / 2f, posY + (height - labels.tileH) / 2f)
// draw text // draw text
if (!paletteShowing) { if (!paletteShowing) {

View File

@@ -154,8 +154,8 @@ internal object WeatherMixer : RNGConsumer {
} }
var turbidity = 4.0; private set var turbidity = 1.0; private set
private var gH = 1.4f * App.scr.height private var gH = 1.8f * App.scr.height
// private var gH = 0.8f * App.scr.height // private var gH = 0.8f * App.scr.height
internal var parallaxPos = 0f; private set internal var parallaxPos = 0f; private set
@@ -168,9 +168,11 @@ internal object WeatherMixer : RNGConsumer {
drawSkybox(camera, batch, world) drawSkybox(camera, batch, world)
} }
private val parallaxDomainSize = 400f
private val turbidityDomainSize = 533.3333f
private fun drawSkybox(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) { private fun drawSkybox(camera: Camera, batch: FlippingSpriteBatch, world: GameWorld) {
val parallaxZeroPos = (world.height / 3f) val parallaxZeroPos = (world.height / 3f)
val parallaxDomainSize = 300f
// we will not care for nextSkybox for now // we will not care for nextSkybox for now
val timeNow = (forceTimeAt ?: world.worldTime.TIME_T.toInt()) % WorldTime.DAY_LENGTH val timeNow = (forceTimeAt ?: world.worldTime.TIME_T.toInt()) % WorldTime.DAY_LENGTH
@@ -199,17 +201,20 @@ internal object WeatherMixer : RNGConsumer {
-+ <- 0.0 = -+ <- 0.0 =
*/ */
val parallax = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / parallaxDomainSize).times(-1f).coerceIn(-1f, 1f) val parallax = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / parallaxDomainSize).times(-1f).coerceIn(-1f, 1f)
val turbidityCoeff = ((parallaxZeroPos - WorldCamera.gdxCamY.div(TILE_SIZEF)) / turbidityDomainSize).times(-1f).coerceIn(-1f, 1f)
parallaxPos = parallax parallaxPos = parallax
// println(parallax) // parallax value works as intended. // println(parallax) // parallax value works as intended.
gdxBlendNormalStraightAlpha() gdxBlendNormalStraightAlpha()
turbidity = (3.5 + turbidityCoeff * 2.5).coerceIn(1.0, 6.0)
val thisTurbidity = forceTurbidity ?: turbidity val thisTurbidity = forceTurbidity ?: turbidity
// TODO trilinear with (deg, turb, alb)
val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f) val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f)
val (tex, uvs) = Skybox.getUV(solarElev, thisTurbidity, 0.3)
val (tex, uvs, turbX, albX) = Skybox.getUV(solarElev, thisTurbidity, 0.3)
val mornNoonBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200;
starmapTex.texture.bind(1) starmapTex.texture.bind(1)
Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it
@@ -220,20 +225,26 @@ internal object WeatherMixer : RNGConsumer {
batch.inUse { batch.inUse {
batch.shader = shaderBlendMax batch.shader = shaderBlendMax
shaderBlendMax.setUniformi("tex1", 1) shaderBlendMax.setUniformi("tex1", 1)
shaderBlendMax.setUniformf("drawOffset", 0f, gradY) shaderBlendMax.setUniformf("drawOffsetSize", 0f, gradY, App.scr.wf, gH)
shaderBlendMax.setUniformf("drawOffsetSize", App.scr.wf, gH) shaderBlendMax.setUniform4fv("uvA", uvs, 0, 4)
shaderBlendMax.setUniform2fv("skyboxUV1", uvs, 0, 2) shaderBlendMax.setUniform4fv("uvB", uvs, 4, 4)
shaderBlendMax.setUniform2fv("skyboxUV2", uvs, 2, 2) shaderBlendMax.setUniform4fv("uvC", uvs, 8, 4)
shaderBlendMax.setUniform4fv("uvD", uvs, 12, 4)
shaderBlendMax.setUniform4fv("uvE", uvs, 16, 4)
shaderBlendMax.setUniform4fv("uvF", uvs, 20, 4)
shaderBlendMax.setUniform4fv("uvG", uvs, 24, 4)
shaderBlendMax.setUniform4fv("uvH", uvs, 28, 4)
shaderBlendMax.setUniformf("texBlend", mornNoonBlend, turbX, albX, 0f)
shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY) shaderBlendMax.setUniformf("astrumScroll", astrumOffX + astrumX, astrumOffY + astrumY)
shaderBlendMax.setUniformf("randomNumber", shaderBlendMax.setUniformf("randomNumber",
// (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(31L) xor 1453L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(37L) xor 862L + 31L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(37L) xor 862L + 31L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(23L) xor 1639L + 29L).and(1023).toFloat(),
// (world.worldTime.TIME_T.plus(29L) xor 2971L + 41L).and(1023).toFloat(), // (world.worldTime.TIME_T.plus(29L) xor 2971L + 41L).and(1023).toFloat(),
world.worldTime.TIME_T.div(+12.1f).plus(31L), world.worldTime.TIME_T.div(+14.1f).plus(31L),
world.worldTime.TIME_T.div(-11.8f).plus(37L), world.worldTime.TIME_T.div(-13.8f).plus(37L),
world.worldTime.TIME_T.div(+11.9f).plus(23L), world.worldTime.TIME_T.div(+13.9f).plus(23L),
world.worldTime.TIME_T.div(-12.3f).plus(29L), world.worldTime.TIME_T.div(-14.3f).plus(29L),
) )
batch.color = Color.WHITE batch.color = Color.WHITE

View File

@@ -349,7 +349,7 @@ internal object BlocksDrawer {
val tileNumber = if (thisTile == Block.AIR) 0 val tileNumber = if (thisTile == Block.AIR) 0
// special case: actorblocks and F3 key // special case: actorblocks and F3 key
else if (BlockCodex.hasProp(thisTile) && BlockCodex[thisTile].isActorBlock && else if (BlockCodex.hasProp(thisTile) && BlockCodex[thisTile].isActorBlock &&
!BlockCodex[thisTile].tags.contains("DORENDER") && !KeyToggler.isOn(Keys.F3)) !BlockCodex[thisTile].hasTag("DORENDER") && !KeyToggler.isOn(Keys.F3))
0 0
// special case: fluids // special case: fluids
else if (mode == FLUID) else if (mode == FLUID)

View File

@@ -6,7 +6,9 @@ import com.badlogic.gdx.graphics.Color
import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Pixmap
import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.Texture
import com.badlogic.gdx.utils.GdxRuntimeException import com.badlogic.gdx.utils.GdxRuntimeException
import com.jme3.math.FastMath
import net.torvald.gdx.graphics.Cvec import net.torvald.gdx.graphics.Cvec
import net.torvald.gdx.graphics.PixmapIO2
import net.torvald.terrarum.* import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
@@ -15,6 +17,7 @@ import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.worlddrawer.CreateTileAtlas.AtlasSource.* import net.torvald.terrarum.worlddrawer.CreateTileAtlas.AtlasSource.*
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.sqrt
/** /**
* This class implements work_files/dynamic_shape_2_0.psd * This class implements work_files/dynamic_shape_2_0.psd
@@ -28,13 +31,12 @@ import kotlin.math.roundToInt
*/ */
class CreateTileAtlas { class CreateTileAtlas {
// min size 1024 = tile_size 16 * atlasCursor 64 var MAX_TEX_SIZE = App.getConfigInt("atlastexsize").coerceIn(1024, App.glInfo.GL_MAX_TEXTURE_SIZE); private set
val MAX_TEX_SIZE = App.getConfigInt("atlastexsize").coerceIn(1024, App.glInfo.GL_MAX_TEXTURE_SIZE) var TILES_IN_X = MAX_TEX_SIZE / TILE_SIZE; private set
val TILES_IN_X = MAX_TEX_SIZE / TILE_SIZE
val SHADER_SIZE_KEYS = floatArrayOf(MAX_TEX_SIZE.toFloat(), MAX_TEX_SIZE.toFloat(), TILES_IN_X.toFloat(), TILES_IN_X.toFloat()) var SHADER_SIZE_KEYS = floatArrayOf(MAX_TEX_SIZE.toFloat(), MAX_TEX_SIZE.toFloat(), TILES_IN_X.toFloat(), TILES_IN_X.toFloat()); private set
private val TOTAL_TILES = TILES_IN_X * TILES_IN_X private var TOTAL_TILES = TILES_IN_X * TILES_IN_X
val wallOverlayColour = Color(.65f, .65f, .65f, 1f) val wallOverlayColour = Color(.65f, .65f, .65f, 1f)
@@ -66,12 +68,54 @@ class CreateTileAtlas {
private val atlasInit = "./assets/graphics/blocks/init.tga" private val atlasInit = "./assets/graphics/blocks/init.tga"
private var itemSheetCursor = 16 private var itemSheetCursor = 16
internal val itemTerrainPixmap = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) internal lateinit var itemTerrainPixmap: Pixmap
internal val itemTerrainPixmapGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) internal lateinit var itemTerrainPixmapGlow: Pixmap
internal val itemWallPixmap = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) internal lateinit var itemWallPixmap: Pixmap
internal val itemWallPixmapGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) internal lateinit var itemWallPixmapGlow: Pixmap
private fun drawInitPixmap() {
val initPixmap = Pixmap(Gdx.files.internal(atlasInit))
val tilesInInitPixmap = (initPixmap.width * initPixmap.height) / (TILE_SIZE * TILE_SIZE)
val tilesPossibleInCurrentPixmap = (atlas.width * atlas.height) / (TILE_SIZE * TILE_SIZE)
if (tilesInInitPixmap > tilesPossibleInCurrentPixmap) throw Error("Atlas size too small -- can't even fit the init.tga (MAX_TEX_SIZE must be at least ${FastMath.nextPowerOfTwo((sqrt(tilesInInitPixmap.toFloat()) * TILE_SIZE).ceilToInt())})")
if (MAX_TEX_SIZE >= initPixmap.width) {
atlas.drawPixmap(initPixmap, 0, 0)
atlasAutumn.drawPixmap(initPixmap, 0, 0)
atlasWinter.drawPixmap(initPixmap, 0, 0)
atlasSpring.drawPixmap(initPixmap, 0, 0)
}
else {
/*
What's happening:
src: dest:
AAAABBBBCCCCDDDD AAAA
BBBB
CCCC
DDDD
*/
val destX = 0
val srcY = 0
val scanW = MAX_TEX_SIZE
val scanH = TILE_SIZE
for (scantile in 0 until (initPixmap.width.toFloat() / MAX_TEX_SIZE).ceilToInt()) {
val srcX = scantile * scanW
val destY = scantile * TILE_SIZE
atlas.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH)
atlasAutumn.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH)
atlasWinter.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH)
atlasSpring.drawPixmap(initPixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH)
}
}
initPixmap.dispose()
}
/** /**
* Must be called AFTER mods' loading so that all the block props are loaded * Must be called AFTER mods' loading so that all the block props are loaded
*/ */
@@ -80,27 +124,17 @@ class CreateTileAtlas {
tags = HashMap<ItemID, RenderTag>() tags = HashMap<ItemID, RenderTag>()
itemSheetNumbers = HashMap<ItemID, Int>() itemSheetNumbers = HashMap<ItemID, Int>()
atlas = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) atlas = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
atlasAutumn = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) atlasAutumn = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
atlasWinter = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) atlasWinter = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
atlasSpring = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) atlasSpring = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
atlasFluid = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) atlasFluid = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
atlasGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) atlasGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also { it.blending = Pixmap.Blending.None }
atlas.blending = Pixmap.Blending.None
atlasAutumn.blending = Pixmap.Blending.None
atlasWinter.blending = Pixmap.Blending.None
atlasSpring.blending = Pixmap.Blending.None
atlasFluid.blending = Pixmap.Blending.None
atlasGlow.blending = Pixmap.Blending.None
// populate the atlantes with atlasInit // populate the atlantes with atlasInit
// this just directly copies the image to the atlantes :p // this just directly copies the image to the atlantes :p
val initPixmap = Pixmap(Gdx.files.internal(atlasInit)) drawInitPixmap()
atlas.drawPixmap(initPixmap, 0, 0)
atlasAutumn.drawPixmap(initPixmap, 0, 0)
atlasWinter.drawPixmap(initPixmap, 0, 0)
atlasSpring.drawPixmap(initPixmap, 0, 0)
// get all the files applicable // get all the files applicable
// first, get all the '/blocks' directory, and add all the files, regardless of their extension, to the list // first, get all the '/blocks' directory, and add all the files, regardless of their extension, to the list
@@ -135,7 +169,7 @@ class CreateTileAtlas {
} }
// test print // test print
//PixmapIO2.writeTGA(Gdx.files.absolute("${AppLoader.defaultDir}/atlas.tga"), atlas, false) // PixmapIO2.writeTGA(Gdx.files.absolute("${App.defaultDir}/atlas.tga"), atlas, false)
// PixmapIO2.writeTGA(Gdx.files.absolute("${AppLoader.defaultDir}/atlasGlow.tga"), atlasGlow, false) // PixmapIO2.writeTGA(Gdx.files.absolute("${AppLoader.defaultDir}/atlasGlow.tga"), atlasGlow, false)
@@ -161,6 +195,11 @@ class CreateTileAtlas {
// val itemTerrainPixmap = Pixmap(16 * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) // val itemTerrainPixmap = Pixmap(16 * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888)
// val itemWallPixmap = Pixmap(16 * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888) // val itemWallPixmap = Pixmap(16 * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888)
itemTerrainPixmap = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888)
itemTerrainPixmapGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888)
itemWallPixmap = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888)
itemWallPixmapGlow = Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888)
tags.toMap().forEach { id, tag -> tags.toMap().forEach { id, tag ->
val tilePosFromAtlas = tag.tileNumber + maskTypetoTileIDForItemImage(tag.maskType) val tilePosFromAtlas = tag.tileNumber + maskTypetoTileIDForItemImage(tag.maskType)
val srcX = (tilePosFromAtlas % TILES_IN_X) * TILE_SIZE val srcX = (tilePosFromAtlas % TILES_IN_X) * TILE_SIZE
@@ -217,7 +256,6 @@ class CreateTileAtlas {
itemWallTextureGlow = Texture(itemWallPixmapGlow) itemWallTextureGlow = Texture(itemWallPixmapGlow)
// itemTerrainPixmap.dispose() // itemTerrainPixmap.dispose()
// itemWallPixmap.dispose() // itemWallPixmap.dispose()
initPixmap.dispose()
initialised = true initialised = true
} } } }
@@ -242,6 +280,7 @@ class CreateTileAtlas {
val blockName = matte.nameWithoutExtension().split('-').last().toInt() // basically a filename val blockName = matte.nameWithoutExtension().split('-').last().toInt() // basically a filename
val blockID = "$modname:$blockName" val blockID = "$modname:$blockName"
// determine the type of the block (populate tags list) // determine the type of the block (populate tags list)
// predefined by the image dimension: 16x16 for (1,0) // predefined by the image dimension: 16x16 for (1,0)
if (tilesPixmap.width == TILE_SIZE && tilesPixmap.height == TILE_SIZE) { if (tilesPixmap.width == TILE_SIZE && tilesPixmap.height == TILE_SIZE) {
@@ -307,8 +346,10 @@ class CreateTileAtlas {
} }
private fun drawToAtlantes(matte: Pixmap, glow: Pixmap, tilesCount: Int) { private fun drawToAtlantes(matte: Pixmap, glow: Pixmap, tilesCount: Int) {
if (atlasCursor >= TOTAL_TILES) { if (atlasCursor + tilesCount >= TOTAL_TILES) {
throw Error("Too much tiles for $MAX_TEX_SIZE texture size: $atlasCursor") // throw Error("Too much tiles for $MAX_TEX_SIZE texture size: $atlasCursor")
println("[CreateTileAtlas] Too much tiles for atlas of ${MAX_TEX_SIZE}x$MAX_TEX_SIZE (tiles so far: $atlasCursor/${(MAX_TEX_SIZE*MAX_TEX_SIZE)/(TILE_SIZE* TILE_SIZE)}, tiles to be added: $tilesCount), trying to expand the atlas...")
expandAtlantes()
} }
val seasonal = matte.width == matte.height && matte.width == 14 * TILE_SIZE val seasonal = matte.width == matte.height && matte.width == 14 * TILE_SIZE
@@ -413,4 +454,60 @@ class CreateTileAtlas {
private enum class AtlasSource { private enum class AtlasSource {
FOUR_SEASONS, SUMMER, AUTUMN, WINTER, SPRING, FLUID, GLOW FOUR_SEASONS, SUMMER, AUTUMN, WINTER, SPRING, FLUID, GLOW
} }
private fun expandAtlantes() {
if (MAX_TEX_SIZE >= App.glInfo.GL_MAX_TEXTURE_SIZE) {
throw RuntimeException("Cannot expand atlas: texture size is already at its maximum possible size allowed by the graphics processor (${MAX_TEX_SIZE}x${MAX_TEX_SIZE})")
}
val oldTexSize = MAX_TEX_SIZE
val newTexSize = oldTexSize * 2
MAX_TEX_SIZE = newTexSize
TILES_IN_X = MAX_TEX_SIZE / TILE_SIZE
SHADER_SIZE_KEYS = floatArrayOf(MAX_TEX_SIZE.toFloat(), MAX_TEX_SIZE.toFloat(), TILES_IN_X.toFloat(), TILES_IN_X.toFloat())
TOTAL_TILES = TILES_IN_X * TILES_IN_X
val newAtlantes = Array<Pixmap>(5) {
Pixmap(TILES_IN_X * TILE_SIZE, TILES_IN_X * TILE_SIZE, Pixmap.Format.RGBA8888).also {
it.blending = Pixmap.Blending.None
it.filter = Pixmap.Filter.NearestNeighbour
}
}
listOf(atlas, atlasAutumn, atlasWinter, atlasSpring, atlasGlow).forEachIndexed { index, pixmap ->
/*
How it works:
old: new:
AAAAAAAA AAAAAAAABBBBBBBB
BBBBBBBB CCCCCCCCDDDDDDDD
CCCCCCCC ...
DDDDDDDD
...
*/
for (scantile in 0 until pixmap.height / TILE_SIZE) {
val srcX = 0
val srcY = scantile * TILE_SIZE
val destX = (scantile % 2) * oldTexSize
val destY = (scantile / 2) * TILE_SIZE
val scanW = pixmap.width
val scanH = TILE_SIZE
newAtlantes[index].drawPixmap(pixmap, srcX, srcY, scanW, scanH, destX, destY, scanW, scanH)
}
pixmap.dispose()
}
atlas = newAtlantes[0]
atlasAutumn = newAtlantes[1]
atlasWinter = newAtlantes[2]
atlasSpring = newAtlantes[3]
atlasGlow = newAtlantes[4]
App.setConfig("atlastexsize", newTexSize)
}
} }

View File

@@ -33,12 +33,12 @@ object FeaturesDrawer {
TileSurvey.submitProposal( TileSurvey.submitProposal(
TileSurvey.SurveyProposal( TileSurvey.SurveyProposal(
"basegame.FeaturesDrawer.coldTiles", 72, 48, 2, 2 "basegame.FeaturesDrawer.coldTiles", 72, 48, 2, 2
) { world, x, y -> BlockCodex[world.getTileFromTerrain(x, y)].tags.contains("COLD") } ) { world, x, y -> BlockCodex[world.getTileFromTerrain(x, y)].hasTag("COLD") }
) )
TileSurvey.submitProposal( TileSurvey.submitProposal(
TileSurvey.SurveyProposal( TileSurvey.SurveyProposal(
"basegame.FeaturesDrawer.warmTiles", 72, 48, 2, 2 "basegame.FeaturesDrawer.warmTiles", 72, 48, 2, 2
) { world, x, y -> BlockCodex[world.getTileFromTerrain(x, y)].tags.contains("WARM") } ) { world, x, y -> BlockCodex[world.getTileFromTerrain(x, y)].hasTag("WARM") }
) )
} }

View File

@@ -28,6 +28,7 @@
package org.dyn4j.geometry package org.dyn4j.geometry
import org.dyn4j.Epsilon import org.dyn4j.Epsilon
import kotlin.math.sign
/** /**
* This class represents a vector or point in 2D space. * This class represents a vector or point in 2D space.
@@ -699,6 +700,9 @@ class Vector2 {
return a return a
} }
val signum: Vector2
get() = Vector2(this.x.sign, this.y.sign)
companion object { companion object {
/** A vector representing the x-axis; this vector should not be changed at runtime; used internally */ /** A vector representing the x-axis; this vector should not be changed at runtime; used internally */
internal val X_AXIS = Vector2(1.0, 0.0) internal val X_AXIS = Vector2(1.0, 0.0)

View File

@@ -13,10 +13,16 @@ out vec4 fragColor;
const vec2 boolean = vec2(0.0, 1.0); const vec2 boolean = vec2(0.0, 1.0);
uniform vec2 drawOffset; // value of the 'gradY' uniform vec4 drawOffsetSize; // (gradX, gradY, gradW, gradH)
uniform vec2 drawOffsetSize; // value of the 'gradH' uniform vec4 uvA; // (u, v, u2, v2) for morn, turbLow, albLow
uniform vec2 skyboxUV1; // (u, v) for the skybox drawing uniform vec4 uvB; // (u, v, u2, v2) for noon, turbLow, albLow
uniform vec2 skyboxUV2; // (u2, v2) for the skybox drawing uniform vec4 uvC; // (u, v, u2, v2) for morn, turbHigh, albLow
uniform vec4 uvD; // (u, v, u2, v2) for noon, turbHigh, albLow
uniform vec4 uvE; // (u, v, u2, v2) for morn, turbLow, albHigh
uniform vec4 uvF; // (u, v, u2, v2) for noon, turbLow, albHigh
uniform vec4 uvG; // (u, v, u2, v2) for morn, turbHigh, albHigh
uniform vec4 uvH; // (u, v, u2, v2) for noon, turbHigh, albHigh
uniform vec4 texBlend; // (morn/noon, turbidity, albedo, unused)
uniform vec2 tex1Size = vec2(4096.0); uniform vec2 tex1Size = vec2(4096.0);
uniform vec2 astrumScroll = vec2(0.0); uniform vec2 astrumScroll = vec2(0.0);
uniform vec4 randomNumber = vec4(1.0, -2.0, 3.0, -4.0); uniform vec4 randomNumber = vec4(1.0, -2.0, 3.0, -4.0);
@@ -112,15 +118,36 @@ vec4 random(vec2 p) {
// draw call to this function must use UV coord of (0,0,1,1)! // draw call to this function must use UV coord of (0,0,1,1)!
void main(void) { void main(void) {
vec2 skyboxTexCoord = mix(skyboxUV1, skyboxUV2, v_texCoords); vec4 colorTexA = texture(u_texture, mix(uvA.xy, uvA.zw, v_texCoords));
vec2 astrumTexCoord = (v_texCoords * drawOffsetSize + drawOffset + astrumScroll) / tex1Size; vec4 colorTexB = texture(u_texture, mix(uvB.xy, uvB.zw, v_texCoords));
vec4 colorTexC = texture(u_texture, mix(uvC.xy, uvC.zw, v_texCoords));
vec4 colorTexD = texture(u_texture, mix(uvD.xy, uvD.zw, v_texCoords));
vec4 colorTexE = texture(u_texture, mix(uvE.xy, uvE.zw, v_texCoords));
vec4 colorTexF = texture(u_texture, mix(uvF.xy, uvF.zw, v_texCoords));
vec4 colorTexG = texture(u_texture, mix(uvG.xy, uvG.zw, v_texCoords));
vec4 colorTexH = texture(u_texture, mix(uvH.xy, uvH.zw, v_texCoords));
vec2 astrumTexCoord = (v_texCoords * drawOffsetSize.zw + drawOffsetSize.xy + astrumScroll) / tex1Size;
vec4 randomness = snoise4((gl_FragCoord.xy - astrumScroll) * 0.16) * 2.0; // multiply by 2 so that the "density" of the stars would be same as the non-random version vec4 randomness = snoise4((gl_FragCoord.xy - astrumScroll) * 0.16) * 2.0; // multiply by 2 so that the "density" of the stars would be same as the non-random version
vec4 colorTex0 = texture(u_texture, skyboxTexCoord);
vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness; vec4 colorTex1 = texture(tex1, astrumTexCoord) * randomness;
// notations used: https://en.wikipedia.org/wiki/File:Enclosing_points.svg and https://en.wikipedia.org/wiki/File:3D_interpolation2.svg
vec4 colorTex0 = mix(
mix(
mix(colorTexA, colorTexE, texBlend.z), // c00 = c000..c100
mix(colorTexC, colorTexG, texBlend.z), // c10 = c010..c110
texBlend.y
), // c0 = c00..c10
mix(
mix(colorTexB, colorTexF, texBlend.z), // c01 = c001..c101
mix(colorTexD, colorTexH, texBlend.z), // c11 = c011..c111
texBlend.y
), // c1 = c01..c11
texBlend.x
); // c = c0..c1
fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy; fragColor = (max(colorTex0, colorTex1) * boolean.yyyx) + boolean.xxxy;

View File

@@ -1,6 +1,6 @@
#version 150 #version 150
#ifdef GL_ES #ifdef GL_ES
precision highp float; precision mediump float;
#endif #endif

View File

@@ -30,7 +30,7 @@ PlainText = ? regular string but does not contain { c U r L y } brackets ? ;
function ShowMsg(string: String, vararg args: String) { ... } // pre-defined function ShowMsg(string: String, vararg args: String) { ... } // pre-defined
val m = "Give {0} {P.0 1} to {2.ACC}" val m = "Give {0} {P.0 1} to {2.ACC}"
ShowMsg(m, 42, "GAME_ITEM_COAL", conversationTarget.actorValue.name) ShowMsg(m, 42, "ITEM_COAL", conversationTarget.actorValue.name)
val m2 = "{0}{G.0 을 를} 찾을 수 없습니다" val m2 = "{0}{G.0 을 를} 찾을 수 없습니다"
ShowMsg(m2, something) ShowMsg(m2, something)