moar kotlin

Former-commit-id: b529f2c7fb04433c3bc5f04f451fec384da1dc75
Former-commit-id: 23ea19279fbf275d69e08ea09ea9f734d50f5722
This commit is contained in:
Song Minjae
2016-03-15 13:24:21 +09:00
parent 6d58a3698f
commit 73a8e51c31
48 changed files with 2817 additions and 2674 deletions

View File

@@ -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" />

View 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
/**
* &lt;ReferenceID, Amounts&gt;
*/
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
}
}
}

View File

@@ -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

View File

@@ -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
}

View 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)
}
}

View File

@@ -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) {

View File

@@ -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/"
}
}

View File

@@ -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)
}

View 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
}
}

View File

@@ -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();
}

View 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()
}

View File

@@ -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);

View File

@@ -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.");

View File

@@ -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
);

View File

@@ -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);

View File

@@ -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);
}

View 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)
}

View File

@@ -1,20 +0,0 @@
package com.Torvald.Terrarum.GameItem;
import java.util.HashMap;
/**
* Created by minjaesong on 16-03-11.
*/
public class ItemCodex {
/**
* &lt;ItemID or RefID for Actor, TheItem&gt;
* Will return corresponding Actor if ID >= 32768
*/
private static HashMap<Long, InventoryItem> itemCodex;
public static InventoryItem getItem(long code) {
return itemCodex.get(code);
}
}

View File

@@ -0,0 +1,21 @@
package com.Torvald.Terrarum.GameItem
import java.util.*
/**
* Created by minjaesong on 16-03-15.
*/
object ItemCodex {
/**
* &lt;ItemID or RefID for Actor, TheItem&gt;
* 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]!!
}
}

View File

@@ -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;
}
}

View 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)

View File

@@ -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);
}
}

View 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
}
}

View File

@@ -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;
}
}

View 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
}
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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) {