Compare commits

...

12 Commits

Author SHA1 Message Date
minjaesong
82d675fd59 hooey indexing, dont know why 2019-05-10 00:32:49 +09:00
minjaesong
bda2f7ba82 wip3 2019-05-09 16:14:49 +09:00
Minjae Song
dfdd0ef411 wip2 2019-05-09 12:09:48 +09:00
minjaesong
4b3736cfa7 wip lets precalc and recycle numbers 2019-05-09 02:53:48 +09:00
minjaesong
48ba273317 comment 2019-05-08 21:23:42 +09:00
minjaesong
52d6955ffc blocksdrawer now draws wire 2019-05-04 18:19:08 +09:00
minjaesong
08f07e566d adding missing resources/moving zip files to lfs 2019-05-04 04:11:33 +09:00
minjaesong
c423190725 block functions now share one centralised core function 2019-05-04 03:29:59 +09:00
minjaesong
61078cf5a8 debug info for upcoming wires 2019-05-02 00:56:25 +09:00
minjaesong
eafdeddc94 fixed a long bug where item would be used twice but discounted by only one on single click 2019-05-01 23:26:01 +09:00
minjaesong
9050378e81 more comments 2019-04-30 03:06:19 +09:00
minjaesong
fecb20da13 sortedarraylist update; physball breaks the actor render dunno why 2019-04-30 02:52:09 +09:00
41 changed files with 599 additions and 266 deletions

1
.gitattributes vendored
View File

@@ -4,3 +4,4 @@
*.gz filter=lfs diff=lfs merge=lfs -text
*.opus filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text

View File

@@ -1,3 +1,3 @@
<component name="MarkdownNavigator.ProfileManager">
<settings default="" pdf-export="" />
<settings default="" pdf-export="" plain-text-search-scope="Project Files" />
</component>

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

View File

@@ -40,6 +40,8 @@ uniform vec4 colourFilter = vec4(1, 1, 1, 1); // used by WALL to darken it
uniform ivec2 cameraTranslation = ivec2(0, 0);
uniform float drawBreakage = 1f; // set it to 0f to not draw breakage; non-zero value to draw it
ivec2 getTileXY(int tileNumber) {
return ivec2(tileNumber % int(tilesInAtlas.x), tileNumber / int(tilesInAtlas.x));
@@ -108,7 +110,13 @@ void main() {
vec4 finalTile = mix(tileCol, tileAltCol, tilesBlend);
vec4 finalBreakage = texture2D(tilesAtlas, finalUVCoordForBreakage);
vec4 finalBreakage;
if (drawBreakage == 0f) {
finalBreakage = vec4(0f);
}
else {
finalBreakage = texture2D(tilesAtlas, finalUVCoordForBreakage);
}
vec4 finalColor = vec4(mix(finalTile.rgb, finalBreakage.rgb, finalBreakage.a), finalTile.a);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/javadoc/lwjgl3-javadoc.zip LFS Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -93,6 +93,13 @@ public class Color {
this.a = a;
}
public Color (float c) {
this.r = c;
this.g = c;
this.b = c;
this.a = c;
}
/** Constructs a new color using the given color
*
* @param color the color */

View File

@@ -95,6 +95,7 @@ class SpriteAssemblerApp(val gdxWindow: SpriteAssemblerPreview) : JFrame() {
}
panelCode.font = Font(Font.MONOSPACED, Font.PLAIN, 12)
panelCode.text = "Enter your descriptor code here…"
panelAnimationsList.model = DefaultListModel()
panelBodypartsList.model = DefaultListModel()
@@ -278,7 +279,7 @@ class SpriteAssemblerPreview: Game() {
}
gdxClearAndSetBlend(.62f,.79f,1f,1f)
gdxClearAndSetBlend(bgCol)
batch.inUse {

View File

@@ -18,7 +18,6 @@ import com.github.strikerx3.jxinput.XInputDevice;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.torvald.util.ArrayListMap;
import net.torvald.getcpuname.GetCpuName;
import net.torvald.terrarum.blockstats.MinimapComposer;
import net.torvald.terrarum.controller.GdxControllerAdapter;
@@ -34,10 +33,12 @@ import net.torvald.terrarum.worlddrawer.BlocksDrawer;
import net.torvald.terrarum.worlddrawer.LightmapRenderer;
import net.torvald.terrarumsansbitmap.gdx.GameFontBase;
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack;
import net.torvald.util.ArrayListMap;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import static net.torvald.terrarum.TerrarumKt.gdxClearAndSetBlend;
@@ -181,7 +182,7 @@ public class AppLoader implements ApplicationListener {
/** A gamepad. Multiple gamepads may controll this single virtualised gamepad. */
public static TerrarumController gamepad = null;
public static float gamepadDeadzone = 0.2f;
public static float gamepadDeadzone = 0.3f;
/**
@@ -205,9 +206,9 @@ public class AppLoader implements ApplicationListener {
try { processor = GetCpuName.getModelName(); }
catch (IOException e1) { processor = "Unknown"; }
catch (IOException e1) { processor = "Unknown CPU"; }
try { processorVendor = GetCpuName.getCPUID(); }
catch (IOException e2) { processorVendor = "Unknown"; }
catch (IOException e2) { processorVendor = "Unknown CPU"; }
ShaderProgram.pedantic = false;
@@ -284,11 +285,14 @@ public class AppLoader implements ApplicationListener {
private FrameBuffer renderFBO;
public static CommonResourcePool resourcePool;
public static HashSet<File> tempFilePool = new HashSet();
@Override
public void create() {
resourcePool = CommonResourcePool.INSTANCE;
newTempFile("wenquanyi.tga"); // temp file required by the font
// set basis of draw
logoBatch = new SpriteBatch();
@@ -376,13 +380,6 @@ public class AppLoader implements ApplicationListener {
}
/**
* @link http://bilgin.esme.org/BitsAndBytes/KalmanFilterforDummies
*/
private void updateKalmanRenderDelta() {
// moved to LwjglGraphics
}
@Override
public void render() {
Gdx.gl.glDisable(GL20.GL_DITHER);
@@ -544,8 +541,7 @@ public class AppLoader implements ApplicationListener {
ModMgr.INSTANCE.disposeMods();
// delete temp files
new File("./tmp_wenquanyi.tga").delete(); // FIXME this is pretty much ad-hoc
deleteTempfiles();
}
@Override
@@ -688,6 +684,7 @@ public class AppLoader implements ApplicationListener {
System.out.println(String.format("os.name = %s (with identifier %s)", OSName, operationSystem));
System.out.println(String.format("os.version = %s", OSVersion));
System.out.println(String.format("default directory: %s", defaultDir));
System.out.println(String.format("java version = %s", System.getProperty("java.version")));
}
private static void createDirs() {
@@ -701,6 +698,17 @@ public class AppLoader implements ApplicationListener {
//dirs.forEach { if (!it.exists()) it.mkdirs() }
}
public static File newTempFile(String filename) {
File tempfile = new File("./tmp_" + filename);
tempFilePool.add(tempfile);
return tempfile;
}
private static void deleteTempfiles() {
for (File file : tempFilePool) {
file.delete();
}
}
// CONFIG //

View File

@@ -9,7 +9,6 @@ import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.ui.ConsoleWindow
import net.torvald.util.SortedArrayList
import java.util.*
import java.util.concurrent.locks.Lock
/**
@@ -127,6 +126,9 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
/**
* Wire version of terrainChanged() event
*
* @param old previous settings of conduits in bit set format.
* @param new current settings of conduits in bit set format.
*/
open fun queueWireChangedEvent(old: Int, new: Int, position: Long) {
val (x, y) = LandUtil.resolveBlockAddr(world, position)
@@ -143,11 +145,11 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
if (actorContainerActive.size == 0 && actorContainerInactive.size == 0)
throw IllegalArgumentException("Actor with ID $ID does not exist.")
var index = actorContainerActive.binarySearch(ID)
if (index < 0) {
index = actorContainerInactive.binarySearch(ID)
var actor = actorContainerActive.searchFor(ID) { it.referenceID!! }
if (actor == null) {
actor = actorContainerInactive.searchFor(ID) { it.referenceID!! }
if (index < 0) {
if (actor == null) {
/*JOptionPane.showMessageDialog(
null,
"Actor with ID $ID does not exist.",
@@ -156,36 +158,14 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
throw IllegalArgumentException("Actor with ID $ID does not exist.")
}
else
return actorContainerInactive[index]
return actor
}
else
return actorContainerActive[index]
return actor
}
fun ArrayList<*>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID!!)
fun ArrayList<*>.binarySearch(ID: Int): Int {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)!!
if (ID > midVal.hashCode())
low = mid + 1
else if (ID < midVal.hashCode())
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
fun SortedArrayList<*>.binarySearch(actor: Actor) = this.toArrayList().binarySearch(actor.referenceID!!)
fun SortedArrayList<*>.binarySearch(ID: Int) = this.toArrayList().binarySearch(ID)
//fun SortedArrayList<*>.binarySearch(actor: Actor) = this.toArrayList().binarySearch(actor.referenceID!!)
//fun SortedArrayList<*>.binarySearch(ID: Int) = this.toArrayList().binarySearch(ID)
open fun removeActor(ID: Int) = removeActor(getActorByID(ID))
/**
@@ -199,9 +179,9 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
open fun removeActor(actor: Actor?) {
if (actor == null) return
val indexToDelete = actorContainerActive.binarySearch(actor.referenceID!!)
if (indexToDelete >= 0) {
actorContainerActive.removeAt(indexToDelete)
val indexToDelete = actorContainerActive.searchFor(actor.referenceID!!) { it.referenceID!! }
if (indexToDelete != null) {
actorContainerActive.remove(indexToDelete)
}
}
@@ -223,13 +203,13 @@ open class IngameInstance(val batch: SpriteBatch) : Screen {
if (actorContainerActive.size == 0)
false
else
actorContainerActive.binarySearch(ID) >= 0
actorContainerActive.searchFor(ID) { it.referenceID!! } != null
fun isInactive(ID: Int): Boolean =
if (actorContainerInactive.size == 0)
false
else
actorContainerInactive.binarySearch(ID) >= 0
actorContainerInactive.searchFor(ID) { it.referenceID!! } != null
/**
* actorContainerActive extensions

View File

@@ -13,6 +13,7 @@ import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.math.Matrix4
import net.torvald.terrarum.gamecontroller.KeyToggler
import net.torvald.terrarum.ui.BasicDebugInfoWindow
import net.torvald.terrarum.worlddrawer.BlocksDrawer
/**
* Must be called by the App Loader
@@ -37,6 +38,8 @@ object PostProcessor {
private val debugUI = BasicDebugInfoWindow()
private var functionRowHelper = Texture(Gdx.files.internal("assets/graphics/function_row_help.png"))
fun dispose() {
batch.dispose()
shapeRenderer.dispose()
@@ -78,6 +81,25 @@ object PostProcessor {
drawSafeArea()
}
if (KeyToggler.isOn(Input.Keys.F1)) {
batch.color = Color.WHITE
batch.inUse {
it.draw(functionRowHelper,
(AppLoader.screenW - functionRowHelper.width) / 2f,
functionRowHelper.height.toFloat(),
functionRowHelper.width.toFloat(),
functionRowHelper.height * -1f
)
}
}
if (KeyToggler.isOn(Input.Keys.F10)) {
batch.color = Color.WHITE
batch.inUse {
AppLoader.fontSmallNumbers.draw(it, "Wire draw bits: ${BlocksDrawer.drawWires.toString(2)}", 2f, 2f)
}
}
if (KeyToggler.isOn(Input.Keys.F3)) {
if (!debugUI.isOpened && !debugUI.isOpening) debugUI.setAsOpen()
batch.inUse { debugUI.renderUI(batch, camera) }

View File

@@ -12,7 +12,6 @@ import com.badlogic.gdx.graphics.glutils.ShaderProgram
import com.badlogic.gdx.graphics.glutils.ShapeRenderer
import com.badlogic.gdx.utils.GdxRuntimeException
import com.jme3.math.FastMath
import net.torvald.util.CircularArray
import net.torvald.random.HQRNG
import net.torvald.terrarum.AppLoader.*
import net.torvald.terrarum.gameactors.Actor
@@ -23,6 +22,7 @@ import net.torvald.terrarum.worlddrawer.CreateTileAtlas
import net.torvald.terrarum.worlddrawer.WorldCamera
import net.torvald.terrarumsansbitmap.gdx.GameFontBase
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
import net.torvald.util.CircularArray
import org.lwjgl.BufferUtils
import java.io.File
import kotlin.math.absoluteValue
@@ -196,7 +196,6 @@ object Terrarum : Screen {
init {
println("$NAME version ${AppLoader.getVERSION_STRING()}")
println("Java Runtime version ${System.getProperty("java.version")}")
println("LibGDX version ${com.badlogic.gdx.Version.VERSION}")
@@ -613,6 +612,10 @@ fun blendNormal(batch: SpriteBatch) {
// - https://www.andersriggelsen.dk/glblendfunc.php
}
fun gdxClearAndSetBlend(color: Color) {
gdxClearAndSetBlend(color.r, color.g, color.b, color.a)
}
fun gdxClearAndSetBlend(r: Float, g: Float, b: Float, a: Float) {
Gdx.gl.glClearColor(r,g,b,a)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)

View File

@@ -16,8 +16,7 @@ class BlockProp {
var shadeColG = 0f
var shadeColB = 0f
var shadeColA = 0f
lateinit var opacity: Color
var opacity: Color = Color(0)
var strength: Int = 0
var density: Int = 0
@@ -36,7 +35,7 @@ class BlockProp {
var lumColG = 0f
var lumColB = 0f
var lumColA = 0f
lateinit var internalLumCol: Color
var internalLumCol: Color = Color(0)
/**
* @param luminosity

View File

@@ -6,21 +6,52 @@ package net.torvald.terrarum.blockproperties
object Wire {
/* A mapping for World's conduitTypes bits */
/* Must be aligned with the sprite sheet */
const val BIT_NONE = 0
const val BIT_SIGNAL_RED = 1
const val BIT_UTILITY_PROTOTYPE = 2
const val BIT_UTILITY_PROTOTYPE = 2 // logic gates/PCLs/Diodes/Caps/etc.
const val BIT_POWER_LOW = 4
const val BIT_POWER_HIGHT = 8
const val BIT_ETHERNET = 16
const val BIT_THICKNET = 16 // the actual datagramme should be represented by another means than the ConduitFills
const val BIT_PARALLEL_8B = 32 // uses bit-to-mantissa encoding
const val BIT_PARALLEL_16B = 64 // uses bit-to-mantissa encoding. 16 bit half duplex OR 8 bit full duplex
/* A mapping for World's conduitFills[] index */
const val FILL_ID_SIGNAL_RED = 0
/* A mapping for World's WiringNode.fills[] index */
/*const val FILL_ID_SIGNAL_RED = 0
const val FILL_ID_UTILITY_PROTOTYPE = 1
fun bitToConduitFillID(bit: Int) = when(bit) {
BIT_SIGNAL_RED -> FILL_ID_SIGNAL_RED
BIT_UTILITY_PROTOTYPE -> FILL_ID_UTILITY_PROTOTYPE
else -> null
}
}*/
/**
* Encodes a byte to Float's mantissa. Normal value range is 1.0..1.99609375. When decoding, the sign and exponent bits
* must be ignored. (e.g. the encoded float might have not-one-point-something value after "bitwise" add/subtraction.
*
* ```
* exponent ,------- mantissa ------,
* s eeeeeeee bbbbbbbb cccccccc xxxxxxx
* s: sign (ignored)
* e: binary32 exponent (non-zero and non-255)
* b: upper octet
* c: lower octet (zero for Byte representation)
* x: not used, all zero
* ```
*
* MSB of the byte is also the highest bit in the mantissa. Therefore ```0x80``` will be encoded as ```1.5```
*/
fun Byte.toFloatMantissa(): Float = Float.fromBits(0x3F800000 or (this.toInt().and(0xFF) shl 15))
fun Short.toFloatMantissa(): Float = Float.fromBits(0x3F800000 or (this.toInt().and(0xFFFF) shl 7))
/**
* This function does the reversal calculation.
*
* @see net.torvald.terrarum.blockproperties.Wire.toFloatMantissa
*/
fun Float.fromMantissaToByte(): Byte = this.toRawBits().ushr(15).and(0xFF).toByte()
fun Float.fromMantissaToShort(): Short = this.toRawBits().ushr(7).and(0xFFFF).toShort()
}

View File

@@ -44,7 +44,7 @@ object CommandDict {
"kill" to KillActor,
"money" to MoneyDisp,
"screenshot" to TakeScreenshot,
//"resize" to ResizeScreen,
"resize" to ResizeScreen,
// Test codes
"bulletintest" to SetBulletin,

View File

@@ -1,12 +1,11 @@
package net.torvald.terrarum.console
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.Terrarum
object ResizeScreen: ConsoleCommand {
override fun execute(args: Array<String>) {
if (args.size == 3) {
Terrarum.resize(args[1].toInt(), args[2].toInt())
AppLoader.resizeScreen(args[1].toInt(), args[2].toInt())
}
else if (args.size == 2) {
when (args[1]) {

View File

@@ -1,5 +1,6 @@
package net.torvald.terrarum.gameactors
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.itemproperties.ItemCodex.ACTORID_MIN
@@ -34,7 +35,7 @@ abstract class Actor(val renderOrder: RenderOrder) : Comparable<Actor>, Runnable
* Valid RefID is equal to or greater than 16777216.
* @return Reference ID. (16777216-0x7FFF_FFFF)
*/
open var referenceID: ActorID? = null
open var referenceID: ActorID = Terrarum.generateUniqueReferenceID(renderOrder) // once this was nullable without initialiser. If you're going to revert to that, add the reason why this should be nullable.
var actorValue = ActorValue(this) // FIXME cyclic reference on GSON
@Volatile var flagDespawn = false

View File

@@ -6,16 +6,12 @@ import com.badlogic.gdx.InputAdapter
import com.badlogic.gdx.controllers.Controllers
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.controller.TerrarumController
import net.torvald.terrarum.floorInt
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameworld.fmod
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.worlddrawer.CreateTileAtlas
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
import net.torvald.terrarum.worlddrawer.WorldCamera
/**
@@ -57,22 +53,16 @@ class IngameController(val ingame: Ingame) : InputAdapter() {
///////////////////
// Use item: assuming the player has only one effective grip (EquipPosition.HAND_GRIP)
// don't separate Player from this! Physics will break, esp. airborne manoeuvre
if (ingame.canPlayerControl) {
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary")) ||
Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) {
// fire world click events; the event is defined as Ingame's (or any others') WorldClick event
if (ingame.uiContainer.map { if ((it.isOpening || it.isOpened) && it.mouseUp) 1 else 0 }.sum() == 0) { // no UI on the mouse, right?
val player = (Terrarum.ingame!! as Ingame).actorNowPlaying
if (player == null) return
val itemOnGrip = player.inventory.itemEquipped[GameItem.EquipPosition.HAND_GRIP]
itemOnGrip?.let {
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary"))) {
player.consumePrimary(ItemCodex[it]!!)
}
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) {
player.consumeSecondary(ItemCodex[it]!!)
}
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mouseprimary"))) {
ingame.worldPrimaryClickStart(AppLoader.UPDATE_RATE.toFloat())
}
if (Gdx.input.isButtonPressed(AppLoader.getConfigInt("mousesecondary"))) {
ingame.worldSecondaryClickStart(AppLoader.UPDATE_RATE.toFloat())
}
}
}
@@ -192,21 +182,6 @@ class IngameController(val ingame: Ingame) : InputAdapter() {
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
// don't separate Player from this! Physics will break, esp. airborne manoeuvre
if (ingame.canPlayerControl) {
// fire world click events; the event is defined as Ingame's (or any others') WorldClick event
if (ingame.uiContainer.map { if ((it.isOpening || it.isOpened) && it.mouseUp) 1 else 0 }.sum() == 0) { // no UI on the mouse, right?
if (button == AppLoader.getConfigInt("mouseprimary")) {
ingame.worldPrimaryClickStart(AppLoader.UPDATE_RATE.toFloat())
}
if (button == AppLoader.getConfigInt("mousesecondary")) {
ingame.worldSecondaryClickStart(AppLoader.UPDATE_RATE.toFloat())
}
}
}
ingame.uiContainer.forEach { it.touchDown(screenX, screenY, pointer, button) }
return true
}

View File

@@ -2,7 +2,6 @@
package net.torvald.terrarum.gameworld
import com.badlogic.gdx.graphics.Color
import net.torvald.util.SortedArrayList
import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.Block
@@ -11,7 +10,7 @@ import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.modulebasegame.gameworld.WorldSimulator
import net.torvald.terrarum.realestate.LandUtil
import net.torvald.terrarum.serialise.ReadLayerDataZip
import net.torvald.terrarum.toInt
import net.torvald.util.SortedArrayList
import org.dyn4j.geometry.Vector2
import kotlin.math.absoluteValue
import kotlin.math.sign
@@ -74,16 +73,17 @@ open class GameWorld {
val fluidTypes: HashMap<BlockAddress, FluidType>
@TEMzPayload("FlFL", TEMzPayload.INT48_FLOAT_PAIR)
val fluidFills: HashMap<BlockAddress, Float>
@TEMzPayload("CtYP", TEMzPayload.INT48_INT_PAIR)
val conduitTypes: HashMap<BlockAddress, Int> // 1 bit = 1 conduit (pipe/wire) type
@TEMzPayload("CfL", TEMzPayload.INT48_FLOAT_PAIR)
val conduitFills: Array<HashMap<BlockAddress, Float>>
val conduitFills0: HashMap<BlockAddress, Float> // size of liquid packet on the block
get() = conduitFills[0]
val conduitFills1: HashMap<BlockAddress, Float> // size of gas packet on the block
get() = conduitFills[1]
private val wiringNodes = SortedArrayList<WiringNode>()
/**
* Single block can have multiple conduits, different types of conduits are stored separately.
*/
@TEMzPayload("WiNt", TEMzPayload.EXTERNAL_JSON)
private val wirings: HashMap<BlockAddress, SortedArrayList<WiringNode>>
/**
* Used by the renderer. When wirings are updated, `wirings` and this properties must be synchronised.
*/
private val wiringBlocks: HashMap<BlockAddress, Int>
//public World physWorld = new World( new Vec2(0, -Terrarum.game.gravitationalAccel) );
//physics
@@ -114,13 +114,13 @@ open class GameWorld {
layerTerrainLowBits = PairedMapLayer(width, height)
layerWallLowBits = PairedMapLayer(width, height)
wallDamages = HashMap<BlockAddress, Float>()
terrainDamages = HashMap<BlockAddress, Float>()
fluidTypes = HashMap<BlockAddress, FluidType>()
fluidFills = HashMap<BlockAddress, Float>()
wallDamages = HashMap()
terrainDamages = HashMap()
fluidTypes = HashMap()
fluidFills = HashMap()
conduitTypes = HashMap<BlockAddress, Int>()
conduitFills = Array(16) { HashMap<BlockAddress, Float>() }
wiringBlocks = HashMap()
wirings = HashMap()
// temperature layer: 2x2 is one cell
//layerThermal = MapLayerHalfFloat(width, height, averageTemperature)
@@ -148,8 +148,8 @@ open class GameWorld {
fluidTypes = layerData.fluidTypes
fluidFills = layerData.fluidFills
conduitTypes = HashMap<BlockAddress, Int>()
conduitFills = Array(16) { HashMap<BlockAddress, Float>() }
wiringBlocks = HashMap()
wirings = HashMap()
spawnX = layerData.spawnX
spawnY = layerData.spawnY
@@ -210,10 +210,6 @@ open class GameWorld {
terrain * PairedMapLayer.RANGE + terrainDamage
}
fun getWires(x: Int, y: Int): Int {
return conduitTypes.getOrDefault(LandUtil.getBlockAddr(this, x, y), 0)
}
fun getWallLowBits(x: Int, y: Int): Int? {
val (x, y) = coerceXY(x, y)
return layerWallLowBits.getData(x, y)
@@ -230,10 +226,11 @@ open class GameWorld {
* *
* @param y
* *
* @param combinedTilenum (tilenum * 16) + damage
* @param combinedTilenum Item id of the wall block. Less-than-4096-value is permitted.
*/
fun setTileWall(x: Int, y: Int, combinedTilenum: Int) {
val (x, y) = coerceXY(x, y)
val combinedTilenum = combinedTilenum % GameWorld.TILES_SUPPORTED // does work without this, but to be safe...
setTileWall(x, y, (combinedTilenum / PairedMapLayer.RANGE).toByte(), combinedTilenum % PairedMapLayer.RANGE)
}
@@ -243,7 +240,7 @@ open class GameWorld {
* *
* @param y
* *
* @param combinedTilenum (tilenum * 16) + damage
* @param combinedTilenum Item id of the terrain block, <4096
*/
fun setTileTerrain(x: Int, y: Int, combinedTilenum: Int) {
val (x, y) = coerceXY(x, y)
@@ -291,25 +288,35 @@ open class GameWorld {
Terrarum.ingame?.queueWireChangedEvent(oldWire, tile.toUint(), LandUtil.getBlockAddr(this, x, y))
}*/
/**
* Overrides entire bits with given value. DO NOT USE THIS if you don't know what this means, you'll want to use setWire().
* Besides, this function won't fire WireChangedEvent
*/
fun setWires(x: Int, y: Int, wireBits: Int) {
val (x, y) = coerceXY(x, y)
conduitTypes[LandUtil.getBlockAddr(this, x, y)] = wireBits
fun getWiringBlocks(x: Int, y: Int): Int {
return wiringBlocks.getOrDefault(LandUtil.getBlockAddr(this, x, y), 0)
}
fun getAllConduitsFrom(x: Int, y: Int): SortedArrayList<WiringNode>? {
return wirings.get(LandUtil.getBlockAddr(this, x, y))
}
/**
* Sets single bit for given tile. YOU'LL WANT TO USE THIS instead of setWires()
* @param selectedWire wire-bit to modify, must be power of two
* @param conduitTypeBit defined in net.torvald.terrarum.blockproperties.Wire, always power-of-two
*/
fun setWire(x: Int, y: Int, selectedWire: Int, bitToSet: Boolean) {
val oldWireBits = getWires(x, y)
val oldStatus = getWires(x, y) or selectedWire != 0
if (oldStatus != bitToSet) {
setWires(x, y, (oldWireBits and selectedWire.inv()) or (selectedWire * oldStatus.toInt()))
Terrarum.ingame?.queueWireChangedEvent(selectedWire * oldStatus.toInt(), selectedWire * bitToSet.toInt(), LandUtil.getBlockAddr(this, x, y))
fun getConduitByTypeFrom(x: Int, y: Int, conduitTypeBit: Int): WiringNode? {
val conduits = getAllConduitsFrom(x, y)
return conduits?.searchFor(conduitTypeBit) { it.typeBitMask }
}
fun addNewConduitTo(x: Int, y: Int, node: WiringNode) {
val blockAddr = LandUtil.getBlockAddr(this, x, y)
// check for existing type of conduit
// if there's no duplicate...
if (getWiringBlocks(x, y) and node.typeBitMask == 0) {
// store as-is
wirings.getOrPut(blockAddr) { SortedArrayList() }.add(node)
// synchronise wiringBlocks
wiringBlocks[blockAddr] = (wiringBlocks[blockAddr] ?: 0) or node.typeBitMask
}
else {
TODO("need overwriting policy for existing conduit node")
}
}
@@ -321,7 +328,7 @@ open class GameWorld {
return getTileFromWall(x, y)
}
else if (mode == WIRE) {
return getWires(x, y)
return getWiringBlocks(x, y)
}
else
throw IllegalArgumentException("illegal mode input: " + mode.toString())
@@ -482,12 +489,17 @@ open class GameWorld {
override fun toString() = "Fluid type: ${type.value}, amount: $amount"
}
private data class WiringNode(
/**
* Connection rules: connect to all nearby, except:
*
* If the wire allows 3- or 4-way connection, make such connection.
* If the wire does not allow them (e.g. wire bridge, thicknet), connect top-bottom and left-right nodes.
*/
data class WiringNode(
val position: BlockAddress,
/** One defined in WireCodex, always power of two */
val typeBitMask: Int,
var fills: Float = 0f,
var connectedNodes: ArrayList<WiringNode>
var fills: Float = 0f
) : Comparable<WiringNode> {
override fun compareTo(other: WiringNode): Int {
return (this.position - other.position).sign
@@ -535,6 +547,9 @@ inline class FluidType(val value: Int) {
*/
annotation class TEMzPayload(val payloadName: String, val arg: Int) {
companion object {
const val EXTERNAL_JAVAPROPERTIES = -3
const val EXTERNAL_CSV = -2
const val EXTERNAL_JSON = -1
const val EIGHT_MSB = 0
const val FOUR_LSB = 1
const val INT48_FLOAT_PAIR = 2

View File

@@ -137,7 +137,7 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) {
internal var showSelection = true
val blockPointingCursor = object : ActorWithBody(Actor.RenderOrder.OVERLAY) {
override var referenceID: ActorID? = 1048575 // custom refID
override var referenceID: ActorID = 1048575 // custom refID
override val hitbox = Hitbox(0.0, 0.0, 16.0, 16.0)
@@ -177,7 +177,7 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) {
private var _testMarkerDrawCalls = 0L
private fun generateNewBlockMarkerVisible(x: Int, y: Int) = object : ActorWithBody(Actor.RenderOrder.OVERLAY) {
override var referenceID: ActorID? = blockPosToRefID(x, y) // custom refID
override var referenceID: ActorID = blockPosToRefID(x, y) // custom refID
override val hitbox = Hitbox(x * 16.0, y * 16.0, 16.0, 16.0)
override fun drawBody(batch: SpriteBatch) {

View File

@@ -1,14 +1,16 @@
package net.torvald.terrarum.modulebasegame
import net.torvald.terrarum.*
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.AppLoader.IS_DEVELOPMENT_BUILD
import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.ModMgr
import net.torvald.terrarum.ModuleEntryPoint
import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.MaterialCodex
import net.torvald.terrarum.modulebasegame.imagefont.WatchFont
import net.torvald.terrarum.modulebasegame.items.BlockBase
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
@@ -74,48 +76,11 @@ class EntryPoint : ModuleEntryPoint() {
}
override fun startPrimaryUse(delta: Float): Boolean {
val ingame = Terrarum.ingame!! as Ingame
return BlockBase.blockStartPrimaryUse(this, i, delta)
}
val mousePoint = Point2d(Terrarum.mouseTileX.toDouble(), Terrarum.mouseTileY.toDouble())
// check for collision with actors (BLOCK only)
if (this.inventoryCategory == Category.BLOCK) {
var ret1 = true
ingame.actorContainerActive.forEach {
if (it is ActorWBMovable && it.hIntTilewiseHitbox.intersects(mousePoint))
ret1 = false // return is not allowed here
}
if (!ret1) return ret1
}
// return false if the tile is already there
if (this.inventoryCategory == Category.BLOCK &&
this.dynamicID == ingame.world.getTileFromTerrain(Terrarum.mouseTileX, Terrarum.mouseTileY) ||
this.inventoryCategory == Category.WALL &&
this.dynamicID - ItemCodex.ITEM_WALLS.start == ingame.world.getTileFromWall(Terrarum.mouseTileX, Terrarum.mouseTileY) ||
this.inventoryCategory == Category.WIRE &&
1.shl(this.dynamicID - ItemCodex.ITEM_WIRES.start) and (ingame.world.getWires(Terrarum.mouseTileX, Terrarum.mouseTileY) ?: 0) != 0
)
return false
// filter passed, do the job
// FIXME this is only useful for Player
if (i in ItemCodex.ITEM_TILES) {
ingame.world.setTileTerrain(
Terrarum.mouseTileX,
Terrarum.mouseTileY,
i
)
}
else {
ingame.world.setTileWall(
Terrarum.mouseTileX,
Terrarum.mouseTileY,
i
)
}
return true
override fun effectWhenEquipped(delta: Float) {
BlockBase.blockEffectWhenEquipped(delta)
}
}
}

View File

@@ -394,7 +394,9 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
override fun worldPrimaryClickStart(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP)
ItemCodex[itemOnGrip]?.startPrimaryUse(delta)
val consumptionSuccessful = ItemCodex[itemOnGrip]?.startPrimaryUse(delta) ?: false
if (consumptionSuccessful)
actorNowPlaying?.inventory?.consumeItem(ItemCodex[itemOnGrip]!!)
}
override fun worldPrimaryClickEnd(delta: Float) {
@@ -404,7 +406,9 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
override fun worldSecondaryClickStart(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP)
ItemCodex[itemOnGrip]?.startSecondaryUse(delta)
val consumptionSuccessful = ItemCodex[itemOnGrip]?.startSecondaryUse(delta) ?: false
if (consumptionSuccessful)
actorNowPlaying?.inventory?.consumeItem(ItemCodex[itemOnGrip]!!)
}
override fun worldSecondaryClickEnd(delta: Float) {
@@ -747,8 +751,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
if (actor.referenceID == theRealGamer.referenceID || actor.referenceID == 0x51621D) // do not delete this magic
throw RuntimeException("Attempted to remove player.")
val indexToDelete = actorContainerActive.binarySearch(actor.referenceID!!)
if (indexToDelete >= 0) {
val indexToDelete = actorContainerActive.searchForIndex(actor.referenceID!!) { it.referenceID!! }
if (indexToDelete != null) {
printdbg(this, "Removing actor $actor")
printStackTrace()
@@ -783,6 +787,28 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
}
}
private fun ArrayList<*>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID!!)
private fun ArrayList<*>.binarySearch(ID: Int): Int {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)!!
if (ID > midVal.hashCode())
low = mid + 1
else if (ID < midVal.hashCode())
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
/**
* Check for duplicates, append actor and sort the list
*/

View File

@@ -94,7 +94,7 @@ object IngameRenderer {
LightmapRenderer.fireRecalculateEvent(actorsRenderBehind, actorsRenderFront, actorsRenderMidTop, actorsRenderMiddle, actorsRenderOverlay)
prepLightmapRGBA()
BlocksDrawer.renderData(selectedWireBitToDraw)
BlocksDrawer.renderData()
drawToRGB(actorsRenderBehind, actorsRenderMiddle, actorsRenderMidTop, actorsRenderFront, particlesContainer)
drawToA(actorsRenderBehind, actorsRenderMiddle, actorsRenderMidTop, actorsRenderFront, particlesContainer)
drawOverlayActors(actorsRenderOverlay)
@@ -214,9 +214,6 @@ object IngameRenderer {
// works but some UI elements have wrong transparency -> should be fixed with Terrarum.gdxCleanAndSetBlend -- Torvald 2019-01-12
blendNormal(batch)
batch.color = Color.WHITE
selectedWireBitToDraw = 0
}

View File

@@ -177,7 +177,9 @@ class ActorInventory(@Transient val actor: Pocketed, var maxCapacity: Int, var c
false
fun consumeItem(actor: Actor, item: GameItem) {
fun consumeItem(item: GameItem) {
val actor = this.actor as Actor
if (item.stackable && !item.isDynamic) {
remove(item, 1)
}

View File

@@ -85,7 +85,7 @@ object PlayerBuilderSigrid {
inventory.add(8448) // copper pick
inventory.add(8449) // iron pick
inventory.add(8450) // steel pick
inventory.add(8466) // wire piece
inventory.add(8466, 9995) // wire piece
inventory.add(9000) // TEST water bucket
inventory.add(9001) // TEST lava bucket
}

View File

@@ -1,7 +1,6 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.itemproperties.ItemID
@@ -79,15 +78,4 @@ interface Pocketed {
fun hasItem(item: GameItem) = inventory.contains(item.dynamicID)
fun hasItem(id: Int) = inventory.contains(id)
fun consumePrimary(item: GameItem) {
if (item.startPrimaryUse(AppLoader.UPDATE_RATE.toFloat())) {
inventory.consumeItem(this as Actor, item) // consume on successful
}
}
fun consumeSecondary(item: GameItem) {
if (item.startSecondaryUse(AppLoader.UPDATE_RATE.toFloat()))
inventory.consumeItem(this as Actor, item) // consume on successful
}
}

View File

@@ -0,0 +1,97 @@
package net.torvald.terrarum.modulebasegame.items
import net.torvald.terrarum.Point2d
import net.torvald.terrarum.Point2i
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.Ingame
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.realestate.LandUtil
/**
* Created by minjaesong on 2019-05-02.
*/
object BlockBase {
/**
* @param dontEncaseActors when set to true, blocks won't be placed where Actors are. You will want to set it false
* for wire items, otherwise you want it to be true.
*/
fun blockStartPrimaryUse(gameItem: GameItem, itemID: Int, delta: Float): Boolean {
val ingame = Terrarum.ingame!! as Ingame
val mousePoint = Point2d(Terrarum.mouseTileX.toDouble(), Terrarum.mouseTileY.toDouble())
val mouseTile = Point2i(Terrarum.mouseTileX, Terrarum.mouseTileY)
// check for collision with actors (BLOCK only)
if (gameItem.inventoryCategory == GameItem.Category.BLOCK) {
var ret1 = true
ingame.actorContainerActive.forEach {
if (it is ActorWBMovable && it.hIntTilewiseHitbox.intersects(mousePoint))
ret1 = false // return is not allowed here
}
if (!ret1) return ret1
}
// return false if the tile is already there
if (gameItem.inventoryCategory == GameItem.Category.BLOCK &&
gameItem.dynamicID == ingame.world.getTileFromTerrain(mouseTile.x, mouseTile.y) ||
gameItem.inventoryCategory == GameItem.Category.WALL &&
gameItem.dynamicID - ItemCodex.ITEM_WALLS.start == ingame.world.getTileFromWall(mouseTile.x, mouseTile.y)
)
return false
// filter passed, do the job
// FIXME this is only useful for Player
if (itemID in ItemCodex.ITEM_TILES) {
ingame.world.setTileTerrain(
mouseTile.x,
mouseTile.y,
itemID
)
}
else {
ingame.world.setTileWall(
mouseTile.x,
mouseTile.y,
itemID
)
}
return true
}
fun blockEffectWhenEquipped(delta: Float) {
IngameRenderer.selectedWireBitToDraw = 0
}
fun wireStartPrimaryUse(gameItem: GameItem, wireTypeBit: Int, delta: Float): Boolean {
val ingame = Terrarum.ingame!! as Ingame
val mouseTile = Point2i(Terrarum.mouseTileX, Terrarum.mouseTileY)
// return false if the tile is already there
if (ingame.world.getWiringBlocks(mouseTile.x, mouseTile.y) and wireTypeBit != 0)
return false
// filter passed, do the job
// FIXME this is only useful for Player
ingame.world.addNewConduitTo(
mouseTile.x,
mouseTile.y,
GameWorld.WiringNode(
LandUtil.getBlockAddr(ingame.world, mouseTile.x, mouseTile.y),
wireTypeBit,
0f
)
)
return true
}
fun wireEffectWhenEquipped(typebit: Int, delta: Float) {
IngameRenderer.selectedWireBitToDraw = typebit
}
}

View File

@@ -6,7 +6,6 @@ import net.torvald.terrarum.blockproperties.Wire
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.itemproperties.ItemID
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
@@ -32,11 +31,10 @@ class WirePieceSignalWire(override val originalID: ItemID) : GameItem() {
}
override fun startPrimaryUse(delta: Float): Boolean {
return super.startPrimaryUse(delta)
return BlockBase.wireStartPrimaryUse(this, Wire.BIT_SIGNAL_RED, delta)
}
override fun effectWhenEquipped(delta: Float) {
IngameRenderer.selectedWireBitToDraw = Wire.BIT_SIGNAL_RED
//println("wires!")
BlockBase.wireEffectWhenEquipped(Wire.BIT_SIGNAL_RED, delta)
}
}

Binary file not shown.

View File

@@ -150,11 +150,13 @@ class BasicDebugInfoWindow : UICanvas() {
printLine(batch, 8, "light@cursor $ccG$lightVal")
if (ingame != null) {
val wallNum = ingame!!.world.getTileFromWall(mouseTileX, mouseTileY) ?: -1
val tileNum = ingame!!.world.getTileFromTerrain(mouseTileX, mouseTileY) ?: -1
val wireNum = ingame!!.world.getWiringBlocks(mouseTileX, mouseTileY)
val fluid = ingame!!.world.getFluid(mouseTileX, mouseTileY)
printLine(batch, 9, "tile@cursor $ccG$tileNum ($mtX, $mtY)")
printLine(batch, 10, "fluid@cursor ${ccY}Type $ccM${fluid.type.value} ${ccY}Fill $ccG${fluid.amount}f")
printLine(batch, 9, "tile@cursor ${ccO}W$ccG$wallNum ${ccO}T$ccG$tileNum ${ccO}C$ccG${wireNum.toString(2)} $ccY($mtX, $mtY)")
printLine(batch, 10, "fluid@cursor ${ccO}Type $ccG${fluid.type.value} ${ccO}Fill $ccG${fluid.amount}f")
}

View File

@@ -27,6 +27,8 @@ import kotlin.math.roundToInt
* in the shader (tiling.frag). This will not be a problem in the base game, but if you are modifying
* this engine for your project, you must edit the shader program accordingly.
*
* To render and draw images, modify the ```selectedWireBitToDraw``` (bitset) property from the IngameRenderer.
*
* Created by minjaesong on 2016-01-19.
*/
internal object BlocksDrawer {
@@ -121,10 +123,8 @@ internal object BlocksDrawer {
TextureRegionPack(Texture(CreateTileAtlas.atlasWinter), TILE_SIZE, TILE_SIZE)
)
// unzip tga.gz for tilesWire and tilesFluid
//TODO
tilesWire = TextureRegionPack(Texture(8, 8, Pixmap.Format.RGBA8888), 1, 1)
//TODO make wire work with the TileAtlas system
tilesWire = TextureRegionPack(ModMgr.getGdxFile("basegame", "blocks/wire.tga"), TILE_SIZE, TILE_SIZE)
tilesFluid = TextureRegionPack(Texture(CreateTileAtlas.atlasFluid), TILE_SIZE, TILE_SIZE)
@@ -185,7 +185,7 @@ internal object BlocksDrawer {
// NO draw lightmap using colour filter, actors must also be hidden behind the darkness
///////////////////////////////////////////
internal fun renderData(wireBit: Int) {
internal fun renderData() {
try {
drawTIME_T = (world as GameWorldExtension).time.TIME_T - (WorldTime.DAY_LENGTH * 15) // offset by -15 days
@@ -198,7 +198,7 @@ internal object BlocksDrawer {
drawTiles(WALL)
drawTiles(TERRAIN) // regular tiles
drawTiles(WIRE, wireBit)
drawTiles(WIRE)
drawTiles(FLUID)
}
@@ -215,6 +215,9 @@ internal object BlocksDrawer {
renderUsingBuffer(FLUID, projectionMatrix)
}
var drawWires = 0
private set
internal fun drawFront(projectionMatrix: Matrix4, drawWires: Int) {
// blend mul
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
@@ -228,8 +231,9 @@ internal object BlocksDrawer {
gdxSetBlendNormal()
this.drawWires = drawWires
if (drawWires != 0) {
//println("drawing wires")
//println("Wires! draw: $drawWires") // use F10 instead
renderUsingBuffer(WIRE, projectionMatrix)
}
}
@@ -261,14 +265,12 @@ internal object BlocksDrawer {
/**
* Turns bitmask-with-single-bit-set into its bit index. The LSB is counted as 1, and thus the index starts at one.
* @return 0 -> null, 1 -> 0, 2 -> 1, 4 -> 2, 8 -> 3, 16 -> 4, ...
* @return 0 -> -1, 1 -> 0, 2 -> 1, 4 -> 2, 8 -> 3, 16 -> 4, ...
*/
private fun Int.toBitOrd(): Int? =
if (this > 0 && !FastMath.isPowerOfTwo(this)) throw IllegalArgumentException("value must be power of two: $this")
else {
val k = FastMath.intLog2(this, -1)
if (k == -1) null else k
}
private fun Int.toBitOrd(): Int {
val k = FastMath.intLog2(this, -1)
return k
}
/**
* Writes to buffer. Actual draw code must be called after this operation.
@@ -276,7 +278,7 @@ internal object BlocksDrawer {
* @param drawModeTilesBlendMul If current drawing mode is MULTIPLY. Doesn't matter if mode is FLUID.
* @param wire coduitTypes bit that is selected to be drawn. Must be the power of two.
*/
private fun drawTiles(mode: Int, wireBit: Int = 0) {
private fun drawTiles(mode: Int) {
// can't be "WorldCamera.y / TILE_SIZE":
// ( 3 / 16) == 0
// (-3 / 16) == -1 <-- We want it to be '-1', not zero
@@ -305,7 +307,7 @@ internal object BlocksDrawer {
val thisTile = when (mode) {
WALL -> world.getTileFromWall(x, y)
TERRAIN -> world.getTileFromTerrain(x, y)
WIRE -> world.getWires(x, y).and(wireBit).toBitOrd()
WIRE -> world.getWiringBlocks(x, y).and(drawWires).toBitOrd() * 16
FLUID -> world.getFluid(x, y).type.abs()
else -> throw IllegalArgumentException()
}
@@ -316,6 +318,9 @@ internal object BlocksDrawer {
val nearbyTilesInfo = if (mode == FLUID) {
getNearbyTilesInfoFluids(x, y)
}
else if (mode == WIRE) {
getNearbyWiringInfo(x, y, thisTile)
}
else if (isPlatform(thisTile)) {
getNearbyTilesInfoPlatform(x, y)
}
@@ -336,10 +341,16 @@ internal object BlocksDrawer {
val tileNumberBase =
if (mode == FLUID)
CreateTileAtlas.fluidToTileNumber(world.getFluid(x, y))
else if (mode == WIRE)
thisTile
else
renderTag.tileNumber
val tileNumber = if (thisTile == 0) 0
val tileNumber = if (mode != WIRE && thisTile == 0) 0
// special case: fluids
else if (mode == FLUID) tileNumberBase + connectLut47[nearbyTilesInfo]
// special case: wires
else if (mode == WIRE) tileNumberBase + connectLut16[nearbyTilesInfo]
// rest of the cases: terrain and walls
else tileNumberBase + when (renderTag.maskType) {
CreateTileAtlas.RenderTag.MASK_NA -> 0
CreateTileAtlas.RenderTag.MASK_16 -> connectLut16[nearbyTilesInfo]
@@ -363,7 +374,11 @@ internal object BlocksDrawer {
// draw a tile
if (mode == FLUID) {
if (mode == WIRE && thisTile < 0) {
// no wire here, draw block id 255 (bottom right)
writeToBuffer(mode, bufferX, bufferY, 15, 15, 0)
}
else if (mode == FLUID || mode == WIRE) {
writeToBuffer(mode, bufferX, bufferY, thisTileX, thisTileY, 0)
}
else {
@@ -402,6 +417,25 @@ internal object BlocksDrawer {
return ret
}
/**
* @param wire -1 for none, 0 for signal red, 1 for untility prototype, 2 for low power, 3 for high power;
* log of bits defined in [net.torvald.terrarum.blockproperties.Wire]
*
* @return offset from the spritesheet's "base" tile number, 0..15.
*/
private fun getNearbyWiringInfo(x: Int, y: Int, wire: Int): Int {
val nearbyTiles = getNearbyTilesPos(x, y).map { world.getWiringBlocks(it.x, it.y).and(drawWires).toBitOrd() * 16 }
var ret = 0
for (i in 0 until nearbyTiles.size) {
if (nearbyTiles[i] == wire) {
ret += (1 shl i) // add 1, 2, 4, 8 for i = 0, 1, 2, 3
}
}
return ret
}
private fun getNearbyTilesInfoConMutual(x: Int, y: Int, mode: Int): Int {
val nearbyTiles = getNearbyTilesPos(x, y).map { world.getTileFrom(mode, it.x, it.y) ?: Block.NULL }
@@ -601,6 +635,7 @@ internal object BlocksDrawer {
else
0f
)
shader.setUniformf("drawBreakage", if (mode == WIRE) 0f else 1f)
tilesQuad.render(shader, GL20.GL_TRIANGLES)
shader.end()

View File

@@ -14,7 +14,6 @@ import net.torvald.terrarum.concurrent.ParallelUtils.sliceEvenly
import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameactors.ActorWithBody
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarum.gameworld.BlockAddress
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.IngameRenderer
import net.torvald.terrarum.realestate.LandUtil
@@ -34,6 +33,7 @@ import net.torvald.terrarum.realestate.LandUtil
*/
object LightmapRenderer {
private const val TILE_SIZE = CreateTileAtlas.TILE_SIZE
private const val SQRT2 = 1.41421356f
private var world: GameWorld = GameWorld.makeNullWorld()
private lateinit var lightCalcShader: ShaderProgram
@@ -83,7 +83,51 @@ object LightmapRenderer {
// it utilises alpha channel to determine brightness of "glow" sprites (so that alpha channel works like UV light)
//private val lightmap: Array<Array<Color>> = Array(LIGHTMAP_HEIGHT) { Array(LIGHTMAP_WIDTH, { Color(0f,0f,0f,0f) }) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
private val lightmap: Array<Color> = Array(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0f,0f,0f,0f) } // Can't use framebuffer/pixmap -- this is a fvec4 array, whereas they are ivec4.
private val lanternMap = HashMap<BlockAddress, Color>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
/**
* Sstores both the block light sources and actor light sources.
*/
//private val lightSourcesMap = HashMap<BlockAddress, Color>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
private val lightSourcesMap = Array<Color>(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0) }
private fun toLightOffset(y: Int, x: Int): Int {
val xpos = x - for_x_start + overscan_open
val ypos = y - for_y_start + overscan_open
val index = ypos * LIGHTMAP_WIDTH + xpos
if (index >= lightSourcesMap.size)
println("$x, $y | $xpos, $ypos | $for_x_start, $for_y_start | $index")
return index
}
private fun getLightSourceMap(worldX: Int, worldY: Int) =
lightSourcesMap[toLightOffset(worldX, worldY)]
private fun setLightSourcesMap(worldX: Int, worldY: Int, value: Color) {
lightSourcesMap[toLightOffset(worldX, worldY)] = value
}
private fun mixLightSourcesMap(worldX: Int, worldY: Int, value: Color) {
lightSourcesMap[toLightOffset(worldX, worldY)].maxAndAssign(value)
}
private fun clearLightSourcesOffset() {
for (i in 0 until lightSourcesMap.size) { lightSourcesMap[i] = Color(0) }
}
/**
* Pair of: Regular shade, the former shade times 1.4142
*/
//private val shadesMap = HashMap<BlockAddress, Pair<Color, Color>>((Terrarum.ingame?.ACTORCONTAINER_INITIAL_SIZE ?: 2) * 4)
private val shadesMap = Array<Pair<Color, Color>>(LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT) { Color(0) to Color(0) }
private fun getShadesMap(worldX: Int, worldY: Int) =
shadesMap[toLightOffset(worldX, worldY)]
private fun setShadesMap(worldX: Int, worldY: Int, value: Color) {
shadesMap[toLightOffset(worldX, worldY)] = value to (value.cpy().mul(SQRT2))
}
private fun mixShadesMap(worldX: Int, worldY: Int, value: Color) {
val field = shadesMap[toLightOffset(worldX, worldY)]
val baseval = field.first.maxAndAssign(value)
field.second.set(baseval); field.second.mul(SQRT2)
}
private fun clearShadesMap() {
for (i in 0 until shadesMap.size) { shadesMap[i] = Color(0) to Color(0) }
}
init {
printdbg(this, "Overscan open: $overscan_open; opaque: $overscan_opaque")
@@ -201,8 +245,15 @@ object LightmapRenderer {
//println("$for_x_start..$for_x_end, $for_x\t$for_y_start..$for_y_end, $for_y")
AppLoader.measureDebugTime("Renderer.Lanterns") {
buildLanternmap(actorContainers)
// set sunlight
sunLight.set(world.globalLight); sunLight.mul(DIV_FLOAT)
AppLoader.measureDebugTime("Renderer.LightPreload") {
// this is to recycle pre-calculated lights and shades for all 4 rounds.
// the old code always re-calculates them (calls 'getLightsAndShades()') for every blocks for every round.
// the light source information can also be used to create no-op mask? I'm sceptical about that, there must
// exist some edge cases like the other time...
buildLightSourcesMap(actorContainers)
} // usually takes 3000 ns
/*
@@ -219,9 +270,6 @@ object LightmapRenderer {
* If you run only 4 sets, orthogonal/diagonal artefacts are bound to occur,
*/
// set sunlight
sunLight.set(world.globalLight); sunLight.mul(DIV_FLOAT)
// set no-op mask from solidity of the block
AppLoader.measureDebugTime("Renderer.LightNoOpMask") {
noopMask.clear()
@@ -245,6 +293,7 @@ object LightmapRenderer {
// The skipping is dependent on how you get ambient light,
// in this case we have 'spillage' due to the fact calculate() samples 3x3 area.
AppLoader.measureDebugTime("Renderer.LightTotal") {
// Round 2
for (y in for_y_end + overscan_open downTo for_y_start) {
@@ -331,8 +380,22 @@ object LightmapRenderer {
internal data class ThreadedLightmapUpdateMessage(val x: Int, val y: Int)
private fun buildLanternmap(actorContainers: Array<out List<ActorWithBody>?>) {
lanternMap.clear()
private var _block = 0
private var _blockProp = BlockCodex[0]
private var _wall = 0
private var _blockLum = Color(0)
private var _fluid = GameWorld.FluidInfo(Fluid.NULL, 0f)
private var _fluidProp = BlockCodex[_fluid.type]
private var _tileAddr = 0L
private var _fluidAmountToCol = Color(0)
private fun buildLightSourcesMap(actorContainers: Array<out List<ActorWithBody>?>) {
clearLightSourcesOffset()
clearShadesMap()
//lightSourcesMapOffset.set(for_x_start - overscan_open, for_y_start + overscan_open)
// lanterns from actors
actorContainers.forEach { actorContainer ->
actorContainer?.forEach {
if (it is Luminous && it is ActorWBMovable) {
@@ -349,17 +412,55 @@ object LightmapRenderer {
val normalisedColor = it.color//.cpy().mul(DIV_FLOAT)
lanternMap[LandUtil.getBlockAddr(world, x, y)] = normalisedColor
//lanternMap[Point2i(x, y)] = normalisedColor
//lightSourcesMap[LandUtil.getBlockAddr(world, x, y)] = normalisedColor
setLightSourcesMap(x, y, normalisedColor)
//lightSourcesMap[Point2i(x, y)] = normalisedColor
// Q&D fix for Roundworld anomaly
//lanternMap[Point2i(x + world.width, y)] = normalisedColor
//lanternMap[Point2i(x - world.width, y)] = normalisedColor
//lightSourcesMap[Point2i(x + world.width, y)] = normalisedColor
//lightSourcesMap[Point2i(x - world.width, y)] = normalisedColor
}
}
}
}
}
}
// light sources and shades from a block
for (y in for_y_start - overscan_open..for_y_end + overscan_open) {
for (x in for_x_start - overscan_open..for_x_end + overscan_open) {
_block = world.getTileFromTerrain(x, y) ?: Block.STONE
_blockProp = BlockCodex[_block]
_wall = world.getTileFromWall(x, y) ?: Block.STONE
_blockLum = _blockProp.luminosity
_fluid = world.getFluid(x, y)
_fluidProp = BlockCodex[_fluid.type]
_tileAddr = LandUtil.getBlockAddr(world, x, y)
_fluidAmountToCol = Color(_fluid.amount.coerceIn(0f, 1f))
// light sources from blocks //
// mix with the existing value
mixLightSourcesMap(x, y, _blockLum)
// see if sunlight is applicable. If it does, mix with the existing value
if ((_block == AIR && _wall == AIR) || (_blockLum.nonZero() && _wall == AIR)) {
mixLightSourcesMap(x, y, sunLight)
}
// mix the lava light
if (_fluid.type != Fluid.NULL) {
mixLightSourcesMap(x, y, _fluidProp.luminosity mul _fluidAmountToCol)
}
// deal with the shades //
// shade from the block
setShadesMap(x, y, _blockProp.opacity)
// shade from the fluid
mixShadesMap(x, y, _fluidProp.opacity mul _fluidAmountToCol)
}
}
}
private fun buildNoopMask() {
@@ -394,14 +495,14 @@ object LightmapRenderer {
//private val ambientAccumulator = Color(0f,0f,0f,0f)
private val lightLevelThis = Color(0)
private var thisTerrain = 0
private var thisFluid = GameWorld.FluidInfo(Fluid.NULL, 0f)
private val fluidAmountToCol = Color(0)
private var thisWall = 0
private val thisTileLuminosity = Color(0)
private val thisTileOpacity = Color(0)
private val thisTileOpacity2 = Color(0) // thisTileOpacity * sqrt(2)
//private val lightLevelThis = Color(0)
//private var thisTerrain = 0
//private var thisFluid = GameWorld.FluidInfo(Fluid.NULL, 0f)
//private val fluidAmountToCol = Color(0)
//private var thisWall = 0
//private val thisTileLuminosity = Color(0)
//private val thisTileOpacity = Color(0)
//private val thisTileOpacity2 = Color(0) // thisTileOpacity * sqrt(2)
private val sunLight = Color(0)
/**
@@ -415,7 +516,10 @@ object LightmapRenderer {
* - thisTileOpacity2
* - sunlight
*/
private fun getLightsAndShades(x: Int, y: Int) {
/*private fun getLightsAndShades(x: Int, y: Int) {
// TODO lanternmap now also holds light sources (incl. sunlight)
lightLevelThis.set(colourNull)
thisTerrain = world.getTileFromTerrain(x, y) ?: Block.STONE
thisFluid = world.getFluid(x, y)
@@ -444,9 +548,9 @@ object LightmapRenderer {
}
// blend lantern
lightLevelThis.maxAndAssign(thisTileLuminosity).maxAndAssign(lanternMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
lightLevelThis.maxAndAssign(thisTileLuminosity).maxAndAssign(lightSourcesMap[LandUtil.getBlockAddr(world, x, y)] ?: colourNull)
}
}*/
private val inNoopMaskp = Point2i(0,0)
@@ -505,7 +609,9 @@ object LightmapRenderer {
// O(9n) == O(n) where n is a size of the map
getLightsAndShades(x, y)
//getLightsAndShades(x, y)
val lightLevelThis = getLightSourceMap(x, y) // it HAS to be a cpy()...?, otherwise all cells gets the same instance
val (thisTileOpacity, thisTileOpacity2) = getShadesMap(x, y)
// calculate ambient
/* + * + 0 4 1
@@ -516,6 +622,7 @@ object LightmapRenderer {
*/
// will "overwrite" what's there in the lightmap if it's the first pass
// using "map"s actually makes it slower. Guess I'll keep it dirty...
// takes about 2 ms on 6700K
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x - 1, y - 1) ?: colourNull, thisTileOpacity2))
/* + */lightLevelThis.maxAndAssign(darkenColoured(getLightInternal(x + 1, y - 1) ?: colourNull, thisTileOpacity2))
@@ -528,7 +635,7 @@ object LightmapRenderer {
//return lightLevelThis.cpy() // it HAS to be a cpy(), otherwise all cells gets the same instance
setLightOf(lightmap, x, y, lightLevelThis.cpy())
setLightOf(lightmap, x, y, lightLevelThis)
}
private fun getLightForOpaque(x: Int, y: Int): Color? { // ...so that they wouldn't appear too dark
@@ -887,4 +994,4 @@ object LightmapRenderer {
}
fun Color.toRGBA() = (255 * r).toInt() shl 24 or ((255 * g).toInt() shl 16) or ((255 * b).toInt() shl 8) or (255 * a).toInt()
//fun Color(c: Float) = Color(c, c, c, c)

View File

@@ -39,9 +39,66 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
fun removeAt(index: Int) = arrayList.removeAt(index)
fun remove(element: T) = arrayList.remove(element)
fun removeLast() = arrayList.removeAt(arrayList.size)
fun removeLast() = arrayList.removeAt(arrayList.size - 1)
operator fun get(index: Int) = arrayList[index]
fun getOrNull(index: Int?) = if (index == null) null else get(index)
/**
* Searches for the element. Null if the element was not found
*/
fun contains(element: T): Boolean {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
if (element > midVal)
low = mid + 1
else if (element < midVal)
high = mid - 1
else
return true // key found
}
return false // key not found
}
/** Searches the element using given predicate instead of the element itself. Returns index in the array where desired, null when there is no such element.
* element is stored.
* (e.g. search the Actor by its ID rather than the actor instance)
*
* @param searchQuery what exactly are we looking for?
* @param searchHow and where or how can it be found?
*/
fun <R: Comparable<R>> searchForIndex(searchQuery: R, searchHow: (T) -> R): Int? {
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = searchHow(get(mid))
if (searchQuery > midVal)
low = mid + 1
else if (searchQuery < midVal)
high = mid - 1
else
return mid // key found
}
return null // key not found
}
/** Searches the element using given predicate instead of the element itself. Returns the element desired, null when there is no such element.
* (e.g. search the Actor by its ID rather than the actor instance)
*
* @param searchQuery what exactly are we looking for?
* @param searchHow and where or how can it be found?
*/
fun <R: Comparable<R>> searchFor(searchQuery: R, searchHow: (T) -> R): T? = getOrNull(searchForIndex(searchQuery, searchHow))
fun iterator() = arrayList.iterator()
fun forEach(action: (T) -> Unit) = arrayList.forEach(action)

Binary file not shown.