diff --git a/src/net/torvald/terrarum/modulebasegame/Ingame.kt b/src/net/torvald/terrarum/modulebasegame/Ingame.kt index 19b3b9815..1d805872b 100644 --- a/src/net/torvald/terrarum/modulebasegame/Ingame.kt +++ b/src/net/torvald/terrarum/modulebasegame/Ingame.kt @@ -2,7 +2,6 @@ package net.torvald.terrarum.modulebasegame import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input -import com.badlogic.gdx.Screen import com.badlogic.gdx.graphics.* import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer @@ -24,12 +23,8 @@ import net.torvald.terrarum.worlddrawer.FeaturesDrawer import net.torvald.terrarum.worlddrawer.LightmapRenderer import net.torvald.terrarum.worlddrawer.WorldCamera -import java.util.ArrayList -import java.util.concurrent.locks.Lock import java.util.concurrent.locks.ReentrantLock -import javax.swing.JOptionPane - import com.badlogic.gdx.graphics.OrthographicCamera import net.torvald.random.HQRNG import net.torvald.terrarum.* @@ -46,6 +41,8 @@ import net.torvald.terrarum.modulebasegame.ui.* import net.torvald.terrarum.ui.* import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser import net.torvald.terrarum.modulebasegame.worldgenerator.WorldGenerator +import java.io.PrintStream +import java.util.* /** @@ -132,7 +129,7 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { var worldDrawFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) var worldGlowFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) - var worldBlendFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + //var worldBlendFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) // RGB elements of Lightmap for Color Vec4(R, G, B, 1.0) 24-bit private lateinit var lightmapFboA: FrameBuffer private lateinit var lightmapFboB: FrameBuffer @@ -632,13 +629,6 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { - worldBlendFrameBuffer.inAction(null, null) { - Gdx.gl.glClearColor(0f,0f,0f,0f) - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) - Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) - Gdx.gl.glEnable(GL20.GL_BLEND) - Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA) - } worldDrawFrameBuffer.inAction(null, null) { Gdx.gl.glClearColor(0f,0f,0f,0f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) @@ -666,7 +656,7 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { /////////////////////////// // draw world to the FBO // /////////////////////////// - processBlur(lightmapFboA, lightmapFboB, LightmapRenderer.DRAW_FOR_RGB) + prepareBlurredLightmap(lightmapFboA, lightmapFboB) worldDrawFrameBuffer.inAction(camera, batch) { @@ -731,33 +721,6 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { - // mix lighpmap canvas to this canvas (Colors -- RGB channel) - if (!KeyToggler.isOn(Input.Keys.F6)) { // F6 to disable lightmap draw - setCameraPosition(0f, 0f) - batch.shader = Terrarum.shaderBayer - batch.shader.setUniformf("rcount", 64f) - batch.shader.setUniformf("gcount", 64f) - batch.shader.setUniformf("bcount", 64f) // de-banding - - val lightTex = lightmapFboB.colorBufferTexture // A or B? flipped in Y means you chose wrong buffer; use one that works correctly - lightTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) // blocky feeling for A E S T H E T I C S - - if (KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) blendNormal() - else blendMul() - - batch.color = Color.WHITE - val xrem = -(WorldCamera.x.toFloat() fmod TILE_SIZEF) - val yrem = -(WorldCamera.y.toFloat() fmod TILE_SIZEF) - batch.draw(lightTex, - xrem, - yrem, - lightTex.width * lightmapDownsample, lightTex.height * lightmapDownsample - //lightTex.width.toFloat(), lightTex.height.toFloat() // for debugging - ) - - } - - Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // don't know why it is REALLY needed; it really depresses me batch.shader = null @@ -769,14 +732,28 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { batch.projectionMatrix = camera.combined } - } + } // let the shits flushed + + worldDrawFrameBuffer.inAction(camera, batch) { + // mix lighpmap canvas to this canvas + val rgbTex = worldDrawFrameBuffer.colorBufferTexture + val lightTex = lightmapFboB.colorBufferTexture // A or B? flipped in Y means you chose wrong buffer; use one that works correctly + lightTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) // blocky feeling for A E S T H E T I C S + + Terrarum.shaderMulRGBX.begin() + Terrarum.shaderMulRGBX.setUniformMatrix("u_projTrans", camera.combined) + Terrarum.shaderMulRGBX.setUniformi("u_texture", 0) + Terrarum.shaderMulRGBX.setUniformi("tex1", 1) + Terrarum.fullscreenQuad.render(Terrarum.shaderMulRGBX, GL20.GL_TRIANGLES) + Terrarum.shaderMulRGBX.end() + } // now worldDrawFrameBuffer has light premultiplied + + ////////////////////////// // draw glow to the FBO // ////////////////////////// - processBlur(lightmapFboA, lightmapFboB, LightmapRenderer.DRAW_FOR_ALPHA) - worldGlowFrameBuffer.inAction(camera, batch) { batch.inUse { batch.shader = null @@ -830,44 +807,23 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { blendNormal() } - } + } // let the shits flushed + + worldGlowFrameBuffer.inAction(camera, batch) { + // mix lighpmap canvas to this canvas + val rgbTex = worldGlowFrameBuffer.colorBufferTexture + val lightTex = lightmapFboB.colorBufferTexture // A or B? flipped in Y means you chose wrong buffer; use one that works correctly + lightTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) // blocky feeling for A E S T H E T I C S + + Terrarum.shaderMulAAAX.begin() + Terrarum.shaderMulAAAX.setUniformMatrix("u_projTrans", camera.combined) + Terrarum.shaderMulAAAX.setUniformi("u_texture", 0) + Terrarum.shaderMulAAAX.setUniformi("tex1", 1) + Terrarum.fullscreenQuad.render(Terrarum.shaderMulAAAX, GL20.GL_TRIANGLES) + Terrarum.shaderMulAAAX.end() + } // now worldGlowFrameBuffer has light premultiplied - worldBlendFrameBuffer.inAction(camera, batch) { - Gdx.gl.glClearColor(0f, 0f, 0f, 0f) - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) - - - // draw blended world - val worldTex = worldDrawFrameBuffer.colorBufferTexture // WORLD: light_color must be applied beforehand - val glowTex = worldGlowFrameBuffer.colorBufferTexture // GLOW: light_uvlight must be applied beforehand - - worldTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) - glowTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) - - worldTex.bind(0) - glowTex.bind(1) - - - Terrarum.shaderBlendGlow.begin() - Terrarum.shaderBlendGlow.setUniformMatrix("u_projTrans", camera.combined) - Terrarum.shaderBlendGlow.setUniformi("u_texture", 0) - Terrarum.shaderBlendGlow.setUniformi("tex1", 1) - Terrarum.fullscreenQuad.render(Terrarum.shaderBlendGlow, GL20.GL_TRIANGLES) - Terrarum.shaderBlendGlow.end() - - - - Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // don't know why it is REALLY needed; it really depresses me - - - batch.inUse { - batch.color = Color.WHITE - blendNormal() - - batch.shader = null - } - } ///////////////////////// @@ -897,15 +853,21 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { // draw framebuffers to screen // ///////////////////////////////// + val worldTex = worldDrawFrameBuffer.colorBufferTexture // WORLD: light_color must be applied beforehand + val glowTex = worldGlowFrameBuffer.colorBufferTexture // GLOW: light_uvlight must be applied beforehand + worldTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + glowTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) - val blendedTex = worldBlendFrameBuffer.colorBufferTexture - blendedTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) - batch.color = Color.WHITE - batch.shader = null - blendNormal() - batch.draw(blendedTex, 0f, 0f, blendedTex.width.toFloat(), blendedTex.height.toFloat()) - + // not sure about the position test + setCameraPosition(0f, 0f) + + batch.shader = Terrarum.shaderBlendGlow + Terrarum.shaderBlendGlow.setUniformi("u_texture", 0) + batch.shader.setUniformi("tex1", 1) + worldTex.bind(0) + glowTex.bind(1) + batch.draw(worldTex, 0f, 0f) @@ -1000,7 +962,7 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { } } - fun processBlur(lightmapFboA: FrameBuffer, lightmapFboB: FrameBuffer, mode: Int) { + fun prepareBlurredLightmap(lightmapFboA: FrameBuffer, lightmapFboB: FrameBuffer) { val blurIterations = 5 // ideally, 4 * radius; must be even/odd number -- odd/even number will flip the image val blurRadius = 4f / lightmapDownsample // (5, 4f); using low numbers for pixel-y aesthetics @@ -1018,24 +980,12 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { } - if (mode == LightmapRenderer.DRAW_FOR_RGB) { - // initialise readBuffer with untreated lightmap - blurReadBuffer.inAction(camera, batch) { - batch.inUse { - blendNormal(batch) - batch.color = Color.WHITE - LightmapRenderer.draw(batch, LightmapRenderer.DRAW_FOR_RGB) - } - } - } - else { - // initialise readBuffer with untreated lightmap - blurReadBuffer.inAction(camera, batch) { - batch.inUse { - blendNormal(batch) - batch.color = Color.WHITE - LightmapRenderer.draw(batch, LightmapRenderer.DRAW_FOR_ALPHA) - } + // initialise readBuffer with untreated lightmap + blurReadBuffer.inAction(camera, batch) { + batch.inUse { + blendNormal(batch) + batch.color = Color.WHITE + LightmapRenderer.draw(batch, 0) } } @@ -1419,8 +1369,6 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { worldDrawFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) worldGlowFrameBuffer.dispose() worldGlowFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) - worldBlendFrameBuffer.dispose() - worldBlendFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) if (lightmapInitialised) { lightmapFboA.dispose() @@ -1478,7 +1426,6 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { override fun dispose() { worldDrawFrameBuffer.dispose() worldGlowFrameBuffer.dispose() - worldBlendFrameBuffer.dispose() lightmapFboA.dispose() lightmapFboB.dispose() diff --git a/src/net/torvald/terrarum/modulebasegame/IngameOld.kt b/src/net/torvald/terrarum/modulebasegame/IngameOld.kt new file mode 100644 index 000000000..373e1cc3e --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/IngameOld.kt @@ -0,0 +1,1518 @@ +package net.torvald.terrarum.modulebasegame + +import com.badlogic.gdx.Gdx +import com.badlogic.gdx.Input +import com.badlogic.gdx.Screen +import com.badlogic.gdx.graphics.* +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import com.badlogic.gdx.graphics.glutils.FrameBuffer + +import net.torvald.dataclass.CircularArray +import net.torvald.terrarum.blockproperties.BlockPropUtil +import net.torvald.terrarum.blockstats.BlockStats +import net.torvald.terrarum.concurrent.ThreadParallel +import net.torvald.terrarum.console.* +import net.torvald.terrarum.gameactors.* +import net.torvald.terrarum.modulebasegame.gameactors.physicssolver.CollisionSolver +import net.torvald.terrarum.gamecontroller.IngameController +import net.torvald.terrarum.gamecontroller.KeyToggler +import net.torvald.terrarum.gameworld.GameWorld +import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator +import net.torvald.terrarum.weather.WeatherMixer +import net.torvald.terrarum.worlddrawer.BlocksDrawer +import net.torvald.terrarum.worlddrawer.FeaturesDrawer +import net.torvald.terrarum.worlddrawer.LightmapRenderer +import net.torvald.terrarum.worlddrawer.WorldCamera + +import java.util.concurrent.locks.Lock +import java.util.concurrent.locks.ReentrantLock + +import javax.swing.JOptionPane + +import com.badlogic.gdx.graphics.OrthographicCamera +import net.torvald.random.HQRNG +import net.torvald.terrarum.* +import net.torvald.terrarum.gameworld.fmod +import net.torvald.terrarum.modulebasegame.console.AVTracker +import net.torvald.terrarum.modulebasegame.console.ActorsList +import net.torvald.terrarum.console.Authenticator +import net.torvald.terrarum.console.SetGlobalLightOverride +import net.torvald.terrarum.itemproperties.ItemCodex +import net.torvald.terrarum.modulebasegame.gameactors.* +import net.torvald.terrarum.modulebasegame.imagefont.Watch7SegMain +import net.torvald.terrarum.modulebasegame.imagefont.WatchDotAlph +import net.torvald.terrarum.modulebasegame.ui.* +import net.torvald.terrarum.ui.* +import net.torvald.terrarum.modulebasegame.worldgenerator.RoguelikeRandomiser +import net.torvald.terrarum.modulebasegame.worldgenerator.WorldGenerator +import java.io.PrintStream +import java.util.* +import kotlin.reflect.KFunction + + +/** + * Created by minjaesong on 2017-06-16. + */ + +/*class IngameOld(batch: SpriteBatch) : IngameInstance(batch) { + + + private val ACTOR_UPDATE_RANGE = 4096 + + lateinit var world: GameWorld + lateinit var historicalFigureIDBucket: ArrayList + + /** + * list of Actors that is sorted by Actors' referenceID + */ + //val ACTORCONTAINER_INITIAL_SIZE = 64 + val PARTICLES_MAX = Terrarum.getConfigInt("maxparticles") + //val actorContainer = ArrayList(ACTORCONTAINER_INITIAL_SIZE) + //val actorContainerInactive = ArrayList(ACTORCONTAINER_INITIAL_SIZE) + val particlesContainer = CircularArray(PARTICLES_MAX) + val uiContainer = ArrayList() + + private val actorsRenderBehind = ArrayList(ACTORCONTAINER_INITIAL_SIZE) + private val actorsRenderMiddle = ArrayList(ACTORCONTAINER_INITIAL_SIZE) + private val actorsRenderMidTop = ArrayList(ACTORCONTAINER_INITIAL_SIZE) + private val actorsRenderFront = ArrayList(ACTORCONTAINER_INITIAL_SIZE) + + lateinit var playableActorDelegate: PlayableActorDelegate // player must exist; use dummy player if there is none (required for camera) + private set + inline val player: ActorHumanoid // currently POSSESSED actor :) + get() = playableActorDelegate.actor + + //var screenZoom = 1.0f // definition moved to IngameInstance + //val ZOOM_MAXIMUM = 4.0f // definition moved to IngameInstance + //val ZOOM_MINIMUM = 0.5f // definition moved to IngameInstance + + companion object { + val lightmapDownsample = 4f //2f: still has choppy look when the camera moves but unnoticeable when blurred + + + /** Sets camera position so that (0,0) would be top-left of the screen, (width, height) be bottom-right. */ + fun setCameraPosition(batch: SpriteBatch, camera: Camera, newX: Float, newY: Float) { + camera.position.set((-newX + Terrarum.HALFW).round(), (-newY + Terrarum.HALFH).round(), 0f) + camera.update() + batch.projectionMatrix = camera.combined + } + + + + /** + * Usage: + * + * override var referenceID: Int = generateUniqueReferenceID() + */ + fun generateUniqueReferenceID(renderOrder: Actor.RenderOrder): ActorID { + fun hasCollision(value: ActorID) = + try { + Terrarum.ingame!!.theGameHasActor(value) || + value < ItemCodex.ACTORID_MIN || + value !in when (renderOrder) { + Actor.RenderOrder.BEHIND -> Actor.RANGE_BEHIND + Actor.RenderOrder.MIDDLE -> Actor.RANGE_MIDDLE + Actor.RenderOrder.MIDTOP -> Actor.RANGE_MIDTOP + Actor.RenderOrder.FRONT -> Actor.RANGE_FRONT + } + } + catch (gameNotInitialisedException: KotlinNullPointerException) { + false + } + + var ret: Int + do { + ret = HQRNG().nextInt().and(0x7FFFFFFF) // set new ID + } while (hasCollision(ret)) // check for collision + return ret + } + } + + + private val worldFBOformat = if (Terrarum.environment == RunningEnvironment.MOBILE) Pixmap.Format.RGBA4444 else Pixmap.Format.RGBA8888 + private val lightFBOformat = Pixmap.Format.RGB888 + + var worldDrawFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + var worldGlowFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + var worldBlendFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + // RGB elements of Lightmap for Color Vec4(R, G, B, 1.0) 24-bit + private lateinit var lightmapFboA: FrameBuffer + private lateinit var lightmapFboB: FrameBuffer + + + init { + } + + + + private val useShader: Boolean = false + private val shaderProgram = 0 + + val KEY_LIGHTMAP_RENDER = Input.Keys.F7 + + + + lateinit var debugWindow: UICanvas + lateinit var notifier: UICanvas + + lateinit var uiPieMenu: UICanvas + lateinit var uiQuickBar: UICanvas + lateinit var uiInventoryPlayer: UICanvas + lateinit var uiInventoryContainer: UICanvas + lateinit var uiVitalPrimary: UICanvas + lateinit var uiVitalSecondary: UICanvas + lateinit var uiVitalItem: UICanvas // itemcount/durability of held block or active ammo of held gun. As for the block, max value is 500. + + private lateinit var uiWatchBasic: UICanvas + private lateinit var uiWatchTierOne: UICanvas + + private lateinit var uiTooltip: UITooltip + + lateinit var uiCheatMotherfuckerNootNoot: UICheatDetected + + // UI aliases + lateinit var uiAliases: ArrayList + private set + lateinit var uiAlasesPausing: ArrayList + private set + + inline val paused: Boolean + get() = uiAlasesPausing.map { if (it.isOpened) return true else 0 }.isEmpty() // isEmply is always false, which we want + /** + * Set to false if UI is opened; set to true if UI is closed. + */ + inline val canPlayerControl: Boolean + get() = !paused // FIXME temporary behab (block movement if the game is paused or paused by UIs) + + var particlesActive = 0 + private set + + + + private lateinit var ingameUpdateThread: ThreadIngameUpdate + private lateinit var updateThreadWrapper: Thread + //private val ingameDrawThread: ThreadIngameDraw // draw must be on the main thread + + + var gameInitialised = false + private set + var gameFullyLoaded = false + private set + + + private val TILE_SIZEF = FeaturesDrawer.TILE_SIZE.toFloat() + + ////////////// + // GDX code // + ////////////// + + var camera = OrthographicCamera(Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat()) + + + // invert Y + fun initViewPort(width: Int, height: Int) { + // Set Y to point downwards + camera.setToOrtho(true, width.toFloat(), height.toFloat()) + + // Update camera matrix + camera.update() + + // Set viewport to restrict drawing + Gdx.gl20.glViewport(0, 0, width, height) + } + + + lateinit var gameLoadMode: GameLoadMode + lateinit var gameLoadInfoPayload: Any + + enum class GameLoadMode { + CREATE_NEW, LOAD_FROM + } + + override fun show() { + //initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT) + + // gameLoadMode and gameLoadInfoPayload must be set beforehand!! + + when (gameLoadMode) { + GameLoadMode.CREATE_NEW -> enter(gameLoadInfoPayload as NewWorldParameters) + GameLoadMode.LOAD_FROM -> enter(gameLoadInfoPayload as GameSaveData) + } + + LightmapRenderer.world = this.world + //BlocksDrawer.world = this.world + FeaturesDrawer.world = this.world + + gameInitialised = true + } + + data class GameSaveData( + val world: GameWorld, + val historicalFigureIDBucket: ArrayList, + val realGamePlayer: ActorHumanoid + ) + + data class NewWorldParameters( + val width: Int, + val height: Int, + val worldGenSeed: Long + // other worldgen options + ) + + /** + * Init instance by loading saved world + */ + private fun enter(gameSaveData: GameSaveData) { + if (gameInitialised) { + println("[Ingame] loaded successfully.") + } + else { + LoadScreen.addMessage("Loading world from save") + + + world = gameSaveData.world + historicalFigureIDBucket = gameSaveData.historicalFigureIDBucket + playableActorDelegate = PlayableActorDelegate(gameSaveData.realGamePlayer) + addNewActor(player) + + + + //initGame() + } + } + + /** + * Init instance by creating new world + */ + private fun enter(worldParams: NewWorldParameters) { + if (gameInitialised) { + println("[Ingame] loaded successfully.") + } + else { + LoadScreen.addMessage("${Terrarum.NAME} version ${AppLoader.getVERSION_STRING()}") + LoadScreen.addMessage("Creating new world") + + + // init map as chosen size + world = GameWorld(worldParams.width, worldParams.height) + + // generate terrain for the map + WorldGenerator.attachMap(world) + WorldGenerator.SEED = worldParams.worldGenSeed + WorldGenerator.generateMap() + + + historicalFigureIDBucket = ArrayList() + + + RoguelikeRandomiser.seed = HQRNG().nextLong() + + + // add new player and put it to actorContainer + //playableActorDelegate = PlayableActorDelegate(PlayerBuilderSigrid()) + //playableActorDelegate = PlayableActorDelegate(PlayerBuilderTestSubject1()) + //addNewActor(player) + + + // test actor + //addNewActor(PlayerBuilderCynthia()) + + + //initGame() + } + } + + private val ingameController = IngameController(this) + + /** Load rest of the game with GL context */ + fun postInit() { + //LightmapRenderer.world = this.world + BlocksDrawer.world = this.world + //FeaturesDrawer.world = this.world + + + MegaRainGovernor // invoke MegaRain Governor + + + + Gdx.input.inputProcessor = ingameController + + + initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT) + + + // init console window + consoleHandler = ConsoleWindow() + consoleHandler.setPosition(0, 0) + + + // init debug window + debugWindow = BasicDebugInfoWindow() + debugWindow.setPosition(0, 0) + + // init notifier + notifier = Notification() + notifier.setPosition( + (Terrarum.WIDTH - notifier.width) / 2, Terrarum.HEIGHT - notifier.height) + + + + + // >- queue up game UIs that should pause the world -< + // inventory + /*uiInventoryPlayer = UIInventory(player, + width = 900, + height = Terrarum.HEIGHT - 160, + categoryWidth = 210, + toggleKeyLiteral = Terrarum.getConfigInt("keyinventory") + )*/ + /*uiInventoryPlayer.setPosition( + -uiInventoryPlayer.width, + 70 + )*/ + uiInventoryPlayer = UIInventoryFull(player, + toggleKeyLiteral = Terrarum.getConfigInt("keyinventory") + ) + uiInventoryPlayer.setPosition(0, 0) + + // >- lesser UIs -< + // quick bar + uiQuickBar = UIQuickBar() + uiQuickBar.isVisible = true + uiQuickBar.setPosition((Terrarum.WIDTH - uiQuickBar.width) / 2 + 12, -10) + + // pie menu + uiPieMenu = UIPieMenu() + uiPieMenu.setPosition(Terrarum.HALFW, Terrarum.HALFH) + + // vital metre + // fill in getter functions by + // (uiAliases[UI_QUICK_BAR]!!.UI as UIVitalMetre).vitalGetterMax = { some_function } + //uiVitalPrimary = UIVitalMetre(player, { 80f }, { 100f }, Color.red, 2, customPositioning = true) + //uiVitalPrimary.setAsAlwaysVisible() + //uiVitalSecondary = UIVitalMetre(player, { 73f }, { 100f }, Color(0x00dfff), 1) customPositioning = true) + //uiVitalSecondary.setAsAlwaysVisible() + //uiVitalItem = UIVitalMetre(player, { null }, { null }, Color(0xffcc00), 0, customPositioning = true) + //uiVitalItem.setAsAlwaysVisible() + + // basic watch-style notification bar (temperature, new mail) + uiWatchBasic = UIBasicNotifier(player) + uiWatchBasic.setAsAlwaysVisible() + uiWatchBasic.setPosition(Terrarum.WIDTH - uiWatchBasic.width, 0) + + uiWatchTierOne = UITierOneWatch(player) + uiWatchTierOne.setAsAlwaysVisible() + uiWatchTierOne.setPosition(Terrarum.WIDTH - uiWatchTierOne.width, uiWatchBasic.height - 2) + + + uiTooltip = UITooltip() + + + uiCheatMotherfuckerNootNoot = UICheatDetected() + + + // batch-process uiAliases + uiAliases = arrayListOf( + // drawn first + //uiVitalPrimary, + //uiVitalSecondary, + //uiVitalItem, + + uiPieMenu, + uiQuickBar, + uiWatchBasic, + uiWatchTierOne, + uiTooltip + // drawn last + ) + uiAlasesPausing = arrayListOf( + uiInventoryPlayer, + //uiInventoryContainer, + consoleHandler, + uiCheatMotherfuckerNootNoot + ) + uiAlasesPausing.forEach { addUI(it) } // put them all to the UIContainer + uiAliases.forEach { addUI(it) } // put them all to the UIContainer + + + + ingameUpdateThread = ThreadIngameUpdate(this) + updateThreadWrapper = Thread(ingameUpdateThread, "Terrarum UpdateThread") + + + + LightmapRenderer.fireRecalculateEvent() + + + + + + // some sketchy test code here + + + + }// END enter + + + protected var updateDeltaCounter = 0.0 + protected val updateRate = 1.0 / Terrarum.TARGET_INTERNAL_FPS + + private var firstTimeRun = true + + /////////////// + // prod code // + /////////////// + private class ThreadIngameUpdate(val ingame: Ingame): Runnable { + override fun run() { + var updateTries = 0 + while (ingame.updateDeltaCounter >= ingame.updateRate) { + ingame.updateGame(Terrarum.deltaTime) + ingame.updateDeltaCounter -= ingame.updateRate + updateTries++ + + if (updateTries >= Terrarum.UPDATE_CATCHUP_MAX_TRIES) { + break + } + } + } + } + + override fun render(delta: Float) { + // Q&D solution for LoadScreen and Ingame, where while LoadScreen is working, Ingame now no longer has GL Context + // there's still things to load which needs GL context to be present + if (!gameFullyLoaded) { + + if (gameLoadMode == GameLoadMode.CREATE_NEW) { + playableActorDelegate = PlayableActorDelegate(PlayerBuilderSigrid()) + + // go to spawn position + player.setPosition( + world.spawnX * FeaturesDrawer.TILE_SIZE.toDouble(), + world.spawnY * FeaturesDrawer.TILE_SIZE.toDouble() + ) + + addNewActor(player) + } + + postInit() + + + gameFullyLoaded = true + } + + + + + + Gdx.graphics.setTitle(AppLoader.GAME_NAME + + " — F: ${Gdx.graphics.framesPerSecond} (${Terrarum.TARGET_INTERNAL_FPS})" + + " — M: ${Terrarum.memInUse}M / ${Terrarum.memTotal}M / ${Terrarum.memXmx}M" + ) + + // ASYNCHRONOUS UPDATE AND RENDER // + + + /** UPDATE CODE GOES HERE */ + updateDeltaCounter += delta + + + + if (false && Terrarum.getConfigBoolean("multithread")) { // NO MULTITHREADING: camera don't like concurrent modification (jittery actor movements) + if (firstTimeRun || updateThreadWrapper.state == Thread.State.TERMINATED) { + updateThreadWrapper = Thread(ingameUpdateThread, "Terrarum UpdateThread") + updateThreadWrapper.start() + + if (firstTimeRun) firstTimeRun = false + } + // else, NOP; + } + else { + var updateTries = 0 + while (updateDeltaCounter >= updateRate) { + + //updateGame(delta) + measureRuntime({ updateGame(delta) }, prependMsg = "Update Game: ") + + updateDeltaCounter -= updateRate + updateTries++ + + if (updateTries >= Terrarum.UPDATE_CATCHUP_MAX_TRIES) { + break + } + } + } + + + + /** RENDER CODE GOES HERE */ + //renderGame(batch) + measureRuntime({ renderGame(batch) }, prependMsg = "Render Game: ") + } + + private fun measureRuntime(function: (() -> Unit), out: PrintStream = System.err, prependMsg: String = "", appendMsg: String = "") { + val startTime = System.nanoTime() + function.invoke() + val endTime = System.nanoTime() + println("$prependMsg${endTime - startTime} ns$appendMsg") + } + + protected fun updateGame(delta: Float) { + particlesActive = 0 + + + KeyToggler.update() + ingameController.update(delta) + + + if (!paused) { + + /////////////////////////// + // world-related updates // + /////////////////////////// + BlockPropUtil.dynamicLumFuncTickClock() + world.updateWorldTime(delta) + //WorldSimulator(player, delta) + WeatherMixer.update(delta, player) + BlockStats.update() + if (!(CommandDict["setgl"] as SetGlobalLightOverride).lightOverride) + world.globalLight = WeatherMixer.globalLightNow + + + //////////////////////////// + // camera-related updates // + //////////////////////////// + FeaturesDrawer.update(delta) + WorldCamera.update(world, player) + + + + /////////////////////////// + // actor-related updates // + /////////////////////////// + repossessActor() + + // determine whether the inactive actor should be activated + wakeDormantActors() + // determine whether the actor should keep being activated or be dormant + KillOrKnockdownActors() + updateActors(delta) + particlesContainer.forEach { if (!it.flagDespawn) particlesActive++; it.update(delta) } + // TODO thread pool(?) + CollisionSolver.process() + } + + + //////////////////////// + // ui-related updates // + //////////////////////// + uiContainer.forEach { it.update(delta) } + debugWindow.update(delta) + notifier.update(delta) + + // update debuggers using javax.swing // + if (Authenticator.b()) { + AVTracker.update() + ActorsList.update() + } + } + + + private fun renderGame(batch: SpriteBatch) { + Gdx.gl.glClearColor(.094f, .094f, .094f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) + Gdx.gl.glEnable(GL20.GL_BLEND) + Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA) + + //camera.position.set(-WorldCamera.x.toFloat(), -WorldCamera.y.toFloat(), 0f) // make camara work + //camera.position.set(0f, 0f, 0f) // make camara work + //batch.projectionMatrix = camera.combined + + + + LightmapRenderer.fireRecalculateEvent() + + + + worldBlendFrameBuffer.inAction(null, null) { + Gdx.gl.glClearColor(0f,0f,0f,0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) + Gdx.gl.glEnable(GL20.GL_BLEND) + Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA) + } + worldDrawFrameBuffer.inAction(null, null) { + Gdx.gl.glClearColor(0f,0f,0f,0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) + Gdx.gl.glEnable(GL20.GL_BLEND) + Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA) + } + worldGlowFrameBuffer.inAction(null, null) { + Gdx.gl.glClearColor(0f,0f,0f,1f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + Gdx.gl.glEnable(GL20.GL_TEXTURE_2D) + Gdx.gl.glEnable(GL20.GL_BLEND) + Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA) + } + + + fun moveCameraToWorldCoord() { + // using custom code for camera; this is obscure and tricky + camera.position.set(WorldCamera.gdxCamX, WorldCamera.gdxCamY, 0f) // make camara work + camera.update() + batch.projectionMatrix = camera.combined + } + + + /////////////////////////// + // draw world to the FBO // + /////////////////////////// + processBlur(lightmapFboA, lightmapFboB, LightmapRenderer.DRAW_FOR_RGB) + + worldDrawFrameBuffer.inAction(camera, batch) { + + + // draw-with-poly doesn't want to co-op with peasant spriteBatch... (it hides sprites) + + batch.inUse { + batch.shader = null + batch.color = Color.WHITE + blendNormal() + } + + + + setCameraPosition(0f, 0f) + BlocksDrawer.renderWall(batch) + + + + batch.inUse { + moveCameraToWorldCoord() + actorsRenderBehind.forEach { it.drawBody(batch) } + particlesContainer.forEach { it.drawBody(batch) } + + } + + + + setCameraPosition(0f, 0f) + BlocksDrawer.renderTerrain(batch) + + + + batch.inUse { + ///////////////// + // draw actors // + ///////////////// + moveCameraToWorldCoord() + actorsRenderMiddle.forEach { it.drawBody(batch) } + actorsRenderMidTop.forEach { it.drawBody(batch) } + player.drawBody(batch) + actorsRenderFront.forEach { it.drawBody(batch) } + // --> Change of blend mode <-- introduced by children of ActorWithBody // + + + ///////////////////////////// + // draw map related stuffs // + ///////////////////////////// + } + + + + setCameraPosition(0f, 0f) + BlocksDrawer.renderFront(batch, false) + + + + batch.inUse { + // --> blendNormal() <-- by BlocksDrawer.renderFront + FeaturesDrawer.drawEnvOverlay(batch) + + + + + // mix lighpmap canvas to this canvas (Colors -- RGB channel) + if (!KeyToggler.isOn(Input.Keys.F6)) { // F6 to disable lightmap draw + setCameraPosition(0f, 0f) + batch.shader = Terrarum.shaderBayer + batch.shader.setUniformf("rcount", 64f) + batch.shader.setUniformf("gcount", 64f) + batch.shader.setUniformf("bcount", 64f) // de-banding + + val lightTex = lightmapFboB.colorBufferTexture // A or B? flipped in Y means you chose wrong buffer; use one that works correctly + lightTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) // blocky feeling for A E S T H E T I C S + + if (KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) blendNormal() + else blendMul() + + batch.color = Color.WHITE + val xrem = -(WorldCamera.x.toFloat() fmod TILE_SIZEF) + val yrem = -(WorldCamera.y.toFloat() fmod TILE_SIZEF) + batch.draw(lightTex, + xrem, + yrem, + lightTex.width * lightmapDownsample, lightTex.height * lightmapDownsample + //lightTex.width.toFloat(), lightTex.height.toFloat() // for debugging + ) + + } + + + Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // don't know why it is REALLY needed; it really depresses me + batch.shader = null + + + // move camera back to its former position + // using custom code for camera; this is obscure and tricky + camera.position.set(WorldCamera.gdxCamX, WorldCamera.gdxCamY, 0f) // make camara work + camera.update() + batch.projectionMatrix = camera.combined + + } + } + + + ////////////////////////// + // draw glow to the FBO // + ////////////////////////// + processBlur(lightmapFboA, lightmapFboB, LightmapRenderer.DRAW_FOR_ALPHA) + + worldGlowFrameBuffer.inAction(camera, batch) { + batch.inUse { + batch.shader = null + + + batch.color = Color.WHITE + blendNormal() + + + + ////////////////////// + // draw actor glows // + ////////////////////// + moveCameraToWorldCoord() + actorsRenderBehind.forEach { it.drawGlow(batch) } + particlesContainer.forEach { it.drawGlow(batch) } + actorsRenderMiddle.forEach { it.drawGlow(batch) } + actorsRenderMidTop.forEach { it.drawGlow(batch) } + player.drawGlow(batch) + actorsRenderFront.forEach { it.drawGlow(batch) } + // --> blendNormal() <-- introduced by childs of ActorWithBody // + + + + // mix lighpmap canvas to this canvas (UV lights -- A channel written on RGB as greyscale image) + if (!KeyToggler.isOn(Input.Keys.F6)) { // F6 to disable lightmap draw + setCameraPosition(0f, 0f) + batch.shader = Terrarum.shaderBayer + batch.shader.setUniformf("rcount", 64f) + batch.shader.setUniformf("gcount", 64f) + batch.shader.setUniformf("bcount", 64f) // de-banding + + val lightTex = lightmapFboB.colorBufferTexture // A or B? flipped in Y means you chose wrong buffer; use one that works correctly + lightTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) // blocky feeling for A E S T H E T I C S + + if (KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) blendNormal() + else blendMul() + + batch.color = Color.WHITE + val xrem = -(WorldCamera.x.toFloat() fmod TILE_SIZEF) + val yrem = -(WorldCamera.y.toFloat() fmod TILE_SIZEF) + batch.draw(lightTex, + xrem, + yrem, + lightTex.width * lightmapDownsample, lightTex.height * lightmapDownsample + //lightTex.width.toFloat(), lightTex.height.toFloat() // for debugging + ) + + } + + + blendNormal() + } + } + + + worldBlendFrameBuffer.inAction(camera, batch) { + Gdx.gl.glClearColor(0f, 0f, 0f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + + + // draw blended world + val worldTex = worldDrawFrameBuffer.colorBufferTexture // WORLD: light_color must be applied beforehand + val glowTex = worldGlowFrameBuffer.colorBufferTexture // GLOW: light_uvlight must be applied beforehand + + worldTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + glowTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + + worldTex.bind(0) + glowTex.bind(1) + + + Terrarum.shaderBlendGlow.begin() + Terrarum.shaderBlendGlow.setUniformMatrix("u_projTrans", camera.combined) + Terrarum.shaderBlendGlow.setUniformi("u_texture", 0) + Terrarum.shaderBlendGlow.setUniformi("tex1", 1) + Terrarum.fullscreenQuad.render(Terrarum.shaderBlendGlow, GL20.GL_TRIANGLES) + Terrarum.shaderBlendGlow.end() + + + + Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // don't know why it is REALLY needed; it really depresses me + + + batch.inUse { + batch.color = Color.WHITE + blendNormal() + + batch.shader = null + } + } + + + ///////////////////////// + // draw to main screen // + ///////////////////////// + camera.setToOrtho(true, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat()) + batch.projectionMatrix = camera.combined + batch.inUse { + + batch.shader = null + + setCameraPosition(0f, 0f) + batch.color = Color.WHITE + blendNormal() + + + /////////////////////////// + // draw skybox to screen // + /////////////////////////// + + WeatherMixer.render(camera, world) + + + + + ///////////////////////////////// + // draw framebuffers to screen // + ///////////////////////////////// + + + + val blendedTex = worldBlendFrameBuffer.colorBufferTexture + blendedTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + batch.color = Color.WHITE + batch.shader = null + blendNormal() + batch.draw(blendedTex, 0f, 0f, blendedTex.width.toFloat(), blendedTex.height.toFloat()) + + + + + + // an old code. + /*batch.shader = null + val worldTex = worldDrawFrameBuffer.colorBufferTexture // WORLD: light_color must be applied beforehand + val glowTex = worldGlowFrameBuffer.colorBufferTexture // GLOW: light_uvlight must be applied beforehand + + + batch.draw(worldTex, 0f, 0f, worldTex.width.toFloat(), worldTex.height.toFloat())*/ + + + + batch.shader = null + + //////////////////////// + // debug informations // + //////////////////////// + + blendNormal() + // draw reference ID if debugWindow is open + if (debugWindow.isVisible) { + + actorContainer.forEachIndexed { i, actor -> + if (actor is ActorWithBody) { + batch.color = Color.WHITE + Terrarum.fontSmallNumbers.draw(batch, + actor.referenceID.toString(), + actor.hitbox.startX.toFloat(), + actor.hitbox.canonicalY.toFloat() + 4 + ) + } + } + } + // debug physics + if (KeyToggler.isOn(Input.Keys.F11)) { + actorContainer.forEachIndexed { i, actor -> + if (actor is ActorWithPhysics) { + /*shapeRenderer.inUse(ShapeRenderer.ShapeType.Line) { + shapeRenderer.color = Color(1f, 0f, 1f, 1f) + //shapeRenderer.lineWidth = 1f + shapeRenderer.rect( + actor.hitbox.startX.toFloat(), + actor.hitbox.startY.toFloat(), + actor.hitbox.width.toFloat(), + actor.hitbox.height.toFloat() + ) + }*/ + + // velocity + batch.color = Color.CHARTREUSE//GameFontBase.codeToCol["g"] + Terrarum.fontSmallNumbers.draw(batch, + "${0x7F.toChar()}X ${actor.externalForce.x}", + actor.hitbox.startX.toFloat(), + actor.hitbox.canonicalY.toFloat() + 4 + 8 + ) + Terrarum.fontSmallNumbers.draw(batch, + "${0x7F.toChar()}Y ${actor.externalForce.y}", + actor.hitbox.startX.toFloat(), + actor.hitbox.canonicalY.toFloat() + 4 + 8 * 2 + ) + } + } + } + // fluidmap debug + if (KeyToggler.isOn(Input.Keys.F4)) { + WorldSimulator.drawFluidMapDebug(batch) + } + + + + + ///////////////////////////// + // draw some overlays (UI) // + ///////////////////////////// + + uiContainer.forEach { + if (it != consoleHandler) { + batch.color = Color.WHITE + it.render(batch, camera) + } + } + + debugWindow.render(batch, camera) + // make sure console draws on top of other UIs + consoleHandler.render(batch, camera) + notifier.render(batch, camera) + + + blendNormal() + } + } + + fun processBlur(lightmapFboA: FrameBuffer, lightmapFboB: FrameBuffer, mode: Int) { + val blurIterations = 5 // ideally, 4 * radius; must be even/odd number -- odd/even number will flip the image + val blurRadius = 4f / lightmapDownsample // (5, 4f); using low numbers for pixel-y aesthetics + + var blurWriteBuffer = lightmapFboA + var blurReadBuffer = lightmapFboB + + + lightmapFboA.inAction(null, null) { + Gdx.gl.glClearColor(0f, 0f, 0f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + } + lightmapFboB.inAction(null, null) { + Gdx.gl.glClearColor(0f, 0f, 0f, 0f) + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + } + + + if (mode == LightmapRenderer.DRAW_FOR_RGB) { + // initialise readBuffer with untreated lightmap + blurReadBuffer.inAction(camera, batch) { + batch.inUse { + blendNormal(batch) + batch.color = Color.WHITE + LightmapRenderer.draw(batch, LightmapRenderer.DRAW_FOR_RGB) + } + } + } + else { + // initialise readBuffer with untreated lightmap + blurReadBuffer.inAction(camera, batch) { + batch.inUse { + blendNormal(batch) + batch.color = Color.WHITE + LightmapRenderer.draw(batch, LightmapRenderer.DRAW_FOR_ALPHA) + } + } + } + + + + if (!KeyToggler.isOn(Input.Keys.F8)) { + for (i in 0 until blurIterations) { + blurWriteBuffer.inAction(camera, batch) { + + batch.inUse { + val texture = blurReadBuffer.colorBufferTexture + + texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) + + + batch.shader = Terrarum.shaderBlur + batch.shader.setUniformf("iResolution", + blurWriteBuffer.width.toFloat(), blurWriteBuffer.height.toFloat()) + batch.shader.setUniformf("flip", 1f) + if (i % 2 == 0) + batch.shader.setUniformf("direction", blurRadius, 0f) + else + batch.shader.setUniformf("direction", 0f, blurRadius) + + + batch.color = Color.WHITE + batch.draw(texture, 0f, 0f) + + + // swap + val t = blurWriteBuffer + blurWriteBuffer = blurReadBuffer + blurReadBuffer = t + } + } + } + } + else { + blurWriteBuffer = blurReadBuffer + } + } + + + + private fun repossessActor() { + // check if currently pocessed actor is removed from game + if (!theGameHasActor(player)) { + // re-possess canonical player + if (theGameHasActor(Player.PLAYER_REF_ID)) + changePossession(Player.PLAYER_REF_ID) + else + changePossession(0x51621D) // FIXME fallback debug mode (FIXME is there for a reminder visible in ya IDE) + } + } + + private fun changePossession(newActor: PlayableActorDelegate) { + if (!theGameHasActor(player)) { + throw IllegalArgumentException("No such actor in the game: $newActor") + } + + playableActorDelegate = newActor + WorldSimulator(player, Terrarum.deltaTime) + } + + private fun changePossession(refid: Int) { + // TODO prevent possessing other player on multiplayer + + if (!theGameHasActor(refid)) { + throw IllegalArgumentException( + "No such actor in the game: $refid (elemsActive: ${actorContainer.size}, " + + "elemsInactive: ${actorContainerInactive.size})") + } + + // take care of old delegate + playableActorDelegate!!.actor.collisionType = HumanoidNPC.DEFAULT_COLLISION_TYPE + // accept new delegate + playableActorDelegate = PlayableActorDelegate(getActorByID(refid) as ActorHumanoid) + playableActorDelegate!!.actor.collisionType = ActorWithPhysics.COLLISION_KINEMATIC + WorldSimulator(player, Terrarum.deltaTime) + } + + /** Send message to notifier UI and toggle the UI as opened. */ + fun sendNotification(msg: Array) { + (notifier as Notification).sendNotification(msg) + } + + fun wakeDormantActors() { + var actorContainerSize = actorContainerInactive.size + var i = 0 + while (i < actorContainerSize) { // loop through actorContainerInactive + val actor = actorContainerInactive[i] + if (actor is ActorWithBody && actor.inUpdateRange()) { + activateDormantActor(actor) // duplicates are checked here + actorContainerSize -= 1 + i-- // array removed 1 elem, so we also decrement counter by 1 + } + i++ + } + } + + /** + * determine whether the actor should be active or dormant by its distance from the player. + * If the actor must be dormant, the target actor will be put to the list specifically for them. + * if the actor is not to be dormant, it will be just ignored. + */ + fun KillOrKnockdownActors() { + var actorContainerSize = actorContainer.size + var i = 0 + while (i < actorContainerSize) { // loop through actorContainer + val actor = actorContainer[i] + val actorIndex = i + // kill actors flagged to despawn + if (actor.flagDespawn) { + removeActor(actor) + actorContainerSize -= 1 + i-- // array removed 1 elem, so we also decrement counter by 1 + } + // inactivate distant actors + else if (actor is ActorWithBody && !actor.inUpdateRange()) { + if (actor !is Projectile) { // if it's a projectile, don't inactivate it; just kill it. + actorContainerInactive.add(actor) // naïve add; duplicates are checked when the actor is re-activated + } + actorContainer.removeAt(actorIndex) + actorContainerSize -= 1 + i-- // array removed 1 elem, so we also decrement counter by 1 + } + i++ + } + } + + /** + * Update actors concurrently. + * + * NOTE: concurrency for actor updating is currently disabled because of it's poor performance + */ + fun updateActors(delta: Float) { + if (false) { // don't multithread this for now, it's SLOWER //if (Terrarum.MULTITHREAD && actorContainer.size > Terrarum.THREADS) { + val actors = actorContainer.size.toFloat() + // set up indices + for (i in 0..Terrarum.THREADS - 1) { + ThreadParallel.map( + i, + ThreadActorUpdate( + actors.div(Terrarum.THREADS).times(i).roundInt(), + actors.div(Terrarum.THREADS).times(i.plus(1)).roundInt() - 1 + ), + "ActorUpdate" + ) + } + + ThreadParallel.startAll() + + playableActorDelegate?.update(delta) + } + else { + actorContainer.forEach { + if (it != playableActorDelegate?.actor) { + it.update(delta) + + if (it is Pocketed) { + it.inventory.forEach { inventoryEntry -> + inventoryEntry.item.effectWhileInPocket(delta) + if (it.equipped(inventoryEntry.item)) { + inventoryEntry.item.effectWhenEquipped(delta) + } + } + } + } + } + playableActorDelegate?.update(delta) + //AmmoMeterProxy(player, uiVitalItem.UI as UIVitalMetre) + } + } + + fun Double.sqr() = this * this + fun Int.sqr() = this * this + fun min(vararg d: Double): Double { + var ret = Double.MAX_VALUE + d.forEach { if (it < ret) ret = it } + return ret + } + private fun distToActorSqr(a: ActorWithBody, p: ActorWithBody) = + min(// take min of normal position and wrapped (x < 0) position + (a.hitbox.centeredX - p.hitbox.centeredX).sqr() + + (a.hitbox.centeredY - p.hitbox.centeredY).sqr(), + (a.hitbox.centeredX - p.hitbox.centeredX + world.width * FeaturesDrawer.TILE_SIZE).sqr() + + (a.hitbox.centeredY - p.hitbox.centeredY).sqr(), + (a.hitbox.centeredX - p.hitbox.centeredX - world.width * FeaturesDrawer.TILE_SIZE).sqr() + + (a.hitbox.centeredY - p.hitbox.centeredY).sqr() + ) + private fun distToCameraSqr(a: ActorWithBody) = + min( + (a.hitbox.startX - WorldCamera.x).sqr() + + (a.hitbox.startY - WorldCamera.y).sqr(), + (a.hitbox.startX - WorldCamera.x + world.width * FeaturesDrawer.TILE_SIZE).sqr() + + (a.hitbox.startY - WorldCamera.y).sqr(), + (a.hitbox.startX - WorldCamera.x - world.width * FeaturesDrawer.TILE_SIZE).sqr() + + (a.hitbox.startY - WorldCamera.y).sqr() + ) + + /** whether the actor is within screen */ + private fun ActorWithBody.inScreen() = + distToCameraSqr(this) <= + (Terrarum.WIDTH.plus(this.hitbox.width.div(2)). + times(1 / screenZoom).sqr() + + Terrarum.HEIGHT.plus(this.hitbox.height.div(2)). + times(1 / screenZoom).sqr()) + + + /** whether the actor is within update range */ + private fun ActorWithBody.inUpdateRange() = distToCameraSqr(this) <= ACTOR_UPDATE_RANGE.sqr() + + override fun removeActor(ID: Int) = removeActor(getActorByID(ID)) + /** + * get index of the actor and delete by the index. + * we can do this as the list is guaranteed to be sorted + * and only contains unique values. + * + * Any values behind the index will be automatically pushed to front. + * This is how remove function of [java.util.ArrayList] is defined. + */ + override fun removeActor(actor: Actor) { + if (actor.referenceID == player.referenceID || actor.referenceID == 0x51621D) // do not delete this magic + throw RuntimeException("Attempted to remove player.") + val indexToDelete = actorContainer.binarySearch(actor.referenceID!!) + if (indexToDelete >= 0) { + actorContainer.removeAt(indexToDelete) + + // indexToDelete >= 0 means that the actor certainly exists in the game + // which means we don't need to check if i >= 0 again + if (actor is ActorWithBody) { + when (actor.renderOrder) { + Actor.RenderOrder.BEHIND -> { + val i = actorsRenderBehind.binarySearch(actor.referenceID!!) + actorsRenderBehind.removeAt(i) + } + Actor.RenderOrder.MIDDLE -> { + val i = actorsRenderMiddle.binarySearch(actor.referenceID!!) + actorsRenderMiddle.removeAt(i) + } + Actor.RenderOrder.MIDTOP -> { + val i = actorsRenderMidTop.binarySearch(actor.referenceID!!) + actorsRenderMidTop.removeAt(i) + } + Actor.RenderOrder.FRONT -> { + val i = actorsRenderFront.binarySearch(actor.referenceID!!) + actorsRenderFront.removeAt(i) + } + } + } + } + } + + /** + * Check for duplicates, append actor and sort the list + */ + override fun addNewActor(actor: Actor) { + if (theGameHasActor(actor.referenceID!!)) { + throw Error("The actor $actor already exists in the game") + } + else { + actorContainer.add(actor) + insertionSortLastElem(actorContainer) // we can do this as we are only adding single actor + + if (actor is ActorWithBody) { + when (actor.renderOrder) { + Actor.RenderOrder.BEHIND -> { + actorsRenderBehind.add(actor); insertionSortLastElemAV(actorsRenderBehind) + } + Actor.RenderOrder.MIDDLE -> { + actorsRenderMiddle.add(actor); insertionSortLastElemAV(actorsRenderMiddle) + } + Actor.RenderOrder.MIDTOP -> { + actorsRenderMidTop.add(actor); insertionSortLastElemAV(actorsRenderMidTop) + } + Actor.RenderOrder.FRONT -> { + actorsRenderFront.add(actor); insertionSortLastElemAV(actorsRenderFront) + } + } + } + } + } + + fun activateDormantActor(actor: Actor) { + if (!isInactive(actor.referenceID!!)) { + if (isActive(actor.referenceID!!)) + throw Error("The actor $actor is already activated") + else + throw Error("The actor $actor already exists in the game") + } + else { + actorContainerInactive.remove(actor) + actorContainer.add(actor) + insertionSortLastElem(actorContainer) // we can do this as we are only adding single actor + + if (actor is ActorWithBody) { + when (actor.renderOrder) { + Actor.RenderOrder.BEHIND -> { + actorsRenderBehind.add(actor); insertionSortLastElemAV(actorsRenderBehind) + } + Actor.RenderOrder.MIDDLE -> { + actorsRenderMiddle.add(actor); insertionSortLastElemAV(actorsRenderMiddle) + } + Actor.RenderOrder.MIDTOP -> { + actorsRenderMidTop.add(actor); insertionSortLastElemAV(actorsRenderMidTop) + } + Actor.RenderOrder.FRONT -> { + actorsRenderFront.add(actor); insertionSortLastElemAV(actorsRenderFront) + } + } + } + } + } + + fun addParticle(particle: ParticleBase) { + particlesContainer.add(particle) + } + + fun addUI(ui: UICanvas) { + // check for exact duplicates + if (uiContainer.contains(ui)) { + throw IllegalArgumentException( + "Exact copy of the UI already exists: The instance of ${ui.javaClass.simpleName}" + ) + } + + uiContainer.add(ui) + } + + private fun insertionSortLastElemAV(arr: ArrayList) { // out-projection doesn't work, duh + lock(ReentrantLock()) { + var j = arr.lastIndex - 1 + val x = arr.last() + while (j >= 0 && arr[j] > x) { + arr[j + 1] = arr[j] + j -= 1 + } + arr[j + 1] = x + } + } + + fun setTooltipMessage(message: String?) { + if (message == null) { + uiTooltip.setAsClose() + } + else { + if (uiTooltip.isClosed || uiTooltip.isClosing) { + uiTooltip.setAsOpen() + } + uiTooltip.message = message + } + } + + override fun pause() { + // TODO no pause when off-focus on desktop + } + + override fun resume() { + } + + override fun hide() { + dispose() + } + + + + private var lightmapInitialised = false // to avoid nullability of lightmapFBO + + /** + * @param width same as Terrarum.WIDTH + * @param height same as Terrarum.HEIGHT + * @see net.torvald.terrarum.Terrarum + */ + override fun resize(width: Int, height: Int) { + + BlocksDrawer.resize(Terrarum.WIDTH, Terrarum.HEIGHT) + LightmapRenderer.resize(Terrarum.WIDTH, Terrarum.HEIGHT) + MegaRainGovernor.resize() + + worldDrawFrameBuffer.dispose() + worldDrawFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + worldGlowFrameBuffer.dispose() + worldGlowFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + worldBlendFrameBuffer.dispose() + worldBlendFrameBuffer = FrameBuffer(worldFBOformat, Terrarum.WIDTH, Terrarum.HEIGHT, false) + + if (lightmapInitialised) { + lightmapFboA.dispose() + lightmapFboB.dispose() + } + lightmapFboA = FrameBuffer( + lightFBOformat, + LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt(), + LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(), + false + ) + lightmapFboB = FrameBuffer( + lightFBOformat, + LightmapRenderer.lightBuffer.width * LightmapRenderer.DRAW_TILE_SIZE.toInt(), + LightmapRenderer.lightBuffer.height * LightmapRenderer.DRAW_TILE_SIZE.toInt(), + false + ) + lightmapInitialised = true // are you the first time? + + + // Set up viewport when window is resized + initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT) + + + + if (gameInitialised) { + LightmapRenderer.fireRecalculateEvent() + } + + + if (gameFullyLoaded) { + // resize UIs + + notifier.setPosition( + (Terrarum.WIDTH - notifier.width) / 2, Terrarum.HEIGHT - notifier.height) + + // inventory + /*uiInventoryPlayer = + UIInventory(player, + width = 840, + height = Terrarum.HEIGHT - 160, + categoryWidth = 210 + )*/ + + + // basic watch-style notification bar (temperature, new mail) + uiWatchBasic.setPosition(Terrarum.WIDTH - uiWatchBasic.width, 0) + uiWatchTierOne.setPosition(Terrarum.WIDTH - uiWatchTierOne.width, uiWatchBasic.height - 2) + } + + + println("[Ingame] Resize event") + } + + override fun dispose() { + worldDrawFrameBuffer.dispose() + worldGlowFrameBuffer.dispose() + worldBlendFrameBuffer.dispose() + lightmapFboA.dispose() + lightmapFboB.dispose() + + actorsRenderBehind.forEach { it.dispose() } + actorsRenderMiddle.forEach { it.dispose() } + actorsRenderMidTop.forEach { it.dispose() } + actorsRenderFront.forEach { it.dispose() } + + uiAliases.forEach { it.dispose() } + uiAlasesPausing.forEach { it.dispose() } + + + WatchDotAlph.dispose() + Watch7SegMain.dispose() + WatchDotAlph.dispose() + + ItemSlotImageBuilder.dispose() + + MessageWindow.SEGMENT_BLACK.dispose() + MessageWindow.SEGMENT_WHITE.dispose() + } + + + /** + * WARNING! this function flushes batch; use this sparingly! + * + * Camera will be moved so that (newX, newY) would be sit on the top-left edge. + */ + fun setCameraPosition(newX: Float, newY: Float) { + setCameraPosition(batch, camera, newX, newY) + } + + +} +*/ \ No newline at end of file diff --git a/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt b/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt index 17c59749c..e0363ee0d 100644 --- a/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt +++ b/src/net/torvald/terrarum/worlddrawer/LightmapRendererNew.kt @@ -310,7 +310,7 @@ object LightmapRenderer { lateinit var lightBuffer: Pixmap - fun draw(batch: SpriteBatch, drawMode: Int) { + fun draw(batch: SpriteBatch, donotuse: Int) { val this_x_start = for_x_start// + overscan_open val this_x_end = for_x_end// + overscan_open @@ -327,15 +327,7 @@ object LightmapRenderer { for (x in this_x_start..this_x_end) { - val color = if (drawMode == DRAW_FOR_RGB) { - (getLightForOpaque(x, y) ?: Color(0f,0f,0f,0f)).normaliseToColourHDR() - } - else if (drawMode == DRAW_FOR_ALPHA) { - (getLightForOpaque(x, y) ?: Color(0f,0f,0f,0f)).normaliseToAlphaHDR() - } - else { - throw IllegalArgumentException() - } + val color = (getLightForOpaque(x, y) ?: Color(0f,0f,0f,0f)).normaliseToRGBAHDR() lightBuffer.setColor(color) @@ -659,6 +651,13 @@ object LightmapRenderer { 1f ) + inline fun Color.normaliseToRGBAHDR() = Color( + hdr(this.r), + hdr(this.g), + hdr(this.b), + hdr(this.a) + ) + /** * color values are normalised -- 0.0 to 1.0 for 0..1023 */