mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
moar kotlin
Former-commit-id: b529f2c7fb04433c3bc5f04f451fec384da1dc75 Former-commit-id: 23ea19279fbf275d69e08ea09ea9f734d50f5722
This commit is contained in:
9
.idea/codeStyleSettings.xml
generated
9
.idea/codeStyleSettings.xml
generated
@@ -4,6 +4,9 @@
|
||||
<option name="PER_PROJECT_SETTINGS">
|
||||
<value>
|
||||
<option name="RIGHT_MARGIN" value="100" />
|
||||
<JetCodeStyleSettings>
|
||||
<option name="ALIGN_IN_COLUMNS_CASE_BRANCH" value="true" />
|
||||
</JetCodeStyleSettings>
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
@@ -27,6 +30,12 @@
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="WRAP_ON_TYPING" value="0" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
||||
</codeStyleSettings>
|
||||
</value>
|
||||
</option>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
|
||||
135
src/com/Torvald/Terrarum/Actors/ActorInventory.kt
Normal file
135
src/com/Torvald/Terrarum/Actors/ActorInventory.kt
Normal file
@@ -0,0 +1,135 @@
|
||||
package com.Torvald.Terrarum.Actors
|
||||
|
||||
import com.Torvald.Terrarum.GameItem.InventoryItem
|
||||
import com.Torvald.Terrarum.GameItem.ItemCodex
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
|
||||
@Transient const val CAPACITY_MAX = 0x7FFFFFFF
|
||||
@Transient const val CAPACITY_MODE_NO_ENCUMBER = 0
|
||||
@Transient const val CAPACITY_MODE_COUNT = 1
|
||||
@Transient const val CAPACITY_MODE_WEIGHT = 2
|
||||
|
||||
class ActorInventory {
|
||||
|
||||
private var capacityByCount: Int = 0
|
||||
private var capacityByWeight: Int = 0
|
||||
private var capacityMode: Int = 0
|
||||
|
||||
/**
|
||||
* <ReferenceID, Amounts>
|
||||
*/
|
||||
private val itemList: HashMap<Long, Int> = HashMap()
|
||||
|
||||
/**
|
||||
* Construct new inventory with specified capacity.
|
||||
* @param capacity if is_weight is true, killogramme value is required, counts of items otherwise.
|
||||
* *
|
||||
* @param is_weight whether encumbrance should be calculated upon the weight of the inventory. False to use item counts.
|
||||
*/
|
||||
constructor(capacity: Int, is_weight: Boolean) {
|
||||
if (is_weight) {
|
||||
capacityByWeight = capacity
|
||||
capacityMode = CAPACITY_MODE_WEIGHT
|
||||
} else {
|
||||
capacityByCount = capacity
|
||||
capacityMode = CAPACITY_MODE_COUNT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capacity of inventory
|
||||
* @return
|
||||
*/
|
||||
fun getCapacity(): Int {
|
||||
if (capacityMode == CAPACITY_MODE_NO_ENCUMBER) {
|
||||
return CAPACITY_MAX
|
||||
}
|
||||
else if (capacityMode == CAPACITY_MODE_WEIGHT) {
|
||||
return capacityByWeight
|
||||
}
|
||||
else {
|
||||
return capacityByCount
|
||||
}
|
||||
}
|
||||
|
||||
fun getCapacityMode(): Int {
|
||||
return capacityMode
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reference to the itemList
|
||||
* @return
|
||||
*/
|
||||
fun getItemList(): Map<Long, Int>? {
|
||||
return itemList
|
||||
}
|
||||
|
||||
/**
|
||||
* Get clone of the itemList
|
||||
* @return
|
||||
*/
|
||||
fun getCopyOfItemList(): Map<Long, Int>? {
|
||||
return itemList.clone() as Map<Long, Int>
|
||||
}
|
||||
|
||||
fun getTotalWeight(): Float {
|
||||
var weight = 0f
|
||||
|
||||
for (item in itemList.entries) {
|
||||
weight += ItemCodex.getItem(item.key).weight * item.value
|
||||
}
|
||||
|
||||
return weight
|
||||
}
|
||||
|
||||
fun getTotalCount(): Int {
|
||||
var count = 0
|
||||
|
||||
for (item in itemList.entries) {
|
||||
count += item.value
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
fun getTotalUniqueCount(): Int {
|
||||
return itemList.entries.size
|
||||
}
|
||||
|
||||
fun appendToPocket(item: InventoryItem) {
|
||||
appendToPocket(item, 1)
|
||||
}
|
||||
|
||||
fun appendToPocket(item: InventoryItem, count: Int) {
|
||||
val key = item.itemID
|
||||
|
||||
// if (key == Player.PLAYER_REF_ID)
|
||||
// throw new IllegalArgumentException("Attempted to put player into the inventory.");
|
||||
|
||||
if (itemList.containsKey(key))
|
||||
// increment amount if it already has specified item
|
||||
itemList.put(key, itemList[key]!! + count)
|
||||
else
|
||||
// add new entry if it does not have specified item
|
||||
itemList.put(key, count)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the itemList contains too many items
|
||||
* @return
|
||||
*/
|
||||
fun isEncumbered(): Boolean {
|
||||
if (getCapacityMode() == CAPACITY_MODE_WEIGHT) {
|
||||
return capacityByWeight < getTotalWeight()
|
||||
} else if (getCapacityMode() == CAPACITY_MODE_COUNT) {
|
||||
return capacityByCount < getTotalWeight()
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,10 +13,9 @@ import org.newdawn.slick.Graphics
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
open class ActorWithBody : Actor, Visible, Glowing, Pocketed {
|
||||
open class ActorWithBody constructor() : Actor, Visible, Glowing {
|
||||
|
||||
internal var actorValue: ActorValue
|
||||
override var inventory: ActorInventory? = null
|
||||
|
||||
var hitboxTranslateX: Float = 0.toFloat()// relative to spritePosX
|
||||
var hitboxTranslateY: Float = 0.toFloat()// relative to spritePosY
|
||||
@@ -66,15 +65,15 @@ open class ActorWithBody : Actor, Visible, Glowing, Pocketed {
|
||||
@Transient private val MASS_LOWEST = 2f
|
||||
/** Valid range: [0, 1] */
|
||||
var elasticity = 0f
|
||||
set(elasticity) {
|
||||
if (elasticity < 0)
|
||||
throw IllegalArgumentException("[ActorWithBody] $elasticity: valid elasticity value is [0, 1].")
|
||||
|
||||
if (elasticity > 1) {
|
||||
set(value) {
|
||||
if (value < 0)
|
||||
throw IllegalArgumentException("[ActorWithBody] $value: valid elasticity value is [0, 1].")
|
||||
else if (value > 1) {
|
||||
println("[ActorWithBody] Elasticity were capped to 1.")
|
||||
this.elasticity = ELASTICITY_MAX
|
||||
} else
|
||||
this.elasticity = elasticity * ELASTICITY_MAX
|
||||
field = ELASTICITY_MAX
|
||||
}
|
||||
else
|
||||
field = value * ELASTICITY_MAX
|
||||
}
|
||||
@Transient private val ELASTICITY_MAX = 0.993f
|
||||
private var density = 1000f
|
||||
|
||||
@@ -14,8 +14,9 @@ import java.io.IOException
|
||||
|
||||
private const val JSONPATH = "./res/raw/"
|
||||
|
||||
class CreatureFactory {
|
||||
object CreatureFactory {
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IOException::class, SlickException::class)
|
||||
fun build(jsonFileName: String): ActorWithBody {
|
||||
val jsonObj = JsonFetcher.readJson(JSONPATH + jsonFileName)
|
||||
@@ -42,8 +43,6 @@ class CreatureFactory {
|
||||
actor.actorValue.set("accel", Player.WALK_ACCEL_BASE)
|
||||
actor.actorValue.set("accelmult", 1f)
|
||||
|
||||
actor.inventory = (ActorInventory(actor.actorValue.get("encumbrance") as Int, true))
|
||||
|
||||
return actor
|
||||
}
|
||||
|
||||
|
||||
22
src/com/Torvald/Terrarum/Actors/DroppedItem.kt
Normal file
22
src/com/Torvald/Terrarum/Actors/DroppedItem.kt
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.Torvald.Terrarum.Actors
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
class DroppedItem constructor() : ActorWithBody() {
|
||||
|
||||
init {
|
||||
isVisible = true
|
||||
}
|
||||
|
||||
override fun update(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun drawBody(gc: GameContainer, g: Graphics) {
|
||||
drawBody(gc, g)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.Torvald.Terrarum.Actors
|
||||
|
||||
import com.Torvald.Rand.HQRNG
|
||||
import com.Torvald.Terrarum.Actors.AI.ActorAI
|
||||
import com.Torvald.Terrarum.Actors.Faction.Faction
|
||||
import com.Torvald.Terrarum.GameItem.InventoryItem
|
||||
@@ -40,14 +41,12 @@ open class NPCIntelligentBase : ActorWithBody()
|
||||
}
|
||||
|
||||
override fun attachItemData() {
|
||||
itemData = object : InventoryItem {
|
||||
override fun getItemID(): Long {
|
||||
return 0
|
||||
}
|
||||
val random: Random = HQRNG()
|
||||
|
||||
override fun getWeight(): Float {
|
||||
return 0f
|
||||
}
|
||||
itemData = object : InventoryItem {
|
||||
override var itemID = random.nextLong()
|
||||
|
||||
override var weight = 0f
|
||||
|
||||
override fun effectWhileInPocket(gc: GameContainer, delta_t: Int) {
|
||||
|
||||
|
||||
@@ -10,8 +10,12 @@ import java.io.IOException
|
||||
/**
|
||||
* Created by minjaesong on 16-03-14.
|
||||
*/
|
||||
class PFSigrid {
|
||||
|
||||
object PFSigrid {
|
||||
|
||||
private val FACTION_PATH = "./res/raw/"
|
||||
|
||||
@JvmStatic
|
||||
@Throws(SlickException::class)
|
||||
fun build(): Player {
|
||||
val p = Player()
|
||||
@@ -88,9 +92,4 @@ class PFSigrid {
|
||||
|
||||
return faction
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val FACTION_PATH = "./res/raw/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,6 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
|
||||
|
||||
@Transient private val BASE_DENSITY = 1020
|
||||
|
||||
override var referenceID: Long? = PLAYER_REF_ID
|
||||
|
||||
/** Must be set by PlayerFactory */
|
||||
override var inventory: ActorInventory? = null
|
||||
|
||||
@@ -94,6 +92,7 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
|
||||
@Throws(SlickException::class)
|
||||
constructor() : super() {
|
||||
isVisible = true
|
||||
referenceID = PLAYER_REF_ID
|
||||
super.setDensity(BASE_DENSITY)
|
||||
}
|
||||
|
||||
|
||||
24
src/com/Torvald/Terrarum/Actors/PlayerFactory.kt
Normal file
24
src/com/Torvald/Terrarum/Actors/PlayerFactory.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.Torvald.Terrarum.Actors
|
||||
|
||||
import org.newdawn.slick.SlickException
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object PlayerFactory {
|
||||
private val JSONPATH = "./res/raw/"
|
||||
private val jsonString = String()
|
||||
|
||||
@JvmStatic
|
||||
@Throws(IOException::class, SlickException::class)
|
||||
fun build(jsonFileName: String): Player {
|
||||
var p: Player = CreatureFactory.build("CreatureHuman") as Player
|
||||
|
||||
// attach sprite
|
||||
|
||||
// do etc.
|
||||
|
||||
return p
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-15.
|
||||
*/
|
||||
interface ConsoleCommand {
|
||||
|
||||
void execute(String[] args) throws Exception;
|
||||
|
||||
void printUsage();
|
||||
|
||||
}
|
||||
13
src/com/Torvald/Terrarum/ConsoleCommand/ConsoleCommand.kt
Normal file
13
src/com/Torvald/Terrarum/ConsoleCommand/ConsoleCommand.kt
Normal file
@@ -0,0 +1,13 @@
|
||||
package com.Torvald.Terrarum.ConsoleCommand
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
interface ConsoleCommand {
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun execute(args: Array<String>)
|
||||
|
||||
fun printUsage()
|
||||
|
||||
}
|
||||
@@ -22,15 +22,15 @@ public class SetGlobalLightLevel implements ConsoleCommand {
|
||||
new Echo().execute("Wrong number input.");
|
||||
}
|
||||
catch (IllegalArgumentException e1) {
|
||||
new Echo().execute("Range: 0-" + LightmapRenderer.CHANNEL_MAX + " per channel");
|
||||
new Echo().execute("Range: 0-" + LightmapRenderer.getCHANNEL_MAX() + " per channel");
|
||||
}
|
||||
}
|
||||
else if (args.length == 2) {
|
||||
try {
|
||||
char GL = (char) (new Integer(args[1]).intValue());
|
||||
|
||||
if (GL < 0 || GL >= LightmapRenderer.COLOUR_DOMAIN_SIZE) {
|
||||
new Echo().execute("Range: 0-" + (LightmapRenderer.COLOUR_DOMAIN_SIZE - 1));
|
||||
if (GL < 0 || GL >= LightmapRenderer.getCOLOUR_DOMAIN_SIZE()) {
|
||||
new Echo().execute("Range: 0-" + (LightmapRenderer.getCOLOUR_DOMAIN_SIZE() - 1));
|
||||
}
|
||||
else {
|
||||
Terrarum.game.map.setGlobalLight(GL);
|
||||
|
||||
@@ -18,8 +18,8 @@ public class TeleportPlayer implements ConsoleCommand {
|
||||
|
||||
int x, y;
|
||||
try {
|
||||
x = new Integer((args[1])) * MapDrawer.TILE_SIZE + (MapDrawer.TILE_SIZE / 2);
|
||||
y = new Integer((args[2])) * MapDrawer.TILE_SIZE + (MapDrawer.TILE_SIZE / 2);
|
||||
x = new Integer((args[1])) * MapDrawer.getTILE_SIZE() + (MapDrawer.getTILE_SIZE() / 2);
|
||||
y = new Integer((args[2])) * MapDrawer.getTILE_SIZE() + (MapDrawer.getTILE_SIZE() / 2);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
new Echo().execute("Wrong number input.");
|
||||
|
||||
@@ -101,14 +101,10 @@ public class Game extends BasicGameState {
|
||||
|
||||
// add new player and put it to actorContainer
|
||||
//player = new Player();
|
||||
player = new PFSigrid().build();
|
||||
player = PFSigrid.build();
|
||||
//player.setNoClip(true);
|
||||
actorContainer.add(player);
|
||||
|
||||
new MapDrawer(map);
|
||||
|
||||
new LightmapRenderer();
|
||||
|
||||
consoleHandler = new UIHandler(new ConsoleWindow());
|
||||
consoleHandler.setPosition(0, 0);
|
||||
|
||||
@@ -180,7 +176,7 @@ public class Game extends BasicGameState {
|
||||
|
||||
// compensate for zoom. UIs have to be treated specially! (see UIHandler)
|
||||
g.translate(
|
||||
-MapCamera.getCameraX() * screenZoom
|
||||
-MapCamera.getCameraX() * screenZoom
|
||||
, -MapCamera.getCameraY() * screenZoom
|
||||
);
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ public class GameController {
|
||||
|
||||
public static void processInput(Input input) {
|
||||
int mouseTileX = (int) ((MapCamera.getCameraX() + input.getMouseX() / Terrarum.game.screenZoom)
|
||||
/ MapDrawer.TILE_SIZE);
|
||||
/ MapDrawer.getTILE_SIZE());
|
||||
int mouseTileY = (int) ((MapCamera.getCameraY() + input.getMouseY() / Terrarum.game.screenZoom)
|
||||
/ MapDrawer.TILE_SIZE);
|
||||
/ MapDrawer.getTILE_SIZE());
|
||||
|
||||
|
||||
KeyToggler.update(input);
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.Torvald.Terrarum.GameItem;
|
||||
|
||||
import org.newdawn.slick.GameContainer;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-16.
|
||||
*/
|
||||
public interface InventoryItem {
|
||||
|
||||
/**
|
||||
* Get internal ID of an Item.
|
||||
* 0-4096: Tiles
|
||||
* 4097-32767: Various items
|
||||
* >=32768: Actor RefID
|
||||
* @return
|
||||
*/
|
||||
long getItemID();
|
||||
|
||||
/**
|
||||
* Weight of the item
|
||||
* @return
|
||||
*/
|
||||
float getWeight();
|
||||
|
||||
/**
|
||||
* Effects applied while in pocket
|
||||
* @param gc
|
||||
* @param delta_t
|
||||
*/
|
||||
void effectWhileInPocket(GameContainer gc, int delta_t);
|
||||
|
||||
/**
|
||||
* Effects applied immediately only once if picked up
|
||||
* @param gc
|
||||
* @param delta_t
|
||||
*/
|
||||
void effectWhenPickedUp(GameContainer gc, int delta_t);
|
||||
|
||||
/**
|
||||
* Effects applied while primary button (usually left mouse button) is down
|
||||
* @param gc
|
||||
* @param delta_t
|
||||
*/
|
||||
void primaryUse(GameContainer gc, int delta_t);
|
||||
|
||||
/**
|
||||
* Effects applied while secondary button (usually right mouse button) is down
|
||||
* @param gc
|
||||
* @param delta_t
|
||||
*/
|
||||
void secondaryUse(GameContainer gc, int delta_t);
|
||||
|
||||
/**
|
||||
* Effects applied immediately only once if thrown from pocket
|
||||
* @param gc
|
||||
* @param delta_t
|
||||
*/
|
||||
void effectWhenThrownAway(GameContainer gc, int delta_t);
|
||||
|
||||
}
|
||||
61
src/com/Torvald/Terrarum/GameItem/InventoryItem.kt
Normal file
61
src/com/Torvald/Terrarum/GameItem/InventoryItem.kt
Normal file
@@ -0,0 +1,61 @@
|
||||
package com.Torvald.Terrarum.GameItem
|
||||
|
||||
import org.newdawn.slick.GameContainer
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
interface InventoryItem {
|
||||
/**
|
||||
* Internal ID of an Item, Long
|
||||
* 0-4096: Tiles
|
||||
* 4097-32767: Various items
|
||||
* >=32768: Actor RefID
|
||||
*/
|
||||
var itemID: Long
|
||||
|
||||
/**
|
||||
* Weight of the item, Float
|
||||
*/
|
||||
var weight: Float
|
||||
|
||||
/**
|
||||
* Effects applied while in pocket
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun effectWhileInPocket(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied immediately only once if picked up
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun effectWhenPickedUp(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied while primary button (usually left mouse button) is down
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun primaryUse(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied while secondary button (usually right mouse button) is down
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun secondaryUse(gc: GameContainer, delta_t: Int)
|
||||
|
||||
/**
|
||||
* Effects applied immediately only once if thrown from pocket
|
||||
* @param gc
|
||||
* *
|
||||
* @param delta_t
|
||||
*/
|
||||
fun effectWhenThrownAway(gc: GameContainer, delta_t: Int)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.Torvald.Terrarum.GameItem;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-11.
|
||||
*/
|
||||
public class ItemCodex {
|
||||
|
||||
/**
|
||||
* <ItemID or RefID for Actor, TheItem>
|
||||
* Will return corresponding Actor if ID >= 32768
|
||||
*/
|
||||
private static HashMap<Long, InventoryItem> itemCodex;
|
||||
|
||||
public static InventoryItem getItem(long code) {
|
||||
return itemCodex.get(code);
|
||||
}
|
||||
|
||||
}
|
||||
21
src/com/Torvald/Terrarum/GameItem/ItemCodex.kt
Normal file
21
src/com/Torvald/Terrarum/GameItem/ItemCodex.kt
Normal file
@@ -0,0 +1,21 @@
|
||||
package com.Torvald.Terrarum.GameItem
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object ItemCodex {
|
||||
/**
|
||||
* <ItemID or RefID for Actor, TheItem>
|
||||
* Will return corresponding Actor if ID >= 32768
|
||||
*/
|
||||
private val itemCodex: HashMap<Long, InventoryItem> = HashMap(
|
||||
// hashmap init here
|
||||
)
|
||||
|
||||
|
||||
fun getItem(code: Long): InventoryItem {
|
||||
return itemCodex[code]!!
|
||||
}
|
||||
}
|
||||
@@ -1,701 +0,0 @@
|
||||
package com.Torvald.Terrarum.MapDrawer;
|
||||
|
||||
import com.Torvald.ColourUtil.Col40;
|
||||
import com.Torvald.Terrarum.Actors.Actor;
|
||||
import com.Torvald.Terrarum.Actors.ActorWithBody;
|
||||
import com.Torvald.Terrarum.Actors.Glowing;
|
||||
import com.Torvald.Terrarum.Actors.Luminous;
|
||||
import com.Torvald.Terrarum.Terrarum;
|
||||
import com.Torvald.Terrarum.TileProperties.TilePropCodex;
|
||||
import com.jme3.math.FastMath;
|
||||
import org.newdawn.slick.Color;
|
||||
import org.newdawn.slick.Graphics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-25.
|
||||
*/
|
||||
public class LightmapRenderer {
|
||||
|
||||
/**
|
||||
* 8-Bit RGB values
|
||||
*/
|
||||
private static volatile char[][] staticLightMap;
|
||||
private static boolean lightMapInitialised = false;
|
||||
|
||||
/**
|
||||
* For entities that emits light (e.g. Player with shine potion)
|
||||
*/
|
||||
private static ArrayList<LightmapLantern> lanterns = new ArrayList<>();
|
||||
|
||||
private static final int AIR = 0;
|
||||
private static final int SUNSTONE = 41; // TODO add sunstone: emits same light as Map.GL. Goes dark at night
|
||||
|
||||
|
||||
private static final int OFFSET_R = 2;
|
||||
private static final int OFFSET_G = 1;
|
||||
private static final int OFFSET_B = 0;
|
||||
|
||||
private static final int TSIZE = MapDrawer.TILE_SIZE;
|
||||
|
||||
// color model related vars
|
||||
public static final int MUL = Col40.MUL;
|
||||
public static final int MUL_2 = Col40.MUL_2;
|
||||
public static final int CHANNEL_MAX = Col40.MAX_STEP;
|
||||
public static final float CHANNEL_MAX_FLOAT = (float) CHANNEL_MAX;
|
||||
public static final int COLOUR_DOMAIN_SIZE = Col40.COLOUR_DOMAIN_SIZE;
|
||||
|
||||
public LightmapRenderer() {
|
||||
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void addLantern(int x, int y, char intensity) {
|
||||
LightmapLantern thisLantern = new LightmapLantern(x, y, intensity);
|
||||
|
||||
for (int i = lanterns.size() - 1; i >= 0; i--) {
|
||||
LightmapLantern lanternInList = lanterns.get(i);
|
||||
// found duplicates
|
||||
if (lanternInList.getX() == x && lanternInList.getY() == y) {
|
||||
// add colour
|
||||
char addedL = addRaw(intensity, lanternInList.getIntensity());
|
||||
lanternInList.intensity = addedL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
//else
|
||||
lanterns.add(thisLantern);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void removeLantern(int x, int y) {
|
||||
for (int i = lanterns.size() - 1; i >= 0; i--) {
|
||||
LightmapLantern lantern = lanterns.get(i);
|
||||
if (lantern.getX() == x && lantern.getY() == y) {
|
||||
lanterns.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void renderLightMap() {
|
||||
if (staticLightMap == null) {
|
||||
staticLightMap = new char[Terrarum.game.map.height][Terrarum.game.map.width];
|
||||
|
||||
if (lightMapInitialised) {
|
||||
throw new RuntimeException("Attempting to re-initialise 'staticLightMap'");
|
||||
}
|
||||
|
||||
lightMapInitialised = true;
|
||||
}
|
||||
|
||||
|
||||
int for_y_start = div16(MapCamera.getCameraY()) - 1; // fix for premature lightmap rendering
|
||||
int for_x_start = div16(MapCamera.getCameraX()) - 1; // on topmost/leftmost side
|
||||
|
||||
int for_y_end = clampHTile(for_y_start + div16(MapCamera.getRenderHeight()) + 2) + 1; // same fix as above
|
||||
int for_x_end = clampWTile(for_x_start + div16(MapCamera.getRenderWidth()) + 2) + 1;
|
||||
|
||||
/**
|
||||
* Updating order:
|
||||
* +-----+ +-----+ +-----+ +-----+
|
||||
* |1 | | 1| |3 | | 3|
|
||||
* | 2 | > | 2 | > | 2 | > | 2 |
|
||||
* | 3| |3 | | 1| |1 |
|
||||
* +-----+ +-----+ +-----+ +-----+
|
||||
* round: 1 2 3 4
|
||||
* for all staticLightMap[y][x]
|
||||
*/
|
||||
|
||||
purgePartOfLightmap(for_x_start, for_y_start, for_x_end, for_y_end);
|
||||
// if wider purge were not applied, GL changing (sunset, sunrise) will behave incorrectly
|
||||
// ("leakage" of non-updated sunlight)
|
||||
|
||||
try {
|
||||
// Round 1
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
staticLightMap[y][x] = calculate(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Round 4
|
||||
for (int y = for_y_end - 1; y > for_y_start; y--) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
staticLightMap[y][x] = calculate(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Round 3
|
||||
for (int y = for_y_end - 1; y > for_y_start; y--) {
|
||||
for (int x = for_x_end - 1; x >= for_x_start; x--) {
|
||||
staticLightMap[y][x] = calculate(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Round 2
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_end - 1; x >= for_x_start; x--) {
|
||||
staticLightMap[y][x] = calculate(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {}
|
||||
}
|
||||
|
||||
public static void draw(Graphics g) {
|
||||
int for_x_start = MapCamera.getRenderStartX() - 1;
|
||||
int for_y_start = MapCamera.getRenderStartY() - 1;
|
||||
int for_x_end = MapCamera.getRenderEndX();
|
||||
int for_y_end = MapCamera.getRenderEndY();
|
||||
|
||||
// draw
|
||||
try {
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
// smooth
|
||||
if (Terrarum.game.screenZoom >= 1
|
||||
&& Terrarum.gameConfig.getAsBoolean("smoothlighting")) {
|
||||
char thisLightLevel = staticLightMap[y][x];
|
||||
if (y > 0 && x < for_x_end && thisLightLevel == 0 && staticLightMap[y - 1][x] == 0) {
|
||||
try {
|
||||
// coalesce zero intensity blocks to one
|
||||
int zeroLevelCounter = 1;
|
||||
while (staticLightMap[y][x + zeroLevelCounter] == 0
|
||||
&& staticLightMap[y - 1][x + zeroLevelCounter] == 0) {
|
||||
zeroLevelCounter += 1;
|
||||
|
||||
if (x + zeroLevelCounter >= for_x_end) break;
|
||||
}
|
||||
|
||||
g.setColor(new Color(0));
|
||||
g.fillRect(
|
||||
Math.round(x * TSIZE * Terrarum.game.screenZoom)
|
||||
, Math.round(y * TSIZE * Terrarum.game.screenZoom)
|
||||
, FastMath.ceil(
|
||||
TSIZE * Terrarum.game.screenZoom) * zeroLevelCounter
|
||||
, FastMath.ceil(TSIZE * Terrarum.game.screenZoom)
|
||||
);
|
||||
|
||||
x += (zeroLevelCounter - 1);
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
else {
|
||||
/** a
|
||||
* +-+-+
|
||||
* |i|j|
|
||||
* b +-+-+ c
|
||||
* |k|l|
|
||||
* +-+-+
|
||||
* d
|
||||
*/
|
||||
char a = (y == 0) ? thisLightLevel
|
||||
: (y == Terrarum.game.map.height - 1) ? thisLightLevel
|
||||
: maximiseRGB(
|
||||
staticLightMap[y][x]
|
||||
,
|
||||
staticLightMap[y - 1][x]);
|
||||
char d = (y == 0) ? thisLightLevel
|
||||
: (y == Terrarum.game.map.height - 1) ? thisLightLevel
|
||||
: maximiseRGB(
|
||||
staticLightMap[y][x]
|
||||
,
|
||||
staticLightMap[y + 1][x]);
|
||||
char b = (x == 0) ? thisLightLevel
|
||||
: (x == Terrarum.game.map.width - 1) ? thisLightLevel
|
||||
: maximiseRGB(
|
||||
staticLightMap[y][x]
|
||||
,
|
||||
staticLightMap[y][x - 1]);
|
||||
char c = (x == 0) ? thisLightLevel
|
||||
: (x == Terrarum.game.map.width - 1) ? thisLightLevel
|
||||
: maximiseRGB(
|
||||
staticLightMap[y][x]
|
||||
,
|
||||
staticLightMap[y][x + 1]);
|
||||
char[] colourMapItoL = new char[4];
|
||||
colourMapItoL[0] = colourLinearMix(a, b);
|
||||
colourMapItoL[1] = colourLinearMix(a, c);
|
||||
colourMapItoL[2] = colourLinearMix(b, d);
|
||||
colourMapItoL[3] = colourLinearMix(c, d);
|
||||
|
||||
for (int iy = 0; iy < 2; iy++) {
|
||||
for (int ix = 0; ix < 2; ix++) {
|
||||
g.setColor(toTargetColour(colourMapItoL[iy * 2 + ix]));
|
||||
|
||||
g.fillRect(
|
||||
Math.round(
|
||||
x * TSIZE * Terrarum.game.screenZoom) + (ix * TSIZE / 2 * Terrarum.game.screenZoom)
|
||||
, Math.round(
|
||||
y * TSIZE * Terrarum.game.screenZoom) + (iy * TSIZE / 2 * Terrarum.game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Terrarum.game.screenZoom / 2)
|
||||
, FastMath.ceil(TSIZE * Terrarum.game.screenZoom / 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Retro
|
||||
else {
|
||||
try {
|
||||
int thisLightLevel = staticLightMap[y][x];
|
||||
|
||||
// coalesce identical intensity blocks to one
|
||||
int sameLevelCounter = 1;
|
||||
while (staticLightMap[y][x + sameLevelCounter] == thisLightLevel) {
|
||||
sameLevelCounter += 1;
|
||||
|
||||
if (x + sameLevelCounter >= for_x_end) break;
|
||||
}
|
||||
|
||||
g.setColor(toTargetColour(staticLightMap[y][x]));
|
||||
g.fillRect(
|
||||
Math.round(x * TSIZE * Terrarum.game.screenZoom)
|
||||
, Math.round(y * TSIZE * Terrarum.game.screenZoom)
|
||||
, FastMath.ceil(
|
||||
TSIZE * Terrarum.game.screenZoom) * sameLevelCounter
|
||||
, FastMath.ceil(TSIZE * Terrarum.game.screenZoom)
|
||||
);
|
||||
|
||||
x += (sameLevelCounter - 1);
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {}
|
||||
}
|
||||
|
||||
private static char calculate(int x, int y) {
|
||||
return calculate(x, y, false);
|
||||
}
|
||||
|
||||
private static char calculate(int x, int y, boolean doNotCalculateAmbient){
|
||||
char lightLevelThis = 0;
|
||||
int thisTerrain = Terrarum.game.map.getTileFromTerrain(x, y);
|
||||
int thisWall = Terrarum.game.map.getTileFromWall(x, y);
|
||||
char thisTileLuminosity = TilePropCodex.getProp(thisTerrain).getLuminosity();
|
||||
char thisTileOpacity = TilePropCodex.getProp(thisTerrain).getOpacity();
|
||||
char sunLight = Terrarum.game.map.getGlobalLight();
|
||||
|
||||
// MIX TILE
|
||||
// open air
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightLevelThis = sunLight;
|
||||
}
|
||||
// luminous tile transparent (allows sunlight to pass)
|
||||
else if (thisWall == AIR && thisTileLuminosity > 0) {
|
||||
char darkenSunlight = darkenColoured(sunLight, thisTileOpacity);
|
||||
lightLevelThis = screenBlend(darkenSunlight, thisTileLuminosity);
|
||||
}
|
||||
// luminous tile (opaque)
|
||||
else if (thisWall != AIR && thisTileLuminosity > 0) {
|
||||
lightLevelThis = thisTileLuminosity;
|
||||
}
|
||||
// END MIX TILE
|
||||
|
||||
// mix lantern
|
||||
for (LightmapLantern lantern : lanterns) {
|
||||
if (lantern.getX() == x && lantern.getY() == y) {
|
||||
lightLevelThis = screenBlend(lightLevelThis, lantern.getIntensity());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// mix luminous actor
|
||||
for (Actor actor : Terrarum.game.actorContainer) {
|
||||
if (actor instanceof Luminous && actor instanceof ActorWithBody) {
|
||||
Luminous actorLum = (Luminous) actor;
|
||||
ActorWithBody actorBody = (ActorWithBody) actor;
|
||||
int tileX = Math.round(actorBody.getHitbox().getPointedX() / TSIZE);
|
||||
int tileY = Math.round(actorBody.getHitbox().getPointedY() / TSIZE)
|
||||
- 1;
|
||||
char actorLuminosity = actorLum.getLuminosity();
|
||||
if (x == tileX && y == tileY) {
|
||||
lightLevelThis = screenBlend(lightLevelThis, actorLuminosity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!doNotCalculateAmbient) {
|
||||
// calculate ambient
|
||||
char ambient = 0;
|
||||
char nearby = 0;
|
||||
findNearbyBrightest:
|
||||
for (int yoff = -1; yoff <= 1; yoff++) {
|
||||
for (int xoff = -1; xoff <= 1; xoff++) {
|
||||
/**
|
||||
* filter for 'v's as:
|
||||
* +-+-+-+
|
||||
* |a|v|a|
|
||||
* +-+-+-+
|
||||
* |v| |v|
|
||||
* +-+-+-+
|
||||
* |a|v|a|
|
||||
* +-+-+-+
|
||||
*/
|
||||
if (xoff != yoff && -xoff != yoff) { // 'v' tiles
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = staticLightMap[y + yoff][x + xoff];
|
||||
}
|
||||
}
|
||||
else if (xoff != 0 && yoff != 0) { // 'a' tiles
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = darkenUniformInt(staticLightMap[y + yoff][x + xoff]
|
||||
, 2); //2
|
||||
// mix some to have more 'spreading'
|
||||
// so that light spreads in a shape of an octagon instead of a diamond
|
||||
}
|
||||
}
|
||||
else {
|
||||
nearby = 0; // exclude 'me' tile
|
||||
}
|
||||
|
||||
ambient = maximiseRGB(ambient, nearby); // keep base value as brightest nearby
|
||||
}
|
||||
}
|
||||
|
||||
ambient = darkenColoured(ambient,
|
||||
thisTileOpacity
|
||||
); // get real ambient by appling opacity value
|
||||
|
||||
// mix and return lightlevel and ambient
|
||||
return maximiseRGB(lightLevelThis, ambient);
|
||||
}
|
||||
else {
|
||||
return lightLevelThis;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract each channel's RGB value.
|
||||
* It works like:
|
||||
* f(data, darken) = RGB(data.r - darken.r, data.g - darken.g, data.b - darken.b)
|
||||
*
|
||||
* @param data Raw channel value [0-39] per channel
|
||||
* @param darken [0-39] per channel
|
||||
* @return darkened data [0-39] per channel
|
||||
*/
|
||||
private static char darkenColoured(char data, char darken) {
|
||||
if (darken < 0 || darken >= COLOUR_DOMAIN_SIZE) { throw new IllegalArgumentException("darken: out of " +
|
||||
"range"); }
|
||||
|
||||
float r = clampZero(getR(data) - getR(darken));
|
||||
float g = clampZero(getG(data) - getG(darken));
|
||||
float b = clampZero(getB(data) - getB(darken));
|
||||
|
||||
return constructRGBFromFloat(r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken each channel by 'darken' argument
|
||||
* It works like:
|
||||
* f(data, darken) = RGB(data.r - darken, data.g - darken, data.b - darken)
|
||||
* @param data [0-39] per channel
|
||||
* @param darken [0-1]
|
||||
* @return
|
||||
*/
|
||||
private static char darkenUniformFloat(char data, float darken) {
|
||||
if (darken < 0 || darken > 1f) { throw new IllegalArgumentException("darken: out of " +
|
||||
"range"); }
|
||||
|
||||
float r = clampZero(getR(data) - darken);
|
||||
float g = clampZero(getG(data) - darken);
|
||||
float b = clampZero(getB(data) - darken);
|
||||
|
||||
return constructRGBFromFloat(r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken each channel by 'darken' argument
|
||||
* It works like:
|
||||
* f(data, darken) = RGB(data.r - darken, data.g - darken, data.b - darken)
|
||||
* @param data [0-39] per channel
|
||||
* @param darken [0-39]
|
||||
* @return
|
||||
*/
|
||||
private static char darkenUniformInt(char data, int darken) {
|
||||
if (darken < 0 || darken > CHANNEL_MAX) { throw new IllegalArgumentException("darken: out of " +
|
||||
"range"); }
|
||||
|
||||
int r = clampZero(getRawR(data) - darken);
|
||||
int g = clampZero(getRawG(data) - darken);
|
||||
int b = clampZero(getRawB(data) - darken);
|
||||
|
||||
return constructRGBFromInt(r, g, b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add each channel's RGB value.
|
||||
* It works like:
|
||||
* f(data, brighten) = RGB(data.r + darken.r, data.g + darken.g, data.b + darken.b)
|
||||
* @param data Raw channel value [0-39] per channel
|
||||
* @param brighten [0-39] per channel
|
||||
* @return brightened data [0-39] per channel
|
||||
*/
|
||||
private static char brightenColoured(char data, char brighten) {
|
||||
if (brighten < 0 || brighten >= COLOUR_DOMAIN_SIZE) { throw new IllegalArgumentException("brighten: out of " +
|
||||
"range"); }
|
||||
|
||||
float r = clampFloat(getR(data) + getR(brighten));
|
||||
float g = clampFloat(getG(data) + getG(brighten));
|
||||
float b = clampFloat(getB(data) + getB(brighten));
|
||||
|
||||
return constructRGBFromFloat(r, g, b);
|
||||
}
|
||||
|
||||
/** Get each channel from two RGB values, return new RGB that has max value of each channel
|
||||
* @param rgb
|
||||
* @param rgb2
|
||||
* @return
|
||||
*/
|
||||
private static char maximiseRGB(char rgb, char rgb2) {
|
||||
int r1 = getRawR(rgb); int r2 = getRawR(rgb2); int newR = (r1 > r2) ? r1 : r2;
|
||||
int g1 = getRawG(rgb); int g2 = getRawG(rgb2); int newG = (g1 > g2) ? g1 : g2;
|
||||
int b1 = getRawB(rgb); int b2 = getRawB(rgb2); int newB = (b1 > b2) ? b1 : b2;
|
||||
|
||||
return constructRGBFromInt(newR, newG, newB);
|
||||
}
|
||||
|
||||
private static char screenBlend(char rgb, char rgb2) {
|
||||
float r1 = getR(rgb); float r2 = getR(rgb2); float newR = 1 - (1 - r1) * (1 - r2);
|
||||
float g1 = getG(rgb); float g2 = getG(rgb2); float newG = 1 - (1 - g1) * (1 - g2);
|
||||
float b1 = getB(rgb); float b2 = getB(rgb2); float newB = 1 - (1 - b1) * (1 - b2);
|
||||
|
||||
return constructRGBFromFloat(newR, newG, newB);
|
||||
}
|
||||
|
||||
public static int getRawR(char RGB) {
|
||||
return RGB / MUL_2;
|
||||
}
|
||||
|
||||
public static int getRawG(char RGB) {
|
||||
return (RGB % MUL_2) / MUL;
|
||||
}
|
||||
|
||||
public static int getRawB(char RGB) {
|
||||
return RGB % MUL;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param RGB
|
||||
* @param offset 2 = R, 1 = G, 0 = B
|
||||
* @return
|
||||
*/
|
||||
public static int getRaw(char RGB, int offset) {
|
||||
if (offset == OFFSET_R) return getRawR(RGB);
|
||||
if (offset == OFFSET_G) return getRawG(RGB);
|
||||
if (offset == OFFSET_B) return getRawB(RGB);
|
||||
else throw new IllegalArgumentException("Channel offset out of range");
|
||||
}
|
||||
|
||||
private static float getR(char rgb) {
|
||||
return getRawR(rgb) / CHANNEL_MAX_FLOAT;
|
||||
}
|
||||
|
||||
private static float getG(char rgb) {
|
||||
return getRawG(rgb) / CHANNEL_MAX_FLOAT;
|
||||
}
|
||||
|
||||
private static float getB(char rgb) {
|
||||
return getRawB(rgb) / CHANNEL_MAX_FLOAT;
|
||||
}
|
||||
|
||||
private static char addRaw(char rgb1, char rgb2) {
|
||||
int newR = clampByte(getRawR(rgb1) + getRawB(rgb2));
|
||||
int newG = clampByte(getRawG(rgb1) + getRawG(rgb2));
|
||||
int newB = clampByte(getRawB(rgb1) + getRawB(rgb2));
|
||||
|
||||
return constructRGBFromInt(newR, newG, newB);
|
||||
}
|
||||
|
||||
public static char constructRGBFromInt(int r, int g, int b) {
|
||||
if (r < 0 || r > CHANNEL_MAX) { throw new IllegalArgumentException("Red: out of range"); }
|
||||
if (g < 0 || g > CHANNEL_MAX) { throw new IllegalArgumentException("Green: out of range"); }
|
||||
if (b < 0 || b > CHANNEL_MAX) { throw new IllegalArgumentException("Blue: out of range"); }
|
||||
return (char) (r * MUL_2 + g * MUL + b);
|
||||
}
|
||||
|
||||
private static char constructRGBFromFloat(float r, float g, float b) {
|
||||
if (r < 0 || r > 1.0f) { throw new IllegalArgumentException("Red: out of range"); }
|
||||
if (g < 0 || g > 1.0f) { throw new IllegalArgumentException("Green: out of range"); }
|
||||
if (b < 0 || b > 1.0f) { throw new IllegalArgumentException("Blue: out of range"); }
|
||||
|
||||
int intR = Math.round(r * CHANNEL_MAX);
|
||||
int intG = Math.round(g * CHANNEL_MAX);
|
||||
int intB = Math.round(b * CHANNEL_MAX);
|
||||
|
||||
return constructRGBFromInt(intR, intG, intB);
|
||||
}
|
||||
|
||||
private static char colourLinearMix(char colA, char colB) {
|
||||
int r = (getRawR(colA) + getRawR(colB)) >> 1;
|
||||
int g = (getRawG(colA) + getRawG(colB)) >> 1;
|
||||
int b = (getRawB(colA) + getRawB(colB)) >> 1;
|
||||
return constructRGBFromInt(r, g, b);
|
||||
}
|
||||
|
||||
private static int quantise16(int x) {
|
||||
if (x < 0) throw new IllegalArgumentException("positive integer only.");
|
||||
return (x & 0xFFFF_FFF0);
|
||||
}
|
||||
|
||||
private static int div16(int x) {
|
||||
if (x < 0) throw new IllegalArgumentException("positive integer only.");
|
||||
return (x & 0x7FFF_FFFF) >> 4;
|
||||
}
|
||||
|
||||
private static int mul16(int x) {
|
||||
if (x < 0) throw new IllegalArgumentException("positive integer only.");
|
||||
return (x << 4);
|
||||
}
|
||||
|
||||
private static int max(int... i) {
|
||||
Arrays.sort(i);
|
||||
return i[i.length - 1];
|
||||
}
|
||||
|
||||
private static int min(int... i) {
|
||||
Arrays.sort(i);
|
||||
return i[0];
|
||||
}
|
||||
|
||||
private static boolean outOfBounds(int x, int y){
|
||||
return ( x < 0 || y < 0 || x >= Terrarum.game.map.width || y >= Terrarum.game.map.height);
|
||||
}
|
||||
|
||||
private static boolean outOfMapBounds(int x, int y){
|
||||
return ( x < 0 || y < 0 || x >= staticLightMap[0].length || y >= staticLightMap.length);
|
||||
}
|
||||
|
||||
private static int clampZero(int i) {
|
||||
return (i < 0) ? 0 : i;
|
||||
}
|
||||
|
||||
private static float clampZero(float i) {
|
||||
return (i < 0) ? 0 : i;
|
||||
}
|
||||
|
||||
private static int clampByte(int i) {
|
||||
return (i < 0) ? 0 : (i > CHANNEL_MAX) ? CHANNEL_MAX : i;
|
||||
}
|
||||
|
||||
private static float clampFloat(float i) {
|
||||
return (i < 0) ? 0 : (i > 1) ? 1 : i;
|
||||
}
|
||||
|
||||
public static char getValueFromMap(int x, int y) {
|
||||
return staticLightMap[y][x];
|
||||
}
|
||||
|
||||
private static void purgePartOfLightmap(int x1, int y1, int x2, int y2) {
|
||||
try {
|
||||
for (int y = y1 - 1; y < y2 + 1; y++) {
|
||||
for (int x = x1 - 1; x < x2 + 1; x++) {
|
||||
if (y == y1 - 1 || y == y2 || x == x1 - 1 || x == x2) {
|
||||
// fill the rim with (pre) calculation
|
||||
staticLightMap[y][x] = preCalculateUpdateGLOnly(x, y);
|
||||
}
|
||||
else {
|
||||
staticLightMap[y][x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {}
|
||||
}
|
||||
|
||||
private static char preCalculateUpdateGLOnly(int x, int y) {
|
||||
int thisWall = Terrarum.game.map.getTileFromWall(x, y);
|
||||
int thisTerrain = Terrarum.game.map.getTileFromTerrain(x, y);
|
||||
char thisTileLuminosity = TilePropCodex.getProp(thisTerrain).getLuminosity();
|
||||
char thisTileOpacity = TilePropCodex.getProp(thisTerrain).getOpacity();
|
||||
char sunLight = Terrarum.game.map.getGlobalLight();
|
||||
|
||||
char lightLevelThis;
|
||||
|
||||
// MIX TILE
|
||||
// open air
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightLevelThis = sunLight;
|
||||
}
|
||||
// luminous tile transparent (allows sunlight to pass)
|
||||
else if (thisWall == AIR && thisTileLuminosity > 0) {
|
||||
char darkenSunlight = darkenColoured(sunLight, thisTileOpacity);
|
||||
lightLevelThis = screenBlend(darkenSunlight, thisTileLuminosity);
|
||||
}
|
||||
else {
|
||||
lightLevelThis = getValueFromMap(x, y);
|
||||
}
|
||||
return lightLevelThis;
|
||||
}
|
||||
|
||||
private static int clampWTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > Terrarum.game.map.width) {
|
||||
return Terrarum.game.map.width;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampHTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > Terrarum.game.map.height) {
|
||||
return Terrarum.game.map.height;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
private static int arithmeticAverage(int... i) {
|
||||
int sum = 0;
|
||||
for (int k = 0; k < i.length; k++) {
|
||||
sum += i[k];
|
||||
}
|
||||
return Math.round(sum / (float) i.length);
|
||||
}
|
||||
|
||||
private static Color toTargetColour(char raw) {
|
||||
return new Col40().createSlickColor(raw);
|
||||
}
|
||||
}
|
||||
|
||||
class LightmapLantern {
|
||||
int x, y;
|
||||
char intensity;
|
||||
|
||||
public LightmapLantern(int x, int y, char intensity) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.intensity = intensity;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public char getIntensity() {
|
||||
return intensity;
|
||||
}
|
||||
}
|
||||
718
src/com/Torvald/Terrarum/MapDrawer/LightmapRenderer.kt
Normal file
718
src/com/Torvald/Terrarum/MapDrawer/LightmapRenderer.kt
Normal file
@@ -0,0 +1,718 @@
|
||||
package com.Torvald.Terrarum.MapDrawer
|
||||
|
||||
import com.Torvald.ColourUtil.Col40
|
||||
import com.Torvald.Terrarum.Actors.ActorWithBody
|
||||
import com.Torvald.Terrarum.Actors.Luminous
|
||||
import com.Torvald.Terrarum.Terrarum
|
||||
import com.Torvald.Terrarum.TileProperties.TilePropCodex
|
||||
import com.jme3.math.FastMath
|
||||
import org.newdawn.slick.Color
|
||||
import org.newdawn.slick.Graphics
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
|
||||
object LightmapRenderer {
|
||||
/**
|
||||
* 8-Bit RGB values
|
||||
*/
|
||||
@Volatile private var staticLightMap: Array<CharArray>? = null
|
||||
private var lightMapInitialised = false
|
||||
|
||||
/**
|
||||
* For entities that emits light (e.g. Player with shine potion)
|
||||
*/
|
||||
private val lanterns = ArrayList<LightmapLantern>()
|
||||
|
||||
private val AIR = 0
|
||||
private val SUNSTONE = 41 // TODO add sunstone: emits same light as Map.GL. Goes dark at night
|
||||
|
||||
|
||||
private val OFFSET_R = 2
|
||||
private val OFFSET_G = 1
|
||||
private val OFFSET_B = 0
|
||||
|
||||
private val TSIZE = MapDrawer.TILE_SIZE
|
||||
|
||||
// color model related vars
|
||||
@JvmStatic val MUL = Col40.MUL
|
||||
@JvmStatic val MUL_2 = Col40.MUL_2
|
||||
@JvmStatic val CHANNEL_MAX = Col40.MAX_STEP
|
||||
@JvmStatic val CHANNEL_MAX_FLOAT = CHANNEL_MAX.toFloat()
|
||||
@JvmStatic val COLOUR_DOMAIN_SIZE = Col40.COLOUR_DOMAIN_SIZE
|
||||
|
||||
@Deprecated("")
|
||||
@JvmStatic
|
||||
fun addLantern(x: Int, y: Int, intensity: Char) {
|
||||
val thisLantern = LightmapLantern(x, y, intensity)
|
||||
|
||||
for (i in lanterns.indices.reversed()) {
|
||||
val lanternInList = lanterns[i]
|
||||
// found duplicates
|
||||
if (lanternInList.x == x && lanternInList.y == y) {
|
||||
// add colour
|
||||
val addedL = addRaw(intensity, lanternInList.intensity)
|
||||
lanternInList.intensity = addedL
|
||||
return
|
||||
}
|
||||
}
|
||||
//else
|
||||
lanterns.add(thisLantern)
|
||||
}
|
||||
|
||||
@Deprecated("")
|
||||
@JvmStatic
|
||||
fun removeLantern(x: Int, y: Int) {
|
||||
for (i in lanterns.indices.reversed()) {
|
||||
val lantern = lanterns[i]
|
||||
if (lantern.x == x && lantern.y == y) {
|
||||
lanterns.removeAt(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun renderLightMap() {
|
||||
if (staticLightMap == null) {
|
||||
staticLightMap = Array(Terrarum.game.map.height) { CharArray(Terrarum.game.map.width) }
|
||||
|
||||
if (lightMapInitialised) {
|
||||
throw RuntimeException("Attempting to re-initialise 'staticLightMap'")
|
||||
}
|
||||
|
||||
lightMapInitialised = true
|
||||
}
|
||||
|
||||
|
||||
val for_y_start = div16(MapCamera.getCameraY()) - 1 // fix for premature lightmap rendering
|
||||
val for_x_start = div16(MapCamera.getCameraX()) - 1 // on topmost/leftmost side
|
||||
|
||||
val for_y_end = clampHTile(for_y_start + div16(MapCamera.getRenderHeight()) + 2) + 1 // same fix as above
|
||||
val for_x_end = clampWTile(for_x_start + div16(MapCamera.getRenderWidth()) + 2) + 1
|
||||
|
||||
/**
|
||||
* Updating order:
|
||||
* +-----+ +-----+ +-----+ +-----+
|
||||
* |1 | | 1| |3 | | 3|
|
||||
* | 2 | > | 2 | > | 2 | > | 2 |
|
||||
* | 3| |3 | | 1| |1 |
|
||||
* +-----+ +-----+ +-----+ +-----+
|
||||
* round: 1 2 3 4
|
||||
* for all staticLightMap[y][x]
|
||||
*/
|
||||
|
||||
purgePartOfLightmap(for_x_start, for_y_start, for_x_end, for_y_end)
|
||||
// if wider purge were not applied, GL changing (sunset, sunrise) will behave incorrectly
|
||||
// ("leakage" of non-updated sunlight)
|
||||
|
||||
try {
|
||||
// Round 1
|
||||
for (y in for_y_start..for_y_end - 1) {
|
||||
for (x in for_x_start..for_x_end - 1) {
|
||||
staticLightMap!![y][x] = calculate(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// Round 4
|
||||
for (y in for_y_end - 1 downTo for_y_start + 1) {
|
||||
for (x in for_x_start..for_x_end - 1) {
|
||||
staticLightMap!![y][x] = calculate(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// Round 3
|
||||
for (y in for_y_end - 1 downTo for_y_start + 1) {
|
||||
for (x in for_x_end - 1 downTo for_x_start) {
|
||||
staticLightMap!![y][x] = calculate(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
// Round 2
|
||||
for (y in for_y_start..for_y_end - 1) {
|
||||
for (x in for_x_end - 1 downTo for_x_start) {
|
||||
staticLightMap!![y][x] = calculate(x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun draw(g: Graphics) {
|
||||
val for_x_start = MapCamera.getRenderStartX() - 1
|
||||
val for_y_start = MapCamera.getRenderStartY() - 1
|
||||
val for_x_end = MapCamera.getRenderEndX()
|
||||
val for_y_end = MapCamera.getRenderEndY()
|
||||
|
||||
// draw
|
||||
try {
|
||||
for (y in for_y_start..for_y_end - 1) {
|
||||
var x = for_x_start
|
||||
while (x < for_x_end) {
|
||||
// smooth
|
||||
if (Terrarum.game.screenZoom >= 1 && Terrarum.gameConfig.getAsBoolean("smoothlighting")) {
|
||||
val thisLightLevel = staticLightMap!![y][x]
|
||||
if (y > 0 && x < for_x_end && thisLightLevel.toInt() == 0 && staticLightMap!![y - 1][x].toInt() == 0) {
|
||||
try {
|
||||
// coalesce zero intensity blocks to one
|
||||
var zeroLevelCounter = 1
|
||||
while (staticLightMap!![y][x + zeroLevelCounter].toInt() == 0 && staticLightMap!![y - 1][x + zeroLevelCounter].toInt() == 0) {
|
||||
zeroLevelCounter += 1
|
||||
|
||||
if (x + zeroLevelCounter >= for_x_end) break
|
||||
}
|
||||
|
||||
g.color = Color(0)
|
||||
g.fillRect(
|
||||
Math.round(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), Math.round(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), (FastMath.ceil(
|
||||
TSIZE * Terrarum.game.screenZoom) * zeroLevelCounter).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat())
|
||||
|
||||
x += zeroLevelCounter - 1
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
/** a
|
||||
* +-+-+
|
||||
* |i|j|
|
||||
* b +-+-+ c
|
||||
* |k|l|
|
||||
* +-+-+
|
||||
* d
|
||||
*/
|
||||
val a = if (y == 0)
|
||||
thisLightLevel
|
||||
else if (y == Terrarum.game.map.height - 1)
|
||||
thisLightLevel
|
||||
else
|
||||
maximiseRGB(
|
||||
staticLightMap!![y][x],
|
||||
staticLightMap!![y - 1][x])
|
||||
val d = if (y == 0)
|
||||
thisLightLevel
|
||||
else if (y == Terrarum.game.map.height - 1)
|
||||
thisLightLevel
|
||||
else
|
||||
maximiseRGB(
|
||||
staticLightMap!![y][x],
|
||||
staticLightMap!![y + 1][x])
|
||||
val b = if (x == 0)
|
||||
thisLightLevel
|
||||
else if (x == Terrarum.game.map.width - 1)
|
||||
thisLightLevel
|
||||
else
|
||||
maximiseRGB(
|
||||
staticLightMap!![y][x],
|
||||
staticLightMap!![y][x - 1])
|
||||
val c = if (x == 0)
|
||||
thisLightLevel
|
||||
else if (x == Terrarum.game.map.width - 1)
|
||||
thisLightLevel
|
||||
else
|
||||
maximiseRGB(
|
||||
staticLightMap!![y][x],
|
||||
staticLightMap!![y][x + 1])
|
||||
val colourMapItoL = CharArray(4)
|
||||
colourMapItoL[0] = colourLinearMix(a, b)
|
||||
colourMapItoL[1] = colourLinearMix(a, c)
|
||||
colourMapItoL[2] = colourLinearMix(b, d)
|
||||
colourMapItoL[3] = colourLinearMix(c, d)
|
||||
|
||||
for (iy in 0..1) {
|
||||
for (ix in 0..1) {
|
||||
g.color = toTargetColour(colourMapItoL[iy * 2 + ix])
|
||||
|
||||
g.fillRect(
|
||||
Math.round(
|
||||
x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom) + ix * TSIZE / 2 * Terrarum.game.screenZoom, Math.round(
|
||||
y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom) + iy * TSIZE / 2 * Terrarum.game.screenZoom, FastMath.ceil(TSIZE * Terrarum.game.screenZoom / 2).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom / 2).toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
val thisLightLevel = staticLightMap!![y][x].toInt()
|
||||
|
||||
// coalesce identical intensity blocks to one
|
||||
var sameLevelCounter = 1
|
||||
while (staticLightMap!![y][x + sameLevelCounter].toInt() == thisLightLevel) {
|
||||
sameLevelCounter += 1
|
||||
|
||||
if (x + sameLevelCounter >= for_x_end) break
|
||||
}
|
||||
|
||||
g.color = toTargetColour(staticLightMap!![y][x])
|
||||
g.fillRect(
|
||||
Math.round(x.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), Math.round(y.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), (FastMath.ceil(
|
||||
TSIZE * Terrarum.game.screenZoom) * sameLevelCounter).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat())
|
||||
|
||||
x += sameLevelCounter - 1
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}// Retro
|
||||
x++
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun calculate(x: Int, y: Int): Char {
|
||||
return calculate(x, y, false)
|
||||
}
|
||||
|
||||
private fun calculate(x: Int, y: Int, doNotCalculateAmbient: Boolean): Char {
|
||||
var lightLevelThis: Char = 0.toChar()
|
||||
val thisTerrain = Terrarum.game.map.getTileFromTerrain(x, y)
|
||||
val thisWall = Terrarum.game.map.getTileFromWall(x, y)
|
||||
val thisTileLuminosity = TilePropCodex.getProp(thisTerrain).luminosity
|
||||
val thisTileOpacity = TilePropCodex.getProp(thisTerrain).opacity
|
||||
val sunLight = Terrarum.game.map.globalLight
|
||||
|
||||
// MIX TILE
|
||||
// open air
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightLevelThis = sunLight
|
||||
}
|
||||
else if (thisWall == AIR && thisTileLuminosity.toInt() > 0) {
|
||||
val darkenSunlight = darkenColoured(sunLight, thisTileOpacity)
|
||||
lightLevelThis = screenBlend(darkenSunlight, thisTileLuminosity)
|
||||
}
|
||||
else if (thisWall != AIR && thisTileLuminosity.toInt() > 0) {
|
||||
lightLevelThis = thisTileLuminosity
|
||||
}// luminous tile (opaque)
|
||||
// luminous tile transparent (allows sunlight to pass)
|
||||
// END MIX TILE
|
||||
|
||||
// mix lantern
|
||||
for (lantern in lanterns) {
|
||||
if (lantern.x == x && lantern.y == y) {
|
||||
lightLevelThis = screenBlend(lightLevelThis, lantern.intensity)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// mix luminous actor
|
||||
for (actor in Terrarum.game.actorContainer) {
|
||||
if (actor is Luminous && actor is ActorWithBody) {
|
||||
val tileX = Math.round(actor.hitbox!!.pointedX / TSIZE)
|
||||
val tileY = Math.round(actor.hitbox!!.pointedY / TSIZE) - 1
|
||||
val actorLuminosity = actor.luminosity
|
||||
if (x == tileX && y == tileY) {
|
||||
lightLevelThis = screenBlend(lightLevelThis, actorLuminosity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!doNotCalculateAmbient) {
|
||||
// calculate ambient
|
||||
var ambient: Char = 0.toChar()
|
||||
var nearby: Char = 0.toChar()
|
||||
findNearbyBrightest@ for (yoff in -1..1) {
|
||||
for (xoff in -1..1) {
|
||||
/**
|
||||
* filter for 'v's as:
|
||||
* +-+-+-+
|
||||
* |a|v|a|
|
||||
* +-+-+-+
|
||||
* |v| |v|
|
||||
* +-+-+-+
|
||||
* |a|v|a|
|
||||
* +-+-+-+
|
||||
*/
|
||||
if (xoff != yoff && -xoff != yoff) {
|
||||
// 'v' tiles
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = staticLightMap!![y + yoff][x + xoff]
|
||||
}
|
||||
}
|
||||
else if (xoff != 0 && yoff != 0) {
|
||||
// 'a' tiles
|
||||
if (!outOfMapBounds(x + xoff, y + yoff)) {
|
||||
nearby = darkenUniformInt(staticLightMap!![y + yoff][x + xoff], 2) //2
|
||||
// mix some to have more 'spreading'
|
||||
// so that light spreads in a shape of an octagon instead of a diamond
|
||||
}
|
||||
}
|
||||
else {
|
||||
nearby = 0.toChar() // exclude 'me' tile
|
||||
}
|
||||
|
||||
ambient = maximiseRGB(ambient, nearby) // keep base value as brightest nearby
|
||||
}
|
||||
}
|
||||
|
||||
ambient = darkenColoured(ambient,
|
||||
thisTileOpacity) // get real ambient by appling opacity value
|
||||
|
||||
// mix and return lightlevel and ambient
|
||||
return maximiseRGB(lightLevelThis, ambient)
|
||||
}
|
||||
else {
|
||||
return lightLevelThis
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract each channel's RGB value.
|
||||
* It works like:
|
||||
* f(data, darken) = RGB(data.r - darken.r, data.g - darken.g, data.b - darken.b)
|
||||
|
||||
* @param data Raw channel value [0-39] per channel
|
||||
* *
|
||||
* @param darken [0-39] per channel
|
||||
* *
|
||||
* @return darkened data [0-39] per channel
|
||||
*/
|
||||
private fun darkenColoured(data: Char, darken: Char): Char {
|
||||
if (darken.toInt() < 0 || darken.toInt() >= COLOUR_DOMAIN_SIZE) {
|
||||
throw IllegalArgumentException("darken: out of " + "range")
|
||||
}
|
||||
|
||||
val r = clampZero(getR(data) - getR(darken))
|
||||
val g = clampZero(getG(data) - getG(darken))
|
||||
val b = clampZero(getB(data) - getB(darken))
|
||||
|
||||
return constructRGBFromFloat(r, g, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken each channel by 'darken' argument
|
||||
* It works like:
|
||||
* f(data, darken) = RGB(data.r - darken, data.g - darken, data.b - darken)
|
||||
* @param data [0-39] per channel
|
||||
* *
|
||||
* @param darken [0-1]
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun darkenUniformFloat(data: Char, darken: Float): Char {
|
||||
if (darken < 0 || darken > 1f) {
|
||||
throw IllegalArgumentException("darken: out of " + "range")
|
||||
}
|
||||
|
||||
val r = clampZero(getR(data) - darken)
|
||||
val g = clampZero(getG(data) - darken)
|
||||
val b = clampZero(getB(data) - darken)
|
||||
|
||||
return constructRGBFromFloat(r, g, b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Darken each channel by 'darken' argument
|
||||
* It works like:
|
||||
* f(data, darken) = RGB(data.r - darken, data.g - darken, data.b - darken)
|
||||
* @param data [0-39] per channel
|
||||
* *
|
||||
* @param darken [0-39]
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun darkenUniformInt(data: Char, darken: Int): Char {
|
||||
if (darken < 0 || darken > CHANNEL_MAX) {
|
||||
throw IllegalArgumentException("darken: out of " + "range")
|
||||
}
|
||||
|
||||
val r = clampZero(getRawR(data) - darken)
|
||||
val g = clampZero(getRawG(data) - darken)
|
||||
val b = clampZero(getRawB(data) - darken)
|
||||
|
||||
return constructRGBFromInt(r, g, b)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add each channel's RGB value.
|
||||
* It works like:
|
||||
* f(data, brighten) = RGB(data.r + darken.r, data.g + darken.g, data.b + darken.b)
|
||||
* @param data Raw channel value [0-39] per channel
|
||||
* *
|
||||
* @param brighten [0-39] per channel
|
||||
* *
|
||||
* @return brightened data [0-39] per channel
|
||||
*/
|
||||
private fun brightenColoured(data: Char, brighten: Char): Char {
|
||||
if (brighten.toInt() < 0 || brighten.toInt() >= COLOUR_DOMAIN_SIZE) {
|
||||
throw IllegalArgumentException("brighten: out of " + "range")
|
||||
}
|
||||
|
||||
val r = clampFloat(getR(data) + getR(brighten))
|
||||
val g = clampFloat(getG(data) + getG(brighten))
|
||||
val b = clampFloat(getB(data) + getB(brighten))
|
||||
|
||||
return constructRGBFromFloat(r, g, b)
|
||||
}
|
||||
|
||||
/** Get each channel from two RGB values, return new RGB that has max value of each channel
|
||||
* @param rgb
|
||||
* *
|
||||
* @param rgb2
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun maximiseRGB(rgb: Char, rgb2: Char): Char {
|
||||
val r1 = getRawR(rgb)
|
||||
val r2 = getRawR(rgb2)
|
||||
val newR = if (r1 > r2) r1 else r2
|
||||
val g1 = getRawG(rgb)
|
||||
val g2 = getRawG(rgb2)
|
||||
val newG = if (g1 > g2) g1 else g2
|
||||
val b1 = getRawB(rgb)
|
||||
val b2 = getRawB(rgb2)
|
||||
val newB = if (b1 > b2) b1 else b2
|
||||
|
||||
return constructRGBFromInt(newR, newG, newB)
|
||||
}
|
||||
|
||||
private fun screenBlend(rgb: Char, rgb2: Char): Char {
|
||||
val r1 = getR(rgb)
|
||||
val r2 = getR(rgb2)
|
||||
val newR = 1 - (1 - r1) * (1 - r2)
|
||||
val g1 = getG(rgb)
|
||||
val g2 = getG(rgb2)
|
||||
val newG = 1 - (1 - g1) * (1 - g2)
|
||||
val b1 = getB(rgb)
|
||||
val b2 = getB(rgb2)
|
||||
val newB = 1 - (1 - b1) * (1 - b2)
|
||||
|
||||
return constructRGBFromFloat(newR, newG, newB)
|
||||
}
|
||||
|
||||
fun getRawR(RGB: Char): Int {
|
||||
return RGB.toInt() / MUL_2
|
||||
}
|
||||
|
||||
fun getRawG(RGB: Char): Int {
|
||||
return RGB.toInt() % MUL_2 / MUL
|
||||
}
|
||||
|
||||
fun getRawB(RGB: Char): Int {
|
||||
return RGB.toInt() % MUL
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param RGB
|
||||
* *
|
||||
* @param offset 2 = R, 1 = G, 0 = B
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
fun getRaw(RGB: Char, offset: Int): Int {
|
||||
if (offset == OFFSET_R) return getRawR(RGB)
|
||||
if (offset == OFFSET_G) return getRawG(RGB)
|
||||
if (offset == OFFSET_B)
|
||||
return getRawB(RGB)
|
||||
else
|
||||
throw IllegalArgumentException("Channel offset out of range")
|
||||
}
|
||||
|
||||
private fun getR(rgb: Char): Float {
|
||||
return getRawR(rgb) / CHANNEL_MAX_FLOAT
|
||||
}
|
||||
|
||||
private fun getG(rgb: Char): Float {
|
||||
return getRawG(rgb) / CHANNEL_MAX_FLOAT
|
||||
}
|
||||
|
||||
private fun getB(rgb: Char): Float {
|
||||
return getRawB(rgb) / CHANNEL_MAX_FLOAT
|
||||
}
|
||||
|
||||
private fun addRaw(rgb1: Char, rgb2: Char): Char {
|
||||
val newR = clampByte(getRawR(rgb1) + getRawB(rgb2))
|
||||
val newG = clampByte(getRawG(rgb1) + getRawG(rgb2))
|
||||
val newB = clampByte(getRawB(rgb1) + getRawB(rgb2))
|
||||
|
||||
return constructRGBFromInt(newR, newG, newB)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun constructRGBFromInt(r: Int, g: Int, b: Int): Char {
|
||||
if (r < 0 || r > CHANNEL_MAX) {
|
||||
throw IllegalArgumentException("Red: out of range")
|
||||
}
|
||||
if (g < 0 || g > CHANNEL_MAX) {
|
||||
throw IllegalArgumentException("Green: out of range")
|
||||
}
|
||||
if (b < 0 || b > CHANNEL_MAX) {
|
||||
throw IllegalArgumentException("Blue: out of range")
|
||||
}
|
||||
return (r * MUL_2 + g * MUL + b).toChar()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun constructRGBFromFloat(r: Float, g: Float, b: Float): Char {
|
||||
if (r < 0 || r > 1.0f) {
|
||||
throw IllegalArgumentException("Red: out of range")
|
||||
}
|
||||
if (g < 0 || g > 1.0f) {
|
||||
throw IllegalArgumentException("Green: out of range")
|
||||
}
|
||||
if (b < 0 || b > 1.0f) {
|
||||
throw IllegalArgumentException("Blue: out of range")
|
||||
}
|
||||
|
||||
val intR = Math.round(r * CHANNEL_MAX)
|
||||
val intG = Math.round(g * CHANNEL_MAX)
|
||||
val intB = Math.round(b * CHANNEL_MAX)
|
||||
|
||||
return constructRGBFromInt(intR, intG, intB)
|
||||
}
|
||||
|
||||
private fun colourLinearMix(colA: Char, colB: Char): Char {
|
||||
val r = getRawR(colA) + getRawR(colB) shr 1
|
||||
val g = getRawG(colA) + getRawG(colB) shr 1
|
||||
val b = getRawB(colA) + getRawB(colB) shr 1
|
||||
return constructRGBFromInt(r, g, b)
|
||||
}
|
||||
|
||||
private fun quantise16(x: Int): Int {
|
||||
if (x < 0) throw IllegalArgumentException("positive integer only.")
|
||||
return x and 0xFFFFFFF0.toInt()
|
||||
}
|
||||
|
||||
private fun div16(x: Int): Int {
|
||||
if (x < 0) throw IllegalArgumentException("positive integer only.")
|
||||
return x and 0x7FFFFFFF shr 4
|
||||
}
|
||||
|
||||
private fun mul16(x: Int): Int {
|
||||
if (x < 0) throw IllegalArgumentException("positive integer only.")
|
||||
return x shl 4
|
||||
}
|
||||
|
||||
private fun max(vararg i: Int): Int {
|
||||
Arrays.sort(i)
|
||||
return i[i.size - 1]
|
||||
}
|
||||
|
||||
private fun min(vararg i: Int): Int {
|
||||
Arrays.sort(i)
|
||||
return i[0]
|
||||
}
|
||||
|
||||
private fun outOfBounds(x: Int, y: Int): Boolean {
|
||||
return x < 0 || y < 0 || x >= Terrarum.game.map.width || y >= Terrarum.game.map.height
|
||||
}
|
||||
|
||||
private fun outOfMapBounds(x: Int, y: Int): Boolean {
|
||||
return x < 0 || y < 0 || x >= staticLightMap!![0].size || y >= staticLightMap!!.size
|
||||
}
|
||||
|
||||
private fun clampZero(i: Int): Int {
|
||||
return if (i < 0) 0 else i
|
||||
}
|
||||
|
||||
private fun clampZero(i: Float): Float {
|
||||
return if (i < 0) 0f else i
|
||||
}
|
||||
|
||||
private fun clampByte(i: Int): Int {
|
||||
return if (i < 0) 0 else if (i > CHANNEL_MAX) CHANNEL_MAX else i
|
||||
}
|
||||
|
||||
private fun clampFloat(i: Float): Float {
|
||||
return if (i < 0) 0f else if (i > 1) 1f else i
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getValueFromMap(x: Int, y: Int): Char {
|
||||
return staticLightMap!![y][x]
|
||||
}
|
||||
|
||||
private fun purgePartOfLightmap(x1: Int, y1: Int, x2: Int, y2: Int) {
|
||||
try {
|
||||
for (y in y1 - 1..y2 + 1 - 1) {
|
||||
for (x in x1 - 1..x2 + 1 - 1) {
|
||||
if (y == y1 - 1 || y == y2 || x == x1 - 1 || x == x2) {
|
||||
// fill the rim with (pre) calculation
|
||||
staticLightMap!![y][x] = preCalculateUpdateGLOnly(x, y)
|
||||
}
|
||||
else {
|
||||
staticLightMap!![y][x] = 0.toChar()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun preCalculateUpdateGLOnly(x: Int, y: Int): Char {
|
||||
val thisWall = Terrarum.game.map.getTileFromWall(x, y)
|
||||
val thisTerrain = Terrarum.game.map.getTileFromTerrain(x, y)
|
||||
val thisTileLuminosity = TilePropCodex.getProp(thisTerrain).luminosity
|
||||
val thisTileOpacity = TilePropCodex.getProp(thisTerrain).opacity
|
||||
val sunLight = Terrarum.game.map.globalLight
|
||||
|
||||
val lightLevelThis: Char
|
||||
|
||||
// MIX TILE
|
||||
// open air
|
||||
if (thisTerrain == AIR && thisWall == AIR) {
|
||||
lightLevelThis = sunLight
|
||||
}
|
||||
else if (thisWall == AIR && thisTileLuminosity.toInt() > 0) {
|
||||
val darkenSunlight = darkenColoured(sunLight, thisTileOpacity)
|
||||
lightLevelThis = screenBlend(darkenSunlight, thisTileLuminosity)
|
||||
}
|
||||
else {
|
||||
lightLevelThis = getValueFromMap(x, y)
|
||||
}// luminous tile transparent (allows sunlight to pass)
|
||||
return lightLevelThis
|
||||
}
|
||||
|
||||
private fun clampWTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
}
|
||||
else if (x > Terrarum.game.map.width) {
|
||||
return Terrarum.game.map.width
|
||||
}
|
||||
else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private fun clampHTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
}
|
||||
else if (x > Terrarum.game.map.height) {
|
||||
return Terrarum.game.map.height
|
||||
}
|
||||
else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
private fun arithmeticAverage(vararg i: Int): Int {
|
||||
var sum = 0
|
||||
for (k in i.indices) {
|
||||
sum += i[k]
|
||||
}
|
||||
return Math.round(sum / i.size.toFloat())
|
||||
}
|
||||
|
||||
private fun toTargetColour(raw: Char): Color {
|
||||
return Col40().createSlickColor(raw.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
internal data class LightmapLantern(var x: Int, var y: Int, var intensity: Char)
|
||||
@@ -1,587 +0,0 @@
|
||||
package com.Torvald.Terrarum.MapDrawer;
|
||||
|
||||
import com.Torvald.Terrarum.*;
|
||||
import com.Torvald.Terrarum.Actors.Player;
|
||||
import com.Torvald.Terrarum.GameMap.GameMap;
|
||||
import com.Torvald.Terrarum.GameMap.PairedMapLayer;
|
||||
import com.Torvald.Terrarum.TileProperties.TileNameCode;
|
||||
import com.Torvald.Terrarum.TileProperties.TilePropCodex;
|
||||
import com.jme3.math.FastMath;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.newdawn.slick.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-01-19.
|
||||
*/
|
||||
public class MapCamera {
|
||||
|
||||
private static GameMap map;
|
||||
|
||||
private static int cameraX = 0;
|
||||
private static int cameraY = 0;
|
||||
|
||||
private static SpriteSheet tilesWall;
|
||||
private static SpriteSheet tilesTerrain;
|
||||
private static SpriteSheet tilesWire;
|
||||
|
||||
private static int TSIZE = MapDrawer.TILE_SIZE;
|
||||
|
||||
|
||||
private static SpriteSheet[] tilesetBook;
|
||||
|
||||
private static final int WALL = GameMap.WALL;
|
||||
private static final int TERRAIN = GameMap.TERRAIN;
|
||||
private static final int WIRE = GameMap.WIRE;
|
||||
|
||||
private static int renderWidth;
|
||||
private static int renderHeight;
|
||||
|
||||
private static final int NEARBY_TILE_KEY_UP = 0;
|
||||
private static final int NEARBY_TILE_KEY_RIGHT = 1;
|
||||
private static final int NEARBY_TILE_KEY_DOWN = 2;
|
||||
private static final int NEARBY_TILE_KEY_LEFT = 3;
|
||||
|
||||
private static final int NEARBY_TILE_CODE_UP = 0b0001;
|
||||
private static final int NEARBY_TILE_CODE_RIGHT = 0b0010;
|
||||
private static final int NEARBY_TILE_CODE_DOWN = 0b0100;
|
||||
private static final int NEARBY_TILE_CODE_LEFT = 0b1000;
|
||||
|
||||
/**
|
||||
* Connectivity group 01 : artificial tiles
|
||||
* It holds different shading rule to discriminate with group 02, index 0 is single tile.
|
||||
* These are the tiles that only connects to itself, will not connect to colour variants
|
||||
*/
|
||||
private static Integer[] TILES_CONNECT_SELF = {
|
||||
TileNameCode.ICE_MAGICAL
|
||||
, TileNameCode.ILLUMINATOR_BLACK
|
||||
, TileNameCode.ILLUMINATOR_BLUE
|
||||
, TileNameCode.ILLUMINATOR_BROWN
|
||||
, TileNameCode.ILLUMINATOR_CYAN
|
||||
, TileNameCode.ILLUMINATOR_FUCHSIA
|
||||
, TileNameCode.ILLUMINATOR_GREEN
|
||||
, TileNameCode.ILLUMINATOR_GREEN_DARK
|
||||
, TileNameCode.ILLUMINATOR_GREY_DARK
|
||||
, TileNameCode.ILLUMINATOR_GREY_LIGHT
|
||||
, TileNameCode.ILLUMINATOR_GREY_MED
|
||||
, TileNameCode.ILLUMINATOR_ORANGE
|
||||
, TileNameCode.ILLUMINATOR_PURPLE
|
||||
, TileNameCode.ILLUMINATOR_RED
|
||||
, TileNameCode.ILLUMINATOR_TAN
|
||||
, TileNameCode.ILLUMINATOR_WHITE
|
||||
, TileNameCode.ILLUMINATOR_YELLOW
|
||||
, TileNameCode.ILLUMINATOR_BLACK_OFF
|
||||
, TileNameCode.ILLUMINATOR_BLUE_OFF
|
||||
, TileNameCode.ILLUMINATOR_BROWN_OFF
|
||||
, TileNameCode.ILLUMINATOR_CYAN_OFF
|
||||
, TileNameCode.ILLUMINATOR_FUCHSIA_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREEN_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREEN_DARK_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_DARK_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_LIGHT_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_MED_OFF
|
||||
, TileNameCode.ILLUMINATOR_ORANGE_OFF
|
||||
, TileNameCode.ILLUMINATOR_PURPLE_OFF
|
||||
, TileNameCode.ILLUMINATOR_RED_OFF
|
||||
, TileNameCode.ILLUMINATOR_TAN_OFF
|
||||
, TileNameCode.ILLUMINATOR_WHITE_OFF
|
||||
, TileNameCode.ILLUMINATOR_YELLOW
|
||||
, TileNameCode.SANDSTONE
|
||||
, TileNameCode.SANDSTONE_BLACK
|
||||
, TileNameCode.SANDSTONE_DESERT
|
||||
, TileNameCode.SANDSTONE_RED
|
||||
, TileNameCode.SANDSTONE_WHITE
|
||||
, TileNameCode.SANDSTONE_GREEN
|
||||
};
|
||||
|
||||
/**
|
||||
* Connectivity group 02 : natural tiles
|
||||
* It holds different shading rule to discriminate with group 01, index 0 is middle tile.
|
||||
*/
|
||||
private static Integer[] TILES_CONNECT_MUTUAL = {
|
||||
TileNameCode.STONE
|
||||
, TileNameCode.DIRT
|
||||
, TileNameCode.GRASS
|
||||
, TileNameCode.PLANK_BIRCH
|
||||
, TileNameCode.PLANK_BLOODROSE
|
||||
, TileNameCode.PLANK_EBONY
|
||||
, TileNameCode.PLANK_NORMAL
|
||||
, TileNameCode.SAND
|
||||
, TileNameCode.SAND_BEACH
|
||||
, TileNameCode.SAND_RED
|
||||
, TileNameCode.SAND_DESERT
|
||||
, TileNameCode.SAND_BLACK
|
||||
, TileNameCode.SAND_GREEN
|
||||
, TileNameCode.GRAVEL
|
||||
, TileNameCode.GRAVEL_GREY
|
||||
, TileNameCode.SNOW
|
||||
, TileNameCode.ICE_NATURAL
|
||||
, TileNameCode.ORE_COPPER
|
||||
, TileNameCode.ORE_IRON
|
||||
, TileNameCode.ORE_GOLD
|
||||
, TileNameCode.ORE_SILVER
|
||||
, TileNameCode.ORE_ILMENITE
|
||||
, TileNameCode.ORE_AURICHALCUM
|
||||
|
||||
, TileNameCode.WATER_1
|
||||
, TileNameCode.WATER_2
|
||||
, TileNameCode.WATER_3
|
||||
, TileNameCode.WATER_4
|
||||
, TileNameCode.WATER_5
|
||||
, TileNameCode.WATER_6
|
||||
, TileNameCode.WATER_7
|
||||
, TileNameCode.WATER_8
|
||||
, TileNameCode.WATER_9
|
||||
, TileNameCode.WATER_10
|
||||
, TileNameCode.WATER_11
|
||||
, TileNameCode.WATER_12
|
||||
, TileNameCode.WATER_13
|
||||
, TileNameCode.WATER_14
|
||||
, TileNameCode.WATER_15
|
||||
, TileNameCode.LAVA
|
||||
, TileNameCode.LAVA_1
|
||||
, TileNameCode.LAVA_2
|
||||
, TileNameCode.LAVA_3
|
||||
, TileNameCode.LAVA_4
|
||||
, TileNameCode.LAVA_5
|
||||
, TileNameCode.LAVA_6
|
||||
, TileNameCode.LAVA_7
|
||||
, TileNameCode.LAVA_8
|
||||
, TileNameCode.LAVA_9
|
||||
, TileNameCode.LAVA_10
|
||||
, TileNameCode.LAVA_11
|
||||
, TileNameCode.LAVA_12
|
||||
, TileNameCode.LAVA_13
|
||||
, TileNameCode.LAVA_14
|
||||
, TileNameCode.LAVA_15
|
||||
, TileNameCode.LAVA
|
||||
};
|
||||
|
||||
/**
|
||||
* Torches, levers, switches, ...
|
||||
*/
|
||||
private static Integer[] TILES_WALL_STICKER = {
|
||||
TileNameCode.TORCH
|
||||
, TileNameCode.TORCH_FROST
|
||||
, TileNameCode.TORCH_OFF
|
||||
, TileNameCode.TORCH_FROST_OFF
|
||||
};
|
||||
|
||||
/**
|
||||
* platforms, ...
|
||||
*/
|
||||
private static Integer[] TILES_WALL_STICKER_CONNECT_SELF = {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Tiles that half-transparent and has hue
|
||||
* will blend colour using colour multiplication
|
||||
* i.e. red hues get lost if you dive into the water
|
||||
*/
|
||||
private static Integer[] TILES_BLEND_MUL = {
|
||||
TileNameCode.WATER
|
||||
, TileNameCode.LAVA
|
||||
};
|
||||
|
||||
/**
|
||||
* @param map
|
||||
*/
|
||||
public MapCamera(GameMap map) throws SlickException {
|
||||
this.map = map;
|
||||
|
||||
tilesWall = new SpriteSheet("./res/graphics/terrain/wall.png"
|
||||
, TSIZE
|
||||
, TSIZE
|
||||
);
|
||||
|
||||
tilesTerrain = new SpriteSheet("./res/graphics/terrain/terrain.png"
|
||||
, TSIZE
|
||||
, TSIZE
|
||||
);
|
||||
|
||||
tilesWire = new SpriteSheet("./res/graphics/terrain/wire.png"
|
||||
, TSIZE
|
||||
, TSIZE
|
||||
);
|
||||
|
||||
tilesetBook = new SpriteSheet[3];
|
||||
tilesetBook[WALL] = tilesWall;
|
||||
tilesetBook[TERRAIN] = tilesTerrain;
|
||||
tilesetBook[WIRE] = tilesWire;
|
||||
}
|
||||
|
||||
public static void update(GameContainer gc, int delta_t) {
|
||||
Player player = Terrarum.game.getPlayer();
|
||||
|
||||
renderWidth = FastMath.ceil(Terrarum.WIDTH / Terrarum.game.screenZoom); // div, not mul
|
||||
renderHeight = FastMath.ceil(Terrarum.HEIGHT / Terrarum.game.screenZoom);
|
||||
|
||||
// position - (WH / 2)
|
||||
cameraX = Math.round(FastMath.clamp(
|
||||
player.getHitbox().getCenteredX() - (renderWidth / 2)
|
||||
, TSIZE, map.width * TSIZE - renderWidth - TSIZE
|
||||
));
|
||||
cameraY = Math.round(FastMath.clamp(
|
||||
player.getHitbox().getCenteredY() - (renderHeight / 2)
|
||||
, TSIZE, map.height * TSIZE - renderHeight - TSIZE
|
||||
));
|
||||
}
|
||||
|
||||
public static void renderBehind(GameContainer gc, Graphics g) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
setBlendModeNormal();
|
||||
drawTiles(WALL, false);
|
||||
drawTiles(TERRAIN, false);
|
||||
}
|
||||
|
||||
public static void renderFront(GameContainer gc, Graphics g) {
|
||||
setBlendModeMul();
|
||||
drawTiles(TERRAIN, true);
|
||||
setBlendModeNormal();
|
||||
}
|
||||
|
||||
private static void drawTiles(int mode, boolean drawModeTilesBlendMul) {
|
||||
int for_y_start = div16(cameraY);
|
||||
int for_x_start = div16(cameraX);
|
||||
|
||||
int for_y_end = clampHTile(for_y_start + div16(renderHeight) + 2);
|
||||
int for_x_end = clampWTile(for_x_start + div16(renderWidth) + 2);
|
||||
|
||||
// initialise
|
||||
tilesetBook[mode].startUse();
|
||||
|
||||
// loop
|
||||
for (int y = for_y_start; y < for_y_end; y++) {
|
||||
for (int x = for_x_start; x < for_x_end; x++) {
|
||||
|
||||
int thisTile;
|
||||
if (mode % 3 == WALL) thisTile = map.getTileFromWall(x, y);
|
||||
else if (mode % 3 == TERRAIN) thisTile = map.getTileFromTerrain(x, y);
|
||||
else if (mode % 3 == WIRE) thisTile = map.getTileFromWire(x, y);
|
||||
else throw new IllegalArgumentException();
|
||||
|
||||
boolean noDamageLayer = (mode % 3 == WIRE);
|
||||
|
||||
// draw
|
||||
try {
|
||||
if (
|
||||
|
||||
(
|
||||
( // wall and not blocked
|
||||
(mode == WALL)
|
||||
)
|
||||
||
|
||||
(mode == TERRAIN)
|
||||
) // not an air tile
|
||||
&& (thisTile > 0)
|
||||
&&
|
||||
// check if light level of upper tile is zero and
|
||||
// that of this tile is also zero
|
||||
(((y > 0)
|
||||
&& !((LightmapRenderer.getValueFromMap(x, y) == 0)
|
||||
&& (LightmapRenderer.getValueFromMap(x, y - 1) == 0))
|
||||
)
|
||||
||
|
||||
// check if light level of this tile is zero, for y = 0
|
||||
((y == 0)
|
||||
&& (LightmapRenderer.getValueFromMap(x, y) > 0)
|
||||
))) {
|
||||
|
||||
int nearbyTilesInfo;
|
||||
if (isWallSticker(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y);
|
||||
}
|
||||
else if (isConnectMutual(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode);
|
||||
}
|
||||
else if (isConnectSelf(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile);
|
||||
}
|
||||
else {
|
||||
nearbyTilesInfo = 0;
|
||||
}
|
||||
|
||||
|
||||
int thisTileX;
|
||||
if (!noDamageLayer)
|
||||
thisTileX = PairedMapLayer.RANGE * (thisTile % PairedMapLayer.RANGE)
|
||||
+ nearbyTilesInfo;
|
||||
else
|
||||
thisTileX = nearbyTilesInfo;
|
||||
|
||||
int thisTileY = thisTile / PairedMapLayer.RANGE;
|
||||
|
||||
if (drawModeTilesBlendMul) {
|
||||
if (isBlendMul(thisTile)) {
|
||||
drawTile(mode, x, y, thisTileX, thisTileY);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// currently it draws all the transparent tile and colour mixes
|
||||
// on top of the previously drawn tile
|
||||
// TODO check wether it works as intended when skybox is dark
|
||||
// add instruction "if (!isBlendMul((byte) thisTile))"
|
||||
if (!isBlendMul(thisTile)) {
|
||||
drawTile(mode, x, y, thisTileX, thisTileY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
// do nothing. This exception handling may hide erratic behaviour completely.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tilesetBook[mode].endUse();
|
||||
}
|
||||
|
||||
private static int getGrassInfo(int x, int y, int from, int to) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return [0-15] 1: up, 2: right, 4: down, 8: left
|
||||
*/
|
||||
private static int getNearbyTilesInfo(int x, int y, int mode, int mark) {
|
||||
int[] nearbyTiles = new int[4];
|
||||
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y); }
|
||||
|
||||
if (x == map.width - 1) { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y); }
|
||||
|
||||
if (y == 0) { nearbyTiles[NEARBY_TILE_KEY_UP] = 0; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1); }
|
||||
|
||||
if (y == map.height - 1) { nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1); }
|
||||
|
||||
// try for
|
||||
int ret = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (nearbyTiles[i] == mark) {
|
||||
ret += (1 << i); // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static int getNearbyTilesInfoNonSolid(int x, int y, int mode) {
|
||||
int[] nearbyTiles = new int[4];
|
||||
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y); }
|
||||
|
||||
if (x == map.width - 1) { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y); }
|
||||
|
||||
if (y == 0) { nearbyTiles[NEARBY_TILE_KEY_UP] = 0; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1); }
|
||||
|
||||
if (y == map.height - 1) { nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1); }
|
||||
|
||||
// try for
|
||||
int ret = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
if (!TilePropCodex.getProp(nearbyTiles[i]).isSolid()) {
|
||||
ret += (1 << i); // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static int getNearbyTilesInfoWallSticker(int x, int y) {
|
||||
int[] nearbyTiles = new int[4];
|
||||
int NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP;
|
||||
if (x == 0) { nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y); }
|
||||
|
||||
if (x == map.width - 1) { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(TERRAIN, x + 1, y); }
|
||||
|
||||
if (y == map.height - 1) { nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096; }
|
||||
else { nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(TERRAIN, x, y + 1); }
|
||||
|
||||
nearbyTiles[NEARBY_TILE_KEY_BACK] = map.getTileFrom(WALL, x, y);
|
||||
|
||||
try {
|
||||
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid()
|
||||
&& TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid()) {
|
||||
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid())
|
||||
return 0;
|
||||
else return 3;
|
||||
}
|
||||
else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid()) {
|
||||
return 2;
|
||||
}
|
||||
else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid()) {
|
||||
return 1;
|
||||
}
|
||||
else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid()) {
|
||||
return 0;
|
||||
}
|
||||
else return 3;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e) {
|
||||
return TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid()
|
||||
? 0 : 3;
|
||||
}
|
||||
}
|
||||
|
||||
private static void drawTile(int mode, int tilewisePosX, int tilewisePosY, int sheetX, int sheetY) {
|
||||
if (Terrarum.game.screenZoom == 1) {
|
||||
tilesetBook[mode].renderInUse(
|
||||
FastMath.floor(tilewisePosX * TSIZE)
|
||||
, FastMath.floor(tilewisePosY * TSIZE)
|
||||
, sheetX
|
||||
, sheetY
|
||||
);
|
||||
}
|
||||
else {
|
||||
tilesetBook[mode].getSprite(
|
||||
sheetX
|
||||
, sheetY
|
||||
).drawEmbedded(
|
||||
Math.round(tilewisePosX * TSIZE * Terrarum.game.screenZoom)
|
||||
, Math.round(tilewisePosY * TSIZE * Terrarum.game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Terrarum.game.screenZoom)
|
||||
, FastMath.ceil(TSIZE * Terrarum.game.screenZoom)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static int div16(int x) {
|
||||
return (x & 0x7FFF_FFFF) >> 4;
|
||||
}
|
||||
|
||||
public static int mod16(int x) {
|
||||
return x & 0b1111;
|
||||
}
|
||||
|
||||
public static int quantise16(int x) {
|
||||
return (x & 0xFFFF_FFF0);
|
||||
}
|
||||
|
||||
public static int clampW(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.width * TSIZE) {
|
||||
return map.width * TSIZE;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public static int clampH(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.height * TSIZE) {
|
||||
return map.height * TSIZE;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public static int clampWTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.width) {
|
||||
return map.width;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public static int clampHTile(int x) {
|
||||
if (x < 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (x > map.height) {
|
||||
return map.height;
|
||||
}
|
||||
else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getCameraX() {
|
||||
return cameraX;
|
||||
}
|
||||
|
||||
public static int getCameraY() {
|
||||
return cameraY;
|
||||
}
|
||||
|
||||
public static int getRenderWidth() {
|
||||
return renderWidth;
|
||||
}
|
||||
|
||||
public static int getRenderHeight() {
|
||||
return renderHeight;
|
||||
}
|
||||
|
||||
public static int getRenderStartX() {
|
||||
return div16(cameraX);
|
||||
}
|
||||
|
||||
public static int getRenderStartY() {
|
||||
return div16(cameraY);
|
||||
}
|
||||
|
||||
public static int getRenderEndX() {
|
||||
return clampWTile(getRenderStartX() + div16(renderWidth) + 2);
|
||||
}
|
||||
|
||||
public static int getRenderEndY() {
|
||||
return clampHTile(getRenderStartY() + div16(renderHeight) + 2);
|
||||
}
|
||||
|
||||
private static boolean isConnectSelf(int b) {
|
||||
return Arrays.asList(TILES_CONNECT_SELF).contains(b);
|
||||
}
|
||||
|
||||
private static boolean isConnectMutual(int b) {
|
||||
return Arrays.asList(TILES_CONNECT_MUTUAL).contains(b);
|
||||
}
|
||||
|
||||
private static boolean isWallSticker(int b) {
|
||||
return Arrays.asList(TILES_WALL_STICKER).contains(b);
|
||||
}
|
||||
|
||||
private static boolean isPlatform(int b) {
|
||||
return Arrays.asList(TILES_WALL_STICKER_CONNECT_SELF).contains(b);
|
||||
}
|
||||
|
||||
private static boolean isBlendMul(int b) {
|
||||
return Arrays.asList(TILES_BLEND_MUL).contains(b);
|
||||
}
|
||||
|
||||
private static void setBlendModeMul() {
|
||||
GL11.glEnable(GL11.GL_BLEND);
|
||||
GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
private static void setBlendModeNormal() {
|
||||
GL11.glDisable(GL11.GL_BLEND);
|
||||
Terrarum.appgc.getGraphics().setDrawMode(Graphics.MODE_NORMAL);
|
||||
}
|
||||
}
|
||||
571
src/com/Torvald/Terrarum/MapDrawer/MapCamera.kt
Normal file
571
src/com/Torvald/Terrarum/MapDrawer/MapCamera.kt
Normal file
@@ -0,0 +1,571 @@
|
||||
package com.Torvald.Terrarum.MapDrawer
|
||||
|
||||
import com.Torvald.Terrarum.GameMap.GameMap
|
||||
import com.Torvald.Terrarum.GameMap.PairedMapLayer
|
||||
import com.Torvald.Terrarum.Terrarum
|
||||
import com.Torvald.Terrarum.TileProperties.TileNameCode
|
||||
import com.Torvald.Terrarum.TileProperties.TilePropCodex
|
||||
import com.jme3.math.FastMath
|
||||
import org.lwjgl.opengl.GL11
|
||||
import org.newdawn.slick.GameContainer
|
||||
import org.newdawn.slick.Graphics
|
||||
import org.newdawn.slick.SlickException
|
||||
import org.newdawn.slick.SpriteSheet
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object MapCamera {
|
||||
private val map: GameMap = Terrarum.game.map;
|
||||
|
||||
internal var cameraX = 0
|
||||
internal var cameraY = 0
|
||||
|
||||
private val TSIZE = MapDrawer.TILE_SIZE
|
||||
|
||||
private var tilesWall: SpriteSheet = SpriteSheet("./res/graphics/terrain/wall.png", TSIZE, TSIZE)
|
||||
private var tilesTerrain: SpriteSheet = SpriteSheet("./res/graphics/terrain/terrain.png", TSIZE, TSIZE)
|
||||
private var tilesWire: SpriteSheet = SpriteSheet("./res/graphics/terrain/wire.png", TSIZE, TSIZE)
|
||||
private var tilesetBook: Array<SpriteSheet> = arrayOf(tilesWall, tilesTerrain, tilesWire)
|
||||
|
||||
private val WALL = GameMap.WALL
|
||||
private val TERRAIN = GameMap.TERRAIN
|
||||
private val WIRE = GameMap.WIRE
|
||||
|
||||
private var renderWidth: Int = 0
|
||||
private var renderHeight: Int = 0
|
||||
|
||||
private val NEARBY_TILE_KEY_UP = 0
|
||||
private val NEARBY_TILE_KEY_RIGHT = 1
|
||||
private val NEARBY_TILE_KEY_DOWN = 2
|
||||
private val NEARBY_TILE_KEY_LEFT = 3
|
||||
|
||||
private val NEARBY_TILE_CODE_UP = 1
|
||||
private val NEARBY_TILE_CODE_RIGHT = 2
|
||||
private val NEARBY_TILE_CODE_DOWN = 4
|
||||
private val NEARBY_TILE_CODE_LEFT = 8
|
||||
|
||||
/**
|
||||
* Connectivity group 01 : artificial tiles
|
||||
* It holds different shading rule to discriminate with group 02, index 0 is single tile.
|
||||
* These are the tiles that only connects to itself, will not connect to colour variants
|
||||
*/
|
||||
private val TILES_CONNECT_SELF = arrayOf(
|
||||
TileNameCode.ICE_MAGICAL
|
||||
, TileNameCode.ILLUMINATOR_BLACK
|
||||
, TileNameCode.ILLUMINATOR_BLUE
|
||||
, TileNameCode.ILLUMINATOR_BROWN
|
||||
, TileNameCode.ILLUMINATOR_CYAN
|
||||
, TileNameCode.ILLUMINATOR_FUCHSIA
|
||||
, TileNameCode.ILLUMINATOR_GREEN
|
||||
, TileNameCode.ILLUMINATOR_GREEN_DARK
|
||||
, TileNameCode.ILLUMINATOR_GREY_DARK
|
||||
, TileNameCode.ILLUMINATOR_GREY_LIGHT
|
||||
, TileNameCode.ILLUMINATOR_GREY_MED
|
||||
, TileNameCode.ILLUMINATOR_ORANGE
|
||||
, TileNameCode.ILLUMINATOR_PURPLE
|
||||
, TileNameCode.ILLUMINATOR_RED
|
||||
, TileNameCode.ILLUMINATOR_TAN
|
||||
, TileNameCode.ILLUMINATOR_WHITE
|
||||
, TileNameCode.ILLUMINATOR_YELLOW
|
||||
, TileNameCode.ILLUMINATOR_BLACK_OFF
|
||||
, TileNameCode.ILLUMINATOR_BLUE_OFF
|
||||
, TileNameCode.ILLUMINATOR_BROWN_OFF
|
||||
, TileNameCode.ILLUMINATOR_CYAN_OFF
|
||||
, TileNameCode.ILLUMINATOR_FUCHSIA_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREEN_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREEN_DARK_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_DARK_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_LIGHT_OFF
|
||||
, TileNameCode.ILLUMINATOR_GREY_MED_OFF
|
||||
, TileNameCode.ILLUMINATOR_ORANGE_OFF
|
||||
, TileNameCode.ILLUMINATOR_PURPLE_OFF
|
||||
, TileNameCode.ILLUMINATOR_RED_OFF
|
||||
, TileNameCode.ILLUMINATOR_TAN_OFF
|
||||
, TileNameCode.ILLUMINATOR_WHITE_OFF
|
||||
, TileNameCode.ILLUMINATOR_YELLOW
|
||||
, TileNameCode.SANDSTONE
|
||||
, TileNameCode.SANDSTONE_BLACK
|
||||
, TileNameCode.SANDSTONE_DESERT
|
||||
, TileNameCode.SANDSTONE_RED
|
||||
, TileNameCode.SANDSTONE_WHITE
|
||||
, TileNameCode.SANDSTONE_GREEN
|
||||
)
|
||||
|
||||
/**
|
||||
* Connectivity group 02 : natural tiles
|
||||
* It holds different shading rule to discriminate with group 01, index 0 is middle tile.
|
||||
*/
|
||||
private val TILES_CONNECT_MUTUAL = arrayOf(
|
||||
TileNameCode.STONE
|
||||
, TileNameCode.DIRT
|
||||
, TileNameCode.GRASS
|
||||
, TileNameCode.PLANK_BIRCH
|
||||
, TileNameCode.PLANK_BLOODROSE
|
||||
, TileNameCode.PLANK_EBONY
|
||||
, TileNameCode.PLANK_NORMAL
|
||||
, TileNameCode.SAND
|
||||
, TileNameCode.SAND_BEACH
|
||||
, TileNameCode.SAND_RED
|
||||
, TileNameCode.SAND_DESERT
|
||||
, TileNameCode.SAND_BLACK
|
||||
, TileNameCode.SAND_GREEN
|
||||
, TileNameCode.GRAVEL
|
||||
, TileNameCode.GRAVEL_GREY
|
||||
, TileNameCode.SNOW
|
||||
, TileNameCode.ICE_NATURAL
|
||||
, TileNameCode.ORE_COPPER
|
||||
, TileNameCode.ORE_IRON
|
||||
, TileNameCode.ORE_GOLD
|
||||
, TileNameCode.ORE_SILVER
|
||||
, TileNameCode.ORE_ILMENITE
|
||||
, TileNameCode.ORE_AURICHALCUM
|
||||
|
||||
, TileNameCode.WATER_1
|
||||
, TileNameCode.WATER_2
|
||||
, TileNameCode.WATER_3
|
||||
, TileNameCode.WATER_4
|
||||
, TileNameCode.WATER_5
|
||||
, TileNameCode.WATER_6
|
||||
, TileNameCode.WATER_7
|
||||
, TileNameCode.WATER_8
|
||||
, TileNameCode.WATER_9
|
||||
, TileNameCode.WATER_10
|
||||
, TileNameCode.WATER_11
|
||||
, TileNameCode.WATER_12
|
||||
, TileNameCode.WATER_13
|
||||
, TileNameCode.WATER_14
|
||||
, TileNameCode.WATER_15
|
||||
, TileNameCode.LAVA
|
||||
, TileNameCode.LAVA_1
|
||||
, TileNameCode.LAVA_2
|
||||
, TileNameCode.LAVA_3
|
||||
, TileNameCode.LAVA_4
|
||||
, TileNameCode.LAVA_5
|
||||
, TileNameCode.LAVA_6
|
||||
, TileNameCode.LAVA_7
|
||||
, TileNameCode.LAVA_8
|
||||
, TileNameCode.LAVA_9
|
||||
, TileNameCode.LAVA_10
|
||||
, TileNameCode.LAVA_11
|
||||
, TileNameCode.LAVA_12
|
||||
, TileNameCode.LAVA_13
|
||||
, TileNameCode.LAVA_14
|
||||
, TileNameCode.LAVA_15
|
||||
, TileNameCode.LAVA
|
||||
)
|
||||
|
||||
/**
|
||||
* Torches, levers, switches, ...
|
||||
*/
|
||||
private val TILES_WALL_STICKER = arrayOf(
|
||||
TileNameCode.TORCH
|
||||
, TileNameCode.TORCH_FROST
|
||||
, TileNameCode.TORCH_OFF
|
||||
, TileNameCode.TORCH_FROST_OFF
|
||||
)
|
||||
|
||||
/**
|
||||
* platforms, ...
|
||||
*/
|
||||
private val TILES_WALL_STICKER_CONNECT_SELF = arrayOf<Int>(
|
||||
|
||||
)
|
||||
|
||||
/**
|
||||
* Tiles that half-transparent and has hue
|
||||
* will blend colour using colour multiplication
|
||||
* i.e. red hues get lost if you dive into the water
|
||||
*/
|
||||
private val TILES_BLEND_MUL = arrayOf(
|
||||
TileNameCode.WATER
|
||||
, TileNameCode.LAVA
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun update(gc: GameContainer, delta_t: Int) {
|
||||
val player = Terrarum.game.player
|
||||
|
||||
renderWidth = FastMath.ceil(Terrarum.WIDTH / Terrarum.game.screenZoom) // div, not mul
|
||||
renderHeight = FastMath.ceil(Terrarum.HEIGHT / Terrarum.game.screenZoom)
|
||||
|
||||
// position - (WH / 2)
|
||||
cameraX = Math.round(FastMath.clamp(
|
||||
player.hitbox!!.centeredX - renderWidth / 2, TSIZE.toFloat(), map.width * TSIZE - renderWidth - TSIZE.toFloat()))
|
||||
cameraY = Math.round(FastMath.clamp(
|
||||
player.hitbox!!.centeredY - renderHeight / 2, TSIZE.toFloat(), map.height * TSIZE - renderHeight - TSIZE.toFloat()))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun renderBehind(gc: GameContainer, g: Graphics) {
|
||||
/**
|
||||
* render to camera
|
||||
*/
|
||||
setBlendModeNormal()
|
||||
drawTiles(WALL, false)
|
||||
drawTiles(TERRAIN, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun renderFront(gc: GameContainer, g: Graphics) {
|
||||
setBlendModeMul()
|
||||
drawTiles(TERRAIN, true)
|
||||
setBlendModeNormal()
|
||||
}
|
||||
|
||||
private fun drawTiles(mode: Int, drawModeTilesBlendMul: Boolean) {
|
||||
val for_y_start = div16(cameraY)
|
||||
val for_x_start = div16(cameraX)
|
||||
|
||||
val for_y_end = clampHTile(for_y_start + div16(renderHeight) + 2)
|
||||
val for_x_end = clampWTile(for_x_start + div16(renderWidth) + 2)
|
||||
|
||||
// initialise
|
||||
tilesetBook[mode].startUse()
|
||||
|
||||
// loop
|
||||
for (y in for_y_start..for_y_end - 1) {
|
||||
for (x in for_x_start..for_x_end - 1) {
|
||||
|
||||
val thisTile: Int
|
||||
if (mode % 3 == WALL)
|
||||
thisTile = map.getTileFromWall(x, y)
|
||||
else if (mode % 3 == TERRAIN)
|
||||
thisTile = map.getTileFromTerrain(x, y)
|
||||
else if (mode % 3 == WIRE)
|
||||
thisTile = map.getTileFromWire(x, y)
|
||||
else
|
||||
throw IllegalArgumentException()
|
||||
|
||||
val noDamageLayer = mode % 3 == WIRE
|
||||
|
||||
// draw
|
||||
try {
|
||||
if (
|
||||
|
||||
(mode == WALL || mode == TERRAIN) // not an air tile
|
||||
|
||||
&& thisTile > 0
|
||||
&&
|
||||
// check if light level of upper tile is zero and
|
||||
// that of this tile is also zero
|
||||
(y > 0 && !(LightmapRenderer.getValueFromMap(x, y).toInt() == 0 && LightmapRenderer.getValueFromMap(x, y - 1).toInt() == 0) || // check if light level of this tile is zero, for y = 0
|
||||
y == 0 && LightmapRenderer.getValueFromMap(x, y).toInt() > 0)) {
|
||||
|
||||
val nearbyTilesInfo: Int
|
||||
if (isWallSticker(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfoWallSticker(x, y)
|
||||
} else if (isConnectMutual(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfoNonSolid(x, y, mode)
|
||||
} else if (isConnectSelf(thisTile)) {
|
||||
nearbyTilesInfo = getNearbyTilesInfo(x, y, mode, thisTile)
|
||||
} else {
|
||||
nearbyTilesInfo = 0
|
||||
}
|
||||
|
||||
|
||||
val thisTileX: Int
|
||||
if (!noDamageLayer)
|
||||
thisTileX = PairedMapLayer.RANGE * (thisTile % PairedMapLayer.RANGE) + nearbyTilesInfo
|
||||
else
|
||||
thisTileX = nearbyTilesInfo
|
||||
|
||||
val thisTileY = thisTile / PairedMapLayer.RANGE
|
||||
|
||||
if (drawModeTilesBlendMul) {
|
||||
if (isBlendMul(thisTile)) {
|
||||
drawTile(mode, x, y, thisTileX, thisTileY)
|
||||
}
|
||||
} else {
|
||||
// currently it draws all the transparent tile and colour mixes
|
||||
// on top of the previously drawn tile
|
||||
// TODO check wether it works as intended when skybox is dark
|
||||
// add instruction "if (!isBlendMul((byte) thisTile))"
|
||||
if (!isBlendMul(thisTile)) {
|
||||
drawTile(mode, x, y, thisTileX, thisTileY)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: NullPointerException) {
|
||||
// do nothing. This exception handling may hide erratic behaviour completely.
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tilesetBook[mode].endUse()
|
||||
}
|
||||
|
||||
private fun getGrassInfo(x: Int, y: Int, from: Int, to: Int): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param x
|
||||
* *
|
||||
* @param y
|
||||
* *
|
||||
* @return [0-15] 1: up, 2: right, 4: down, 8: left
|
||||
*/
|
||||
private fun getNearbyTilesInfo(x: Int, y: Int, mode: Int, mark: Int): Int {
|
||||
val nearbyTiles = IntArray(4)
|
||||
if (x == 0) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y)
|
||||
}
|
||||
|
||||
if (x == map.width - 1) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y)
|
||||
}
|
||||
|
||||
if (y == 0) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = 0
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1)
|
||||
}
|
||||
|
||||
if (y == map.height - 1) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1)
|
||||
}
|
||||
|
||||
// try for
|
||||
var ret = 0
|
||||
for (i in 0..3) {
|
||||
if (nearbyTiles[i] == mark) {
|
||||
ret += 1 shl i // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getNearbyTilesInfoNonSolid(x: Int, y: Int, mode: Int): Int {
|
||||
val nearbyTiles = IntArray(4)
|
||||
if (x == 0) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(mode, x - 1, y)
|
||||
}
|
||||
|
||||
if (x == map.width - 1) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(mode, x + 1, y)
|
||||
}
|
||||
|
||||
if (y == 0) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = 0
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_UP] = map.getTileFrom(mode, x, y - 1)
|
||||
}
|
||||
|
||||
if (y == map.height - 1) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(mode, x, y + 1)
|
||||
}
|
||||
|
||||
// try for
|
||||
var ret = 0
|
||||
for (i in 0..3) {
|
||||
try {
|
||||
if (!TilePropCodex.getProp(nearbyTiles[i]).isSolid) {
|
||||
ret += 1 shl i // add 1, 2, 4, 8 for i = 0, 1, 2, 3
|
||||
}
|
||||
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun getNearbyTilesInfoWallSticker(x: Int, y: Int): Int {
|
||||
val nearbyTiles = IntArray(4)
|
||||
val NEARBY_TILE_KEY_BACK = NEARBY_TILE_KEY_UP
|
||||
if (x == 0) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_LEFT] = map.getTileFrom(TERRAIN, x - 1, y)
|
||||
}
|
||||
|
||||
if (x == map.width - 1) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_RIGHT] = map.getTileFrom(TERRAIN, x + 1, y)
|
||||
}
|
||||
|
||||
if (y == map.height - 1) {
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = 4096
|
||||
} else {
|
||||
nearbyTiles[NEARBY_TILE_KEY_DOWN] = map.getTileFrom(TERRAIN, x, y + 1)
|
||||
}
|
||||
|
||||
nearbyTiles[NEARBY_TILE_KEY_BACK] = map.getTileFrom(WALL, x, y)
|
||||
|
||||
try {
|
||||
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid && TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid) {
|
||||
if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid)
|
||||
return 0
|
||||
else
|
||||
return 3
|
||||
} else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_RIGHT]).isSolid) {
|
||||
return 2
|
||||
} else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_LEFT]).isSolid) {
|
||||
return 1
|
||||
} else if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid) {
|
||||
return 0
|
||||
} else
|
||||
return 3
|
||||
} catch (e: ArrayIndexOutOfBoundsException) {
|
||||
return if (TilePropCodex.getProp(nearbyTiles[NEARBY_TILE_KEY_BACK]).isSolid)
|
||||
0
|
||||
else
|
||||
3
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun drawTile(mode: Int, tilewisePosX: Int, tilewisePosY: Int, sheetX: Int, sheetY: Int) {
|
||||
if (Terrarum.game.screenZoom == 1f) {
|
||||
tilesetBook[mode].renderInUse(
|
||||
FastMath.floor((tilewisePosX * TSIZE).toFloat()), FastMath.floor((tilewisePosY * TSIZE).toFloat()), sheetX, sheetY)
|
||||
} else {
|
||||
tilesetBook[mode].getSprite(
|
||||
sheetX, sheetY).drawEmbedded(
|
||||
Math.round(tilewisePosX.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), Math.round(tilewisePosY.toFloat() * TSIZE.toFloat() * Terrarum.game.screenZoom).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat(), FastMath.ceil(TSIZE * Terrarum.game.screenZoom).toFloat())
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun div16(x: Int): Int {
|
||||
return x and 0x7FFFFFFF shr 4
|
||||
}
|
||||
|
||||
fun mod16(x: Int): Int {
|
||||
return x and 15
|
||||
}
|
||||
|
||||
fun quantise16(x: Int): Int {
|
||||
return x and 0xFFFFFFF0.toInt()
|
||||
}
|
||||
|
||||
fun clampW(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.width * TSIZE) {
|
||||
return map.width * TSIZE
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
fun clampH(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.height * TSIZE) {
|
||||
return map.height * TSIZE
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun clampWTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.width) {
|
||||
return map.width
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun clampHTile(x: Int): Int {
|
||||
if (x < 0) {
|
||||
return 0
|
||||
} else if (x > map.height) {
|
||||
return map.height
|
||||
} else {
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getRenderWidth(): Int {
|
||||
return renderWidth
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getRenderHeight(): Int {
|
||||
return renderHeight
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getRenderStartX(): Int {
|
||||
return div16(cameraX)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getRenderStartY(): Int {
|
||||
return div16(cameraY)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getRenderEndX(): Int {
|
||||
return clampWTile(getRenderStartX() + div16(renderWidth) + 2)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getRenderEndY(): Int {
|
||||
return clampHTile(getRenderStartY() + div16(renderHeight) + 2)
|
||||
}
|
||||
|
||||
private fun isConnectSelf(b: Int): Boolean {
|
||||
return Arrays.asList(*TILES_CONNECT_SELF).contains(b)
|
||||
}
|
||||
|
||||
private fun isConnectMutual(b: Int): Boolean {
|
||||
return Arrays.asList(*TILES_CONNECT_MUTUAL).contains(b)
|
||||
}
|
||||
|
||||
private fun isWallSticker(b: Int): Boolean {
|
||||
return Arrays.asList(*TILES_WALL_STICKER).contains(b)
|
||||
}
|
||||
|
||||
private fun isPlatform(b: Int): Boolean {
|
||||
return Arrays.asList(*TILES_WALL_STICKER_CONNECT_SELF).contains(b)
|
||||
}
|
||||
|
||||
private fun isBlendMul(b: Int): Boolean {
|
||||
return Arrays.asList(*TILES_BLEND_MUL).contains(b)
|
||||
}
|
||||
|
||||
private fun setBlendModeMul() {
|
||||
GL11.glEnable(GL11.GL_BLEND)
|
||||
GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_ONE_MINUS_SRC_ALPHA)
|
||||
}
|
||||
|
||||
private fun setBlendModeNormal() {
|
||||
GL11.glDisable(GL11.GL_BLEND)
|
||||
Terrarum.appgc.graphics.setDrawMode(Graphics.MODE_NORMAL)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCameraX(): Int {
|
||||
return cameraX
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getCameraY(): Int {
|
||||
return cameraY
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package com.Torvald.Terrarum.MapDrawer;
|
||||
|
||||
import com.Torvald.Terrarum.*;
|
||||
import com.Torvald.Terrarum.Game;
|
||||
import com.Torvald.Terrarum.GameMap.GameMap;
|
||||
import com.Torvald.Terrarum.TileProperties.TileNameCode;
|
||||
import com.Torvald.Terrarum.TileStat.TileStat;
|
||||
import com.jme3.math.FastMath;
|
||||
import org.newdawn.slick.*;
|
||||
import org.newdawn.slick.geom.Rectangle;
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 15-12-31.
|
||||
*/
|
||||
public class MapDrawer {
|
||||
|
||||
public static final int TILE_SIZE = 16;
|
||||
|
||||
private static Image envOverlayColourmap;
|
||||
|
||||
private static final int ENV_COLTEMP_LOWEST = 5500;
|
||||
private static final int ENV_COLTEMP_HIGHEST = 7500;
|
||||
|
||||
private static final int ENV_COLTEMP_GOLDEN_HOUR = 5000;
|
||||
private static final int ENV_COLTEMP_NOON = 6500;
|
||||
|
||||
private static int colTemp;
|
||||
|
||||
private static final int[] TILES_COLD = {
|
||||
TileNameCode.ICE_MAGICAL
|
||||
, TileNameCode.ICE_FRAGILE
|
||||
, TileNameCode.ICE_NATURAL
|
||||
, TileNameCode.SNOW
|
||||
};
|
||||
|
||||
private static final int[] TILES_WARM = {
|
||||
TileNameCode.SAND_DESERT
|
||||
, TileNameCode.SAND_RED
|
||||
};
|
||||
|
||||
public MapDrawer(GameMap map) throws SlickException {
|
||||
new MapCamera(map);
|
||||
|
||||
envOverlayColourmap = new Image("./res/graphics/black_body_col_1000_40000_K.png");
|
||||
|
||||
System.gc();
|
||||
}
|
||||
|
||||
public static void update(GameContainer gc, int delta_t) {
|
||||
}
|
||||
|
||||
public static void render(GameContainer gc, Graphics g) {
|
||||
}
|
||||
|
||||
public static void drawEnvOverlay(Graphics g) {
|
||||
int onscreen_tiles_max = FastMath.ceil(Terrarum.HEIGHT * Terrarum.WIDTH / FastMath.sqr(TILE_SIZE))
|
||||
* 2;
|
||||
float onscreen_tiles_cap = onscreen_tiles_max / 4f;
|
||||
float onscreen_cold_tiles = TileStat.getCount(TILES_COLD);
|
||||
float onscreen_warm_tiles = TileStat.getCount(TILES_WARM);
|
||||
|
||||
int colTemp_cold = colTempLinearFunc((onscreen_cold_tiles / onscreen_tiles_cap));
|
||||
int colTemp_warm = colTempLinearFunc(-(onscreen_warm_tiles / onscreen_tiles_cap));
|
||||
colTemp = colTemp_warm + colTemp_cold - ENV_COLTEMP_NOON;
|
||||
float zoom = Terrarum.game.screenZoom;
|
||||
|
||||
g.setColor(getColourFromMap(colTemp));
|
||||
g.fillRect(MapCamera.getCameraX() * zoom
|
||||
, MapCamera.getCameraY() * zoom
|
||||
, Terrarum.WIDTH * ((zoom < 1) ? 1f / zoom : zoom)
|
||||
, Terrarum.HEIGHT * ((zoom < 1) ? 1f / zoom : zoom)
|
||||
);
|
||||
|
||||
// TODO colour overlay by sun position (5000-morning -> 6500-noon -> 5000-twilight)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param x [-1 , 1], 0 for 6500K (median of ENV_COLTEMP_HIGHEST and ENV_COLTEMP_LOWEST)
|
||||
* @return
|
||||
*/
|
||||
private static int colTempLinearFunc(float x) {
|
||||
int colTempMedian = (ENV_COLTEMP_HIGHEST + ENV_COLTEMP_LOWEST) / 2;
|
||||
|
||||
return Math.round((ENV_COLTEMP_HIGHEST - ENV_COLTEMP_LOWEST) / 2 * FastMath.clamp(x, -1f, 1f)
|
||||
+ colTempMedian);
|
||||
}
|
||||
|
||||
private static Color getColourFromMap(int K) {
|
||||
return envOverlayColourmap.getColor(colTempToImagePos(K), 0);
|
||||
}
|
||||
|
||||
private static int colTempToImagePos(int K) {
|
||||
if (K < 1000 || K >= 40000) throw new IllegalArgumentException("K: out of range. (" + K + ")");
|
||||
return (K - 1000) / 10;
|
||||
}
|
||||
|
||||
public static int getColTemp() {
|
||||
return colTemp;
|
||||
}
|
||||
}
|
||||
87
src/com/Torvald/Terrarum/MapDrawer/MapDrawer.kt
Normal file
87
src/com/Torvald/Terrarum/MapDrawer/MapDrawer.kt
Normal file
@@ -0,0 +1,87 @@
|
||||
package com.Torvald.Terrarum.MapDrawer
|
||||
|
||||
import com.Torvald.Terrarum.GameMap.GameMap
|
||||
import com.Torvald.Terrarum.Terrarum
|
||||
import com.Torvald.Terrarum.TileProperties.TileNameCode
|
||||
import com.Torvald.Terrarum.TileStat.TileStat
|
||||
import com.jme3.math.FastMath
|
||||
import org.newdawn.slick.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 16-03-15.
|
||||
*/
|
||||
object MapDrawer {
|
||||
@JvmStatic val TILE_SIZE = 16
|
||||
|
||||
private var envOverlayColourmap: Image = Image("./res/graphics/black_body_col_1000_40000_K.png")
|
||||
|
||||
@JvmStatic private val ENV_COLTEMP_LOWEST = 5500
|
||||
@JvmStatic private val ENV_COLTEMP_HIGHEST = 7500
|
||||
|
||||
@JvmStatic private val ENV_COLTEMP_GOLDEN_HOUR = 5000
|
||||
@JvmStatic private val ENV_COLTEMP_NOON = 6500
|
||||
|
||||
private var colTemp: Int = 0
|
||||
|
||||
private val TILES_COLD = intArrayOf(
|
||||
TileNameCode.ICE_MAGICAL
|
||||
, TileNameCode.ICE_FRAGILE
|
||||
, TileNameCode.ICE_NATURAL
|
||||
, TileNameCode.SNOW)
|
||||
|
||||
private val TILES_WARM = intArrayOf(
|
||||
TileNameCode.SAND_DESERT
|
||||
, TileNameCode.SAND_RED)
|
||||
|
||||
@JvmStatic
|
||||
fun update(gc: GameContainer, delta_t: Int) {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun render(gc: GameContainer, g: Graphics) {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun drawEnvOverlay(g: Graphics) {
|
||||
val onscreen_tiles_max = FastMath.ceil(Terrarum.HEIGHT * Terrarum.WIDTH / FastMath.sqr(TILE_SIZE.toFloat())) * 2
|
||||
val onscreen_tiles_cap = onscreen_tiles_max / 4f
|
||||
val onscreen_cold_tiles = TileStat.getCount(*TILES_COLD).toFloat()
|
||||
val onscreen_warm_tiles = TileStat.getCount(*TILES_WARM).toFloat()
|
||||
|
||||
val colTemp_cold = colTempLinearFunc(onscreen_cold_tiles / onscreen_tiles_cap)
|
||||
val colTemp_warm = colTempLinearFunc(-(onscreen_warm_tiles / onscreen_tiles_cap))
|
||||
colTemp = colTemp_warm + colTemp_cold - ENV_COLTEMP_NOON
|
||||
val zoom = Terrarum.game.screenZoom
|
||||
|
||||
g.color = getColourFromMap(colTemp)
|
||||
g.fillRect(MapCamera.getCameraX() * zoom, MapCamera.getCameraY() * zoom, Terrarum.WIDTH * if (zoom < 1) 1f / zoom else zoom, Terrarum.HEIGHT * if (zoom < 1) 1f / zoom else zoom)
|
||||
|
||||
// TODO colour overlay by sun position (5000-morning -> 6500-noon -> 5000-twilight)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
* @param x [-1 , 1], 0 for 6500K (median of ENV_COLTEMP_HIGHEST and ENV_COLTEMP_LOWEST)
|
||||
* *
|
||||
* @return
|
||||
*/
|
||||
private fun colTempLinearFunc(x: Float): Int {
|
||||
val colTempMedian = (ENV_COLTEMP_HIGHEST + ENV_COLTEMP_LOWEST) / 2
|
||||
|
||||
return Math.round((ENV_COLTEMP_HIGHEST - ENV_COLTEMP_LOWEST) / 2 * FastMath.clamp(x, -1f, 1f) + colTempMedian)
|
||||
}
|
||||
|
||||
private fun getColourFromMap(K: Int): Color {
|
||||
return envOverlayColourmap.getColor(colTempToImagePos(K), 0)
|
||||
}
|
||||
|
||||
private fun colTempToImagePos(K: Int): Int {
|
||||
if (K < 1000 || K >= 40000) throw IllegalArgumentException("K: out of range. ($K)")
|
||||
return (K - 1000) / 10
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getColTemp(): Int {
|
||||
return colTemp
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.Torvald.Terrarum.MapGenerator;
|
||||
|
||||
import com.Torvald.Rand.HQRNG;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class FloatingIslandsPreset {
|
||||
|
||||
public static final int PRESETS = 5;
|
||||
@@ -11,7 +13,7 @@ public class FloatingIslandsPreset {
|
||||
return generatePreset(index, random);
|
||||
}
|
||||
|
||||
static int[][] generatePreset(int index, HQRNG random){
|
||||
static int[][] generatePreset(int index, Random random){
|
||||
if (index == 0){
|
||||
return processPreset(random, FloatingIslePreset01.data, FloatingIslePreset01.w, FloatingIslePreset01.h);
|
||||
}
|
||||
@@ -30,7 +32,7 @@ public class FloatingIslandsPreset {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int[][] processPreset(HQRNG random, int[] preset, int w, int h){
|
||||
private static int[][] processPreset(Random random, int[] preset, int w, int h){
|
||||
int[][] temp = new int[h][w];
|
||||
int counter = 0;
|
||||
boolean mirrored = random.nextBoolean();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1099
src/com/Torvald/Terrarum/MapGenerator/MapGenerator.kt
Normal file
1099
src/com/Torvald/Terrarum/MapGenerator/MapGenerator.kt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ public class TileStat {
|
||||
|
||||
private static short[] tilestat = new short[GameMap.TILES_SUPPORTED];
|
||||
|
||||
private static final int TSIZE = MapDrawer.TILE_SIZE;
|
||||
private static final int TSIZE = MapDrawer.getTILE_SIZE();
|
||||
|
||||
/**
|
||||
* Update tile stats from tiles on screen
|
||||
|
||||
@@ -34,8 +34,8 @@ class BasicDebugInfoWindow : UICanvas {
|
||||
val sb = StringBuilder()
|
||||
val formatter = Formatter(sb)
|
||||
|
||||
val mouseTileX = ((MapCamera.getCameraX() + gc.getInput().mouseX / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
|
||||
val mouseTileY = ((MapCamera.getCameraY() + gc.getInput().mouseY / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
|
||||
val mouseTileX = ((MapCamera.cameraX + gc.getInput().mouseX / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
|
||||
val mouseTileY = ((MapCamera.cameraY + gc.getInput().mouseY / Terrarum.game.screenZoom) / MapDrawer.TILE_SIZE).toInt()
|
||||
|
||||
g.setColor(Color.white)
|
||||
|
||||
@@ -115,21 +115,33 @@ class BasicDebugInfoWindow : UICanvas {
|
||||
// Hitbox
|
||||
val zoom = Terrarum.game.screenZoom
|
||||
g.setColor(Color(0x007f00))
|
||||
g.drawRect(hitbox.getHitboxStart().getX() * zoom - MapCamera.getCameraX() * zoom, hitbox.getHitboxStart().getY() * zoom - MapCamera.getCameraY() * zoom, hitbox.getWidth() * zoom, hitbox.getHeight() * zoom)
|
||||
g.drawRect(hitbox.getHitboxStart().getX() * zoom - MapCamera.cameraX * zoom
|
||||
, hitbox.getHitboxStart().getY() * zoom - MapCamera.cameraY * zoom
|
||||
, hitbox.getWidth() * zoom
|
||||
, hitbox.getHeight() * zoom)
|
||||
// ...and its point
|
||||
g.fillRect(
|
||||
(hitbox.getPointedX() - 1) * zoom - MapCamera.getCameraX() * zoom, (hitbox.getPointedY() - 1) * zoom - MapCamera.getCameraY() * zoom, 3f, 3f)
|
||||
(hitbox.getPointedX() - 1) * zoom - MapCamera.cameraX * zoom
|
||||
, (hitbox.getPointedY() - 1) * zoom - MapCamera.cameraY * zoom
|
||||
, 3f, 3f)
|
||||
g.drawString(
|
||||
Lang.get("DEV_COLOUR_LEGEND_GREEN") + " : hitbox", (Terrarum.WIDTH - 200).toFloat(), line(2).toFloat())
|
||||
Lang.get("DEV_COLOUR_LEGEND_GREEN") + " : hitbox", (Terrarum.WIDTH - 200).toFloat()
|
||||
, line(2).toFloat())
|
||||
|
||||
// Next hitbox
|
||||
g.setColor(Color.blue)
|
||||
g.drawRect(nextHitbox!!.getHitboxStart().getX() * zoom - MapCamera.getCameraX() * zoom, nextHitbox.getHitboxStart().getY() * zoom - MapCamera.getCameraY() * zoom, nextHitbox.getWidth() * zoom, nextHitbox.getHeight() * zoom)
|
||||
g.drawRect(nextHitbox!!.getHitboxStart().getX() * zoom - MapCamera.cameraX * zoom
|
||||
, nextHitbox.getHitboxStart().getY() * zoom - MapCamera.cameraY * zoom
|
||||
, nextHitbox.getWidth() * zoom
|
||||
, nextHitbox.getHeight() * zoom)
|
||||
// ...and its point
|
||||
g.fillRect(
|
||||
(nextHitbox!!.getPointedX() - 1) * zoom - MapCamera.getCameraX() * zoom, (nextHitbox.getPointedY() - 1) * zoom - MapCamera.getCameraY() * zoom, 3f, 3f)
|
||||
(nextHitbox!!.getPointedX() - 1) * zoom - MapCamera.cameraX * zoom
|
||||
, (nextHitbox.getPointedY() - 1) * zoom - MapCamera.cameraY * zoom
|
||||
, 3f, 3f)
|
||||
g.drawString(
|
||||
Lang.get("DEV_COLOUR_LEGEND_BLUE") + " : nextHitbox", (Terrarum.WIDTH - 200).toFloat(), line(3).toFloat())
|
||||
Lang.get("DEV_COLOUR_LEGEND_BLUE") + " : nextHitbox", (Terrarum.WIDTH - 200).toFloat()
|
||||
, line(3).toFloat())
|
||||
}
|
||||
|
||||
private fun printLine(g: Graphics, l: Int, s: String) {
|
||||
|
||||
Reference in New Issue
Block a user