From 009b55481b30fe6e65351dfd65023fc045a3719f Mon Sep 17 00:00:00 2001 From: minjaesong Date: Thu, 6 Jul 2017 16:35:58 +0900 Subject: [PATCH] new collision displacer: got one-block-ceiling-passthru bug, but otherwise tolerable --- assets/4096.frag | 9 +- assets/4096_bayer.frag | 59 +++++ assets/blur.frag | 4 + assets/{test.frag => quickgreyscale.frag} | 0 assets/{test.vert => quickgreyscale.vert} | 0 src/net/torvald/terrarum/ColorLimiterTest.kt | 8 +- src/net/torvald/terrarum/StateInGameGDX.kt | 118 +++++++-- src/net/torvald/terrarum/TerrarumGDX.kt | 12 +- src/net/torvald/terrarum/TestTestTest.kt | 9 - .../terrarum/gameactors/ActorWithPhysics.kt | 247 +++++++----------- .../terrarum/gameactors/HistoricalFigure.kt | 21 ++ 11 files changed, 298 insertions(+), 189 deletions(-) create mode 100644 assets/4096_bayer.frag rename assets/{test.frag => quickgreyscale.frag} (100%) rename assets/{test.vert => quickgreyscale.vert} (100%) diff --git a/assets/4096.frag b/assets/4096.frag index abf3dd0ff..2fb526f57 100644 --- a/assets/4096.frag +++ b/assets/4096.frag @@ -2,10 +2,13 @@ varying vec4 v_color; varying vec2 v_texCoords; uniform sampler2D u_texture; - void main(void) { - vec4 color = texture2D(u_texture, v_texCoords); - color = floor(15.0 * color + 0.5) / 15.0; + vec4 color = texture2D(u_texture, v_texCoords).rgba; + + color.r = floor(15.0 * color.r + 0.5) / 15.0; + color.g = floor(15.0 * color.g + 0.5) / 15.0; + color.b = floor(15.0 * color.b + 0.5) / 15.0; + // a: passthrough gl_FragColor = color; } \ No newline at end of file diff --git a/assets/4096_bayer.frag b/assets/4096_bayer.frag new file mode 100644 index 000000000..59cf3dc5c --- /dev/null +++ b/assets/4096_bayer.frag @@ -0,0 +1,59 @@ +varying vec4 v_color; +varying vec2 v_texCoords; +uniform sampler2D u_texture; + + + +uniform mat4 Bayer; +uniform float monitorGamma; + +vec4 gammaIn(vec4 col) { + return pow(col, vec4(vec3(monitorGamma), 1.0)); +} + +vec4 gammaOut(vec4 col) { + return pow(col, vec4(vec3(1.0 / monitorGamma), 1.0)); +} + +void main(void) { + // create texture coordinates based on pixelSize // + float pixelSize = 1.0; + + vec2 pixelSizeVec = vec2(float(pixelSize), float(pixelSize)); + + vec2 discrete = (gl_FragCoord.xy + 0.001) / v_texCoords / pixelSizeVec; + //vec2 discrete = (gl_FragCoord.xy) / v_texCoords / pixelSizeVec; + + discrete = floor(discrete * v_texCoords) / discrete; + + vec4 color = texture2D(u_texture, discrete).rgba; + + + // add Bayer matrix entry to current pixel // + vec2 entry = mod(gl_FragCoord.xy / pixelSizeVec, vec2(4, 4)); + + color.r = color.r + Bayer[int(entry.x)][int(entry.y)] / 17.0 - 0.5; + color.g = color.g + Bayer[int(entry.x)][int(entry.y)] / 17.0 - 0.5; + color.b = color.b + Bayer[int(entry.x)][int(entry.y)] / 17.0 - 0.5; + //color.a = color.a + Bayer[int(entry.x)][int(entry.y)] / 17.0 - 0.5; + + + // find nearest 8-bit color // + color.r = floor(15.0 * color.r + 0.5) / 15.0; + color.g = floor(15.0 * color.g + 0.5) / 15.0; + color.b = floor(15.0 * color.b + 0.5) / 15.0; + //color.a = floor(15.0 * color.a + 0.5) / 15.0; + + gl_FragColor = color; + + + + + + + + //vec4 color = texture2D(u_texture, v_texCoords); + //color = floor(15.0 * color + 0.5) / 15.0; +// + //gl_FragColor = color; +} \ No newline at end of file diff --git a/assets/blur.frag b/assets/blur.frag index 98ecb63fa..8a0566b82 100644 --- a/assets/blur.frag +++ b/assets/blur.frag @@ -1,3 +1,7 @@ +#ifdef GL_ES + precision mediump float; +#endif + varying vec4 v_color; varying vec2 v_texCoords; diff --git a/assets/test.frag b/assets/quickgreyscale.frag similarity index 100% rename from assets/test.frag rename to assets/quickgreyscale.frag diff --git a/assets/test.vert b/assets/quickgreyscale.vert similarity index 100% rename from assets/test.vert rename to assets/quickgreyscale.vert diff --git a/src/net/torvald/terrarum/ColorLimiterTest.kt b/src/net/torvald/terrarum/ColorLimiterTest.kt index 5e27f97cf..cc248b8fb 100644 --- a/src/net/torvald/terrarum/ColorLimiterTest.kt +++ b/src/net/torvald/terrarum/ColorLimiterTest.kt @@ -9,6 +9,7 @@ import com.badlogic.gdx.graphics.GL20 import com.badlogic.gdx.graphics.Texture import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.ShaderProgram +import com.badlogic.gdx.math.Matrix4 /** * Created by minjaesong on 2017-07-05. @@ -37,6 +38,11 @@ object ColorLimiterTest : ApplicationAdapter() { ShaderProgram.pedantic = false shader4096 = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/4096.frag")) + shader4096.begin() + //shader4096.setUniformMatrix("Bayer", Matrix4(floatArrayOf(0f,8f,2f,10f,12f,4f,14f,6f,3f,11f,1f,9f,15f,7f,13f,5f))) + shader4096.end() + + img = Texture("assets/test_texture.tga") batch = SpriteBatch() @@ -51,7 +57,7 @@ object ColorLimiterTest : ApplicationAdapter() { batch.inUse { batch.shader = shader4096 - //batch.shader.setUniformf("iResolution", Gdx.graphics.width.toFloat(), Gdx.graphics.height.toFloat()) + //batch.shader.setUniformf("monitorGamma", 2.2f) batch.color = Color.WHITE batch.draw(img, 0f, 0f) diff --git a/src/net/torvald/terrarum/StateInGameGDX.kt b/src/net/torvald/terrarum/StateInGameGDX.kt index 0a55263b8..87dde62fc 100644 --- a/src/net/torvald/terrarum/StateInGameGDX.kt +++ b/src/net/torvald/terrarum/StateInGameGDX.kt @@ -47,6 +47,7 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { private val ACTOR_UPDATE_RANGE = 4096 lateinit var world: GameWorld + lateinit var historicalFigureIDBucket: ArrayList /** * list of Actors that is sorted by Actors' referenceID @@ -76,9 +77,19 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { val lightmapDownsample = 1f } - var worldDrawFrameBuffer = FrameBuffer(Pixmap.Format.RGBA8888, TerrarumGDX.WIDTH, TerrarumGDX.HEIGHT, true) - var lightmapFboA = FrameBuffer(Pixmap.Format.RGBA8888, TerrarumGDX.WIDTH.div(lightmapDownsample.toInt()), TerrarumGDX.HEIGHT.div(lightmapDownsample.toInt()), true) - var lightmapFboB = FrameBuffer(Pixmap.Format.RGBA8888, TerrarumGDX.WIDTH.div(lightmapDownsample.toInt()), TerrarumGDX.HEIGHT.div(lightmapDownsample.toInt()), true) + + private val worldFBOformat = Pixmap.Format.RGBA4444 // just a future-proof for mobile + private val lightFBOformat = Pixmap.Format.RGBA8888 + + var worldDrawFrameBuffer = FrameBuffer(worldFBOformat, TerrarumGDX.WIDTH, TerrarumGDX.HEIGHT, true) + var lightmapFboA = FrameBuffer(lightFBOformat, TerrarumGDX.WIDTH.div(lightmapDownsample.toInt()), TerrarumGDX.HEIGHT.div(lightmapDownsample.toInt()), true) + var lightmapFboB = FrameBuffer(lightFBOformat, TerrarumGDX.WIDTH.div(lightmapDownsample.toInt()), TerrarumGDX.HEIGHT.div(lightmapDownsample.toInt()), true) + + + init { + println("worldDrawFrameBuffer.colorBufferTexture.textureData.format: ${worldDrawFrameBuffer.colorBufferTexture.textureData.format}") + println("lightmapFboB.colorBufferTexture.textureData.format: ${lightmapFboB.colorBufferTexture.textureData.format}") + } //private lateinit var shader12BitCol: Shader // grab LibGDX if you want some shader @@ -152,15 +163,28 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { initViewPort(TerrarumGDX.WIDTH, TerrarumGDX.HEIGHT) } + data class GameSaveData( + val world: GameWorld, + val historicalFigureIDBucket: ArrayList, + val realGamePlayer: ActorHumanoid + ) + fun enter(gameSaveData: GameSaveData) { + world = gameSaveData.world + historicalFigureIDBucket = gameSaveData.historicalFigureIDBucket + playableActorDelegate = PlayableActorDelegate(gameSaveData.realGamePlayer) + addNewActor(player!!) + + + + initGame() + } + + + /** + * Create new world + */ fun enter() { - - Gdx.input.inputProcessor = GameController - - - initViewPort(TerrarumGDX.WIDTH, TerrarumGDX.HEIGHT) - - // load things when the game entered this "state" // load necessary shaders //shader12BitCol = Shader.makeShader("./assets/4096.vert", "./assets/4096.frag") @@ -176,6 +200,9 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { WorldGenerator.generateMap() + historicalFigureIDBucket = ArrayList() + + RoguelikeRandomiser.seed = HQRNG().nextLong() @@ -190,6 +217,17 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { + initGame() + } + + fun initGame() { + + Gdx.input.inputProcessor = GameController + + + initViewPort(TerrarumGDX.WIDTH, TerrarumGDX.HEIGHT) + + // init console window consoleHandler = UIHandler(ConsoleWindow()) consoleHandler.setPosition(0, 0) @@ -391,7 +429,7 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { /////////////////// // blur lightmap // /////////////////// - val blurIterations = 3 // ideally, 4 * radius; must be even/odd number -- odd/even number will flip the image + val blurIterations = 5 // ideally, 4 * radius; must be even/odd number -- odd/even number will flip the image val blurRadius = 4f / lightmapDownsample // (3, 4f); using low numbers for pixel-y aesthetics @@ -401,7 +439,7 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } - lightmapFboA.inAction(null, null) { + lightmapFboB.inAction(null, null) { Gdx.gl.glClearColor(0f, 0f, 0f, 0f) Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT) } @@ -423,7 +461,11 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { blurReadBuffer.inAction(camera, batch) { batch.inUse { // using custom code for camera; this is obscure and tricky - camera.position.set((WorldCamera.gdxCamX / lightmapDownsample).round(), (WorldCamera.gdxCamY / lightmapDownsample).round(), 0f) // make camara work + camera.position.set( + (WorldCamera.gdxCamX / lightmapDownsample).round(), + (WorldCamera.gdxCamY / lightmapDownsample).round(), + 0f + ) // make camara work camera.update() batch.projectionMatrix = camera.combined @@ -445,7 +487,8 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { batch.shader = TerrarumGDX.shaderBlur - batch.shader.setUniformf("iResolution", blurWriteBuffer.width.toFloat(), blurWriteBuffer.height.toFloat()) + 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) @@ -480,7 +523,6 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { batch.inUse { batch.shader = null - // using custom code for camera; this is obscure and tricky camera.position.set(WorldCamera.gdxCamX, WorldCamera.gdxCamY, 0f) // make camara work camera.update() @@ -519,16 +561,26 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { // mix lighpmap canvas to this canvas if (!KeyToggler.isOn(Input.Keys.F6)) { // F6 to disable lightmap draw setCameraPosition(0f, 0f) + batch.shader = null val lightTex = blurWriteBuffer.colorBufferTexture // TODO zoom! + lightTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) + if (KeyToggler.isOn(KEY_LIGHTMAP_RENDER)) blendNormal() else blendMul() + batch.color = Color.WHITE - batch.draw(lightTex, 0f, 0f, lightTex.width * lightmapDownsample, lightTex.height * lightmapDownsample) + batch.draw(lightTex, + 0f, 0f, + lightTex.width * lightmapDownsample, lightTex.height * lightmapDownsample + ) } + 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 @@ -567,9 +619,11 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { ///////////////////////////////// // draw framebuffers to screen // ///////////////////////////////// - WeatherMixer.render(batch) + batch.flush() + + batch.shader = null batch.color = Color.WHITE val worldTex = worldDrawFrameBuffer.colorBufferTexture // TODO zoom! worldTex.setFilter(Texture.TextureFilter.Nearest, Texture.TextureFilter.Nearest) @@ -577,6 +631,8 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { + batch.shader = null + batch.color = Color.RED batch.fillRect(0f, 0f, 16f, 16f) @@ -640,7 +696,7 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { ///////////////////////////// // draw some overlays (UI) // ///////////////////////////// - uiContainer.forEach { if (it != consoleHandler) it.render(batch) } // FIXME draws black of grey coloured box on top right + uiContainer.forEach { if (it != consoleHandler) it.render(batch) } debugWindow.render(batch) // make sure console draws on top of other UIs @@ -842,7 +898,9 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { // 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})") + throw IllegalArgumentException( + "No such actor in the game: $refid (elemsActive: ${actorContainer.size}, " + + "elemsInactive: ${actorContainerInactive.size})") } // take care of old delegate @@ -975,8 +1033,10 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { /** whether the actor is within screen */ private fun ActorWithBody.inScreen() = distToCameraSqr(this) <= - (TerrarumGDX.WIDTH.plus(this.hitbox.width.div(2)).times(1 / TerrarumGDX.ingame!!.screenZoom).sqr() + - TerrarumGDX.HEIGHT.plus(this.hitbox.height.div(2)).times(1 / TerrarumGDX.ingame!!.screenZoom).sqr()) + (TerrarumGDX.WIDTH.plus(this.hitbox.width.div(2)). + times(1 / TerrarumGDX.ingame!!.screenZoom).sqr() + + TerrarumGDX.HEIGHT.plus(this.hitbox.height.div(2)). + times(1 / TerrarumGDX.ingame!!.screenZoom).sqr()) /** whether the actor is within update range */ @@ -1111,7 +1171,9 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { fun addUI(ui: UIHandler) { // check for exact duplicates if (uiContainer.contains(ui)) { - throw IllegalArgumentException("Exact copy of the UI already exists: The instance of ${ui.UI.javaClass.simpleName}") + throw IllegalArgumentException( + "Exact copy of the UI already exists: The instance of ${ui.UI.javaClass.simpleName}" + ) } uiContainer.add(ui) @@ -1126,7 +1188,11 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { index = actorContainerInactive.binarySearch(ID) if (index < 0) { - JOptionPane.showMessageDialog(null, "Actor with ID $ID does not exist.", null, JOptionPane.ERROR_MESSAGE) + JOptionPane.showMessageDialog( + null, + "Actor with ID $ID does not exist.", + null, JOptionPane.ERROR_MESSAGE + ) throw IllegalArgumentException("Actor with ID $ID does not exist.") } else @@ -1203,11 +1269,11 @@ class StateInGameGDX(val batch: SpriteBatch) : Screen { override fun resize(width: Int, height: Int) { worldDrawFrameBuffer.dispose() - worldDrawFrameBuffer = FrameBuffer(Pixmap.Format.RGBA8888, width, height, true) + worldDrawFrameBuffer = FrameBuffer(worldFBOformat, width, height, true) lightmapFboA.dispose() - lightmapFboA = FrameBuffer(Pixmap.Format.RGBA8888, width.div(lightmapDownsample.toInt()), height.div(lightmapDownsample.toInt()), true) + lightmapFboA = FrameBuffer(lightFBOformat, width.div(lightmapDownsample.toInt()), height.div(lightmapDownsample.toInt()), true) lightmapFboB.dispose() - lightmapFboB = FrameBuffer(Pixmap.Format.RGBA8888, width.div(lightmapDownsample.toInt()), height.div(lightmapDownsample.toInt()), true) + lightmapFboB = FrameBuffer(lightFBOformat, width.div(lightmapDownsample.toInt()), height.div(lightmapDownsample.toInt()), true) // Set up viewport when window is resized diff --git a/src/net/torvald/terrarum/TerrarumGDX.kt b/src/net/torvald/terrarum/TerrarumGDX.kt index 93d9a4735..616c138c8 100644 --- a/src/net/torvald/terrarum/TerrarumGDX.kt +++ b/src/net/torvald/terrarum/TerrarumGDX.kt @@ -14,6 +14,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch import com.badlogic.gdx.graphics.glutils.FrameBuffer import com.badlogic.gdx.graphics.glutils.ShaderProgram import com.badlogic.gdx.graphics.glutils.ShapeRenderer +import com.badlogic.gdx.math.Matrix4 import com.google.gson.JsonArray import com.google.gson.JsonPrimitive import com.jme3.math.FastMath @@ -50,7 +51,6 @@ fun main(args: Array) { config.height = 742 config.backgroundFPS = 9999 config.foregroundFPS = 9999 - //config.useGL30 = true config.title = GAME_NAME LwjglApplication(TerrarumGDX, config) @@ -242,6 +242,8 @@ object TerrarumGDX : ApplicationAdapter() { lateinit var shaderBlur: ShaderProgram + lateinit var shader4096: ShaderProgram + lateinit var shader4096Bayer: ShaderProgram init { @@ -301,6 +303,14 @@ object TerrarumGDX : ApplicationAdapter() { ShaderProgram.pedantic = false shaderBlur = ShaderProgram(Gdx.files.internal("assets/blur.vert"), Gdx.files.internal("assets/blur.frag")) + shader4096 = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/4096.frag")) + shader4096Bayer = ShaderProgram(Gdx.files.internal("assets/4096.vert"), Gdx.files.internal("assets/4096_bayer.frag")) + + shader4096Bayer.begin() + shader4096Bayer.setUniformMatrix("Bayer", Matrix4(floatArrayOf(0f,8f,2f,10f,12f,4f,14f,6f,3f,11f,1f,9f,15f,7f,13f,5f))) + shader4096Bayer.setUniformf("monitorGamma", 2.2f) + shader4096Bayer.end() + ModMgr // invoke Module Manager, will also invoke BlockCodex ItemCodex // invoke Item Codex diff --git a/src/net/torvald/terrarum/TestTestTest.kt b/src/net/torvald/terrarum/TestTestTest.kt index be6bd9986..db30bc4da 100644 --- a/src/net/torvald/terrarum/TestTestTest.kt +++ b/src/net/torvald/terrarum/TestTestTest.kt @@ -196,15 +196,6 @@ object TestTestMain : ApplicationAdapter() { ShaderProgram.pedantic = false blurShader = ShaderProgram(Gdx.files.internal("assets/blur.vert"), Gdx.files.internal("assets/blur.frag")) - // culprit #4 - blurShader.begin() - blurShader.setUniformf("dir", 0f, 0f); //direction of blur; nil for now - blurShader.setUniformf("resolution", maxOf(TerrarumGDX.WIDTH.toFloat(), TerrarumGDX.HEIGHT.toFloat())) //size of FBO texture - blurShader.setUniformf("radius", 9f) //radius of blur - blurShader.end() - - Gdx.graphics.isContinuousRendering = true // culprit #3 - batch = SpriteBatch() diff --git a/src/net/torvald/terrarum/gameactors/ActorWithPhysics.kt b/src/net/torvald/terrarum/gameactors/ActorWithPhysics.kt index e297bde17..229e3b05f 100644 --- a/src/net/torvald/terrarum/gameactors/ActorWithPhysics.kt +++ b/src/net/torvald/terrarum/gameactors/ActorWithPhysics.kt @@ -61,7 +61,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean */ override val hitbox = Hitbox(0.0, 0.0, 0.0, 0.0) // Hitbox is implemented using Double; - inline val tilewiseHitbox: Hitbox + val tilewiseHitbox: Hitbox get() = Hitbox.fromTwoPoints( hitbox.startX.div(TILE_SIZE).floor(), hitbox.startY.div(TILE_SIZE).floor(), @@ -104,7 +104,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean } @Transient val MASS_LOWEST = 0.1 // Kilograms /** Apparent mass. Use "avBaseMass" for base mass */ - inline val mass: Double + val mass: Double get() = actorValue.getAsDouble(AVKey.BASEMASS) ?: MASS_DEFAULT * Math.pow(scale, 3.0) /*set(value) { // use "var avBaseMass: Double" if (value <= 0) @@ -185,7 +185,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean @Transient private val gravitation: Vector2 = world.gravitation @Transient val DRAG_COEFF_DEFAULT = 1.2 /** Drag coefficient. Parachutes have much higher value than bare body (1.2) */ - inline var dragCoefficient: Double + var dragCoefficient: Double get() = actorValue.getAsDouble(AVKey.DRAGCOEFF) ?: DRAG_COEFF_DEFAULT set(value) { if (value < 0) @@ -541,7 +541,7 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean fun debug1(wut: Any?) { // vvvvv set it true to make debug print work - if (false) println(wut) + if (true) println(wut) } fun debug2(wut: Any?) { // vvvvv set it true to make debug print work @@ -622,10 +622,10 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean val newHitbox = hitbox.reassign(sixteenStep[collidingStep]) var selfCollisionStatus = 0 - if (isWalled(newHitbox, COLLIDING_LEFT)) selfCollisionStatus += COLL_LEFTSIDE - if (isWalled(newHitbox, COLLIDING_RIGHT)) selfCollisionStatus += COLL_RIGHTSIDE - if (isWalled(newHitbox, COLLIDING_TOP)) selfCollisionStatus += COLL_TOPSIDE - if (isWalled(newHitbox, COLLIDING_BOTTOM)) selfCollisionStatus += COLL_BOTTOMSIDE + if (isWalled(newHitbox, COLLIDING_LEFT)) selfCollisionStatus += COLL_LEFTSIDE // 1 + if (isWalled(newHitbox, COLLIDING_RIGHT)) selfCollisionStatus += COLL_RIGHTSIDE // 4 + if (isWalled(newHitbox, COLLIDING_TOP)) selfCollisionStatus += COLL_TOPSIDE // 8 + if (isWalled(newHitbox, COLLIDING_BOTTOM)) selfCollisionStatus += COLL_BOTTOMSIDE // 2 // fixme UP and RIGHT && LEFT and DOWN bug @@ -645,34 +645,81 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean 4, 14 -> { newHitbox.translatePosX( - newHitbox.endX.modTileDelta()) ; bounceX = true } 8, 13 -> { newHitbox.translatePosY(TILE_SIZE - newHitbox.startY.modTileDelta()); bounceY = true } 2, 7 -> { newHitbox.translatePosY( - newHitbox.endY.modTileDelta()) ; bounceY = true } - // two-side collision - 3 -> { - debug1("translateX: ${(TILE_SIZE - newHitbox.startX.modTileDelta()).rem(TILE_SIZE)}") - newHitbox.translatePosX((TILE_SIZE - newHitbox.startX.modTileDelta()).rem(TILE_SIZE)) - newHitbox.translatePosY( - newHitbox.endY.modTileDelta()) - bounceX = true; bounceY = true - } - 6 -> { - debug1("translateX: ${( - newHitbox.endX.modTileDelta())}") - newHitbox.translatePosX( - newHitbox.endX.modTileDelta()) - newHitbox.translatePosY( - newHitbox.endY.modTileDelta()) - bounceX = true; bounceY = true - } - 9 -> { - newHitbox.translatePosX(TILE_SIZE - newHitbox.startX.modTileDelta()) - newHitbox.translatePosY(TILE_SIZE - newHitbox.startY.modTileDelta()) - bounceX = true; bounceY = true - } - 12 -> { - debug1("translateY: ${(TILE_SIZE - newHitbox.startY.modTileDelta()).rem(TILE_SIZE)}") - newHitbox.translatePosX( - newHitbox.endX.modTileDelta()) - newHitbox.translatePosY((TILE_SIZE - newHitbox.startY.modTileDelta()).rem(TILE_SIZE)) - bounceX = true; bounceY = true - } } + // two-side collision if (selfCollisionStatus in listOf(3,6,9,12)) { debug1("twoside collision $selfCollisionStatus") + + // !! this code is based on Dyn4j Vector's coord system; V(1,0) -> 0, V(0,1) -> pi, V(0,-1) -> -pi !! // + + // we can use selfCollisionStatus to tell which of those four side we care + + // points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!) + val offendingTileWorldX = if (selfCollisionStatus in listOf(6, 12)) + newHitbox.endX.div(TILE_SIZE).floor() * TILE_SIZE + else + newHitbox.startX.div(TILE_SIZE).ceil() * TILE_SIZE + + // points to the EDGE of the tile in world dimension (don't use this directly to get tilewise coord!!) + val offendingTileWorldY = if (selfCollisionStatus in listOf(3, 6)) + newHitbox.endY.div(TILE_SIZE).floor() * TILE_SIZE + else + newHitbox.startY.div(TILE_SIZE).ceil() * TILE_SIZE + + val offendingHitboxPointX = if (selfCollisionStatus in listOf(6, 12)) + newHitbox.endX + else + newHitbox.startX + + val offendingHitboxPointY = if (selfCollisionStatus in listOf(3, 6)) + newHitbox.endY + else + newHitbox.startY + + val angleOfIncidence = vectorSum.direction.toPositiveRad() + val angleThreshold = (Vector2(offendingHitboxPointX, offendingHitboxPointY) - + Vector2(offendingTileWorldX, offendingTileWorldY)).direction.toPositiveRad() + + + val displacementAbs = Vector2( + (offendingTileWorldX - offendingHitboxPointX).abs(), + (offendingTileWorldY - offendingHitboxPointY).abs() + ) + + + // conditions should be four? for 4 corners? + // or mathe works regardless? + // (need to account for I < TH; I > TH; I == TH) + + val displacementUnitVector = + if (angleOfIncidence == angleThreshold) + -vectorSum + else { + when (selfCollisionStatus) { + 3 -> if (angleOfIncidence > angleThreshold) Vector2(1.0, 0.0) else Vector2(0.0, -1.0) + 6 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, -1.0) else Vector2(-1.0, 0.0) + 9 -> if (angleOfIncidence > angleThreshold) Vector2(0.0, 1.0) else Vector2(1.0, 0.0) + 12 -> if (angleOfIncidence > angleThreshold) Vector2(-1.0, 0.0) else Vector2(0.0, 1.0) + else -> throw InternalError("Blame hardware or universe") + } + } + + val finalDisplacement = + if (angleOfIncidence == angleThreshold) + displacementUnitVector + else + Vector2( + displacementAbs.x * displacementUnitVector.x, + displacementAbs.y * displacementUnitVector.y + ) + + newHitbox.translate(finalDisplacement) + + + bounceX = angleOfIncidence == angleThreshold || displacementUnitVector.x != 0.0 + bounceY = angleOfIncidence == angleThreshold || displacementUnitVector.y != 0.0 + } @@ -712,113 +759,6 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean return - /*val BLOCK_LEFTSIDE = 1 - val BLOCK_BOTTOMSIDE = 2 - val BLOCK_RIGHTSIDE = 4 - val BLOCK_TOPSIDE = 8 - fun getBlockCondition(hitbox: Hitbox, blockAddress: BlockAddress): Int { - val blockX = (blockAddress % world.width) * TILE_SIZE - val blockY = (blockAddress / world.width) * TILE_SIZE - var ret = 0 - - // test leftside - if (hitbox.startX >= blockX && hitbox.startX < blockX + TILE_SIZE) { // TEST ME: <= or < - ret += BLOCK_LEFTSIDE - } - // test bottomside - if (hitbox.endY >= blockY && hitbox.endY < blockY + TILE_SIZE) { - ret += BLOCK_BOTTOMSIDE - } - // test rightside - if (hitbox.endX >= blockX && hitbox.endX < blockX + TILE_SIZE) { - ret += BLOCK_RIGHTSIDE - } - // test topside - if (hitbox.startY >= blockY && hitbox.startY < blockY + TILE_SIZE) { - ret += BLOCK_TOPSIDE - } - - // cancel two superpositions (change 0b-numbers if you've changed side indices!) - //if (ret and 0b1010 == 0b1010) ret = ret and 0b0101 - //if (ret and 0b0101 == 0b0101) ret = ret and 0b1010 - - return ret - } - infix fun Int.hasSide(side: Int) = this and side != 0 - - - - var bounceX = false - var bounceY = false - // collision NOT detected - if (collidingStep == null) { - hitbox.translate(vectorSum) - // grounded = false - } - // collision detected - else { - //debug1("Collision detected") - - val newHitbox = hitbox.clone() // this line is wrong (must be hitbox.reassign(sixteenStep[collidingStep])) HOWEVER the following method is also wrong; think about the case where I am placed exactly in between two tiles) - // see if four sides of hitbox CROSSES the tile - // that information should be able to tell where the hitbox be pushed - // blocks can have up to 4 status at once - affectingTiles.forEach { blockAddr -> - val blockCollStatus = getBlockCondition(newHitbox, blockAddr) - - if (blockCollStatus != 0) debug4("--> blockCollStat: $blockCollStatus") - - // displacements (no ELSE IFs!) superpositions are filtered in getBlockCondition() - if (blockCollStatus hasSide BLOCK_LEFTSIDE) { - val displacement = TILE_SIZE - newHitbox.startX.modTileDelta() - newHitbox.translatePosX(displacement) - bounceX = true - - debug4("--> leftside") - } - if (blockCollStatus hasSide BLOCK_RIGHTSIDE) { - val displacement = newHitbox.endX.modTileDelta() - newHitbox.translatePosX(displacement) - bounceX = true - - debug4("--> rightside") - } - if (blockCollStatus hasSide BLOCK_TOPSIDE) { - val displacement = TILE_SIZE - newHitbox.startY.modTileDelta() - newHitbox.translatePosY(displacement) - bounceY = true - - debug4("--> topside") - } - if (blockCollStatus hasSide BLOCK_BOTTOMSIDE) { - val displacement = newHitbox.endY.modTileDelta() - newHitbox.translatePosY(displacement) - bounceY = true - - debug4("--> bottomside") - } - } - - - hitbox.reassign(newHitbox) - - - // bounce X/Y - if (bounceX) { - externalForce.x *= elasticity - controllerMoveDelta?.let { controllerMoveDelta!!.x *= elasticity } - } - if (bounceY) { - externalForce.y *= elasticity - controllerMoveDelta?.let { controllerMoveDelta!!.y *= elasticity } - } - - - // grounded = true - - }// end of collision not detected*/ - - // if collision not detected, just don't care; it's not your job to apply moveDelta @@ -1458,35 +1398,44 @@ open class ActorWithPhysics(renderOrder: RenderOrder, val immobileBody: Boolean /** * Apparent strength. 1 000 is default value */ - internal inline val avStrength: Double + internal val avStrength: Double get() = (actorValue.getAsDouble(AVKey.STRENGTH) ?: 1000.0) * (actorValue.getAsDouble(AVKey.STRENGTHBUFF) ?: 1.0) * scale - internal inline var avBaseStrength: Double? + internal var avBaseStrength: Double? get() = actorValue.getAsDouble(AVKey.STRENGTH) set(value) { actorValue[AVKey.STRENGTH] = value!! } - internal inline var avBaseMass: Double - get() = actorValue.getAsDouble(AVKey.BASEMASS) ?: MASS_DEFAULT + internal var avBaseMass: Double + inline get() = actorValue.getAsDouble(AVKey.BASEMASS) ?: MASS_DEFAULT set(value) { + if (value <= 0 || value.isNaN() || value.isInfinite()) + throw IllegalArgumentException("Tried to set base mass to invalid value ($value)") actorValue[AVKey.BASEMASS] = value } - internal inline val avAcceleration: Double + internal val avAcceleration: Double get() = actorValue.getAsDouble(AVKey.ACCEL)!! * actorValue.getAsDouble(AVKey.ACCELBUFF)!! * accelMultMovement * scale.sqrt() - internal inline val avSpeedCap: Double + internal val avSpeedCap: Double get() = actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDBUFF)!! * speedMultByTile * scale.sqrt() + + private fun Double.toPositiveRad() = // rad(0..2pi, -2pi..0) -> rad(0..4pi) + if (this >= -2 * Math.PI && this < 0.0) + 4 * Math.PI - this + else + this } inline fun Int.sqr(): Int = this * this inline fun Double.floorInt() = Math.floor(this).toInt() inline fun Float.floorInt() = FastMath.floor(this) inline fun Float.floor() = FastMath.floor(this).toFloat() +inline fun Double.ceilInt() = Math.ceil(this).toInt() inline fun Float.ceilInt() = FastMath.ceil(this) inline fun Double.round() = Math.round(this).toDouble() inline fun Double.floor() = Math.floor(this) @@ -1498,12 +1447,12 @@ inline fun Double.sqr() = this * this inline fun Double.sqrt() = Math.sqrt(this) inline fun Float.sqrt() = FastMath.sqrt(this) inline fun Int.abs() = if (this < 0) -this else this -inline fun Double.bipolarClamp(limit: Double) = +fun Double.bipolarClamp(limit: Double) = if (this > 0 && this > limit) limit else if (this < 0 && this < -limit) -limit else this -inline fun absMax(left: Double, right: Double): Double { +fun absMax(left: Double, right: Double): Double { if (left > 0 && right > 0) if (left > right) return left else return right @@ -1518,8 +1467,8 @@ inline fun absMax(left: Double, right: Double): Double { } } -inline fun Double.magnSqr() = if (this >= 0.0) this.sqr() else -this.sqr() -inline fun Double.sign() = if (this > 0.0) 1.0 else if (this < 0.0) -1.0 else 0.0 +fun Double.magnSqr() = if (this >= 0.0) this.sqr() else -this.sqr() +fun Double.sign() = if (this > 0.0) 1.0 else if (this < 0.0) -1.0 else 0.0 fun interpolateLinear(scale: Double, startValue: Double, endValue: Double): Double { if (startValue == endValue) { diff --git a/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt b/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt index ed622499e..d104d65f4 100644 --- a/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt +++ b/src/net/torvald/terrarum/gameactors/HistoricalFigure.kt @@ -1,5 +1,7 @@ package net.torvald.terrarum.gameactors +import net.torvald.random.HQRNG +import net.torvald.terrarum.TerrarumGDX import net.torvald.terrarum.gameworld.WorldTime typealias AnyPlayer = HistoricalFigure @@ -18,6 +20,25 @@ open class HistoricalFigure( realAirFriction: Boolean = false ) : ActorWithPhysics(Actor.RenderOrder.MIDDLE, realAirFriction) { + val historicalFigureIdentifier: Int = generateHistoricalFigureIdentifier() + + private fun generateHistoricalFigureIdentifier(): Int { + fun hasCollision(value: Int) = + try { + TerrarumGDX.ingame!!.historicalFigureIDBucket.contains(value) + } + catch (gameNotInitialisedException: KotlinNullPointerException) { + false + } + + var ret: Int + do { + ret = HQRNG().nextInt() // set new ID + } while (hasCollision(ret)) // check for collision + return ret + } + + init { this.actorValue["_bornyear"] = born.year this.actorValue["_borndays"] = born.yearlyDay