From 702cddcc5cafb0819bea8e804f9f70d155fa1311 Mon Sep 17 00:00:00 2001 From: minjaesong Date: Sun, 1 Jul 2018 01:38:07 +0900 Subject: [PATCH] title screen using IngameRenderer --- src/net/torvald/terrarum/AppLoader.java | 2 +- .../terrarum/FuckingWorldRenderer.kt.unused | 1724 +++++++++++++---- src/net/torvald/terrarum/TitleScreen.kt | 193 +- .../torvald/terrarum/modulebasegame/Ingame.kt | 1 + .../terrarum/modulebasegame/IngameRenderer.kt | 65 +- 5 files changed, 1395 insertions(+), 590 deletions(-) diff --git a/src/net/torvald/terrarum/AppLoader.java b/src/net/torvald/terrarum/AppLoader.java index 008ca23ec..58fb85f91 100644 --- a/src/net/torvald/terrarum/AppLoader.java +++ b/src/net/torvald/terrarum/AppLoader.java @@ -78,7 +78,7 @@ public class AppLoader implements ApplicationListener { * * e.g. 0x02010034 can be translated as 2.1.52 */ - public static final int VERSION_RAW = 0x00_02_0226; + public static final int VERSION_RAW = 0x00_02_0270; public static final String getVERSION_STRING() { return String.format("%d.%d.%d", VERSION_RAW >>> 24, (VERSION_RAW & 0xff0000) >>> 16, VERSION_RAW & 0xFFFF); } diff --git a/src/net/torvald/terrarum/FuckingWorldRenderer.kt.unused b/src/net/torvald/terrarum/FuckingWorldRenderer.kt.unused index 9f1f9999d..e6ce8e9bf 100644 --- a/src/net/torvald/terrarum/FuckingWorldRenderer.kt.unused +++ b/src/net/torvald/terrarum/FuckingWorldRenderer.kt.unused @@ -1,34 +1,210 @@ -package net.torvald.terrarum +package net.torvald.terrarum.modulebasegame import com.badlogic.gdx.Gdx -import com.badlogic.gdx.InputAdapter +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.g2d.TextureRegion import com.badlogic.gdx.graphics.glutils.FrameBuffer -import com.jme3.math.FastMath -import net.torvald.terrarum.blockproperties.BlockCodex -import net.torvald.terrarum.gameactors.Actor -import net.torvald.terrarum.gameactors.ai.ActorAI + +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.gameworld.fmod -import net.torvald.terrarum.langpack.Lang -import net.torvald.terrarum.modulebasegame.Ingame -import net.torvald.terrarum.modulebasegame.gameactors.* -import net.torvald.terrarum.serialise.ReadLayerData -import net.torvald.terrarum.ui.UICanvas -import net.torvald.terrarum.modulebasegame.ui.UITitleRemoConRoot +import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator import net.torvald.terrarum.weather.WeatherMixer -import net.torvald.terrarum.worlddrawer.* -import java.io.FileInputStream +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.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.* +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 kotlin.system.measureNanoTime + /** - * Created by minjaesong on 2017-09-02. + * Created by minjaesong on 2017-06-16. */ -class FuckingWorldRenderer(val batch: SpriteBatch) : Screen { - var camera = OrthographicCamera(Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat()) +class Ingame(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 // + ////////////// + // invert Y @@ -44,370 +220,782 @@ class FuckingWorldRenderer(val batch: SpriteBatch) : Screen { } - private var loadDone = false + lateinit var gameLoadMode: GameLoadMode + lateinit var gameLoadInfoPayload: Any - private lateinit var demoWorld: GameWorld - private lateinit var cameraNodes: FloatArray // camera Y-pos - private val cameraAI = object : ActorAI { - private val axisMax = 1f - - private var firstTime = true - - override fun update(actor: Actor, delta: Float) { - val actor = actor as HumanoidNPC - - // fuck - val avSpeed = 1.0 // FIXME camera goes faster when FPS is high - actor.actorValue[AVKey.SPEED] = avSpeed - actor.actorValue[AVKey.ACCEL] = avSpeed / 6.0 - // end fuck - - - - val tileSize = FeaturesDrawer.TILE_SIZE.toFloat() - val catmullRomTension = 0f - - // pan camera - actor.moveRight(axisMax) - - - val domainSize = demoWorld.width * tileSize - val codomainSize = cameraNodes.size - val x = actor.hitbox.canonicalX.toFloat() - - val p1 = (x / (domainSize / codomainSize)).floorInt() - val p0 = (p1 - 1) fmod codomainSize - val p2 = (p1 + 1) fmod codomainSize - val p3 = (p1 + 2) fmod codomainSize - val u: Float = 1f - (p2 - (x / (domainSize / codomainSize))) / (p2 - p1) - - //val targetYPos = FastMath.interpolateCatmullRom(u, catmullRomTension, cameraNodes[p0], cameraNodes[p1], cameraNodes[p2], cameraNodes[p3]) - val targetYPos = FastMath.interpolateLinear(u, cameraNodes[p1], cameraNodes[p2]) - val yDiff = targetYPos - actor.hitbox.canonicalY - - /*if (!firstTime) { - actor.moveDown(yDiff.bipolarClamp(axisMax.toDouble()).toFloat()) - } - else { - actor.hitbox.setPosition(actor.hitbox.canonicalX, targetYPos.toDouble()) - firstTime = false - }*/ - actor.hitbox.setPosition(actor.hitbox.canonicalX, targetYPos.toDouble()) // just move the cameraY to interpolated path - - - //println("${actor.hitbox.canonicalX}, ${actor.hitbox.canonicalY}") - } - } - private lateinit var cameraPlayer: HumanoidNPC - - private val gradWhiteTop = Color(0xf8f8f8ff.toInt()) - private val gradWhiteBottom = Color(0xd8d8d8ff.toInt()) - - private val lightFBOformat = Pixmap.Format.RGB888 - lateinit var lightmapFboA: FrameBuffer - lateinit var lightmapFboB: FrameBuffer - private var lightmapInitialised = false // to avoid nullability of lightmapFBO - - lateinit var logo: TextureRegion - - val uiContainer = ArrayList() - private lateinit var uiMenu: UICanvas - - private lateinit var worldFBO: FrameBuffer - - private val TILE_SIZE = FeaturesDrawer.TILE_SIZE - private val TILE_SIZEF = TILE_SIZE.toFloat() - - private fun loadThingsWhileIntroIsVisible() { - demoWorld = ReadLayerData(FileInputStream(ModMgr.getFile("basegame", "demoworld"))) - - - // construct camera nodes - val nodeCount = 100 - cameraNodes = kotlin.FloatArray(nodeCount, { it -> - val tileXPos = (demoWorld.width.toFloat() * it / nodeCount).floorInt() - var travelDownCounter = 0 - while (!BlockCodex[demoWorld.getTileFromTerrain(tileXPos, travelDownCounter)].isSolid) { - travelDownCounter += 4 - } - travelDownCounter * FeaturesDrawer.TILE_SIZE.toFloat() - }) - - - cameraPlayer = object : HumanoidNPC(demoWorld, cameraAI, GameDate(1, 1), usePhysics = false) { - init { - setHitboxDimension(2, 2, 0, 0) - hitbox.setPosition( - 38000.0,//HQRNG().nextInt(demoWorld.width) * FeaturesDrawer.TILE_SIZE.toDouble(), - 0.0 // Y pos: placeholder; camera AI will take it over - ) - noClip = true - } - } - - //demoWorld.time.timeDelta = 150 - - - LightmapRenderer.world = demoWorld - BlocksDrawer.world = demoWorld - FeaturesDrawer.world = demoWorld - - - uiMenu = UITitleRemoConRoot() - uiMenu.setPosition(0, 0) - uiMenu.setAsOpen() - - - uiContainer.add(uiMenu) - - loadDone = true - } - - - override fun hide() { + enum class GameLoadMode { + CREATE_NEW, LOAD_FROM } override fun show() { - initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT) + //initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT) - logo = TextureRegion(Texture(Gdx.files.internal("assets/graphics/logo_placeholder.tga"))) - logo.flip(false, true) + // gameLoadMode and gameLoadInfoPayload must be set beforehand!! + when (gameLoadMode) { + GameLoadMode.CREATE_NEW -> enter(gameLoadInfoPayload as NewWorldParameters) + GameLoadMode.LOAD_FROM -> enter(gameLoadInfoPayload as GameSaveData) + } - Gdx.input.inputProcessor = TitleScreenController(this) + LightmapRenderer.world = this.world + //BlocksDrawer.world = this.world + FeaturesDrawer.world = this.world - - worldFBO = FrameBuffer(Pixmap.Format.RGBA8888, Terrarum.WIDTH, Terrarum.HEIGHT, false) + gameInitialised = true } + data class GameSaveData( + val world: GameWorld, + val historicalFigureIDBucket: ArrayList, + val realGamePlayer: ActorHumanoid + ) - private val introUncoverTime: Second = 0.3f - private var introUncoverDeltaCounter = 0f - private var updateDeltaCounter = 0.0 - protected val updateRate = 1.0 / Terrarum.TARGET_INTERNAL_FPS + data class NewWorldParameters( + val width: Int, + val height: Int, + val worldGenSeed: Long + // other worldgen options + ) - override fun render(delta: Float) { - if (!loadDone) { - loadThingsWhileIntroIsVisible() + /** + * Init instance by loading saved world + */ + private fun enter(gameSaveData: GameSaveData) { + if (gameInitialised) { + println("[Ingame] loaded successfully.") } else { - // async update - updateDeltaCounter += delta - while (updateDeltaCounter >= updateRate) { - updateScreen(delta) - updateDeltaCounter -= updateRate - } + LoadScreen.addMessage("Loading world from save") - // render? just do it anyway - renderScreen() + + world = gameSaveData.world + historicalFigureIDBucket = gameSaveData.historicalFigureIDBucket + playableActorDelegate = PlayableActorDelegate(gameSaveData.realGamePlayer) + addNewActor(player) + + + + //initGame() } } - fun updateScreen(delta: Float) { - Gdx.graphics.setTitle("WorldRenderTest" + + /** + * 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" ) - demoWorld.globalLight = WeatherMixer.globalLightNow - demoWorld.updateWorldTime(delta) - WeatherMixer.update(delta, cameraPlayer) - cameraPlayer.update(delta) - - // worldcamera update AFTER cameraplayer in this case; the other way is just an exception for actual ingame SFX - WorldCamera.update(demoWorld, cameraPlayer) + // ASYNCHRONOUS UPDATE AND RENDER // - // update UIs // + /** 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) + Terrarum.debugTimers["Ingame.update"] = measureNanoTime { updateGame(delta) } + + updateDeltaCounter -= updateRate + updateTries++ + + if (updateTries >= Terrarum.UPDATE_CATCHUP_MAX_TRIES) { + break + } + } + } + + + + /** RENDER CODE GOES HERE */ + //renderGame(batch) + Terrarum.debugTimers["Ingame.render"] = measureNanoTime { renderGame(batch) } + } + + 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) - - - LightmapRenderer.fireRecalculateEvent() // don't half-frame update; it will jitter! + // update debuggers using javax.swing // + if (Authenticator.b()) { + AVTracker.update() + ActorsList.update() + } } - fun renderScreen() { - processBlur(LightmapRenderer.DRAW_FOR_RGB) - //camera.setToOrtho(true, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat()) - - // render world - Gdx.gl.glClearColor(.64f, .754f, .84f, 1f) + 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 - batch.inUse { - setCameraPosition(0f, 0f) - batch.shader = null + 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) - renderDemoWorld() + 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 - renderMenus() - renderOverlayTexts() - } - } + blendNormal() - private fun renderDemoWorld() { - println("camera TL: ${WorldCamera.x}, ${WorldCamera.y}") - println("camera CN: ${WorldCamera.gdxCamX}, ${WorldCamera.gdxCamY}") - println() + + /////////////////////////// + // draw skybox to screen // + /////////////////////////// + + WeatherMixer.render(camera, world) - // draw skybox // - setCameraPosition(0f, 0f) - batch.color = Color.WHITE - blendNormal() - WeatherMixer.render(camera, demoWorld) - - - // draw tiles // - BlocksDrawer.renderWall(batch) - BlocksDrawer.renderTerrain(batch) - - - Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it - - - FeaturesDrawer.drawEnvOverlay(batch) - - - /////////////////// - // draw lightmap // - /////////////////// - - setCameraPosition(0f, 0f) - - batch.shader = Terrarum.shaderBayer - batch.shader.setUniformf("rcount", 64f) - batch.shader.setUniformf("gcount", 64f) - batch.shader.setUniformf("bcount", 64f) // de-banding + ///////////////////////////////// + // draw framebuffers to screen // + ///////////////////////////////// - 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 - - blendMul() - //blendNormal() - - 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 * Ingame.lightmapDownsample, lightTex.height * Ingame.lightmapDownsample - //lightTex.width.toFloat(), lightTex.height.toFloat() // for debugging - ) + 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()) - ////////////////////// - // Draw other shits // - ////////////////////// - batch.shader = null + // 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 - // 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 - } - - private fun renderMenus() { - setCameraPosition(0f, 0f) - blendNormal() - batch.shader = null + batch.draw(worldTex, 0f, 0f, worldTex.width.toFloat(), worldTex.height.toFloat())*/ - uiContainer.forEach { it.render(batch, camera) } - } - private fun renderOverlayTexts() { - setCameraPosition(0f, 0f) - blendNormal() - batch.shader = null + batch.shader = null - batch.color = Color.LIGHT_GRAY + //////////////////////// + // debug informations // + //////////////////////// - val COPYTING = arrayOf( - AppLoader.COPYRIGHT_DATE_NAME, - Lang["COPYRIGHT_GNU_GPL_3"] - ) + blendNormal() + // draw reference ID if debugWindow is open + if (debugWindow.isVisible) { - COPYTING.forEachIndexed { index, s -> - val textWidth = Terrarum.fontGame.getWidth(s) - Terrarum.fontGame.draw(batch, s, - Terrarum.WIDTH - textWidth - 1f - 0.2f, - Terrarum.HEIGHT - Terrarum.fontGame.lineHeight * (COPYTING.size - index) - 1f - ) - } - } + 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() + ) + }*/ - override fun pause() { - } + // 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) + } - override fun resume() { - } - override fun resize(width: Int, height: Int) { - // Set up viewport when window is resized - initViewPort(Terrarum.WIDTH, Terrarum.HEIGHT) - BlocksDrawer.resize(Terrarum.WIDTH, Terrarum.HEIGHT) - LightmapRenderer.resize(Terrarum.WIDTH, Terrarum.HEIGHT) - if (loadDone) { - // resize UI by re-creating it (!!) - uiMenu.resize(Terrarum.WIDTH, Terrarum.HEIGHT) - uiMenu.setPosition(0, UITitleRemoConRoot.menubarOffY) + ///////////////////////////// + // 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() } - 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? } - override fun dispose() { - logo.texture.dispose() - lightmapFboA.dispose() - lightmapFboB.dispose() - - uiMenu.dispose() - } - - - - fun setCameraPosition(newX: Float, newY: Float) { - Ingame.setCameraPosition(batch, camera, newX, newY) - } - - - fun processBlur(mode: Int) { + 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 / Ingame.lightmapDownsample // (5, 4f); using low numbers for pixel-y aesthetics + val blurRadius = 4f / lightmapDownsample // (5, 4f); using low numbers for pixel-y aesthetics var blurWriteBuffer = lightmapFboA var blurReadBuffer = lightmapFboB @@ -446,79 +1034,475 @@ class FuckingWorldRenderer(val batch: SpriteBatch) : Screen { - for (i in 0 until blurIterations) { - blurWriteBuffer.inAction(camera, batch) { + if (!KeyToggler.isOn(Input.Keys.F8)) { + for (i in 0 until blurIterations) { + blurWriteBuffer.inAction(camera, batch) { - batch.inUse { - val texture = blurReadBuffer.colorBufferTexture + batch.inUse { + val texture = blurReadBuffer.colorBufferTexture - texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) + 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.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) + batch.color = Color.WHITE + batch.draw(texture, 0f, 0f) - // swap - val t = blurWriteBuffer - blurWriteBuffer = blurReadBuffer - blurReadBuffer = t + // 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() } - class TitleScreenController(val screen: FuckingWorldRenderer) : InputAdapter() { - override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { - screen.uiContainer.forEach { it.touchUp(screenX, screenY, pointer, button) } - return true + + 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() } - override fun mouseMoved(screenX: Int, screenY: Int): Boolean { - screen.uiContainer.forEach { it.mouseMoved(screenX, screenY) } - return true + + 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) } - override fun keyTyped(character: Char): Boolean { - screen.uiContainer.forEach { it.keyTyped(character) } - return true - } - override fun scrolled(amount: Int): Boolean { - screen.uiContainer.forEach { it.scrolled(amount) } - return true - } - - override fun keyUp(keycode: Int): Boolean { - screen.uiContainer.forEach { it.keyUp(keycode) } - return true - } - - override fun touchDragged(screenX: Int, screenY: Int, pointer: Int): Boolean { - screen.uiContainer.forEach { it.touchDragged(screenX, screenY, pointer) } - return true - } - - override fun keyDown(keycode: Int): Boolean { - screen.uiContainer.forEach { it.keyDown(keycode) } - return true - } - - override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { - screen.uiContainer.forEach { it.touchDown(screenX, screenY, pointer, button) } - return true - } + println("[Ingame] Resize event") } -} \ No newline at end of file + + 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) + } + + +} diff --git a/src/net/torvald/terrarum/TitleScreen.kt b/src/net/torvald/terrarum/TitleScreen.kt index b30b44a1a..a25f8fc9b 100644 --- a/src/net/torvald/terrarum/TitleScreen.kt +++ b/src/net/torvald/terrarum/TitleScreen.kt @@ -105,10 +105,6 @@ class TitleScreen(val batch: SpriteBatch) : Screen { private val gradWhiteTop = Color(0xf8f8f8ff.toInt()) private val gradWhiteBottom = Color(0xd8d8d8ff.toInt()) - private val lightFBOformat = Pixmap.Format.RGBA8888 - lateinit var lightmapFboA: FrameBuffer - lateinit var lightmapFboB: FrameBuffer - private var lightmapInitialised = false // to avoid nullability of lightmapFBO lateinit var logo: TextureRegion @@ -241,7 +237,6 @@ class TitleScreen(val batch: SpriteBatch) : Screen { fun renderScreen() { - processBlur(LightmapRenderer.DRAW_FOR_RGB) //camera.setToOrtho(true, Terrarum.WIDTH.toFloat(), Terrarum.HEIGHT.toFloat()) // render world @@ -249,99 +244,17 @@ class TitleScreen(val batch: SpriteBatch) : Screen { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) + IngameRenderer.invoke(world = demoWorld, uisToDraw = uiContainer) + + batch.inUse { setCameraPosition(0f, 0f) - batch.shader = null - - Gdx.gl.glEnable(GL20.GL_BLEND) - renderDemoWorld() - batch.shader = null batch.color = Color.WHITE - renderMenus() renderOverlayTexts() } } - private fun renderDemoWorld() { - //println("camera TL: ${WorldCamera.x}, ${WorldCamera.y}") - //println("camera CN: ${WorldCamera.gdxCamX}, ${WorldCamera.gdxCamY}") - //println() - - - - // draw skybox // - - setCameraPosition(0f, 0f) - batch.color = Color.WHITE - blendNormal() - WeatherMixer.render(camera, demoWorld) - - - // draw tiles // - BlocksDrawer.renderWall(batch.projectionMatrix) - BlocksDrawer.renderTerrain(batch.projectionMatrix) - BlocksDrawer.renderFront(batch.projectionMatrix, false) - - - FeaturesDrawer.drawEnvOverlay(batch) - - - /////////////////// - // draw lightmap // - /////////////////// - - 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 - - blendMul() - //blendNormal() - - 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 * IngameRenderer.lightmapDownsample, lightTex.height * IngameRenderer.lightmapDownsample - //lightTex.width.toFloat(), lightTex.height.toFloat() // for debugging - ) - - - - ////////////////////// - // Draw other shits // - ////////////////////// - - - 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 - } - - private fun renderMenus() { - setCameraPosition(0f, 0f) - blendNormal() - batch.shader = null - - - uiContainer.forEach { it.render(batch, camera) } - } - private fun renderOverlayTexts() { setCameraPosition(0f, 0f) blendNormal() @@ -385,29 +298,13 @@ class TitleScreen(val batch: SpriteBatch) : Screen { // 2: The UI is coded shit } - 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? + IngameRenderer.resize(Terrarum.WIDTH, Terrarum.HEIGHT) } override fun dispose() { logo.texture.dispose() - lightmapFboA.dispose() - lightmapFboB.dispose() + + IngameRenderer.dispose() uiMenu.dispose() } @@ -419,84 +316,6 @@ class TitleScreen(val batch: SpriteBatch) : Screen { } - fun processBlur(mode: Int) { - val blurIterations = 5 // ideally, 4 * radius; must be even/odd number -- odd/even number will flip the image - val blurRadius = 4f / IngameRenderer.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) - Gdx.gl.glDisable(GL20.GL_BLEND) - } - lightmapFboB.inAction(null, null) { - Gdx.gl.glClearColor(0f, 0f, 0f, 0f) - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) - Gdx.gl.glDisable(GL20.GL_BLEND) - } - - - if (mode == LightmapRenderer.DRAW_FOR_RGB) { - // initialise readBuffer with untreated lightmap - blurReadBuffer.inAction(camera, batch) { - batch.inUse { - //blendNormal(batch) - blendDisable(batch) - batch.color = Color.WHITE - LightmapRenderer.draw(batch) - } - } - } - else { - // initialise readBuffer with untreated lightmap - blurReadBuffer.inAction(camera, batch) { - batch.inUse { - //blendNormal(batch) - blendDisable(batch) - batch.color = Color.WHITE - LightmapRenderer.draw(batch) - } - } - } - - - - 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 - } - } - } - - } - class TitleScreenController(val screen: TitleScreen) : InputAdapter() { override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { diff --git a/src/net/torvald/terrarum/modulebasegame/Ingame.kt b/src/net/torvald/terrarum/modulebasegame/Ingame.kt index d4278abd4..89890c518 100644 --- a/src/net/torvald/terrarum/modulebasegame/Ingame.kt +++ b/src/net/torvald/terrarum/modulebasegame/Ingame.kt @@ -584,6 +584,7 @@ class Ingame(batch: SpriteBatch) : IngameInstance(batch) { private fun renderGame() { IngameRenderer.invoke( + world, actorsRenderBehind, actorsRenderMiddle, actorsRenderMidTop, diff --git a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt index e3e845bcc..fa20ee239 100644 --- a/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt +++ b/src/net/torvald/terrarum/modulebasegame/IngameRenderer.kt @@ -9,6 +9,7 @@ import net.torvald.dataclass.CircularArray import net.torvald.terrarum.* import net.torvald.terrarum.gameactors.ActorWithBody import net.torvald.terrarum.gamecontroller.KeyToggler +import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.ParticleBase @@ -49,8 +50,7 @@ object IngameRenderer { private var initDone = false - private val world = (Terrarum.ingame as Ingame).world - private lateinit var player: ActorHumanoid + private var player: ActorHumanoid? = null var uiListToDraw = ArrayList() @@ -59,12 +59,13 @@ object IngameRenderer { private var debugMode = 0 operator fun invoke( - actorsRenderBehind: List, - actorsRenderMiddle: List, - actorsRenderMidTop: List, - actorsRenderFront : List, - particlesContainer: CircularArray, - player: ActorHumanoid, + world: GameWorld, + actorsRenderBehind: List? = null, + actorsRenderMiddle: List? = null, + actorsRenderMidTop: List? = null, + actorsRenderFront : List? = null, + particlesContainer: CircularArray? = null, + player: ActorHumanoid? = null, uisToDraw: ArrayList? = null ) { @@ -72,7 +73,7 @@ object IngameRenderer { uiListToDraw = uisToDraw } - init() + init(world) this.player = player @@ -214,11 +215,11 @@ object IngameRenderer { } private fun drawToRGB( - actorsRenderBehind: List, - actorsRenderMiddle: List, - actorsRenderMidTop: List, - actorsRenderFront : List, - particlesContainer: CircularArray + actorsRenderBehind: List?, + actorsRenderMiddle: List?, + actorsRenderMidTop: List?, + actorsRenderFront : List?, + particlesContainer: CircularArray? ) { fboRGB.inAction(null, null) { clearBuffer() } fboRGB_lightMixed.inAction(null, null) { clearBuffer() } @@ -235,8 +236,8 @@ object IngameRenderer { batch.inUse { moveCameraToWorldCoord() - actorsRenderBehind.forEach { it.drawBody(batch) } - particlesContainer.forEach { it.drawBody(batch) } + actorsRenderBehind?.forEach { it.drawBody(batch) } + particlesContainer?.forEach { it.drawBody(batch) } } setCameraPosition(0f, 0f) @@ -247,10 +248,10 @@ object IngameRenderer { // draw actors // ///////////////// moveCameraToWorldCoord() - actorsRenderMiddle.forEach { it.drawBody(batch) } - actorsRenderMidTop.forEach { it.drawBody(batch) } - player.drawBody(batch) - actorsRenderFront.forEach { it.drawBody(batch) } + 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 // } @@ -299,11 +300,11 @@ object IngameRenderer { } private fun drawToA( - actorsRenderBehind: List, - actorsRenderMiddle: List, - actorsRenderMidTop: List, - actorsRenderFront : List, - particlesContainer: CircularArray + actorsRenderBehind: List?, + actorsRenderMiddle: List?, + actorsRenderMidTop: List?, + actorsRenderFront : List?, + particlesContainer: CircularArray? ) { fboA.inAction(null, null) { clearBuffer() @@ -325,8 +326,8 @@ object IngameRenderer { batch.inUse { moveCameraToWorldCoord() - actorsRenderBehind.forEach { it.drawGlow(batch) } - particlesContainer.forEach { it.drawGlow(batch) } + actorsRenderBehind?.forEach { it.drawGlow(batch) } + particlesContainer?.forEach { it.drawGlow(batch) } } setCameraPosition(0f, 0f) @@ -337,10 +338,10 @@ object IngameRenderer { // draw actors // ///////////////// moveCameraToWorldCoord() - actorsRenderMiddle.forEach { it.drawGlow(batch) } - actorsRenderMidTop.forEach { it.drawGlow(batch) } - player.drawGlow(batch) - actorsRenderFront.forEach { it.drawGlow(batch) } + actorsRenderMiddle?.forEach { it.drawGlow(batch) } + actorsRenderMidTop?.forEach { it.drawGlow(batch) } + player?.drawGlow(batch) + actorsRenderFront?.forEach { it.drawGlow(batch) } // --> Change of blend mode <-- introduced by children of ActorWithBody // } } @@ -382,7 +383,7 @@ object IngameRenderer { } - private fun init() { + private fun init(world: GameWorld) { if (!initDone) { batch = SpriteBatch() camera = OrthographicCamera(widthf, heightf)